路由背景
- 什么是 router 以及 router 发展的历史
- 路由是伴随着 SPA 出现的,在此之前,页面的跳转是后端实现的
现在的模式:客户端向后台请求数据,后台组织好 json 发回给前端
以前的模式:前端表单给后端一个formdata,后端有个controller验证信息,再从controller中取出一个模版,讲数据注入,然后把模版传回前端;
- 传统的⻚面的跳转,是通过前端向后台发送请求;
- 后台通过模板引擎的渲染,将一个新的html界面
- 比如⻚面跳转时:
- from表单的提交;
- a标签的默认属性;
- js调用location.href,给其赋值;
- H5: history 的 go / forward / back -- // history.push / replace ?
- 在 SPA 的出现之后,前端可以自由的控制组建的渲染,来模拟页面的跳转。
- hash 路由和 history 路由 总结:
- 传统的路由,是根据
url访问相关的controller进行数据资源和模版的拼接,返回前端; - 前端路由使用过
JS根据url返回对应的组建加载 * 所以,前端路由包含两个部分:url的处理- 组建加载
简单的 hash 路由 原理:
- 路由发生改变,改变可以被监听到,改变又可以触发事件
模拟一个 hash 路由:
class BaseRouter {
constructor() {
// 初始化一个依赖
this.routes = {};
this.refresh = this.refresh.bind(this);
// 解决刷新后消失
window.addEventListener('load', this.refresh);
// 考虑到刷新
window.addEventListener('hashchange', this.refresh);
}
route(path, callback) {
this.routes[path] = callback || function() {}
}
refresh() {
const path = `/${window.location.hash.slice(1) || ''}`;
this.routes[path]();
}
}
const Route = new BaseRouter();
Route.route('/about', () => changeText('关于我们'));
Route.route('/user', () => changeText('用户详情'));
Route.route('/', () => changeText('首页'));
function changeText(arg) {
document.getElementById('context').innerHTML = arg;
}
<div id="container">
<button onclick="window.location.hash='#'">首页</button>
<button onclick="window.location.hash='#about'">关于我们</button>
<button onclick="window.location.hash='#user'">用户详情</button>
<div id="context"></div>
</div>
模拟一个 history 路由: t
<div id="container">
<a href="./">首页</a>
<a href="./about">关于我们</a>
<a href="./user">用户详情</a>
</div>
<div id="context"></div>
class BaseRouter {
// 初始化依赖
constructor () {
this.routes = {};
// 刷新问题
this.init();
// 解决无法调用前进后退
this._bindPopState();
}
init(path) {
window.history.replaceState({path}, null, path);
const cb = this.routes[path];
if (cb) {
cb();
}
}
route(path, callback) {
this.routes[path] = callback || function() {};
}
go(path) {
window.history.pushState({path}, null, path);
const cb = this.routes[path];
if (cb) {
cb();
}
}
// 内部函数 不暴露
_bindPopState() {
window.addEventListener('popstate', e => {
const path = e.state && e.state.path;
this.routes[path] && this.routes[path]();
})
}
}
const Route = new BaseRouter();
Route.route('./about', () => changeText('关于我们'));
Route.route('./user', () => changeText('用户详情'));
Route.route('./', () => changeText('首页'));
function changeText(arg) {
document.getElementById('context').innerHTML = arg;
}
container.addEventListener('click', e => {
if (e.target.tagName === 'A') {
e.preventDefault();
Route.go(e.target.getAttribute('href'));
}
})
- 动态路由 静态路由:
import About from '../views/About.vue'
{
path: '/about',
name: 'About',
component: About
}
动态路由: 会重新请求一个about.js
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
其实,动态路由 包括 React.lazy 、 import() 就是一种对代码进行拆分的技术,一般叫做 code splitting
在需要的时候,才进行加载;
动态路由拆包越多,请求越多,拆包越少,bundle体积越大,要做个平衡
动态路由/异步组件,就是需要让首屏加载是 bundle 大小最小
-
router 的配置: 入口处 router-link 跳转链接;router-view 加载的内容; router.js 中的home,about 对应加载到router-view 中
-
路由守卫 路由跳转的时候写一些函数钩子 分为:组件中的路由守卫,全局路由守卫
全局路由守卫
// router/index.js
router.beforeEach((to, from, next) => {
console.log("全局-beforeEach", to);
next();
});
router.beforeResolve((to, from, next) => {
console.log("全局-beforeResolve", to);
next();
});
router.afterEach((to, from, next) => {
console.log("全局-afterEach", to);
next();
});
组件中的路由守卫
// Home.vue
beforeRouterEnter(from, to, next) {
console.log('组件内-beforeRouterEnter', to);
next();
},
beforerouterUpdate(from, to, next) {
console.log('组件内-beforerouterUpdate', to);
next();
},
beforeRouterLeave(from, to, next) {
console.log('组件内-beforeRouterLeave', to);
next();
},
4.1 路由守卫触发流程
- 【组件】 - 前一个组件 beforeRouterLeave
- 【全局】 - router.beforeEach
- 【组件】 - 如果是由路由的参数变化,触发beforeRouterUpdate
- 【配置文件】 里,下一个beforeEnter
- 【组件】 - 内部声明的beforeRouterEnter
- 【全局】 - beforeResolve
- 【全局】 - router.afterEach
题目总结
- hash 路由和 history 路由的区别?
- hash 路由一般会携带一个
#号,不够美观;history 路由不存在 - 默认 hash 路由是不会向浏览器端发出请求的,主要一般适用于锚点(用于定位页面中某部分内容);history 中 go/back/forward 以及浏览器中的前进和后退按钮,一般都会向服务器端发出请求 -- history 的所有url内容,服务器端都可以获取到
- 基于此,hash 路由不支持 SSR 的,但 history 路由是可以的
- history 路由在部署的时候,如 nginx,只需要渲染首页,让首页根据路径重新跳转
- hash 路由的监听,一般用onhashChange 事件监听; history 路由监听,一般是 onPopState 监听
- nginx 如何部署:
# 单个服务器的部署
location / {
try_files uri $uri /xxx/main/index.html
}
// 面试了解即可
# 存在代理的情况
location / {
rewrite ^ /file/index.html break; # 这里代表的是 xxx.cdn 的资源路径
proxy_pass https://www.xxx.cdn.com
}
- history history 是一个 BOM api,里面有 go/forward/back 三个API,以及pushState/replaceState;
- pushState/replaceState 都不会触发popState 事件,(不一定会重新渲染)
- popState 什么时候触发?
- 点击浏览器前进,后退按钮
- back/forward/go
- 实现一个hash/history