Webpack入門到了解

Webpack的安裝和介紹

1.1 Webpack是什麼

webpack是現代前端開發中最火的模塊打包工具,只需要通過簡單的配置,便可以完成模塊的加載和打包。

官網文檔

Webpack 是一個前端資源加載/打包工具。它將根據模塊的依賴關係進行靜態分析,然後將這些模塊按照指定的規則生成對應的靜態資源。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序需要的每個模塊,然後將所有這些模塊打包成一個或多個 bundle。

  • 爲什麼要使用Webpack
    • 模塊化開發(import,require)
    • 預處理(Less,Sass,ES6,TypeScript……)
    • 主流框架腳手架支持(Vue,React,Angular)
    • 龐大的社區(資源豐富,降低學習成本)
    • webpack 不僅能處理 js, 也能處理 css, 還能處理 html,甚至是圖片等各種前端資源;
    • 開發便捷,僅僅使用一個配置文件,就能替代部分 grunt/gulp 的工作,比如打包、壓縮混淆、圖片轉 base64等;
    • 擴展性強,插件機制完善。

1.2 webpack核心概念

  • Entry : 輸入入口,webpack構建第一步從這裏開始
  • Moudle :一個模塊對應一個文件,從entry 開始遞歸找到所有依賴的模塊
  • Chunk:代碼塊,一個 Chunk 由多個模塊組合而成,用於代碼合併與分割
  • Loader:模塊轉換器,將模塊原內容按照需求轉換成新內容
  • Plugin:擴展插件,在 Webpack 構建流程中的特定時機注入擴展邏輯來改變構建結果或做你想要的事情
  • Output:輸出,在 Webpack 經過一系列處理並得出最終想要的代碼後輸出結果
//webpack.config.js
var HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');  //分離CSS和JS文件
const CleanWebpackPlugin = require('clean-webpack-plugin');     //去除build文件中的殘餘文件
const path = require('path');
const webpack=require('webpack');

let pathsToClean = [
    'dist'
]

module.exports = {
    entry: {  //唯一入口文件
        "app.bundle": "./src/app.js",
        "contact": "./src/contact.js"
    },
    devServer: {  //配置本地服務器
        port: 8686,  //端口號
        open: true,  //設置是否自動打開
        hot:true     //模塊熱替換,熱更新
        //inline:true   //實時刷新
    },
    output: {  //輸出文件
        path: path.resolve(__dirname, 'dist'),  //打包後的文件存放的地方
        filename: '[name].[hash].js'   //打包後輸出文件的文件名
    },
    plugins: [
        new HtmlWebpackPlugin({    //new 一個插件的實例,並傳入相關的參數
            // title:'hello world'   //自動添加HTML的標題
            template: './src/index.html',
            filename: 'index.html',
            minify: {
                collapseWhitespace: true
            },
            hash: true,
            //excludeChunks代表的不包含
            excludeChunks: ['contact']
        }),
        new HtmlWebpackPlugin({
            template: './src/contact.html',
            filename: 'contact.html',
            minify: {
                collapseWhitespace: true
            },
            hash: true,
            //chunks代表的是包含
            chunks: ['contact']
        }),
        new CleanWebpackPlugin(pathsToClean),
        new ExtractTextPlugin({
            filename:'style.css',
            disable:true
        }),
        new webpack.NamedModulesPlugin(),
        new webpack.HotModuleReplacementPlugin()   //自動刷新實時預覽修改後的效果
        //new webpack.optimize.UglifyJsPlugin()     //壓縮JS代碼
    ],
    module: {
        rules: [
            {
                test: /\.css$/,   //用以匹配loaders所處理文件的拓展名的正則表達式
                use: ['style-loader','css-loader','postcss-loader']    //style-loader要配置在css-loader之前
            }
        ]
    }
}

entry 對象是用於 webpack 查找啓動並構建 bundle。其上下文是入口文件所處的目錄的絕對路徑的字符串。
注:“__dirname”是node.js中的一個全局變量,它指向當前執行腳本所在的目錄。

1.3 Webpack的安裝

在安裝 Webpack 前,本地需要安裝nodejs運行環境,下載地址:node,根據你的系統下載版本。

[外鏈圖片轉存失敗(img-XlB4nGjM-1568858497838)(E:\Typora\筆記地址\markdown\img\webpackImg\node.webp)]

下載安裝包後,點擊可執行文件,不斷地按下一步,就可以安裝成功。測試安裝是否成功,使用 node -v,如果顯示版本號則表示安裝成功。

[外鏈圖片轉存失敗(img-vFtHVEyv-1568858497840)(E:\Typora\筆記地址\markdown\img\webpackImg\node-v.webp)]

1.3.1 全局安裝(不推薦)

在命令行上輸入以下命令:npm install -g webpack,測試是否安裝成功輸入 webpack -v,如果顯示版本號則安裝成功。

超過4.0安裝方式

npm i -D webpack i 是 install 的縮寫

npm i -D webpack-cli 命令行的使用

#### 命令解釋

-S:--save 的簡寫,等同於npm run start 只需輸入npm start,這兩個效果是一樣的。
-D:--save-dev 的簡寫,其包名稱及版本號會存在package.json的devDependencies這個裏面,而–save則會將包名稱及版本號放在dependencies裏面。

npm 是 nodejs 管理插件用的工具,install 表示安裝,-g 時 gobal 的縮寫 表示全局安裝。

也可以使用 cnpm, 但是在此之前 需要執行以下命令

npm install -g cnpm --registry=https://registry.npm.taobao.org

[外鏈圖片轉存失敗(img-xDIjPqlr-1568858497840)(E:\Typora\筆記地址\markdown\img\webpackImg\webpack-v.webp)]

1.3.2 初始化項目文件

在任意盤符新建一個文件夾,如在D盤創建webpackDemo文件夾;

images

之後你會看到提示輸入一些內容,這不用管,直接全部回車。

如果不想輸入 信息 可以加 -y 代表 yes

npm init -y

創建完成之後,會發現 webpack-test目錄下多出了一個名爲 package.json 的文件,主要是顯示這個項目的名稱、版本、作者、協議等信息。

{
  "name": "webpack-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^3.11.0"
  }
}

1.3.3 在項目中安裝webpack

在命令行中輸入

npm install --save-dev webpack

[外鏈圖片轉存失敗(img-r4FiAjNH-1568858497841)(E:\Typora\筆記地址\markdown\img\webpackImg\npm-install.webp)]

你會看到正在安裝 webpack 的進度,稍等片刻,成功之後,我們再來看看 package.json 這個文件的內容,多了下面這幾行:

  "devDependencies": {
    "webpack": "^3.11.0"
  }

同時你也會發現,多了一個目錄,叫 node_modules,這就是存放剛纔安裝的 webpack 庫所有要用到的源碼文件。
如果你使用的是 npm 5,可能還會在目錄中看到一個 package-lock.json 文件。

Webpack的快速使用

