vm.$set()

The use of vm.$set() can be found here.

What problem does vm.$set() solve? To avoid the abuse of

In Vue. Js, only properties that already exist in data are considered responsive by Observe. If you add a property, it is not considered responsive.

<! 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">
        {{user.name}}
        {{user.age}}
        <button @click="addUserAgeField"</button> </div> <script> const app = new Vue({el:"#app",
            data: {
                user: {
                    name: 'test'}},mounted () {
            },
            methods: {
                addUserAgeField() {// this.user.age = 20 so this does not work and will not be used by the Observer this.$set(this.user, 'age'}}}) </script> </body> </ HTML >Copy the code

The principle of

Vm. $set() is injected into the Vue prototype when new Vue() is created.

Source location: vue/SRC/core/instance/index. Js

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '.. /util/index'

function Vue (options) {
  if(process.env.NODE_ENV ! = ='production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')} this._init(options)} initMixin(Vue) // Bind the proxy properties to the prototype$props.$data// Bind three instance methods to the Vue prototype: VM.$watch, the vm.$set, the vm.$deleteStateMixin (Vue) // Bind the Vue prototype to the event-related instance method: VM.$on, vm.$once ,vm.$off , vm.$emitEventsMixin (Vue) // Bind the Vue prototype to a life-cycle dependent instance method: VM.$forceUpdate, vm.destroy, and the private method _update lifecycleMixin(Vue) // bind the Vue prototype to the life-cycle related instance method: VM.$nextTickAnd the private _render method, and a bunch of utility methods called renderMixin(Vue)export default Vue

Copy the code

stateMixin()

. Vue.prototype.$set = set
 Vue.prototype.$delete = del
 ...
Copy the code

set()

Source location: vue/SRC/core/observer/index. Js

export function set(target: Array < any > | Object, key: any, val: any) : any {/ / ifsetThe first argument to the function is undefined or null or a primitive type value, which prints a warning message in non-production environmentsif(process.env.NODE_ENV ! = ='production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)}if(array.isarray (target) && isValidArrayIndex(key)) {// Similar$vm.set(vm.$dataTarget. Length = math.max (target. Length, key); Target.splice (key, 1, val)returnVal} // target is the object. Key is on target or target.prototype. On / / at the same time must not be in the Object. The prototype can be modified directly / /, interested can look at issue: https://github.com/vuejs/vue/issues/6845if (key intarget && ! (keyin Object.prototype)) {
    target[key] = val
    returnConst ob = (target:) const ob = (target:); Any).__ob__ // Vue instance objects have _isVue attributes, that is, it is not allowed to add attributes to Vue instance objects // nor Vue. Set /$setFunction is the root data object (VM).$data) Add attributesif(target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV ! = ='production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    )
    returnVal} // Target itself is not reactive data; it is directly assignedif(! ob) { target[key] = valreturnDefineReactive (ob.value, key, val) ob.dep.notify() defineReactive(ob.value, key, val)return val
}

Copy the code

Tool function

// Determine if a given variable is undefined. If the value is null, it is also considered undefinedexport function isUndef (v: any): boolean %checks {
  returnV = = = undefined | | v = = = null} / / original type value to determine whether a given variableexport function isPrimitive (value: any): boolean %checks {
  return (
    typeof value === 'string' ||
    typeof value === 'number' ||
    // $flow-disable-line
    typeof value === 'symbol' ||
    typeof value === 'boolean'} // Determine if the value of the given variable is a valid array indexexport function isValidArrayIndex (val: any): boolean {
  const n = parseFloat(String(val))
  return n >= 0 && Math.floor(n) === n && isFinite(val)
}
Copy the code

About (OB && OB.vmCount).

export functionobserve (value: any, asRootData: ? Boolean) : the Observer | void {/ / ellipsis...if(asRootData &&ob) {// If the vue has been observed and is the root data object, vmCount will be ++ ob.vmcount ++}return ob
}
Copy the code
During Vue initialization there are
export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props)
  if (opts.methods) initMethods(vm, opts.methods)
  if(opts.data) {//opts.data is the object property initData(vm)}else {
    observe(vm._data = {}, true /* asRootData */)
  }
  if (opts.computed) initComputed(vm, opts.computed)
  if(opts.watch && opts.watch ! == nativeWatch) { initWatch(vm, opts.watch) } }Copy the code
initData(vm)
function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'? GetData (data, vm) : data | | {} / / ellipsis... // observe data observe(data,true /* asRootData */)
}
Copy the code