前言
还是那样,懂得如何使用一个常用库,还得了解其原理或者怎么模拟实现,今天实现一下 vue-router 。
有一些知识我这篇文章提到了,这里就不详细一步步写,请看我 手写一个简易的 Vuex
基本骨架
- Vue 里面使用插件的方式是
Vue.use(plugin),这里贴出它的用法:
安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象。
- 全局混入
使用 Vue.mixin(mixin)
全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。可以使用混入向组件注入自定义的行为,它将影响每一个之后创建的 Vue 实例。
- 路由用法
比如简单的:
// 路由数组
const routes = [
{
path: '/',
name: 'Page1',
component: Page1,
},
{
path: '/page2',
name: 'Page2',
component: Page2,
},
]
const router = new VueRouter({
mode: 'history', // 模式
routes,
})
它是传入了mode和routes,我们实现的时候需要在VueRouter构造函数中接收。
在使用路由标题的时候是这样:
<p> <!-- 使用 router-link 组件来导航. --> <!-- 通过传入 `to` 属性指定链接. --> <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 --> <router-link to="/page1">Go to Foo</router-link> <router-link to="/page2">Go to Bar</router-link> </p> <!-- 路由出口 --> <!-- 路由匹配到的组件将渲染在这里 --> <router-view></router-view>
故我们需要使用Vue.component( id, [definition] )注册一个全局组件。
了解了大概,我们就可以写出一个基本骨架
let Vue = null
class VueRouter {
constructor(options) {
this.mode = options.mode || 'hash'
this.routes = options.routes || []
}
}
VueRouter.install = function (_Vue) {
Vue = _Vue
Vue.mixin({
beforeCreate() {
// 根组件
if (this.$options && this.$options.router) {
this._root = this // 把当前vue实例保存到_root上
this._router = this.$options.router // 把router的实例挂载在_router上
} else if (this.$parent && this.$parent._root) {
// 子组件的话就去继承父组件的实例,让所有组件共享一个router实例
this._root = this.$parent && this.$parent._root
}
},
})
Vue.component('router-link', {
props: {
to: {
type: [String, Object],
required: true,
},
tag: {
type: String,
default: 'a', // router-link 默认渲染成 a 标签
},
},
render(h) {
let tag = this.tag || 'a'
return <tag href={this.to}>{this.$slots.default}</tag>
},
})
Vue.component('router-view', {
render(h) {
return h('h1', {}, '视图显示的地方') // 暂时置为h1标签,下面会改
},
})
}
export default VueRouter
mode
vue-router有两种模式,默认为 hash 模式。
history 模式
通过window.history.pushStateAPI 来添加浏览器历史记录,然后通过监听popState事件,也就是监听历史记录的改变,来加载相应的内容。
- popstate 事件
当活动历史记录条目更改时,将触发 popstate 事件。如果被激活的历史记录条目是通过对 history.pushState()的调用创建的,或者受到对 history.replaceState()的调用的影响,popstate 事件的 state 属性包含历史条目的状态对象的副本。
- History.pushState()方法
window.history.pushState(state, title, url)
该方法用于在历史中添加一条记录,接收三个参数,依次为:
- state:一个与添加的记录相关联的状态对象,主要用于popstate事件。该事件触发时,该对象会传入回调函数。也就是说,浏览器会将这个对象序列化以后保留在本地,重新载入这个页面的时候,可以拿到这个对象。如果不需要这个对象,此处可以填null。
- title:新页面的标题。但是,现在所有浏览器都忽视这个参数,所以这里可以填空字符串。
- url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。
hash 模式
使用 URL 的 hash 来模拟一个完整的 URL。,通过监听hashchange事件,然后根据hash值(可通过 window.location.hash 属性读取)去加载对应的内容的。
继续增加代码,
let Vue = null
class HistoryRoute {
constructor() {
this.current = null // 当前路径
}
}
class VueRouter {
constructor(options) {
this.mode = options.mode || 'hash'
this.routes = options.routes || []
this.routesMap = this.createMap(this.routes)
this.history = new HistoryRoute() // 当前路由
this.initRoute() // 初始化路由函数
}
createMap(routes) {
return routes.reduce((pre, current) => {
pre[current.path] = current.component
return pre
}, {})
}
initRoute() {
if (this.mode === 'hash') {
// 先判断用户打开时有没有hash值,没有的话跳转到 #/
location.hash "htmlcode">
VueRouter.install = function (_Vue) {
Vue = _Vue
Vue.component('router-link', {
props: {
to: {
type: [String, Object],
required: true,
},
tag: {
type: String,
default: 'a',
},
},
methods: {
handleClick(event) {
// 阻止a标签默认跳转
event && event.preventDefault && event.preventDefault()
let mode = this._self._root._router.mode
let path = this.to
this._self._root._router.history.current = path
if (mode === 'hash') {
window.history.pushState(null, '', '#/' + path.slice(1))
} else {
window.history.pushState(null, '', path.slice(1))
}
},
},
render(h) {
let mode = this._self._root._router.mode
let tag = this.tag || 'a'
let to = mode === 'hash' "htmlcode">
let Vue = null
class HistoryRoute {
constructor() {
this.current = null
}
}
class VueRouter {
constructor(options) {
this.mode = options.mode || 'hash'
this.routes = options.routes || []
this.routesMap = this.createMap(this.routes)
this.history = new HistoryRoute() // 当前路由
// 初始化路由函数
this.initRoute()
}
createMap(routes) {
return routes.reduce((pre, current) => {
pre[current.path] = current.component
return pre
}, {})
}
initRoute() {
if (this.mode === 'hash') {
// 先判断用户打开时有没有hash值,没有的话跳转到 #/
location.hash "_blank" href="https://github.com/Jacky-Summer/personal-blog" rel="external nofollow" >个人技术博文 Github 仓库,觉得不错的话欢迎 star,给我一点鼓励继续写作吧~
以上就是如何手写简易的 Vue Router的详细内容,更多关于手写简易的 Vue Router的资料请关注其它相关文章!
明霞山资源网 Design By www.htccd.com
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?