webpack4.X 實戰(三):企業SPA 24點總結(上)

1. 區分 開發 / 生產環境 webpack-merge

  • 遵循不重複原則(DRY)vue

    webpack 的相關配置須要保留一個 common配置、一個dev配置、一個prod配置node

    經過 webpack-merge 包將其整合jquery

  • 開發環境時 一些工具的使用是沒有意義的,好比 壓縮代碼、文件名哈希、分離代碼等...webpack

  • 項目中 安裝、配置以下es6

    npm i webpack-merge@4.1.5 -D
    複製代碼
    // webpack/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/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/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()
      ]
    });
    複製代碼
    // package.json 配置 npm script
    
    {
      "scripts": {
        "start": "webpack-dev-server --open --config webpack/webpack.dev.js",
        "build": "webpack --config webpack/webpack.prod.js"
      }
    }
    複製代碼

2. 配置 source map

  • source map 反應資源的映射關係,用於 定位代碼中的錯誤web

  • 開發環境下 建議正則表達式

    {
        devtool: "cheap-module-eval-source-map"
    }
    複製代碼
  • 生產環境下 建議

    {
        devtool: false
    }
    複製代碼
  • 詳細配置 見官方

3. 搖樹優化 Tree Shaking

  • 做用:

    • 淨化 JS 中無用的代碼
  • 符合以下條件,自動開啓 Tree Shaking:

    • webpack 4.X 生產模式下

    • 編碼時,遵循 ES6 模塊化語法(require 無效)

    • 編譯時,不要編譯 ES6模塊

    // .babelrc 配置以下
    
    {
        "presets": [
            [
                "@babel/preset-env",
                {
                    "modules": false
                }
            ]
        ]
    }
    複製代碼
  • 生產模式下 ES6 模塊化語法 實踐:

    • 實踐一:

      // a.js 文件
      
      const dict = 'dict';
      const dictMedia = 'dictMedia';
      
      export default {
          dict,
          dictMedia
      };
      複製代碼
      // 引入 a.js 文件
      
      import dicts from '/a';
      
      console.log(dicts);
      // a.js 文件中的 dict、dictMedia 都被打包   
      複製代碼
    • 實踐二:

      // a.js 文件
      
      const dict = 'dict';
      const dictMedia = 'dictMedia';
      
      export default {
          dict,
          dictMedia
      };
      複製代碼
      // 引入 a.js 文件
      
      import dicts from '/a';
      
      console.log(dicts.dict);    
      // a.js 文件中的 dict 被打包;dictMedia 不被打包
      複製代碼
    • 實踐三:

      // a.js 文件
         
      const dict = 'dict';
      const dictMedia = 'dictMedia';
         
      export {
          dict,
          dictMedia
      };
      複製代碼
      // 引入 a.js 文件
         
      import {dict, dictMedia} from '/a';
         
      console.log(dict, dictMedia);    
      // a.js 文件中的 dict 被打包;dictMedia 不被打包
      複製代碼
    • 實踐四:

      // a.js 文件
      
      const dict = 'dict';
      const dictMedia = 'dictMedia';
      
      export {
          dict,
          dictMedia
      };
      複製代碼
      // 引入 a.js 文件
      
      import {dict} from '/a';
      
      console.log(dict);  
      // a.js 文件中的 dict 被打包;dictMedia 不被打包
      複製代碼

4. 做用域提高 Scope Hoisting

  • 概述

    • Scope Hoisting 可讓webpack打包出來的代碼文件更小、運行更快

    • webpack4 的生產模式,默認開啓 Scope Hoisting

    • webpack3 推出的功能,須要手動開啓 粗略講解 在此

  • 原理:

    • 分析出模塊之間的依賴關係,儘量的把打散的模塊合併到一個函數中去

    • 前提是不能形成代碼冗餘

  • 是否開啓 Scope Hoisting 的對比

    // util.js 文件
    
    export default 'Hello,Webpack';
    複製代碼
    // main.js 入口文件
    
    import str from './util.js';
    console.log(str);
    複製代碼
    // 未開啓 Scope Hoisting 打包後以下
    
    [
      (function (module, __webpack_exports__, __webpack_require__) {
        var __WEBPACK_IMPORTED_MODULE_0__util_js__ = __webpack_require__(1);
        console.log(__WEBPACK_IMPORTED_MODULE_0__util_js__["a"]);
      }),
      (function (module, __webpack_exports__, __webpack_require__) {
        __webpack_exports__["a"] = ('Hello,Webpack');
      })
    ]
    複製代碼
    // 開啓 Scope Hoisting 打包後以下
    
    [
      (function (module, __webpack_exports__, __webpack_require__) {
        var util = ('Hello,Webpack');
        console.log(util);
      })
    ]
    複製代碼

