vite2+vue3+typescript按需打包多页面项目

1,462 阅读2分钟

多页面项目配置百度有好多,但是没找到符合自己实际开发需求的。于是在春节期间自己捣腾了一个,在此分享。(不足之处还望大家斧正) 多页面配置有好多,但是最终打包的时候在dist目录中都混到了一起,而且不能按需打包。感觉这种模式不是在实际项目开发种想要的,故而对以上痛点做了调整。

1. 目录

├── package.json
├── public
│   ├── favicon.ico
│   └── index.html //多页面入口文件
dist // 打包后项目目录
├── page1
│   ├── index.html
│   └── static
│       └── js
│           ├── index-9db47fe5.js
│           └── vendor-602fa68c.js
└── page2
│    ├── index.html
│    └── static
│        └── js
│           ├── index-dd26faef.js
│           └── vendor-719f3429.js
├── scripts
│   ├── build.js // 打包工具函数
│   ├── createPages.js // 创建多页面目录工具函数
│   └── templates // 模板目录
│       ├── morePages // 多页面模板
│       │   ├── App.vue
│       │   ├── index.html
│       │   └── main.ts
│       └── srcHtml.ejs // 多页面入口文件模板
├── src
│   ├── assets
│   │   └── logo.png
│   ├── components
│   │   └── HelloWorld.vue
│   ├── env.d.ts
│   └── pages // 多页面项目目录
│       ├── page1 // 项目
│       │   ├── App.vue
│       │   ├── index.html
│       │   └── main.ts
│       └── page2 // 项目
│           ├── App.vue
│           ├── index.html
│           └── main.ts
├── tsconfig.json
├── vite.config.ts // 多页面配置文件

2. vite.config.ts

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
// 打包工具函数
import build from "./scripts/build";

export default defineConfig(async ({ command, mode }) => {
  let pagesDir;
  // 选择要打包的多页面项目
  if (mode !== "development") {
    await build.main();
    pagesDir = build.pageName;
  }
  return {
    plugins: [vue()],
    root: `./src/pages`,
    base: `http://www.xxx.com`,
    publicDir:`${process.cwd()}/public`,
    resolve: {
      alias: {
        "@": path.resolve(__dirname, "src"),
        components: path.resolve(__dirname, "src/components"),
      },
      dedupe: [],
      // 情景导出package.json 配置中的 exports 字段
      conditions: [],
      // 解析package.json 中的字段
      mainFields: ["module", "jsnext:main", "jsnext"],
      // 导入时想要省略的扩展名列表
      extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"],
    },
    clearScreen: false,
    build: {
      // 浏览器兼容性 ‘esnext’ | 'modules'
      target: "modules",
      //输出路径
      outDir: `${process.cwd()}/dist/`,
      // 小于此阈值的导入或引用资源将内联为 base64 编码, 以避免额外的http请求, 设置为 0, 可以完全禁用此项,
      assetsInlineLimit: 4096,
      // 启动 / 禁用 CSS 代码拆分
      cssCodeSplit: true,
      // 构建后是否生成 soutrce map 文件
      sourcemap: false,
      // 自定义底层的 Rollup 打包配置
      rollupOptions: {
        input: path.resolve(`src/pages/${pagesDir}/index.html`),
        output: {
          chunkFileNames: `${pagesDir}/static/js/[name]-[hash].js`,
          entryFileNames: `${pagesDir}/static/js/[name]-[hash].js`,
          assetFileNames: `${pagesDir}/static/[ext]/[name]-[hash].[ext]`,
        },
      },
    },
  };
});

3. 打包工具函数

import { readdir } from "fs/promises";
import { resolve } from "path";
// 命令行交互工具函数
import inquirer from "inquirer";

class Build {
  constructor() {
    this.pagesPath = resolve("src/pages");
  }
  async main() {
    // 读取pages 项目目录并存储
    await this.setData();
    // 命令行交互(选择打包的项目)
    await this.selectBuildName();
  }
  async setData() {
    const pagesDir = await readdir(this.pagesPath);
    const choices = pagesDir.map((item) => `${item} => 多页面项目`);
    this.questions = [
      {
        type: "rawlist",
        name: "page",
        message: "选择要打包的项目",
        choices,
      },
    ];
  }
  selectBuildName() {
    return inquirer.prompt(this.questions).then(async (answers) => {
      const { page } = answers;
      const pageName = page.split("=>")[0].trim();
      this.pageName = pageName;
    });
  }
}

export default new Build();


4. 创建多页面目录工具函数

import { mkdir, readFile, writeFile, readdir } from "fs/promises";
import { resolve } from "path";

import chalk from "chalk";
import ejs from "ejs";

class CreatePages {
  constructor() {
    this.pagesPath = resolve("src/pages");
    this.pageName = process.argv[2];
    this.mkPageDir();
  }
  // 1 创建目录
  mkPageDir() {
    if(!this.pageName) return this.log(["red", `未输入创建项目的名称`]);
    mkdir(`${this.pagesPath}/${this.pageName}`)
      .then(() => {
        this.log(["green", `项目创建成功!`]);
        this.createFile();
        this.rewriteSrcHtml();
      })
      .catch((e) => {
        this.log(["red", e.message]);
      });
  }
  // 2 复制文件
  createFile() {
    ["main.ts", "index.html", "App.vue"].forEach(async (item) => {
      try {
        const fileStr = await readFile(
          `./scripts/templates/morePages/${item}`,
          "utf-8"
        );
        await writeFile(`${this.pagesPath}/${this.pageName}/${item}`, fileStr, "utf-8");
        this.log(["green", `${item} 文件创建成功!`]);
      } catch (e) {
        this.log(["red", e.message]);
      }
    });
  }
  // 3 根目录index.html 中插入 多页面入口
  async rewriteSrcHtml() {
    const pagesDir = await readdir(this.pagesPath);
    ejs
      .renderFile("./scripts/templates/srcHtml.ejs", { pagesDir })
      .then((res) => {
        return writeFile(`${resolve("public")}/index.html`, res, "utf-8");
      })
      .then(() => {
        this.log(["green", "多页面入口文件创建成功!"]);
      })
      .catch((e) => {
        this.log(["red", e.message]);
      });
  }
  log([key, value]) {
    try {
      console.log(chalk[key](`\n => ${value}`));
    } catch (e) {
      console.log(chalk.red(`\n => ${e.message} \n`));
    }
  }
}

new CreatePages();


5.创建多页面配置命令

// package.json 
"scripts": {
    "dev": "vite --host",
    "beta": "vite build --mode=beta",
    "build": "vite build",
    "preview": "vite preview",
    "mkpage": "node ./scripts/createPages" // 创建命令
  },

yarn mkpage page1(项目名称)

6. 按需打包

MacBook-Pro vite-pages % yarn build
yarn run v1.22.17
warning package.json: No license field
$ vite build
? 选择要打包的项目 
  1) page1 => 多页面项目
  2) page2 => 多页面项目
  Answer:  

项目地址: gitee.com/whplove/vit…