題記
“誰掌握了過去,誰就掌握了未來”——喬治.奧威爾
前言
發端於2013年的個人專案,已然成為全世界三大前端框架之一,在中國大陸更是前端首選。
它的設計思想、編碼技巧也被眾多的框架借鑑、模仿。
學習研究Vue的演進,對於前端同學來說,是提高自身認識和水平的法門。
紀略
Ø 2013年,在Google工作的尤雨溪,受到Angular的啟發,從中提取自己所喜歡的部分,開發出了一款輕量框架,最初命名為Seed。
Ø 同年12月,這粒種子發芽了,更名為Vue,版本號是0.6.0。
Ø 2014.01.24,Vue正式對外發布,版本號是0.8.0。
Ø 釋出於2014.02.25的0.9.0,有了自己的代號:Animatrix,這個名字來自動畫版的《駭客帝國》,此後,重要的版本都會有自己的代號。
Ø 0.12.0釋出於2015.06.13,代號Dragon Ball(龍珠),這一年,Vue迎來了大爆發,Laravel 社群(一款流行的 PHP 框架的社群)首次使用 Vue(我也是在這個論壇上認識Vue的),Vue在JS社群也打響了知名度。
Ø 1.0.0 Evangelion(新世紀福音戰士)是Vue歷史上的第一個里程碑。同年,vue-router(2015-08-18)、vuex(2015-11-28)、vue-cli(2015-12-27)相繼釋出,標誌著 Vue從一個檢視層庫發展為一個漸進式框架。很多前端同學也是從這個版本開始成為Vue的使用者。
Ø 2.0.0 Ghost in the Shell(攻殼機動隊)是第二個重要的里程碑,它吸收了React的Virtual Dom方案,還支援服務端渲染。
Ø 就在不久前,Vue釋出了2.6.0 Macross(超時空要塞),這是一個承前啟後的版本,因為在它之後,3.0.0也呼之欲出了。
1.0
Vue最初的目標是成為大型專案的一個良好補充。設計思想是一種“漸進式框架”,淡化框架本身的主張,降低框架作為工具的複雜度,從而降低對使用者的要求。
相較於之前的版本,1.0的主要改進點是:
1. 提供指令的縮寫
針對v-bind和v-on提供縮寫形式:
<!-- full syntax -->
<a v-bind:href="url"></a>
<!-- shorthand -->
<a :href="url"></a>
<!-- full syntax -->
<button v-bind:disabled="someDynamicCondition">Button</button>
<!-- shorthand -->
<button :disabled="someDynamicCondition">Button</button>複製程式碼
<!-- full syntax -->
<a v-on:click="doSomething"></a>
<!-- shorthand -->
<a @click="doSomething"></a>複製程式碼
2. 清理精簡所提供的介面
此時Vue的目標還是希望作為輕量級框架,成為大型專案的補充(這些大型專案也許已經有自己的前端框架了,如Angular)。
首先清理的當然是那些基本不用的API。
3. 提高初始化的渲染效率
將v-repeat指令換成了v-for指令。同時優化了這個指令的渲染,效率提升了一倍。
4. 兩個官方工具的增強:vue-loader和vueify
除了這些,我們還知道,Vue中資料繫結採用的是資料劫持的方式,使用Object的defineProperty方法。和當時主流的Angular使用的事件觸發的髒檢查是不同的。
這類做法將開發者與直接的DOM操作隔離開來,使得開發者能夠更專注於業務邏輯,同時也改變了開發者的思維方式。
2.0
2016年10月1日釋出的2.0版本對Vue做了大幅度的重構,效能有了很大的提高,也為日後的跨端發展打下了基礎。
1. Virtual DOM
在1.0的時候,Vue和Angular一樣,都是把template扔給瀏覽器解析渲染,然後遍歷DOM樹,提取節點,繫結資料。
用過1.0版本的同學可能還有印象,如果你使用了Moustache語法來展示內容,會在頁面上看到一閃而過的”{{…}}”
2.0借鑑了React的做法,先將template編譯為render函式,render函式返回Virtual DOM物件,然後再交由patch函式,呼叫瀏覽器介面,渲染出DOM。
Virtual DOM並不能保證渲染效率一定高於直接呼叫原生DOM介面(例如innerHTML),還需要配合diff,儘量縮小重新整理的範圍。
除了渲染效率,這種做法還為Vue能夠擴充套件到多端打下了基礎。Virtual DOM實際上可視為一種通用的資料格式,如果小學生能看懂VDOM的話,你可以讓他們在操場上用團體操擺出個網頁來。
2. Render函式
前面已經提到了Render函式的作用,這裡不再贅述。不過我覺得Render函式最大的意義在於:
a) 通過Vue提供的構建工具,將template的編譯從執行時放到了編譯時,提高了執行時的效率
b) 可以只使用Vue的runtime版本,減小檔案體積。
3. 服務端渲染
服務端渲染的目的主要有兩點:
a) 更好的SEO,由於搜尋引擎爬蟲抓取工具可以直接檢視完全渲染的頁面。
b) 更快的首屏渲染速度。特別是對於網路速度慢或者執行緩慢的裝置,無需等待所有的js都下載和解析完成才渲染頁面,而是在服務端渲染好直接傳送給客戶端渲染頁面。
2.6
在2.6釋出之前的很長一段時間,Vue核心團隊都在忙著vue-cli3.0的開發,積攢了不少需求,一直到2019.02.04,釋出了2.6版本。
這個版本主要的改動涉及:語法更新,效能提升,以及向計劃中的3.0靠攏。
1. slot語法更新
提供了一個新的指令:v-slot,用來代替slot元件,以統一slot和scoped slot。
2. 非同步錯誤處理
和2.5相比,errorCaptured 鉤子和全域性的 errorHandler 配置項現在也會處理 v-on 偵聽函式中丟擲的錯誤了。另外,如果元件的生命週期鉤子或事件handler中有非同步操作,那麼可以通過返回一個 Promise 的方式來讓 Vue 處理可能存在的非同步錯誤。
3. 動態指令引數
參看官網的例子,現在指令的引數可以是動態值,如果引數值為 null,則繫結和監聽器會被移除。
<!-- full syntax -->
<a v-bind:href="url"> ... </a>
<!-- shorthand -->
<a :href="url"> ... </a>
<!-- shorthand with dynamic argument (2.6.0+) -->
<a :[key]="url"> ... </a>複製程式碼
<!-- full syntax -->
<a v-on:click="doSomething"> ... </a>
<!-- shorthand -->
<a @click="doSomething"> ... </a>
<!-- shorthand with dynamic argument (2.6.0+) -->
<a @[event]="doSomething"> ... </a>複製程式碼
4. 編譯器警告位置資訊
2.6 版本開始,大多數模板編譯警告訊息現在都帶有原始碼位置資訊:
5. 顯式建立獨立的響應式物件
2.6 引入了一個新的全域性 API,可以用來顯式地建立響應式物件
constreactiveState =Vue.observable({ count:0})複製程式碼
生成的物件可以直接用在計算屬性和 render 函式中,在被改動時觸發相應的更新。
6. 新 ES 模組構建,可直接匯入使用
<script type="module">
import Vue from 'https://unpkg.com/vue/dist/vue.esm.browser.js'
new Vue({
// ...
})
</script>複製程式碼
7. 讓 nextTick 恢復使用 Microtask
在 2.5 版本中,開發團隊做出了一個內部調整:如果更新是在 v-on 事件處理程式中觸發的,則會導致 nextTick 使用 Macrotask(而不是 Microtask)來讓更新進入佇列。最初這麼做是為了修復一些瀏覽器的邊界情況,但反過來又導致了很多其他問題。在 2.6 版本中,開發團隊為原始問題找到了一個更簡單的修復方案,這樣我們就可以在任何情況下恢復 nextTick 使用 Microtask。
8. this.$scopedSlots 函式統一返回陣列
在 render 函式中,scoped slot 通過 this.$scopedSlots 暴露為函式。在之前版本,呼叫 scoped slot 函式會根據父元件傳入內容返回單個 VNode 或 VNode 陣列。這種設計實際上是一種疏忽,因為它返回值的型別不確定,可能會導致意外的邊界情況。
在 2.6 版本,scoped slot 函式確保只返回 VNode 陣列或 undefined。
3.0
3.0是非常大的重構,原始碼使用TypeScript重寫,並且還有許多令人期待的新特性:
1. Virtual DOM完全重構
編譯器承擔了更多的責任,將之前一些在runtime時做的工作挪了過來:
a) 元件快速路徑+單一型別+子型別檢測:
b) 優化slot的編譯,避免無謂的父/子節點重渲染
普通的 slot 是在父元件的渲染函式中生成的,因此當一個普通的 slot 所依賴的資料發生變化時,首先觸發的是父元件的更新,然後新的 slot 內容被傳到子元件,觸發子元件更新。
相比之下,scoped slot 在編譯時生成的是一個函式,這個函式被傳入子元件之後會在子元件的渲染函式中被呼叫。這意味著 scoped slot 的依賴會被子元件收集,那麼當依賴變動時就只會直接觸發子元件更新。
c) 靜態內容和靜態屬性提取
當編譯器發現這些靜態屬性的時候,就會直接提取這部分的靜態屬性作為例項屬性的一部分,在後面的資料比較中直接忽略這部分靜態屬性,以提升效能。
d) 內聯事件函式的提取
2. 使用Proxy代替defineProperty
defineProperty監聽不到物件屬性的增刪、陣列元素和長度的變化,同時會在vue初始化的時候把所有的Observer都建立好,才能觀察到資料物件屬性的變化。
而proxy可以做到監聽物件屬性的增刪和陣列元素和長度的修改,還可以監聽Map、Set、WeakSet、WeakMap,同時還實現了惰性的監聽,不會在初始化的時候建立所有的Observer,而是會在用到的時候才去監聽。
3. 使用TypeScript重構
因為Flow爛尾了,而TypeScript又越來越好。
4. 自定義的Renderer API
這一點是為了向多端擴充套件,我們知道,現在Weex適配Vue採取的是fork的方式。3.0之後,這些native的適配層就可以通過自定義渲染器的方式來擴充套件了。
5. 支援Time Slicing
目前時間切片還處於實驗階段。
Vue執行在瀏覽器的主執行緒,如果執行了一個非常耗費時間的操作,那麼瀏覽器會停止對使用者操作的響應,直到計算完畢。顯然這種使用者體驗是非常糟糕的,使用者還以為瀏覽器已經掛掉了。在有了Time Slicing支援後,Vue將會限制自己的執行時間,只在一個時間片段內執行,這個時間片段被稱為“幀”,預設情況下一幀的時間是16ms,對於小型計算任務來說基本足夠了,而一秒鐘可以執行62.5幀,有60幀左右的重新整理率,瀏覽器對使用者的響應將會是比較順滑的了。
參考資料
State of Vue.js report 2017 中文版
Vue2.0 中,“漸進式框架”和“自底向上增量開發的設計”這兩個概念是什麼?
網上都說操作真實 DOM 慢,但測試結果卻比 React 更快,為什麼?
實現雙向繫結Proxy比defineproperty優劣如何
如何評價React的新功能Time Slice 和Suspense?
學習React Fiber架構,理解如何實現時間分片(Time Slicing)和 Suspense.
擴充套件閱讀
|
中譯名 |
一句話介紹 |
Animatrix |
駭客帝國動畫版 |
錫安到底是不是真的 |
Blade Runner |
銀翼殺手 |
影史最佳科幻片,催生了賽博朋克這一科幻流派的產生 |
Cowboy Bebop |
星際牛仔 |
因為東京電視臺單方面認為動畫存在較大的尺度問題而並沒有讓這部動畫如期播完。 |
Dragon Ball |
龍珠/七龍珠 |
鳥山明的代表作 |
Evangelion |
新世紀福音戰士 |
日本動畫史上的里程碑,被公認為日本歷史中最偉大的動畫之一 |
Ghost in the shell |
攻殼機動隊 |
故事設定在2029年,快來了 |
Hunter x Hunter |
全職獵人 |
連載二十年,休刊無數次 |
Initial D |
頭文字D |
還是周杰倫演的好看 |
JoJos Bizarre Adventure |
JOJO的奇妙冒險 |
我儘量避免戰鬥,但是我從不逃避 |
Kill la Kill |
斬服少女 |
|
Level E |
靈異E接觸 |
開啟了富堅義博休刊習慣的罪惡之源 |
Macross |
超時空要塞 |
做工精良的精品 |