[学习vue]全家桶的原理和实现

2020-06-28  本文已影响0人  巧克力_404

在正式进入之前先抛出几个问题:
1 vue-router能不能放在react中使用?
2 use vueRouter的时候发生了什么?
3 为什么要把router作为一个选项放在new vue 中?
4 为什么router-link router-view 不需要注册,就可以直接使用?
答案就在文章中~~

vue-router

Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。主要解决的问题就单页面应用的导航问题。

router的任务分析

//vue-router.js
//声明插件:vue插件需求实现一个install静态方法
let Vue; //保存vue构造函数引用 目的是不用吧vue打包进去
class KVueRouter{
}
//参数是vue构造函数
KVueRouter.install = function(_vue){
    Vue = _Vue;
    //实现一个混入
    Vue.mixin({
          beforeCreate(){
               //获取KVueRouter实例并挂载到Vue.prototype
               if(this.$options.router){
                  // 在跟组件beforeCreate时执行一次且只会执行一次
                   Vue.prototype.$router = this.$options.router;
               }
         }
    })
}

这时候有一个疑问,为什么要写把router挂载的配置写在混入里,而不是直接写在install方法呢?
这是因为和实例的关系很大,我们先执行了Vue.use的方法,其实是执行了插件的install的方法,但是这时候,实例还不存在呢,那怎么办呢,我们只好退而求其次,把代码延后执行,延后到当beforeCreate的时候,才执行。
接下来我们需要注册两个全局组件 router-view 和 router-link

...
Vue.component('router-link',{})
Vue.component('router-view',{})

接下来来完成核心任务

class KVueRouter{
         //解析routes
         //监听事件
         //声明组件
    constructor(options){
          this.$options = options;
          this.routeMap = {};  // {'/index': {component: Index,...}}
          //当前url需要响应式的
          this.app = new Vue({
               data : { current : '/'}
          })
    }

    //初始化
   init(){
      //监听事件
       this.bindEvents();
      //解析routes
       this.createRouteMap();
       //声明组件
       this.initComponent();
   }
   bindEvents(){
        window.addEventListener('hashchange', this.onHashchange.bind(this))
   }
   onHashchange(){
       this.app.current = window.location.hash.slice(1) || '/'
   }
   createRouteMap(){
      //遍历用户配置路由数组
      this.$options.routes.forEach(route => {
            this.routeMap[route.path] = route;
      })
   }
  initComponent(){
      //转换目标: <a href = '/'>xx</a>
      // <router-link to = '/'>
     Vue.component('router-link', {
           props: {
                 to: String
           },
           render(h){
                 // h(tag, data,children)
                return h( 'a' , {
                      attrs:{href : '#' + this.to}
                }, [ this.$slots.default ]) // 这里还可以放其他的具名插槽 甚至作用域插槽 
               // 也可以使用jsx
           }
     })
  }
}

最后执行一下install方法,把KVueRouter导出一下

export default KVueRouter

这时候可以引入写好的文件,来调试一下了

//router.js
import Vue from 'vue'
// import Router from 'vue-router'
import Router from './kvue-router'
import Home from './views/Home.vue'

// 1.应用插件:做了什么?
Vue.use(Router) // use执行了插件install()

// 2.创建Router实例
export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  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')
    }
  ]
})

在main.js中挂载router

//main.js
import router from './router'
new Vue({
  router, // 配置router实例
  render: h => h(App),
}).$mount("#app");

//app.vue
<div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view></router-view>

这时候我们可以实现路由跳转了,但是并不能真正的加载对应的组件,就差渲染内容组件了
router-view 这个组件负责实现渲染组件工作,我们拿出我们要渲染的component

   // 获取path对应的Component将它渲染出来
    Vue.component("router-view", {
        render: (h) => {
            const Component = this.routeMap[this.app.current].component;
            return h(Component)
        }
    })

另外vue也像我们暴露了一个可以定义一个响应式数据的方法

const initial = window.location.hash.slice(1) || ‘/’
Vue.util.defineReactive(this,’current’,initial)

接下来,我们来总结一下
关于实现路由,我们要做的第一件事是实现一个插件install(),这个install方法,会把vue的构造函数传进来,拿到vue的构造函数之后,我们就可以做很多事情,接下来我们另一个要实现的kvuerouter里,监听事件,事件发生变化以后,我们要做的事情把this.app的current的改成新的hash,但是为什么修改完新的hash之后,会在router-view把对应的组件渲染出来呢,原因是只要render函数里面用到了某个响应式的数据,这个数据发生变化了,我们的组件就会重新执行render,这就是典型的依赖收集,意思就是说render函数里只要用的data里的东西,就会产生依赖,编辑器在执行render函数的时候,会先执行依赖收集的过程,先把依赖全部找到,vue里的current只要改变,和它相关的组件都会发生改变,也就会导致router-view的component的重新执行,界面就渲染了。
该示例没有解决嵌套路由的问题,我们可以参考一下官方的文档。

vuex数据管理

vuex是一个专门为vue.js应用开发的状态管理模式,集中式存储管理应用所有组件的状态。它是一个单项数据流的设计思想,它为了让数据可控,数据可追踪,设计出这样一个单项数据流, 我们在实践的时候,也要避免同时被父子组件操作的情况,维持这样一个单向的关系,怎么去维系呢,我们把一些通用的全局的数据,把他抽象到一个store里去保管,只能用不能改,如果想改数据,只能commit一个mutaions,或者dispath一个actions,让actions去commit一个mutaions,而且在vuex 里面必须实现一个数据的响应式,实现的方式也是利用了vuex的构造初始化的时候做了响应式。


vuex

核心概念

state状态,数据

状态和状态的变更

state保存数据状态,mutations用于修改状态,store.js

export default new Vuex.Store({
      state: {count : 0},
      mutations:{
            increment(state){
                 state.count += 1;
            }
      }
})

vuex的任务分析

//kvuex.js
let Vue;
class Store {
  // 持有state,并使其响应化
  // 实现commit和dispatch两个方法
  constructor(options) {
    //   数据响应式
    // this.state是Vue实例,访问this.state.count
    this.state = new Vue({ data: options.state });

    this.mutations = options.mutations;
    this.actions = options.actions;
    this.getters = options.getters;

    // bind this
    this.commit = this.commit.bind(this);
    this.dispatch = this.dispatch.bind(this);
    this.getters = this.getters.bind(this);
  }

  //   实现commit:可以修改state中的数据
  commit(type, arg) {
    this.mutations[type](this.state, arg);
  }

  dispatch(type, arg) {
    return this.actions[type](this, arg);
  }

 getters(getters){
     //遍历getters选项,为this.getters定义property
     //属性名就是选项中的key ,只需定义get函数保证只读性
    Object.keys(getters).forEach(key =>{
        Object.defineProperty(this.getters, key, {
           get : () =>{
               return getters[key](this.state)
           }
       })
   })
 }
// 声明插件install
// _Vue是形参:Vue构造函数,use会把它传进来
function install(_Vue) {
  Vue = _Vue;

  Vue.mixin({
    beforeCreate() {
      // this指的是组件实例
      if (this.$options.store) {
        Vue.prototype.$store = this.$options.store;
      }
    },
  });
}

// 导出Vuex
export default { Store, install };

vuex和vuerouter的实现思想大致相同,以上在不考虑代码健壮性的前提下,来实现核心思想的。

上一篇下一篇

猜你喜欢

热点阅读