Vue.js中如何实现前端路由与异步组件的配合使用?
- 内容介绍
- 文章标签
- 相关推荐
本文共计3128个文字,预计阅读时间需要13分钟。
目录+文章目标+P6+P6++~P7+一、背景+二、前端路由特性+三、面试!+四、Hash+原理及实现+1、特性+2、如何更改hash+3、手动实现一个基于hash的路由+五、History+原理及实现+1、HTML5 Hist
目录
- 文章目标
- P6
- P6+ ~ P7
- 一、背景
- 二、前端路由特性
- 三、面试!!!
- 四、Hash 原理及实现
- 1、特性
- 2、如何更改 hash
- 3、手动实现一个基于 hash 的路由
- 五、History 原理及实现
- 1、HTML5 History 常用的 API
- 2、pushState/replaceState 的参数
- 3、History 的特性
- 4、面试!!!
- 5、手动实现一个基于 History 的路由
- 六、Vue-Router
- 1、router 使用
- 2、动态路由匹配
- 3、响应路由参数的变化
- 4、捕获所有路由或 404 Not found 路由
- 5、导航守卫
- 6、完整的导航解析流程
- 7、导航守卫执行顺序(面试!!!)
- 8、滚动行为(面试!!!)
- 9、路由懒加载
文章目标
P6
- 针对
react/vue能够根据业务需求口喷router的关键配置,包括但不限于:路由的匹配规则、路由守卫、路由分层等; - 能够描述清楚
history的主要模式,知道history和router的边界;
P6+ ~ P7
- 在没有路由的情况下,也可以根据也无需求,实现一个简单的路由;
- 读过
router底层的源码,不要求每行都读,可以口喷关键代码即可;
一、背景
远古时期,当时前后端还是不分离的,路由全部都是由服务端控制的,前端代码和服务端代码过度融合在一起。
客户端 使用 举个例子: <!-- 路由匹配到的组件将渲染在这里 -->
<div id="app">
<router-view></router-view>
</div>
// 如果使用模块化机制编程,导入 Vue 和 VueRouter,要调用 Vue.use(VueRouter)
// 1、定义(路由)组件
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' };
const Bar = { template: '<div>bar</div>' };
// 2、定义路由
//每个路由应该映射一个组件,其中 component 可以是通过 Vue.extend() 创建的组件构造器,或者只是一个组件配置对象
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar },
];
// 3、创建 router 实例,然后传 routes 配置
const router = new VueRouter({
routes,
});
// 4、创建和挂载根实例
// 记得要通过 router 配置参数注入路由,从而让整个应用都有路由功能
const app = new Vue({
router,
}).$mount('#app');
我们经常需要把某种模式匹配到的所有路由,全部映射到同个组件,比如用户信息组件,不同用户使用同一个组件。 可以通过 const router = new VueRouter({
routes: [
// 动态路径参数,以冒号开头
{ path: '/user/:id', component: User },
],
});
const User = {
template: '<div>User: {{ $route.params.id }}</div>',
};
复用组件时,想对路由参数的变化作出响应的话,可以使用 举个例子: const User = {
template: '...',
watch: {
$route(to, from) {
// 对路由变化作出响应...
},
},
};
const User = {
template: '...',
beforeRouteUpdate(to, from, next) {
// 对路由变化作出响应...
// don't forget to call next()
},
};
-->前端发起127.0.0.1:8080/green ==> /green
this.init(location.pathname);
this._bindPopState();
}
init(path) {
// pushState/replaceState 不会触发页面的渲染,需要我们手动触发
window.history.replaceState({ path }, null, path);
const cb = this.routes[path];
if (cb) {
cb();
}
}
route(path, callback) {
this.routes[path] = callback || function () {};
}
// ! 跳转并执行对应的 callback
go(path) {
// pushState/replaceState 不会触发页面的渲染,需要我们手动触发
window.history.pushState({ path }, null, path);
const cb = this.routes[path];
if (cb) {
cb();
}
}
// ! 演示一下 popstate 事件触发后,会发生什么
_bindPopState() {
window.addEventListener('popstate', e => {
/*
触发条件:
1、点击浏览器前进按钮
2、点击浏览器后退按钮
3、js 调用 forward 方法
4、js 调用 back 方法
5、js 调用 go 方法
*/
console.log('popstate 触发了');
const path = e.state && e.state.path;
console.log('path >>> ', path);
this.routes[path] && this.routes[path]();
});
}
}
const Router = new BaseRouter();
const body = document.querySelector('body');
const container = document.querySelector('.container');
function changeBgColor(color) {
body.style.backgroundColor = color;
}
Router.route('/', () => changeBgColor('white'));
Router.route('/gray', () => changeBgColor('gray'));
Router.route('/green', () => changeBgColor('green'));
container.addEventListener('click', e => {
if (e.target.tagName === 'A') {
e.preventDefault();
console.log(e.target.getAttribute('href')); // /gray /green 等等
Router.go(e.target.getAttribute('href'));
}
});
六、Vue-Router
1、router 使用
Vue.js,我们已经可以通过组合组件来组成应用程序,当你要把Vue Router添加进来,我们需要做的是,将组件(components)映射到路由(routes),然后告诉Vue Router在哪里渲染它们。2、动态路由匹配
$route.params.id或者参数。3、响应路由参数的变化
watch或者beforeRouteUpdate:
4、捕获所有路由或 404 Not found 路由
当时用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该在最后。
举个例子:
5、导航守卫
vue-router提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种方式植入路由导航过程中:
- 全局的
- 全局前置守卫:
router.beforeEach - 全局解析守卫:
router.beforeResolve - 全局后置钩子:
router.afterEach
- 全局前置守卫:
- 单个路由独享的
- 路由独享守卫:
beforeEnter
- 路由独享守卫:
- 组件级的
beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave
6、完整的导航解析流程
- 导航被触发;
- 在失活的组件里调用离开守卫(前一个组件的
beforeRouteLeave); - 调用全局的
beforeEach守卫; - 在重用的组件里调用
beforeRouteUpdate守卫; - 在路由配置里调用
beforeEnter; - 解析异步路由组件;
- 在被激活的组件里调用
beforeRouterEnter; - 调用全局的
beforeResolve守卫; - 导航被确认;
- 调用全局的
afterEach钩子; - 触发
DOM更新; - 用创建好的实例调用
beforeRouterEnter守卫中传给next的回调函数;
举个例子:
// 全局 const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes, }); // 全局的导航守卫 router.beforeEach((to, from, next) => { console.log(`Router.beforeEach => from=${from.path}, to=${to.path}`); // 可以设置页面的 title document.title = to.meta.title || '默认标题'; // 执行下一个路由导航 next(); }); router.afterEach((to, from) => { console.log(`Router.afterEach => from=${from.path}, to=${to.path}`); }); // 路由独享 const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // 配置数组里针对单个路由的导航守卫 console.log(`TestComponent route config beforeEnter => from=${from.path}, to=${to.path}`); next(); }, }, ], }); // 组件 const Foo = { template: `...`, beforeRouteEnter(to, from, next) { // 在渲染该组件的对应路由被 comfirm 前调用 // 不!能!获取组件实例 this,因为当守卫执行前,组件实例还没被调用 }, beforeRouteUpdate(to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 举个例子来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候 // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用 // 可以访问组件实例 this }, beforeRouteLeave(to, from, next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例 this }, };
next必须调用:
next():进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是confirmed(确认的)。next(false):中断当前的导航。如果浏览器的URL改变了(可能是用户手动或者浏览器后退按钮),那么URL地址会重置到from路由对应的地址。next("/")或者next({ path: "/" }):跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。可以向next传递任意位置对象,且允许设置诸如replace: true、name: "home"之类的选项以及任何用在router-link的to prop或router.push中的选项。next(error):如果传入next的参数是一个Error实例,则导航会被终止且该错误会被传递给router.onError()注册过的回调。
7、导航守卫执行顺序(面试!!!)
- 前一个组件的
beforeRouteLeave - 的
router.beforeEach- 如果是路由参数变化,触发
beforeRouteUpdate
- 如果是路由参数变化,触发
- 里,下一个的
beforeEnter - 内部声明的
beforeRouteEnter - 的
router.afterEach
8、滚动行为(面试!!!)
vue-router里面,怎么记住前一个页面的滚动条的位置???
使用前端路由,当切换到新路由时,想要页面滚动到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。
Vue-router能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。
:这个功能只在支持history.pushState的浏览器中可用。
scrollBehavior生效的条件:
- 浏览器支持
history API; - 页面间的交互是通过
go,forward,back或者浏览器的前进/返回按钮;
window.history.back(); // 后退 window.history.forward(); // 前进 window.history.go(-3); // 接收 number 参数,后退 N 个页面
举个例子:
// 1. 记住:手动点击浏览器返回或者前进按钮,记住滚动条的位置,基于 history API 的,其中包括:go、back、forward、手动点击浏览器返回或者前进按钮 // 2. 没记住:router-link,并没有记住滚动条的位置 const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes, scrollBehavior: (to, from, savedPosition) => { console.log(savedPosition); // 已保存的位置信息 return savedPosition; }, });
9、路由懒加载
当打包构建应用时,JavaScript包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
举个例子:
const Foo = () => import(/* webpackChunkName: "foo" */ './Foo.vue'); const router = new VueRouter({ routes: [{ path: '/foo', component: Foo }], });
到此这篇关于Vue.js 前端路由和异步组件介绍的文章就介绍到这了,更多相关Vue.js 异步组件内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!
本文共计3128个文字,预计阅读时间需要13分钟。
目录+文章目标+P6+P6++~P7+一、背景+二、前端路由特性+三、面试!+四、Hash+原理及实现+1、特性+2、如何更改hash+3、手动实现一个基于hash的路由+五、History+原理及实现+1、HTML5 Hist
目录
- 文章目标
- P6
- P6+ ~ P7
- 一、背景
- 二、前端路由特性
- 三、面试!!!
- 四、Hash 原理及实现
- 1、特性
- 2、如何更改 hash
- 3、手动实现一个基于 hash 的路由
- 五、History 原理及实现
- 1、HTML5 History 常用的 API
- 2、pushState/replaceState 的参数
- 3、History 的特性
- 4、面试!!!
- 5、手动实现一个基于 History 的路由
- 六、Vue-Router
- 1、router 使用
- 2、动态路由匹配
- 3、响应路由参数的变化
- 4、捕获所有路由或 404 Not found 路由
- 5、导航守卫
- 6、完整的导航解析流程
- 7、导航守卫执行顺序(面试!!!)
- 8、滚动行为(面试!!!)
- 9、路由懒加载
文章目标
P6
- 针对
react/vue能够根据业务需求口喷router的关键配置,包括但不限于:路由的匹配规则、路由守卫、路由分层等; - 能够描述清楚
history的主要模式,知道history和router的边界;
P6+ ~ P7
- 在没有路由的情况下,也可以根据也无需求,实现一个简单的路由;
- 读过
router底层的源码,不要求每行都读,可以口喷关键代码即可;
一、背景
远古时期,当时前后端还是不分离的,路由全部都是由服务端控制的,前端代码和服务端代码过度融合在一起。
客户端 使用 举个例子: <!-- 路由匹配到的组件将渲染在这里 -->
<div id="app">
<router-view></router-view>
</div>
// 如果使用模块化机制编程,导入 Vue 和 VueRouter,要调用 Vue.use(VueRouter)
// 1、定义(路由)组件
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' };
const Bar = { template: '<div>bar</div>' };
// 2、定义路由
//每个路由应该映射一个组件,其中 component 可以是通过 Vue.extend() 创建的组件构造器,或者只是一个组件配置对象
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar },
];
// 3、创建 router 实例,然后传 routes 配置
const router = new VueRouter({
routes,
});
// 4、创建和挂载根实例
// 记得要通过 router 配置参数注入路由,从而让整个应用都有路由功能
const app = new Vue({
router,
}).$mount('#app');
我们经常需要把某种模式匹配到的所有路由,全部映射到同个组件,比如用户信息组件,不同用户使用同一个组件。 可以通过 const router = new VueRouter({
routes: [
// 动态路径参数,以冒号开头
{ path: '/user/:id', component: User },
],
});
const User = {
template: '<div>User: {{ $route.params.id }}</div>',
};
复用组件时,想对路由参数的变化作出响应的话,可以使用 举个例子: const User = {
template: '...',
watch: {
$route(to, from) {
// 对路由变化作出响应...
},
},
};
const User = {
template: '...',
beforeRouteUpdate(to, from, next) {
// 对路由变化作出响应...
// don't forget to call next()
},
};
-->前端发起127.0.0.1:8080/green ==> /green
this.init(location.pathname);
this._bindPopState();
}
init(path) {
// pushState/replaceState 不会触发页面的渲染,需要我们手动触发
window.history.replaceState({ path }, null, path);
const cb = this.routes[path];
if (cb) {
cb();
}
}
route(path, callback) {
this.routes[path] = callback || function () {};
}
// ! 跳转并执行对应的 callback
go(path) {
// pushState/replaceState 不会触发页面的渲染,需要我们手动触发
window.history.pushState({ path }, null, path);
const cb = this.routes[path];
if (cb) {
cb();
}
}
// ! 演示一下 popstate 事件触发后,会发生什么
_bindPopState() {
window.addEventListener('popstate', e => {
/*
触发条件:
1、点击浏览器前进按钮
2、点击浏览器后退按钮
3、js 调用 forward 方法
4、js 调用 back 方法
5、js 调用 go 方法
*/
console.log('popstate 触发了');
const path = e.state && e.state.path;
console.log('path >>> ', path);
this.routes[path] && this.routes[path]();
});
}
}
const Router = new BaseRouter();
const body = document.querySelector('body');
const container = document.querySelector('.container');
function changeBgColor(color) {
body.style.backgroundColor = color;
}
Router.route('/', () => changeBgColor('white'));
Router.route('/gray', () => changeBgColor('gray'));
Router.route('/green', () => changeBgColor('green'));
container.addEventListener('click', e => {
if (e.target.tagName === 'A') {
e.preventDefault();
console.log(e.target.getAttribute('href')); // /gray /green 等等
Router.go(e.target.getAttribute('href'));
}
});
六、Vue-Router
1、router 使用
Vue.js,我们已经可以通过组合组件来组成应用程序,当你要把Vue Router添加进来,我们需要做的是,将组件(components)映射到路由(routes),然后告诉Vue Router在哪里渲染它们。2、动态路由匹配
$route.params.id或者参数。3、响应路由参数的变化
watch或者beforeRouteUpdate:
4、捕获所有路由或 404 Not found 路由
当时用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该在最后。
举个例子:
5、导航守卫
vue-router提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种方式植入路由导航过程中:
- 全局的
- 全局前置守卫:
router.beforeEach - 全局解析守卫:
router.beforeResolve - 全局后置钩子:
router.afterEach
- 全局前置守卫:
- 单个路由独享的
- 路由独享守卫:
beforeEnter
- 路由独享守卫:
- 组件级的
beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave
6、完整的导航解析流程
- 导航被触发;
- 在失活的组件里调用离开守卫(前一个组件的
beforeRouteLeave); - 调用全局的
beforeEach守卫; - 在重用的组件里调用
beforeRouteUpdate守卫; - 在路由配置里调用
beforeEnter; - 解析异步路由组件;
- 在被激活的组件里调用
beforeRouterEnter; - 调用全局的
beforeResolve守卫; - 导航被确认;
- 调用全局的
afterEach钩子; - 触发
DOM更新; - 用创建好的实例调用
beforeRouterEnter守卫中传给next的回调函数;
举个例子:
// 全局 const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes, }); // 全局的导航守卫 router.beforeEach((to, from, next) => { console.log(`Router.beforeEach => from=${from.path}, to=${to.path}`); // 可以设置页面的 title document.title = to.meta.title || '默认标题'; // 执行下一个路由导航 next(); }); router.afterEach((to, from) => { console.log(`Router.afterEach => from=${from.path}, to=${to.path}`); }); // 路由独享 const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // 配置数组里针对单个路由的导航守卫 console.log(`TestComponent route config beforeEnter => from=${from.path}, to=${to.path}`); next(); }, }, ], }); // 组件 const Foo = { template: `...`, beforeRouteEnter(to, from, next) { // 在渲染该组件的对应路由被 comfirm 前调用 // 不!能!获取组件实例 this,因为当守卫执行前,组件实例还没被调用 }, beforeRouteUpdate(to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 举个例子来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候 // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用 // 可以访问组件实例 this }, beforeRouteLeave(to, from, next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例 this }, };
next必须调用:
next():进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是confirmed(确认的)。next(false):中断当前的导航。如果浏览器的URL改变了(可能是用户手动或者浏览器后退按钮),那么URL地址会重置到from路由对应的地址。next("/")或者next({ path: "/" }):跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。可以向next传递任意位置对象,且允许设置诸如replace: true、name: "home"之类的选项以及任何用在router-link的to prop或router.push中的选项。next(error):如果传入next的参数是一个Error实例,则导航会被终止且该错误会被传递给router.onError()注册过的回调。
7、导航守卫执行顺序(面试!!!)
- 前一个组件的
beforeRouteLeave - 的
router.beforeEach- 如果是路由参数变化,触发
beforeRouteUpdate
- 如果是路由参数变化,触发
- 里,下一个的
beforeEnter - 内部声明的
beforeRouteEnter - 的
router.afterEach
8、滚动行为(面试!!!)
vue-router里面,怎么记住前一个页面的滚动条的位置???
使用前端路由,当切换到新路由时,想要页面滚动到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。
Vue-router能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。
:这个功能只在支持history.pushState的浏览器中可用。
scrollBehavior生效的条件:
- 浏览器支持
history API; - 页面间的交互是通过
go,forward,back或者浏览器的前进/返回按钮;
window.history.back(); // 后退 window.history.forward(); // 前进 window.history.go(-3); // 接收 number 参数,后退 N 个页面
举个例子:
// 1. 记住:手动点击浏览器返回或者前进按钮,记住滚动条的位置,基于 history API 的,其中包括:go、back、forward、手动点击浏览器返回或者前进按钮 // 2. 没记住:router-link,并没有记住滚动条的位置 const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes, scrollBehavior: (to, from, savedPosition) => { console.log(savedPosition); // 已保存的位置信息 return savedPosition; }, });
9、路由懒加载
当打包构建应用时,JavaScript包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
举个例子:
const Foo = () => import(/* webpackChunkName: "foo" */ './Foo.vue'); const router = new VueRouter({ routes: [{ path: '/foo', component: Foo }], });
到此这篇关于Vue.js 前端路由和异步组件介绍的文章就介绍到这了,更多相关Vue.js 异步组件内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!

