05-01-webpack

js 痛點

  • 文件依賴度高;
  • 命名沖突/變量污染;
  • 可讀性差蹋嵌;

模塊化

  • 把復雜問題分解成相對獨立的模塊,這樣的設計可以降低程序復雜性葫隙,提高代碼的重用栽烂,也有利于團隊協(xié)作開發(fā)與后期的維護和擴展;

模塊化規(guī)范

node:cjs-commonjs
  • 有一個全局對象:module恋脚;
  • 導出一:通過 module.exports腺办;
  • 導出二:通過 exports.a = function() {};
  • 引入:require(path),const a = require('./a.js');
  • 優(yōu)點:
    • 擁有獨立作用域糟描;
    • 依賴關系處理簡單怀喉;
瀏覽器:amd,異步模塊定義
  • 定義模塊:define(id?, depend?, factory)
  • 工廠函數會提供三個參數:
  • require
  • exports
cmd船响,類似于 amd
umd:處理各種模塊化之間的差異
es6:esm-ECMAScript module
  • 使用:<script type='module'></script>

  • 導出一:export default 模塊磺送,默認導出;

  • 引入一:import 模塊名 from 模塊路徑灿意,默認導入估灿;

  • 要求必須在服務器環(huán)境下,才能運行

  • 導出二:export 模塊;缤剧,多導出馅袁;

  • 引入二:import {a as A} from './a.js';,模塊名必須等同于導出模塊名荒辕,可以通過 as 重命名汗销;

  • esm,IE8抵窒、IE9 不支持弛针,這是引入了 webpack,模塊打包器李皇。

webpack

  • webpack:核心代碼削茁;
  • webpack-cli:cli 命令行界面;
正常目錄解讀
  • src:js 代碼,邏輯代碼茧跋;
  • public:html 存放目錄慰丛;
使用
  • webpack 只認識相對路徑;
沒有配置時
  • 可以在控制臺瘾杭,執(zhí)行 node_modules/.bin/webpack ./src/index.js诅病;
自定義配置
  • 創(chuàng)建 webpack.config.js;

  • webpack 會運行到 node 環(huán)境下面粥烁,所有使用module.exports = {};贤笆;

  • entry:入口文件;

  • output:輸出目錄讨阻,是一個對象苏潜;,里面路徑接受絕對路徑变勇;

    // 一對一
    const { resolve } = require("path");
    
    module.exports = {
      // 入口文件
      entry: "./src/index.js",
    
      // 輸出目錄
      output: {
        // 接受絕對路徑
        path: resolve(__dirname, "build"),
        filename: "index.js",
      },
    };
    
    // 多對一
    const { resolve } = require("path");
    
    module.exports = {
      // 入口文件
      entry: [
        './src/index.js',
        './src/b.js'
      ],
    
      // 輸出目錄
      output: {
        // 接受絕對路徑
        path: resolve(__dirname, "build"),
        filename: "index.js",
      },
    };
    
    // 多對多
    entry: {
      index: "./src/index.js",
      b: "./src/b.js"
    },
    
    // 輸出目錄
    output: {
      // 接受絕對路徑
      path: resolve(__dirname, "dist"),
      filename: "[name].js",
      clean: true,
    },
    
  • mode:production恤左、development、none

    • 默認 production搀绣;

webpack 會將任意文件數據作為模塊進行處理;

