不定日拱卒:分享平常开发过程当中的一些小技巧,为更多人提供相似问题的解决方案前端
现代前端开发中,模块化已经成为主流node
通常状况下,咱们引入其余模块是使用 es6 的 import 方法webpack
import moduleA from './modules/moduleA.tsx';
moduleA.doSomething();
复制代码
在具体项目中,有时候咱们的代码须要「经过变量拼接」动态引用其余的模块es6
import 语句不支持拼接路径web
const moduleName = 'moduleA';
const aModule = require(`./modules/${moduleName}.tsx`).default;
aModule.doSomething();
复制代码
测试后,页面效果是符合预期的express
然而,随着 ./modules 目录下文件的增长,咱们发现打包速度愈来愈慢,并且包体积愈来愈大,这违背了咱们初始的认知npm
查阅资料,发现这是因为 webpack 编译原理致使的json
若是你的 require 含有表达式(expressions),webpack 会建立一个上下文(context),由于在编译时(compile time)并不清楚具体是哪个模块被导入bash
webpack 解析 require 语句,提取到的信息以下markdown
Directory: ./modules
Regular expression: /^.*\.tsx$/
复制代码
webpack 会根据信息此信息去遍历目录下全部符合的文件路径,并生成以下的 map
// matchResult.js
var matched = {
'moduleA.tsx': require('./modules/moduleA.tsx'),
'moduleB.tsx': require('./modules/moduleB.tsx'),
'moduleC.tsx': require('./modules/moduleC.tsx'),
...
};
module.exports = function(key) {
return matched[key];
}
复制代码
而后,上面的 require 语句就等效于
require('matchResult.js')(moduleName + '.tsx').default;
复制代码
这意味着 webpack 可以支持动态 require,但会致使全部可能用到的模块都包含在 bundle 中
既然没法经过语言特性解决问题,咱们仍是从代码自己入手,让 webpack 打包时就已经有了完整的代码
而咱们要引用的模块会根据 moduleName 这个参数动态变化,则咱们能够经过编写预处理脚本,在打包前执行,根据参数生成咱们想要的最终代码,再交给 webpack 去打包
动态参数咱们以环境变量的方式传入(此处有其余作法,不在此文讨论范围内)
假设 moduleName 为 moduleA,那么咱们的代码就转化为
// output.tsx
import moduleA from './modules/moduleA.tsx';
export default moduleA;
// main.tsx
import aModule from './output.tsx';
moduleA.doSomething();
...
复制代码
而咱们的关注点就转化为如何经过变量生成 output.tsx 文件了
// output.tpl
import {{moduleName}} from './modules/{{moduleName}}.tsx';
export default {{moduleName}};
复制代码
而后用 moduleName 的值替换掉它们
// output.tsx
import moduleA from './modules/moduleA.tsx';
import moduleB from './modules/moduleB.tsx';
import moduleC from './modules/moduleC.tsx';
const modules = {
moduleA,
moduleB,
moduleC
};
export default modules;
复制代码
// output.tpl
//@importSlot
//@mergeSlot
export default modules;
复制代码
这样的标记位,因为是注释,也不用在生成代码后专门去清除
// preprocess.js
// 从环境变量中读取须要的变量
const modules = (process.env.MODULES || 'moduleA,moduleB,moduleC').split(',');
// 获取标记位末尾坐标值
const getSlotEndIndex = (origin, slot) => origin.indexOf(slot) + slot.length;
// 在指定坐标位置插入内容
const insertContent = (origin, index, content) =>
origin.substring(0, index) + content + origin.substring(index + 1, origin.length);
const generateImportContent = () =>
modules.reduce(
(result, module) => (result += `\nimport ${module} from './modules/${module}.tsx';`),
''
);
const generateMergeContent = () =>
`const modules = {\n` +
modules.reduce(
(result, module) => (result += `${module},`),
''
) +
`\n};`
// 读取 output.tpl 模板文件
const tpl = fs.readFileSync('./output.tpl').toString();
let content = tpl;
// 插入 import 语句
content = insertContent(
content,
getSlotEndIndex(content, '//@importSlot'),
generateImportContent()
);
// 插入 merge 语句
content = insertContent(
content,
getSlotEndIndex(content, '//@mergeSlot'),
generateMergeContent()
);
// 生成最终文件
fs.writeFileSync('./output.tsx', content);
复制代码
node preprocess.js && npm run build
复制代码
有时候,咱们能够适当转变一下思路,采用一些「偏方」来快速解决问题。不定日拱卒,慢慢进步。