Vuex principle analysis

The design concept of VUEX is centralized state management and predictable state changes

The plug-in

To use vuex, you first need vue. use(vuex), that is, to install the vuex plug-in.

  • How to implement a plug-in?

    Simply expose the install() method.

  • What are the main functions of this plug-in?

    Mount $store, since vue.use () is executed first, new Vue() instance has not been created yet, so mixins are used to mix the lazy mount.

function install(_Vue) {
Vue. Use () can receive Vue instances
  Vue = _Vue

  Vue.mixin({
    beforeCreate() {
      // Because of mixin, the hook is called when each component instance is created
      // The root instance has this option
      if (this.$options.store) {
        // Mount the vue prototype so that other components can call $store
        Vue.prototype.$store = this.$options.store
      }
    }
  })
}
Copy the code

State Reactive state management

  • How many ways to implement data responsiveness?
  1. Object.defineProperty()
  2. Vue.util.defineReactive()
  3. Vue.observable()
  4. new Vue({data(){}})

This is the fourth one

new Vue({
  data: {
    $$state = $$state = $$state = $$state
    $$state:options.state
  }
})
Copy the code

The state assembly

// Read-only state, which can obtain data
get state() {
    return this._vm._data.$$state
}

set state(v) {
    console.error('please use replaceState to reset state');
}
Copy the code

implementationcommit() dispatch()methods

Pass in the method name and parameters, go mutations and actions to match

// Save mutaions and Actions options
    this._mutations = options.mutations;
    this._actions = options.actions;
    
 commit(type, payload) {
    const entry = this._mutations[type]
    if(! entry) {console.error('unkown mutation type');
    }
    / / to the state
    entry(this.state, payload)
  }
  
  dispatch(type, payload) {
    const entry = this._actions[type]
    if(! entry) {console.error('unkown action type');
    }
    //{commit} context object is the current instance this
    entry(this, payload)
  }
Copy the code

Bind this to

this.commit = this.commit.bind(this)
this.dispatch = this.dispatch.bind(this)
Copy the code

Implement getters

Vuex’s getters realize real-time data monitoring through computed property of VUE

this._wrappedGetters = options.getters
// The outside world accesses getters via $store.getters. XXX
this.getters = {};
Define computed data
const computed = {};
// Get the current this
const store =this;
Object.keys(this._wrappedGetters).forEach(key= > {
    // This refers to change, use the above defined this
    const fn = store._wrappedGetters[key]
    // Computed is a parameterless function, and getters is a parameterless function, so higher-order functions are included
    computed[key] = function(){
        return fn(store.state)
    }
})
// Specify getters as read-only
Object.defineProperty(store.getters,key,{
   get: () = > store._vm[key]
})

this._vm = new Vue({
  data: {
    $$state: options.state  // GoEast is not a proxy and can not be accessed directly
  },
  computed
})
Copy the code

Vue – principle of the router

In a single-page program, the URL changes and the corresponding view content is not refreshed

Implement the plug-in to mount $router

// Argument 1 is passed in when vue. use is called
VueRouter.install = function(_Vue) {
  Vue = _Vue;

  // 1. Mount $router
  //Vue. Use () takes precedence. New Vue() has not been executed yet, so $router mount is delayed
  // Global mixin purpose: delay the following logic until the router is created and appended to the option
  Vue.mixin({
    //new Vue() the first hook function to execute
    beforeCreate() {
      // Because of mixin, the hook is called when each component instance is created
      // The root instance has this option
      if (this.$options.router) {
        // Mount the vue prototype so that other components can call $router
        Vue.prototype.$router = this.$options.router; }}});Copy the code

The PAGE does not refresh when the URL changes

hash

The #/user/1 hash mode carries a ‘#’, does not refresh the browser, and listens for URL changes by listening for hashchange

this.current = window.localtion.hash.slice(1) | |"/";
// Listen for hash changes
window.addEventListener("hashchange".() = > {
  console.log(this.current);
  this.current = window.location.hash.slice(1);
});
Copy the code

history

/user/1 history Normal URL format By listening for POPState, listen for URL changes

Data responsive, URL changing content is re-rendered

Vue.util. DefineReactive Defines reactive data

 // Define the response-matched array, which stores the nested components matched by the current route
 Vue.util.defineReactive(this.'matched'[]);// Routing table routes
 this.match();
 
 match(routes){
      / / the routing table
      let routes = routes || this.$options.routes;
      // Recursively traverses the routing table
      for(route of routes){
          if(route.path === '/' && this.current === '/') {// Add routing information
              this.matched.push(route);
              return;
          }
          // /about/info
          if(route.path ! = ='/' && this.current.indexOf(route.path) ! = -1) {/ / include/about
              this.matched.push(route);
              // There are nested subroutes, recursively calling match
              if(route.children.length > 0) {this.match(route.children)
              }
              return; }}}}Copy the code

Implement two global components router-link and router-view

To realize the router – the link

user

Vue.component("router-link", {
    props: {
      to: {
        type: String.required: true,}},render(h) {
      // <a href="to">xxx</a>
      // return <a href={'#'+this.to}>{this.$slots.default}</a>
      return h(
        "a",
        {
          attrs: {
            href: "#" + this.to,
          },
        },
        this.$slots.default ); }});Copy the code

Implement the router – the view

Determine the depth of router-view to realize route nesting. Render nested components via matched array

Vue.component("router-view", {
    render(h) {
      // Mark the current router-view depth
      this.$vnode.data.routerView = true;  
      // Depth identifier
      let depth = 0;
      // Look up the routerView
      let parent = this.$parent;
      while(parent){
          const vnodeData = parent.$vnode && parent.$vnode.data;
          if(vnodeData.routerView){
              // Parent is a router-view
              depth++;
          }
          parent = parent.$parent;
      }
      // Get the component of the current route
      let component = null;
      // Matched is responsive so it dynamically renders the route
      const route = this.$router.matched[depth];
      if (route) {
        component = route.component
      }
      console.log(this.$router.matched, component);
     
      returnh(component); }}); };Copy the code