當 webpack 遇到不能解析的模塊時飞袋,webpack 會找到 module 對象下面的 rules,去匹配對應規(guī)則链患;如果有對應規(guī)則匹配時巧鸭,我們就使用對應的 loader 去解析;

  • module

    module: {
      rules: [
        {
          test: /\.txt$/,
          use: "raw-loader",
        },
        {
          test: /\.(png|gif|jpe?g)$/,
          use: {
            loader: "file-loader",
            options: {
              // 圖片打包后文件名
              name: "[name]_[contenthash].[ext]",
              // 打包后存放目錄麻捻,相對于打包文件夾 dist
              outputPath: './images',  // 使用時纲仍,獲取的是絕對路徑
              // 打包后存放目錄,相對于 dist 文件
              publicPath: "./images", // 使用時贸毕,引用的是相對路徑 ./dist/images/redux_xxx.jpg
    
              // url-loader郑叠,多一個 limit,number 類型明棍;
              // 當圖片小于此值時乡革,url-loader 不會對文件進行打包,而是轉為 base64 格式摊腋,引入到 js 文件中
              // limit: 10240,
            },
          },
        },
        {
          test: /\.css$/,
          // loader 執(zhí)行順序:從下至上沸版,從右至左
          use: [
            "style-loader",
            {
              loader: "css-loader",
              options: {
                // 啟用 url
                url: true,
                // 啟用 @import
                import: true,
                // 啟用 sourcemap
                sourceMap: false,
              },
            },
          ],
        },
      ],
    }
    
    // 自定義 loader
    module.exports = {
      // ...
      // 配置 loader 解析路徑,當自定義 loaders 時兴蒸,需要使用
      // 如在根目錄下創(chuàng)建 loaders 來存放自定義 loader
      resolveLoader: {
        modules: ["node_modules", resolve(__dirname, "./loaders")],
      },
      module: {
        rules: [
          {
            test: /\.doc$/,
            use: "doc-loader",
          },
        ],
      },
    }
    
    const MarkDown = require('markdown-it');
    
    const md = new MarkDown(); 
    
    module.exports = (source) => {
      console.log('source: ', md.render(source));
    
      // return a Buffer or String视粮,返回的結果會直接 eval
      return `export default \`${md.render(source)}\``;
    }
    
    • webpack5 之后,loader 變更
    - asset/resource 發(fā)送一個單獨的文件并導出 URL橙凳。之前通過使用 file-loader 實現蕾殴。
    - asset/inline 導出一個資源的 data URI笑撞。之前通過使用 url-loader 實現。
    - asset/source 導出資源的源代碼区宇。之前通過使用 raw-loader 實現娃殖。
    
  • plugin:都是以類或者構造函數的形式出現

    • clean-webpack-plugin
      // CleanWebpackPlugin
      // webpack4.0
      const { CleanWebpackPlugin } = require("clean-webpack-plugin");
      // ...
      plugins: [
        new CleanWebpackPlugin(),
      ]
      
      // webpack5.0
      output: {
        // 接受絕對路徑
        path: resolve(__dirname, "dist"),
        filename: "main.js",
        clean: true,
      },
      
    • html-webpack-plugin:自動生成一個 html 文件值戳,與打包后的 js 文件相關聯(lián)
    • mini-css-extract-plugin:
      const MiniCssExtractPlugin = require("mini-css-extract-plugin");
      // ...
      module: {
        rules: [
          {
            test: /\.css$/,
            // loader 執(zhí)行順序:從下至上议谷,從右至左
            use: [
              // "style-loader", // 將 css 樣式添加到 head 當中的 style 里面;如果樣式很多時堕虹,導致html文件過大卧晓,同時也不能對 css 進行優(yōu)化,這時可以用 mini-c's's
              MiniCssExtractPlugin.loader,
              "css-loader", // 處理 css 文件
            ],
          },
          {
            test: /\.doc$/,
            use: "doc-loader",
          },
        ],
      },
      
      // ...
      plugins: [
        new MiniCssExtractPlugin({
          filename: '[name].css'
        }),
      ],
      
  • devServer

    devServer: {
      port: 9000,
      open: true, // 自動開啟瀏覽器
      proxy: {
        // 請求地址:服務器地址
        // '/api' : 'http://localhost:3000'
        'api': {
          target: 'http://localhost:3000',
          pathRewrite: {'^/api': ''},
          secure: false,
        }
      },
      hot: true,  // 默認開啟
    }
    
  • devtool:開發(fā)模式下赴捞,方便調試用

    // source-map逼裆,要嚴格遵循下列順序:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
    devtool: 'eval-cheap-source-map',
    
webpack-dev-server
  • webpack4.0:在 package.json 中添加執(zhí)行命令 dev: webpack-dev-server
  • webpack5.0:在 package.json 中添加執(zhí)行命令 dev: webpack serve
  • 啟動開發(fā)服務器赦政,打包后生成的文件胜宇,會存到內存中,不會寫入硬盤恢着,這樣可以提高開發(fā)效率

代碼分割

