(一)需求
Vue 在非响应式的时候会有新增属性但并没有新增属性的情况。解决方案是$set 重新设置下。
(二)介绍
1、Demo
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) {
// 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 | 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
或者可以关注我的公众号:国星聊成长(我会分享成长的方法)