首先我們創建一個目錄,初始化 npm,然後 在本地安裝 webpack,接着安裝 webpack-cli(此工具用於在命令行中運行 webpack):

mkdir webpack-demo && cd webpack-demo
npm init -y
`npm i -D webpack`  
`npm  i -D webpack-cli `

項目的結構

  webpack-demo
+ |- package.json
+ |- /dist
+   |- index.html
+ |- /src
+   |- index.js
  • 第二步:安裝 loadash 依賴和編寫 js 文件

    npm install --save lodash
    
    • 編寫:src/index.js 文件
    import _ from 'lodash';
    
    function createDomElement() {
      var dom = document.createElement('div');
      dom.innerHTML = _.join(['aicoder', '.com', ' wow'], '');
      return dom;
    }
    
    document.body.appendChild(createDomElement());
    

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>起步</title>
    </head>
    <body>
      <script src="./main.js"></script>
    </body>
    </html>
    
  • 第三步:編寫 webpack 配置文件

    根目錄下添加 webpack.config.js文件。

      webpack-demo
      |- package.json
    + |- webpack.config.js
      |- /dist
        |- index.html
      |- /src
        |- index.js
    
    • webpack.config.js 內容如下:

      const path = require('path');
      
      module.exports = {
        mode: 'development',
        entry: './src/index.js',
        output: {
          filename: 'main.js',
          path: path.resolve(__dirname, './dist')
        }
      };
      
  • 執行構建任務

    • 直接執行構建任務
    npx webpack
    

    打開: dist/index.html 可以查看到頁面的結果。

    [外鏈圖片轉存失敗(img-ofms2Wxw-1568858497842)(E:\Typora\筆記地址\markdown\img\webpackImg\newinst.png)]

2.1 加載非js文件

webpack 最出色的功能之一就是,除了 JavaScript,還可以通過 loader 引入任何其他類型的文件

在webpack的世界裏,一起文件都是模塊。

默認webpack只會打包js代碼,想要打包其它內容,就需要相對應的loader。

不使用loader 的情況

[外鏈圖片轉存失敗(img-EWhENnao-1568858497843)(E:\Typora\筆記地址\markdown\img\webpackImg\loader-error.png)]

2.1.1 加載CSS文本

  • 第一步: 安裝 css 和 style 模塊解析的依賴 style-loadercss-loader
	npm install --save-dev style-loader css-loader

or

	npm i -D style-loader css-loader
  • 第二步: 添加 css 解析的 loader

    const path = require('path');
    
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
      },
      module: { // 這個節點,用於配置 所有 第三方模塊加載器
        rules: [ // 所有第三方模塊匹配規則
          {
            // 加載 以 css爲後綴的 文件  
            test: /\.css$/,
            // 從右向左讀取loader  
            use: ['style-loader', 'css-loader']
          }
        ]
      }
    };
    
    
    • css-loader: 輔助解析 js 中的 import './main.css' 並將其打包

    • style-loader: 把 js 中引入的 css 內容 注入到 html 標籤中,並添加 style 標籤.依賴 css-loader

解釋

打開 webpack.config.js 這個配置文件,在裏面,新增一個配置節點,叫做 module,它是一個對象;在 這個 module 對象身上,有一個 rules 屬性,這個 rules屬性是個 數組;這個數組中存放了所有第三方文件的配置 和處理規則;

webpack 處理第三方文件類型的過程

注:

1.發現這個 要處理的文件 不是 JS 文件,然後就去 配置文件中,查找有沒有對應的第三方 loader 規則
2.如果能找到對應的規則,就會調用 對應的 loader 處理 這種文件類型
3.在調用loader 的時候,是從後往前調用的;
4.當最後的一個 loader 調用完畢,會把處理的結果,直接交給 webpack 進行打包合併,最終輸出到bundle.js中去

  • 第三步: 編寫 css 文件和修改 js 文件

    • 在 src 目錄中添加 style.css文件

       webpack-demo
        |- package.json
        |- webpack.config.js
        |- /dist
          |- bundle.js
          |- index.html
        |- /src
      +   |- style.css
          |- index.js
        |- /node_modules
       
      

    src/style.css

    .hello {
      color: red;
    }
    

    修改 js 文件

      import _ from 'lodash';
    + import './style.css';
    
      function createDomElement() {
        let dom = document.createElement('div');
        dom.innerHTML = _.join(['aicoder', '.com', ' wow'], '');
    +   dom.className = 'hello';
    + or
    +  dom.classList.add('box')
        return dom;
      }
    
      document.body.appendChild(createDomElement());
    

    最後重新打開 dist 目錄下的 index.html 看一下文字是否變成顏色。

    [外鏈圖片轉存失敗(img-EDUycR5E-1568858497844)(E:\Typora\筆記地址\markdown\img\webpackImg\webpack-lightgren.png)]

    2.1.2 module 配置補充

模塊(module): 這些選項決定了如何處理項目中的不同類型的模塊。

webpack 模塊可以支持如下:

  • ES2015 import 語句
  • CommonJS require() 語句
  • AMD define 和 require 語句
  • css/sass/less 文件中的 @import 語句。
  • 樣式(url(...))或 HTML 文件(<img src=...>)中的圖片鏈接(image url)

module.noParse

值的類型: RegExp | [RegExp] | function

防止 webpack 解析那些任何與給定正則表達式相匹配的文件。忽略的文件中不應該含有 import, require, define 的調用,或任何其他導入機制。忽略大型的 library 可以提高構建性能。

module.exports = {
  mode: 'devleopment',
  entry: './src/index.js',
  ...
  module: {
    noParse: /jquery|lodash/,
    // 從 webpack 3.0.0 開始,可以使用函數,如下所示
    // noParse: function(content) {
    //   return /jquery|lodash/.test(content);
    // }
  }
  ...
};

npx webpack 簡寫

我們可以將 npx webpack 設置 到 package.json 當中 scripts處 ,重複的書寫都可以如此

    "build": "npx webpack -c webpack.config.js"

module.rules

創建模塊時,匹配請求的規則數組。這些規則能夠修改模塊的創建方式。這些規則能夠對模塊(module)應用 loader,或者修改解析器(parser)。

module.exports = {
  ...
  module: {
    noParse: /jquery|lodash/,
    rules: [
      {
        test: /\.css$/, 
        use: ['style-loader', 'css-loader']
      }
    ]
  }
  ...
};

規則 -->rules : 意思就是 滿足 /.css 開頭的 必須經過 use[‘style-loader’,‘css-loader’]

module.Rule

上面的每一組就是一個 rule

  • Rule 條件詳解
    • 字符串:匹配輸入必須以提供的字符串開始。是的。目錄絕對路徑或文件絕對路徑。
    • 正則表達式:test 輸入值。
    • 函數:調用輸入的函數,必須返回一個真值(truthy value)以匹配。
    • 條件數組:至少一個匹配條件。
    • 對象:匹配所有屬性。每個屬性都有一個定義行爲。

