vm.$delete()

Vm.$delete

Why is vue.delete () needed?

Prior to ES6, JS did not provide a way to detect that a property was deleted, so if we deleted a property by delete, Vue would not detect it and therefore would not trigger data responsiveness.

See the demo below.

<! DOCTYPE html> <html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Vue Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  </head>
  <body>
    <div id="app"< span style = "max-width: 100%; clear: both; min-height: 1em"addUserAgeField"</button> </div> <script> const app = new Vue({el:"#app",
        data: {
          user: {
            name: "test",
            age: 10
          }
        },
        mounted() {},
        methods: {
          addUserAgeField() { // delete this.user.age; // This will not work and will not trigger data responsive updates to this.$delete(this.user, 'age'}}}); </script> </body> </html>Copy the code

Source code analysis internal implementation

Source location vue/SRC/core/instance/state js stateMixin method

export function stateMixin (Vue: Class<Component>) {
    ...
    
    Vue.prototype.$set = set
    Vue.prototype.$delete = del
    
    ...

}
Copy the code

Then look at del function position, vue/SRC/core/observer/index. Js.

/**
 * Delete a property and trigger change ifNecessary. * target: the target of the attribute to be deleted, which can be an object/array * key: delete attribute */export functionDel (target: Array < any > | Object, key: any) {/ / non-production environments, is not allowed to delete a primitive data types, or undefined, nullif(process.env.NODE_ENV ! = ='production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}')} // If target is an array and key is a valid index, the value is removed by the array's splCIE method, and the data response can also be triggered (the array interceptor intercepts the change to the element, notifying the dependency to update the data).if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.splice(key, 1)
    return} // get ob const ob = (target: any).__ob__ // target._isVue: cannot delete attributes on Vue instance object // (ob && ob.vmcount): cannot delete attributes on root data object, cannot trigger responseif(target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV ! = ='production' && warn(
      'Avoid deleting properties on a Vue instance or its root $data ' +
      '- just set it to null.'
    )
    return} // If the attribute is not on the object at all, nothing is doneif(! hasOwn(target, key)) {returnDelete target[key] // If ob does not exist, target itself is not responsive.if(! ob) {returnOb.dep.notify ()} // Ob.dep.notify ()}Copy the code

Tool function

// Check whether v is undefinedexport function isUndef (v: any): boolean %checks {
  returnV = = = undefined | | v = = = null} / / to determine whether v primitive data types (basic data types)export function isPrimitive (value: any): boolean %checks {
  return (
    typeof value === 'string' ||
    typeof value === 'number' ||
    // $flow-disable-line
    typeof value === 'symbol' ||
    typeof value === 'boolean'} / / whether Object has attributes const hasOwnProperty = Object. The prototype. The hasOwnPropertyexport function hasOwn (obj: Object | Array<*>, key: string): boolean {
  return hasOwnProperty.call(obj, key)
}

Copy the code

For the __ob__ attribute, we’ll see something like this in many source code places.

const ob = (target: any).__ob__
Copy the code

Remember that this private property occurs whenever data has been observed. it occurs in the constructor of the Observer class

exportclass Observer { constructor (value: Any) {this.value = value // Dependencies exist on observedep, __ob__.dep.notify() this.dep = new dep () this.vmcount = 0 // define __ob__ def(value,'__ob__', this)
    if (Array.isArray(value)) {
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      this.observeArray(value)
    } else {
      this.walk(value)
    }
   }
    ...

}
Copy the code

Vue.use()

Everyone knows that this method is used to install plug-ins and is a global API. See the official website for specific usage.

Through vue.use () source code +Vuex part of the source code analysis plug-in installation process

When is vue.use () tied to the Vue prototype

Vue/SRC /core/index.js

initGlobalAPI()

Vue/SRC /core/global-api/index.js

export functioninitGlobalAPI (Vue: GlobalAPI) { ... // Initialize use() initUse(Vue)... }Copy the code

initUse()

Vue/SRC /core/global-api/use.js

export functionInitUse (Vue: GlobalAPI) {// Vue is the constructor function. // vue-dev/ SRC /core/global-api/index.js initGlobalAPI() where initGlobalAPI() => initializes some global apis  // Vue.use(): The plug-in that installs vue.js // must provide the install method if the plug-in is an object. // If the plug-in is a function, it will be called as the install method. // When the install method is called, Vue is passed as an argument to vue.use =function(plugin: Function | Object) {/ / installedPlugins storage after install plugin const installedPlugins = (enclosing _installedPlugins | | (this._installedPlugins = []))if(installedplugins.indexof (plugin) > -1) {// The same plugin will only be installed oncereturnVue. Use (MyPlugin, {someOption:true}) const args = toArray(arguments, 1) // Stores the Vue constructor to args for the plug-in's install method to use args. Unshift (this) // Throw the this(Vue) argument back to the install method // So we say that the first argument to the install method is the Vue constructor and the second argument is an optional option object:if (typeof plugin.install === 'function'Plugin.install. apply(plugin, args)}else if (typeof plugin === 'function') {// plugin is a function plugin.apply(null, args)} // After install the plugin will be stored to avoid repeated installation installedplugins.push (plugin)return this
  }
}

Copy the code

Vuex source

We all know that developing a vue.js plug-in should expose an install method. The first argument to this method is the Vue constructor, and the second argument is an optional option object:

So let’s first look at how Vuex install is implemented

Vuex-dev /src/store.js

let Vue // bindOn install // install: install vuex to vue, vue.use (vuex) is also executed in the install method // about vue.use () source code. Vue-dev/SRC /core/global-api/use.jsexport function install (_Vue) {
  if (Vue && _Vue === Vue) {
    if(process.env.NODE_ENV ! = ='production') {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.')}return} // The first time the plugin is installed, the local Vue will be cached to the global window.vue. Use () Vue = _Vue applyMixin(Vue)}Copy the code
applyMixin()

Vuex/SRC /mixin.js

export default function (Vue) {
  const version = Number(Vue.version.split('. ') [0])ifVue. Mixin ({beforeCreate: vuexInit})} (version >= 2) {// If 2.x.x or later, inject a global mixin and execute vueInit.else {
    // override init and inject vuex init procedure
    // for1. X backwards compatibility. // Rewrite _init on Vue Injection vueinit method _init see vue dev/SRC/core/instance/init. Js const _init = vue. Prototype. _init. / / as a cache variable vue prototype. _init =function(options = {}) { options.init = options.init ? [vuexInit].concat(options.init) : Call (this, options)}} /** * Vuex init hook, Injected into each instances init hooks list. */ / inject store into Vue constructorfunction vuexInit/** * new Vue({*... , * store, * route * }) */ // options: Is the new Vue (options) / / source Vue - dev/SRC/core/instance/init. Js initMixin method const options = this.$options// store injection // store is an instance where we use new vuex.store (options) // to inject store into the Vue constructor$storeProperty, so we use this in Vue components.$storeTo use theif(options.store) {// options.store if true, root this.$store = typeof options.store === 'function'
        ? options.store()
        : options.store
    } else if (options.parent && options.parent.$store) {// The child component is fetched directly from the parent component$storeThis ensures that all components share the same global store this.$store = options.parent.$store}}}Copy the code

How is the install method Vuex executed?

exportClass Store {constructor (options = {}) {// Install vuex in a browser environmentif(! Vue && typeof window ! = ='undefined'&& window.Vue) { install(window.Vue) } ... }}Copy the code