webpack從入門到放棄

futurewan發表於2018-03-07

webpack構建流程

從啟動webpack構建到輸出結果經歷了一系列過程:

  1. 解析webpack.config.js配置引數,呼叫shell並追加命令列引數,通過 optimist將前兩者引數整合成 options 物件傳到了下一個流程的控制物件中
  2. 註冊所有配置的外掛,讓外掛監聽webpack構建生命週期的事件節點,以做出對應的反應。
  3. 從配置的entry入口檔案開始解析檔案構建AST語法樹,找出每個檔案所依賴的檔案,遞迴下去。
  4. 在解析檔案遞迴的過程中根據檔案型別和loader配置找出合適的loader用來對檔案進行轉換。
  5. 遞迴完後得到每個檔案的最終結果,根據entry配置生成程式碼塊chunk。
  6. 輸出所有chunk到檔案系統。

入口和上下文(entry and context)

entry

指示 webpack 應該使用哪個模組,來作為構建其內部依賴圖的開始

  • 單入口

entry: './src/app.js'

等同於下面寫法:

 entry: {
    main: './src/app.js'
  }
複製程式碼
  • 物件語法

分離 應用程式(app) 和 第三方庫(vendor) 入口

  entry: {
    app: './src/main.js',
    vendor: './src/jquery.js'
  }
複製程式碼

這告訴我們 webpack 從 main.js 和 jquery.js 開始建立依賴圖(dependency graph)。這些依賴圖是彼此完全分離、互相獨立的。

context

webpack 編譯時的基礎目錄,entry和loader 會相對於此目錄查詢

預設值為當前目錄,不建議修改

出口(output)

告訴 webpack 在哪裡輸出它所建立的 bundles

output: {
    path: path.join(__dirname, 'dist'),
    publicPath: '/',
    filename: 'js/[name].js'
}
複製程式碼

publicPath

用於指定打包後的檔案需要載入的外部資源(如圖片、js、css等)的跟路徑

預設值是一個空字串 "",通常設定成"/"
靜態資源最終訪問路徑 = output.publicPath + 資源loader或外掛等配置路徑

  • loader 輸出圖片檔案配置
{ name: 'imgs/[name].[ext]' }
// 那麼圖片最終的訪問路徑為
output.publicPath('/') + 'imgs/[name].[ext]' = '/imgs/[name].[ext]'
複製程式碼
  • plugin 提取css檔案配置:
new ExtractTextPlugin('css/[name].[contenthash:10].css')
// html中載入css打包後程式碼
<link href="/css/app.9502b0c565.css" rel="stylesheet">
複製程式碼
  • html中載入js打包後程式碼
<script type="text/javascript" src="/js/runtime.4ece365fd5.js"></script>
複製程式碼

path

打包檔案輸出的目錄

建議絕對路徑;預設值為當前路徑。
path 中用使用 [hash] 模板可用於版本回歸

output: {
    path: path.resolve('./dist/[hash:8]/')
}
複製程式碼

loader

loader 讓 webpack 能夠去處理那些非 JavaScript 檔案(webpack 自身只理解 JavaScript)

注意:module.loaders 改為 module.rules;鏈式loader

  • webpack1語法
module: {
    loaders: [{
        test: /\.less$/,
        loader: "style!css!less"
    })
}
複製程式碼
  • webpack2、3語法
module: {
    rules: [{
        test: /\.less$/,
        use: [
            "style-loader",
            "css-loader",
            "less-loader"
        ]
    }]
}
複製程式碼

檔案類

raw-loader 載入檔案原始內容,比如.txt檔案

file-loader 將檔案傳送到打包後資料夾中

{
    test: /\.(gif|png|jpe?g|svg)$/,
    use: [{
        loader: 'file-loader',
        options:{}
    }]
}
複製程式碼

預設輸出到output的根目錄下,name為32為hash值

名稱 型別 預設值 描述
name {String | Funciton} [hash].[ext] 自定義檔名

placeholders

名稱 型別 預設值 描述
[ext] {String} file.extname 資源副檔名
[name] {String} file.name 資源名
[path] {String} file.dirname 資源路徑
[hash] {String} md5 內容的雜湊值

url-loader 功能類似file-loader

對file-loader的擴充套件,可以設定小圖片轉換base64圖片

{
    test: /\.(png|jpg|gif)$/,
        use: [
        {
            loader: 'url-loader',
            options: {
                limit:5000,
                name: 'img/[name].[ext]?[hash]'
            }
        }
    ]
}
複製程式碼

轉換編譯類

script-loader

