npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@star_work/vite-plugin-virtual-mpa

v2.0.3

Published

Enhanced fork of vite-plugin-virtual-mpa with file scan mode, virtual module CORS fix, and improved documentation.

Readme

@star_work/vite-plugin-virtual-mpa ⚡

npm version awesome-vite license install size

增强版 fork,基于 vite-plugin-virtual-mpa 开发,包含额外功能和改进。

开箱即用的 Vite MPA插件 📦,支持HTML模板引擎和虚拟文件功能,能够使用一份模板生成多个文件。

原项目: vite-plugin-virtual-mpa by QinXuYang

English | 中文

✨ 增强功能(相比原版)

本 fork 版本包含以下增强:

  • 🆕 文件扫描模式:自动从目录中的 Vue/React 文件生成页面
    • 扫描子目录中的 .vue.tsx.jsx 文件
    • 每个文件自动成为独立的 HTML 页面
    • 只扫描直接子目录(可配置深度)
  • 🎯 页面配置扫描功能:自动提取页面中的 definePage 配置
    • 扫描页面目录,提取 definePage({ ... }) 配置
    • 生成 virtual:page-configs 虚拟模块供运行时使用
    • 自动注入 definePage 函数,避免运行时错误
    • 支持热更新,文件变化时自动重新扫描
  • 🐛 修复虚拟模块跨域问题:自动使用 /@id/ 前缀处理虚拟模块
    • 不再出现加载虚拟入口文件时的 CORS 错误
    • 浏览器兼容的虚拟模块路径
  • 🔧 增强的入口模板:文件扫描模式下支持 componentPath 变量
  • 📝 完善的文档:完整的使用示例和指南

主要功能

  • 💡 EJS 模板渲染(支持 HTML 和 Entry 模板)
  • 💡 完备的 TypeScript 类型提示支持,是一款小而美的插件
  • 🛠️ 自定义模板HTML文件的输出路径,使用一份模板生成多份文件
  • 🛠️ 文件扫描模式:从组件文件自动生成页面
  • 🛠️ 虚拟入口模板:使用一个 entry 模板文件生成多个虚拟入口文件,减少重复代码
  • 🎯 页面配置扫描:自动提取 definePage 配置,生成虚拟模块
  • 🛠️ 支持 MPA 多页面应用,为开发和预览服务器提供 History Fallback 支持
  • 🐛 修复虚拟模块跨域问题

安装

pnpm add -D @star_work/vite-plugin-virtual-mpa
# 或
npm install -D @star_work/vite-plugin-virtual-mpa

使用方式

// vite.config.ts
import { createMpaPlugin, createPages } from '@star_work/vite-plugin-virtual-mpa'

// @see https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    createMpaPlugin({
      pages: [
        // 你可以直接在这里书写页面配置,也可以单独使用 `createPages` 函数并将结果传递到这里。
      ]
    }),
  ],
})

/**
 * 该函数仅仅是将参数转换为一个 pages 数组。
 * 它帮助你在插件之外创建页面配置,主要是为了能够拥有类型提示。
 * 同时在别处统一管理配置的方式可能也能帮助你简化 vite 的配置文件。
 */
const pages = createPages([
  // 你可以传递一个 page 对象或一个 pages 数组。
])

// @see https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    createMpaPlugin({
      pages,
    }),
  ],
})

Options

type FilterPattern = string | RegExp | (string | RegExp)[]
type RewriteRule = false | Rewrite[]
interface WatchHandler {
  (ctx: {
    server: ViteDevServer,
    file: string,
    type: Event
    /**
     * 可以调用这个方法更新页面配置
     * @params pages MPA 页面核心配置,这将会替换默认的 `pages`
     */
    reloadPages: (pages: Page[]) => void
  }): void
}

