本文會從三個方面講起:css
- cli3產生的緣由
- cli3中的最佳實踐
- cli3的源碼簡析
1、cli3產生的緣由
cli1.x,2.x
看一下以前的vue-cli建立項目的方式:html
vue init webpack
複製代碼
vue-cli 的缺點也很明顯:vue
- 從多個模板中選擇一個,模板間沒有交集
- 一旦建立項目,就與vue cli沒什麼關係了,建立的配置並不能反饋到vue cli的上游
- 沒有一個可視化頁面能夠來管理咱們的vue項目,同時項目依賴的更新也是一個難題
cli3的能力
上面簡述了原有的cli的缺點,下面羅列了一些cli3的新的特色:node
- 去掉了cli2原有的config和build文件夾
- 提供了一些最佳實踐,默認使用這些最佳實踐,好比thread-loader的使用多個處理器來處理babel或者ts的編譯,默認使用cache-loader等
- 雖然有了最佳實踐,可是也不必定是咱們想要的。因此cli3提供了可配置化功能,即在根目錄的vue.config.js,經過vueconfigjs,咱們能夠依然具有了操做webpack,webpack-dev-server,loader,plugin的能力。
- cli3採用了插件機制,這裏的插件機制的解析咱們後面會詳細講到。正是由於這個插件機制,讓咱們更便於擴展,能夠看到,目前已有590個插件,包括咱們常見的一些框架與技術,好比graphql。storybook,jest等
5. 快速原型開發,是相似react生態的umi,能夠運行單個jsx文件。咱們可使用 vue serve 和 vue build 命令對單個 *.vue 文件進行快速原型開發。
6. cli3提供了圖形化界面和命令行兩種方式,經過圖形化界面,咱們能夠很直觀的管理vue項目,啓動編譯測試項目,添加插件,更新安裝依賴,更改部分配置等。值得一提的是,打包後也爲咱們分析了包的大小(webpack-bundle-analyzer),若是包較大的話,會有warning提示咱們利用webpack的code-split去拆包,達到一個更好的性能。
cli3中的最佳實踐
安利了一波vue-cli3以後,接下來咱們來看一下,咱們所說的vu-cli3爲咱們集成的最佳實踐究竟是什麼呢?react
resource hint(preload-wepack-plugin)
這個截圖是項目中打包後index.html中的狀況。能夠看到js是用link標籤引入的。 那preoload和prefetch是什麼呢?咱們來看一下官方的文檔
這兩個是resoutce hint,標識了瀏覽器對資源的處理方式,其餘的reasource hint還有DNS Prefetch ,preconnect等。
在cli3中,這個功能的實現是採用了webpack的一個插件,
preload-wepack-plugin。也就是在插入script標籤到index.html以前,獲取chunk,並對資源的引入作了改變。
動態添加polyfill(browerslist):
默認狀況下,它會把 useBuiltIns: 'usage'
傳遞給 @babel/preset-env
,這樣它會根據源代碼中出現的語言特性自動檢測須要的 polyfill。這確保了最終包裏 polyfill 數量的最小化。webpack
如何使用:
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage"
}
]
]
}
複製代碼
contenthash
webpack中對於輸出文件名能夠有三種hash值: hash chunkhash contenthash
這三者有什麼區別呢?git
- hash: 每次修改任何一個文件,全部文件名的hash都將改變。因此一旦修改了任何一個文件,整個項目的文件緩存都將失效
- chunkhash: 根據不一樣的入口文件(Entry)進行依賴文件解析、構建對應的chunk,生成對應的哈希值。將樣式做爲模塊import到JavaScript文件中的,因此它們的chunkhash是一致的,這樣就會有個問題,只要對應css或則js改變,與其關聯的文件hash值也會改變,但其內容並無改變呢,因此沒有達到緩存意義。
- contenthash: 是針對文件內容級別的,只有你本身模塊的內容變了,那麼hash值才改變
那麼,使用這種contenthash模式,再結合咱們的http緩存,咱們能夠作到針對文件級別的緩存。若是咱們的項目上線是使用一個非覆蓋式發佈,那這種模式是再適合不過了。es6
modern mode
什麼是modern mode? 官網給出了一個友好的介紹 github
es6的包加載速度更快,包體積也更小。由於瀏覽器正在新的語法作性能優化。同時去掉了babel編譯時添加的一些polyfill,體積更小。 這個也是基於babel的能力而拓展的一個功能,babel給咱們提供了
如何使用:
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"esmodules": true
}
}
]
]
}
複製代碼
實際上,vue-cli3還爲咱們集成了不少的best-practice,例如web
- webpack的devtools在dev環境使用的cheap-module-eval-source-map,在prod環境使用的source-map
cli3源碼簡析
首先安利一下vue-cli3源碼分析的文檔。可是接下來對於源碼的分析可能不會直接上代碼,我更但願經過流程圖,以一個簡單明瞭的方式,和你們闡明插件機制是如何實現的.
不一樣於以前 1.x/2.x 的 vue-cli 工具都是基於遠程模板去完成項目的初始化的工做,它屬於那種大而全的方式,當你須要完成自定義的腳手架工具時,你可能要對 vue-cli 進行源碼級別的改造,或者是在遠程模板裏面幫開發者將全部的配置文件初始化完成好。而 @vue/cli3 是基於插件機制的,它將原來的大而全的模板拆解爲如今基於插件系統的工做方式,每一個插件只須要完成本身對於項目的拓展工做
vue-cli3主要由cli和cliservice組成。
整個插件系統當中包含2個重要的組成部分:
- @vue/cli,提供 cli 命令服務,例如vue create建立一個新的項目;
- @vue/cli-service,提供了本地開發構建服務
1. cli
cli爲咱們提供了11個命令,這裏重點會講到vue create和vue add 命令
vue create
vue create是用來建立項目的,看一下vue create命令作了什麼事情
- 首先會對項目名進行驗證,使用的是validate-npm-package-name 這個包來驗證。而後會判斷項目名在當前目錄中是否已經存在,並提供了三種方式來進行操做,分別是 重寫,合併,取消。
- 接下來是獲取預設,什麼是預設呢,引用官方文檔上的話,預設就是
Vue CLI 預設配置是一個包含建立新項目所需的預約義選項和插件的 JSON 對象,讓用戶無需在命令提示中選擇它們。
也就是說,咱們上一次建立的項目配置,能夠保留在一個文件(也就是vuerc)中,下一次建立時,就可使用這些配置,而不用從新在命令行中再次操做,再去選擇一次。
這第二步就是獲取咱們須要的配置。
使用到了inquirer
,inquirer.js
是一個nodejs模塊,是用戶與命令行交互的工具
3. 接下來是依賴安裝。
在上一步中咱們已經知道了須要的插件,cli幫咱們生成pkg.json,並經過yarn或者npm安裝依賴,這裏使用了promise.race,經過獲取同個npm包來看看是默認鏡像仍是淘寶鏡像返回更快,那個更快就使用哪一個。
4. 最後一步就是generator,也就是生成咱們的文件,又分別作了如下的操做.
- i. 調用各個插件,各個插件均可以對咱們的項目進行修改
- ii. 修改完了以後,合併配置(由於各個插件都擁有改變文件的能力,因此須要將改動合併起來)
- iii. 先將整個項目的文件在內存中生成好
- iv. 生成文件
vue add
以上講的是關於vue create的,接下來咱們看一下vue add命令,add命令是用來添加插件的。經過add的命令,cli3會幫咱們完成插件的下載,安裝以及執行插件所提供的 generator,咱們來看一下流程圖
- 安裝插件和依賴
- 加載generator: 能夠來修改pkg.json中的字段,或者是基於ejs模板來修改文件,或者是生成一個新的文件
- 加載並調用Prompts:這是一個對話模塊,底層使用 inquirer 進行展現。若是這個插件在其根目錄包含一個 prompts.js,那麼它將會用在該插件被初始化調用的時候。Inquirer.js會幫咱們解析 這些被解析的答案對象會做爲選項被傳遞給插件的 generator
- run generator: 使用上一步的選項來生成,修改文件
2. cli-service
接下來咱們來看一下@vue/cli-service 內部是如何搭建整個插件系統的。就拿執行npm run serve啓動本地開發服務來講,大概流程是這樣的:
- 實例化 Service類
- 在實例化 Service的過程中完成了兩個比較重要的工做:
- 加載插件(resolvePlugin:加載@vue/cli-service 內部提供的插件以及項目應用當中須要使用的插件,內部插件又分爲兩類,一類插件在內部動態註冊新的 CLI 命令,開發者便可經過 npm script 的形式去啓動對應的 CLI 命令服務,一類插件主要是完成 webpack 本地編譯構建時的各類相關的配置。cli-service 將 webpack 的開發構建功能收斂到內部來完成)
- Service 實例化完成後,調用實例上的 run方法來執行全部被加載的插件
總結: cli-service 將基於 webpack 的本地開發構建配置收斂至內部來實現,當你沒有特殊的開發構建需求的時候,內部配置能夠開箱即用,不用開發者去關心一些細節。固然在實際團隊開發當中,內部配置確定是沒法知足的,得益於插件構建設計,開發者不須要重構內部的配置,而是直接使用 @vue/cli-service 暴露出來的 API 去完成對於特殊的開發構建需求。
最後
- 舉個插件的栗子: 傳送門: vue-cli-plugin-element 儘管我以爲vue-cli-plugin-element能夠作得更好,好比自定義選擇須要添加的組件,自定義主題色(vue ui的交互頁面實際上是提供了顏色選擇器的,以下圖),模板選擇等
2. webpack,vue-cli,vue都使用到了插件機制。面向接口編程,確實是很優秀的想法。這個也是插件模式的一種體現
插件(Plugin)模式向用戶提供了一種擴展程序的接口,用戶能夠在程序本體以外,按照指定接口編寫插件來爲程序增長功能
參考
vueconf 2018
vue-cli 3.0 源碼分析
Vue-cli@3.0 插件系統簡析