Webpack4那點事

Gyan發表於2019-12-09

Webpack4那點事

知識點掃盲

構建

  • 概念:把原始碼轉換成釋出線上可執行的jscsshtml程式碼的過程
  • 特點:
    • 1.程式碼轉換(ts—>jsless/scss—>css等)
    • 2.檔案優化(壓縮jscsshtml程式碼;壓縮合並圖片等)
    • 3.程式碼分割(提取多個頁面的公共程式碼,提取首屏不需要執行部分的程式碼讓其非同步載入)
    • 4.模組合併(把多個模組檔案分類合併成一個檔案)
    • 5.自動重新整理(在開發過程中監聽本地原始碼的變化,自動重新構建、重新整理瀏覽器)
    • 6.程式碼校驗(在程式碼被提交到倉庫之前需要校驗程式碼是否符合規範,以及單元測試是否通過)
    • 7.自動釋出(更新完程式碼後,自動構建出線上釋出程式碼並傳輸給釋出系統)

怎麼用

注:如果你是初學者,希望你能跟著我的步驟操作一下

安裝

  • 1、為了版本一直我們推薦使用本地安裝(npm install webpack --save-dev)
  • 2、安裝webpack-cli(npm install webpack-cli --save-dev),以便解析使用者傳參(核心yargs)

零配置打包

檔案目錄建立

  • 根目錄建立src資料夾並在其中建立index.js檔案
    Webpack4那點事
// index.js
console.log('hello webpack')
複製程式碼

簡單實現一(入口無依賴檔案)

  • 執行npx webpack
    Webpack4那點事
    • 此時發現,根目錄下會出現一個包含main.js檔案的dist資料夾;同時你也一定注意到瞭如上圖中紅框所圈出的WARNING,是因為我們沒有指定mode(模式:包含生產(production)和開發(development)兩種),不傳會預設production模式
  • 執行npx webpack --mode development
    • 此時不會出現上圖中的WARNING,比較一下此時main.js和之前的區別

簡單實現二(入口有依賴檔案)

  • src資料夾中再建立一個other.js,在index.js通過require引入
// other.js
module.exports = 'hello webpack'

// index.js 
const other = require('./other.js')
console.log(other)
複製程式碼
  • 執行npx webpack --mode development
  • 在生成的dist資料夾下建立一個index.html檔案並引入產生的main.js檔案,在瀏覽器中檢視index.htmlconsole
    Webpack4那點事
  • 再嘗試一下在index.html中直接引入src資料夾下的index.js,在瀏覽器中檢視index.htmlconsole
    Webpack4那點事

後者的原因是因為我們的瀏覽器不支援coomonjs,這裡我不做過多解釋;我們來分析一下前者是如何實現的

// 刪除掉冗餘部分的main.js檔案
(function (modules) {
  var installedModules = {};
  function __webpack_require__(moduleId) { // ./src/index.js  ./src/other.js
    if (installedModules[moduleId]) { // 類似Commonjs中的快取處理
      return installedModules[moduleId].exports;
    }
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    };
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    module.l = true;
    return module.exports;
  }
  return __webpack_require__("./src/index.js");
})
  ({
    "./src/index.js":
      (function (module, exports, __webpack_require__) {
       // 預設會呼叫 require("./src/other.js")
        eval("const other = __webpack_require__(\"./src/other.js\");console.log(other);");
    
      }),
    "./src/other.js":
      (function (module, exports) {
        eval("module.exports = 'hello webpack';");
      })
  });
  /* webpack打包流程
   * 1.找到入口檔案(index.js)
   * 2.找到入口檔案中的依賴模組(other.js)  ast解析載入依賴模組
   * 3.從入口檔案開始執行 require方法改寫成 __webpack_require__
   * 內部自己實現了一個Commonjs模組(需要找到入口,需要找到入口中的所有依賴,載入依賴 + 模板——>渲染後的結果) 
   */
複製程式碼

增加配置打包

真正的開發中我們肯定不會零配置打包,肯定根據我們的需要來手動配置webpack

配置入口檔案

  • 在全域性建立webpack.config.jswebpackfile.js檔案。(本文以webpack.config.js為例)
    Webpack4那點事
  • 編輯webpack.config.js(採用coomonjs規範)
