preface

  • Data hijacking: Interventions that occur when accessing or setting members in a VUE instance
  • At the heart of vue’s responsive philosophy is the hijacking of data and reflecting updates to the corresponding DOM view
  • The response principle of VUe2 relies on Object.defineProperty, and the response principle of VUe3 relies on Proxy

vue-router

  • Hash mode:
  • The history mode:
  1. IE10 just started supporting pushState (changing urls, but not as a server request, and saved to history) and replaceState.
  2. Server support is required, such as www.baidu.com/foo.html if the page does not exist, the default 404 will be returned. So the refresh requires a full redirect to index.html. The client modifies the URL link

Server support node: enable app.use(history())

Vue.use () calls the function, or the install method of the object

let _Vue = null
export default class VueRouter {
  // static requires only the class name to access the method, without new
  static install (Vue) {
    // Check to see if it has been installed
    if (VueRouter.install.installed) return
    VueRouter.install.installed = true
    // Mount vue to the global object
    _Vue = Vue
    // Mount the router to the vue object
    _Vue.mixin({
      beforeCreate () {
        const router = this.$options.router
        if (router) _Vue.prototype.$router = router
      }
    })
  }

  constructor (options) {
    this.options = options
    this.routeMap = {} // View the simplest route mapping
    this.data = _Vue.observable({
      // Create a responsive object. Default is the current routing address
      current: '/'
    })
    this.init()
  }

  init () {
    this.createRouteMap()
    this.initComponents(_Vue)
    this.initEvent()
  }

  createRouteMap () {
    this.options.routes.forEach(route= > {
      this.routeMap[route.path] = route.component
    })
  }

  initComponents (Vue) {
    // Initialize router-link router-view
    Vue.component('router-link', {
      props: {
        to: String
      },
      render (h) {
        return h('a', {
          attrs: {
            href: this.to
          },
          on: {
            click: this.clickHandler
          }
        }, [this.$slots.default])
      },
      methods: {
        clickHandler (e) {
          // history.pushState({}, '', '#' + this.to)
          window.location.hash = The '#' + this.to
          // Change the component in the current routing address to the current view to get the current custom routing information
          this.$router.data.current = this.to
          e.preventDefault()
        }
      }
    })

    const self = this
    Vue.component('router-view', {
      render (h) {
        const component = self.routeMap[self.data.current]
        return h(component)
      }
    })
  }

  // Pressing the browser back key will handle component changes
  initEvent () {
    window.addEventListener('hashchange'.e= > {
      this.data.current = e.newURL.split(The '#') [1]})}}Copy the code

Object. DefineProperty contrast Proxy

The following is the realization simulation of 2:

// Simulate the data option in vue
let data = {
  msg: 'hello'
}

// Simulate the vUE instance
let vm = {}

// Data hijacking: Perform some intervention when accessing or setting vm members
object.defineProperty(vm, 'msg', {
  / / can be enumerated
  enumerable: true.// configurable (can be deleted using delete and redefined via defineProperty)
  configurable:true.// execute when fetch is worthwhile
  get() {
    console.log('get', data.msg)
    return data.msg
  },
  // Set the time to execute
  set(newVal) {
    if (newVal === data.msg) return 
    data.msg = newVal
    document.querySelector('#app').textContent = data.msg
  }
})


let vm = new Proxy(data, {
  // Execute the proxy function
  // Is executed when accessing vm members
  get(target, key) {
    console.log('get, key', key, target[key])
    return target[key]
  },
  // execute when set up in vm
  set(target, key, newValue) {
    console.log(target, key, newValue)
    if (target[key] === newValue) return 
    target[key] = newValue
    document.querySelector('#app').textContent = newValue
  }
})

/ / test
vm.msg = 'HELLO WORLD'
console.log(vm.msg)
Copy the code

We can see that using Proxy is much simpler, because Proxy uses the entire object, and all properties of the object trigger get and set when accessing Settings. Whereas we’re using defineProperty we need a loop. In addition, proxy is optimized by the browser, the performance is much better.

Publish and subscribe model

I’m sure many people have used $ON and $emit, but I don’t know exactly how. And the use of the need to monitor, is not particularly performance. The vUE event mechanism is publish-subscribe. So let’s start by implementing a basic version of $ON and $emit

class EventEmitter {
  // Define an object to hold an array of events
  constructor () {
    this.subs = Object.create(null) // No prototype chain, more performance saving
  }

  // Execute the event
  $emit(eventType) {
    if (!this.subs[eventType]) return
    this.subs[eventType].forEach(handler= > {
      handler()
    })
  }

  // Trigger the event
  $on(eventType, handler) {
    this.subs[eventType] = this.subs[eventType] || []
    this.subs[eventType].push(handler)
  }
}

let em = new EventEmitter()
em.$on('click'.() = > {
    console.log('click1')
})

em.$emit('click')
Copy the code

As can be seen from the above code, publish and subscribe, in fact, is to put a series of function FN into an array first, wait until the need to execute, then through the array traversal, execute one by one. So the very lossy performance mentioned above does not exist

Observer model

  • Observer (Subscriber) — Watch Update: What exactly to do when an event occurs

  • Target (publisher) –Dep

    1. Subs array: Stores all observers
    2. AddSub (): Adds an observer
    3. Notify (): When an event occurs, the update() method of all observers is called

Many people tend to confuse the publish-subscribe model with the observer model, but there is a difference

  • In the observer mode, the target is interdependent with itself
  • In the publish-subscribe model, having an event center for isolation reduces dependencies and is relatively flexible

// Publisher - target
class Dep {
  constructor() {
    // Record all subscribers
    this.subs = []
  }
  // Add subscribers
  addSub(sub) {
    if (sub && sub.update) {
      this.subs.push(sub)
    }
  }
  // Issue a notification
  notify() {
    this.subs.forEach(sub= > {
      sub.update()
    })
  }
}

// Subscribe - observer
class Watcher {
  update() {
    console.log('update')}}/ / test
const dep = new Dep()
const watcher = new Watcher()

dep.addSub(watcher)
dep.notify()
Copy the code

Vue response principle analysis