乐趣区

关于javascript:Day-46100-Vue-thisset原理

(一)需要

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.$delete
stateMixin(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
或者能够关注我的公众号:国星聊成长(我会分享成长的办法)

退出移动版