初衷
当你看到这个标题的时候,可能会觉得很奇怪,为什么需要支持webpack和vite两种构建工具?
确实,对于很多业务场景,webpack和vite都能解决问题,但是相对于一些比较老的项目,vite可能就显得相对乏力了,在进行webpack迁移至vite项目的过程中,经常会遇到很多奇奇怪怪的报错,并且无从下手,这很令人头疼。vite极速的构建效率,可以解决大型项目webpack构建缓慢带来的困扰。
webpack发展历史时长,有丰富的生态环境,随着不断的优化,其稳定性及构建性能都是相当优秀;vite在开发环境利用浏览器Es module特性和esbuild为开发提供了极速的响应,在生产环境使用rollup构建,在treeShakeing还是有很大优势。
结合两者的特性和具体的业务场景:
- 对于能使用vite构建项目,推荐用vite进行构建;
- 历史原因,迁移至vite困难,或者vite不能解决的问题,使用webpack5进行构建。
一、如何设计?
1. 设计目标
- 支持webpack和vite两种构建方式
- 支持React和Vue3两种框架应用开发
2. 基础准备
- 掌握webpack和vite开发环境和生产环境的基本配置。
- 熟悉React和Vue3常用的babel插件、vite插件,根据不同构建使用对应框架配置。
3. 设计原理
- 设计cli工具提供dev和build构建命令。
- 从项目的一份配置文件读取基本配置。
- 根据配置文件或cli命令,进行webpack或vite构建。
二、设计配置文件读取功能
- 判断项目根目录是否存在配置文件。
- 读取项目根目录的hull.conf.ts或hull.conf.js。
- 支持配置文件导出一个对象或者一个函数模块。
1. 判断是否存在某个文件
import { existsSync } from 'fs';
import { join } from 'path';
interface IGetExistFile{
appDirectory: string;
files?: string[];
returnRelative?: boolean;
}
interface IGetExistFileRe{
isOk: boolean;
absFilePath: string;
}
export function getExistFile({ appDirectory, files = [] }: IGetExistFile): IGetExistFileRe {
for (const file of files) {
const absFilePath = join(appDirectory, file);
if (existsSync(absFilePath)) {
return {
isOk: true,
absFilePath,
};
}
}
return {
isOk: false,
absFilePath: '',
};
}
2.读取项目根目录的hull.conf.ts或hull.conf.js。
由于在node环境需要支持ts、es+语法,所以需要@babel/register实现。
import { join } from 'path';
import babel, { IGetBabelOptions } from '@hulljs/babel-preset-hull-app';
const slash = (input: string) => {
const isExtendedLengthPath = /^\\\\\?\\/.test(input);
if (isExtendedLengthPath) {
return input;
}
return input.replace(/\\/g, '/');
};
export const isDefault = <T>(obj: any): T => obj.default || obj;
export const registerNodeCiBabel = function(appDirectory: string, only: string[]): void {
const babelOptions: IGetBabelOptions = {
isTypeScript: true,
isProduction: true,
};
require('@babel/register')({
presets: [[babel, babelOptions]],
extensions: ['.es6', '.es', '.jsx', '.js', '.mjs', '.ts', '.tsx'],
only: only.map((file) => slash(join(appDirectory, file))),
babelrc: false,
cache: false,
});
};
export const getFileExport = async<T>(appDirectory: string, files: string[]): Promise<T> => {
try {
registerNodeCiBabel(appDirectory, files);
const { isOk, absFilePath } = getExistFile({ appDirectory, files: files, returnRelative: false });
if(isOk){
return isDefault<T>(await import(absFilePath));
}else{
throw Error(`${appDirectory} isn't has any ${files.join('、')}`);
}
} catch (error: any) {
throw Error(error);
}
};
3. 读取配置文件
export const CONFIG_FILES = [
'hull.conf.js',
'hull.conf.ts',
];
const configFnOrObj = await getFileExport<IngetUserConfigRe>(fs.realpathSync(process.cwd()), CONFIG_FILES);
let config;
if(typeof configFnOrObj === 'function'){
const resConf = configFnOrObj(env);
config = resConf;
} else if(typeof configFnOrObj === 'object'){
config = configFnOrObj;
}else{
log.error('hull.conf export is a funtion or object!');
return;
}
三、根据不同构建选择不同的配置。
- 读取项目配置配置文件
- 校验配置文件、生成默认值
- webpack构建则生成webpack配置
- vite构建生成vite配置
具体实现:github.com/luoguoxiong…
四、CLI设计
后续更新
最后
项目开源地址:github.com/luoguoxiong…
详细文档:hulljs.netlify.app
欢迎顺手点个star~ 十分感谢!