本地开发太慢了,试试模块按需启动?

263 阅读2分钟

背景

项目长期迭代中,功能模块越来越多,项目越来越大,每次启动项目都需要等待很长时间,开发环境下热更新也特别慢。

并且很多时候,其实有些功能模块对于当前的需求是不必要的,有没有方法可以按需加载功能模块呢?

问:入口文件注释掉不需要的功能模块,不就好了?

答:这样必然可以,但是有没有更高级,更方便的方法呢。那有请今天的主角 jscodeshift。

jscodeshift

jscodeshift 是一个基于 codemod 理念的 JavaScript/TypeScript 重构工具,其原理是将 JS/TS 代码解析为抽象语法树(Abstract Syntax Tree,AST),并提供一系列用于访问和修改 AST 的 API 以实现自动化的代码重构。

使用 jscodeshift 可以进行一系列代码转换操作,比如替换变量名、修改函数调用、重构类定义等。它可以帮助开发人员快速而准确地进行大规模的代码修改,尤其适用于需要对遗留代码进行更新或者升级的情况。

方案

原始入口文件 App.tsx

import React from 'react';
import AComp from './aComp';
import BComp from './bComp';
import CComp from './cComp';
function App() {  
    return (    
        <div className="App">
            <AComp />
            <BComp />
            <CComp />
        </div>
   );
}

export default App;

ondemand_build.js 生成新入口 NewApp.tsx

// ondemand_build.js

const { run: jscodeshift } = require('jscodeshift/src/Runner');
const path = require('path');
const colors = require('colors');
const options = {    
    dry: true,    
    print: false,    
    verbose: 1,    
    cpus: 1,
};

const transformPath = path.resolve(__dirname, './transformer.js');
const filepaths = [path.resolve(__dirname, './App.tsx')];
const runParse = async() => {    
    if (filepaths.length === 0) {        
        return;
    } 
    try {        
        const res = await jscodeshift(transformPath, filepaths, options);        
        console.log(colors.bgRed('transform success!', res));    
    } catch (err) {        
        console.log(colors.bgYellow(err), '---err');    
    }
};
runParse();

transformer.js

const fs = require('fs');
const path = require('path');
const outputFilePath = path.resolve(__dirname, './NewApp.tsx');
module.exports = function transformer(file, api, options) {    
    const j = api.jscodeshift;   
    const ast = j(file.source);    
    ast.find(j.ImportDeclaration).forEach(path => {        
        let needChange = false;        
        let name = '';        
        j(path).find(j.Identifier).forEach(path1 => {            
            if (path1.value.name === 'AComp' || path1.value.name === 'BComp') {
                // 将 AComp 和 BComp 删除                
                needChange = true;                
                name = path1.value.name;            
            }        
        });        
        if (needChange) {            
            j(path).replaceWith();            
            j(path).insertBefore(`const ${name} = () => null;`);        
        }    
    });   

    fs.writeFileSync(outputFilePath, ast.toSource(), 'utf-8');    
    return ast.toSource();
};
module.exports.parser = 'tsx';

新入口 NewApp.tsx

import React from 'react';
import CComp from './cComp.tsx';
const AComp = () => null;
const BComp = () => null;
function App() {  
    return (    
        <div className="App">      
            <AComp />      
            <BComp />      
            <CComp />    
        </div>  
    );
}
export default App;

总结

如果想要更加的智能,可以利用 inquirer 命令行交互工具,在 yarn start 动态选择需要加载的功能模块,收集用户答案后,以键值对的方式存储到配置文件中,transformer.js 根据配置文件动态按需引入需要的功能模块。