动态修改对象的属性时,vue2视图template不会跟新渲染的数据,来一探究竟
测试案例:
config属性被我动态添加name:yma16
结论:
如果template动态添加则说明对象的属性添加删除具有响应式
{{ msg }}
config渲染:{{ config }}
添加config属性添加保存
效果
如下图所示。
结论:
vue2动态添加对象属性不具有响应式
改动点:
submit使用$set添加替换属性
{{ msg }}
config渲染:{{ config }}
添加config属性添加保存
效果
结论:
vue2动态添加对象属性使用$set可以让对象具有响应式渲染
给对象添加属性
var myObject = {};
Object.defineProperty( myObject, "a", {
value: 2,
writable: false, // 不可写!
configurable: true,
enumerable: true
} );
myObject.a = 3;
myObject.a; // 2
object获取和配置属性时
定义新属性b设置为2的两倍达到监听变量啊变化的结果,只有a变量地址改变时属性b也会改变
var myObject = {
// 给 a 定义一个 getter
get a() {
return 2;
}
};
Object.defineProperty(
myObject,
"b",
{
get: function(){ return this.a * 2 },
enumerable: true
}
);
myObject.a; // 2
myObject.b; // 4
vue的原型链挂了set
set里面定义属性且可枚举
使用了枚举enumerable属性和注册configurable属性
/*** Define a reactive property on an Object.*/
export function defineReactive(obj: object,key: string,val?: any,customSetter?: Function | null,shallow?: boolean,mock?: boolean
) {const dep = new Dep()const property = Object.getOwnPropertyDescriptor(obj, key)if (property && property.configurable === false) {return}// cater for pre-defined getter/settersconst getter = property && property.getconst setter = property && property.setif ((!getter || setter) &&(val === NO_INIITIAL_VALUE || arguments.length === 2)) {val = obj[key]}let childOb = !shallow && observe(val, false, mock)Object.defineProperty(obj, key, {enumerable: true,configurable: true,get: function reactiveGetter() {const value = getter ? getter.call(obj) : valif (Dep.target) {if (__DEV__) {dep.depend({target: obj,type: TrackOpTypes.GET,key})} else {dep.depend()}if (childOb) {childOb.dep.depend()if (isArray(value)) {dependArray(value)}}}return isRef(value) && !shallow ? value.value : value},set: function reactiveSetter(newVal) {const value = getter ? getter.call(obj) : valif (!hasChanged(value, newVal)) {return}if (__DEV__ && customSetter) {customSetter()}if (setter) {setter.call(obj, newVal)} else if (getter) {// #7981: for accessor properties without setterreturn} else if (!shallow && isRef(value) && !isRef(newVal)) {value.value = newValreturn} else {val = newVal}childOb = !shallow && observe(newVal, false, mock)if (__DEV__) {dep.notify({type: TriggerOpTypes.SET,target: obj,key,newValue: newVal,oldValue: value})} else {dep.notify()}}})return dep
}
重点
当一个数据为响应式时,vue会给该数据添加一个__ob__属性,因此可以通过判断target对象是否存在__ob__属性来判断target是否是响应式数据
dep.notify({type: TriggerOpTypes.SET,target: obj,key,newValue: newVal,oldValue: value})} else {dep.notify()}
当target对象是响应式数据时,我们将target的属性key也设置为响应式并手动触发通知其属性值的更新
defineReactive(ob.value, key, val)
ob.dep.notify()
承接$set的notify
notify(info?: DebuggerEventExtraInfo) {// stabilize the subscriber list firstconst subs = this.subs.filter(s => s) as DepTarget[]if (__DEV__ && !config.async) {// subs aren't sorted in scheduler if not running async// we need to sort them now to make sure they fire in correct// ordersubs.sort((a, b) => a.id - b.id)}for (let i = 0, l = subs.length; i < l; i++) {const sub = subs[i]if (__DEV__ && info) {sub.onTrigger &&sub.onTrigger({effect: subs[i],...info})}sub.update()}}
update 继承 DebuggerOptions
/*** @internal*/
export interface DepTarget extends DebuggerOptions {id: numberaddDep(dep: Dep): voidupdate(): void
}
onTrack和onTrigger
watch update
_update更新渲染节点
function _update(oldVnode, vnode) {const isCreate = oldVnode === emptyNodeconst isDestroy = vnode === emptyNodeconst oldDirs = normalizeDirectives(oldVnode.data.directives,oldVnode.context)const newDirs = normalizeDirectives(vnode.data.directives, vnode.context)const dirsWithInsert: any[] = []const dirsWithPostpatch: any[] = []let key, oldDir, dirfor (key in newDirs) {oldDir = oldDirs[key]dir = newDirs[key]if (!oldDir) {// new directive, bindcallHook(dir, 'bind', vnode, oldVnode)if (dir.def && dir.def.inserted) {dirsWithInsert.push(dir)}} else {// existing directive, updatedir.oldValue = oldDir.valuedir.oldArg = oldDir.arg// update 更新 vnodecallHook(dir, 'update', vnode, oldVnode)if (dir.def && dir.def.componentUpdated) {dirsWithPostpatch.push(dir)}}}if (dirsWithInsert.length) {const callInsert = () => {for (let i = 0; i < dirsWithInsert.length; i++) {callHook(dirsWithInsert[i], 'inserted', vnode, oldVnode)}}if (isCreate) {mergeVNodeHook(vnode, 'insert', callInsert)} else {callInsert()}}if (dirsWithPostpatch.length) {mergeVNodeHook(vnode, 'postpatch', () => {for (let i = 0; i < dirsWithPostpatch.length; i++) {callHook(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode)}})}if (!isCreate) {for (key in oldDirs) {if (!newDirs[key]) {// no longer present, unbindcallHook(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy)}}}
}
update的调用地方太多
找了一个监听的update
/*** @internal since we are not exposing this in Vue 2, it's used only for* internal testing.*/
export function effect(fn: () => any, scheduler?: (cb: any) => void) {const watcher = new Watcher(currentInstance, fn, noop, {sync: true})if (scheduler) {watcher.update = () => {scheduler(() => watcher.run())}}
}
感谢阅读,如有不足或者错误欢迎指正!
上一篇:3/19考后总结