Rule.test

  • { test: Condition }:匹配特定條件。一般是提供一個正則表達式或正則表達式的數組,但這不是強制的。

    module.exports = {
      ...
      module: {
        rules: [
          {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
          }
        ]
      }
      ...
    };
    

    其他的條件比如:

    • { include: Condition }:匹配特定條件。一般是提供一個字符串或者字符串數組,但這不是強制的。
    • { exclude: Condition }:排除特定條件。一般是提供一個字符串或字符串數組,但這不是強制的。
    • { and: [Condition] }:必須匹配數組中的所有條件
    • { or: [Condition] }:匹配數組中任何一個條件
    • { not: [Condition] }:必須排除這個條件
module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.css$/,
        include: [
          path.resolve(__dirname, "app/styles"),
          path.resolve(__dirname, "vendor/styles")
        ],
        use: ['style-loader', 'css-loader']
      }
    ]
  }
  ...
};

匹配 css 並需要 包含 上面的路徑

Rule.use

應用於模塊指定使用一個 loader。

Loaders can be chained by passing multiple loaders, which will be applied from right to left (last to first configured).

加載器可以鏈式傳遞,從右向左進行應用到模塊上。

use: [
  'style-loader',
	//通過json 形式
  {
    loader: 'css-loader'
  },
// 好處是可以附加一些選項的參數
  {
    loader: 'less-loader',
    options: {
      noIeCompat: true
    }
  }
];

加載 Sass 文件

加載 Sass 需要sass-loader

安裝

npm install sass-loader node-sass webpack --save-dev

or

npm i -D  sass-loader node-sass

使用

// webpack.config.js
module.exports = {
   
  module: {
    rules: [{
   	// 加載 以 sa c sa 開頭的 ss 文件 
      test: /\.(sc|c|sa)ss$/,
      // 完整書寫, (推薦簡寫)
      use: [{
        loader: "style-loader"
      }, {
        loader: "css-loader"
      }, {
        loader: "sass-loader"
      }]
    }]
  }
};

爲 sass 文件注入內容:

如果你要將 Sass 代碼放在實際的入口文件(entry file)之前,可以設置 data 選項。此時 sass-loader 不會覆蓋 data 選項,只會將它拼接在入口文件的內容之前。

{
    loader: "sass-loader",
    options: {
        data: "$env: " + process.env.NODE_ENV + ";"
    }
}

注意:由於代碼注入, 會破壞整個入口文件的 source map。 通常一個簡單的解決方案是,多個 Sass 入口。

創建Source Map

css-loadersass-loader都可以通過該 options 設置啓用 sourcemap。

啓用的好處就是 可以在審查元素中尋找到

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [{
      test: /\.scss$/,
      use: [{
        loader: 'style-loader', {
        loader: "css-loader",
        options: {
          sourceMap: true
        }
      }, {
        loader: "sass-loader",
        options: {
          sourceMap: true
        }
      }]
    }]
  }
};

PostCss 處理loader (附帶:添加css3前綴)

PostCSS是一個 CSS 的預處理工具,可以幫助我們:給 CSS3 的屬性添加前綴,樣式格式校驗(stylelint),提前使用 css 的新特性比如:表格佈局,更重要的是可以實現 CSS 的模塊化,防止 CSS 樣式衝突。

我們常用的就是使用 PostCSS 進行添加前綴,以此爲例:

安裝

npm i -D postcss-loader
npm install autoprefixer --save-dev

# 以下可以不用安裝
# cssnext可以讓你寫CSS4的語言,並能配合autoprefixer進行瀏覽器兼容的不全,而且還支持嵌套語法
$ npm install postcss-cssnext --save-dev

# 類似scss的語法,實際上如果只是想用嵌套的話有cssnext就夠了
$ npm install precss --save-dev

# 在@import css文件的時候讓webpack監聽並編譯
$ npm install postcss-import --save-dev

postcss-loader官網參考

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
               // ident 相當於 唯一標識符
              ident: 'postcss',
              sourceMap: true,
              plugins: loader => [
                  // 添加前綴 
                require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) 
              ]
            }
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      }
    ]
  }
};

樣式表抽離成專門的單獨文件並且設置版本號

首先以下的 css 的處理我們都把 mode 設置爲 production

webpack4 開始使用: mini-css-extract-plugin插件, 1-3 的版本可以用: extract-text-webpack-plugin

抽取了樣式,就不能再用 style-loader注入到 html 中了。

npm install --save-dev mini-css-extract-plugin
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'production'; // 判斷當前環境是開發環境還是 部署環境,主要是 mode屬性的設置值。

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
      rules: [
        {
          test: /\.(sa|sc|c)ss$/,
          use: [
            MiniCssExtractPlugin.loader,
            'css-loader',
            {
              loader:'postcss-loader',
              options:{
                    // ident 相當於 唯一標識符
                  ident: 'postcss',
                  sourceMap: true,
                  plugins: loader => [
                      // 添加前綴 
                    require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) 
                  ]
              }
            },
            {
              loader:'sass-loader',
              options: {
                sourceMap: true
              }
            }
          ]
        }
      ]
    },
  plugins: [
    new MiniCssExtractPlugin({
      filename: devMode ? '[name].css' : '[name].[hash].css', // 設置最終輸出的文件名
      chunkFilename: devMode ? '[id].css' : '[id].[hash].css'
    })
  ]
};

再次運行打包:

"dist": "npx webpack --config webpack.product.config.js"

最後運行項目

npm run dist

在 dist 目錄中已經把 css 抽取到單獨的一個 css 文件中了。修改 html,引入此 css 就能看到結果了。

壓縮CSS

webpack5 貌似會內置 css 的壓縮,webpack4 可以自己設置一個插件即可。

壓縮 css 插件:optimize-css-assets-webpack-plugin

使用optimize-css-assets-webpack-plugin 壓縮後

[外鏈圖片轉存失敗(img-mYLX7Q6F-1568858497850)(E:\Typora\筆記地址\markdown\img\webpackImg\1568791443933.png)]

npm i -D optimize-css-assets-webpack-plugin
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const autoprefixer = require('autoprefixer');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader'
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: loader => [autoprefixer({ browsers: ['> 0.15% in CN'] })]
            }
          },
          {
            loader: 'sass-loader'
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name][hash].css',
      chunkFilename: '[id][hash].css'
    })
  ],
  optimization: {
    minimizer: [new OptimizeCSSAssetsPlugin({})]
  }
};

JS 壓縮

壓縮需要一個插件: uglifyjs-webpack-plugin, 此插件需要一個前提就是:mode: 'production'.

安裝

npm i -D uglifyjs-webpack-plugin
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const autoprefixer = require('autoprefixer');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader'
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: loader => [autoprefixer({ browsers: ['> 0.15% in CN'] })]
            }
          },
          {
            loader: 'sass-loader'
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name][hash].css',
      chunkFilename: '[id][hash].css'
    })
  ],
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourceMap: true // set to true if you want JS source maps
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  }
};

