使用webpack的require.context按需打包

前端性能優化中有一個關鍵點就是:減小打包體積javascript

目前經常使用的按需打包組件的方式有以下幾種:html

  1. 手工import全部須要打包的組件;目前大部分前端 ui 庫都使用的這種方式,在入口文件處導出全部的組件。前端

  2. 使用 Lerna 將每一個庫單獨打包,單獨發佈vue-next等都使用此種方式,在宿主工程手動引入所需packagevue

那麼還有沒有其餘的方式,作到按需打包呢?答案是有的java

1、 分析場景

現有以下一個工程目錄:webpack

├─App.vue
├─assets
│ └─logo.png
├─components
│ └─HelloWorld.vue
├─main.js
├─packages //存放全部組件
│ ├─button
│ │ ├─button-prop.vue  //設計態組件的vue
│ │ └─button.vue //運行時組件的vue
│ └─input
│   ├─input-prop.vue
│   └─input.vue
├─require.js //使用webpack的require.context自動讀取packages目錄下的組件
└─widget-render.js //運行態組件打包

複製代碼

個人工程目錄下全部的組件都存放在packages目錄下。每個組件又分爲:git

  • 設計態的組件的屬性配置面板
  • 運行態組件

如今個人要求是:github

使用require.context自動讀取packages目錄下的運行態組件,並打包發佈。不容許打包設計態組件web

2、解決方案

1. 手動使用 import 逐個引入

// widget-render.js
import button from "./packages/button.vue";
import input from "./packages/input.vue";
...
複製代碼

顯然這不符合個人要求:使用 require.context自動讀取vue-cli

2. 使用 lerna 管理

使用 lerna 管理,此處不作詳情贅述;lerna 管理會將 packages 目錄下的組件單獨發佈成一個包。可是會讓用戶在用戶側手動import多個組件(固然您也能夠說 本身建立一個 index.js 作全局引入)。可是這彷佛也不太符合咱們的要求。

2. 使用 webpack 的 alias+externals 實現

首先咱們先查看一下widget-render.jsrequire.js的內容: 其主要工做就是使用 require.context 去加載全部的 vue 組件,並同步註冊發佈

//require.js

import endsWith from "lodash/endsWith";
/** * 加載組件和組件的屬性面板 * @param {*} ignoreProps */
export default function(ignoreProps = false) {
  let components = [],
    propComponents = [];
  const requireAll = context => context.keys().map(context);

  const component = require.context("./packages", true, /\.vue$/);

  requireAll(component).forEach(file => {
    let item = file.default;
    if (endsWith(item.name, "prop") && !ignoreProps) {
      propComponents.push({
        key: item.name,
        value: item
      });
    } else if (!endsWith(item.name, "prop") && item.name) {
      components.push({
        key: item.name,
        value: item
      });
    }
  });

  return { components, propComponents };
}
複製代碼
import getAllWidgets from "./require";
// 若是你只須要打包組件的話,而忽略屬性面板
const widgets = getAllWidgets(true).components;

export { widgets };

export default {
  widgets
};
複製代碼

(1)默認打包 lib

"scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "lib": "vue-cli-service build --mode lib --target lib src/widget-render.js -name render.js --dest dist-lib"
  }

複製代碼

運行

yarn lib --report
複製代碼

其打包的結果爲:是包含了prop.vue文件的

展現效果爲:

疑問?

爲何,明明打包出來的組件沒有prop,可是爲什麼 report 文件中卻存在prop.vue相關文件呢?

答案

由於 webpack 的require.context會自動去加載packages目錄下的指定文件,因爲上面寫的匹配規則爲.vue\。因此也會加載 prop 結尾的文件。並打包到 lib 中。

(2) 使用 alias+externals 打包

上面講到使用默認的 require.context 會將匹配到的文件都統一打包的,那麼確定有朋友說,我把匹配規則寫成匹配非 prop 結尾的文件。不就能夠了嗎? 歡迎嘗試。

解決辦法:

既然以 prop 結尾的文件被打包,那麼咱們是否是能夠利用 webpack 自帶的 externals 去排除掉呢?同時將全部以 prop 結尾的文件都設置別名。這樣打包便可。

//vue.config.js
module.exports = {
  productionSourceMap: false,
  configureWebpack: config => {
    if (process.env.BUILD_TARGET === "lib") {
      // 將全部匹配的文件都設置一個empty-widget的別名
      config.resolve.alias[/^(\.\/).+(-prop.vue)/] = "empty-widget";
      // 而後利用externals的function模式,對匹配到的文件設置external
      config.externals = [].concat(config.externals || [], [
        function(context, request, callback) {
          if (/^(\.\/).+(-prop.vue)/.test(request)) {
            return callback(null, "empty-widget");
          }
          callback();
        }
      ]);
    }
  }
};
複製代碼

其打包的結果爲:不存在prop文件了

展現效果爲:

最後只須要在宿主工程引入一個yarn add empty-widget便可。empty-widget的體積 19B,能夠忽略不計哦。

最後: 其核心思想就是使用佔位符的方式,將不須要的打包組件進行佔位,從而減小打包體積

代碼地址

原文地址

相關文章
相關標籤/搜索