动态 require 的使用

478 阅读3分钟

动态 require 问题的解决

需求

目前正在开发一个基于element-ui二次开发的UI库。这个库名字暂且叫etion-ui

实际的项目中需要进行etion-ui的调试,这个业务项目暂且叫etion-web,该项目使用的是vue2.x版本的框架。

etion-ui的打包

为了给 etion-web的调试,我这里将etion-ui打包成两个文件夹:

└── etion-ui
    └── dist // 生产环境下的压缩后的代码
          └── etion-ui.umd.js
    └── example // 开发环境下 需要调试无压缩的代码
          └── dist
                └── etion-ui.umd.js

etion-web的引用处理

具体实现

运行脚本如下:

## 1. 我这里采用的`yalc`做本地环境的映射。需要本地已经启动了 yalc 对 etion-ui的映射
## 2. 添加一个环境变量 VUE_APP_UI_TEST=TRUE,然后在etion-web项目中读取到,引入不同的文件达到调试跟线上环境的区分
yalc add etion-ui && cross-env VUE_APP_UI_TEST=TRUE vue-cli-service serve

main.js中的处理

import Vue from 'vue'

// 获取到是否是UI测试环境
const isUiTest = process.env.VUE_APP_UI_TEST === 'TRUE'

/**
* 这里采用 require 动态引入的方式
*/ 
const etionUIPath = isUiTest ? 'etion-ui/example/dist/etion-ui.umd.js' : 'etion-ui/dist/etion-ui.umd.js'
const EtionUI = require(etionUIPath)

Vue.use(EtionUI)

产生的问题

直接运行后直接报错:

Uncaught Error: Cannot find module 'etion-ui/example/dist/etion-ui.umd.js'
    at webpackEmptyContext (eval at ./src sync recursive (app.js:2660:1), <anonymous>:2:10)
    at eval (main.js?56d7:59:1)
    at ./src/main.js (app.js:4923:1)
    at __webpack_require__ (app.js:854:30)
    at fn (app.js:151:20)
    at 1 (app.js:5527:18)
    at __webpack_require__ (app.js:854:30)
    at checkDeferredModules (app.js:46:23)
    at app.js:994:18
    at app.js:997:10

解决产生的问题

看到上述报错的时候,我是一脸黑线。地址也都没有写错。为什么不行呢?

产生原因:

webpack本身是一个预编译路径 不能require纯变量的打包工具,无法预测未知变量路径。所以require(path) ,path 至少要有三部分组成, 目录,文件名和后缀

  • 目录:webpack 才知道从哪里开始查找
  • 文件名:可用变量表示
  • 后缀(文件后缀):必须要加上,不然会报错

错误引用

像上面main.js中的写法是有错误的。因为全变量的时候,webpack找不到具体是哪个模块被引入

const etionUIPath = isUiTest ? 'etion-ui/example/dist/etion-ui.umd.js' : 'etion-ui/dist/etion-ui.umd.js'
const EtionUI = require(etionUIPath)

正确引用

这里放一个范例进行分析:

let imgName = 'a'; 
let imgAllName = 'a.png';
// example 1
let imgUrl = require('../images/a.png');                // 纯字符串
// example 2
let imgUrl = require('../images/' + imgAllName);        // 目录 + 文件全名
// example 3
let imgUrl = require('../images/' + imgName + '.png');  // 目录 + 文件名 + 后缀

鉴于require在纯变量的情况下找不到模块,所以我们至少要在require参数中写明一个目录(如代码中的example 2example 3),这样的话,虽然不知道具体的模块,但是webpack也会为我们做下述的分析工作:

  • 分析目录:'../images'
  • 提取正则表达式 : /^.*.png$/

此种情况下,webpack生成的上下文模块(context module)。并且对上下文模块做如下处理:

  1. 它包含目录下的所有模块的引用,是通过一个 request 解析出来的正则表达式,去匹配目录下所有符合的模块,然后都 require 进来
  2. 此 context module 包含一个 map 对象,会把 request 中所有模块翻译成对应的模块 id。
  3. 根据对应模块id,再去获取到相对应需要引用的模块

这里户产生一个问题:所有可能用到的模块都包含在 bundle 中,导致包的体积变大!!!

main.js 代码调整

import Vue from 'vue'

const isUiTest = process.env.VUE_APP_UI_TEST === 'TRUE'

- const etionUIPath = isUiTest ? 'etion-ui/example/dist/etion-ui.umd.js' : 'etion-ui/dist/etion-ui.umd.js'
- const EtionUI = require(etionUIPath)
+ const prefix = isEtionUiTest() ? 'example/' : ''
+ const EtionUI = require(`etion-ui/${prefix}dist/etion-ui.umd.js`)

Vue.use(EtionUI)

写在最后

这个文章主要是分析,动态require的解决以及自己学习的总结。
通过最后的分析之后,发现其实这里使用动态require的方式并不是一个比较好的方案,会导致包变大。
所以,后续会再进行调整使用更合适的方案。

参考文章

blog.csdn.net/liubangbo/a… (动态引入(require, import))