更新:謝謝你們的支持,最近折騰了一個博客官網出來,方便你們系統閱讀,後續會有更多內容和更多優化,猛戳這裏查看javascript
------ 如下是正文 ------html
Vue進階系列彙總以下,歡迎閱讀。前端
根據第一篇文章介紹的響應式原理,以下圖所示。 git
在初始化階段,本質上發生在auto run
函數中,而後經過render
函數生成Virtual DOM
,view
根據Virtual DOM
生成Actual DOM
。由於render
函數依賴於頁面上全部的數據data
,而且這些數據是響應式的,全部的數據做爲組件render
函數的依賴。一旦這些數據有所改變,那麼render
函數會被從新調用。github
在更新階段,render
函數會從新調用而且返回一個新的Virtual Dom
,新舊Virtual DOM
之間會進行比較,把diff以後的最小改動應用到Actual DOM
中。面試
Watcher負責收集依賴,清除依賴和通知依賴。在大型複雜的組件樹結構下,因爲採用了精確的依賴追蹤系統,因此會避免組件的過分渲染。編程
Actual DOM 經過document.createElement('div')生成一個DOM節點。
document.createElement('div')
// 瀏覽器原生對象(開銷大)
"[object HTMLDivElement]"
複製代碼
Virtual DOM 經過 vm.$createElement('div')生成一個JS對象,VDOM對象有一個表示div的tag屬性,有一個包含了全部可能特性的data屬性,可能還有一個包含更多虛擬節點的children列表。
vm.$createElement('div')
// 純JS對象(輕量)
{ tag: 'div', data: { attrs: {}, ...}, children: [] }
複製代碼
由於Virtual DOM的渲染邏輯和Actual DOM解耦了,因此有能力運行在的非瀏覽器環境中,這就是爲何Virtual DOM出現以後混合開發開始流行的緣由,React Native 和 Weex可以實現的原理就是這個。
JSX和Template都是用於聲明DOM和state之間關係的一種方式,在Vue中,Template是默認推薦的方式,可是也能夠使用JSX來作更靈活的事。
JSX更加動態化,對於使用編程語言是頗有幫助的,能夠作任何事,可是動態化使得編譯優化更加複雜和困難。
Template更加靜態化而且對於表達式有更多約束,可是能夠快速複用已經存在的模板,模板約束意味着能夠在編譯時作更多的性能優化,相對於JSX在編譯時間上有着更多優點。
要求使用以下
<example :tags="['h1', 'h2', 'h3']"></example>
複製代碼
要求輸出以下
<div>
<h1>0</h1>
<h2>1</h2>
<h3>2</h3>
</div>
複製代碼
上面這個需求能夠經過render
函數來作,官方提供了createElement
函數用來生成模板。createElement('div', {}, [...])
可接受的參數以下。
// @returns {VNode}
createElement(
// {String | Object | Function}
// 一個 HTML 標籤字符串,組件選項對象,或者
// 解析上述任何一種的一個 async 異步函數。必需參數。
'div',
// {Object}
// 一個包含模板相關屬性的數據對象
// 你能夠在 template 中使用這些特性。可選參數。
{
},
// {String | Array}
// 子虛擬節點 (VNodes),由 `createElement()` 構建而成,
// 也能夠使用字符串來生成「文本虛擬節點」。可選參數。
[
'先寫一些文字',
createElement('h1', '一則頭條'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)
複製代碼
知道了用法以後,就能夠在render
中返回createElement
生成的虛擬節點,外層是div
,內層是三個錨點標題h1 h2 h3
,因此內層須要遍歷下,使用兩個createElement
就能夠完成了。
一般使用h
做爲createElement
的別名,這是Vue
的通用慣例,也是JSX
的要求。
實現以下
<!--引用-->
<script src="../node_modules/vue/dist/vue.js"></script>
<!--定義template -->
<div id="app">
<example :tags="['h1', 'h2', 'h3']"></example>
</div>
<script> // 定義example組件 Vue.component('example', { props: ['tags'], render (h) { // 第二個參數是一個包含模板相關屬性的數據對象,可選參數 // 子虛擬節點(VNodes)參數能夠傳入字符串或者數字, // 經過createElement生成,可選參數 return h('div', this.tags.map((tag, i) => h(tag, i))) } }) // 實例化 new Vue({ el: '#app' }) </script>
複製代碼
<example>
組件要求以下
Foo
組件渲染<div>foo</div>
,實現一個Bar
組件渲染<div>bar</div>
。<example>
組件,根據屬性ok
動態渲染Foo
組件或者Bar
組件。若是屬性ok
是true
,那麼最終的渲染應該是<div>foo</div>
。ok
,經過這個屬性讓<example>
在Foo
或者Bar
之間切換。根據上面的要求,在模板中調用<example>
組件,而後定義<button>
組件,同時綁定屬性ok
。
實現以下
<!--引用-->
<script src="../node_modules/vue/dist/vue.js"></script>
<!--定義template -->
<div id="app">
<!--綁定屬性ok-->
<example :ok="ok"></example>
<!--綁定點擊事件-->
<button @click="ok = !ok">toggle</button>
</div>
<script> // 定義Foo const Foo = { render (h) { return h('div', 'foo') } } // 定義Bar const Bar = { render (h) { return h('div', 'bar') } } // 定義example組件 // 根據ok屬性動態切換 Vue.component('example', { props: ['ok'], render (h) { return h(this.ok ? Foo : Bar) } }) // 實例化 new Vue({ el: '#app', data: { ok: true } }) </script>
複製代碼
要求以下
withAvatarURL
函數,要求傳入一個帶有url
屬性的組件,返回一個接收username
屬性的高階組件,這個高階組件主要負責獲取相應的頭像URL。http://via.placeholder.com/200x200
傳遞給內部組件。例子以下
const SmartAvatar = withAvatarURL(Avatar)
// 使用這個方式
<smart-avatar username="vuejs"></smart-avatar> // 替換下面的方式 <avatar url="/path/to/image.png"></avatar> 複製代碼
withAvatarURL
函數返回一個對象,接收username
屬性,在生命週期created
獲取頭像URL。Avatar
對象接收src
屬性,src
的內容從withAvatarURL
中獲取,而後展現在上。實例化的時候,傳入新定義的組件名
SmartAvatar
。
實現以下
<!--引用-->
<script src="../node_modules/vue/dist/vue.js"></script>
<!--定義template-->
<div id="app">
<smart-avatar username="vuejs"></smart-avatar>
</div>
<script> // 獲取頭像URL function fetchURL (username, cb) { setTimeout(() => { // 獲取頭像並回傳 cb('https://avatars3.githubusercontent.com/u/6128107?v=4&s=200') }, 500) } // 傳遞的InnerComponent const Avatar = { props: ['src'], template: `<img :src="src">` } function withAvatarURL (InnerComponent) { return { props: ['username'], inheritAttrs: false, // 2.4 only,組件將不會把未被註冊的props呈現爲普通的HTML屬性 data () { return { url: null } }, created () { // 獲取頭像URL並回傳給this.url fetchURL(this.username, url => { this.url = url }) }, render (h) { return h(InnerComponent, { attrs: this.$attrs, // 2.4 only,獲取到沒有使用的註冊屬性 props: { src: this.url || 'http://via.placeholder.com/200x200' } }) } } } const SmartAvatar = withAvatarURL(Avatar) // 實例化,新構造組件名爲SmartAvatar或smart-avatar new Vue({ el: '#app', components: { SmartAvatar } }) </script>
複製代碼
本文內容參考自VUE做者尤大的付費視頻
Vue官網之渲染函數 & JSX
本人Github連接以下,歡迎各位Star
我是木易楊,網易高級前端工程師,跟着我每週重點攻克一個前端面試重難點。接下來讓我帶你走進高級前端的世界,在進階的路上,共勉!