interface MpaOptions {
  /**
   * 是否在控制台打印log
   * @default true
   */
  verbose?: boolean,
  /**
   * 默认模板文件
   * @default index.html
   */
  template?: `${string}.html`,
  /**
   * 默认入口模板文件,用于生成虚拟入口文件。
   * 与 `template` 类似,允许使用一个模板文件生成多个入口文件。
   * 如果页面配置中指定了 `entryTemplate`,将优先使用页面级别的配置。
   * @see 关于虚拟入口文件章节
   */
  entryTemplate?: string,
  /**
   * 为开发服务器配置 fallback rewrite rules,只会处理 accept=text/html 的文件请求。
   * @see https://github.com/bripkens/connect-history-api-fallback
   */
  rewrites?: RewriteRule,
  /**
   * 为预览服务器配置重定向规则,配置方式同 rewrites。
   * @see https://github.com/bripkens/connect-history-api-fallback
   */
  previewRewrites?: RewriteRule,
  /**
   * 处理模版 HTML 文件,继承自 `transformIndexHtml`。
   * @see https://vitejs.dev/guide/api-plugin#transformindexhtml
   */
  transformHtml?: (
    html: string,
    ctx: IndexHtmlTransformContext & { page: Page },
  ) => IndexHtmlTransformResult;
  /**
   * 用于扫描相似的目录结构,自动生成 pages 配置。
   * 扫描到的配置会追加到 `pages` 中,具有相同 name 的 page 将被忽略
   */
  scanOptions?: {
    /**
     * 要扫描的目录,子目录名称将作为唯一的 page name。
     */
    scanDirs: string | string[];
    /**
     * 扫描模式:'directory' 扫描子目录(默认),'file' 扫描匹配 filePattern 的文件。
     * 当 scanMode 为 'file' 时,只扫描 scanDirs 的直接子目录下的文件。
     * 更深层的文件和 scanDirs 根目录下的文件将被忽略。
     * @default 'directory'
     */
    scanMode?: 'directory' | 'file';
    /**
     * 当 scanMode 为 'file' 时匹配的文件模式(如 '*.vue', '*.tsx')。
     * 支持 glob 模式。只有匹配此模式的文件才会被扫描。
     * 文件只在 scanDirs 的直接子目录中扫描,不会扫描更深层。
     * @default '*.vue'
     */
    filePattern?: string;
    /**
     * 相对于扫描目录的入口文件路径
     * 当 scanMode 为 'file' 时,此选项将被忽略,应使用 entryTemplate 代替。
     */
    entryFile?: string;
    /**
     * 文件扫描模式的入口模板文件。
     * 当 scanMode 为 'file' 时,此模板将用于生成虚拟入口文件。
     * 模板可以使用 EJS 语法,支持变量:componentPath(文件路径)、pageName 和 data 字段。
     */
    entryTemplate?: string;
    /**
     * 自定义虚拟文件的名称(输出文件名)
     * @param name 子目录名称(目录模式)或相对于 scanDirs 的文件路径(文件模式)
     * @param filePath 完整文件路径(仅文件模式)
     */
    filename?: (name: string, filePath?: string) => string;
    /**
     * 为每个扫描到的页面注入的数据。
     * 可以是函数,接收文件/目录信息并返回数据对象。
     */
    data?: Record<string, any> | ((info: { name: string; path: string; dir: string }) => Record<string, any>);
  };
  /**
   * 当项目目录下有一些文件触发相应的事件如添加、删除、修改时,你可能想要重新加载 `pages` 配置 或 重启 ViteDevServer。
   * 你可以通过设置 `watchOptions` 来实现这一目的。
   */
  watchOptions?: WatchHandler | {
    /**
     * 指定需要**包含**的文件,基于 `Rollup.createFilter` 过滤
     * @see https://vitejs.dev/guide/api-plugin.html#filtering-include-exclude-pattern
     */
    include?: Exclude<FilterPattern, null>,
    /**
     * 指定需要**排除**的文件,基于 `Rollup.createFilter` 过滤
     * @see https://vitejs.dev/guide/api-plugin.html#filtering-include-exclude-pattern
     */
    excluded?: Exclude<FilterPattern, null>,
    /**
     * 想要监听的文件事件
     * @default ['add', 'unlink', 'change', 'unlinkDir', 'addDir']
     */
    events?: Event[],
    /**
     * 定义的文件事件触发后,执行自定义逻辑
     */
    handler: WatchHandler
  },
  pages: Array<{
    /**
     * 必填。该名称是一个不包含'/'的普通字符串,它用于生成默认的重定向规则。
     * 如果你想自定义生成文件的路径,请使用filename选项,而不是name选项。
     */
    name: string;
    /**
     * 相对于`build.outDir`的路径,应该以html结尾
     * @default `${name}.html`
     */
    filename?: `${string}.html`;
    /**
     * 更高优先级的模板文件,将会覆盖默认模板
     */
    template?: string;
    /**
     * 自动注入入口文件,如果设置了entry,需要移除模板文件中的entry。
     * 入口文件路径必须以 `'/'` 开头(相对于项目根目录),
     * 或者使用 Vite 虚拟模块格式(以 `'virtual:'` 或 `'\0'` 开头)。
     * 如果同时设置了 `entryTemplate`,此选项将被忽略。
     */
    entry?: string;
    /**
     * 入口模板文件,用于生成虚拟入口文件。
     * 与 `template` 类似,允许使用一个模板文件生成多个入口文件。
     * 模板文件支持 EJS 语法,可以注入页面数据(`data`)和页面名称(`pageName`)。
     * 如果页面配置中未指定,将使用全局的 `entryTemplate` 配置。
     *
     * **注意**:不能同时使用 `entry` 和 `entryTemplate`。
     *
     * @example
     * ```js
     * // entry-template.js
     * import App from '/src/pages/<%= pageName %>/App.vue';
     * console.log('Page:', '<%= pageName %>');
     * createApp(App).mount('#root');
     * ```
     */
    entryTemplate?: string;
    /**
     * 注入到模板文件的数据
     */
    data?: Record<string, any>,
  }>,
  /**
   * 是否使用 html-minify-terser 压缩 html 文件
   * @default false
   * @see https://github.com/terser/html-minifier-terser
   */
  htmlMinify?: Options | boolean,
  /**
   * 是否启用页面配置扫描功能。
   * 如果为 true,将自动扫描页面目录并生成 virtual:page-configs 虚拟模块。
   * 如果为对象,可以自定义配置选项。
   * @default false
   */
  pageConfigs?:
    | boolean
    | {
        /**
         * 要扫描的页面目录
         * @default 'src/pages'
         */
        pagesDir?: string | string[];
        /**
         * 文件匹配模式
         * @default '*.vue'
         */
        filePattern?: string;
        /**
         * 虚拟模块 ID
         * @default 'virtual:page-configs'
         */
        virtualModuleId?: string;
      };
}