// webpack.config.js
module.exports = { // 匯出一個物件;物件中放置配置;會呼叫這個配置進行構建
}
複製程式碼
  • 修改src資料夾下index.js檔名為guYan.js,執行npx webpack --mode development,報錯顯示找不到入口檔案
    Webpack4那點事
    Webpack4那點事
  • webpack.config.js檔案中增加入口的位置,並執行npx webpack --mode development;無報錯
// webpack.config.js
module.exports = {
    entry: './src/guYan.js' //當前入口檔案的位置
}
複製程式碼

注:我們可以通過執行npx webpack --mode development時增加config引數來實現修改預設配置檔案webpack.config.js的名字。比如我們現在修改webpack.config.js檔名為webpack.guYan.js然後執行npx webpack --mode development --config webpack.guYan.js效果和之前一樣

配置出口檔案

  • webpack.config.js檔案中增加出口檔案的配置,並執行npx webpack --mode development,效果如圖
// webpack.config.js
const path = require('path');
module.export = {
    entry: './src/guYan.js',
    output:{ // 配置當前出口檔案
        filename: 'bundle.js',  // 出口檔名
        path: path.resolve(__dirname,'./build') // 出口目錄,該配置項必須是絕對路徑,預設dist目錄
    }
}
複製程式碼

Webpack4那點事

配置HTML模板

  • 在根目錄建立public目錄,並在其中建立index.html檔案
    Webpack4那點事
  • 下載html-webpack-plugin外掛(npm install html-webpack-plugin --save-dev)
    • 作用:根據模板產生一個打包後的html
  • webpack.config.js檔案中增加HTML模板的配置(使用html-webpack-plugin外掛)
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');// 1.引入外掛
module.exports = {
    entry:'./src/guYan.js',
    output:{
        filename:'bundle.js',
        path:path.resolve(__dirname,'./build')
    },
    plugins:[ // 2.在plugins中註冊外掛
        new HtmlWebpackPlugin({
            template:'./public/index.html', // 指定模板
            filename:'gy.html' // 指定打包後的檔名,預設為index.html
        })
    ]
}
複製程式碼
  • 執行npx webpack --mode development,輸出檔案目錄如圖,並且你會發現在輸出的gy.html中自動引入了打包後的bundle.js
    Webpack4那點事
  • 上述雖然已經完成了動態引入,但是執行npx webpack --mode production會發現打包後的bundle.js被壓縮,但是gy.html沒有被壓縮,還有就是引用的檔案一直是一個我們不清楚會不會出現快取的問題,這上線肯定是不允許的啊,所以我們需要繼續配置
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.export = {
    entry:'./src/guYan.js',
    output:{
        filename:'bundle.js',
        path:path.resolve(__dirname,'./build')
    },
    plugins:[
        new HtmlWebpackPlugin({
            template: './public/index.html',
            filename:'gy.html',
            minify:{ // 配置壓縮形式
                removeAttributeQuotes: true, // 刪除所有屬性的引號
                collapseWhitespace: true // 刪除所有空白
            },
            hash:true // 每次引用js檔案都加入雜湊戳
        })
    ]
}
複製程式碼
  • 執行npx webpack --mode production後對比之前的效果檢視

配置mode(模式)

  • 上文中我們執行打包命令的時候都會傳一個mode引數,其實沒有必要這麼麻煩,我們可以在webpack.config.js中配置mode;執行npx webpack結果和上文一樣
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports= {
    mode: 'production', // 預設兩個引數 development production
    entry: './src/guYan.js',
    ...
}
複製程式碼

配置clean-webpack-plugin(清理資料夾)

在開發構建的過程中我們更希望在打包後的資料夾中一直是最新的打包檔案,即先刪除掉之前打包產生的資料夾及檔案

  • 安裝clean-webpack-plugin(npm install clean-webpack-plugin --save-dev)
  • webpack.config.js檔案中進行配置
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clearn-webpack-plugin'); // 1.引入外掛
module.exports = {
    mode:'development',
    entry: './src/guYan.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname,'./build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html',
            filename: 'gy.html',
            minify: {
                removeAttributeQuotes: true,
                collapseWhitespace: true
            },
            hash:true
        }),
        /*清空檔案的外掛*/
        new CleanWebpackPlugin({ // 2.註冊外掛
            cleanOnceBeforeBuildPatterns:['**/*'], // 匹配目錄下的所有檔案清空,預設也是所有
        })
    ]
}
複製程式碼

