vue-router简单实现

1.构造VueRouter类

constructor(options) {
	/* 初始化 */
	this.mode = options.mode || 'hash';
	this.routes = options.routes || [];
	/* 转化为{'/home':Home}的形式 */
	this.routesMap = this.createMap(this.routes);
	/* 初始化状态 */
	this.history = new HistoryRoute();
	/* 初始化路由设置 */
	this.init();
}

2.将routes格式[{path:'/home',component:Home}]转化为{'/home':Home}的形式

createMap(routes) {
	return routes.reduce((data, item) => {
		data[item.path] = item.component;
		return data;
	}, {});
}

3.对hash模式和history模式的路径进行赋值,设置监听函数,用this.history.current保存当前path

init() {
	if (this.mode === 'hash') {
		location.hash ? '' : location.hash = '/';
		// 加载完成事件监听
		window.addEventListener('load', () => {
			this.history.current = location.hash.slice(1);
		})
		/* 监听hash改变 */
		window.addEventListener('hashchange', () => {
			this.history.current = location.hash.slice(1);
		})
	} else {
		location.pathname ? '' : location.pathname = '/';
		window.addEventListener('load', () => {
			this.history.current = location.pathname;
		})
		/* 前进后退 */
		window.addEventListener('popstate', () => {
			this.history.current = location.pathname;
		})
	}
}

4.实现每个子组件this.$routerthis.$route的功能
(1)mixin的内容所有组件适用,并且不会相互干扰
(2)找到根组件并设置this._root=this,每次实例化子组件时继承父组件的_root属性,然后通过数据劫持的方式获取到VueRouter实例
(3)对this._router.history设置响应式,路径改变视图更新

VueRouter.install = (Vue, opts) => {
	Vue.mixin({
		/* 创建实例之前 */
		beforeCreate() {
			/* 只有根组件存在router */
			if (this.$options && this.$options.router) {
				this._root = this;/* 根组件 */
				this._router = this.$options.router;/* 路由实例 */
				/* 监听current */
				Vue.util.defineReactive(this, 'xxx', this._router.history);
			} else {
				this._root = this.$parent._root;/* 传递根组件使每个子组件都可以访问到根组件 */
			}
			/* 劫持$router的获取 */
			Object.defineProperty(this, '$router', {
				get() {/* 获取根组件,然后取根组件的路由项 */
					return this._root._router;
				}
			})
			/* 劫持$route的获取 */
			Object.defineProperty(this, '$route', {
				get() {//路由状态
					return this._root._router.history;
				}
			})
		}
	});
	//省略...
}

5.创建router-linkrouter-view
(1)router-link负责提供触发入口,改变路由状态
(2)router-view接受响应并放到render函数中进行视图更新

VueRouter.install = (Vue, opts) => {
	//省略...
	Vue.component('router-link', {
		props: { to: String, tag: String },
		methods: {
			handleClick() {//点击router-link
				let mode = this._self._root._router.mode;//模式
				if (mode === 'hash') {
					location.hash = this.to;
				} else {
					history.pushState({}, null, this.to);
				}
				//改变状态,从而更新router-view更新视图
				this._self._root._router.history.current = this.to;
			}
		},
		render(h) {
			let mode = this._self._root._router.mode;
			let tag = this.tag ? this.tag : 'a';
			//区别a标签和自定义标签
			if (tag === 'a') return <a href={mode === 'hash' ? `#${this.to}` : this.to}>{this.$slots.default}</a>;
			else return <tag onclick={this.handleClick}>{this.$slots.default}</tag>
		}
	});
	Vue.component('router-view', {
		render(h) {
			/* current加载前获取,需要在beforCreated中设置成响应式对象*/
			let current = this._self._root._router.history.current;/* /home */
			let routesMap = this._self._root._router.routesMap;
			return h(routesMap[current]);//渲染得到的组件
		}
	})

}

源码:https://gitee.com/aeipyuan/vue_router.git

原文地址:https://www.cnblogs.com/aeipyuan/p/12726197.html