Vue 路由及异步组件
前端路由 router 原理及表现
背景
路由全部都是由服务端控制的, 前端代码和服务端代码过度融合在一起.
客户端/前端发起 http 请求 -> 服务器 -> url 路径去匹配不同的路由/返回不同的数据
优点: 直接返回一个 html, 渲染了页面结构. SEO的效果非常好, 首屏时间特别快.
在浏览器输入一个 url 开始 <-> 页面任意元素加载出来/渲染出来 => 首屏时间
缺点: 前端代码和服务端代码过度融合在一起, 开发协同非常的乱, 服务器压力大, 因为把构建 html 的工作放在服务端.
Ajax
异步请求, 浏览器端异步请求获取所需数据.
单页应用
页 -> html 文件
单页 -> 单个 html 文件 (以前 user/index.html. foods/index.html)
特点:
页面中的交互是不刷新页面的.
多个页面间的交互, 不需要刷新页面; 加载过的公共资源, 无需再重复加载.
支撑起单页应用这种特性的是什么?
前端路由
vue -> hash, history
react -> hash, history
页面间的交互不刷新页面
不同 url会渲染不同的内容
Hash
特性
url中带有一个 # 符号, 但是 # 只是浏览器端/客户端的状态, 不会传递给服务端.
www.baidu.com/#/user -> http -> www.baidu.com/
www.baidu.com/#/list/test -> http -> www.baidu.com/
hash 值的更改, 不会导致页面的刷新.
location.hash = '#aaa';
location.hash = '#bbb';
从 #aaa 到 #bbb, 页面是不会刷新的
hash 值的更改, 会在浏览器的访问历史中添加一条记录. 所以我们才可以通过浏览器的返回、前进按钮来控制 hash 的切换
hash 值的更改, 会触发 hashchange 事件
jswindow.addEventListener('hashchange', () => {});
如何更改 hash
location.hash
html 标签的方式
html<a href="#user">点击跳转到 user</a>
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 100%;
height: 60px;
display: flex;
justify-content: space-around;
align-items: center;
font-size: 18px;
font-weight: bold;
background: black;
color: white;
}
a:link,
a:hover,
a:active,
a:visited {
text-decoration: none;
color: white;
}
</style>
</head>
<body>
<div class="container">
<a href="#gray">灰色</a>
<a href="#green">绿色</a>
<a href="#">白色</a>
<button onclick="window.history.go(-1)">返回</button>
</div>
<script type="text/javascript" src="./index.js"></script>
</body>
</html>/* index.js */
class BaseRouter {
constructor() {
this.routes = {};
/* 改变 this 指向 */
const refresh = this.refresh.bind(this);
window.addEventListener('load', refresh);
window.addEventListener('hashchange', refresh);
}
route(path, callback) {
if (!callback) {
throw new Error('callback is required!');
}
this.routes[path] = callback;
}
refresh() {
const path = `/${location.hash.slice(1) || ''}`;
this.routes[path]();
}
}
const body = document.querySelector('body');
function changeBgColor(color) {
return function() {
body.style.backgroundColor = color;
}
}
const Router = new BaseRouter();
Router.route('/', changeBgColor('white'));
Router.route('/green', changeBgColor('green'));
Router.route('/gray', changeBgColor('gray'));History
hash 有个 # 符号, 不美观, 服务端无法接收到 hash 路径和参数
html5 history
window.history.back(); // 后退
window.history.forward(); // 前进
window.history.go(-3); // 接收 number 参数, 后退三个页面
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);pushState/replaceState 的参数
state, 是一个对象, 是一个与指定网址相关的对象, 当 popstate 事件触发的时候, 该对象会传入回调函数
title, 新页面的标题, 浏览器支持不一, null
url, 页面的新地址
pushState, 页面的浏览记录里添加一个历史记录
replaceState, 替换当前历史记录
History 的特性
没有 #
pushState/replaceState 并不会触发popstate事件, 这时我们需要手动触发页面的重新渲染.
我们可以使用 popstate来监听 url 的变化.
popstate 到底什么时候才能触发.
4.1 点击浏览器后退按钮
4.2 点击浏览器前进按钮
4.3 js 调用 forward 方法
4.4 js 调用 back 方法
4.5 js 调用 go 方法
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.container {
width: 100%;
height: 60px;
display: flex;
justify-content: space-around;
align-items: center;
font-size: 18px;
font-weight: bold;
background: black;
color: white;
}
a:link,
a:hover,
a:active,
a:visited {
text-decoration: none;
color: white;
}
</style>
</head>
<body>
<div class="container">
<a href="/gray">灰色</a>
<a href="/green">绿色</a>
<a href="/">白色</a>
<button onclick="window.history.go(-1)">返回</button>
</div>
<script type="text/javascript" src="./index-history.js"></script>
</body>
</html>/* index-history.js */
class BaseRouter {
constructor() {
this.routes = {};
this.go(location.pathname);
this._bindPopState();
}
route(path, callback) {
if (!callback) {
throw new Error('callback is required!');
}
this.routes[path] = callback;
}
go(path) {
// 跳转并执行对应的 callback
window.history.pushState({
path
}, null, path);
const cb = this.routes[path];
cb && cb();
}
_bindPopState() {
// 演示一个 popstate事件触发后, 会发生什么
window.addEventListener('popstate', e => {
const path = e.state && e.state.path;
const cb = this.routes[path];
cb && cb();
});
}
}
const Router = new BaseRouter();
const body = document.querySelector('body');
const container = document.querySelector('.container');
function changeBgColor(color) {
return function () {
body.style.backgroundColor = color;
}
}
Router.route('/', changeBgColor('white'));
Router.route('/green', changeBgColor('green'));
Router.route('/gray', changeBgColor('gray'));
container.addEventListener('click', e => {
if (e.target.tagName === 'A') {
e.preventDefault();
Router.go(e.target.getAttribute('href'));
}
});Hash 和 History
hash 有 #, history 没有
hash 的 # 部分内容不会给服务端, history 的所有内容都会给服务端
hash 通过 hashchange 监听变化, history 通过 popstate 监听变化
vue.js router 使用详解
vue-cli 新建了一个 vue 项目, ts
导航守卫执行顺序
「组件」前一个组件的 beforeRouteLeave
「全局」的 router.beforeEach
(3) 「组件」如果是路由参数变化, 触发 beofreRouteUpdate
「配置文件」里, 下一个的 beforeEnter
「组件」内部声明的 beforeRouteEnter
「全局」的 router.afterEach
vue-router 里面, 怎么记住前一个页面的滚动条的位置.
滚动到了
{top: 100}list -> detail -> list
scrollBehavior 生效的条件
浏览器支持 history api
页面间的交互是通过 go, forward, back 或者浏览器的前进、返回按钮
/*
1. 手动点击浏览器返回或者前进按钮, 记住滚动条的位置, 基于 history, 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;
}
});