Определение маршрутов

Vue.js имеет полноценную систему маршрутизации, которая позволяет сопоставлять запросы к приложению с определенными компонентами. За работу системы маршрутизации во Vue.js отвечает специальная библиотека - vue-router.

Итак, создадим приложение, которое будет использовать маршруты. При этом следует учитывать, что чтобы использовать маршрутизацию в приложении Vue.js, необходимо запускать приложение на сервере, то есть просто кинуть страницу с кодом в браузер не получится. Поэтому для запуска будем использовать Node.js, как наиболее демократичный вариант.

Вначале создадим каталог на жестком диске, где будет размещаться проект. Добавим в этот каталог новый файл package.json:

{
    "name": "routerapp",
    "description": "A Vue.js project with routing",
    "version": "1.0.0",
    "author": "Orkhan Alishov <alishoff.com>",
    "scripts": {
        "start": "lite-server"
    },
    "devDependencies": {
        "lite-server": "^2.2.1"
    }
}

Данный файл просто определяет пакет lite-server, который будет необходим для запуска приложения. Но вообще при желании можно использовать и другие веб-серверы, например, Apache, IIS и т.д.

Затем определим в каталоге проекта файл index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>VueJS</title>
</head>
<body>
    <div id="app">
        <router-view></router-view>
    </div>
    <script src="https://unpkg.com/vue"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    <script>
        const NotFound = { template: '<h2>Page Not Found</h2>' }
        const Home = { template: '<h2>Home Page</h2>' }
        const About = { template: '<h2>About Page</h2>' }
        
        const routes = [
            { path: '/', component: Home },
            { path: '/about', component: About },
            { path: '*', component: NotFound }
        ]

        const router = new VueRouter({
            mode: 'history',
            routes: routes
        })
        
        new Vue({
            el: '#app',
            router: router
        })
    </script>
</body>
</html>

Прежде всего чтобы использовать маршрутизацию в приложении, в начале файла подключается соответствующая библиотека vue-router:

<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

В коде JavaScript вначале определяются несколько компонентов, которые будут обрабатывать запросы по определенным путям:

const NotFound = { template: '<h2>Page Not Found</h2>' }
const Home = { template: '<h2>Home Page</h2>' }
const About = { template: '<h2>About Page</h2>' }

Затем определяются маршруты, которые сопоставляют пути запроса и компоненты:

const routes = [
    { path: '/', component: Home },
    { path: '/about', component: About },
    { path: '*', component: NotFound }
]

Каждый маршрут определяет свойство path, которое представляет путь запроса, и свойство component - компонент, который будет обрабатывать запрос по этому пути.

Таким образом, компонент Home будет обрабатывать запрос по пути "/", то есть по сути запрос к корню сайта. Компонент About обрабатывает запросы по пути "/about". А компонент NotFound будет обрабатывать все остальные пути, для этого для него свойство path имеет в качестве значения "*". Причем когда приложение получит запрос, то этот запрос будет последовательно сопоставляться со всеми маршрутами. Первый маршрут, у которого свойство path совпадет с путем запроса и будет выбран для обработки.

Затем создается объект маршрутизатора VueRouter:

const router = new VueRouter({
    mode: 'history',
    routes: routes
})

У этого объекта устанавливаются два свойства. Свойство mode указывает на используемый режим навигации. В частности, значение "history" применяет history.pushState API, которое позволяет использовать навигацию без перезагрузки страницы.

Далее свойство routes устанавливает маршруты - это выше определенные маршруты.

И в конце объект маршрутизатора передается в объект Vue. И кроме того, в шаблоне объекта Vue помещается компонент router-view:

<router-view></router-view>

В этот элемент и будет помещаться выбранный для обработки запроса компонент.

И в конце перейдем к командной строке с помощью команды cd к папке проекта и для установки пакета lite-server выполним команду:

npm install

После ее выполнения запустим проект, введя в консоль команду:

npm start

По умолчанию будет идти запрос к корню приложению, поэтому такой запрос будет обрабатываться компонентом Home. А если мы обратимся по пути "/about", то запрос будет обработан компонентом About. Запросы по остальным путям будет обрабатывать компонент NotFound.


Навигация и ссылки

Для создания системы навигации в приложении Vue.js применяется компонент router-link. С помощью свойства to у данного компонента можно установить путь для создаваемой ссылки.

Так, возьмем проект из прошлой темы и изменим файл index.html следующим образом:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>VueJS</title>
    <style>
        ul {
            list-style-type: none;
            padding: 0;
        }
        li {
            display: inline-block;
        }
        a {
            padding: 5px;
        }
        a.router-link-active,
        li.router-link-active > a {
            color: red;
        }
    </style>
