>[success] # vueRouter -- 简单实现
1. 这里实现按照 `router 3.x` 配合 `vue2.x `的一个简单实现模型
2. 如果你已经使用过 `vuerouter `或者正在学习如何使用,你要知道第一点就是为什么绑定在根实例上的`vuerouter `可以在任何组件都能调用它,以`vue2.x` 为例
~~~
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
});
const vm = new Vue({
router, // vue2 使用路由
ccc: 'ccc',
render: h => h(App)
}).$mount('#app')
~~~
* 此时`router `已经在`vue `实例上
![](https://img.kancloud.cn/69/c5/69c5adca997a63072b1eedadefb1d0cb_489x246.png)
* 注册组件`$opitions`中可以通过`$opitions`中的`parent `字段一级一级往上推找到最开始的根 `new Vue`这个对象,因此每个组件都能用到`vuerouter `实例
![](https://img.kancloud.cn/f0/2e/f02e683d51da49d749c7ad2a4aad80a9_629x331.png)
>[info] ## 简单实现
1. 关于整个类的参数定义介绍
~~~
'data':
options - 记录构造函数中传入的对象
data - 是一个对象包含current 记录当前的路由地址, 调用Vue.observable方法将其设置为响应式对象
routeMap - 记录路由地址与组件的对应关系,将路由规则解析到这里
'methods': +号是公共方法 _ 是静态方法
+Constructor(Options): VueRouter - 初始化options
_install(Vue): void - 实现vue插件的机制
+init(): void - 用于调用下面三个方法
+initEvent(): void - 用于实现popState方法, 监听浏览器历史的变化
+createRouteMap(): void - 初始化routemap属性,将options转换成键值对存储到routemap中 ,
键为路由地址,值为对应组件
+initComponents(Vue): void - 创建router-link 和 router-view两个组件
~~~
~~~
// 实现Vue插件机制
static install(Vue) {
// 1. 判断当前插件是否被安装 用于是否需要安装
if (VueRouter.install.installed) {
return
}
VueRouter.install.installed = true
// 2. 把Vue构造函数记录到全局变量 用于使用Vue构造函数相关方法
_Vue = Vue
// 3. 创建Vue实例时传入的router对象注入到所有的Vue实例中 可以使用this.$router
// _Vue.prototype.$router = this.$options.router // this指向VueRouter
// 使用混入获取Vue实例 所有Vue实例混入选项
_Vue.mixin({
// beforeCreate钩子,实例创建前执行,添加router对象
beforeCreate() {
// Vue实例执行 Vue组件不执行 避免重复执行
if (this.$options.router) {
_Vue.prototype.$router = this.$options.router
this.$options.router.init()
}
}
})
}
~~~
>[danger] ##### initComponents
~~~
1.这里还需要创建'router-link'和'router-view
' 两个组件,配合路由的使用
~~~
~~~
// 创建两个组件 router-link与router-view
initComponents(Vue) {
// 创建router-link 接收路由地址 并且本质为超链接
// 此处超链接跳转会向服务器发送请求 单页面不需要发送服务器请求
// 因此拦截超链接跳转并修改地址栏中的地址 使用history.pushState 并且将记录到data中current中
Vue.component('router-link', {
props: {
to: String
},
// 使用插槽,在使用时会将内容填充到插槽中
// 运行时版本 不支持template
// template: '<a :href="to"><slot></slot></a>'
render(h) {
// 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()
}
}
})
// 存储vuerouter实例
const self = this
// 创建router-view 占位符 根据当前路由地址渲染相应路由
Vue.component('router-view', {
render(h) {
// 找到当前路由地址 然后再routeMap对象中查找对应的组件
const component = self.routeMap[self.data.current]
// 然后调用h函数转换为虚拟Dom直接返回
return h(component)
}
})
}
~~~
>[danger] ##### 整体代码
~~~
// 记录传入的Vue 构造函数
let _Vue = null
export default class VueRouter {
// 实现Vue插件机制
static install(Vue) {
// 1. 判断当前插件是否被安装 用于是否需要安装
if (VueRouter.install.installed) {
return
}
VueRouter.install.installed = true
// 2. 把Vue构造函数记录到全局变量 用于使用Vue构造函数相关方法
_Vue = Vue
// 3. 创建Vue实例时传入的router对象注入到所有的Vue实例中 可以使用this.$router
// _Vue.prototype.$router = this.$options.router // this指向VueRouter
// 使用混入获取Vue实例 所有Vue实例混入选项
_Vue.mixin({
// beforeCreate钩子,实例创建前执行,添加router对象
beforeCreate() {
// Vue实例执行 Vue组件不执行 避免重复执行
if (this.$options.router) {
_Vue.prototype.$router = this.$options.router
this.$options.router.init()
}
}
})
}
// Vue Router 构造函数 返回Vue Router
// 初始化vue router 三个属性
constructor(options) {
// vue router 规则
this.options = options
// 将规则以键值对的方式存储 键为路由地址 值为路由组件
// 根据键值对查找路由组件进行渲染
this.routeMap = {}
// 响应式对象 current用于记录当前路由地址 默认为‘/’根目录
// Vue.observable()将传入对象转换为响应式对象
this.data = _Vue.observable({
current: '/'
})
}
// 将下面的初始化方法整合到一起
init() {
this.createRouteMap()
this.initComponents(_Vue)
this.initEvent()
}
// 将options路由规则转换为键值对存储
createRouteMap() {
// 遍历所有路由规则,把路由规则解析成键值对的形式存储到routeMap中
this.options.routes.forEach(route => {
this.routeMap[route.path] = route.component
})
}
// 创建两个组件 router-link与router-view
initComponents(Vue) {
// 创建router-link 接收路由地址 并且本质为超链接
// 此处超链接跳转会向服务器发送请求 单页面不需要发送服务器请求
// 因此拦截超链接跳转并修改地址栏中的地址 使用history.pushState 并且将记录到data中current中
Vue.component('router-link', {
props: {
to: String
},
// 使用插槽,在使用时会将内容填充到插槽中
// 运行时版本 不支持template
// template: '<a :href="to"><slot></slot></a>'
render(h) {
// 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()
}
}
})
// 存储vuerouter实例
const self = this
// 创建router-view 占位符 根据当前路由地址渲染相应路由
Vue.component('router-view', {
render(h) {
// 找到当前路由地址 然后再routeMap对象中查找对应的组件
const component = self.routeMap[self.data.current]
// 然后调用h函数转换为虚拟Dom直接返回
return h(component)
}
})
}
// 注册popState事件 处理前进和后退没有加载相应组件的问题
initEvent() {
window.addEventListener('popstate', () => {
// 此处this指向vuerouter
// 将data中当前路由指向更改
this.data.current = window.location.pathname
})
}
}
~~~
- 官网给的工具
- 声明vue2 和 vue3
- 指令速览
- Mustache -- 语法
- v-once -- 只渲染一次
- v-text -- 插入文本
- v-html -- 渲染html
- v-pre -- 显示原始的Mustache标签
- v-cloak -- 遮盖
- v-memo(新)-- 缓存指定值
- v-if/v-show -- 条件渲染
- v-for -- 循环
- v-bind -- 知识
- v-bind -- 修饰符
- v-on -- 点击事件
- v-model -- 双向绑定
- 其他基础知识速览
- 快速使用
- 常识知识点
- key -- 作用 (后续要更新)
- computed -- 计算属性
- watch -- 侦听
- 防抖和节流
- vue3 -- 生命周期
- vue-cli 和 vite 项目搭建方法
- vite -- 导入动态图片
- 组件
- 单文件组件 -- SFC
- 组件通信 -- porp
- 组件通信 -- $emit
- 组件通信 -- Provide / Inject
- 组件通信 -- 全局事件总线mitt库
- 插槽 -- slot
- 整体使用案例
- 动态组件 -- is
- keep-alive
- 分包 -- 异步组价
- mixin -- 混入
- v-model-- 组件
- 使用计算属性
- v-model -- 自定义修饰符
- Suspense -- 实验属性
- Teleport -- 指定挂载
- 组件实例 -- $ 属性
- Option API VS Composition API
- Setup -- 组合API 入口
- api -- reactive
- api -- ref
- 使用ref 和 reactive 场景
- api -- toRefs 和 toRef
- api -- readonly
- 判断性 -- API
- 功能性 -- API
- api -- computed
- api -- $ref 使用
- api -- 生命周期
- Provide 和 Inject
- watch
- watchEffect
- watch vs. watchEffect
- 简单使用composition Api
- 响应性语法糖
- css -- 功能
- 修改css -- :deep() 和 var
- Vue3.2 -- 语法
- ts -- vscode 配置
- attrs/emit/props/expose/slots -- 使用
- props -- defineProps
- props -- defineProps Ts
- emit -- defineEmits
- emit -- defineEmits Ts
- $ref -- defineExpose
- slots/attrs -- useSlots() 和 useAttrs()
- 自定义指令
- Vue -- 插件
- Vue2.x 和 Vue3.x 不同点
- $children -- 移除
- v-for 和 ref
- attribute 强制行为
- 按键修饰符
- v-if 和 v-for 优先级
- 组件使用 v-model -- 非兼容
- 组件
- h -- 函数
- jsx -- 编写
- Vue -- Router
- 了解路由和vue搭配
- vueRouter -- 简单实现
- 安装即使用
- 路由懒加载
- router-view
- router-link
- 路由匹配规则
- 404 页面配置
- 路由嵌套
- 路由组件传参
- 路由重定向和别名
- 路由跳转方法
- 命名路由
- 命名视图
- Composition API
- 路由守卫
- 路由元信息
- 路由其他方法 -- 添加/删除/获取
- 服务器配置映射
- 其他
- Vuex -- 状态管理
- Option Api -- VUEX
- composition API -- VUEX
- module -- VUEX
- 刷新后vuex 数据同步
- 小技巧
- Pinia -- 状态管理
- 开始使用
- pinia -- state
- pinia -- getter
- pinia -- action
- pinia -- 插件 ??
- Vue 源码解读
- 开发感悟
- 练手项目