本科機械設計製造及其自動化,16年稀裏糊塗的進了一家幹變廠,17年自學了大半年,18年正式跨行來到前端。工做中主要寫業務代碼,不多涉及造輪子工做,一直但願可以提升編程能力。剛好,公司業務棧以vue爲主,理解它的邏輯,相信對從此確定會有幫助。因而就有了使用ts重構vue的衝動。更甚者,但願可以參與到開源社區的建設,努力變得更好。html
Vue的功能仍是很複雜的,源碼也涉及到跨平臺部分,本次僅學習web方向的源碼,指望經過重構引導閱讀,增強體會。前端
使用到的技術棧以下:vue
使用TypeScript編寫,使用Jest作單元測試,使用rollup進行構建。這次重構並非徹底的照(拷)搬(貝),將選取經常使用功能去實現。指望最佳的開發模式是:以問題(feature)引領,去閱讀源碼,理解後經過本身的方式去實現。以虛擬DOM爲例,重構過程可能至少分3步實現,一、建立虛擬DOM,二、虛擬DOM映射爲真實DOM,三、給真實DOM設置其餘屬性(style、event等)。node
Vue分爲運行版和完整版,完整版本包含compile模塊,對如<div id="app">{{message}}</div>
的模版語法進行了編譯。爲了簡化理解邏輯,筆者重構時直接將compile模塊忽略了,所有經過渲染函數render
進行編寫。git
談到三大框架,一定要了解虛擬DOM。經過訪問vm._vnode
能夠查看vue虛擬DOM的結構,藉助children屬性實現了DOM的樹形結構。es6
虛擬DOM是什麼? 怎麼定義虛擬DOM? 虛擬DOM有什麼好處?github
推薦閱讀snabbdom開源庫,snabbdom的核心很是精簡,總共318行,最關鍵一點,Vue的虛擬DOM是參考它改進的。web
談到Vue的特性,一定要了解數據驅動和響應式。經過訪問app._data
能夠查看處理後的data結構。express
Vue源碼使用Object.defineProperty,筆者將使用Proxy進行屬性攔截,以下述代碼,實例化後經過p.name
能夠訪問到this.data={name: 'xiaoming'}
的屬性。編程
class P {
constructor () {
this.data = {name: 'xiaoming'}
return new Proxy(this, {
get (target, key) {
return Reflect.get(target.data, key)
}
})
}
}
let p = new P()
console.log(p.name)
複製代碼
觀察下面demo,在定時器中修改name
和message
屬性,能夠發現視圖更新了。此時,runCount的值是多少?若是咱們同步修改了更多的屬性,會影響runCount的值嗎?
let runCount = 0
let vm = new Vue({
el: '#app',
data: {
name: 'xiaoming',
message: 'Hello Vue!'
},
render (h) {
runCount += 1
return h('h1', this.name + this.message)
}
})
setTimeout(() => {
vm.name = 'xiaohong'
vm.message = 'Hello world'
}, 1000)
複製代碼
Vue源碼對渲染過程進行了優化,其每次更新都是異步的。此外,你是否在業務中使用到$nextTick
,爲何會用到它?瞭解Js的事件循環,有助於理解Vue的更新原理。
觀察下面demo,用戶首先看到Hello Vue!
,1秒鐘以後觀看到UI變化Hello world
。
vue是如何進行渲染的?當修改message
值,又是如何進行更新的?
let vm = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
render (h) {
return h('h1', this.message)
}
})
setTimeout(() => {
vm.message = 'Hello world'
}, 1000)
複製代碼
觀察下面demo,當用戶點擊按鈕時,點擊次數加1,同時控制檯輸出customClick click
。
Vue是如何綁定事件的?自定義事件和原生事件的處理方式有何不一樣?
有時候在項目中可能會使用到eventBus
,這又是如何實現的?
Vue.component('button-count', {
data () {
return {
count: 0
}
},
render (h) {
const self = this
return h('button', {
on: {
click () {
self.count += 1
self.$emit('customClick', self.count)
}
}
}, `點擊次數:${this.count}`)
}
})
let vm = new Vue({
el: '#app',
render (h) {
return h('button-count', {
nativeOn: {
click () {
console.log('click')
}
},
on: {
customClick () {
console.log('customClick')
}
}
})
}
})
複製代碼
咱們定義了button-count
組件,當用戶點擊時,組件自動記錄點擊次數並更新視圖。假設用戶點擊了2次,此時的runButtonCount
和runCount
的值分別是多少?爲何是這樣的?
Vue不只支持自定義組件,也內置了transition
/keep-alive
等組件,Vue是如何實現組件功能的?父子組件如何進行消息傳遞?
let runButtonCount = 0
let runCount = 0
Vue.component('button-count', {
data () {
return {
count: 0
}
},
render (h) {
runButtonCount += 1
const self = this
return h('button', {
on: {
click () {
self.count += 1
}
}
}, `點擊次數:${this.count}`)
}
})
let vm = new Vue({
el: '#app',
render (h) {
runCount += 1
return h('button-count')
}
})
複製代碼
觀察下面demo,用戶首先看不到任何文字,1秒鐘以後觀看到111&222&333
。
Vue不只支持內置指令,也容許用戶自定義指令。指令代碼是如何控制UI的?
ps:新的項目使用vue進行開發,以iframe形式被嵌入在老頁面中。iframe技術仍是很是有效,但咱們發現新項目中的彈窗沒法全屏展現(半透明mask沒法全屏),後來藉助指令解決了問題。推薦開源庫https://github.com/calebroseland/vue-dom-portal。
let vm = new Vue({
el: '#app',
data () {
return {
news: [111, 222]
}
},
render (h) {
const self = this
return h('h1', {
directives: [
{
name: 'show',
value: self.news.length > 2,
expression: 'news.length > 2',
arg: '',
modifiers: { }
}
]
}, self.news.join('&'))
}
})
setTimeout(() => {
vm.news.push(333)
}, 1000)
複製代碼
觀察下面demo,頁面最終輸出什麼內容?插槽功能是如何實現的?
Vue.component('app-layout', {
render (h) {
const self = this
return h('div', [
h('header', [self._t('header')]),
h('main', [self._t('default', [h('', '默認內容')])]),
h('footer', [self._t('footer')])
])
}
})
let v = new Vue({
el: '#app',
data () {
return {
title: 'hello world!',
msg: 'msg',
desc: 'desc'
}
},
render (h) {
return h('div', [
h('app-layout', [
h('h1', {attrs: {slot: 'header'}, slot: 'header'}, this.title),
h('p', this.msg),
h('p', {attrs: {slot: 'footer'}, slot: 'footer'}, this.desc)
])
])
}
})
複製代碼
頁面渲染後,最終的demo結構以下:
<div>
<header>
<h1>hello world!</h1>
</header>
<main>
<p>msg</p>
</main>
<footer>
<p>desc</p>
</footer>
</div>
複製代碼
待完善
前面咱們定下了不少小目標,接下來就同樣樣去實現。咱們的目標很簡單,就是demo可以按照要求運行。
vue源碼很複雜,有跨平臺代碼(web、weex、server),有性能監控代碼。看源碼時切記不要完美主義,不必必須理解全部的代碼。經過問題主線去閱讀,去了解vue實現的原理。
爲何使用ts?
爲何使用rollup?
筆者重構Vue的儲備均來黃老師的兩套課程,再次着重推薦。文章有些demo來自黃老師,不知道是否侵權(若有侵權一定刪除)。
ts學習推薦:coding.imooc.com/class/chapt…
vue源碼學習:ustbhuangyi.github.io/vue-analysi…