解決 CSS 文件或者 JS 文件名字哈希變化的問題

HtmlWebpackPlugin插件,可以把打包後的 CSS 或者 JS 文件引用直接注入到 HTML 模板中,這樣就不用每次手動修改文件引用了。

安裝

npm install --save-dev html-webpack-plugin
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const autoprefixer = require('autoprefixer');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader'
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: loader => [autoprefixer({ browsers: ['> 0.15% in CN'] })]
            }
          },
          {
            loader: 'sass-loader'
          }
        ]
      }
    ]
  },
    plugins: [
    new MiniCssExtractPlugin({
      filename: '[name][hash].css', // 設置最終輸出的文件名
      chunkFilename: '[id][hash].css'
    }),
    new HtmlWebpackPlugin({
      title: 'SpiritMark 全棧線下實習', // 默認值:Webpack App
      filename: 'main.html', //  最終生成的文件名 默認值: 'index.html'
      template: path.resolve(__dirname, 'src/main.html'),  // 模板位置
      minify: {
        collapseWhitespace: true,
        removeComments: true, //移除註釋
        removeAttributeQuotes: true // 移除屬性的引號
      }
    })
  ],
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourceMap: true // set to true if you want JS source maps
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  }
};

清理dist目錄

每次構建,我們的 /dist 文件夾都會保存生成的文件,然後就會非常雜亂。

通常,在每次構建前清理 /dist 文件夾,是比較推薦的做法

