Определение маршрутов
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. И такому маршруту , к примеру, будут соответствовать такие пути запроса как:
Та часть, которая идет после последнего слеша, будет интерпретироваться как значение параметра 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, поэтому такому маршруту будут соответствовать следующие пути:
Вложенные маршруты
Во 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 применяется для установки значений параметров маршрута (если маршрут использует параметры).
В итоге также будут создаваться ссылки, но теперь с привязкой к маршруту.
Какое преимущество у именованных маршрутов? Если, к примеру, мы захотим изменить путь, который используется маршрутом, то после измения пути не надо будет менять ссылки, которые привязаны к этому маршруту. Как в данном случае четыре ссылки привязаны к одному маршруту. Если бы маршрут был бы неименованным, то при изменении его пути (без изменения параметров) пришлось бы менять все четыре ссылки. А с именованным маршрутом не надо менять адрес создаваемых ссылок, поскольку они автоматически привязаны к этому маршруту.