原文: http://blog.gqylpy.com/gqy/280
"一切分离都是为了更好的结合,本文详细介绍了前后端架构分离之后,前端如何实现路由的控制,即Vue路由系统——VueRouter.
VueRouter下载地址(默认最新版本):https://unpkg.com/vue-router@3.0.1/dist/vue-router.js
@
*
VueRouter实现原理:**
==根据锚点值的改变,修改组件内容.==
我们先来看看不使用VueRouter,自己实现路由的控制,如下代码:
<body> <div id="app"></div> <script> let oDiv = document.getElementById('app'); window.onhashchange = function () { // vue-router的实现原理是根据锚值的改变来对页面进行切换 switch (location.hash) { case '#/login': oDiv.innerHTML=`<h1>我是登陆页面</h1>`; break; case '#/register': oDiv.innerHTML = `<h1>我是注册页面</h1>`; break; // 输入其他路径将显示为首页 default: oDiv.innerHTML = `<h1>我是首页</h1>`; break; } }; console.log(location.hash); </script> </body>
测试效果如下图:

可以看到,通过改变锚点值,页面跳转到我们需要的内容.
再来看看VueRouter实现路由的控制:
<body> <div id="app"></div> <script> // 第一步:在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是首页</h1> </div> `, }; let Login = { template: ` <div> <h1>我是登陆页面</h1> </div> `, }; let Register = { template: ` <div> <h1>我是注册页面</h1> </div> `, }; let App = { // 第四部:监听a标签的锚点值的改变 // 下面的router-link会渲染成a标签,to为标签的href属性,to后面是router中定义的路径 // 下面的router-view是页面内容的渲染出口 template: ` <div> <router-link to="/">首页</router-link> <router-link to="/login">登陆</router-link> <router-link to="/register">注册</router-link> <router-view></router-view> </div> `, }; // 第二步:实例化一个router对象(本质上是将路径和页面绑定了对应关系) let router = new VueRouter({ routes: [ // 注意:routes,不是routers { path: '/', component: Home, }, { path: '/login', component: Login, }, { path: '/register', component: Register, }, ], }); new Vue({ el: '#app', template: `<App/>`, // 第三步:在根实例中注册router对象 router: router, components: { App, }, }); </script> </body>
测试效果如下图:

路由命名
方式一:
==通过在vue-router对象中增加name属性来指定路由名称.==
==调用方式:v-bind:to={ name: "路由名称" }==
<body> <div id="app"></div> <script> // 在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是首页</h1> </div> `, }; let Login = { template: ` <div> <h1>我是登陆页面</h1> </div> `, }; let Register = { template: ` <div> <h1>我是注册页面</h1> </div> `, }; let App = { // 下面的router-link会渲染成a标签,to为a标签的href属性,to后面的name指定的是路由别名 // 下面的router-view是页面内容的渲染出口 // 通过v-bind绑定定义的路由名称, v-bind:to="{ ... }", 可简写为:to="{ ... }" template: ` <div> <router-link v-bind:to="{ name: 'home' }">首页</router-link> <router-link :to="{ name: 'login' }">登陆</router-link> <router-link :to="{ name: 'register' }">注册</router-link> <router-view></router-view> </div> `, }; // 实例化一个router对象(本质上是将路径和页面内容绑定了对应关系) let router = new VueRouter({ // 注意:routes, 不是routers routes: [ { // 路由命名 name: 'home', path: '/', component: Home, }, { name: 'login', path: '/login', component: Login, }, { name: 'register', path: '/register', component: Register, }, ], }); new Vue({ el: '#app', template: `<App/>`, // 在根实例中注册router对象 router: router, components: { App, }, }); </script> </body>
方式二:
<body> <div id="app"></div> <script> Vue.use(VueRouter); let App = { template: ` <div> <router-link to="/">首页</router-link> <router-view name="header"></router-view> <router-view name="footer"></router-view> </div> `, }; let router = new VueRouter({ routes: [ { path: '/', components: { header: { template: ` <div>头部</div> `, }, footer: { template: ` <div>底部</div> `, }, }, }, ], }); new Vue({ el: '#app', template: `<App/>`, router: router, components: { App, }, }); </script> </body>
路由参数
<body> <div id="app"></div> <script> /* 在真实的场景中,有以下两种路径形式: 1. xx.html/users/1 2. xx.html/users?userId=1 */ // 在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是主页面</h1> </div> `, }; let userParams = { template: ` <div> <h1>我是用户1的信息</h1> </div> `, }; let userQuery = { template: ` <div> <h1>我是用户2的信息</h1> </div> `, }; let App = { // 下面的router-link会渲染成a标签,to为a标签的href属性,to后面的name指定的是路由别名 // 下面的router-view是页面内容的渲染出口 // 通过v-bind绑定路由名称, v-bind:to="{ ... }", 可简写为:to="{ ... }" // 下面的params与query参数分别对应上面所说的两种路径形式 template: ` <div> <router-link :to="{ name: 'home' }">首页</router-link> <router-link :to="{ name: 'userParams', params: {userId: 1 } }">登陆</router-link> <router-link :to="{ name: 'userQuery', query: { userId: 2 } }">注册</router-link> <router-view></router-view> </div> `, }; // 创建一个vue-router对象(本质上是将路径和页面内容绑定了对应关系) let router = new VueRouter({ routes: [ { name: 'home', path: '/', component: Home, }, { // 1. xx.html/users/1 name: 'userParams', path: '/users/:userId', component: userParams, }, { // 2. xx.html/users?userId=1 name: 'userQuery', path: '/users', component: userQuery, }, ], }); new Vue({ el: '#app', template: `<App/>`, // 在跟实例中注册router对象 router: router, components: { App, }, }); </script> </body>
测试效果如下图:


***
路由参数的实现原理
<body> <div id="app"></div> <script> /* 在真实的场景中,有以下两种路径形式: 1. xx.html/user/1 2. xx.html/user/?userId=1 */ // 在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是主页面</h1> </div> `, }; let userParams = { template: ` <div> <h1>我是用户1的信息</h1> </div> `, created () { console.log('this.$route:', this.$route); console.log('userId:', this.$route.params.userId); // 此时可以发送ajax请求到后端获取数据 }, }; let userQuery = { template: ` <div> <h1>我是用户2的信息</h1> </div> `, created () { console.log('this.$route:', this.$route); console.log('userId:', this.$route.query.userId); // 此时可以发送ajax请求到后端获取数据 } }; let App = { // 下面的router-link会渲染成a标签,to为a标签的href属性,to后面的name指定的是路由别名 // 下面的router-view是页面内容的渲染出口 // 通过v-bind绑定路由名称, v-bind:to="{ ... }", 可简写为:to="{ ... }" // 下面params与query参数分别对应上面所说的两种路径形式 template: ` <div> <router-link :to="{ name: 'home' }">首页</router-link> <router-link :to="{ name: 'userParams', params: {userId: 1 } }">登陆</router-link> <router-link :to="{ name: 'userQuery', query: { userId: 2 } }">注册</router-link> <router-view></router-view> </div> `, }; // 创建一个vue-router对象(本质上是将路径和页面内容绑定了对应关系) let router = new VueRouter({ routes: [ { name: 'home', path: '/', component: Home, }, { // 1. xx.html/user/1 name: 'userParams', path: '/user/:userId', component: userParams, }, { // 2. xx.html/user/?userId=1 name: 'userQuery', path: '/user', component: userQuery, }, ], }); new Vue({ el: '#app', template: `<App/>`, // 在跟实例中注册router对象 router: router, components: { App, }, }); </script> </body>
测试效果如下图:


子路由
<body> <div id="app"></div> <script> // 在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是首页</h1> </div> `, }; let Test = { template: ` <div> <h1>我是测试页面</h1> <router-link to="childpages01">子页面01</router-link> <router-link to="childpages02">子页面02</router-link> <router-view></router-view> </div> `, }; let ChildPages01 = { template: ` <div> <h1>我是子页面01</h1> </div> `, }; let ChildPages02 = { template: ` <div> <h1>我是子页面02</h1> </div> `, }; let App = { template: ` <div> <router-link :to="{ name: 'home' }">首页</router-link> <router-link :to="{ name: 'test' }">测试页面</router-link> <router-view></router-view> </div> `, }; // 实例化一个router对象(本质上是将路径和页面内容绑定了对应关系) let router = new VueRouter({ routes: [ { name: 'home', path: '/', component: Home, }, { name: 'test', path: '/courses', component: Test, // children实现子路由(子页面) children: [ { path: 'childpages01', component: ChildPages01, }, { path: 'childpages02', component: ChildPages02, }, ], }, ], }); new Vue({ el: '#app', template: `<App/>`, // 在根实例中注册router对象 router: router, components: { App, }, }); </script> </body>
测试效果如下图:

可见,使用此方法,子页面不能正常显示.
***
子路由之append
==append参数会在路径后面追加子路由的路径,注意:会在后面一直追加.
append参数只适用于只需一层子路由的情况.==
<body> <div id="app"></div> <script> // 在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是首页</h1> </div> `, }; let Test = { // 添加append参数 template: ` <div> <h1>我是测试页面</h1> <router-link to="childpages01" append>子页面01</router-link> <router-link to="childpages02" append>子页面02</router-link> <router-view></router-view> </div> `, }; let ChildPages01 = { template: ` <div> <h1>我是子页面01</h1> </div> `, }; let ChildPages02 = { template: ` <div> <h1>我是子页面02</h1> </div> `, }; let App = { template: ` <div> <router-link :to="{ name: 'home' }">首页</router-link> <router-link :to="{ name: 'test' }">测试页面</router-link> <router-view></router-view> </div> `, }; // 实例化一个router对象(本质上是将路径和页面内容绑定了对应关系) let router = new VueRouter({ routes: [ { name: 'home', path: '/', component: Home, }, { name: 'test', path: '/test', component: Test, // children实现子路由(子页面) children: [ { path: 'childpages01', component: ChildPages01, }, { path: 'childpages02', component: ChildPages02, }, ], }, ], }); new Vue({ el: '#app', template: `<App/>`, // 在根实例中注册router对象 router: router, components: { App, }, }); </script> </body>
测试效果如下图:

这里只点击了一次 子页面01,显示的内容是正常的,后面的路径也是正常的.
下面的是点击了两次:

可见,内容显示不正常了,后面的路径也不正常了——==append参数会在后面一直添加子路由中的路径.==
使用以上方式,路径是写死在属性中的,所以子路径会不断的append到最后面,导致后续访问不到响应的页面,可以采用另一种方式——动态绑定属性.
动态绑定属性
==动态绑定属性即给子路由命名,使用子路由命的名称来实现子路径的正常切换.==
<body> <div id="app"></div> <script> // 在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是首页</h1> </div> `, }; let Test = { // 使用子路由命的名称来实现子路径的正常切换 template: ` <div> <h1>我是测试页面</h1> <router-link :to="{ name: 'childpages01' }">子页面01</router-link> <router-link :to="{ name: 'childpages02' }">子页面02</router-link> <router-view></router-view> </div> `, }; let ChildPages01 = { template: ` <div> <h1>我是子页面01</h1> </div> `, }; let ChildPages02 = { template: ` <div> <h1>我是子页面02</h1> </div> `, }; let App = { template: ` <div> <router-link :to="{ name: 'home' }">首页</router-link> <router-link :to="{ name: 'test' }">测试页面</router-link> <router-view></router-view> </div> `, }; // 实例化一个router对象(本质上是将路径和页面内容绑定了对应关系) let router = new VueRouter({ routes: [ { name: 'home', path: '/', component: Home, }, { name: 'test', path: '/test', component: Test, // children实现子路由(子页面) children: [ { // 给子路径命名 name: 'childpages01', path: 'childpages01', component: ChildPages01, }, { // 给子路径命名 name: 'childpages02', path: 'childpages02', component: ChildPages02, }, ], }, ], }); new Vue({ el: '#app', template: `<App/>`, // 在根实例中注册router对象 router: router, components: { App, }, }); </script> </body>
==注意:此方法必须使用name查找组件和路径的对应关系,而不能使用path.==
***
子路由之append升级版
==所谓的append升级版其实就是同时使用动态绑定属性和append.==
<body> <div id="app"></div> <script> // 在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是首页</h1> </div> `, }; let Test = { // append升级版:<router-link :to="{ name: 'childpages01' }" append>...</router-link> template: ` <div> <h1>我是测试页面</h1> <router-link :to="{ name: 'childpages01' }" append>子页面01</router-link> <router-link :to="{ name: 'childpages02' }" append>子页面02</router-link> <router-view></router-view> </div> `, }; let ChildPages01 = { template: ` <div> <h1>我是子页面01</h1> </div> `, }; let ChildPages02 = { template: ` <div> <h1>我是子页面02</h1> </div> `, }; let App = { template: ` <div> <router-link :to="{ name: 'home' }">首页</router-link> <router-link :to="{ name: 'test' }">测试页面</router-link> <router-view></router-view> </div> `, }; // 实例化一个router对象(本质上是将路径和页面内容绑定了对应关系) let router = new VueRouter({ routes: [ { name: 'home', path: '/', component: Home, }, { name: 'test', path: '/test', component: Test, // children实现子路由(子页面) children: [ { // 给子路径命名 name: 'childpages01', path: 'childpages01', component: ChildPages01, }, { // 给子路径命名 name: 'childpages02', path: 'childpages02', component: ChildPages02, }, ], }, ], }); new Vue({ el: '#app', template: `<App/>`, // 在根实例中注册router对象 router: router, components: { App, }, }); </script> </body>
子路由之非append
<body> <div id="app"></div> <script> // 在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是首页</h1> </div> `, }; let Test = { template: ` <div> <h1>我是测试页面</h1> <router-link to="/test/childpages01">子页面01</router-link> <router-link to="/test/childpages02">子页面02</router-link> <router-view></router-view> </div> `, }; let ChildPages01 = { template: ` <div> <h1>我是子页面01</h1> </div> `, }; let ChildPages02 = { template: ` <div> <h1>我是子页面02</h1> </div> `, }; let App = { template: ` <div> <router-link :to="{ name: 'home' }">首页</router-link> <router-link :to="{ name: 'test' }">测试页面</router-link> <router-view></router-view> </div> `, }; // 实例化一个router对象(本质上是将路径和页面内容绑定了对应关系) let router = new VueRouter({ routes: [ { name: 'home', path: '/', component: Home, }, { name: 'test', path: '/test', component: Test, // children实现子路由(子页面) children: [ { // 子路由之非append(写全路径) path: '/test/childpages01', component: ChildPages01, }, { // 子路由之非append(写全路径) path: '/test/childpages02', component: ChildPages02, }, ], }, ], }); new Vue({ el: '#app', template: `<App/>`, // 在根实例中注册router对象 router: router, components: { App, }, }); </script> </body>
路由重定向
<body> <div id="app"></div> <script> // // 在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是首页</h1> </div> `, }; let Login = { template: ` <div> <h1>我是登陆页面</h1> </div> `, }; let Pay = { template: ` <div> <h1>我是支付页面</h1> </div> `, }; let App = { template: ` <div> <router-link :to="{ name: 'home' }">首页</router-link> <router-link :to="{ name: 'login' }">登陆</router-link> <router-link :to="{ name: 'pay' }">支付</router-link> <router-view></router-view> </div> `, }; // 实例化一个router对象(本质上是将路径和页面内容绑定了对应关系) let router = new VueRouter({ routes: [ { name: 'home', path: '/', component: Home, }, { name: 'login', path: '/login', component: Login, }, { name: 'pay', path: '/pay', // 使用redirect实现路由重定向 // 把a标签的锚点值和页面内容绑定了对应关系 redirect: '/login', component: Pay, }, ], }); new Vue({ el: '#app', template: `<App/>`, // 在根实例中注册router对象 router: router, components: { App, } }); </script> </body>
如上代码,我们点击支付链接时,将会跳转至登陆页面.
手动路由
<body> <div id="app"></div> <script> // 在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是首页</h1> </div> `, }; let Test = { template: ` <div> <h1>我是测试页面</h1> </div> `, }; let App = { // 使用v-on(简写为@)给按钮绑定一个事件 template: ` <div> <router-link to="/">首页</router-link> <button @click="myClick">点击跳转至测试页面</button> <router-view></router-view> </div> `, // 定义一个用于跳转至测试页面的的事件 methods: { myClick: function () { this.$router.push('/test'); } }, }; // 实例化一个router对象(本质上是将路径和页面内容绑定了对应关系) let router = new VueRouter({ routes: [ { path: '/', component: Home, }, { path: '/test', component: Test, }, ], }); new Vue({ el: '#app', template: `<App/>`, // 在根实例中注册router对象 router: router, components: { App, } }); </script> </body>
如上代码,点击按钮即可跳转至测试页面.
路由钩子
<body> <div id="app"></div> <script> // 在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是首页</h1> </div> `, }; let Login = { template: ` <div> <h1>我是登陆页面</h1> </div> `, }; let Pay = { template: ` <div> <h1>我是支付页面</h1> </div> `, }; let App = { template: ` <div> <router-link :to="{ name: 'home' }">首页</router-link> <router-link :to="{ name: 'login' }">登陆</router-link> <router-link :to="{ name: 'pay' }">支付</router-link> <router-view></router-view> `, }; // 实例化一个router对象(本质上是将路径和页面内容绑定了对应关系) let router = new VueRouter({ routes: [ { name: 'home', path: '/', component: Home, }, { name: 'login', path: '/login', component: Login, }, { name: 'pay', path: '/pay', // 这一步骤将调用路由钩子函数(通过布尔值来判断是否调用该钩子函数) meta: {required_login: true}, component: Pay, }, ], }); // 定义一个路由钩子函数 // 通过router对象的beforeEach(function (to, form, next) {}) router.beforeEach(function (to, from, next) { // to:从哪来 // from:到哪去 // next:下一步做什么 console.log('to:', to); console.log('from:', from); console.log('next:', next); if (to.meta.required_login) { next('/login'); } else { next(); // 正常跳转 } // router.afterEach(function (to, from) { ··· }); new Vue({ el: '#app', template: `<App/>`, // 在根实例中注册router对象 router: router, components: { App, } }); </script> </body>
如上代码,通过路由钩子函数实现了点击支付链接时跳转至登陆页面的功能.
在路径中去掉"#"号
在实例化的router对象中增加一个属性:==mode: 'history'==,该属性可将路径中显示的"#"号去掉.
<body> <div id="app"></div> <script> // 在Vue根实例中使用VueRouter Vue.use(VueRouter); let Home = { template: ` <div> <h1>我是首页</h1> </div> `, }; let Login = { template: ` <div> <h1>我是登陆页面</h1> </div> `, }; let Pay = { template: ` <div> <h1>我是支付页面</h1> </div> `, }; let App = { template: ` <div> <router-link :to="{ name: 'home' }">首页</router-link> <router-link :to="{ name: 'login' }">登陆</router-link> <router-link :to="{ name: 'pay' }">支付</router-link> <router-view></router-view> `, }; // 实例化一个router对象(本质上是将路径和页面内容绑定了对应关系) let router = new VueRouter({ // 使用mode: 'history'实现取去掉路径中显示的"#"号 mode: 'history', routes: [ { name: 'home', path: '/', component: Home, }, { name: 'login', path: '/login', component: Login, }, { name: 'pay', path: '/pay', component: Pay, }, ], }); new Vue({ el: '#app', template: `<App/>`, // 在根实例中注册router对象 router: router, components: { App, } }); </script> </body>
"