5. 啓動開發服務 熱替換 HMR

  • 全稱:Hot Module Replacement

  • 應用場景:開發環境下

  • 做用:熱替換 HMR,在啓動開發服務時,局部加載 頁面被修改之處;加快開發編譯速度

    保留在徹底從新加載頁面時丟失的應用程序狀態

    只更新變動內容,以節省寶貴的開發時間

    調整樣式更加快速:幾乎至關於在瀏覽器調試器中更改樣式

  • 限制:HMR 是可選功能(只會影響包含 HMR 代碼的模塊)

    舉個例子,經過 style-loader 爲 style 樣式追加補丁。爲了運行追加補丁,style-loader 實現了 HMR 接口;當它經過 HMR 接收到更新,它會使用新的樣式替換舊的樣式

    若是一個模塊沒有 HMR 處理函數,更新就會冒泡(bubble up)。這意味着一個簡單的處理函數可以對整個模塊樹(complete module tree)進行更新

  • 配置 HMR

    // webpack 配置文件中
    const webpack = require('webpack');
    
    
    module.exports = {
        devServer: {
          contentBase: path.resolve(__dirname,'dist'),
          compress: true,
          host: 'localhost',   
          port:3000,
          hot: true         // 開啓 熱替換
        },
    
        plugins: [
          new webpack.NamedModulesPlugin(),         // 必要的配置
          new webpack.HotModuleReplacementPlugin()  // 必要的配置
        ]
    };
    複製代碼

6. 項目開發中 善用 按需加載

  • 方式一:使用 ES6 的模塊化語法,動態加載模塊 import()

    import('') 語法目前只是 ECMAScript 提案階段,還沒被正式發佈

    詳細見 import API

    // .babelrc 中
    // babel 7
    
    {
        "plugins": [
            "@babel/plugin-syntax-dynamic-import"
        ]
    }
    複製代碼
  • 方式二:使用 webpack 對代碼進行分割,按需加載(webpack 的 require.ensure 語法)

    • require.ensure 是 webpack 語法:

      • 參數1:要依賴的模塊;類型爲 字符串數組;通常爲空

      • 參數2:加載依賴後,自動執行的回調函數

      • 參數3:打包後,js 文件的輸出路徑、js 文件名(chunk名稱)

      const router = new VueRouter({
          routes: [               // 定義路由信息對象
              {
                  path: string,
                 
                  name?: string,    
                 
                  component: (resolve) => {
                      require.ensure([], () => {
                          resolve(require('../../view/demo/index.vue'))
                      }, 'demo')
                  }
                 
              }
          ]
      });
      複製代碼

7. 配置 loader 生效範圍

  • 做用:配置 loader 的生效範圍,可提升編譯速度

  • 以配置 babel-loader 爲例

    // 不推薦
    // webpack配置文件中 配置
    const path = require('path');
    
    module: {
        rules: [{
            test: /\.js$/,
            use: ['babel-loader']
        }]
    }
    複製代碼
    // 推薦
    // webpack配置文件中 配置
    const path = require('path');
    
    module: {
        rules: [{
            test: /\.js$/,
            use: ['babel-loader'],
            exclude: '', // 排除不要加載的文件夾
            include: [path.resolve(__dirname, 'src'), /node_modules/] // 指定須要加載的文件夾
        }]
    }
    複製代碼
  • excludeinclude 的值:

    • 值能夠是單獨項、能夠是數組

    • 能夠是路徑、能夠是正則

  • 項目實戰:

    項目開發中,針對主要 lodaer 如 babel-loaderstyle-loadersass-lodaer

    配置生效範圍: 除了要轉換項目代碼,還要轉換 node_modules 中代碼;不然 針對沒有徹底轉成JS的node包,會報錯