配置多入口打包

  • 上述示例中所有webpack.config.js中的entry的值都是一個路徑字串的格式('./src/guYan.js'),當我們的入口只有一個的時候我們這樣寫ok,但是當我們需要同時打包多個檔案的時候呢?我們如何處理呢?對的,它的值還可以是個物件
  • 首先我們準備再準備一個入口檔案guYan2.js
// src/guYan2.js
console.log('I am GuYan');
複製程式碼
  • webpack.config.js中修改我們的配置,並執行npx webpack打包後的資料夾變化效果
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
    mode:'development',
    entry:{ // 配置多入口
        index:'./src/guYan.js',
        other:'./src/guYan2.js'
    },
    output:{ 
        filename: '[name].js',// 為了響應多入口出口檔名需要動態建立,保證每個入口都有輸出
        path: path.resolve(__dirname,'./bundle')
    },
    plugins:[
        new HtmlWebpackPlugin({
            template:'./public/index.html',
            filename:'index.html',
            minify:{
                removeAttributeQuotes:true,
                collapseWhitespace:true
            },
            hash:true
        }),
        new CleanWebpackPlugin({
            clearnOnceBeforeBuildPatterns:['**/*']
        })
    ]
}
複製程式碼

Webpack4那點事

  • 在上文中我們為了打包後的html檔案引入打包後的js檔案不出現快取給我們造成的困擾,我們在使用html-webpack-plugin的時候配了一個hash,我們也可以在出口檔名的位置加入[contentHash]引數
// webpack.config.js
...
output:{
    filenama:'[name].[contentHash:8].js' // :後面的是代表長度
}
...
複製程式碼

Webpack4那點事

  • 上文打包之後我們發現現在打包出兩個js檔案一個html檔案中引用了兩個js檔案,那如果我們想分別引用呢?怎麼處理?對的,再new一次html-webpack-plugin。所以我們寫一個迴圈來處理一個plugin需要多次引用的問題
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
let htmlPlugins = ['index','other'].map(chunkName=>(new HtmlWebpackPlugin({
    filename:`${chunkName}.html`,
    chunks:[chunkName] // 保證按需引入 即index.html檔案只引入index.js;other.html只引入other.js
})));
modeule.exports = {
    mode:'development',
    entry:{
        index:'./src/guYan.js',
        other:'./src/guYan2.js'
    },
    output:{
        filename:'[name].[contentHash:8].js',
        path:path.resolve(__dirname,'./build')
    },
    plugins:[
        new CleanWebpackPlugin(),
        ...htmlPlugins
    ]
}
複製程式碼

配置devServer(webpack-dev-server)

在開發過程中我們更希望的是我們一邊修改內碼表面自動更新,當然這種處理我們有兩種方案一個是通過編譯的時候傳入--watch不推薦使用(所以我就不做介紹了),另一種就是通過webpack-dev-server來啟動,這種方式不會產生構建好的檔案,並且會給我們啟動一個服務

注:本小節的例子我們採用單入口輸入,示例如下

// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
let htmlPlugins = ['index'].map(chunkName=>(new HtmlWebpackPlugin({
  filename:`${chunkName}.html`,
  chunks:[chunkName]
})));
module.exports = {
  devServer:{
    port:3000, //指定埠號
    open:true, // 是否自動開啟瀏覽器
    compress:true, // 是否啟用gzip壓縮
    contentBase:'./dist' // 指定啟動靜態服務的檔案目錄
    // 通過webpack-dev-server打包的結果放到記憶體中,而且目錄是當前的根目錄
  },
  mode: 'development',
  entry: {
    index: './src/guYan.js',
  },
  output: {
    filename: '[name].js',
  },
  plugins: [
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: ['**/*']
    }),
    ...htmlPlugins,
  ]
}
複製程式碼
  • 通過npx webpack-dev-sever啟動來檢視結果

配置loader

  • 作用:webpack 只能理解 JavaScriptJSON 檔案。loaderwebpack 能夠去處理其他型別的檔案,並將它們轉換為有效 模組,以供應用程式使用,以及被新增到依賴圖中。
  • 特點
    • 執行順序:從右向左,從下到上
    • 分類:前置(enforce:'pre')、普通(enforce:'normal')、後置(enforce:'post')
    • 引數型別:陣列,字串,物件

處理css

  • 全域性建立一個style資料夾,並在其中建立一個index.css檔案,並在src/guYan.js中引入
    Webpack4那點事