在全域性上下文(global context)執行一次 JS指令碼,就像你在網頁上通過<script>把它們引進來一樣。

babel-loader

由於瀏覽器只能讀懂ES5語法,需要babel將ES2015+語法編譯為ES5語法

  1. 安裝
    npm install babel-loader babel-core babel-preset-env webpack
  2. 用法
{
    test: /\.js$/,
    use: {
        loader: 'babel-loader',
        exclude: /node_modules/,
        options: {}
    }
}
複製程式碼

options

  • cacheDirectory:預設值為 false。當有設定時,指定的目錄將用來快取 loader 的執行結果
  • babelrc 預設true,設定false,.babelrc將不會啟用

ts-loader 像 JavaScript 一樣載入 TypeScript 2.0+

模版類

html-loader 解決html里載入圖片問題

minimize:true 壓縮html檔案

handlebars-loader 載入handlebars檔案並編譯為html檔案

handlebars-template-loader 解決handlebars圖片路徑問題

markup-inline-loader

將內聯的 SVG/MathML 檔案轉換為 HTML。在應用於圖示字型,或將 CSS 動畫應用於 SVG 時非常有用

{
    test: /\.html$/,
    use: [
      'html-loader',
      'markup-inline-loader'
    ]
}
複製程式碼

html中使用:
<img markup-inline src="./_images/camera.svg" />
<img data-markup-inline src="./_images/camera.svg" />

樣式類

style-loader 將模組的匯出作為樣式新增到 DOM 中

css程式碼放入js程式碼裡再加入到html裡

css-loader 解析 CSS 檔案後,使用 import 載入,並且返回 CSS 程式碼

得到css程式碼

less-loader 載入和轉譯 LESS 檔案

sass-loader 載入和轉譯 SASS/SCSS 檔案

得到css檔案

postcss 使用 PostCSS 載入和轉譯 CSS 檔案

後處理css檔案,對css檔案做語法分析,真正的核心操作,依賴於postcss龐大的外掛群體 postcss外掛
比如css的語法驗證,壓縮,支援變數和混入語法

  • autoprefixer 補全瀏覽器字首
  • postcss-import css檔案中使用@import引入其他樣式檔案,但是使用autoprefixer發現,import進來的樣式沒有處理
postcss: function(webpack) {
  return [
    postcssImport({
      addDependencyTo: webpack
    }),
    autoprefixer
  ]
}
複製程式碼

清理和測試類

mocha-loader 使用 mocha 測試(瀏覽器/NodeJS)

eslint-loader 使用 ESLint 清理程式碼

jshint-loader 使用 JSHint 清理程式碼

框架類

vue-loader 載入和轉譯 Vue 元件

angular2-template-loader 載入和轉譯 Angular 元件

外掛

loader 被用於轉換某些型別的模組,而外掛則可以用於執行範圍更廣的任務。外掛的範圍包括,從打包優化和壓縮,一直到重新定義環境中的變數

html-webpack-plugin 生成html檔案

  • 為html檔案中引入的外部資源如script、link動態新增每次compile後的hash,防止引用快取的外部檔案問題
  • 可以生成建立html入口檔案,比如單頁面可以生成一個html檔案入口,配置N個html-webpack-plugin可以生成N個頁面入口
new htmlWebpackPlugin(options)
複製程式碼
名稱 型別 預設值 描述
title {String} `` 用於生成的HTML文件的標題
filename {String} 'index.html' 生成html的檔名
template {String} `` 模版路徑及檔名(路徑相對與output.context)
inject {Boolean|String} true 當傳遞true或'body'所有JavaScript資源將被放置在正文元素的底部。'head'將指令碼放置在head元素中,false不會將指令碼放進html中
favicon {String} `` 將給定的圖示路徑新增到輸出HTML
minify {Boolean|Object} true 縮小輸出html html-minifier
hash {Boolean} false true:將webpack所有包含的指令碼和CSS檔案附加一個獨特的編譯雜湊。這對快取清除非常有用
cache {Boolean} true 僅在檔案被更改時才發出檔案
showErrors {Boolean} true 錯誤細節將寫入HTML頁面
chunks {?} ? 允許新增的chunk名
chunksSortMode {String|Function} auto 允許控制chunk在被包含到HTML之前應該如何排序。允許的值是'none'不分類 | 'auto'按塊ID排序 | 'dependency'通過其“父項”屬性對塊之間的依賴關係進行排序 | 'manual' | {Function}
excludeChunks {String} `` 允許您跳過一些chunk
xhtml {Boolean} false 如果true將link標籤呈現為自動關閉(符合XHTML)
  • chunks按照順序載入
