网站建设客户合同,郑州网站建设丶汉狮网络,网站优化搜索,网站原型图展示#x1f308;个人主页#xff1a;前端青山 #x1f525;系列专栏#xff1a;Vue篇 #x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:vue-data属性 目录
为什么data属性是一个函数而不是一个对象#xff1f;
一、实例和组件定义dat…
个人主页前端青山 系列专栏Vue篇 人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:vue-data属性 目录
为什么data属性是一个函数而不是一个对象
一、实例和组件定义data的区别
二、组件data定义函数与对象的区别
三、原理分析
四、结论
动态给vue的data添加一个新的属性时会发生什么怎样解决
一、直接添加属性的问题
二、原理分析
三、解决方案
Vue.set()
Object.assign()
$forceUpdate
小结
为什么data属性是一个函数而不是一个对象 一、实例和组件定义data的区别
vue实例的时候定义data属性既可以是一个对象也可以是一个函数
const app new Vue({el:#app,// 对象格式data:{foo:foo},// 函数格式data(){return {foo:foo}}
})
组件中定义data属性只能是一个函数
如果为组件data直接定义为一个对象
Vue.component(component1,{template:div组件/div,data:{foo:foo}
})
则会得到警告信息 警告说明返回的data应该是一个函数在每一个组件实例中
二、组件data定义函数与对象的区别
上面讲到组件data必须是一个函数不知道大家有没有思考过这是为什么呢
在我们定义好一个组件的时候vue最终都会通过Vue.extend()构成组件实例
这里我们模仿组件构造函数定义data属性采用对象的形式
function Component(){}
Component.prototype.data {count : 0
}
创建两个组件实例
const componentA new Component()
const componentB new Component()
修改componentA组件data属性的值componentB中的值也发生了改变
console.log(componentB.data.count) // 0
componentA.data.count 1
console.log(componentB.data.count) // 1
产生这样的原因这是两者共用了同一个内存地址componentA修改的内容同样对componentB产生了影响
如果我们采用函数的形式则不会出现这种情况函数返回的对象内存地址并不相同
function Component(){this.data this.data()
}
Component.prototype.data function (){return {count : 0}
}
修改componentA组件data属性的值componentB中的值不受影响
console.log(componentB.data.count) // 0
componentA.data.count 1
console.log(componentB.data.count) // 0
vue组件可能会有很多个实例采用函数返回一个全新data形式使每个实例对象的数据不会受到其他实例对象数据的污染
三、原理分析
首先可以看看vue初始化data的代码data的定义可以是函数也可以是对象
function initData (vm: Component) {let data vm.$options.datadata vm._data typeof data function? getData(data, vm): data || {}...
}
data既能是object也能是function那为什么还会出现上文警告呢
别急继续看下文
组件在创建的时候会进行选项的合并
自定义组件会进入mergeOptions进行选项合并
Vue.prototype._init function (options?: Object) {...// merge optionsif (options options._isComponent) {// optimize internal component instantiation// since dynamic options merging is pretty slow, and none of the// internal component options needs special treatment.initInternalComponent(vm, options)} else {vm.$options mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm)}...}
定义data会进行数据校验
这时候vm实例为undefined进入if判断若data类型不是function则出现警告提示
strats.data function (parentVal: any,childVal: any,vm?: Component
): ?Function {if (!vm) {if (childVal typeof childVal ! function) {process.env.NODE_ENV ! production warn(The data option should be a function that returns a per-instance value in component definitions.,vm);
return parentVal;}return mergeDataOrFn(parentVal, childVal);}return mergeDataOrFn(parentVal, childVal, vm);
};
四、结论 根实例对象data可以是对象也可以是函数根实例是单例不会产生数据污染情况 组件实例对象data必须为函数目的是为了防止多个组件实例对象之间共用一个data产生数据污染。采用函数的形式initData时会将其作为工厂函数都会返回全新data对象
动态给vue的data添加一个新的属性时会发生什么怎样解决 一、直接添加属性的问题
我们从一个例子开始
定义一个p标签通过v-for指令进行遍历
然后给botton标签绑定点击事件我们预期点击按钮时数据新增一个属性界面也 新增一行
p v-for(value,key) in item :keykey{{ value }}
/p
button clickaddProperty动态添加新属性/button
实例化一个vue实例定义data属性和methods方法
const app new Vue({el:#app,data:(){item:{oldProperty:旧属性}},methods:{addProperty(){this.items.newProperty 新属性 // 为items添加新属性console.log(this.items) // 输出带有newProperty的items}}
})
点击按钮发现结果不及预期数据虽然更新了console打印出了新属性但页面并没有更新
二、原理分析
为什么产生上面的情况呢
下面来分析一下
vue2是用过Object.defineProperty实现数据响应式
const obj {}
Object.defineProperty(obj, foo, {get() {console.log(get foo:${val});return val},set(newVal) {if (newVal ! val) {console.log(set foo:${newVal});val newVal}}})
}
当我们访问foo属性或者设置foo值的时候都能够触发setter与getter
obj.foo
obj.foo new
但是我们为obj添加新属性的时候却无法触发事件属性的拦截
obj.bar 新属性
原因是一开始obj的foo属性被设成了响应式数据而bar是后面新增的属性并没有通过Object.defineProperty设置成响应式数据
三、解决方案
Vue 不允许在已经创建的实例上动态添加新的响应式属性
若想实现数据与视图同步更新可采取下面三种解决方案 Vue.set() Object.assign() $forcecUpdated()
Vue.set()
Vue.set( target, propertyName/index, value )
参数 {Object | Array} target {string | number} propertyName/index {any} value
返回值设置的值
通过Vue.set向响应式对象中添加一个property并确保这个新 property同样是响应式的且触发视图更新
关于Vue.set源码
function set (target: Arrayany | Object, key: any, val: any): any {...defineReactive(ob.value, key, val)ob.dep.notify()return val
}
这里无非再次调用defineReactive方法实现新增属性的响应式
关于defineReactive方法内部还是通过Object.defineProperty实现属性拦截
大致代码如下
function defineReactive(obj, key, val) {Object.defineProperty(obj, key, {get() {console.log(get ${key}:${val});return val},set(newVal) {if (newVal ! val) {console.log(set ${key}:${newVal});val newVal}}})
}
Object.assign()
直接使用Object.assign()添加到对象的新属性不会触发更新
应创建一个新的对象合并原对象和混入对象的属性
this.someObject Object.assign({},this.someObject,{newProperty1:1,newProperty2:2 ...})
$forceUpdate
如果你发现你自己需要在 Vue中做一次强制更新99.9% 的情况是你在某个地方做错了事
$forceUpdate迫使Vue 实例重新渲染
PS仅仅影响实例本身和插入插槽内容的子组件而不是所有子组件。
小结 如果为对象添加少量的新属性可以直接采用Vue.set() 如果需要为新对象添加大量的新属性则通过Object.assign()创建新对象 如果你实在不知道怎么操作时可采取$forceUpdate()进行强制刷新 (不建议)