/*index.css*/
body{
    background:yellow;
}
複製程式碼
  • 安裝css-loader,style-loader(npm install css-loader style-loader --save-dev)
    • 原理:通過css-loadercss檔案處理成字元傳形式,通過style-loaderhtml中建立<style>標籤並將css-loader處理後的字串插入其中
  • webpack.config.js中配置loader
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
let htmlPlugins = ['index'].map(chunkName=>(new HtmlWebpackPlugin({
    template:'./public/index.html',
    filename:`${chunkName}.html`,
    chunks:[chunkName],
    minify:{
        removeAttributeQuotes:true,
        collapseWhitespace:true
    },
    hash:true
})))
module.exports = {
    mode:'development',
    devServer:{
      port:3000,
      open:true,
      compress:ture,
      contentBase:'./dist'
    },
    entry:{
        index:'./src/guYan.js'
    },
    output:{
        filename:'[name].js'
    },
    plugins:[
        new CleanWebpackPlugin(),
        ...htmlPlugins
    ],
    module:{
        rules:[
            {
                test:/\.css$/,
                use:['style-loader','css-loader']
            }
        ]
    }
}
複製程式碼
  • 執行npx webpack-dev-server結果如下
    Webpack4那點事

CSS相關

配置css-modules

  • 在日常的開發中我們通常會將一些公共的樣式抽離到一個common.css檔案中,然後在我們js檔案中按需引入;這樣我們就會遇到css模組化的問題(css-modules),我們舉例說明如何配置解決這個問題
  • 修改src/guYan.js,在style目錄下建立一個common.css檔案
// guYan.js
import { other } from './other';
import '../style/index.css';
import commonStyle from '../style/common.css';
let div = document.createElement('div');
div.className = commonStyle.guYan;
div.innerHTML = 'GuYan';
document.body.appendChild(div);
console.log(other);
複製程式碼
/*common.css*/
.guYan{
    color:red;
}
複製程式碼
  • 配置webpack.config.js
// webpack.config.js
...
    module:{
        rules:[
            {
                test:/\.css$/,
                use:['style-loader',
                     {
                         loader:'css-loader',
                         options:{
                             modules:true
                         }
                     }
                ]
            }
        ]
    }
...
複製程式碼
  • 執行npx webapck-dev-server
    Webpack4那點事

特殊注意loader的順序一定要特別注意,如果就想把style-loader寫在後面需要在其options中增加enforce:'post'引數或者在css-loaderoptions中增加enforce:'pre'引數

配置css3增加瀏覽器核心字首

  • 修改style/common.css如下
/*common.css*/
.guYan{
  color: red;
  background: green;
  transform: rotateY(180deg);
}
複製程式碼
  • 安裝postcss-loader(npm install postcss-loader --save-dev)
  • postcss-loader只是一個幹活的,它不知道幹什麼活,所以需要一個東西來告訴它,它需要做的是增加字首,這個東西就是autoprefixer,所以我們安裝autoprefixer(npm install autoprefixer --save-dev)
  • 現在我們幹活的(postcss-loader)有了,指揮幹活的(autoprefixer)也有了,但是呢這個幹活的(postcss-loader)比較裝X,它要求這個指揮幹活的(autoprefixer)不能像其他loader那樣你隨隨便便在options物件中指揮我,你必須專門在我的配置檔案(postcss.config.js)中指揮我幹活,所以我們需要在根目錄下建立一個postcss.config.js檔案

Webpack4那點事

  • 配置postcss.config.js檔案
// postcss.config.js
module.exports = {
    plugins:[
        require('autoprefixer')
    ]
}
複製程式碼
  • 配置webpack.config.js
// webpack.config.js
...
module:{
    rules:[
        {
            test:/\.css$/,
            use:[
                'style-loader',
                {
                    loader:'css-loader',
                    options:{
                        modules:true
                    }
                },
                'postcss-loader'
            ]
        }
    ]
}
...
複製程式碼
  • 執行npx webpack-dev-server
    Webpack4那點事

