[TOC]
-----
## Vue是漸進式框架
Vue.js是一套構建用戶界面的 **漸進式框架**。

Vue從設計角度來講, 雖然能夠涵蓋這張圖上所有的東西,但是你并不需要一上手就把所有東西全用上 ,因為沒有必要。無論從學習角度,還是實際情況,這都是可選的。**聲明式渲染**和**組件系統**是Vue的核心庫所包含內容,而**客戶端路由**、**狀態管理**、**構建工具**都有專門解決方案。這些解決方案相互獨立,你可以在核心的基礎上任意選用其他的部件,不一定要全部整合在一起。
###聲明式渲染
現在基本所有的框架都已經認同這個看法——DOM應盡可能是一個函數式到狀態的映射。狀態即是唯一的真相,而DOM狀態只是數據狀態的一個映射。如下圖所示,所有的邏輯盡可能在狀態的層面去進行,當狀態改變的時候,View應該是在框架幫助下自動更新到合理的狀態,而不是說當你觀測到數據變化之后手動選擇一個元素,再命令式地去改動它的屬性。

在Vue2.0中,渲染層的實現做了根本性改動,那就是引入了虛擬DOM。

Vue的編譯器在編譯模板之后,會把這些模板編譯成一個渲染函數 。
而函數被調用的時候就會渲染并且返回一個 **虛擬DOM的樹 **。
這個樹非常輕量,它的職責就是描述當前界面所應處的狀態。當我們 有了這個虛擬的樹之后,再交給一個patch函數,負責把這些虛擬DOM真正施加到真實的DOM上 。在這個過程中,Vue有自身的響應式系統來偵測在渲染過程中所依賴到的數據來源。在渲染過程中,偵測到的數據來源之后,之后就可以精確感知數據源的變動。到時候就可以根據需要重新進行渲染。當重新進行渲染之后,會生成一個新的樹,將新樹與舊樹進行對比,就可以最終得出應施加到真實DOM上的改動。最后再通過patch函數施加改動。
這樣做的主要原因是, 在瀏覽器當中,JavaScript的運算在現代的引擎中非常快,但DOM本身是非常緩慢的東西 。當你調用原生DOM API的時候,瀏覽器需要在JavaScript引擎的語境下去接觸原生的DOM的實現,這個過程有相當的性能損耗。所以,本質的考量是,要把耗費時間的操作盡量放在純粹的計算中去做,保證最后計算出來的需要實際接觸真實DOM的操作是最少的。
下面看 **渲染函數**
如下圖所示,在Vue2.0當中,可以看到就是說當比如左側的模板,經過Vue的編譯之后就會變成右側的東西。

這個函數類似于創建一個虛擬元素的函數,我們可以給它一個名字,給它描述應該有的屬性特性和可能其他的數據。然后后面這個最后這個參數是個數組,包含了該虛擬元素的子元素。總的來說2.0的編譯器做的就是這個活。
同時,在Vue2.0里,用戶可以選擇直接跳過模板這一層去手寫渲染函數,同時也有可選JSX支持。從開發者的偏好以及開發者的效益的角度來考量,模板和JSX是各有利弊的東西。模板更貼近我們的HTML,可以讓我們更直觀地思考語義結構,更好地結合CSS的書寫。JSX和直接渲染函數,因為是真正的JavaScript,擁有這個語言本身的所有的能力,可以進行復雜的邏輯判斷,進行選擇性的返回最終要返回的DOM結構,能夠實現一些在模板的語法限制下,很難做到的一些事情。

