Vue Router 基础回顾
- vue.use()的作用:注册插件VueRouter
- 参数是函数的话,vue.use()调用这个函数来注册组件
- 参数是对象的话,vue.use()调用这个对象的 install 方法注册组件
-
创建路由规则 routes
-
创建路由对象 router,将路由规则作为参数传递进来
const router =new VueRouter({
routes
})
<!-- 导出这个路由对象 -->
export default router
- 在 mian.js 中,这册 router 对象
new Vue({
// 注册router对象
router,
store,
render: h => h(App)
}).$mount('#app')
- 在创建的 vue 实例中传入 router 的作用
- 当 vue 实例中没有 router 的时候,vue 实例没有 r o u t e 和 route和 route和router 属性
- 当 vue 实例中没有 router 的时候,vue 实例中被注入了 r o u t e 和 route和 route和router 属性
2.1 $route:路由规则
2.2 $router:路由对象,提供了路由的相关方法,例如 push - 当我们在某些情况下不方便获取到 vue 市里的 r o u t e 时 候 , 可 以 通 过 route时候,可以通过 route时候,可以通过router.currentRoute 属性访问到
动态路由
- 动态路由在路由规则中,通过一个占位来匹配变化的位置
- 组件加载的方式:懒加载
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
- 在组件中获取动态参数的方式
{
path: '/about/:id', //动态路由
name: 'About',
component: () => import(/* webpackChunkName:"about" */ '../views/About.vue'),
props:true // 开启路由传参
}
<template>
<div class="about">
<!-- 方式1: 通过当前路由规则,获取数据 -->
通过当前路由规则,获取 : {
{ this.$route.params.id }}
<!-- 方式2: 路由规则中开启 props 传参 -->
通过开启 props 获取 : {
{ id }}
</div>
</template>
<script>
export default {
props:["id"]
};
</script>
- 优先使用第二种传参方式,组件和路由解绑
嵌套路由
- 关于嵌套路由默认首页的问题,可以将默认首页的 path 设置为空串("")
const routes = [
{
path: '/',
name: 'Home',
component: Home,
children: [
{
path: '', // 当加载Home页的时候默认显示About内容
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}, {
path: 'info',
name: 'info',
component: () => import(/* webpackChunkName: "info" */ '../views/info.vue')
}
]
}
]
编程式导航
- push => 记录历史
- repalce => 替换当前历史记录
- go 前进/后退
Hash 和 History 模式的区别
-
两种模式都是客户端路由的实现方式,当路径发生变化的时候,不会向服务器发送请求,是由 js 监视路由的变化,然后根据不同的地址来渲染不同的内容
-
Hash 模式
- https://music.163.com/#/playlist?id=3102961863
-
History 模式
- https://music.163.com/playlist/3102961863
原理的区别
-
Hash 模式是基于锚点,以及 onhaschange 事件
-
History 模式是基于 HTML5 中的 History API
- history.pushState() : 路径发生变化,不向服务器发送请求
- history.replaceState() : 路径发生变化,不向服务器发送请求,只会改变浏览器地址栏中的地址,并将该地址添加到历史记录中,实现客户端路由
-
history.pushState()是 IE10 以后才支持,IE10 以前的浏览器只能使用 Hash 模式
-
当路径发生变化
Hash 模式和 History 都不会向服务器发送请求,但是浏览器刷新的时候,都会向服务器发送方请求(都做了静态资源管理,=>请求 url 和根目录下的静态资源一一对应)- Hash 模式:请求更目录下的 index.html,刷新之前的状态在 idnex.html 中通过锚点定位
- History 模式:请求地址栏对应的静态资源(比如https://music.163.com/playlist/),因为我们我们打包的是单页面应用(index.html),找不到playlist.html页面,浏览器报错
History 模式
- History 需要服务器的支持
- 单页面应用,服务器只存在 index.html,不存在http://www.testurl.com/login 这样的地址会会返回 404=>找不到该页面
History 模式 -Node.js
- 在 vue-cli 中,默认是 hash 模式,修改成 history 模式
const router = new VueRouter({
mode: "history",
routes
})
- 在开发环境中 dev-server 做了 history 模式的处理=>当请求的页面在根目录中找不到的时候,就返回 index.html
- 但是我们打包上线的代码所在的服务器没有处理 history 模式,需要我们手动完成该功能
Node 服务器的实现
- 将正常的 vue-cli 创建的项目打包,生成 dist 目录
$ npm run build
-
创建 node 服务器=>入口文件 app.js
-
将 dist 目录下的文件拷贝到 web 目录(网站根目录)下
-
安装第三方模块 express
$ yarn add express -D
-
因为 node 服务器默认没有对 history 模式进行处理(当请求的页面不存在时,返回根目录中的 index.html 文件),我们需要借助第三方模块来完成该工作=>connect-history-api-fallback
-
安装 connect-history-api-fallback 模块
$ yarn add connect-history-api-fallback -D
- 入口文件 app.js(我们先看没处理 history 模式的情形)
const path = require('path')
// 导入处理history模式的模块
const history = require("connect-history-api-fallback")
// 导入express
const express = require("express")
const app = express()
// 注册处理history模式的中间件
// app.use(history())
// 处理静态资源的中间件,网站根目录 ../web
app.use(express.static(path.join(__dirname, "../web")))
app.listen(3000, () => {
console.log("服务器开启,端口:3000")
})
-
切换到 about 页面,刷新
-
改建:服务器处理 history 模式
const path = require('path')
// 导入处理history模式的模块
const history = require("connect-history-api-fallback")
// 导入express
const express = require("express")
const app = express()
// 注册处理history模式的中间件
app.use(history())
// 处理静态资源的中间件,网站根目录 ../web
app.use(express.static(path.join(__dirname, "../web")))
app.listen(3000, () => {
console.log("服务器开启,端口:3000")
})
当我们在 about 页面刷新的时候,会向服务器请求 about.html 的静态资源,但是服务器根目录下没有改资源,由于 connect-history-api-fallback 模块的作用,当根目录下没有相应的静态资源的时候,返回 index.html,然后在客户端(浏览器),根据路由规则,展示 about 页面的相关内容
History 模式-nginx
nginx 服务器配置
- 从官网下载 nginx 的压缩包
- 把压缩包解压到 c 盘根目录(路径无中文),c:\nginx-1.18.0 文件夹
- 打开命令行,切换到目录 c:\nginx-1.18.0
nginx 相关命令
// 启动-在后台启动服务器,不会阻塞当前命令行
$ start nginx
// 重启
$ nginx -s reload
// 停止
$ nginx -s stop
- 将打包好的dist目录下文件拷贝到html目录下
- 启动nginx
$ start nginx.exe
-
在about页面刷新,依然报错
-
改进,修改nginx配置文件
-
重新启动服务器,刷新不报错
$ nginx.exe -s reload
Vue-router实现原理
Vue前置知识
- 插件
- 混入
- Vue.observable()
- 插槽
- render函数
- 运行时和完整版的vue
Hash 模式
- URL 中的#后面的内容作为路径地址,当#后面的内容发生变化的时候,不会像服务器发送请求,浏览器记录列表新增一条记录
- 监听hashchange事件
- 根据当前路由地址找到对应组件重新渲染
Hsitory 模式
- 通过history.pushState()方法改变地址栏,该方法仅仅是改变地址栏地址,不会重新向服务器发送请求,而是在浏览器记录中新增一条记录
- 监听popstate事件,监听浏览器历史操作的变化,在popstate事件中可以记录浏览器改变后的地址,
该事件不会因为history.pushState()或者history.replaceState()而出发,只有通过调用history.forward()或者history.back()才能被触发
- 根据当前路由地址找到对应组件重新渲染
回顾vue-Router核心代码
核心代码
vue-Router类图
属性
- options
记录vueRouter类传入的对象 - data
- 响应式对象,当路由地址发生变化,对应的组件要自动更新,该对象有一个 current 属性,用来记录当前路由地址
- 将普通对象转换成响应式对象=>Vue.observable()
- routeMap
记录路由地址和组件之间的映射关系
方法
- Constructor(options):VueRouter
- 返回值是一个 VueRouter 实例
- 初始化传入的属性和init()方法
- static install(Vue):void 私有方法
注册插件 - init():void
调用下面三个方法,把不同的代码分割到不同的方法中来实现 - initEvent():void
注册popState()事件,监听浏览器历史的变化window.popState() - createRouteMap():void
初始化routeMap属性,把构造函数中传入的路由规则转换成键值对的形式存储到routeMap对象中,键:路由地址 值:对应的组件
- initComponents(Vue):void
创建router-link和router-view两个组件
VueRouter-install方法的实现
install()参数,参数一:Vue构造函数,参数二:可选,选项配置
install()的三个目标
- 判断当前插件(VueRouter)是否已经被安装
- 把Vue构造函数记录到全局变量=>
因为当前的install是静态方法,在这个静态方法中我们接收了一个Vue构造函数,而将来我们在VueRouter中的一些实例方法中还要使用这个Vue的构造函数,比如我们创建router-link和router-view组件的时候,需要调用Vue.compoent()方法,所以需要将这个Vue构造函数记录到全局变量上
- 把创建的Vue实例时候传入的router对象注入到Vue实例上
3.1所有的组件都是Vue的实例
3.2 我们在组件中使用的this.$router就是在这个时候注入到Vue实例上的
install()的三个目标的实现
- install(Vue)中的Vue参数,实际上是Vue.use(VueRouter)传进来的Vue构造函数
1:判断当前插件是否已经被安装
export default class VueRouter {
static install(Vue) {
/* 1:判断当前插件是否已经被安装:因为install是静态方法,
也是一个对象,可以在该对象上挂载一个属性 */
if (VueRouter.install.installed) {
return
}
VueRouter.install.installed = true
}
}
2.把Vue构造函数记录到全局变量
let _Vue= null //定义一个全局变量_Vue
export default class VueRouter {
static install(Vue) {
if (VueRouter.install.installed) {
return
}
VueRouter.install.installed = true
//2.把Vue构造函数记录到全局变量
_vue = Vue
}
}
- 把
创建的Vue实例时候传入的route
r对象注入到Vue实例上
在mian.js入口文件中
new Vue({
router, //创建Vue实例的时候传入的router选项
store,
render: h => h(App)
}).$mount('#app')
即我们需要获取Vue构造函数的选项
let _Vue= null
export default class VueRouter {
static install(Vue) {
if (VueRouter.install.installed) {
return
}
VueRouter.install.installed = true
_Vue = Vue
//3. 把创建的Vue实例时候传入的router对象注入到Vue实例上
_Vue.prototype.$router= "Vue构造函数的选项"
}
}
- 我们知道每个Vue实例(包含组件)其实都有一个$options选项,就是new Vue()创建Vue实例的选项
let _Vue = null
export default class VueRouter {
static install(Vue) {
if (VueRouter.install.installed)