赞
踩
本文将浅析vue-router的具体实现,并且实现一个简易的vue-router。
如果你很了解vue的插件应该怎么写,可以跳过此部分。
Vue.js 的插件应该暴露一个 install
方法。这个方法的第一个参数是 Vue
构造器,第二个参数是一个可选的选项对象:
- MyPlugin.install = function (Vue, options) {
- // 1. 添加全局方法或属性
- Vue.myGlobalMethod = function () {
- // 逻辑...
- }
-
- // 2. 添加全局资源
- Vue.directive('my-directive', {
- bind (el, binding, vnode, oldVnode) {
- // 逻辑...
- }
- ...
- })
-
- // 3. 注入组件选项
- Vue.mixin({
- created: function () {
- // 逻辑...
- }
- ...
- })
-
- // 4. 添加实例方法
- Vue.prototype.$myMethod = function (methodOptions) {
- // 逻辑...
- }
- }
全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。
- Vue.mixin({
- created: function () {
- var myOption = this.$options.myOption
- if (myOption) {
- console.log(myOption)
- }
- }
- })
-
- new Vue({
- myOption: 'hello!'
- })
- // => "hello!"
1.使用一个current变量储存当前url
2.监听浏览器url变化,改变current
3.监听current,获取应该渲染的新组件
4.渲染新组件
下面细讲:
- class HistoryRoute{
- constructor(){
- this.current=null;
- }
- }
- class vueRouter{
- constructor(options){
- this.mode=options.mode||'hash';
- this.routes=options.routes;
- this.history=new HistoryRoute;
- }
- }
-
-
- export default vueRouter;
在上述代码中,我们写了两个类,current用于保存当前路径,vueRouter则是我们要export出去的对象,所以他可以接受到一个options,这个options是从main.js(如果是vue-cli3则是router/index.js)中,new vueRouter时传进来的。(如下图,红框部分)
我们在构造函数中,取出mode,如果没有则默认是hash模式。把路由表保存到routes中。
- window.addEventListener('load',()=>{
- this.history.current=location.hash.slice(1);
- })
- window.addEventListener('hashchange',()=>{
- this.history.current=location.hash.slice(1);
- })
通过对load的监听,初始化history中current的值,通过对hashchange的监听,改变current的值。
current如何监听?vue提供了一个defineReactive的公共方法:
- // exposed util methods.
- // NOTE: these are not considered part of the public API - avoid relying on
- // them unless you are aware of the risk.
- Vue.util = {
- //warn: warn,
- //extend: extend,
- //mergeOptions: mergeOptions,
- defineReactive: defineReactive$$1
- };
- /**
- * Define a reactive property on an Object.
- */
- function defineReactive$$1 (
- obj,
- key,
- val,
- customSetter,
- shallow
- ) {
- var dep = new Dep();
-
- var property = Object.getOwnPropertyDescriptor(obj, key);
- if (property && property.configurable === false) {
- return
- }
-
- // cater for pre-defined getter/setters
- var getter = property && property.get;
- var setter = property && property.set;
- if ((!getter || setter) && arguments.length === 2) {
- val = obj[key];
- }
-
- var childOb = !shallow && observe(val);
- Object.defineProperty(obj, key, {
- enumerable: true,
- configurable: true,
- get: function reactiveGetter () {
- var value = getter ? getter.call(obj) : val;
- if (Dep.target) {
- dep.depend();
- if (childOb) {
- childOb.dep.depend();
- if (Array.isArray(value)) {
- dependArray(value);
- }
- }
- }
- return value
- },
- set: function reactiveSetter (newVal) {
- var value = getter ? getter.call(obj) : val;
- /* eslint-disable no-self-compare */
- if (newVal === value || (newVal !== newVal && value !== value)) {
- return
- }
- /* eslint-enable no-self-compare */
- if (customSetter) {
- customSetter();
- }
- // #7981: for accessor properties without setter
- if (getter && !setter) { return }
- if (setter) {
- setter.call(obj, newVal);
- } else {
- val = newVal;
- }
- childOb = !shallow && observe(newVal);
- dep.notify();
- }
- });
- }
有了这个方法,我们就可以把监听current了
- vueRouter.install=function(vue){
- vue.mixin({
- beforeCreate(){
- if(this.$options&&this.$options.router){
- this._root=this;
- this._router=this.$options.router;
- vue.util.defineReactive(this,'current',this._router.history);
- }else{
- this._root=this.$parent._root;
- }
- }
- })
- }
我们能拿到当前hash了,也有一个router的路由可以对上了,如果我们把router改造成hash为对象的key的数组,这样取对应的组件的时候就容易多了
- createMap(routes){
- return routes.reduce((memo,current)=>{
- memo[current.path]=current.component;
- return memo
- },{})
- }
这样我们就可以通过memo[hash] 很快的取到对应的组件。
- vue.component('router-view',{
- render(h){
- let current=this._self._root._router.history.current;
- let routesMap=this._self._root._router.routesMap;
- return h(routesMap[current])
- }
- })
这样,我们就基本完工啦~
测试:
新建一个vue项目,在项目中新建一个my-router的文件夹,在文件夹中新建index.js,把VueRouter的引用指向新的文件
import VueRouter from '../myrouter'
在浏览器地址栏更改地址,成功跳转!
留个小问题:
请在myrouter/index.js中实现一个简单的<router-link>
答案:
- vue.component('router-link',{
- props:{
- to: String,
- },
- render(h){
- var getChildrenTextContent = function (children) {
- return children.map(function (node) {
- return node.children
- ? getChildrenTextContent(node.children)
- : node.text
- }).join('')
- }
- var headingId = getChildrenTextContent(this.$slots.default);
- return h(
- "div",
- [
- h('a', {
- attrs: {
- name: headingId,
- href: '#' + this.to
- }
- },this.$slots.default)
- ]
- )
- }
-
- })
完整代码已经上传到文章附件中
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。