[干货]Vite 多项目工程

1,416 阅读3分钟

Vite 多项目工程

概要

  • 打包采取并行编译的方式。因为打包每个 project 都是一个异步的过程,并且它们之间的编译是没有依赖关系的,所以可以并行 => 和 Vue3 源码打包的方式一样
  • Vite 开发环境是 unbundle,生产环境使用 rollup 打包

看得见的思考

npm run build ,发生了什么?

举个🌰

{
  "scripts": {
    "serve": "vue-cli-service serve"
   },
}

控制台运行命令都是相关的可执行命令,node 环境中可以使用 npm 相关的命令,而 npm install 的时候,会把依赖包相关的可执行程序放在 node_modules/.bin。运行 npm run xxx 的时候就会去 bin 文件中查找。运行 npm run serve 实际上是运行 vue-cli-service serve,那为什么不直接运行呢?(这个问题留给屏幕可爱的你传送门)

believe yourself

遇到难题了,不妨想一想它是如何开始的~~~

实现多个打包器

从 package.json 的命令开始,修改 script 中的 "build": "node ./server/server.build.js build",原先的是 "build": "vite build --mode production",当我们运行 npm run build 的时候就会使用 node 去执行 js 文件,此时是 cjs 模块。执行 js 文件的目的是为了能够让我们自定义 vite 打包器。打个比方,vite 是个发动机,在这个 js 文件中我们通过循环,生成多个 vite 发动机。每一个 vite 发动机都是独立的,不会影响其它需要打包的文件。

"build": "vite build --mode production" 这行命令中,是执行了 vite 的 build 方法(不确定是不是这样理解)。所以笔者就想找到 vite 提供打包器,这个打包器是一个方法,参数是配置项。

// ./server/server.build.js
const vite = require("vite");

引入 vite 后,进入到相关的 vite 源码,发现 vite 提供了一个 build 方法,并且参数就是配置项。

/**
 * Bundles the app for production.
 * Returns a Promise containing the build result.
 */
export declare function build(inlineConfig?: InlineConfig): Promise<RollupOutput | RollupOutput[] | RollupWatcher>;

既然知道了 build 方法就是我们要找的,那就按照之前的思路循环要打包的文件。

const vite = require("vite");
const build = vite.build;

async function loopBuild() {
  for (let key in '目标文件夹 List') {
    await buildFunc(key)
      .then((e) => {
        console.log(colors.green("success"));
        console.log(colors.green("> build complete \n"));
      })
      .catch(({ type, err }) => {
        console.log(colors.green("fail"));
      });
  }
}

async function buildFunc(key) {
  return build('配置文件 config');
}

loopBuild();

以上只是实现了多文件打包器,但还有一个问题,我们需要检索项目中的文件。vite 默认是在根目录下的 index.html,现在需求变了,src/pages/project1 下面会有 index.html,src/pages/project2 下面也会有 index.html,我们要打包这两个目录,所以要先检索。检索这两个文件的时候,又会产生一个问题,要从检索到目录中找到目标目录。在项目中增加一个配置文件。

const config = {
  //  src 目录绝对路劲
  srcPath: path.resolve(__dirname, "../src"),
  //  需要打包、本地开发启动的文件路径正则, 空数组代表全部
  includeProject: [/project1/],
};

module.exports = Object.assign(config, ENV_CONFIG);

通过 includeProject 正则匹配的方式,检索全局文件的过程中筛选出目标文件。

实现全局检索目标文件

通过 glob 插件检索全局

const glob = require("glob");
const path = require("path");
const base = require("./base");

const tool = {
  // 缓存路径数据
  entryList: {},
  init() {
    this.getEntryList();
  },
  getEntryList() {
    let entryJS = {},
      entryHtml = {},
      files = glob.sync(`src/page/**/*(index.html|main.{js,ts})`);
    
    //  根据 base.js 中 includeProject 选项过滤不需要编译的项目
    let includeProject = files.reduce(
      (total, filePath) => (base.includeProject.some((val) => val.test(filePath)) && total.push(filePath), total),
      []
    );
    //  最终需要编译的项目
    let buildProject = (includeProject.length && includeProject) || (base.isBuildAll ? files : []);
    //  当前文件是否为JS、TS 正则
    let entryJSReg = /[js|ts]$/g;
    for (let val of buildProject) {
      let pathSplit = val.split("/").slice(1);
      let key = pathSplit.slice(1, 3).join("_");
      (entryJSReg.test(val) ? entryJS : entryHtml)[key] = [path.resolve(base.srcPath, pathSplit.join(path.sep))];
    }
    this.entryList = { entryJS, entryHtml };
  },

};
tool.init();
module.exports = tool;

这样就找到了我们需要打包的目录文件。

Ending

此项目的 github 地址 传送门,如果觉得有用的话,给个 star 哦~

说了好多口水话,希望屏幕面前的你不要介意,最后,希望屏幕面前的你我一直保持着 Stay hungry,Stay foolish 的状态前行~