配置css前處理器

  • 前處理器分類
    • lesssass(node-sass),stylus
  • 前處理器對應loader
    • less-loadersass-loaderstylus-loader
  • 引入前處理器檔案的位置
    • js檔案
      • 配置對應的loader(以less為例)
        // webpack.config.js
        ...
        module:{
            rules:[
                {
                    test:/\.less$/,
                    use:[
                        'style-loader',
                        {
                            loader:'css-loader',
                            options:{
                                modules:true
                            }
                        },
                        'postcss-loader',
                        'less-loader'
                    ]
                }
            ]
        }
        ...
        複製程式碼
    • css檔案
      • 配置對應的loader(以less為例)
        // webpack.config.js
        ...
        module:{
            rules:[
                {
                    test:/\.css$/,
                    use:[
                        'style-loader',
                        {
                            loader:'css-loader',
                            options:{
                                modules:true
                            }
                        },
                        'postcss-loader',
                        'less-loader'
                    ]
                }
            ]
        }
        ...
        複製程式碼

配置抽離css樣式

上文中提到的樣式,不難發現都是巢狀在html檔案中的head裡的<style>標籤中的,但是在日常的開發中我們一般都是單獨在xxx.css檔案中寫樣式,然後通過<link>引入,所以我們接下來抽離css樣式吧

  • 安裝mini-css-extract-plugin(npm install mini-css-extract-plugin --save-dev)
  • 配置webpack.config.js,需要注意的是:
    • 1.這個外掛做的工作是將css-loader處理後的字串寫到一個指定的檔案中,想要使其以<link>標籤的形式引入,需要配合其自帶的loader使用,即下文的MiniCssExtractPlugin.loader
    • 2.官方建議生產模式下使用MiniCssExtractPlugin.loader,開發模式使用style-loader
    // webpack.config.js
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');// 引入外掛
        ...
    module.exports = {
          ...
        plugins:[
          new CleanWebpackPlugin(),
          ...htmlPlugins,
          new MiniCssExtractPlugin({ // 註冊外掛
              filename:'index.css'//指定抽離的檔名
          })
          ...
        ],
        module:{
            rules:[
              {
                  test:/\.css$/,
                  use:[
                      // 'style-loader' 
                      MiniCssExtractPlugin.loader, // 配合外掛自身loader使用
                      ...
                  ]
              }
            ]
        }
    }
    複製程式碼
  • 執行npx webpack-dev-server結果如下
    Webpack4那點事

配置壓縮抽離的css樣式檔案

  • 上述程式碼執行npx webpack --mode production,檢視打包後的檔案,你會發現我們抽離的css檔案並沒有像jshtml檔案一樣被壓縮,這在我們上線的時候肯定是不允許的,所以我們需要壓縮它
  • 安裝optimize-css-assets-webpack-plugin(npm install optimize-css-assets-webpack-plugin --save-dev)
  • 配置webpack.config.js檔案
    // webpack.config.js
    const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
    ...
    module.exports = {
        ...
        optimization:{ // 配置優化項
            minimizer:[ // 手動指定壓縮項
            new OptimizeCssAssetsWebpackPlugin({})
            ]
        }
        mode:'production'
        ...
    }
    複製程式碼
  • 執行npx webpack檢視結果,發現我們提取的css檔案已經實現壓縮
    Webpack4那點事
  • 但是你再看js檔案沒有壓縮,這是因為當我們沒有手動配置壓縮項的時候webpack內部自己實現了配置其自帶的terser-webpack-plugin外掛進行壓縮,而我們配置後覆蓋掉了它內部自己的配置,所以我們需要手動將terser-webpack-plugin配置到minimizer
// webpack.config,js
const TerserJSPlugin  = require('terser-webpack-plugin'); //webpack的內部外掛
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
...
module.exports = {
  ...
  optimization:{ 
      minimizer:[
      new TerserJSPlugin({}),// 手動加上壓縮js
      new OptimizeCssAssetsWebpackPlugin({})
      ]
  }
  mode:'production'
  ...
}
複製程式碼
  • 執行npx webpack檢視結果

圖片相關

配置圖片引入

  • 在根目錄下建立image資料夾並準備一張guYan.jpg圖片放入其中
  • src/guYan.js中引入guYan.jpg
// guYan.js
import url from '../image/guYan.jpg';
...
let img = new Image();
img.src = url;
documnt.body.appendChild(img)
複製程式碼
  • 安裝file-loader(npm install file-loader --save-dev)
    • 作用:
      • 1.拷貝一個檔案放到打包後的檔案中
      • 2.返回拷貝的路徑
  • 配置webpack.config.js
