webpack4搭建的一次嘗試

啊啊嗯嗯發表於2019-03-22

目前支援typescript,react,jsx,less,eslint,prettier,husky等

具體的配置請檢視:github.com/nvnvyezi/in…

如需搭配electron,可以檢視:github.com/nvnvyezi/el…

好了,正文開始,這不是一個文章,只作為想自己搭建webpack的一個參考

概念

Entry:指定webpack開始構建的入口模組,從該模組開始構建並計算出直接或間接依賴的模組或者庫

Output:告訴webpack如何命名輸出的檔案以及輸出的目錄

Loaders:由於webpack只能處理javascript,所以我們需要對一些非js檔案處理成webpack能夠處理的模組,比如sass檔案

PluginsLoaders將各型別的檔案處理成webpack能夠處理的模組,plugins有著很強的能力。外掛的範圍包括,從打包優化和壓縮,一直到重新定義環境中的變數。但也是最複雜的一個。比如對js檔案進行壓縮優化的UglifyJsPlugin外掛

Chunk:coding split的產物,我們可以對一些程式碼打包成一個單獨的chunk,比如某些公共模組,去重,更好的利用快取。或者按需載入某些功能模組,優化載入時間。在webpack3及以前我們都利用CommonsChunkPlugin將一些公共程式碼分割成一個chunk,實現單獨載入。在webpack4 中CommonsChunkPlugin被廢棄,使用SplitChunksPlugin

webpack啟動引數

  • color 輸出結果帶彩色,比如:會用紅色顯示耗時較長的步驟
  • profile 輸出效能資料,可以看到每一步的耗時
  • progress 輸出當前編譯的進度,以百分比的形式呈現
  • display-modules 預設情況下 node_modules 下的模組會被隱藏,加上這個引數可以顯示這些被隱藏的模組
  • display-error-details 輸出詳細的錯誤資訊

webpack4特性

  • Webpack4需要與webpack-cli一起使用
  • 新添了mode屬性,
    • development
      • Process.env.NODE_ENV = development,需在啟動時設定NODE_ENV=development,否則在編譯過程中取不到該值
      • 預設開啟以下外掛,利用持久化快取
      • namedChunksPlugin:以名稱固化chunk id,
      • namemodulesPlugin:以名稱固化module id
    • production
      • Process.env.NODE_ENV = production,同樣需配置
      • 預設開啟以下外掛
      • SideEffectsFlagPluginUglifyJsPlugin 用於 tree-shaking
      • FlagDependencyUsagePlugin :編譯時標記依賴
      • FlagIncludedChunksPlugin :標記子chunks,防子chunks多次載入
      • ModuleConcatenationPlugin :作用域提升(scope hosting),預編譯功能,提升或者預編譯所有模組到一個閉包中,提升程式碼在瀏覽器中的執行速度
      • NoEmitOnErrorsPlugin :在輸出階段時,遇到編譯錯誤跳過
      • OccurrenceOrderPlugin :給經常使用的ids更短的值
      • SideEffectsFlagPlugin :識別 package.json 或者 module.rules 的 sideEffects 標誌(純的 ES2015 模組),安全地刪除未用到的 export 匯出
      • UglifyJsPlugin :刪除未引用程式碼,並壓縮
  • 移除 CommonsChunkPlugin外掛,取而代之的是兩個新的配置項(optimization.splitChunks 和 optimization.runtimeChunk)
  • module.loaders 替換為 modules.rules
  • webpack4 增加了 WebAssembly 的支援,可以直接 import/export wasm 模組,也可以通過編寫 loaders 直接 import C++/C/Rust

splitChunks

可以參考https://segmentfault.com/a/1190000013476837

extra-text-webpack-plugin

關於[hash][chunkhash]的區別,簡單來說,[hash]是編譯(compilation)後的hash值,compilation物件代表某個版本的資源對應的編譯程式。專案中任何一個檔案改動,webpack就會重新建立compilation物件,然後計算新的compilation的hash值,所有的編譯輸出檔名都會使用相同的hash指紋,改一個就一起變。而[chunkhash]是根據具體模組檔案的內容計算所得的hash值,某個檔案的改動只會影響它本身的hash指紋,不會影響其他檔案。

hash/chunkhash:

hash:在 webpack 一次構建中會產生一個 compilation 物件,該 hash 值是對 compilation 內所有的內容計算而來的

與整個專案的構建有關。只要整個專案中有檔案更改,就會產生新的hash值,並且所有的檔案共用一個hash值。

hash一般是結合CDN快取來使用,通過webpack構建之後,生成對應檔名自動帶上對應的MD5值。如果檔案內容改變的話,那麼對應檔案雜湊值也會改變,對應的HTML引用的URL地址也會改變,觸發CDN伺服器從源伺服器上拉取對應資料,進而更新本地快取。但是在實際使用的時候,這幾種hash計算還是有一定區別。

compiler物件代表的是配置完備的Webpack環境。 compiler物件只在Webpack啟動時構建一次,由Webpack組合所有的配置項構建生成。

