Vue(ES6)中的data属性为什么不能是一个对象?
以下引官網原文:當一個組件被定義,data 必須聲明為返回一個初始數據對象的函數,因為組件可能被用來創建多個實例。如果 data 仍然是一個純粹的對象,則所有的實例將共享引用同一個數據對象!通過提供 data 函數,每次創建一個新實例后,我們能夠調用 data 函數,從而返回初始數據的一個全新副本數據對象。
最近來面試的很多人。我都會問這個問題“vue中,為什么data是一個方法返回一個對象,而不是直接賦給一個對象”,只有少數人會回答出是怕重復創建實例造成多實例共享一個數據對象。更多的人回答是不知道,或者是官方文檔要求這么寫就這么寫了。
其實這個問題的考點無非就是對vue的熟悉情況,挖掘應聘者的自驅學習能力,對技術的求知欲。這樣的人往往技術成長快,具備很強的獨立解決問題能力。也是各個技術團隊都喜歡的一種人。
首先在vue的源碼中,有這樣的處理:
// vue/src/core/instance/state.jsfunction initData (vm: Component) {var data = vm.$options.data;data = vm._data = typeof data === 'function'? getData(data, vm): data || {};if (!isPlainObject(data)) {data = {};process.env.NODE_ENV !== 'production' && warn('data functions should return an object:\n' 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',vm);}...}顯然,vue是支持將一個對象作為vue構造參數中data屬性的值并且,如果data是方法的話,也會先取得內部返回的對象結果。并且在vuex中又存在這樣的用法:
// vuex/src/store.jsfunction resetStoreVM (store, state, hot) {...const silent = Vue.config.silentVue.config.silent = truestore._vm = new Vue({data: {$$state: state},computed})...}這是怎么回事呢?既然支持,又不讓我們用,而且當我們在一個vue文件中,直接給一個data賦予一個對象則會引起紅色警告:
[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.這個警告來自于Vue源碼中的vue/src/core/util/options.js
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)}首先我們需要了解在vue文件的代碼被實例化成vue組件的過程需要經歷下面這些步驟:
在4中因為使用了mergeOptions,進而觸發了對data的類型驗證,也就顯示了之初的那個警告。
那么為一個對象的屬性賦予一個對象真的就會造成共享對象么?讓我們看下面的代碼:
class A {constructor(opt) {this.opt = opt;}update() {this.opt.data.a ;}notify() {console.log(this.opt);}}我們用這個類來虛擬化Vue的構造。然后進行測試:
// testlet c = new A({ data: { a: 1 }});let d = new A({ data: { a: 1 }});c.update();d.update();c.notify(); // Object data: a: 2我們通過字面量的方式來為構造參數傳入一個對象屬性,然而我們驚奇的發現,其實并沒有發生共享引用的問題。這是什么鬼?
哦,不對,我們通常在使用vue的時候是在vue文件中export出一個對象,然后這個對象會在vue-loader的時候被編譯傳入到模版編譯后的render函數中。那么我們換一個方法來做一個實驗:
// test.js文件,用于虛擬vue文件導出的vue options對象export default {data: {a: 1}}// index.jslet a = new A(test);let b = new A(test);a.update();b.update();a.notify(); // Object data: a: 3什么?在這里產生了vue文檔中提到的共享引用的問題。這是為什么呢?
原因在于vue的編譯過程以及引入的import過程,通過babel編譯,test.js會被轉化為es5語法的js文件:
var Re = {data: {a: 1}};var Oe = function () {function e(t) {Object(i["a"])(this, e), this.opt = t}return Object(o["a"])(e, [{key: "update",value: function () {this.opt.data.a }}, {key: "notify",value: function () {console.log(this.opt)}}]), e}(),Fe = new Oe(Re),Ne = new Oe(Re);Fe.update(), Ne.update(), Fe.notify();var $e = new Oe({data: {a: 1}}),Ve = new Oe({data: {a: 1}});$e.update(), Ve.update(), $e.notify(),What?原來我們的每一個vue文件經過babel編譯,將導出的對象直接替換成了一個對象變量,然后將這個變量傳入到對應的組件構造函數中。因此,也就產生了引用共享的問題(所有js對象皆引用)。
由于vue源碼并沒有通讀,因此如有錯誤請指教
總結
以上是生活随笔為你收集整理的Vue(ES6)中的data属性为什么不能是一个对象?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 移动端日期选择插件rolldate
- 下一篇: 今天的考核题目: 你知道React和Vu