clean-webpack-plugin` 是一個比較普及的管理插件,讓我們安裝和配置下。

npm install clean-webpack-plugin --save-dev

webpack.config.js

  const path = require('path');
  ....
+ const CleanWebpackPlugin = require('clean-webpack-plugin');
+ 上面糾錯    const { CleanWebpackPlugin } = require("clean-webpack-plugin");

  module.exports = {
    entry: {
      app: './src/index.js',
      print: './src/print.js'
    },
    plugins: [
+     new CleanWebpackPlugin(['dist'])
+       new CleanWebpackPlugin()
      ...
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
    ...
  };

現在執行 npm run build,再檢查 /dist 文件夾。如果一切順利,你現在應該不會再看到舊的文件,只有構建後生成的文件!

由於最新版本變化@2.0.1之前的寫法已經不能使用:new CleanWebpackPlugin(['/dist'])。 官方文檔地址:https://www.npmjs.com/package/clean-webpack-plugin 可以直接設置一個對象參考: new CleanWebpackPlugin({cleanOnceBeforeBuildPatterns: ['**/*', '!static-files*']})

加載圖片與圖片優化

在 css 文件或者 sass 文件中添加如下代碼

$red: #900;
$size: 20px;

.box {
  height: 30px*2;
  font-size: $size;
  transform: translate3d( 0, 0, 0 );
+ background: url('../static/1.jpeg')
}

運行打包發現如下錯誤:

ERROR in ./src/static/1.jpeg 1:0
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type.

解決方案:file-loader處理文件的導入

npm install --save-dev file-loader

webpack.config.js

  const path = require('path');

  module.exports = {
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            'style-loader',
            'css-loader'
          ]
        },
+       {
+         test: /\.(png|svg|jpg|gif)$/,
+         use: [
+           'file-loader'
+         ]
+       }
      ]
    }
  };

直接複製下面


		 {  test: /\.(png|svg|jpg|gif)$/,
                     use: [
                        'file-loader'
                      ]
                    }

此時運行打包,發現 dist 目錄多了一個圖片文件,另外報錯不再出現。

那更進一步,圖片如何進行優化呢?

image-webpack-loader可以幫助我們對圖片進行壓縮和優化。

npm install image-webpack-loader --save-dev

使用:webpack.config.js

  const path = require('path');

  module.exports = {
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            'style-loader',
            'css-loader'
          ]
        },
        {
          test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
          use: [
            'file-loader',
+           {
+             loader: 'image-webpack-loader',
+             options: {
+               mozjpeg: {
+                 progressive: true,
+                 quality: 65
+               },
+               optipng: {
+                 enabled: false,
+               },
+               pngquant: {
+                 quality: '65-90',
+                 speed: 4
+               },
+               gifsicle: {
+                 interlaced: false,
+               },
+               webp: {
+                 quality: 75
+               }
+             }
+           },
          ]
        }
      ]
    }
  };
{ 
          test: /\.(png|svg|jpg|gif)$/,
          include: [path.resolve(__dirname,'src/')],
           use: [
                  'file-loader',
                  {
                    loader: 'image-webpack-loader',
                    options: {
                      mozjpeg: {
                        progressive: true,
                        quality: 65
                      },
                      optipng: {
                        enabled: false,
                      },
                      pngquant: {
                        quality: '65-90',
                        speed: 4
                      },
                      gifsicle: {
                        interlaced: false,
                      },
                      webp: {
                        quality: 75
                      }
                    }
                  },
                ]
        }

上面是重複方便複製

此時在運行 webpack,發現會 生成的圖片的大小會被壓縮很多。

更進一步處理圖片成 base64

url-loader功能類似於 file-loader,可以把 url 地址對應的文件,打包成 base64 的 DataURL,提高訪問的效率。

如何使用:

npm install --save-dev url-loader

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|svg|jpg|gif|jpeg|ico|woff|woff2|eot|ttf|otf)$/,
        use: [
          {
            loader: 'url-loader', // 根據圖片大小,把圖片優化成base64
            options: {
              limit: 10000  // 當圖片小於10k的時候 壓縮成 bases64 減少網頁請求的次數
            }
          },
          {
            loader: 'image-webpack-loader', // 先進行圖片優化
            options: {
              mozjpeg: {
                progressive: true,
                quality: 65
              },
              optipng: {
                enabled: false
              },
              pngquant: {
                quality: '65-90',
                speed: 4
              },
              gifsicle: {
                interlaced: false
              },
              webp: {
                quality: 75
              }
            }
          }
        ]
      }
    ]
  }
};
字體的處理同圖片

由於 css 中可能引用到自定義的字體,處理也是跟圖片一致。

const path = require('path');

  module.exports = {
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            'style-loader',
            'css-loader'
          ]
        },
        {
          test: /\.(png|svg|jpg|gif)$/,
          use: [
            'file-loader'
          ]
        },
+       {
+         test: /\.(woff|woff2|eot|ttf|otf)$/,
+         use: [
+           'file-loader'
+         ]
+       }
      ]
    }
  };

3.1 開發相關輔助

合併兩個webpack配置文件

開發環境(development)和生產環境(production)配置文件有很多不同點,但是也有一部分是相同的配置內容,如果在兩個配置文件中都添加相同的配置節點,
就非常不爽。

webpack-merge 的工具可以實現兩個配置文件進合併,這樣我們就可以把 開發環境和生產環境的公共配置抽取到一個公共的配置文件中。

安裝

npm install --save-dev webpack-merge

例如:

  webpack-demo
  |- package.json
- |- webpack.config.js
+ |- webpack.common.js  // 公共樣式
+ |- webpack.dev.js  //開發樣式
+ |- webpack.prod.js // 生產環境樣式
  |- /dist
  |- /src
    |- index.js
    |- math.js
  |- /node_modules

自己開發配置

webpack.dev.js

// 路徑
const path = require('path');
// 引入函數
const merge = require('webpack-merge');
// 引入公共配置
const common = require('./webpack.common');
// 開發配置
 let devConfig ={
  // entry  common  公共裏面存在 
    mode: 'development',
    output:{
        filename: 'main.js',
        path: path.resolve(__dirname,'dist')
    },
    devtool: 'inline-source-map',
    module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          'style-loader',
          'css-loader',
          {
            loader:'postcss-loader',
            options:{
                  // ident 相當於 唯一標識符
                ident: 'postcss',
                sourceMap: true,
                plugins: loader => [
                    // 添加前綴  
                  require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) 
                ]
            }
          },
          {
            loader:'sass-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      }
    ]
  }
}

module.exports = merge(common,devConfig)

webpack.common.js

// 存放公共js 
const path = require('path');
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports={
  entry: './src/index.js',
//   output:{
//     //   生產環境需要加hash 開發可以不用
//       filename: 'main.[hash].js',
//       path: path.resolve(__dirname,'dist')
//   },
  module: {
    rules: [
      
    //   圖片處理先經過 image-loader 在經過壓縮處理 
     { 
        test: /\.(png|svg|jpg|gif)$/,
        include: [path.resolve(__dirname,'src/')],
         use: [
                'file-loader',
                {
                  loader: 'url-loader', // 根據圖片大小,把圖片優化成base64
                  options: {
                    // 小於 100k 的全部壓縮 成 base64
                    limit: 100000
                  }
                },
                {
                  loader: 'image-webpack-loader',
                  options: {
                    mozjpeg: {
                      progressive: true,
                      quality: 65
                    },
                    optipng: {
                      enabled: false,
                    },
                    pngquant: {
                      quality: '65-90',
                      speed: 4
                    },
                    gifsicle: {
                      interlaced: false,
                    },
                    webp: {
                      quality: 75
                    }
                  }
                },
              ]
      }
    ]
  },
 plugins: [
    //  壓縮 在 生產壞境需要,開發壞境不需要 
  new HtmlWebpackPlugin({
    title: 'SpiritMark 全棧線下實習', // 默認值:Webpack App
    filename: 'main.html', //  最終生成的文件名 默認值: 'index.html'
    template: path.resolve(__dirname, 'src/main.html'),  // 模板位置
    minify: {
      collapseWhitespace: true,
      removeComments: true, //移除註釋
      removeAttributeQuotes: true // 移除屬性的引號
    }
  }),
  new CleanWebpackPlugin()
]
};

在 Common.js 中公共的地方

  • enry 肯定是一樣的 入口文件

  • 圖片的處理肯定需要,css 在Common.js 中不需要,開發階段用style-loader 生產環境把他提取成一個單獨的css 文件

  • 壓縮 在生產環境(prod)需要 在開發環境不需要,原因是可以提高效率 去掉所有 optimization壓縮設置

webpack.common.js

+ const path = require('path');
+ const CleanWebpackPlugin = require('clean-webpack-plugin');
+ const HtmlWebpackPlugin = require('html-webpack-plugin');
+
+ module.exports = {
+   entry: {
+     app: './src/index.js'
+   },
+   plugins: [
+     new CleanWebpackPlugin(['dist']),
+     new HtmlWebpackPlugin({
+       title: 'Production'
+     })
+   ],
+   output: {
+     filename: '[name].bundle.js',
+     path: path.resolve(__dirname, 'dist')
+   }
+ };

**webpack.dev.js **

+ const merge = require('webpack-merge');
+ const common = require('./webpack.common.js');
+
+ module.exports = merge(common, {
+   devtool: 'inline-source-map',
+   devServer: {
+     contentBase: './dist'
+   }
+ });

webpack.prod.js

+ const merge = require('webpack-merge');
+ const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
+ const common = require('./webpack.common.js');
+
+ module.exports = merge(common, {
+   plugins: [
+     new UglifyJSPlugin()
+   ]
+ });

js 使用 source map

當 webpack 打包源代碼時,可能會很難追蹤到錯誤和警告在源代碼中的原始位置。例如,如果將三個源文件(a.js, b.js 和
c.js)打包到一個 bundle(bundle.js)中,而其中一個源文件包含一個錯誤,那麼堆棧跟蹤就會簡單地指向到 bundle.js。

使用 inline-source-map 選項,這有助於解釋說明 js 原始出錯的位置。(不要用於生產環境):

生產環境越乾淨越好

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const CleanWebpackPlugin = require('clean-webpack-plugin');

  module.exports = {
    entry: {
      app: './src/index.js',
      print: './src/print.js'
    },
+   devtool: 'inline-source-map',
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Development'
      })
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

[外鏈圖片轉存失敗(img-Lote1K22-1568858497853)(E:\Typora\筆記地址\markdown\img\webpackImg\webpackinline.png)]

監控文件變化,自動編譯。使用觀察模式

每次修改完畢後,都手動編譯異常痛苦。最簡單解決的辦法就是啓動watch

npx webpack --watch

當然可以添加到 npm 的 script 中

package.json

{
    "name": "development",
    "version": "1.0.0",
    "description": "",
    "main": "webpack.config.js",
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "npx webpack --config webpack.dev.js",
    "dist": "npx webpack --config webpack.prod.js",
 + 	"watch": "npx webpack --watch --config webpack.dev.js"
    },
    "devDependencies": {
      "clean-webpack-plugin": "^0.1.16",
      "css-loader": "^0.28.4",
      "csv-loader": "^2.1.1",
      "file-loader": "^0.11.2",
      "html-webpack-plugin": "^2.29.0",
      "style-loader": "^0.18.2",
      "webpack": "^3.0.0",
      "xml-loader": "^1.2.1"
    }
  }

但是有個 bug,就是每次我們修改 js 或者 css 文件後,要看到修改後的 html 的變化,需要我自己重新刷新頁面。

如何能不刷新頁面,自動更新變化呢?

使用 webpack-dev-server 和熱更新

webpack-dev-server 爲你提供了一個簡單的 web 服務器,並且能夠實時重新加載(live reloading)。

安裝

npm install --save-dev webpack-dev-server

編譯到內存當中,生產環境需要開發環境不需要

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const CleanWebpackPlugin = require('clean-webpack-plugin');

  module.exports = {
    entry: {
      app: './src/index.js',
      print: './src/print.js'
    },
    devtool: 'inline-source-map',
+   devServer: {
+     contentBase: './dist' // 文件輸出路徑
+   },
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Development'
      })
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

啓動此 webserver:

webpack-dev-server --open

官網其他配置

devServer: {
  clientLogLevel: 'warning', // 可能的值有 none, error, warning 或者 info(默認值)
  hot: true,  // 啓用 webpack 的模塊熱替換特性, 這個需要配合: webpack.HotModuleReplacementPlugin插件
  contentBase:  path.join(__dirname, "dist"), // 告訴服務器從哪裏提供內容, 默認情況下,將使用當前工作目錄作爲提供內容的目錄
  compress: true, // 一切服務都啓用gzip 壓縮
  host: '0.0.0.0', // 指定使用一個 host。默認是 localhost。如果你希望服務器外部可訪問 0.0.0.0
  port: 8080, // 端口
  open: true, // 是否打開瀏覽器
  overlay: {  // 出現錯誤或者警告的時候,是否覆蓋頁面線上錯誤消息。
    warnings: true,
    errors: true
  },
  publicPath: '/', // 此路徑下的打包文件可在瀏覽器中訪問。
  proxy: {  // 設置代理
    "/api": {  // 訪問api開頭的請求,會跳轉到  下面的target配置
      target: "http://192.168.0.102:8080",
      pathRewrite: {"^/api" : "/mockjsdata/5/api"}
    }
  },
  quiet: true, // necessary for FriendlyErrorsPlugin. 啓用 quiet 後,除了初始啓動信息之外的任何內容都不會被打印到控制檯。這也意味着來自 webpack 的錯誤或警告在控制檯不可見。
  watchOptions: { // 監視文件相關的控制選項
    poll: true,   // webpack 使用文件系統(file system)獲取文件改動的通知。在某些情況下,不會正常工作。例如,當使用 Network File System (NFS) 時。Vagrant 也有很多問題。在這些情況下,請使用輪詢. poll: true。當然 poll也可以設置成毫秒數,比如:  poll: 1000
    ignored: /node_modules/, // 忽略監控的文件夾,正則
    aggregateTimeout: 300 // 默認值,當第一個文件更改,會在重新構建前增加延遲
  }
}

如何啓用熱更新呢?

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const CleanWebpackPlugin = require('clean-webpack-plugin');
+ const webpack = require('webpack');

  module.exports = {
    entry: {
       app: './src/index.js'
    },
    devtool: 'inline-source-map',
    devServer: {
      contentBase: './dist',
+     hot: true
    },
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Hot Module Replacement'
      }),
+     new webpack.NamedModulesPlugin(),  // 更容易查看(patch)的依賴
+     new webpack.HotModuleReplacementPlugin()  // 替換插件
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

JS啓用babel轉碼

雖然現代的瀏覽器已經兼容了**96%**以上的ES6的語法了,但是爲了兼容老式的瀏覽器(IE8、9)我們需要把最新的ES6的語法轉成ES5的。那麼babel的loader就出場了。

安裝

npm i -D babel-loader babel-core babel-preset-env

用法

在webpack的配置文件中,添加js的處理模塊。

module: {
  rules: [
    {
        
      test: /\.js$/, //滿足js 的文件 就使用 babel-loader 處理
      exclude: /(node_modules)/,  // 加快編譯速度,不包含node_modules文件夾內容
      use: {
        loader: 'babel-loader'
      }
    }
  ]
}

然後,在項目根目錄下,添加babel的配置文件 .babelrc.

.babelrc文件如下:

{
  "presets": ["env"]
}

最後,在入口js文件中,添加ES6的❤新語法:

class Temp {
  show() {
    console.log('this.Age :', this.Age);
  }
  get Age() {
    return this._age;
  }
  set Age(val) {
    this._age = val + 1;
  }
}

let t = new Temp();
t.Age = 19;

t.show();

最後打包:

npx webpack

最終打包後的js代碼:

var a = 1,
    b = 3,
    c = 9;

console.log('a :', a);
console.log('b :', b);
console.log('c :', c);

var Temp = function () {
  function Temp() {
    _classCallCheck(this, Temp);
  }

  _createClass(Temp, [{
    key: 'show',
    value: function show() {
      console.log('this.Age :', this.Age);
    }
  }, {
    key: 'Age',
    get: function get() {
      return this._age;
    },
    set: function set(val) {
      this._age = val + 1;
    }
  }]);

  return Temp;
}();

var t = new Temp();
t.Age = 19;

t.show();

Babel優化

babel-loader可以配置如下幾個options:

  • 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
    目錄,將會降級回退到操作系統默認的臨時文件目錄。
  • cacheIdentifier:默認是一個由 babel-core 版本號,babel-loader
    版本號,.babelrc 文件內容(存在的情況下),環境變量 BABEL_ENV 的值(沒有時降級到
    NODE_ENV)組成的字符串。可以設置爲一個自定義的值,在 identifier 改變後,強制緩存失效。
  • forceEnv:默認將解析 BABEL_ENV 然後是 NODE_ENV。允許你在 loader 級別上覆蓋 BABEL_ENV/NODE_ENV。對有不同 babel 配置的,客戶端和服務端同構應用非常有用。

注意:sourceMap 選項是被忽略的。當 webpack 配置了 sourceMap 時(通過 devtool 配置選項),將會自動生成 sourceMap。

babel 在每個文件都插入了輔助代碼,使代碼體積過大.babel 對一些公共方法使用了非常小的輔助代碼,比如 _extend。 默認情況下會被添加到每一個需要它的文件中。你可以引入 babel runtime 作爲一個獨立模塊,來避免重複引入。

安裝:

npm install babel-plugin-transform-runtime --save-dev
npm install babel-runtime --save

配置:

webpack.config.js

rules: [
  // 'transform-runtime' 插件告訴 babel 要引用 runtime 來代替注入。
  {
    test: /\.js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
      loader: 'babel-loader',
    }
  }
]

修改.babelrc

{
  "presets": ["env"],
  "plugins": [
    ["transform-runtime", {
      "helpers": true,
      "polyfill": true,
      "regenerator": true,
      "moduleName": "babel-runtime"
    }]
  ]
}

此時,webpack打包的時候,會自動優化重複引入公共方法的問題。

ESLint校驗代碼格式規範

安裝

npm install eslint --save-dev
npm install eslint-loader --save-dev

# 以下是用到的額外的需要安裝的eslint的解釋器、校驗規則等
npm i -D babel-eslint standard

使用

// webpack.config.js
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        options: {
          // eslint options (if necessary)
          fix: true
        }
      },
    ],
  },
  // ...
}

eslint配置可以直接放到webpack的配置文件中,也可以直接放到項目根目錄的 .eslintrc文檔

// .eslintrc.js
// https://eslint.org/docs/user-guide/configuring
module.exports = {
  root: true,
  parserOptions: {
    parser: 'babel-eslint'
  },
  env: {
    browser: true  //瀏覽器
  },
  extends: [
    // https://github.com/standard/standard/blob/master/docs/RULES-en.md
    'standard'  //校驗標準 是 standad.js
  ],
  globals: {
    NODE_ENV: false
  },
  rules: {
    // allow async-await
    'generator-star-spacing': 'off',
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    // 添加,分號必須
    semi: ['error', 'always'],
    'no-unexpected-multiline': 'off',
    'space-before-function-paren': ['error', 'never'],
    // 'quotes': ["error", "double", { "avoidEscape": true }]
    quotes: [
      'error',
      'single',
      {
        avoidEscape: true
      }
    ]
  }
};

此時eslint的配置就結束了。

到此爲止,一個完整的開發階段的webpack的配置文件

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');
const autoprefixer = require('autoprefixer');
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, './dist')
  },
  devtool: 'inline-source-map',
  devServer: {
    clientLogLevel: 'warning', // 可能的值有 none, error, warning 或者 info(默認值)
    hot: true, // 啓用 webpack 的模塊熱替換特性, 這個需要配合: webpack.HotModuleReplacementPlugin插件
    contentBase: path.join(__dirname, "dist"), // 告訴服務器從哪裏提供內容, 默認情況下,將使用當前工作目錄作爲提供內容的目錄
    compress: true, // 一切服務都啓用gzip 壓縮
    host: '0.0.0.0', // 指定使用一個 host。默認是 localhost。如果你希望服務器外部可訪問 0.0.0.0
    port: 8085, // 端口
    open: true, // 是否打開瀏覽器
    overlay: { // 出現錯誤或者警告的時候,是否覆蓋頁面線上錯誤消息。
      warnings: true,
      errors: true
    },
    publicPath: '/', // 此路徑下的打包文件可在瀏覽器中訪問。
    proxy: { // 設置代理
      "/api": { // 訪問api開頭的請求,會跳轉到  下面的target配置
        target: "http://192.168.0.102:8080",
        pathRewrite: {
          "^/api": "/mockjsdata/5/api"
        }
      }
    },
    quiet: true, // necessary for FriendlyErrorsPlugin. 啓用 quiet 後,除了初始啓動信息之外的任何內容都不會被打印到控制檯。這也意味着來自 webpack 的錯誤或警告在控制檯不可見。
    watchOptions: { // 監視文件相關的控制選項
      poll: true, // webpack 使用文件系統(file system)獲取文件改動的通知。在某些情況下,不會正常工作。例如,當使用 Network File System (NFS) 時。Vagrant 也有很多問題。在這些情況下,請使用輪詢. poll: true。當然 poll也可以設置成毫秒數,比如:  poll: 1000
      ignored: /node_modules/, // 忽略監控的文件夾,正則
      aggregateTimeout: 300 // 默認值,當第一個文件更改,會在重新構建前增加延遲
    }
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules)/, // 加快編譯速度,不包含node_modules文件夾內容
        use: [{
          loader: 'babel-loader'
        },{
          loader: 'eslint-loader',
          options: {
            fix: true
          }
        }]
      },
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          'style-loader', {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          }, {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              sourceMap: true,
              plugins: (loader) => [autoprefixer({browsers: ['> 0.15% in CN']})]
            }
          }, {
            loader: 'sass-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      }, {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10000
            }
          }
        ]
      }, {
        test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10000
            }
          }, {
            loader: 'image-webpack-loader',
            options: {
              mozjpeg: {
                progressive: true,
                quality: 65
              },
              optipng: {
                enabled: false
              },
              pngquant: {
                quality: '65-90',
                speed: 4
              },
              gifsicle: {
                interlaced: false
              },
              webp: {
                quality: 75
              }
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({filename: '[name].css', chunkFilename: '[id].css'}),
    new CleanWebpackPlugin(['dist']),
    new webpack.NamedModulesPlugin(), // 更容易查看(patch)的依賴
    new webpack.HotModuleReplacementPlugin(), // 替換插件
    new HtmlWebpackPlugin({
      title: 'AICODER 全棧線下實習', // 默認值:Webpack App
      filename: 'index.html', // 默認值: 'index.html'
      minify: {
        collapseWhitespace: true,
        removeComments: true,
        removeAttributeQuotes: true, // 移除屬性的引號
      },
      template: path.resolve(__dirname, 'src/index.html')
    })
  ],
  optimization: {}
};

用於生產環境的配置

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');
const autoprefixer = require('autoprefixer');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules)/, // 加快編譯速度,不包含node_modules文件夾內容
        use: [{
          loader: 'babel-loader'
        },{
          loader: 'eslint-loader',
          options: {
            fix: true
          }
        }]
      },
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader, {
            loader: 'css-loader'
          }, {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: (loader) => [autoprefixer({browsers: ['> 0.15% in CN']})]
            }
          }, {
            loader: 'sass-loader'
          }
        ]
      }, {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10000
            }
          }
        ]
      }, {
        test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
        use: [
          'file-loader', {
            loader: 'image-webpack-loader',
            options: {
              mozjpeg: {
                progressive: true,
                quality: 65
              },
              optipng: {
                enabled: false
              },
              pngquant: {
                quality: '65-90',
                speed: 4
              },
              gifsicle: {
                interlaced: false
              },
              webp: {
                quality: 75
              }
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({filename: '[name][hash].css', chunkFilename: '[id][hash].css'}),
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      title: 'AICODER 全棧線下實習', // 默認值:Webpack App
      filename: 'index.html', // 默認值: 'index.html'
      template: path.resolve(__dirname, 'src/index.html'),
      minify: {
        collapseWhitespace: true,
        removeComments: true,
        removeAttributeQuotes: true, // 移除屬性的引號
      }
    })
  ],
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  }
};

解析(resolve)

配置模塊如何解析。比如: import _ from 'lodash' ,其實是加載解析了lodash.js文件。此配置就是設置加載和解析的方式。

  • resolve.alias

創建 import 或 require 的別名,來確保模塊引入變得更簡單。例如,一些位於 src/ 文件夾下的常用模塊:

// webpack.config.js
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
+ resolve: {
+   alias: {
+     vue$: path.resolve(__dirname, 'src/lib/vue/dist/vue.esm.js'),
+     '@': path.resolve(__dirname, 'src/')
+   }
+ }
  ...
}

// index.js
// 在我們的index.js文件中,就可以直接import
import vue from 'vue';
// 等價於
import vue from  'src/lib/vue/dist/vue.esm.js';
  • resolve.extensions的應用

自動解析確定的擴展。

// webpack.config.js
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  resolve: {
    alias: {
      vue$: path.resolve(__dirname, 'src/lib/vue/dist/vue.esm.js'),
      '@': path.resolve(__dirname, 'src/')
    },
+   extensions: [".js", ".vue",".json"]   // 默認值: [".js",".json"]
  }
  ...
}

給定對象的鍵後的末尾添加 $,以表示精準匹配

外部擴展(externals)

externals 配置選項提供了「從輸出的 bundle 中排除依賴」的方法。 文檔

例如,從 CDN 引入 jQuery,而不是把它打包:

index.html

<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous">
</script>

webpack.config.js

// webpack.config.js
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  alias: {
    extensions: [".js", ".vue",".json"]   // 默認值: [".js",".json"]
    vue$: path.resolve(__dirname, 'src/lib/vue/dist/vue.esm.js'),
    '@': path.resolve(__dirname, 'src/')
  },
+ externals: {
+   jquery: 'jQuery'
+ },
  ...
}

這樣就剝離了那些不需要改動的依賴模塊,換句話,下面展示的代碼還可以正常運行:

import $ from 'jquery';

$('.my-element').animate(...);

具有外部依賴(external dependency)的 bundle 可以在各種模塊上下文(module context)中使用,例如 CommonJS, AMD, 全局變量和 ES2015 模塊。外部 library 可能是以下任何一種形式:

  • root:可以通過一個全局變量訪問 library(例如,通過 script 標籤)。
  • commonjs:可以將 library 作爲一個 CommonJS 模塊訪問。
  • commonjs2:和上面的類似,但導出的是 module.exports.default.
  • amd:類似於 commonjs,但使用 AMD 模塊系統。

​ 不同的配置方式:

externals : {
  react: 'react'
}

// 或者

externals : {
  lodash : {
    commonjs: "lodash",
    amd: "lodash",
    root: "_" // 指向全局變量
  }
}

// 或者

externals : {
  subtract : {
    root: ["math", "subtract"]   // 相當於: window.math.substract
  }
}

構建目標(targets)

webpack 能夠爲多種環境或 target 構建編譯。想要理解什麼是 target 的詳細信息,請閱讀 target 概念頁面。

target: 告知 webpack 爲目標(target)指定一個環境。

可以支持以下字符串值:

選項 描述
async-node 編譯爲類 Node.js 環境可用(使用 fs 和 vm 異步加載分塊)
electron-main 編譯爲 Electron 主進程。
electron-renderer 編譯爲 Electron 渲染進程,使用 JsonpTemplatePlugin, FunctionModulePlugin 來爲瀏覽器環境提供目標,使用 NodeTargetPlugin 和 ExternalsPlugin 爲 CommonJS 和 Electron 內置模塊提供目標。
node 編譯爲類 Node.js 環境可用(使用 Node.js require 加載 chunk)
node-webkit 編譯爲 Webkit 可用,並且使用 jsonp 去加載分塊。支持 Node.js 內置模塊和 nw.gui 導入(實驗性質)
web 編譯爲類瀏覽器環境裏可用(默認)
webworker 編譯成一個 WebWorker

例如,當 target 設置爲 “electron”,webpack 引入多個 electron 特定的變量.

webpack.config.js

// webpack.config.js
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  alias: {
    extensions: [".js", ".vue",".json"]   // 默認值: [".js",".json"]
    vue$: path.resolve(__dirname, 'src/lib/vue/dist/vue.esm.js'),
    '@': path.resolve(__dirname, 'src/')
  },
  externals: {
    jquery: 'jQuery'
  },
+ target: 'node'
  ...
}

相關的loader列表

webpack 可以使用 loader 來預處理文件。這允許你打包除 JavaScript 之外的任何靜態資源。你可以使用 Node.js 來很簡單地編寫自己的 loader。

文件

  • raw-loader 加載文件原始內容(utf-8)
  • val-loader 將代碼作爲模塊執行,並將 exports 轉爲 JS 代碼
  • url-loader 像 file loader 一樣工作,但如果文件小於限制,可以返回 data URL
  • file-loader 將文件發送到輸出文件夾,並返回(相對)URL

JSON

  • json-loader 加載 JSON 文件(默認包含)
  • json5-loader 加載和轉譯 JSON 5 文件
  • cson-loader 加載和轉譯 CSON 文件

轉換編譯(Transpiling)

  • script-loader 在全局上下文中執行一次 JavaScript 文件(如在 script 標籤),不需要解析
  • babel-loader 加載 ES2015+ 代碼,然後使用 Babel 轉譯爲 ES5
  • buble-loader 使用 Bublé 加載 ES2015+ 代碼,並且將代碼轉譯爲 ES5
  • traceur-loader 加載 ES2015+ 代碼,然後使用 Traceur 轉譯爲 ES5
  • ts-loaderawesome-typescript-loader 像 JavaScript 一樣加載 TypeScript 2.0+
  • coffee-loader 像 JavaScript 一樣加載 CoffeeScript

模板(Templating)

  • html-loader 導出 HTML 爲字符串,需要引用靜態資源
  • pug-loader 加載 Pug 模板並返回一個函數
  • jade-loader 加載 Jade 模板並返回一個函數
  • markdown-loader 將 Markdown 轉譯爲 HTML
  • react-markdown-loader 使用 markdown-parse parser(解析器) 將 Markdown 編譯爲 React 組件
  • posthtml-loader 使用 PostHTML 加載並轉換 HTML 文件
  • handlebars-loader 將 Handlebars 轉移爲 HTML
  • markup-inline-loader 將內聯的 SVG/MathML 文件轉換爲 HTML。在應用於圖標字體,或將 CSS 動畫應用於 SVG 時非常有用。

樣式

  • style-loader 將模塊的導出作爲樣式添加到 DOM 中
  • css-loader 解析 CSS 文件後,使用 import 加載,並且返回 CSS 代碼
  • less-loader 加載和轉譯 LESS 文件
  • sass-loader 加載和轉譯 SASS/SCSS 文件
  • postcss-loader 使用 PostCSS 加載和轉譯 CSS/SSS 文件
  • stylus-loader 加載和轉譯 Stylus 文件

清理和測試(Linting && Testing)

  • mocha-loader 使用 mocha 測試(瀏覽器/NodeJS)
  • eslint-loader PreLoader,使用 ESLint 清理代碼
  • jshint-loader PreLoader,使用 JSHint 清理代碼
  • jscs-loader PreLoader,使用 JSCS 檢查代碼樣式
  • coverjs-loader PreLoader,使用 CoverJS 確定測試覆蓋率

框架(Frameworks)

  • vue-loader 加載和轉譯 Vue 組件
  • polymer-loader 使用選擇預處理器(preprocessor)處理,並且 require() 類似一等模塊(first-class)的 Web 組件
  • angular2-template-loader 加載和轉譯 Angular 組件
  • Awesome 更多第三方 loader,查看 awesome-webpack 列表

打包分析優化

webpack-bundle-analyzer插件可以幫助我們分析打包後的圖形化的報表。

僅僅在開發環境使用。

安裝

npm install --save-dev webpack-bundle-analyzer
+ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  module.exports = {
    plugins: [
+     new BundleAnalyzerPlugin()
    ]
  }

other

webpack還是有很多其他需要學習的內容。 請參考官網,或者研究一下vue-cli的生成的webpack的相關配置,也很值得學習。

另外其他腳手架生成的相關配置都可以研究一下比如:create-react-appyo

本篇博客,由於圖片是存放在本地,所以有些地方就顯示不出來,就刪除省略了,不過既然是文字筆記,有沒有圖片應該都差不多, 博客寫的不是很好,謝謝大家

本地筆記地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章