所以在Vue2.0里,兩個都是可以選擇的。在絕大部分情況下使用模板,但是在需要復雜邏輯的情況下,使用渲染函數。 在Vue2.0的路由和內部的一些實踐上,都大量地應用渲染函數做復雜的抽象組件 ,比如過渡動畫組件以及路由里面的link組件,都是用渲染函數實現的,同時還保留了它本身的依賴追蹤系統。
如下圖所示,Vue的依賴追蹤通過ES5的 Object.defineProperty 方法實現。比如,我們給它一個原生對象,Vue會遍歷這個數據對象的屬性,然后進行屬性轉換。每一個屬性會被轉換為一個 getter 和一個 setter。同時每個組件會有一個對應的 watcher 對象,這個對象的職責就是在當前組件被渲染的時候,記錄數據上面的哪些屬性被用到了。
例如,在渲染函數里面用到A.B的時候,這個就會觸發對應的 getter。整個渲染流程具體要點如下:
- 當某個數據屬性被用到時,觸發 getter,這個屬性就會被作為依賴被 watcher 記錄下來。
- 整個函數被渲染完的時候,每一個被用到的數據屬性都會被記錄。
- 相應的數據變動時,例如給它一個新的值,就會觸發 setter,通知數據對象對應數據有變化。
- 此時會通知對應的組件,其數據依賴有所改動,需要重新渲染。
- 對應的組件再次調動渲染函數,生成 Virtual DOM,實現 DOM 更新。
如下圖所示,在Vue里面由于依賴追蹤系統的存在,當任意數據變動的時,Vue的每一個組件都精確地知道自己是否需要重繪,所以并不需要手動優化。用Vue渲染這些組件的時候,數據變了,對應的組件基本上去除了手動優化的必要性。

### 組件系統
相信基本上所有的現代框架都已經走向了組件化道路,Web Components 從規范層面做這個實踐。主流框架都有各有不同的封裝,但 核心思想都是一樣,把UI結構映射到恰當的組件樹 ,如下圖所示。

在Vue中,父子組件之間的通信是通過 props 傳遞。從父向子單向傳遞;而如果子組件想要在父組件作用里面產生副作用,就需要去派發事件。這樣就形成一個基本的父子通信模式,在涉及大規模狀態管理的時候會有額外的方案,這個后面會提到。

Vue的組件引入構建工具之后有一個 單文件組件概念 ,如下圖所示,就是這個Vue文件。在同一個Vue文件里,可以同時寫 template, script 和 style,三個東西放在一個里面。同時,Vue的單文件組件和 Web Components 有一個本質不同,它是基于構建工具實現。這樣的好處是有了一個構建的機會,可以對這些單文件組件做更多的分析處,在每一個語言塊里可以單獨使用不同的處理器,這點后面還會講到。

### 客戶端路由
在做一個界面復雜度非常的高應用時,它會有很多的狀態,這樣的應用顯然不可能在每做一次操作后都刷新一個頁面作為用戶反饋。這就要這個應用有多個復雜的狀態,同時這些狀態還要對應到URL。有一個重要的功能叫做 deep-linking,也就是當用戶瀏覽到一個URL,然后把它傳給另外的人或者復制重新打開,應用需要直接渲染出這個URL對應的狀態。這就意味著應用的URL和組件樹的狀態之間有一個映射關系,客戶端路由的職責就是讓這個映射關系聲明式地對應起來。

若要自己實現一個這樣的路由,看上去倒是很簡單,用hash去模擬一下,就可以自己很快地做出很簡單的路由。但事實上,客戶端路由涉及很多更復雜的問題,如下圖所示。

可能同一層的路由有多個不同的出口,還有著復雜的URL匹配規則,等等。這些問題如果都由自己去一一實現,那么復雜度是非常高的。而Vue基本都有對應的解決方案(router.vuejs.org)。配合Webpack還可以實現基于路由的懶加載,一條路徑所對應的組件在打包的時候,會分離成另外一塊,只有當該路由被訪問的時候,才會被加載出來。這有相應的解決方案,同時也有實例。
### 狀態管理
說到狀態管理,本質上就是把整個應用抽象為下圖中的循環。臉書最早提出 Flux 這個概念的時,也是一個很松散的概念,而且官方的實現本身做得很難用。所以,社區就做了各種各樣的探索。圖中的這三個東西是一個單向數據流,State 驅動 View 的渲染,而用戶對 View 進行操作產生 Action,會使State產生變化,從而導致 View 重新渲染。