Examples

注意:CodeSandbox 示例链接到原项目仓库。要体验增强功能(文件扫描模式),请查看下面的示例部分。

点击链接 codesandbox 快速体验原项目功能!

基础示例(使用 entry)

// vite.config.ts
import { normalizePath } from "vite";
import { createMpaPlugin } from "vite-plugin-virtual-mpa"

const base = "/sites/"

// @see https://vitejs.dev/config/
export default defineConfig({
  base,
  plugins: [
    createMpaPlugin({
      htmlMinify: false,
      pages: [
        {
          name: "apple",
          /**
           * 文件名是可选的,默认将会是`${name}.html`,这个路径是相对于`build.outDir`
           */
          filename: "fruits/apple.html", // 将会在编译时输出到sites/fruits/apple.html
          entry: "/src/fruits/apple/index.js",
          data: {
            title: "This is Apple page"
          }
        },
        {
          name: "banana",
          filename: "fruits/banana.html",
          entry: "/src/fruits/banana/index.js",
          data: {
            title: "This is Banana page"
          }
        },
        {
          name: "strawberries",
          filename: "fruits/strawberries.html",
          entry: "/src/fruits/strawberries/index.js",
          data: {
            title: "This is Strawberries page"
          }
        }
      ],
      /**
       * 以下示例的 scanOptions 配置可以替换上面的 pages 配置,除了 data 的注入。
       */
      scanOptions: {
        scanDirs: 'src/fruits',
        entryFile: 'index.js',
        filename: name => `fruits/${name}.html`,
        template: '../../template.html',
      },
      /**
       * 使用入口模板生成虚拟入口文件(可选)
       * 这样可以避免为每个页面创建重复的入口文件
       */
      // entryTemplate: 'src/entry-template.js',
      /**
       * 通过该选项来配置 history fallback rewrite rules
       * 如果你像上面这样配置页面的话,那下面的这份配置将会自动生成。
       * 否则你需要自己编写重定向规则。
       */
      rewrites: [
        {
          from: new RegExp(normalizePath(`/${base}/(apple|banana|strawberries)`)),
          to: (ctx) => normalizePath(`/fruits/${ctx.match[1]}.html`),
        }
      ],
      /**
       * 配置预览服务器的重定向规则,配置方式同 rewrites
       */
      previewRewrites: [
        // 如果产物目录没有 index.html,你需要手动配置规则,以便服务器能正确找到入口文件。
        { from: /.*/, to: '/home.html' },
      ],
      /** 自定义处理模板内容 */
      transformHtml(html, ctx) {
        return {
          html,
          tags: [
            {
              tag: 'div',
              injectTo: 'body-prepend',
              children: `[Auto Injected] Page name: ${ctx.page.name}`,
            },
          ],
        };
      },
    }),
  ],
})

