不定日拱卒:分享平常開發過程當中的一些小技巧,爲更多人提供相似問題的解決方案前端
現代前端開發中,模塊化已經成爲主流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
複製代碼
有時候,咱們能夠適當轉變一下思路,採用一些「偏方」來快速解決問題。不定日拱卒,慢慢進步。