compilation物件代表某個版本的資源對應的編譯程式。當使用Webpack的development中介軟體時,每次檢測到專案檔案有改動就會建立一個compilation,進而能夠針對改動生產全新的編譯檔案。compilation物件包含當前模組資源、待編譯檔案、有改動的檔案和監聽依賴的所有資訊。

compiler代表配置好的webpack環境,compilation針對隨時可變得專案檔案,只要檔案有改動就會重新建立。

chunkhash:每一個 chunk 都根據自身的內容計算而來。

根據不同的entry進行檔案依賴分析,構建對應的chunk,生成對應的hash值,即可以將一些公共庫單獨打包構建,只要不改動公共庫的程式碼,生成的hash值就不變。

保證了線上上構建的時候只要檔案內容沒有更改就不會重複構建

contenthash:在js中引用css時,保證css不變的情況下hash值不變

juejin.im/post/5a4502…

分析webpack中的Compiler/Compilation/Stats物件及構建順序:github.com/liangklfang…

tsconfig.json

概述

如果一個目錄下存在一個tsconfig.json檔案,那麼它意味著這個目錄是TypeScript專案的根目錄。tsconfig.json檔案中指定了用來編譯這個專案的根檔案和編譯選項。 一個專案可以通過以下方式之一來編譯:

使用tsconfig.json

  • 不帶任何輸入檔案的情況下呼叫tsc,編譯器會從當前目錄開始去查詢tsconfig.json檔案,逐級向上搜尋父目錄。
  • 不帶任何輸入檔案的情況下呼叫tsc,且使用命令列引數--project(或-p)指定一個包含tsconfig.json檔案的目錄。

當命令列上指定了輸入檔案時,tsconfig.json檔案會被忽略

配置示例

{
  "compilerOptions": {
    "module": "commonjs",
    "noImplicitAny": true,
    "removeComments": true,
    "preserveConstEnums": true,
    "sourceMap": true
  },
  "files": [
    "app.ts",
    "foo.ts",
  ],
  // 或者使用include,exclude指定待編譯檔案
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
  ]
}
複製程式碼

compilerOptions 用來配置編譯選項,files 用來指定待編譯檔案。

這裡的待編譯檔案是指入口檔案,任何被入口檔案依賴的檔案

files 屬性是一個陣列,陣列元素可以是相對檔案路徑和絕對檔案路徑。

includeexclude 屬性也是一個陣列,但陣列元素是類似 glob 的檔案模式。它支援的 glob 萬用字元包括:

  • * :匹配 0 或多個字元(注意:不含路徑分隔符)
  • ? :匹配任意單個字元(注意:不含路徑分隔符)
  • **/ :遞迴匹配任何子路徑

TS 檔案指擴充名為 .ts.tsx.d.ts 的檔案。如果開啟了 allowJs 選項,那 .js.jsx 檔案也屬於 TS 檔案。

如果僅僅包含一個 * 或者 .* ,那麼只有TS 檔案才會被包含。

如果 filesinclude 都未設定,那麼除了 exclude 排除的檔案,編譯器會預設包含路徑下的所有 TS 檔案

如果同時設定 filesinclude ,那麼編譯器會把兩者指定的檔案都引入。

如果未設定 exclude ,那其預設值為 node_modulesbower_componentsjspm_packages 和編譯選項 outDir 指定的路徑。

exclude 只對 include 有效,對 files 無效。即 files 指定的檔案如果同時被 exclude 排除,那麼該檔案仍然會被編譯器引入。

前面提到,任何被 filesinclude 引入的檔案的依賴會被自動引入。 反過來,如果 B.tsA.ts 依賴,那麼 B.ts 不能被 exclude 排除,除非 A.ts 也被排除了。

有一點要注意的是,編譯器不會引入疑似為輸出的檔案。比如,如果引入的檔案中包含 index.ts ,那麼 index.d.tsindex.js 就會被排除。通常來說,只有擴充名不一樣的檔案命名法是不推薦的。

tsconfig.json 也可以為空檔案,這種情況下會使用預設的編譯選項來編譯所有預設引入的檔案。

官方編譯選項列表:www.tslang.cn/docs/handbo…

可以參考:www.tslang.cn/docs/handbo…

tslint/eslint選擇

ts.xcatliu.com/engineering…

Eslint規則:eslint.cn/docs/user-g…

參考掘金的eslint;juejin.im/post/5b3859…

react配置eslint一些規則: segmentfault.com/a/119000001…

eslint 配置項
  • root 限定配置檔案的使用範圍
  • parser 指定eslint的解析器
  • parserOptions 設定解析器選項
  • extends 指定eslint規範
  • plugins 引用第三方的外掛
  • env 指定程式碼執行的宿主環境
  • rules 啟用額外的規則或覆蓋預設的規則
  • globals 宣告在程式碼中的自定義全域性變數

git提交檢測/husky/pre-commit/lint-staged

husky繼承了Git下所有的鉤子,在觸發鉤子的時候,husky可以阻止不合法的commit,push等等。