文件扫描模式示例

当你希望为 pages 目录下的每个 Vue 文件生成独立的 HTML 页面时,可以使用文件扫描模式:

1. 创建入口模板文件

// src/entry-template-vue.js
import { createApp } from 'vue';
// 根据 componentPath 动态导入 Vue 组件
import Component from '<%= componentPath %>';

// 页面特定的数据可以在这里使用
console.log('组件路径:', '<%= componentPath %>');
console.log('页面标题:', '<%= title %>');

// 挂载 Vue 应用
createApp(Component).mount('#root');

2. 配置插件

// vite.config.ts
import { createMpaPlugin } from "vite-plugin-virtual-mpa"

export default defineConfig({
  plugins: [
    createMpaPlugin({
      template: 'src/template.html',
      scanOptions: {
        scanDirs: 'src/pages',
        scanMode: 'file',        // 扫描文件而不是目录
        filePattern: '*.vue',    // 匹配 .vue 文件
        entryTemplate: 'src/entry-template-vue.js', // 用于生成入口文件的模板
        filename: (name) => `${name}.html`, // 输出:apple/About.html, apple/App.html 等
        data: ({ name, dir }) => ({
          title: `${dir} - ${name.split('/').pop()}`,
        }),
      },
    }),
  ],
})

扫描规则:

  • 只扫描 scanDirs直接子目录下的文件
  • 忽略更深层嵌套目录中的文件
  • 忽略 scanDirs 根目录下的文件
  • 每个匹配的文件都会成为一个独立的 HTML 页面

目录结构示例:

src/pages/
  ├── apple/
  │   ├── About.vue     ✅ 扫描 → apple/About.html
  │   ├── App.vue       ✅ 扫描 → apple/App.html
  │   └── deep/
  │       └── Deep.vue  ❌ 忽略(更深层)
  ├── page2/
  │   └── App.vue       ✅ 扫描 → page2/App.html
  └── Root.vue          ❌ 忽略(不在子目录下)

entryTemplate 中可用的变量:

  • <%= componentPath %> - Vue 组件文件的完整路径(自动注入)
  • <%= pageName %> - 页面名称(如 "apple/About")
  • <%= title %> - 来自 data.title
  • <%= customVar %> - data 配置中的任何自定义变量
  • <%= VITE_XXX %> - 以 VITE_ 开头的环境变量

使用入口模板(entryTemplate)示例

使用 entryTemplate 可以避免为每个页面创建重复的入口文件:

1. 创建入口模板文件

// src/entry-template.js
import { createApp } from 'vue';
// 使用 EJS 语法动态导入对应页面的组件
import App from '/src/pages/<%= pageName %>/App.vue';

// 页面数据会自动注入
console.log('🚀 Page name:', '<%= pageName %>');
console.log('📄 Page title:', '<%= title %>');

// 如果有自定义数据,也可以使用
// console.log('Custom data:', '<%= customData %>');

