VueRouter基础回顾
文章内容输出来源:拉勾教育大前端高薪训练营 P.S.此处手动@雪姨
使用步骤
-
创建Router对象,router/index.js
import Vue from 'vue' import VueRouter from 'vue-router' import Index from '../views/Index.vue' // 1. 注册路由插件 // Vue.use是用来注册组件,他会调用传入对象的install方法 Vue.use(VueRouter) // 路由规则 const routes = [ { path: '/', name: 'Index', component: Index } ] // 2. 创建 router 对象 const router = new VueRouter({ routes }) export default router
-
注册router对象,main.js
import Vue from 'vue' import App from './App.vue' import router from './router' new Vue({ // 3. 注册 router 对象 router, render: h => h(App) }).$mount('#app')
-
创建路由组件的占位,App.vue
<router-view/>
-
创建链接
<router-link to="/">Index</router-link> <router-link to="/blog">Blog</router-link>
动态路由
const routes = [
{
name: 'detail',
// 路径中携带参数
path: '/detail/:id',
// 路由懒加载
component: () => import(/* webpackChunkName: "detail" */ '../views/Detail.vue')
}
]
// detail 组件中接收路由参数
$route.params.id
推荐使用以下方式传递参数
const routes = [
{
name: 'detail',
// 路径中携带参数
path: '/detail/:id',
component: detail,
props: true
}
]
// detail 组件中接收路由参数
const detail = {
props: ['id'],
template: '<div>Detail ID: {{ id }}</div>'
}
嵌套路由
编程式导航
// 跳转到指定路径
router.push('/login')
// 命名的路由
router.push({ name: 'user', params: { id: '5' }})
router.replace()//不记录本次历史
router.go()//跳转到历史中的某一次,如后退两次或者前进
Hash和History模式
两种模式的区别
-
表现形式的区别
-
Hash模式
-
History模式
-
-
原理的区别
-
hash模式
hash模式基于锚点,以及onhashchange事件
vue Router默认使用的是hash模式,使用hash来模拟一个完整的URL,通过onhashchange监听路径的变化
-
history模式是基于HTML5的HistoryAPI
history.pushState() //IE10以后才支持 history.replaceState() history.go()
-
-
开启History模式
const router = new VueRouter({
// mode: 'hash',
mode: 'history',
routes
})
HTML5 History 模式的使用
- History 需要服务器的支持
- 单页应用中,服务端不存在 www.testurl.com/login 这样的地址会返回找不到该页面
- 在服务端应该除了静态资源外都返回单页应用的 index.html
Node.js环境
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')))
// 开启服务器,端口是 3000
app.listen(3000, () => {
console.log('服务器开启,端口:3000')
})
nginx环境配置
- 从官网下载 nginx 的压缩包
- 把压缩包解压到 c 盘根目录,c:\nginx-1.18.0 文件夹
- 修改 conf\nginx.conf 文件
location / {
root html;
index index.html index.htm;
#新添加内容
#尝试读取$uri(当前请求的路径),如果读取不到读取$uri/这个文件夹下的首页
#如果都获取不到返回根目录中的 index.html
try_files $uri $uri/ /index.html;
}
- 打开命令行,切换到目录 c:\nginx-1.18.0
- nginx 启动、重启和停止
# 启动
start nginx
# 重启
nginx -s reload
# 停止
nginx -s stop
模拟实现自己的Vue Router
**前置知识:**插件、混入、Vue.observable()、插槽、render函数、运行时和完整版的Vue
Hash模式
- URL中#后面的内容作为路径地址,当#后面的路径发生变化的时候,不会去请求服务器
- 监听hashchange事件
- 在hashchange事件中,根据当前的路由地址找到对应的组件并重新渲染
History模式
- 通过history.pushState()方法改变地址栏,把当前地址记录到浏览器的访问地址中,浏览器不会向服务器发送请求
- 监听popstate事件,可以监听到浏览器历史操作的变化,在popstate事件的处理函数中,可以记录改变后的地址;
调用pushState和replaceState方法不会出发该事件,点击浏览器前进后退按钮的时候或者调用history的back或forward方法的时候才会触发
- 根据当前路由地址找到对应组件并重新渲染
模拟实现Vue Router
Vue Router的核心代码
// 注册插件
// Vue.use() 内部调用传入对象的 install 方法
Vue.use(VueRouter)
// 创建路由对象
const router = new VueRouter({
routes: [
{ name: 'home', path: '/', component: homeComponent }
]
})
// 创建 Vue 实例,注册 router 对象
new Vue({
router,
render: h => h(App)
}).$mount('#app')
Vue.use()函数可以传入对象也可以传入方法
- 传入方法会直接执行这个方法
- 传入对象会调用对象内部的install方法
Vue Router的类图
实现思路
- 创建 LVueRouter 插件,静态方法 install
- 判断插件是否已经被加载
- 当 Vue 加载的时候把传入的 router 对象挂载到 Vue 实例上(注意:只执行一次)
- 创建 LVueRouter 类
- 初始化,options、routeMap、app(简化操作,创建 Vue 实例作为响应式数据记录当前路径)
- initRouteMap() 遍历所有路由信息,把组件和路由的映射记录到 routeMap 对象中
- 注册 popstate 事件,当路由地址发生变化,重新记录当前的路径
- 创建 router-link 和 router-view 组件
- 当路径改变的时候通过当前路径在 routerMap 对象中找到对应的组件,渲染 router-view
代码实现
-
创建VueRouter插件
let _Vue = null export default class VueRouter { static install (Vue) { // 1. 判断插件是否已安装 if (VueRouter.install.installed) { return } VueRouter.install.installed = true // 2. 把Vue构造函数记录到全局变量 _Vue = Vue // 3. 把创建Vue实力时候传入的router对象注入到Vue实例上 // _Vue.prototype.$router = // 混入 _Vue.mixin({ beforeCreate () { if (this.$options.router) { _Vue.prototype.$router = this.$options.router this.$options.router.init() } } }) } }
-
实现VueRouter类的构造函数
constructor (options) { this.options = options this.routeMap = {} this.data = _Vue.observable({ current: '/' }) }
-
实现VueRouter类的路由映射函数
createRouteMap () { // 遍历所有路由规则,解析成键值对,存储到routeMap this.options.routes.forEach(route => { // 记录路径和组件的映射关系 this.routeMap[route.path] = route.component }) }
-
实现 LVueRouter 类 - router-link 和 router-view 组件
initComponents (Vue) { Vue.component('router-link', { props: { to: String }, // template: '<a :href="to"><slot></slot></a>' render (h) { return h('a', { attrs: { href: this.to }, on: { click: this.clickHandler } }, [this.$slots.default]) }, methods: { clickHandler (e) { history.pushState({}, '', this.to) this.$router.data.current = this.to e.preventDefault() } } }) const _this = this Vue.component('router-view', { render (h) { let component = _this.routeMap[_this.data.current] // 若该路由没有匹配到相应的组件,则默认匹配到404页面 if (!component) { component = _this.routeMap['*'] } return h(component) } }) }
注意:
-
vue-cli创建项目默认使用的是运行时版本的vue.js
-
如果想切换成带编译器版本的Vue.js,需要修改vue-cli配置
- 项目根目录创建vue.config.js文件,添加runtimeCompoler
module.exports = { runtimeCompiler: true }
-
-
实现VueRouter类的初始化方法init()
init () { this.createRouteMap() this.initComponents(_Vue) this.initEvent() }