8. 配置 如何解析模塊 resolve

  • 給模塊起別名:resolve.alias

    // webpack 配置
    module.exports = {
      //...
      resolve: {
        alias: {
            '@components': '/src/common'
        }
      }
    };
    複製代碼
    // 項目代碼
    
    import a from '@components/utils';
    
    // 等同於
    import a from '/src/common/utils';
    複製代碼
  • 自動添加後綴 規則:resolve.extensions

    當引入模塊時不帶文件後綴,webpack 會根據配置依次添加後綴 尋找文件

    // webpack 配置
    module.exports = {
      //...
      resolve: {
        extensions: ['.vue', '.js', '.scss', '.css', '.json']
      }
    };
    複製代碼
  • 解析目錄時要使用的文件名:resolve.mainFiles

    默認尋找 index 命名的文件

    // webpack 配置
    module.exports = {
      //...
      resolve: {
        mainFiles: ["index"]
      }
    };
    複製代碼
  • 指明第三方模塊存放的位置,以減小搜索步驟

    • 默認值:[node modules]

    • 默認搜索步驟:先去當前目錄的 /node modules 目錄下去找咱們想找的模塊,若是沒找到,就去上一級目錄 ../node modules 中找,再沒有就去 ../../node modules 中找

    // webpack 配置
    module.exports = {
      //...
      resolve: {
        modules: [path.resolve(__dirname, 'node_modules')]
      }
    };
    複製代碼
  • 引入的模塊,尋找規則:

    不建議寫文件後綴名,就意味着引入的模塊路徑,路徑最後一層有多是文件;有多是文件夾

    默認將路徑的最後一層 視爲文件名,依次匹配 配置的後綴名

    若是沒找到該文件,將路徑的最後一層視爲文件夾,依次匹配 設置的文件名,找到後再 依次匹配設置的後綴名

    如都沒找到,就會報錯

  • resolve 詳細配置 見官網

9. 配置 noParse 讓webpack編譯時 忽略一些文件

  • 場景:

    • 一些沒有采用模塊化的文件,沒有必要讓 webpack 進行處理編譯

    • 經過配置 modules.noParse,可讓 webpack 忽略,提高編譯速度

  • 配置:

    module.exports = merge(common, {
        modules: {
            // 使用正則表達式
            noParse: /jquery|chartjs/
           
            // 或
           
            // 使用函數,從 Webpack 3.0.0 開始支持
            noParse: (content)=> {
              // content 表明一個模塊的文件路徑
              // 返回 true or false
              return /jquery|chartjs/.test(content);
            }
        }
    });
    複製代碼

10. 配置 performance 在生產環境 移除性能警告

  • webpack 打包後,若是文件體積超出默認(250kb)大小,會輸出警告

  • 開發環境下這是一個不錯的提示,但生產環境下徹底不必,能夠經過 performance 進行相關配置

  • 配置以下

    詳細配置 見官網

    // webpack 配置
    
    module.exports = merge(common, {
        performance: {
            hints: false,               // 關閉警告
            maxEntrypointSize: 400000   // 預警值設置成40kb
        }
    });
    複製代碼

