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