一個單獨的Vue的組件,其實就已經是這樣的結構。但是當多個這樣的組件來配套的時候,就會遇到一個問題。每個組件都有它自己的狀態,但整個應用的狀態,跟組件之間并不一定存在一一對應的關系。這個狀態可能是一個全局狀態。那么狀態到底放在哪里?大部分解決方案是把這個狀態從組件樹中提取出來,放在一個全局的 Store 里面。Vuex 也是這樣做的,但是它是針對 Vue 做了特化。我們看到最左邊就是Vue的組件,這些組件在大部分情況下,就不再有私有的狀態,而是從全局的 Store 里面獲取狀態。Actions 和 Mutations 比較難用一兩句話說清楚,大致就是當應用狀態進行改變的時候,需要通過 Mutations 去顯式地觸發,而 Actions 則是負責異步和其他副作用。由于 Mutations 會被記錄下來,我們可以把這些記錄發到工具里面去做分析,甚至進行回滾。當發現bug的時候,這使得我們可以更好地理解大型應用中的狀態變化。更多的細節,還請看官方文檔(vuex.vuejs.org)。

### 構建工具
構建工具方面,Vue有一個官方的,全局安裝的 vue-cli。這里有一個筆誤。全局安裝之后,我們就可以用 vue 命令創建一個新的項目,Vue 的 CLI 跟其他 CLI 不同之處在于,有多個可選模板,有簡單的也有復雜的。極簡的配置,更快的安裝,可以更快的上手。它也有一個更完整的模板,包括單元測試在內的各種內容都涵蓋,但是,它的復雜度也更高,這又涉及到根據用例來選擇恰當復雜度的問題。所有的模板在創建之后,構建腳本都是通過 npm 腳本來執行,在國內安裝 npm 依賴的時候有點卡,可以用 yarn 或者推薦用淘寶的 npm 鏡象源,可以很大地提升安裝速度。

之前提到了單文件組件,如下圖所示,支持任意的處理器,開箱即用的熱重載,所以組件都支持熱重載 (hot-reload)。當你做了修改,不會刷新頁面,只是對組件本身進行立刻重載,不會影響整個應用當前的狀態。CSS也支持熱重載。我們看下左下角,在使用這個預處理器的同時,我們只需要添加一個 scoped 特性,Vue 會通過對模板和CSS代碼的解析改寫,來模擬CSS的效果。同時單文件組件也支持懶加載,一個懶加載的組件和它的依賴會被打包成一個額外的包,只有被用到的時候才加載,這對首屏的加載速度也是很有幫助的。

如下圖所示,這個開發者工具本身也是用Vue寫的。

使用它的話可以看到我們當前應用的組件樹結構。

點擊組件,就可以觀察這個組件當前的狀態。也可以把這個組件發送到控制臺里。同時這個開發者工具還有一個 Vuex 面板,如果你用了 Vuex,那么每次操作都會被記錄下來,記錄下來的狀態之間可以進行跳轉。除此之外,還支持把當前應用的狀態快照發送給另外一個人,這個人可以在他的控制臺里導入你發送的狀態,就可以立刻跳轉到你之前所在的狀態。這對于重現一些 bug,或要描述當前狀態都很有幫助。