11. 配置 項目環境變量 webpack.DefinePlugin

  • 場景:項目開發過程當中,配置 每一個文件中均可以使用的 JS 變量

  • 直接獲取 開發環境變量 process.env.NODE_ENV

    使用webpack4.x,webpack 會將環境變量 process.env.NODE_ENV 的值,設置爲 webpack配置中 mode 的值

    在項目代碼中 能夠直接使用 process.env.NODE_ENV

    // 項目代碼中
    
    console.log(process.env.NODE_ENV);  // development
    複製代碼
  • 設置 / 獲取 新的環境變量

    使用 webpack 的內置插件 DefinePlugin,能夠爲項目代碼定義環境變量

    限制:DefinePlugin 設置的環境變量只能在項目代碼中獲取,不能再 webpack 的配置文件中獲取

    // webpack 配置以下
       
    const webpack = require('webpack');
       
    module.exports = merge(common, {
        plugins: [
            new webpack.DefinePlugin({
                'process.env.NODE_ENV': JSON.stringify('ddd'),      // 覆蓋 process.env.NODE_ENV
                'process.env.globalName': JSON.stringify('globalName'),
                'globalName': JSON.stringify('eeee'),
    
            })
        ]
    });
    複製代碼
    // 項目代碼中
    
    console.log(process.env.NODE_ENV);      // ddd
    console.log(process.env.globalName);    // globalName
    console.log(globalName);                // eeee
    複製代碼

12. npm script 指令傳參(三種方式)

  • 場景:配置webpack時,可能須要經過 npm script 傳參,用來處理不一樣場景下的不一樣需求

  • 方式一:不一樣系統存在 兼容問題

    • 在腳本命令的配置(package.json 的 script 下) 中傳參(window)

      // window 系統下:傳參
      
      "scripts": {
          "server": "webpack-dev-server --open",
          "build:dev":"set type=dev&webapck",
          "build:prod": "set type=prod&webpack"
      },
      複製代碼
    • 在腳本命令的配置(package.json 的 script 下) 中傳參(mac)

      // mac 系統下:傳參
      
      "scripts": {
          "server": "webpack-dev-server --open",
          "build:dev":"export type=dev&&webpack",
          "build:prod": "export type=prod&&webpack"
      },
      複製代碼
    • 接收參數

      // node的語法來讀取type的值,而後根據type的值用ifelse判斷
      
      if(process.env.type== "build"){
          // 生產環境
          var website={
              publicPath:"http://192.168.0.104:1717/"
          }
      }else{
          // 開發環境
          var website={
              publicPath:"http://cdn.jspang.com/"
          }
      }
      複製代碼
  • 方式二:要求 webpack 配置項輸入函數(無系統兼容問題)

    • 在腳本命令的配置(package.json 的 script 下) 中傳參

      // package.json 以下
         
      {
          "scripts": {
              "build": "webpack --env.NODE_ENV=local --env.production --progress"
          },
      }
      複製代碼
    • webpack 配置文件中 獲取環境變量

      必須對 webpack 配置進行一處修改。一般,module.exports 指向配置對象;要使用 env 變量,你必須將 module.exports 轉換成一個函數

      module.exports = env => {
        console.log('NODE_ENV: ', env.NODE_ENV) // 'local'
        console.log('Production: ', env.production) // true
      
        return {
          entry: './src/index.js',
          output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, 'dist')
          }
        }
      };
      複製代碼
    • 執行命令 npm run build,便可在控制檯中 看到輸出的 環境變量的值

      這樣就能夠在 webpack 中經過區分不一樣的環境變量,來配置不一樣的webpack

  • 方式三:無系統兼容問題,無過多要求

    • 在腳本命令的配置(package.json 的 script 下) 中傳參

      // package.json 以下
         
      {
          "scripts": {
              "build": "webpack --prod"
          },
      }
      複製代碼
    • webpack 配置文件中 獲取參數以下

      // webpack 配置文件中
      
      console.log(process.argv);
      // 輸出 [ node 路徑, webpack 路徑, '--prod']
      複製代碼
    • 若是隻是想傳遞一個布爾值,獲取參數以下

      // 安裝 minimist
      
      npm i minimist@1.2.0 -D
      複製代碼
      // webpack 配置文件中
      const processArgv = require('minimist')(process.argv.slice(2));
      
      
      console.log(processArgv.prod);      // true
      複製代碼

附言

  • 小夥伴們,有什麼問題 能夠留言,一塊兒交流哈

  • 接下來,我還會發布幾篇 webpack4.X 實戰文章,敬請關注

  • 我是一名熱衷於編程的前端開發,WX:ZXvictory66

相關文章
相關標籤/搜索