createApp(App).mount('#root');

2. 在配置中使用

// vite.config.ts
import { createMpaPlugin } from "vite-plugin-virtual-mpa"

export default defineConfig({
  plugins: [
    createMpaPlugin({
      template: 'src/template.html',
      // 全局入口模板(所有页面共享)
      entryTemplate: 'src/entry-template.js',
      pages: [
        {
          name: "apple",
          filename: "fruits/apple.html",
          // 使用全局 entryTemplate,无需单独指定
          data: {
            title: "This is Apple page",
            customData: "Apple specific data"
          }
        },
        {
          name: "banana",
          filename: "fruits/banana.html",
          // 也可以为特定页面指定不同的入口模板
          // entryTemplate: 'src/banana-entry-template.js',
          data: {
            title: "This is Banana page"
          }
        },
        {
          name: "strawberries",
          filename: "fruits/strawberries.html",
          data: {
            title: "This is Strawberries page"
          }
        }
      ],
      // ... 其他配置
    }),
  ],
})

优势对比:

  • 传统方式:需要为每个页面创建 index.js 文件,代码重复
  • 使用 entryTemplate:只需一个模板文件,通过 EJS 动态生成,减少重复代码

注意事项:

  • Entry 模板中的 import 路径建议使用绝对路径(以 /src/ 开头)
  • 不能同时使用 entryentryTemplate
  • 页面级别的 entryTemplate 会覆盖全局配置

本 Fork 版本的新特性

这是基于原版 vite-plugin-virtual-mpa 的增强版本,包含以下改进:

🆕 文件扫描模式(v1.13.0+)

自动扫描并生成页面,无需手动配置每个页面:

  • 自动发现:扫描子目录中的 .vue.tsx.jsx 文件
  • 智能过滤:只扫描直接子目录(可配置)
  • 基于模板:使用入口模板生成虚拟入口文件
  • 灵活配置:支持自定义文件模式和数据注入

🐛 虚拟模块跨域修复(v1.13.1+)

修复了浏览器加载虚拟入口模块时的 CORS 问题:

  • 自动路径转换:使用 /@id/ 前缀确保浏览器兼容性
  • 无需手动操作:插件自动处理所有路径转换
  • 开箱即用:无需额外配置

🎯 页面配置扫描功能(v2.0.0+)

自动扫描并提取页面中的 definePage 配置,生成虚拟模块供运行时使用:

  • 自动扫描:递归扫描页面目录,提取 definePage({ ... }) 配置
  • 虚拟模块:生成 virtual:page-configs 虚拟模块,提供配置数据
  • 运行时支持:自动注入 definePage 函数,避免运行时错误
  • 热更新:文件变化时自动重新扫描并更新配置
  • 类型安全:完整的 TypeScript 类型支持

📚 完善的文档

包含所有功能的完整示例和使用指南,包括文件扫描模式的使用方法。

关于原项目

本插件基于 vite-plugin-virtual-mpa(作者:QinXuYang)开发。原项目旨在解决使用 Vite 构建多页面应用(MPA)时的以下需求:

  1. 支持模板引擎(如 EJS),能够使用一个模板生成多份文件,且能自定义构建时生成文件的路径
  2. 自动配置 rollupOptions.input,并提供能力配置开发服务器的代理(主要是 history fallback API)

关于原项目的设计理念和与其他插件的对比,请参考原项目仓库

默认重定向规则

正如上面提到的👆🏻,如果你的配置遵循约定,插件将会自动生成一份重定向规则,这份配置会同时应用到开发和预览服务器,如下:

{
  from: new RegExp(normalizePath(`/${base}/(${Object.keys(inputMap).join('|')})`)),
  to: ctx => normalizePath(`/${base}/${inputMap[ctx.match[1]]}`),
}

其中, inputMap 是一个name到对应虚拟文件的映射,结构如下:

{
  apple: 'fruits/apple.html',
  banana: 'fruits/banana.html',
  strawberries: 'fruits/strawberries.html',
}

