「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战」。
vue-router
动态路由 以及 嵌套路由
const routes = [
{
name: 'login',
path: '/login',
component: Login
},
// 嵌套路由
{
path: '/',
component: Layout,
children: [
{
name: 'index',
path: '',
component: Index
},
{
name: 'detail',
path: 'detail/:id',
// 开启props 会把url中的参数传递给组件
// 在组件中通过 props 来接收
props: true,
component: () => import('@/views/Detail.vue')
}
]
}
]
<template>
<div>
<!-- 方式1: 通过当前路由规则,获取数据 -->
通过当前路由规则获取:{{ $route.params.id }}
<br>
<!-- 方式2:路由规则中开启 props 传参 -->
通过开启 props 获取:{{ id }}
</div>
</template>
<script>
export default {
name: 'Detail',
props: ['id']
}
</script>
<style>
</style>
编程式导航
this.$router.push("/")//path
this.$router.push({name: "Home"}) //name
this.$router.push({name: "detail",params:{id:1}}) // path: 'detail/:id',
this.$router.push({path: "detail",query:{id:1}})
this.$router.go(-1) //后退上一次
this.$router.replace() //与push 方法用法相同 但是 会替换当前的地址 不会向浏览器历史栈中添加记录
Hash与History模式区别
不管哪种模式 都是客户端路由的实现方式 当路径发生变化后,不会向服务器发送请求
-
表现形式
-
hash模式 music.163.com/#/playlist?…
-
history模式 music.163.com/playlist/23…
要用好history模式需要服务端的配合支持
-
-
原理区别
- hash模式是基于锚点,以及onhashchange事件
- history模式是基于html5中的historyAPI history.pushState() IE10以后才支持 history.replaceState()
History模式的使用
- History需要服务器的支持
- 单页面应用中,服务端不存在www.testurl.com/login这样的地址会…
- 在服务端应该除了静态资源外都返回单页面应用的index.html
在node服务器中如何配置
//导入处理history模式的模块
const history = require("connect-history-api-fallback")
//注册处理history模式的中间件
app.use(history())
在nginx中如何配置
// start nginx.exe 启动nginx
// nginx.exe -s reload 重启 nginx
// 把打包好的 放在nginx安装目录下的 html 文件夹里
// 修改nginx的配置文件 config/nginx.conf 文件
location / {
//加一行代码 $uri 是指当前请求的这个路径 根据配置 如果找不到文件 依次往后找
try_files $uri $uri/ /index.html
}
VueRouter实现原理
Hash模式
- URL中#后面的内容作为路径地址
- 监听hashchange事件
- 根据当前路由地址找到对应组件重新渲染
History模式
- 通过history.pushState()方法改变地址栏
- 监听popstate事件
- 根据当前路由地址找到对应组件重新渲染
VueRouter类图
VueRouter
- options
- data
- routeMap
- Constructor(Options):VueRouter _ install(Vue):void
- init():void
- initEvent():void
- createRouteMap():void
- initComponents(Vue):void
Vue的构建版本
- 运行时版本:不支持template模板,需要打包的时候提前编译
- 完整版:包含运行时和编译器,体积比运行时版本大10K左右,程序运行的时候把模板转换成render函数
// 路由规则
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
children:[
{
path:"/about/children",
name:"children",
component:() => import("@/views/children.vue")
}
]
}
]
// VueRouter
let _Vue = null;
export default class VueRouter{
static install(Vue){
// 1.判断当前插件是否已经被安装
if ( VueRouter.install.installed ) {
return; //如果插件被安装 则直接 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()
}
}
})
}
// 初始化三个属性
constructor(options){
this.options = options
// this.routeMap = {}
//this.routeMap = [] //嵌套路由的实现
_Vue.util.defineReactive(this,'routeMap',[])
this.current = "/" //创建一个响应式的对象
}
// createRouteMap(){
// // 遍历所有的路由规则,以键值对的形式 存储到routeMap中 键是 path 值是对应的组件
// this.options.routes.forEach( route => {
// this.routeMap[route.path] = route.component
// })
// }
createRouteMap(routes){
routes = routes || this.options.routes
for ( const route of routes ) {
if (route.path === "/" && this.current === "/") {
this.routeMap.push(route)
return
}
if( route.path !== "/" && this.current.indexOf(route.path) != -1 ) {
this.routeMap.push(route)
if (route.children) {
this.createRouteMap(route.children)
}
return
}
}
}
initComponents(Vue){ //vue实例在全局变量中可以拿到,传递参数是为了减少这个方法和外部的依赖
const self = this
Vue.component("router-link",{
props:{
to:String
},
// template:"<a :href='to' ><slot></slot></a>" 运行时版本的Vue是不支持模板编译 可以通过vue.config.js 中去配置 runtimeCompiler 设置为 true 默认值是false
render(h){
return h("a",{
attrs:{
href:this.to
},
on:{
click:this.clickHandler
}
},[this.$slots.default])
},
methods: {
clickHandler(e){
e.preventDefault()
history.pushState({},"",this.to) //三个参数 第一个data是传给popState事件的参数 第二个是title 第三个是 要改变的url
// 将浏览器的url 赋值给 current 由于current 是响应式的数据 所以视图会发生对应的更新
this.$router.current = this.to
self.routeMap=[]
self.createRouteMap()
}
}
})
Vue.component("router-view",{
render(h){
//标记自己是一个 router-view组件
this.$vnode.data.routerView = true
//标记当前router-view的深度
let depth = 0;
let parent = this.$parent
while(parent){
const vondeData = parent.$vnode && parent.$vnode.data
if ( vondeData ) {
if ( vondeData.routerView ) {
// 说明当前的parent是一个router-view
depth++;
}
}
parent = parent.$parent
}
let component = null
const route = self.routeMap[depth]
if (route) {
component = route.component
}
return h(component)
}
})
}
init () { //包装 createRouteMap initComponents initEvent 这三个方法 方便使用
this.createRouteMap();
this.initComponents(_Vue);
this.initEvent()
}
initEvent(){
window.addEventListener("popstate",() => {
this.data.current = window.location.pathname
this.routeMap=[]
this.createRouteMap()
})
}
}