vue初始化data方法有兩種:1、object方式,語(yǔ)法“var data = { 鍵值對(duì) }”;2、function方式,語(yǔ)法“data: function () {return { 鍵值對(duì) }}”。需要注意組件和extend中的data初始化不能是Object,否則會(huì)報(bào)錯(cuò)。組件中data用function方式是為了防止多個(gè)組件實(shí)例對(duì)象之間共用一個(gè)data,產(chǎn)生數(shù)據(jù)污染。
本教程操作環(huán)境:windows7系統(tǒng)、vue3版,DELL G3電腦。
vue data有兩種初始化的方式,function和object,但是這兩種情況適用場(chǎng)景有哪些?能不能通用?帶著這兩個(gè)問(wèn)題咱們一起分析下
data初始化
// 代碼來(lái)源于官網(wǎng)示例 // 第一種定義方式 var data = { a: 1 } // 直接創(chuàng)建一個(gè)實(shí)例 var vm = new Vue({ data: data }) // Vue.extend() 中 data 必須是函數(shù) var Component = Vue.extend({ // 第二種定義方式 data: function () { return { a: 1 } } })
上述代碼簡(jiǎn)單描述了data定義的兩種方式
-
function
-
object
官網(wǎng)demo中也著重說(shuō)了extend中data初始化不能用object。那么為什么呢?
源碼分析
按照官網(wǎng)demo,Vue.extend中的data初始化不能是Object,如果我們強(qiáng)制寫成Object會(huì)出現(xiàn)什么?
var Component = Vue.extend({ data: { a: 1 } })
運(yùn)行以后chrome的consolo直接報(bào)錯(cuò),信息如下
vue.esm.js?efeb:591 [Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.
通過(guò)分析源碼以及報(bào)錯(cuò)信息,當(dāng)觸發(fā)Vue.extend的時(shí)候,他會(huì)做一個(gè)合并操作,把一個(gè)基礎(chǔ)組件(里面vmode, transtion等)和你定義在extend內(nèi)的信息,通過(guò)mergeField往options上合并,當(dāng)合并到data的時(shí)候,他會(huì)觸發(fā)strats.data,在這個(gè)里面會(huì)check data是不是一個(gè)function,這里需要注意的是filter、components等和data走的是兩套合并流程,詳細(xì)的請(qǐng)看代碼注釋,如下
// vue.extend 源碼地址https://github.com/vuejs/vue/blob/dev/src/core/global-api/extend.js Vue.extend = function (extendOptions: Object): Function { ... // 在這里會(huì)觸發(fā)mergeOptions方法 Sub.options = mergeOptions( Super.options, extendOptions ) ... } // mergeOptions 源碼地址https://github.com/vuejs/vue/blob/dev/src/core/util/options.js export function mergeOptions ( parent: Object, child: Object, vm?: Component ): Object { ... const options = {} let key // parent對(duì)象內(nèi)包含components、filter,、directive for (key in parent) { mergeField(key) } // child對(duì)象內(nèi)對(duì)應(yīng)的是Vue.extend內(nèi)定義的參數(shù) for (key in child) { if (!hasOwn(parent, key)) { mergeField(key) } } function mergeField (key) { // 這一步是根據(jù)傳入的key找到不同的合并策略filter、components、directives用到合并策略是這個(gè)方法mergeAssets和data用到的不一樣,當(dāng)合并到data的時(shí)候會(huì)進(jìn)入專屬的合并策略方法內(nèi) const strat = strats[key] || defaultStrat options[key] = strat(parent[key], child[key], vm, key) } } // strats.data 源碼地址https://github.com/vuejs/vue/blob/dev/src/core/util/options.js strats.data = function ( parentVal, childVal, vm ) { if (!vm) { // 如果data不是function的話會(huì)直接走下面的報(bào)錯(cuò)信息 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) };
其他情況
其實(shí)我們上述代碼只是一個(gè)簡(jiǎn)單的流程,在實(shí)際開發(fā)中同類情況有:子組件內(nèi)、路由內(nèi)都不可以把data定義為一個(gè)對(duì)象,因?yàn)樗麄兊讓佣颊{(diào)用了mergeOptions方法
什么時(shí)候可以定義成一個(gè)對(duì)象
在vue初始化的時(shí)候,如下
new Vue({ data: { linke: '//sinker.club' } })
意義
ok,上面說(shuō)了那么多,那么這么做的意義是什么?為什么那幾種情況不可以定義為對(duì)象? 其實(shí)回答這個(gè)問(wèn)題,需要回到j(luò)s本身,眾所周知js數(shù)據(jù)類型分為引用和基本,引用類型包含Object, Array, Function,何為引用類型就不在這里闡述了
var obj = {link: '//www.sinker.club'} var obj2 = obj var obj3 = obj obj2.link = "//gitlab.sinker.club" console.log(obj3.link) // "//gitlab.sinker.club"
上述代碼反應(yīng)了一個(gè)問(wèn)題,由于obj3和obj2在內(nèi)存中都是指向一個(gè)地址,那么obj2的修改會(huì)影響到obj3,當(dāng)然處理這種問(wèn)題可以用深copy來(lái)做到
-
JSON.parse(JSON.stringify(obj))
-
deepClone(obj)
但是這兩種做法需要開發(fā)或者框架每一次都要深copy一次,當(dāng)數(shù)據(jù)量大的時(shí)候?qū)π阅苁裁炊疾挥押?,那么Vue怎么做的呢?把data定義成一個(gè)function
function data() { return { link: '//sinker.club' } } var obj = test() var obj2 = test() obj2.link ="//gitlab.sinker.club" console.log(obj.link) '//sinker.club'
為什么這么做?解決的場(chǎng)景是什么呢?
比如我定一個(gè)子組件,data是按照對(duì)象的方式定義的,這個(gè)組件在多個(gè)地方引用,如果其中一個(gè)引用此組件的data修改了,那么就會(huì)造成其余引用此組件的data同時(shí)改變, end.
擴(kuò)展知識(shí):
vue實(shí)例的時(shí)候定義data屬性既可以是一個(gè)對(duì)象,也可以是一個(gè)函數(shù)
const app = new Vue({ el:"#app", // 對(duì)象格式 data:{ foo:"foo" }, // 函數(shù)格式 data(){ return { foo:"foo" } } })
組件中定義data屬性,只能是一個(gè)函數(shù)
如果為組件data直接定義為一個(gè)對(duì)象
Vue.component('component1',{ template:`<div>組件</div>`, data:{ foo:"foo" }})
則會(huì)得到警告信息
說(shuō)明:
-
vue中組件是用來(lái)復(fù)用的,為了防止data復(fù)用,將其定義為函數(shù)。
-
vue組件中的data數(shù)據(jù)都應(yīng)該是相互隔離,互不影響的,組件每復(fù)用一次,data數(shù)據(jù)就應(yīng)該被復(fù)制一次,之后,當(dāng)某一處復(fù)用的地方組件內(nèi)data數(shù)據(jù)被改變時(shí),其他復(fù)用地方組件的data數(shù)據(jù)不受影響,就需要通過(guò)data函數(shù)返回一個(gè)對(duì)象作為組件的狀態(tài)。
-
當(dāng)我們將組件中的data寫成一個(gè)函數(shù),數(shù)據(jù)以函數(shù)返回值形式定義,這樣每復(fù)用一次組件,就會(huì)返回一份新的data,擁有自己的作用域,類似于給每個(gè)組件實(shí)例創(chuàng)建一個(gè)私有的數(shù)據(jù)空間,讓各個(gè)組件實(shí)例維護(hù)各自的數(shù)據(jù)。
-
當(dāng)我們組件的date單純的寫成對(duì)象形式,這些實(shí)例用的是同一個(gè)構(gòu)造函數(shù),由于JavaScript的特性所導(dǎo)致,所有的組件實(shí)例共用了一個(gè)data,就會(huì)造成一個(gè)變了全都會(huì)變的結(jié)果。
【