//webpack.config.js
...
module.exports={
    ...
    module:{
        rules:[
            ...,
            {
                test:/\.(jpg|png|gif)$/,
                use:'file-loader'
            }
        ]
    }
}
複製程式碼
  • 執行npx webpack --mode production檢視打包後的檔案,會發現生成一個拷貝的圖片
    Webpack4那點事

配置圖片壓縮

  • 在上一小節我們實現了圖片的引入,但是在我們的開發過程中有一些Icon之類的我們更希望它可以轉化為base64的格式壓縮,所以這就需要我們配置另一個可以處理圖片的loader,url-loader
  • 安裝url-loader(npm install url-loader --save-dev)
  • 配置webpack.config.js
// webpack.config.js
...
module.exports={
    ...
    module:{
        rules:[
            ...,
            {
                test:/\.(jpg|png|gif)$/,
                use:{
                    loader:'url-loader',
                    options:{
                        limit:100*1024 // 指定壓縮檔案的最大位元組,超過最大限制會自動呼叫file-loader處理
                    }
                }
            }
        ]
    }
}
複製程式碼
  • 執行npx webpack --mode production,檢視打包後的檔案目錄並沒有生成拷貝的檔案(嘗試修改limit引數的大下感受一下變化)

特殊注意:字型圖示(xxx.eot,xxx.svg,xxx.ttf,xxx.woff,xxx.woff2)只能用file-loader

如果你給的limit過大,也就是說會將大的圖片也會轉成base64的格式會導致打包的時候會有一個效能的警告提示你檔案過大,這樣的時候你可以給一個performance:false屬性來使打包的時候忽略效能問題的提示,但是一般我們上線肯定考慮效能的問題的,也就是說我們一般不會將limit的值設定過大

  • 上面我們只舉例了在js檔案中引用圖片的例子,其實在開發中我們也有可能直接在html檔案中引用圖片,安裝我們之前的打包方式你會發現會出現找不到圖片,因為路徑變了,解決這個問題我們需要配置一個html-withimg-loader
// webpack.config.js
...
    {
        test:/\.html$/,
        use:'html-withimg-loader'
    }
...
複製程式碼

配置輸出的圖片增加公共字首比如需要加域名

  • url-loaderoptions增加publicPath屬性即可
//webpack.config.js
...
    options:{
        limit:2*1024,
        publicPath:'https://juejin.im/user/5b685661e51d4517df153771'
    }
...
複製程式碼

同理css``js增加字首一個套路

配置分類打包

  • 圖片
    • options中增加outputPath屬性
      Webpack4那點事
  • css
    • 在抽離外掛的filename引數屬性前面配置資料夾的名字
      Webpack4那點事
  • html
    • html-webpack-plugin外掛的filename屬性前面配置資料夾的名字
      Webpack4那點事
  • js
    • 在出口引數(output)裡的filename屬性前面配置資料夾的名字
      Webpack4那點事
  • 執行npx webpack --mode production檢視打包後的檔案變化吧

JS相關

配置JS高階語法降級(通過babel一系列包)

  • 修改guYan.js檔案如下
// guYan.js
class Animal{
    constructor(type){
        this.type = type;
    }
    getType(){
        return this.type
    }
}
let animal = new Animal('哺乳類');
console.log(animal.type);
複製程式碼
  • 安裝babel-loader,@babel/core,@babel/preset-env(npm install babel-loader @babel/core @babel/preset-env --save-dev)
  • 配置webpack.config.js
// webpack.config.js
...
module.exports={
    ...,
    module:{
        rules:[
            ...,
            {
                test:/\.js$/,
                use:{
                    loader:'babel-loader',
                    options:{
                        presets:['@babel/preset-env'],// 起指揮loader怎麼幹活的作用
                    }
                },
                exclude:/node_module/, // 忽略掉不想用loader處理的檔案
                include:path.resolve(__dirname,'./src') // 指定需要loader處理的檔案
            }
        ]
    }
}
複製程式碼
  • 執行npx webpack --mode development,檢視打包後的js檔案,我們發現,他內部實現了一個_classCallCheck,來作為轉化我們的class類。