</head>
<body>
    <div id="app">
        <ul>
            <li><router-link to="/" exact>Home</router-link></li>
            <li><router-link to="/products">Products</router-link></li>
            <li><router-link to="/about">About</router-link></li>
        </ul>
        <router-view></router-view>
    </div>
    <script src="https://unpkg.com/vue"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    <script>
        const NotFound = { template: '<h2>Page Not Found</h2>' }
        const Home = { template: '<h2>Home Page</h2>' }
        const Products = { template: '<h2>Products Page</h2>' }
        const About = { template: '<h2>About Page</h2>' }
        
        const routes = [
            { path: '/', component: Home },
            { path: '/products', component: Products },
            { path: '/about', component: About },
            { path: '*', component: NotFound }
        ]

        const router = new VueRouter({
            mode: 'history',
            routes: routes
        })
        
        new Vue({
            el: '#app',
            router: router
        })
    </script>
</body>
</html>

Теперь шаблон объекта Vue фактически содержит набор ссылок:

<ul>
    <li><router-link to="/" exact>Home</router-link></li>
    <li><router-link to="/products">Products</router-link></li>
    <li><router-link to="/about">About</router-link></li>
</ul>

Атрибут to в качестве значения принимает один из путей, который используется для определения маршрута.

Кроме того, у первой ссылки указан атрибут exact. Дело в том, что путь "/" фактически соответствует любому пути, который начинается с этого символа, в том числе и путям "/about" и "/products". Атрибут exact обеспечивает точное соответствие маршрута только с путем "/".

Также стоит отметить, что при нажатии на ссылку к элементу будет добавляться класс router-link-active. И мы можем использовать это обстоятельство для стилизации активной ссылки.

Также дополнительно мы можем установить другие свойства для router-link. Например, определим новый класс CSS:

.active {
    color: green;
}

Чтобы этот класс использовался в качестве класса для активной ссылки, необходимо использовать свойство active-class:

<ul>
    <li><router-link to="/" exact active-class="active">Home</router-link></li>
    <li><router-link to="/products" active-class="active">Products</router-link></li>
    <li><router-link to="/about" active-class="active">About</router-link></li>
</ul>

Параметры маршрутов

Определяемые во Vue.js маршруты могут содержать параметры. Для определения параметра в конец маршрута добавляется двоеточие, после которого идет название параметра:

{ path: '/products/:id', component: Products }

Например, в данном случае определен параметр id. И такому маршруту , к примеру, будут соответствовать такие пути запроса как:

  • /products/6-tom
  • /products/21
  • /products/phones

Та часть, которая идет после последнего слеша, будет интерпретироваться как значение параметра id.

В компоненте Vue.js мы можем получить параметры запроса через объект $route.params, который содержит значения всех параметров. В частности, для обращения к параметру id нужно использовать выражение $route.params.id.

К примеру, определим следующую страницу index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>VueJS</title>
    <style>
        ul {
            list-style-type: none;
            padding: 0;
        }
        li {
            display: inline-block;
        }
        a {
            padding: 5px;
        }
        a.router-link-active,
        li.router-link-active > a {
            color: red;
        }
    </style>
</head>
<body>
    <div id="app">
        <ul>
            <li><router-link to="/" exact>Home</router-link></li>
            <li><router-link to="/products/1">Product 1</router-link></li>
            <li><router-link to="/products/2">Product 2</router-link></li>
        </ul>
        <router-view></router-view>
    </div>
    <script src="https://unpkg.com/vue"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    <script>
        const NotFound = { template: '<h2>Page Not Found</h2>' }
        const Home = { template: '<h2>Home Page</h2>' }
        const Products = { template: '<h2>Product {{ $route.params.id }}</h2>' }
        
        const routes = [
            { path: '/', component: Home },
            { path: '/products/:id', component: Products },
            { path: '*', component: NotFound }
        ]

        const router = new VueRouter({
            mode: 'history',
            routes: routes
        })
        
        new Vue({
            el: '#app',
            router: router
        })
    </script>
</body>
</html>

Здесь маршрут, использующий компонент Products, применяет один параметр id.

Подобным образом можно использовать большее количество параметров:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>VueJS</title>
    <style>
        ul {
            list-style-type: none;
            padding: 0;
        }
        li {
            display: inline-block;
        }
        a {
            padding: 5px;
        }
        a.router-link-active,
        li.router-link-active > a {
            color: red;
        }
    </style>
</head>
<body>
    <div id="app">
        <ul>
            <li><router-link to="/" exact>Home</router-link></li>
            <li><router-link to="/products/tablets/2">Tablet 2</router-link></li>
            <li><router-link to="/products/phones/3">Phone 3</router-link></li>
        </ul>
        <router-view></router-view>
    </div>
    <script src="https://unpkg.com/vue"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    <script>
        const NotFound = { template: '<h2>Page Not Found</h2>' }
        const Home = { template: '<h2>Home Page</h2>' }
        const Products = { template: `<div><h2>Product</h2>
                                        <h3>Category: {{ $route.params.category }}</h3>
                                        <h3>Id: {{ $route.params.id }}</h3>
                                        </div>` }
        
        const routes = [
            { path: '/', component: Home },
            { path: '/products/:category/:id', component: Products },
            { path: '*', component: NotFound }
        ]

        const router = new VueRouter({
            mode: 'history',
            routes: routes
        })
        
        new Vue({
            el: '#app',
            router: router
        })
    </script>