chunksSortMode: function (chunk1, chunk2) {
    var order = ['common', 'public', 'index'];
    var order1 = order.indexOf(chunk1.names[0]);
    var order2 = order.indexOf(chunk2.names[0]);
    return order1 - order2;  
}
複製程式碼

extract-text-webpack-plugin

webpack 把所有的資源都當成了一個模組, CSS,Image, JS 字型檔案資源, 都打包到一個 bundle.js 檔案中

它將*.css輸入塊中的所有必需模組移動到單獨的CSS檔案中
new ExtractTextPlugin([id: string], filename: string, [options])

  1. id 該外掛例項的唯一標誌,一般是不會傳的,其自己會生成。
  2. filename 檔名。可以是[name]、[id]、[contenthash] [name]:將會和entry中的chunk的名字一致 [id]:chunk 的數量 [contenthash]:根據內容生成hash值
  3. options allchunk:是否將所有額外的chunk都壓縮成一個檔案 disable:禁止使用外掛 ignoreOrder: 禁用順序檢查 (這對 CSS 模組很有用!),預設 false
  • extract 配置:
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",//編譯後用什麼loader來提取css檔案
          publicPath:, //重寫此 loader 的 publicPath 配置
          use: "css-loader" //需要什麼樣的loader去編譯檔案
        })
      }
    ]
  }
複製程式碼

CopyWebpackPlugin 拷貝資源外掛

CopyWebpackPlugin([
        {
            context: 'global/img',
            from: '**/*',
            to:'img/common'
        },
        {
            from: 'img',
            to:'img'
        },
        {
            from :'global/lib/es5-shim-sham.js'
        }
])
複製程式碼

from 定義要拷貝的源目錄.
to 定義要拷盤的目標目錄.
context 上下文.
flatten 只拷貝檔案不管資料夾 預設是false.
ignore 忽略拷貝指定的檔案 可以用模糊匹配.
force 強制覆蓋先前的外掛 可選 預設false.

UglifyJsPlugin 壓縮js檔案

new webpack.optimize.UglifyJsPlugin(options)
複製程式碼
名稱 型別 預設值 描述
test {RegExp|Array<RegExp>} /.js$/i 匹配檔案
include {RegExp|Array<RegExp>} undefined 包含目錄
exclude {RegExp|Array<RegExp>} undefined 非匹配目錄
cache {Boolean|String} false 啟用檔案快取;cache: 'path/to/cache'
parallel {Boolean|Number} false 使用多程式並行執行來提高構建速度
sourceMap {Boolean} false 使用源對映將錯誤訊息位置對映到模組(這會減慢編譯速度) ⚠️ cheap-source-map 選項不適用於這個外掛
uglifyOptions {Object} {...defaults} uglify 選項

webpack.optimize.UglifyJsPlugin()遇到的 Unexpected token: operator (>) from UglifyJs問題可以使用 uglifyjs-webpack-plugin外掛

DefinePlugin 允許在編譯時(compile time)配置的全域性常量

new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify('production')
})
複製程式碼

CleanWebpackPlugin 打包前清理dist目錄

new CleanWebpackPlugin(['./dist'])

webpack自帶外掛

CommonsChunkPlugin 提取 chunks 之間共享的通用模組

webpack將多個模組打包之後的程式碼集合稱為chunk

將公共js程式碼提取到單獨檔案裡

  • 是一個可選的用於建立一個獨立檔案(又稱作 chunk)的功能
  • 通過將公共模組拆出來,最終合成的檔案能夠在最開始的時候載入一次,便存到快取中供後續使用
  • 語法: new webpack.optimize.CommonsChunkPlugin(options)
  • 配置
  1. 決定生成chunk的引數: name, names, async
  • name: string: 公共chunk的名字
  • names: string[]: 和name一樣,不過傳入的是一個陣列
  • async: boolean|string: 把公共程式碼提取到一個懶載入的chunk,在被使用到時才進行下載
  1. 決定被提取的chunk: chunks, children, deepChildren
  2. 決定提取條件: minChunks
  • minChunks: number|infinity|function(module,count)->boolean: 如果傳入數字(預設值為3),就是告訴webpack,只有當模組重複的次數大於等於該數字時,這個模組才會被提取出來;Infinity表示不會打包多餘的第三方庫;當傳入為函式時,所有符合條件的chunk中的模組都會被傳入該函式做計算,返回true的模組會被提取到目標chunk