1.最基本的代碼分割
  // 多對多打包方式桐愉,可能會導致同一模塊打包進多個 chunk
  entry: {
    index: {
      import: "./src/index.js",
      dependOn: ["axios", "a"]
    },
    b: {
      import: "./src/b.js",
      dependOn: ["axios", "a"]
    },
    axios: "axios",
    a: "./src/a.js"
  },
  // 當你選擇使用代碼分割時,webpack可能會受[啟發(fā)式]的在多個chunk中復制依賴模塊掰派。如果發(fā)生了這種事情从诲,這時您有可能得到相同模塊的多個實例,他們之間的狀態(tài)將會很難保證同步靡羡。當然系洛,您可以通過設置 `optimization.runtimeChunk` 為 `“single”` 進行解決。這并不能阻止Webpack在入口點之間復制模塊代碼略步,但它可以防止Webpack在運行時創(chuàng)建同一模塊的兩個實例描扯。
  optimization: {
    // 配置 runtimeChunk 為 single,webpack 會幫助生成一個 runtime chunk趟薄,然后他會把加載的包移到 runtime 中
    runtimeChunk: "single"
  },
2.SplitChunksPlugin
  • CommonsChunkPlugin 曾被用來避免他們之間的重復依賴荆烈,但是不可能再做進一步的優(yōu)化。
  • 從 webpack v4 開始竟趾,移除了 CommonsChunkPlugin憔购,取而代之的是 optimization.splitChunks。
entry: {
  index: "./src/index.js",
  b: "./src/b.js",
},
optimization: {
  splitChunks: {
    // async 表示只從異步加載模塊中進行拆分
    // initial 表示只從入口模塊進行拆分
    chunks: 'all'
  }
},
  • 默認配置
module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 20000,
      minRemainingSize: 0,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      enforceSizeThreshold: 50000,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};
動態(tài)導入
// 異步加載 按需加載
// 使用 import() 函數時岔帽,會返回一個 promise 對象
// 內聯(lián)注釋 magic comments玫鸟,需要按照 json 格式編寫
import( 
  /* webpackChunkName: "my-chunk-name" */
  'axios'
).then(res => {
  console.log('axios: ', res);
})

預加載 / 預獲取

  • 其實是針對于動態(tài)導入資源加載優(yōu)化
  • preload:預加載,當我們配置時犀勒,被配置的模塊會在父 chunk 加載時并行加載該模塊屎飘;
  • prefetch:預獲取妥曲,當我們配置時,被配置的模塊會在瀏覽器閑置時钦购,也就是未來某個時刻下載模塊
import( 
  /* webpackChunkName: "my-chunk-name" */
  /* webpackPrefetch: true */
  /* webpackPreload: true */
  'axios'
).then(res => {
  console.log('axios: ', res);
})

外部擴展

  • 防止將某些 import 的包(package)打包到 bundle 中檐盟,而是在運行時(runtime)再去從外部獲取這些擴展依賴(external dependencies)。
  • key:是我們需要進行外部擴展的包名押桃,后續(xù)打包時不會將該包打包葵萎;
  • value:是該庫聲明的全局變量;
module.exports = {
  //...
  externals: {
    jquery: 'jQuery',
  },
};

tree shaking

  • 將上下文中的dead-code移除唱凯,就像搖樹上的枯葉使其掉落一樣羡忘;
optimization: {
  usedExports: true,
},  
mode: 'production',

webpack.config.js

