使用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,能够忽略不计哦。

最后: 其核心思想就是使用占位符的方式,将不须要的打包组件进行占位,从而减小打包体积

代码地址

原文地址

相关文章
相关标签/搜索