请求Url/sites/apple/xxx将会被默认重定向规则处理并重定向到对应的url,也就是/fruits/apple.html(name 'apple' 对应 'fruits/apple.html', 其他同理),重定向后的路径将会基于viteConfig.base(这里是'/sites/')去寻找目标文件,所以最终的Url会变成/sites/fruits/apple.html.

关于虚拟入口文件

通常在开发时,我们的文件都是写在本地的,我们通过DevServer的代理能够通过url访问到本地对应的文件。虚拟文件也是如此,只不过对应的文件没有写到文件系统中,而是保存在内存中而已。

该插件通过模板系统生成了对应的虚拟文件,让你可以在开发时通过代理访问到内存中的虚拟文件,并在构建时生成到对应的目录下。

你完全可以认为这些虚拟文件是真实存在的,这将有助于你在脑海中构建关于虚拟文件的直觉,以便能够正确地编写代理配置。

虚拟模块路径格式

从 v1.13.1 开始,插件自动使用 /@id/ 前缀来生成虚拟入口模块的路径,确保浏览器可以正确加载虚拟模块,避免 CORS 错误。

生成的 HTML 格式:

<!-- ✅ 正确的格式(v1.13.1+) -->
<script type="module" src="/@id/virtual:entry:pageName"></script>

为什么需要 /@id/ 前缀?

  • 浏览器无法直接识别 virtual:entry:xxx 这种裸模块标识符
  • Vite 开发服务器通过 /@id/ 前缀来识别和处理虚拟模块
  • 这样可以确保虚拟模块在浏览器中正确加载,避免 CORS 错误

插件会自动处理路径转换,你无需手动添加 /@id/ 前缀。

虚拟入口模板(entryTemplate)

从 v1.12.2 开始,插件支持虚拟入口模板功能,允许你像使用 HTML 模板一样,使用一个 entry 模板文件生成多个虚拟入口文件。

使用场景

当你需要为多个页面生成相似的入口文件时,可以使用 entryTemplate 来避免重复代码:

  • 传统方式:每个页面都需要一个独立的 index.js 文件
  • 使用 entryTemplate:只需要一个模板文件,通过 EJS 语法动态生成每个页面的入口文件

基本用法

1. 创建入口模板文件

// src/entry-template.js
import { createApp } from 'vue';
// 使用 EJS 语法注入页面名称
import App from '/src/pages/<%= pageName %>/App.vue';

// 页面数据会自动注入
console.log('Page name:', '<%= pageName %>');
console.log('Page title:', '<%= title %>');

createApp(App).mount('#root');

2. 在配置中使用

// vite.config.ts
export default defineConfig({
  plugins: [
    createMpaPlugin({
      template: 'src/template.html',
      // 全局入口模板(可选)
      entryTemplate: 'src/entry-template.js',
      pages: [
        {
          name: 'apple',
          entryTemplate: 'src/entry-template.js', // 页面级别配置(优先级更高)
          data: {
            title: 'This is Apple page',
            // 自定义数据也会注入到模板中
          }
        },
        {
          name: 'banana',
          // 使用全局 entryTemplate
          data: {
            title: 'This is Banana page',
          }
        }
      ]
    })
  ]
})

可用变量

entryTemplate 中,你可以使用以下变量:

  • <%= pageName %> - 页面名称(自动注入)
  • <%= componentPath %> - 组件文件路径(仅在文件扫描模式下可用,自动注入)
  • <%= title %> - 来自 data.title 的数据
  • <%= customVar %> - 来自 data 配置的任何自定义变量
  • <%= VITE_XXX %> - 以 VITE_ 开头的环境变量(自动注入)

注意事项

  1. 路径问题:在 entry 模板中使用 import 时,建议使用绝对路径(以 /src/ 开头),因为虚拟入口文件没有实际的文件系统路径。

  2. 不能同时使用:不能同时设置 entryentryTemplate,否则会报错。

  3. 优先级:页面级别的 entryTemplate 配置会覆盖全局配置。

  4. 热更新:修改 entry 模板文件会自动触发页面重载。

对比示例

传统方式(使用 entry):