### 總結
我們可能在任何情況下都需要 **聲明式的渲染功能** ,并希望盡可能避免手動操作,或者說是可變的 **命令式操作** ,希望盡可能地讓DOM的更新操作是自動的,狀態變化的時候它就應該自動更新到正確的狀態;我們需要 **組件系統** ,將一個大型的界面切分成一個一個更小的可控單元; **客戶端路由**——這是針對單頁應用而言,不做就不需要,如果需要做單頁應用,那么就需要有一個URL對應到一個應用的狀態,就需要有路由解決方案; **大規模的狀態管理** ——當應用簡單的時候,可能一個很基礎的狀態和界面映射可以解決問題,但是當應用變得很大,涉及多人協作的時候,就會涉及多個組件之間的共享、多個組件需要去改動同一份狀態,以及如何使得這樣大規模應用依然能夠高效運行,這就涉及大規模狀態管理的問題,當然也涉及到可維護性,還有 **構建工具**。現在,如果放眼前端的未來,當HTTP2普及后,可能會帶來構建工具的一次革命。但就目前而言,尤其是在中國的網絡環境下,打包和工程構建依然是非常重要且不可避免的一個環節。
- 前端新手村
- 前言
- 第1章 遇見Vue.js
- 第一個Vue.js程序
- vue嘗鮮
- 第2章 概念理解
- 漸進式框架
- 虛擬DOM
- MVVM模式
- MVX模式是什么
- 第3章 Vue基礎概覽
- 第4章 Vue內置指令詳解
- vue-text
- vue-html
- v-show
- v-if
- v-else
- v-else-if
- v-for
- v-on
- v-bind
- v-model
- v-pre
- v-cloak
- v-once
- 第5章 基礎demo小練習
- 圖書管理系統
- 頁面布局
- 列表渲染
- 功能實現
- 基于BootStrap+Vuejs實現用戶信息表
- 功能描述
- 布局實現
- 星座判斷
- 第6章 組件
- 什么是組件
- 使用組件
- Prop
- 自定義事件
- 使用Slot分發內容
- 動態組件
- 雜項
- 第7章-過渡
- 過渡效果
- 概述
- 單元素/組件的過渡
- 初始渲染的過渡
- 多個元素的過渡
- 多個組件的過渡
- 列表過渡
- 可復用的過渡
- 動態過渡
- 過渡狀態
- 狀態動畫與watcher
- 動態狀態轉換
- 通過組件組織過渡
- Render函數
- 基礎
- createElement參數
- 使用JavaScript代替模板功能
- JSX
- 函數化組件
- 模板編譯
- 自定義指令
- 簡介
- 鉤子函數
- 鉤子函數參數
- 函數簡寫
- 對象字面量
- Vuex狀態管理
- Vuex是什么?
- Vuex的安裝
- Vuex起步
- data的替代品-State和Getter
- 測試Getter
- Action-操作的執行者
- 測試Action
- 只用Mutation修改狀態
- 測試Mutations
- Vuex的基本結構
- 子狀態和模塊
- 用服務分離外部操作
- Vue-router
- Vue-router是什么
- Vue-router安裝
- 基本用法1
- 基本用法2
- Vue-cli
- Vue中的Node.js
- Vue中的npm、cnpm
- Vue中的webpack
- 安裝
- 基本使用
- 模板
- 全局API
- Vue.extend
- Vue.nextTick
- Vue.set
- Vue.delete
- Vue.directive
- Vue.filter
- Vue.component
- Vue.use
- Vue.mixin
- Vue.compile
- 附錄
- 相關網站
- 尤雨溪
- 第10章 webpack
- webpack安裝
- webpack基本使用
- webpack命令行
- webpack配置文件
- 單頁面應用SPA
- 第1章 Vue.js簡介
- 1.1 Vue.js簡介
- 1.1.1 Vue.js是什么
- 1.1.2 為什么要用Vue.js
- 1.1.3 Vue.js的發展歷史
- 1.1.4 Vue.js與其他框架的區別
- 1.2 如何使用Vue.js
- 1.2.1 第一個Vue.js程序
- 1.2.2 Vue.js小嘗鮮
- 1.3 概念詳解
- 1.3.1 什么是漸進式框架
- 1.3.2 虛擬DOM是什么
- 1.3.3 如何理解MVVM
- 第2章 基礎概覽
- 2.1 Vue實例
- 2.1.1 構造器
- 2.1.2 屬性與方法
- 2.1.3 實例生命周期
- 2.1.4 生命周期圖示
- 2.2 模板語法
- 2.2.1 插值
- 2.2.2 指令
- 2.2.3 過濾器
- 2.2.4 縮寫
- 第3章 Class與Style的綁定
- 第4章 模板渲染
- 第5章 事件詳解
- 第6章 表單控件綁定
- 第7章 指令詳解
- 7.1 內部指令
- 7.2 自定義指令
- 7.3 指令的高級選項
- 第8章 計算屬性
- 第9章 過濾器
- 第10章 組件
- 10.1 什么是組件
- 10.2 注冊組件
- 10.3 組件選項
- 10.4 組件間的通信
- 10.5 內容分發
- 10.6 動態組件