husky是一個npm包,安裝後,可以很方便的在package.json配置git hook 指令碼 。

比如,在 package.json 內配置如

 "scripts": {
    "lint": "eslint src"
  },
  "husky": {
    "hooks": {
      "pre-commit": "npm run lint"
    }
  },
複製程式碼

那麼,在後續的每一次git commit 之前,都會執行一次對應的 hook 指令碼npm run lint 。其他hook同理

pre-commit:github.com/PaicFE/blog…

lint-staged: github.com/okonet/lint…

prettire配置

prettier.io/docs/en/opt…

優化

  1. include & exclude
  2. babel-loader
  • cacheDirectory:預設值為 false。當有設定時,指定的目錄將用來快取 loader 的執行結果。之後的 webpack 構建,將會嘗試讀取快取,來避免在每次執行時,可能產生的、高效能消耗的 Babel 重新編譯過程(recompilation process)。如果設定了一個空值 (loader: 'babel-loader?cacheDirectory') 或者 true (loader: babel-loader?cacheDirectory=true),loader 將使用預設的快取目錄 node_modules/.cache/babel-loader,如果在任何根目錄下都沒有找到 node_modules 目錄,將會降級回退到作業系統預設的臨時檔案目錄。
  1. 引入babel runtime,避免重複引入
  2. resolve.modules解析時搜尋的目錄
  3. resolve.mainFields用於配置第三方模組使用那個入口檔案

安裝的第三方模組中都會有一個 package.json檔案,用於描述這個模組的屬性,其中有些欄位用於描述入口檔案在哪裡,resolve.mainFields 用於配置採用哪個欄位作為入口檔案的描述。

可以存在多個欄位描述入口檔案的原因是因為有些模組可以同時用在多個環境中,針對不同的執行環境需要使用不同的程式碼。 以 isomorphic-fetch API 為例,它是 Promise的一個實現,但可同時用於瀏覽器和 Node.js 環境。

  1. resolve.alias配置項通過別名來把原匯入路徑對映成一個新的匯入路徑

  2. resolve.extensions在匯入語句沒帶檔案字尾時,webpack自動帶上字尾去嘗試查詢檔案是否存在

    1. 字尾列表儘可能小
    2. 頻率最高的往前方
    3. 匯出語句裡儘可能帶上字尾
  3. module.noParse讓webpack忽略對部分沒采用模組化的檔案的遞迴解析處理

    1. 被忽略掉的檔案裡不應該包含 import 、 require 、 define 等模組化語句
  4. 使用動態連結庫

    1. webapck.DllPlugin
      1. context (optional): manifest 檔案中請求的上下文(context)(預設值為 webpack 的上下文(context))
      2. name: 暴露出的 DLL 的函式名 (TemplatePaths: [hash] & [name] )
      3. path: manifest json 檔案的絕對路徑 (輸出檔案)`
    2. webpack.DllReferencePlugin
      1. context: (絕對路徑) manifest (或者是內容屬性)中請求的上下文
      2. manifest: 包含 contentname 的物件,或者在編譯時(compilation)的一個用於載入的 JSON manifest 絕對路徑
      3. content (optional): 請求到模組 id 的對映 (預設值為 manifest.content)
      4. name (optional): dll 暴露的地方的名稱 (預設值為 manifest.name) (可參考 externals)
      5. scope (optional): dll 中內容的字首
      6. sourceType (optional): dll 是如何暴露的 (libraryTarget)
    3. libraryTargetlibrary
      1. output.libraryTarget 配置以何種方式匯出庫。
      2. output.library 配置匯出庫的名稱。 它們通常搭配在一起使用。
    4. 推薦參考: segmentfault.com/a/119000001…
  5. HappyPack: 讓Webpack把任務分解給多個子程式去併發的執行,子程式處理完後再把結果傳送給主程式。

    1. id: String 用唯一的識別符號 id 來代表當前的 HappyPack 是用來處理一類特定的檔案.

      loaders: Array 用法和 webpack Loader 配置中一樣.

      threads: Number 代表開啟幾個子程式去處理這一型別的檔案,預設是3個,型別必須是整數。

      verbose: Boolean 是否允許 HappyPack 輸出日誌,預設是 true。

      threadPool: HappyThreadPool 代表共享程式池,即多個 HappyPack 例項都使用同一個共享程式池中的子程式去處理任務,以防止資源佔用過多。

      verboseWhenProfiling: Boolean 開啟webpack --profile ,仍然希望HappyPack產生輸出。

      debug: Boolean 啟用debug 用於故障排查。預設 false

  6. ParallelUglifyPlugin:可以把對JS檔案的序列壓縮變為開啟多個子程式並行執行&oq=可以把對JS檔案的序列壓縮變為開啟多個子程式並行執行

  7. 在支援es6的環境中直接使用es6,不進行轉換

  8. splitChunks: 提取公共程式碼

  9. 區分環境,使用mode

  10. 程式碼分離

相關文章