// 刪除後冗餘的程式碼後的打包檔案
(function(modules) {
	var installedModules = {};
	function __webpack_require__(moduleId) {
		if(installedModules[moduleId]) {
			return installedModules[moduleId].exports;
		}
		var module = installedModules[moduleId] = {
			i: moduleId,
			l: false,
			exports: {}
		};
		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
		module.l = true;
		return module.exports;
	}
	return __webpack_require__(__webpack_require__.s = "./src/guYan.js");
})
({
 "./src/guYan.js":
 (function(module, exports) {
eval(`function _classCallCheck(instance, Constructor) {
   if (!(instance instanceof Constructor)) {
      throw new TypeError(\"Cannot call a class as a function\"); 
    } 
  }
  function _defineProperties(target, props) {
     for (var i = 0; i < props.length; i++) {
        var descriptor = props[i]; 
        descriptor.enumerable = descriptor.enumerable || false; 
        descriptor.configurable = true; 
        if (\"value\" in descriptor) descriptor.writable = true; 
        Object.defineProperty(target, descriptor.key, descriptor); 
      } 
    }
    function _createClass(Constructor, protoProps, staticProps) {
       if (protoProps) _defineProperties(Constructor.prototype, protoProps);
        if (staticProps) _defineProperties(Constructor, staticProps); 
        return Constructor; 
      }
      var Animal =function () {
        function Animal(type) {
           _classCallCheck(this, Animal);
           this.type = type;
          }
           _createClass(Animal, [{
              key: \"getType\",
              value: function getType() {
                return this.type;
              }
            }]);
            return Animal;
          }();
          var animal = new Animal('哺乳類');
          console.log(animal.type);`);
 })
});
複製程式碼
  • 新建一個src/otherEs6.js檔案,在其中再用class定義一個類匯出,在src/guYan.js中引入這個檔案,再執行編譯。你會發現他內部實現這個公共的_classCallCheck程式碼,實現了兩次,所以我們需要提取這個公共的程式碼
  • 安裝@babel/runtime,@babel/plugin-transform-runtime(npm install @babel/runtime @bebel/plugin-transform-runtime --save-dev)
  • 配置webpack.config.js檔案
// webapack.config.js
...
    test:/\.js$/,
    use:{
        loader:'babel-loader',
        options:{
            presets:['@babel/preset-env'],
            plugins:['@babel/plugin-transform-runtime']
        }
    }
...
複製程式碼
  • 執行npx webpack --mode development,檢視打包後的js檔案,比較兩次有什麼不同

配置JS的一些API降級處理,如promise(為了相容IE)

  • 方案一,通過@babel/preset-env+core-js@3
    • 安裝core-js@3(npm install core-js@3 --save)
    • 配置webpack.config.js
    // webpack.config.js
    ...
    test:/\.js$/,
    use:{
        loader:'babel-loader',
        options:{
            preset:[
                [
                    '@babel/preset-env',
                    {
                        useBuiltIns:'usage',// 只轉化使用的API
                        corejs:{version:3}
                    }
                ]
            ]
        }
    }
    ...
    複製程式碼
  • 方案二在入口檔案中引入@babel/polyfill
  • 方案三,通過@babel/plugin-transform-runtime+@babel/runtime-corejs3
    • 安裝@babel/runtime-corejs3(npm install @babel/runtime-corejs3)
    • 配置webpack.config.js
    // webapck.config.js
    ...
        test:/\.js$/,
        use:{
            loader:'babel-loader',
            options:{
                presets:['@babel/preset-env'],
                plugins:[
                    [
                        '@babel/plugin-transform-runtime',
                        {
                            "absoluteRuntime": false,
                            "corejs": {version:3},
                            "helpers": true,
                            "regenerator": true,
                            "useESModules": false
                        }
                    ]
                ]
            }
        }
    ...
    複製程式碼

像裝飾器和類的屬性這些更高階的需要再去配外掛去處理,這裡我就不一一舉例了(@babel/plugin-proposal-decorators@babel/plugin-proposal-class-properties)

ESlint相關

  • 安裝 eslint,eslint-loader(npm install eslint eslint-loader --save-dev)
  • 配置webpack.config.js
// webpack.config.js
...
    test:/\.js$/,
    use:'eslint-loader',
    enforce:'pre' // 強制在編譯之前執行 
...
複製程式碼
  • 配置eslint
    • 方案一,在eslint中去選擇自動生成然後引入專案列表
    • 方案二,自己手動配置
      • 執行npx eslint --init
        Webpack4那點事
  • 配置webpack.config.js
// we
複製程式碼

配置TS

  • ts-loader
  • tsconfig.json

配置代理

優化策略

常見pluginloader的原理及實現

基於create-react-app實現一個簡單的腳手架

注: 參考文件

相關文章