(一)需要

Vue 在非响应式的时候会有新增属性但并没有新增属性的状况。解决方案是$set 从新设置下。

(二)介绍

1、Demo

<script>  const app = new Vue({    el: "#app",    data: {      user: {        name: "test",      },    },    mounted() {},    methods: {      addAge() {        // this.user.age = 20 这样是不起作用, 不会被监听到        this.$set(this.user, "age", 20); // 应该应用      },    },  });</script>

2、原理

vm.$set() 在new Vue() 时就被注入到Vue 原型上。

(1)入口

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)// 给Vue原型绑定三个实例办法: vm.$watch,vm.$set,vm.$deletestateMixin(Vue)eventsMixin(Vue)lifecycleMixin(Vue)renderMixin(Vue)export default Vue

(2) stateMixin 文件

src/core/instance/state.js

export function stateMixin (Vue: Class<Component>) {  // flow somehow has problems with directly declared definition object  // when using Object.defineProperty, so we have to procedurally build up  // the object here.  const dataDef = {}  dataDef.get = function () { return this._data }  const propsDef = {}  propsDef.get = function () { return this._props }  if (process.env.NODE_ENV !== 'production') {    dataDef.set = function () {      warn(        'Avoid replacing instance root $data. ' +        'Use nested data properties instead.',        this      )    }    propsDef.set = function () {      warn(`$props is readonly.`, this)    }  }  Object.defineProperty(Vue.prototype, '$data', dataDef)  Object.defineProperty(Vue.prototype, '$props', propsDef)  Vue.prototype.$set = set //在这里导入set办法到$set 下面  Vue.prototype.$delete = del  Vue.prototype.$watch = function (    expOrFn: string | Function,    cb: any,    options?: Object  ): Function {    const vm: Component = this    if (isPlainObject(cb)) {      return createWatcher(vm, expOrFn, cb, options)    }    options = options || {}    options.user = true    const watcher = new Watcher(vm, expOrFn, cb, options)    if (options.immediate) {      const info = `callback for immediate watcher "${watcher.expression}"`      pushTarget()      invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)      popTarget()    }    return function unwatchFn () {      watcher.teardown()    }  }}

导入的入口

import {  set,  del,  observe,  defineReactive,  toggleObserving} from '../observer/index'

(3) set 办法

src/core/observer/index.js

/** * Set a property on an object. Adds the new property and * triggers change notification if the property doesn't * already exist. */export function set (target: Array<any> | Object, key: any, val: any): any {  if (process.env.NODE_ENV !== 'production' &&    (isUndef(target) || isPrimitive(target))  ) {    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)  }    //target 为数组:调用 splice 办法  if (Array.isArray(target) && isValidArrayIndex(key)) {    target.length = Math.max(target.length, key)    target.splice(key, 1, val)    return val  }    //target 为对象,且 key 不是原型上的属性解决:间接批改  if (key in target && !(key in Object.prototype)) {    target[key] = val    return val  }    //target 不能是 Vue 实例,或者 Vue 实例的根数据对象,否则报错  const ob = (target: any).__ob__  if (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.'    )    return val  }    //target 是非响应式数据时,咱们就依照一般对象增加属性的形式来解决  if (!ob) {    target[key] = val    return val  }//target 为响应数据,且**key 为新增属性**,咱们 key 设置为响应式,并手动触发其属性值的更新  defineReactive(ob.value, key, val)  ob.dep.notify()  return val}

写在最初的话

学习路上,经常会懈怠。

《有想学技术须要监督的同学嘛~》
https://mp.weixin.qq.com/s/Fy...

如果有须要的搭档,能够加我微信:learningisconnecting
或者能够关注我的公众号:国星聊成长(我会分享成长的办法)