關(guān)于Vue-extend和VueComponent問題小結(jié)
在一個非單文件組件中(一個文件中包含n個組件,最常見的就是單個html文件中存在多個組件),如果我們需要在這個文件中創(chuàng)建n個組件,然后再頁面上展示,這時候我們就需要先定義組件,然后注冊組件,最后使用組件。在定義組件這一步,我們就需要使用到 extend 這個方法。當(dāng)然,也可以在一個html文件中使用多個 new Vue () 來注冊組件,但是這么做有問題,下面再說。
Vue.extend(option)
官方文檔解釋:使用基礎(chǔ) Vue 構(gòu)造器,創(chuàng)建一個“子類”。參數(shù)是一個包含組件選項的對象。
組件選項:templete模板(el屬性只能在 new Vue() 時使用)、data數(shù)據(jù)、methods方法、computed計算屬性等等正常組件擁有的。
我的理解:首先 extend 是 Vue 自帶的一個全局方法,該方法接收一個對象作為參數(shù),在實例化的時候,通過傳遞的參數(shù)將一個空的 Vue實例 進(jìn)行擴(kuò)展,并以此來創(chuàng)造出一個 Vue 子類,也就是我們說的 Vue 組件。
使用方法:
1、非單文件組件內(nèi)部所有組件全部使用 Vue.extend() 方式注冊(未指定根組件,無法渲染)
2、非單文件組件中使用 new Vue() 注冊根組件,其余子組件則使用 Vue.extend() 方式注冊。
3、全部使用 new Vue() 注冊組件(若是存在組件嵌套,則子組件內(nèi)部雙向綁定的的數(shù)據(jù)失效)
1、全部使用 Vue.extend() 方式注冊組件
const vm2 = Vue.extend({ template: `<div id='root2'> {{msg}} </div>`, data() { return { msg: 'root2', count: 2, pets: 'dog' } } }) const vm1 = Vue.extend({ template: `<div id='root1'> {{msg}} <!-- 使用 root2 組件 --> <vm2 /> </div>`, data() { return { msg: 'root1', count: 1, pets: 'dog' } }, components: { // 注冊子組件 vm2 } })
無法展示,頁面也不會報錯,因為根本沒有指定根組件進(jìn)行渲染
2、使用第二種方式分別注冊子組件和根組件
注冊子組件
const vm1 = Vue.extend({ template: `<div id='root2'> {{msg}} <input v-model="count" /> </div> `, data() { return { msg: 'root2', count: 2, pets: 'dog' } } })
注冊根組件
// 注冊根組件 const vm = new Vue({ el: '#root1', data() { return { msg: 'root1', count: 1, pets: 'dog' } }, components: { // 注冊子組件 vm1 } })
根組件以及子組件使用
<div id='root1'> {{msg}} <input v-model="count" /> <!-- 使用 root2 組件 --> <vm1 /> </div>
頁面展示效果正常,且雙向綁定數(shù)據(jù)正常
3、全部使用 new Vue 定義組件。其實在正常的開發(fā)中,這個方法用的極少,因為我們的開發(fā)一般都是單文件組件,在工程中每個組件都是通過 new Vue() 創(chuàng)建的,直接掛載到 根組件 app上。但是在單文件組件中,我們一般不使用多個 new Vue() 來創(chuàng)建組件,這是因為在使用 new Vue() 時,必須要傳入一個 el 屬性,這樣會導(dǎo)致html頁面上存在多個根節(jié)點,如果你的根節(jié)點嵌套了,那嵌套的根節(jié)點中綁定的數(shù)據(jù)會失效,不會展示。例如:
<div id='root1'> {{msg}} <input v-model="count" /> <select name="pets" id="pet-select" v-model="pets"> <option value="">--Please choose an option--</option> <option value="dog">Dog</option> </select> <div id='root2'> {{msg}} <input v-model="count" /> <select name="pets" id="pet-select" v-model="pets"> <option value="">--Please choose an option--</option> <option value="dog">Dog</option> </select> </div> </div>
在這個 html 文件中,存在兩個根節(jié)點 ,root1、root2,兩個跟節(jié)點內(nèi)部的子節(jié)點完全一樣,綁定的數(shù)據(jù)也完全一樣,但是 root1根節(jié)點包裹住了 root2根節(jié)點。
const vm = new Vue({ el: '#root1', data() { return { msg: 'root1', count: 1, pets: 'dog' } }, }) const vm1 = new Vue({ el: '#root2', data() { return { msg: 'root2', count: 2, pets: 'dog' } } })
按照原本的想法,兩個節(jié)點展示的數(shù)據(jù)應(yīng)該完全一樣,但是在頁面上的效果是這樣的。
可以看到,只有外部的root1根節(jié)點展示了對的數(shù)據(jù), root2的根節(jié)點數(shù)據(jù)要么為空不展示,要么展示的是錯誤數(shù)據(jù)。
如果我們使用 Vue.extend() 來注冊子組件又會是什么情況呢?
首先,注冊root2組件,其實就是將root2的所有節(jié)點放在了 templete 屬性內(nèi)部,用字符串模板包裹
const vm1 = Vue.extend({ template: `<div id='root2'> {{msg}} <input v-model="count" /> <select name="pets" id="pet-select" v-model="pets"> <option value="">--Please choose an option--</option> <option value="dog">Dog</option> </select> </div>`, data() { return { msg: 'root2', count: 2, pets: 'dog' } } })
2、在父組件中注冊 root2 組件
const vm = new Vue({ el: '#root1', data() { return { msg: 'root1', count: 1, pets: 'dog' } }, components: { vm1 } })
3、使用 root2 組件
<div id='root1'> {{msg}} <input v-model="count" /> <select name="pets" id="pet-select" v-model="pets"> <option value="">--Please choose an option--</option> <option value="dog">Dog</option> </select> <!-- 使用 root2 組件 --> <vm1 /> </div>
4、頁面效果
結(jié)論:如果是在非單組件文件(或者是html頁面),最好是只用 一個new Vue()注冊一個根組件,其余子組件則是用 Vue.extend() 注冊。否則如果使用 new Vue() 注冊所有組件的話,若是存在組件包裹的情況,則被包裹的組件內(nèi)部雙向數(shù)據(jù)綁定會失效。
VueComponent
在組件定義之后,我們其實還沒有去理解這個過程和內(nèi)部操作,下面我們就來剖析一下,看看在 Vue.extend() 之后,發(fā)生了什么。
首先,我們來看看 Vue.extend() 之后,返回的是一個什么東西。
樣例代碼就不貼了,就是上面的 vm1 實例。打印 vm1 之后,看看是個啥
打印之后發(fā)現(xiàn),這玩意是個函數(shù),而且還是個構(gòu)造函數(shù)。在這個函數(shù)里面啥操作也沒做,只不過調(diào)用了 _init() 方法。
Vue.extend = function (extendOptions) { /*****其余操作***/ var Sub = function VueComponent(options) { console.log("VueComponent被調(diào)用了"); this._init(options); }; /*****其余操作***/ return Sub; }; }
所以說,
1、組件的本質(zhì)就是一個 【VueComponent 的構(gòu)造函數(shù)】,且這個函數(shù)是 Vue.extend() 生成的。
2、在使用組件時,我們只需要寫上組件標(biāo)簽,Vue 會自動幫我們生成 組件的實例對象( 因為組件的本質(zhì)就是一個構(gòu)造函數(shù),構(gòu)造函數(shù)被調(diào)用之后,當(dāng)然會產(chǎn)生實例對象),即 Vue 幫我們執(zhí)行的 new VueCopmonennt(options)
3、特別注意,每次調(diào)用 Vue.extend(),返回的都是一個新的組件,因為是通過函數(shù)返回的。這個地方我們看看上面的源碼就能知道,因為 每次調(diào)用之后返回的 Sub 都是不一樣的。
4、關(guān)于this指向,
a、通過 Vue.extend() 配置的組件,在data,methods,component,watch等可能用到 this 的地方,this 指向的是 【VueComponent 的實例對象】
b、通過new Vue() 配置的組件,在data,methods,component,watch等可能用到 this 的地方,this 指向的是 【Vue 的實例對象】
驗證一下:
分別在上面的實例 vm,vm1上配置 show 方法,在方法內(nèi)部打印當(dāng)前this
點擊按鈕之后,查看 this 指向
展開的地方太大了,就不展開了,但是在控制臺上對比發(fā)現(xiàn),除了 一個是 Vue {} 一個是 VueComponent {} 之外,內(nèi)部的所有屬性全部一致,包括數(shù)據(jù)劫持,數(shù)據(jù)代理,底層方法等等。
組件管理
之前說的Vue.extend(option) 這個模塊時,說到了非單文件組件內(nèi)部,最好是使用Vue.extend(option) 來定義子組件,然后使用 new Vue(option) 來注冊根組件,從而使得 根組件好方便管理子組件,那么從那里能看出來管理狀態(tài)呢?
看看上面的this 指向問題,展開之后,發(fā)現(xiàn) 一個 $children屬性,這是一個數(shù)組
在 new Vue() 配置的組件中,發(fā)現(xiàn)存在一個 VueComponent {} 實例對象,這個對象指向的就是 vm1實例對象
而在 Vue.extend() 配置的組件中,發(fā)現(xiàn)這是一個空數(shù)組,這就是因為, 根組件調(diào)用了 vm1子組件,而 vm1子組件,內(nèi)部是沒有調(diào)用別的子組件的。
這就是 Vue 的組件管理模式
總結(jié)
如何使用 Vue.extend() 定義一個組件:
1、Vue.extend(option) 和 new Vue(option) 創(chuàng)建組件時所傳入的 option 配置項幾乎一樣.
區(qū)別在于:
(a)、el不能指定:所有的組件最終只會由一個vm管理,由這個vm中的 el 指定掛載容器
(b)、data必須寫成函數(shù):避免組件復(fù)用時,數(shù)據(jù)存在引用關(guān)系。
Vue.extend() 定義組件的本質(zhì):
本質(zhì)上是 調(diào)用了 VueComponent () 這個構(gòu)造函數(shù),去返回了一個 【VueComponent 實例對象】,且每次在Vue.extend() 調(diào)用時,返回的組件實例對象都不一樣
非單文件組件中定義根組件和子組件
原則上,默認(rèn)一個非單文件組件中 只存在一個 new Vue() 定義的根組件,可以有無數(shù)個 Vue.extend() 定義的子組件,這是因為,如果所有組件都用 new Vue() 定義,那么如果存在組件包裹的情況,子組件內(nèi)部雙向綁定的數(shù)據(jù)不會生效。如果都用 Vue.extend() 定義組件,那么則沒有指定根組件,無法渲染。
this指向問題
使用 new Vue() 定義的組件,在組件內(nèi)部能用到 this 的地方,this指向為【Vue實例對象】
使用 Vue.extend() 定義的組件,~~~~~~~~~~~~~this指向為【VueComponent實例對象】
官網(wǎng)補(bǔ)充
這里說明了,使用 VueComponent 和 new Vue 的異同。但是其實還有一點:在 new Vue 中,傳遞的 data 屬性,可以是對象,也可以是函數(shù)( 當(dāng)然,我們還是推薦函數(shù)寫法 ),但是在VueComponent 中傳遞的 data 屬性,則只能是函數(shù),因為 new Vue 注冊的是 根組件,不存在復(fù)用情況,data中的屬性不存在引用關(guān)系,不會導(dǎo)致數(shù)據(jù)錯亂,但是VueComponent 則不同
相關(guān)文章
一個vue組件庫發(fā)布到npm的完整實現(xiàn)過程
工作的時候總是使用別人的npm包,然而我有時心底會好奇自己如何發(fā)布一個npm包呢,什么時候自己的包能夠被很多人喜歡并使用呢,下面這篇文章主要給大家介紹了關(guān)于一個vue組件庫發(fā)布到npm的相關(guān)資料,需要的朋友可以參考下2022-03-03elementui中tabel組件的scope.$index的使用及說明
這篇文章主要介紹了elementui中tabel組件的scope.$index的使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10uni-app制作小程序?qū)崿F(xiàn)左右菜單聯(lián)動效果
這篇文章主要介紹了uni-app制作小程序?qū)崿F(xiàn)左右菜單聯(lián)動效果,實現(xiàn)步驟和思路都很簡單,今天通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-11-11vue中數(shù)據(jù)不響應(yīng)的問題及解決
這篇文章主要介紹了vue中數(shù)據(jù)不響應(yīng)的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09