const { resolve } = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  // 最基本的代碼分割
  // 多對多打包方式,可能會導致同一模塊打包進多個 chunk
  entry: {
    index: "./src/index.js",
    b: "./src/b.js",
  },
  // 當你選擇使用代碼分割時磕昼,webpack可能會受[啟發(fā)式]的在多個chunk中復制依賴模塊卷雕。如果發(fā)生了這種事情,這時您有可能得到相同模塊的多個實例票从,他們之間的狀態(tài)將會很難保證同步漫雕。當然,您可以通過設置 `optimization.runtimeChunk` 為 `“single”` 進行解決峰鄙。這并不能阻止Webpack在入口點之間復制模塊代碼浸间,但它可以防止Webpack在運行時創(chuàng)建同一模塊的兩個實例。
  // optimization: {
  //   // runtimeChunk: "single"
  //   splitChunks: {
  //     // async 表示只從異步加載模塊中進行拆分
  //     // initial 表示只從入口模塊進行拆分
  //     chunks: 'all'
  //   }
  // },

  // 輸出目錄
  output: {
    // 接受絕對路徑
    path: resolve(__dirname, "dist"),
    filename: "[name].js",
    clean: true,
  },

  mode: "development",

  // 配置 loader 解析路徑先馆,當自定義 loaders 時发框,需要使用
  // 在根目錄下創(chuàng)建 loaders 來存放自定義 loader
  resolveLoader: {
    modules: ["node_modules", resolve(__dirname, "./loaders")],
  },

  // loader 使用
  module: {
    rules: [
      {
        test: /\.txt$/,
        use: "raw-loader",
      },
      {
        test: /\.(png|gif|jpe?g)$/,
        use: {
          loader: "file-loader",
          options: {
            // 圖片打包后文件名
            name: "[name]_[contenthash].[ext]",
            // 打包后存放目錄,相對于打包文件夾 dist
            outputPath: "./images", // 使用時煤墙,獲取的是絕對路徑
            // 打包后引用目錄
            publicPath: "./images", // 使用時梅惯,引用的是相對路徑 ./images/redux_xxx.jpg

            // url-loader,多一個 limit仿野,number 類型铣减;
            // 當圖片小于此值時,url-loader 不會對文件進行打包脚作,而是轉為 base64 格式葫哗,引入到 js 文件中
            // limit: 10240,
          },
        },
      },
      {
        test: /\.css$/,
        // loader 執(zhí)行順序:從下至上,從右至左
        use: [
          // "style-loader", // 將 css 樣式添加到 head 當中的 style 里面球涛;如果樣式很多時劣针,導致html文件過大,同時也不能對 css 進行優(yōu)化亿扁,這時可以用 mini-c's's
          MiniCssExtractPlugin.loader,
          "css-loader", // 處理 css 文件
        ],
      },
      {
        test: /\.doc$/,
        use: "doc-loader",
      },
    ],
  },

  // 注冊插件的位置捺典,plugins 的使用
  // webpack 會自動調用對應插件的 apply 方法,把插件注冊到 webpack 對應的生命周期當中
  // 插件 是 webpack 的 支柱 功能从祝。Webpack 自身也是構建于你在 webpack 配置中用到的 相同的插件系統(tǒng) 之上
  // 插件目的在于解決 loader 無法實現的其他事襟己。Webpack 提供很多開箱即用的 插件引谜。
  plugins: [
    // new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: "app.html", // 會自動創(chuàng)建一個純凈的 app.html 文件
      template: "./public/index.html", // 會把已有 html 文件中的信息帶上
      title: "app", // <title><%=htmlWebpackPlugin.options.title%></title>
    }),
    new MiniCssExtractPlugin({
      filename: '[name].css'
    }),
  ],

  devServer: {
    port: 9000,
    open: true, // 自動開啟瀏覽器
    proxy: {
      // 請求地址:服務器地址
      // '/api' : 'http://localhost:3000'
      'api': {
        target: 'http://localhost:3000',
        pathRewrite: {'^/api': ''},
        secure: false,
      }
    },
    hot: true,  // 默認開啟
  },

  // source-map,要嚴格遵循下列順序:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
  devtool: 'eval-cheap-source-map',
};
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末擎浴,一起剝皮案震驚了整個濱河市员咽,隨后出現的幾起案子,更是在濱河造成了極大的恐慌贮预,老刑警劉巖贝室,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異萌狂,居然都是意外死亡档玻,警方通過查閱死者的電腦和手機怀泊,發(fā)現死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門茫藏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人霹琼,你說我怎么就攤上這事务傲。” “怎么了枣申?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵售葡,是天一觀的道長。 經常有香客問我忠藤,道長挟伙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任模孩,我火速辦了婚禮尖阔,結果婚禮上,老公的妹妹穿的比我還像新娘榨咐。我一直安慰自己介却,他們只是感情好,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布块茁。 她就那樣靜靜地躺著齿坷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪数焊。 梳的紋絲不亂的頭發(fā)上永淌,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音佩耳,去河邊找鬼遂蛀。 笑死,一個胖子當著我的面吹牛蚕愤,可吹牛的內容都是我干的答恶。 我是一名探鬼主播饺蚊,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼悬嗓!你這毒婦竟也來了污呼?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤包竹,失蹤者是張志新(化名)和其女友劉穎燕酷,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體周瞎,經...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡苗缩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了声诸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酱讶。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖彼乌,靈堂內的尸體忽然破棺而出泻肯,到底是詐尸還是另有隱情,我是刑警寧澤慰照,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布灶挟,位于F島的核電站,受9級特大地震影響毒租,放射性物質發(fā)生泄漏稚铣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一墅垮、第九天 我趴在偏房一處隱蔽的房頂上張望惕医。 院中可真熱鬧,春花似錦噩斟、人聲如沸曹锨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沛简。三九已至,卻和暖如春斥废,著一層夾襖步出監(jiān)牢的瞬間椒楣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工牡肉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留捧灰,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像毛俏,于是被迫代替她去往敵國和親炭庙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內容