module.exports = {
    entry: {
        main1: '/src/main1.js',
        main2: '/src/main2.js',
        jquery:["jquery"],
        vue:["vue"]
    },
    plugins: [
        new CommonsChunkPlugin({
            name: ["common",'jquery','vue'],//對應於上面的entry的key
            minChunks:2
        })
    ]
};
複製程式碼

打包後jquery和vue會生成獨立chunk,main1和main2中的公共業務模組會打包到common.js中;minChunks為infinity時,公共業務模組會分別打包到main1.js和main2.js中
webpack用外掛CommonsChunkPlugin進行打包的時候 將符合引用次數(minChunks)的模組打包到name引數的陣列的第一個塊裡(chunk),然後陣列後面的塊依次打包,最後一個塊包含webpack生成的在瀏覽器上使用各個塊的載入程式碼

HotModuleReplacementPlugin 熱模組替換

new webpack.HotModuleReplacementPlugin()
複製程式碼
devServer: {
    hot: true // 啟用伺服器的HMR
}
複製程式碼

其他

cross-env

在不同系統環境下設定變數

webpack-dev-server

  • 開發環境用於除錯報錯資訊等,生成一個開發用的伺服器,在檔案有變化的時候自動給我們打包,然後重新整理頁面
  • 它還有個模組熱替換的功能 .. 就是它可以只替換有變化的地方 .. 不需要重新整理整個頁面 ...

優化

優化輸出

壓縮css

css-loader?minimize去除css檔案裡有很多空格和tab

tree-shaking

藉助es6 import export語法靜態性的特點來刪掉export但是沒有import過的東西

  • 配置babel讓它在編譯轉化es6程式碼時不把import export轉換為cmd的module.export,配置如下:
"presets": [
    [
      "es2015",
      {
        "modules": false
      }
    ]
]
複製程式碼

優化 UglifyJsPlugin

new UglifyJsPlugin({
    // 最緊湊的輸出
    beautify: false,
    // 刪除所有的註釋
    comments: false,
    compress: {
      // 在UglifyJs刪除沒有用到的程式碼時不輸出警告  
      warnings: false,
      // 刪除所有的 `console` 語句
      // 還可以相容ie瀏覽器
      drop_console: true,
      // 內嵌定義了但是隻用到一次的變數
      collapse_vars: true,
      // 提取出出現多次但是沒有定義成變數去引用的靜態值
      reduce_vars: true,
    }
})
複製程式碼

imagemin-webpack-plugin壓縮圖片

更快的構建

縮小檔案搜尋範圍

  • 配置模組庫.
    在js裡出現import 'redux'這樣不是相對也不是絕對路徑的寫法時會去node_modules目錄下找。但是預設的配置會採用向上遞迴搜尋的方式去尋找node_modules,但通常專案目錄裡只有一個node_modules在專案根目錄
module.exports = {
    resolve: {
        modules: [path.resolve(__dirname, 'node_modules')]
    }
};
複製程式碼
  • 配置loader
{
    test: /\.js$/,
    loader: 'babel-loader',
    include: path.resolve(__dirname, 'src')
}
複製程式碼

開啟 babel-loader 快取

babel編譯過程很耗時,好在babel-loader提供快取編譯結果選項,在重啟webpack時不需要創新編譯而是複用快取結果減少編譯流程。babel-loader快取機制預設是關閉的,開啟的配置如下:

{
test: /\.js$/,
      loader: 'babel-loader?cacheDirectory',
}
複製程式碼

使用 alias

resolve.alias 配置路徑對映。 釋出到npm的庫大多數都包含兩個目錄,一個是放著cmd模組化的lib目錄,一個是把所有檔案合成一個檔案的dist目錄,多數的入口檔案是指向lib裡面下的。
預設情況下webpack會去讀lib目錄下的入口檔案再去遞迴載入其它依賴的檔案這個過程很耗時,alias配置可以讓webpack直接使用dist目錄的整體檔案減少檔案遞迴解析。配置如下:

module.exports = {
  resolve: {
    alias: {
      'moment': 'moment/min/moment.min.js',
      'react': 'react/dist/react.js',
      'react-dom': 'react-dom/dist/react-dom.js'
    }
  }
};
複製程式碼

使用 noParse

module.noParse 配置哪些檔案可以脫離webpack的解析。 有些庫是自成一體不依賴其他庫的沒有使用模組化的,比如jquey、momentjs、chart.js,要使用它們必須整體全部引入。
webpack是模組化打包工具完全沒有必要去解析這些檔案的依賴,因為它們都不依賴其它檔案體積也很龐大,要忽略它們配置如下:

module.exports = {
  module: {
    noParse: /node_modules\/(jquey|moment|chart\.js)/
  }
};
複製程式碼

相關文章