</body>
</html>

В данном случае маршрут { path: '/products/:category/:id', component: Products } применяет два параметра category и id, поэтому такому маршруту будут соответствовать следующие пути:

  • /products/tablets/2
  • /products/phones/3

Вложенные маршруты

Во Vue.js одни маршруты могут быть вложенными в другие (nested routes), то есть подмаршрутами. Для их использования определим следующую веб-страницу index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>VueJS</title>
    <style>
        ul {
            list-style-type: none;
            padding: 0;
        }
        li {
            display: inline-block;
        }
        a {
            padding: 5px;
        }
        a.router-link-active,
        li.router-link-active > a {
            color: red;
        }
    </style>
</head>
<body>
    <div id="app">
        <ul>
            <li><router-link to="/" exact>Home</router-link></li>
            <li><router-link to="/products/phones">Phones</router-link></li>
            <li><router-link to="/products/tablets">Tablets</router-link></li>
        </ul>
        <router-view></router-view>
    </div>
    <script src="https://unpkg.com/vue"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    <script>
        const NotFound = { template: '<h1>Page not found</h1>' }
        const Home = { template: '<h1>Home page</h1>' }
        
        const Phones = { template: '<h2>Phones</h2>' }
        const Tablets = { template: '<h2>Tablets</h2>' }
        const Products = { template: '<div><h1>Products</h1><router-view></router-view></div>' }
        
        const routes = [
            { path: '/', component: Home },
            {
                path: '/products',
                component: Products,
                children: [
                    {
                        path: 'phones',
                        component: Phones
                    },
                    {
                        path: 'tablets',
                        component: Tablets
                    }
                ]
            },
            { path: '*', component: NotFound }
        ]
        
        const router = new VueRouter({
            mode: 'history',
            routes: routes
        })
        
        new Vue({
            el: '#app',
            router: router
        })
    </script>
</body>
</html>

Здесь для одного маршрута установлено два подмаршрута. Для этого применяется свойство children, которое в качестве параметров принимает массив подмаршрутов.

Родительский маршрут будет использовать компонент Products. Поэтому в шаблоне этого компонента определяется элемент, в который будут помещаться компоненты, которые выбраны для обработки подмаршрутов.

В итоге пути "products/tablets" и "products/phones" будут сопоставляться с подмаршрутами.


Именованные маршруты

Маршруты во Vue.js могут иметь имена. Используя имена маршрутов в дальнейшем мы можем, к примеру, привязывать пути ссылок к этим маршрутам.

Для установки имени в определении маршрута используется свойство name:

{ path: '/', component: Home, name: 'Home' }

Например, определим следующую веб-страницу:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>VueJS</title>
    <style>
        a {
            padding: 5px;
        }
        a.router-link-active,
        li.router-link-active > a {
            color: red;
        }
    </style>
</head>
<body>
    <div id="app">
        <div>
            <router-link to="/" exact>Home page</router-link>
        </div>
        <router-view></router-view>
    </div>
    <script src="https://unpkg.com/vue"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    <script>
        const NotFound = { template: '<h2>Page not found</h2>' }
        const Home = { template: `<div><h2>Home page</h2>
            <ul>
                <li><router-link :to="{ name: 'products', params: { id: 1 }}">Product 1</router-link></li>
                <li><router-link :to="{ name: 'products', params: { id: 2 }}">Product 2</router-link></li>
                <li><router-link :to="{ name: 'products', params: { id: 3 }}">Product 3</router-link></li>
                <li><router-link :to="{ name: 'products', params: { id: 4 }}">Product 4</router-link></li>
            </ul></div>` }
        const Products = { template: '<h2>Product {{ $route.params.id }}</h2>' }
        
        const routes = [
            { path: '/', component: Home },
            {
                path: '/products/:id',
                component: Products,
                name: 'products'
            },
            { path: '*', component: NotFound }
        ]
        
        const router = new VueRouter({
            mode: 'history',
            routes: routes
        })

        new Vue({
            el: '#app',
            router: router
        })
    </script>
</body>
</html>

Здесь используется именованный маршрут Products. В шаблоне объекта Vue определен ряд ссылок, которые привязаны к данному маршруту:

<router-link :to="{ name: 'products', params: { id: 1 }}">Product 1</router-link>

Для установки привязки атрибут v-bind:to (или просто :to) принимает объект, в котором параметр name представляет имя маршрута, а параметр params применяется для установки значений параметров маршрута (если маршрут использует параметры).

В итоге также будут создаваться ссылки, но теперь с привязкой к маршруту.

Какое преимущество у именованных маршрутов? Если, к примеру, мы захотим изменить путь, который используется маршрутом, то после измения пути не надо будет менять ссылки, которые привязаны к этому маршруту. Как в данном случае четыре ссылки привязаны к одному маршруту. Если бы маршрут был бы неименованным, то при изменении его пути (без изменения параметров) пришлось бы менять все четыре ссылки. А с именованным маршрутом не надо менять адрес создаваемых ссылок, поскольку они автоматически привязаны к этому маршруту.