pages: [
  {
    name: 'apple',
    entry: '/src/pages/apple/index.js', // 需要为每个页面创建文件
  },
  {
    name: 'banana',
    entry: '/src/pages/banana/index.js', // 重复的代码结构
  }
]

使用 entryTemplate:

pages: [
  {
    name: 'apple',
    entryTemplate: 'src/entry-template.js', // 共享模板
  },
  {
    name: 'banana',
    entryTemplate: 'src/entry-template.js', // 共享模板
  }
]

使用 entryTemplate 可以大大减少重复代码,特别是在页面结构相似的情况下。

页面配置扫描示例

页面配置扫描功能允许你在 Vue 文件中使用 definePage 定义页面配置,然后通过虚拟模块在运行时访问这些配置。

1. 在 Vue 文件中定义页面配置

<!-- src/pages/apple/About.vue -->
<script setup>
// 使用 definePage 定义页面配置
definePage({
  name: 'About',
  title: '关于我们',
  platform: ['web', 'mobile'],
  locale: 'zh-CN',
  meta: {
    description: '这是关于页面',
  },
});
</script>

<template>
  <div>This is Apple About</div>
</template>

2. 在 vite.config.ts 中启用页面配置扫描

// vite.config.ts
import { createMpaPlugin } from '@star_work/vite-plugin-virtual-mpa'

export default defineConfig({
  plugins: [
    createMpaPlugin({
      template: 'src/template.html',
      scanOptions: {
        scanDirs: 'src/pages',
        scanMode: 'file',
        filePattern: '*.vue',
        entryTemplate: 'src/entry-template-vue.js',
      },
      // 启用页面配置扫描
      pageConfigs: true, // 或 { pagesDir: 'src/pages', filePattern: '*.vue' }
    }),
  ],
})

3. 在代码中使用页面配置

// 导入页面配置
import { pageConfigs, getPageConfig } from 'virtual:page-configs';

// 获取所有页面配置
console.log('All page configs:', pageConfigs);
// 输出: [{ path: 'apple/About', name: 'About', title: '关于我们', ... }, ...]

// 根据路径获取特定页面配置
const aboutConfig = getPageConfig('apple/About');
console.log('About page config:', aboutConfig);
// 输出: { path: 'apple/About', name: 'About', title: '关于我们', ... }

// 业务逻辑:根据平台筛选配置(自己实现)
function getPageConfigsByPlatform(platform) {
  return pageConfigs.filter((config) => {
    if (!config.platform) return false;
    if (typeof config.platform === 'string') {
      return config.platform === platform;
    }
    return config.platform.includes(platform);
  });
}

const webConfigs = getPageConfigsByPlatform('web');
console.log('Web platform configs:', webConfigs);

虚拟模块提供的功能:

  • pageConfigs - 所有页面配置数组(原始数据)
  • getPageConfig(path) - 根据路径获取配置,如 getPageConfig('apple/About')

注意事项:

  • definePage 函数会在编译时自动注入,无需手动导入
  • 如果页面没有 definePage,插件会创建默认配置(使用页面路径作为名称)
  • 业务逻辑的筛选函数(如按平台、语言筛选)需要自己实现
  • 文件变化时会自动重新扫描配置并更新虚拟模块

关于 EJS 模板引擎

插件使用 ejs 模板引擎进行数据注入,支持两种模板类型:

HTML 模板(template)

用于生成 HTML 文件,除页面配置中提供的 data 外,插件默认会将以 'VITE_' 开头的环境变量注入所有的页面配置中。

入口模板(entryTemplate)

用于生成虚拟入口文件(如 index.js),支持以下变量:

  • <%= pageName %> - 页面名称(自动注入,无需在 data 中配置)
  • <%= title %> - 来自 data.title 的数据
  • <%= customVar %> - 来自 data 配置的任何自定义变量
  • <%= VITE_XXX %> - 以 VITE_ 开头的环境变量(自动注入)

示例:

// entry-template.js
import App from '/src/pages/<%= pageName %>/App.vue';
console.log('Page:', '<%= pageName %>');
console.log('Title:', '<%= title %>');
console.log('Custom:', '<%= customData %>');

更多关于环境变量的信息可以查看官网 —— envprefix