無痛學會各種 2 的 Vue2+Vuex2+Webpack2 前後端同構渲染
感謝有人看我扯技術,這篇文章主要介紹最近非常火的vue2前端框架的特點和vue2+vuex2+webpack2各種2的前後端同構渲染架構搭建流程,最後會附上程式碼,文章想到啥寫啥,如果存在錯誤,或者大家有什麼意見建議,歡迎大家指出來
Vue2 和 Vue1 的對比
vue2出來之後,基本上逛論壇逛技術群都能看到各種文章,各種討論 ,一時間大家都在學習vue2了 ,我今年年初就開始接觸vue,最初也是在react ,angular,vue 之中對比選擇,最終選擇了vue,因為其對前端比較友好(使用正常的模板,而不是JSX)、概念及學習成本相對簡單(對於團隊開發,引入技術必須要考慮其學習成本),下面介紹下我理解的vue2和vue1的不同之處 ,如有不足,歡迎補充:
1.引入了virtual Dom
在vue1中,資料和檢視的繫結流程是通過
Object.defineProperty
將資料轉化為getter/setter
,getter/setter
中加入watcher
,當對資料進行操作的時候,setter
的watch
被觸發重新計算,然後更新和這個資料有關聯的dom元素,這就是vue1的資料驅動檢視原理
在vue2中,資料的繫結和觸發和vue1相同,基本原理都是通過Object.defineProperty
對資料加入'鉤子',以便在資料發生變化的時候得以響應,而在響應之後,不像vue1一樣直接更新dom元素,而是放入virtual Dom
中,進行比對計算,然後對dom元素做相應的處理。下面是vue1和vue2的響應流程對比vue:
vue2:
關於
virtual Dom
: 虛擬dom最初是在react上面認識的,其實做的事情就是在js記憶體中建立好dom的結構,然後再更新虛擬dom時做差異比對,將差異的地方真正更新到頁面中,做到最小化頁面的渲染。當然,也不是說對於所有情景,虛擬dom的效能都是最好的,畢竟比起直接操作dom元素,他還是需要在記憶體中進行計算,因此對於少量的元素更新,可能其效能比起直接操作dom元素要差。當然虛擬dom的引入,不只是在效能方面的考慮,虛擬dom可以帶來程式設計的變化,比如你可以使用render
方法直接建立新的節點,虛擬dom也是vue2可以進行服務端渲染的關鍵,由於虛擬dom是在記憶體重點,vue2的ssr可以將虛擬dom直接生成html的字串,從而實現ssr。除此之外,vue2 從模板到 virtuel-DOM 的編譯階段使用了一些高階優化:(1). 它會檢測出靜態的
class
名和attributes
這樣它們在初始化渲染之後就永遠都不會再被比對。
(2). 它會檢測出最大靜態子樹 (就是不需要動態性的子樹) 並且從渲染函式中萃取出來。這樣在每次重渲染的時候,它就會直接重用完全相同的virtual nodes
同時跳過比對。
這些高階優化通常只會在使用JSX
時通過Babel plugin
來做,但是 vue2 即使在使用瀏覽器內的編譯器時也能做到。
2.元件事件傳遞機制的改變,元件資料雙向繫結的去除
vue2元件廢除了
$dispath/$broadcast
父子元件的事件傳播方式,廢除了過濾器,props
引數等的資料雙向繫結以及處理功能,說明作者希望使用者通過建立全域性的狀態管理,事件管理機制,通過使用事件中心,允許元件自由交流,無論元件處於元件樹的哪一層,將狀態管理集中在一起處理,官方提供的vuex
就是用來幾種管理狀態的。
3.服務端渲染 ssr:server-side-render
由於
virtual dom
的引入,使得vue的服務端渲染成為了可能,下面是官方vue-server-renderer
提供的渲染流程圖:可以看出vue的
server-side-render
有三部分組成,一部分是頁面的原始碼(source),還有node層的渲染部分和瀏覽器端的渲染部分。
source
分為兩種entry point
,一個是前端頁面的入口client entry
,主要是例項化Vue物件,將其掛載到頁面中;另外一個是後端渲染服務入口server entry
,主要是控服務端渲染模組回撥,返回一個Promise物件,最終返回一個Vue物件(經過測試,直接返回Vue物件也是可以的);前面的
source
部分就是業務開發的程式碼,開發完成之後通過webpack
進行構建,生成對應的bundle,這裡不再贅述client bundle
,就是一個可在瀏覽器端執行的打包檔案;這裡說下server bundle
, vue2提供vue-server-renderer
模組,模組可以提供兩種render:rendererer/bundleRenderer
,下面分別介紹下這兩種render。
renderer
接收一個vue物件 ,然後進行渲染,這種對於簡單的vue物件,可以這麼去做,但是對於複雜的專案,如果使用這種直接require一個vue物件,這個對於服務端程式碼的結構和邏輯都不太友好,首先模組的狀態會一直延續在每個請求渲染請求,我們需要去管理和避免這次渲染請求的狀態影響到後面的請求,因此vue-server-renderer
提供了另外一種渲染模式,通過一個bundleRenderer
去做渲染。
bundleRenderer
是較為複雜專案進行服務端渲染官方推薦的方式,通過webpack以server entry
按照一定的要求打包生成一個server-bundle
,它相當於一個可以給服務端用的app的打包壓縮檔案,每一次呼叫都會重新初始化 vue物件,保證了每次請求都是獨立的,對於開發者來說,只需要專注於當前業務就可以,不用為服務端渲染開發更多的邏輯程式碼。
renderer生成完成之後,都存在兩個介面,分別是renderToString
和renderToStream
,一個是一次性將頁面渲染成字串檔案,另外一個是流式渲染,適用於支援流的web伺服器,可以是請求服務的速度更快
4.除了上面說的那些不同,vue2在生命週期管理,動畫機制等地方都與vue有些差別,具體請瀏覽migration
Vuex2 和 Vuex 、Webpack2 和 Webpack 的不同之處
vuex
相對於 vue2和vue 較大改動,vue的狀態管理工具 vuex的改動不是很大,底層改動由於時間關係還沒有來得及細究,但是在使用方面多了幾個 Helper,利用ES6的展開函式可以更加方便的使用
state,getters,mutations,actions
。下面簡單介紹下vuex各個部分的概念
state
是一個全域性的狀態儲存,資料會儲存在其中,vue元件可以直接訪問其中的值,但是隻可以讀,不可以進行寫操作
getter
,有些時候我們需要對獲取的資料進行加工,而不是直接獲取state中的資料,這時候可以通過getter定義函式,返回對應的資料
mutations
是vuex中唯一一個可以修改資料的地方,mutations
可以定義事件函式,在vue元件中可以通過commit發射事件,呼叫函式。需要注意的是,mutations
中的操作必須是同步的,不可以存在非同步操作的情況。
actions
和mutation
比較相似,不同的是actions中不直接修改state,而是通過commit呼叫mutations修改資料,而且actions中可以存在非同步處理邏輯
webpack
webpack2
和webpack
對比,有以下的新特性:
ES6 Modules : webpack 2 已經支援原生的 ES6 的模組載入器了,這意味著 webpack 2 能夠理解和處理 import和export了,而不用把他們轉化成 CommonJS 來處理了。
用 ES6 來做程式碼拆分 : ES6 的模組載入器定義了System.import這一個方法,System.import能夠在執行時動態載入 ES6 模組。
混合使用 ES6 和 AMD 和 CommonJS (Mixing ES6 with AMD and CommonJS)
更加具體的新特性可以瀏覽連結地址
從零開始搭專案
好了,前面扯了那麼多東西,估計沒什麼人看,我們還是迴歸題目,開始敲程式碼吧,哈哈,接下來我會使用vue2 + vuex2 + webpack2 搭建一個簡單的 ssr專案,能夠直出頁面,還能夠儲存成靜態檔案。雖然官方頁面響應的例項vue-hackernews-2.0,但是如果一開始把程式碼下下來,還是不太容易理解的,所以我參考其例子,從零開始搭建專案,原始碼在文章的最後
首先當然是使用
npm init
新建一個專案
然後往package.json
中寫入下列依賴
然後執行
npm i
,然後去上個廁所,喝杯茶也行,等所有的依賴安裝完畢
介紹下一些模組的作用vue,vuex
為vue專案使用的基本框架,express,vue-server-renderer,serialize-javascript
為服務端渲染使用的模組,babel-*
為ES6轉換成ES5模組,其他的webpack*,*-loader
為webpack構建所需要的模組,如果需要專案學習webpack的使用,可以閱讀官方文件安裝階段完成了,下面進入愉快的編碼階段了,其實按照流程應該是編碼同時搞定打包、開發環境配置等工作的,為了文章的效果,就分開說了
製作的頁面是一個沒有什麼業務情景的頁面(請原諒我,想到什麼就寫什麼程式碼了),主要是為了演示元件的引用流程,vuex狀態管理以及引用,狀態改變之後的檢視更新,非同步操作的檢視更新,所以,當你下了原始碼,大概頁面,你會看到下面這個奇奇怪怪的東西:
好了不要在意這些細節了,我們來看看這個怪怪的東西是怎麼出來的,先展示下專案最終的目錄結構:
其中
index.html
就是頁面最終生成頁的模板頁,裡面有簡單的頭部資訊和佔位符,可以在服務端渲染後進行內容替換app.js
就是頁面的邏輯入口檔案,Vue物件在這裡例項化,其中使用的store,route可以在例項化中傳入可以看到
app.js
引用了App.vue
元件,.vue
是vue官方推出的單檔案開發方式,配合webpack的vue-loader
可以方便的實現模組化開發,.vue
檔案在打包的時候會被編譯成為一個js物件,裡面包含一個render
方法,用於渲染頁面,下面是App.vue
檔案
可以看到頁面包含三個部分
template 、script 、style
,其中template為元件使用的模板,現在vue2除了使用template,還可以使用JSX和js模板,但是相對其他兩種,template對於前端開發者來說還是比較直觀的,script為元件的邏輯部分,使用es6的進行模組化,構建的時候會使用Vue.extend生成一個元件,style為頁面的樣式部分,可以指定lang
來宣告使用的樣式語法型別,可以用原生的css,也可以用stylus,sass等等,只要配置不同的webpack loader進行進行編譯就行了,另外可以指定scoped
,使得元件中的樣式只對元件生效,不會影響其他元件,不用擔心命名重複的問題,其原理是在生成的時候為標籤生成一段隨機數(沒研究過生成數的演算法),並且為選擇器加上對應隨機數的屬性選擇器。可以看到元件
import
另外一個List.vue
元件,並且在components
中進行了引用,template
中進行了引用,這就實現了元件的巢狀和複用,下面是List.vue
檔案
這個檔案使用了vuex管理的資料,在此前的版本的vuex中,在元件使用資料需要寫很多的
computed,methods
,在新版本的可以配合ES6的展開函式和vuex的helper,簡寫很多函式,元件部分內容就說到這裡了,可能有人會說啥是computed,啥是methods,這些通通自己看文件,
總的來說,寫一個元件需要了解下面幾點:
模板指令,例如
v-for,v-bind,v-on
等資料使用配置屬性
data,computed,props,watch
等元件的生命週期屬性
created,mounted
等全域性方法
Vue.set,Vue.nextTick
等進階開發: 動畫效果,自定義指令,自定義外掛,混合組件開發
下面說下用vuex做狀態管理,下面是
store/index.js
檔案
可以看到使用vuex需要在
Vue.use
中引入,然後例項化一個Vuex.Store
物件就可以了,物件中需要定義state,actions,mutations,getters
等內容,這樣子就可以建立一個全域性的狀態管理機制,可以從應用的頂端去處理資料,各個元件中對資料進行操作也是通過事件直接傳遞到Vuex中進行資料更新,然後再進行響應到其他使用同個資料的元件中,進行檢視更新。專案的邏輯程式碼已經完成了,但是對照上面ssr的概念,會發現還少了兩個webpack的
entry point
,一個是前端程式碼的入口,可以是供服務端渲染的入口,下面是前端client-entry.js
檔案
檔案引入了
app.js
,判斷如果在服務端渲染時已經寫入狀態,則將vuex的狀態進行替換,使得服務端渲染的html和vuex管理的資料是同步的。然後將vue例項掛載到html指定的節點中。下面是
server-entry.js
檔案這裡面服務端會傳過來一個context物件,可以從獲取資訊,也可以寫入資訊,可以看到將現有的vuex state賦值給context,給服務端渲染使用,最後返回vue物件(文件中寫著需要返回一個Promise物件,物件中再返回一個vue物件,經過實驗直接返回也是可以的,如果應用中存在非同步操作會影響檢視和vuex資料狀態,那麼應該返回一個Promise物件,使得服務端得到的vue物件是最後資料和檢視同步統一的)
程式碼擼完了,下面要讓他跑起來了,配置分為兩部分,一個是webpack打包的配置,一個是服務端渲染伺服器的搭建,這裡使用express進行伺服器的簡單搭建,不涉及任何負載均衡和效能優化問題,下面分別說說這兩個部分 :
webpack打包:webpack打包主要有三個檔案
webpack.base.config.js
,webpack.client.config.js
,webpack.server.cofnig.js
,其中base
主要配置了對應檔案型別的loader,還有指定了entry的切割點,將業務程式碼和庫,client
指定了client-entry.js
作為entry point ,還將庫檔案和業務檔案進行分別打包,還有一些圖片處理,程式碼壓縮的工作。server
指定了server-entry.js
作為entry point,並且指定了打包了型別標準是commonjs2
,供服務端渲染模組使用。而在開發過程中,可以使用
webpack-hot-middleware/client
,webpack-hot-middleware
去實現程式碼的watch和重新構建雙端的程式碼的流程,是得開發更加便捷,具體配置在setup-dev-server.js
中最後說一下服務端渲染的伺服器配置,服務端部分使用
vue-server-renderer
模組的createBundleRenderer
通過傳入剛才webpack生成的server-bundler
去生成一個bundleRenderer
,就可以呼叫renderToStream
或者renderToString
渲染頁面了,具體配置在server.js
中具體怎麼跑起專案可以看下
package.json
的scripts屬性,其中dev
是開發用,start
是正式環境動態生成頁面用,build
可以直接生成client-bundler
和server-bundler
基本上比較完整的vue2 前段端同構渲染已經介紹完了,下面說下我對框架的看法,前端框架這個東西基本上就是一時火一個,我們在學習新東西的同時也應該不忘老本,要有自己的技術棧和工作流,就像《人月神話》中的一句話說的好,沒有解決任何事情的銀彈,對於不同的專案,不同的業務情景,應該採取不同的框架,使用最合適的開發架構去開發。
附上程式碼 點我點我,給個star唄~