Navim
面向 single-spa + Vue 2 场景的前端打包工具库。当前实现已经统一到三套引擎:
文档按当前仓库实现编写,重点说明可用命令、配置 schema、引擎差异和兼容行为。本文中把体积分析能力统一称为 analyze,当前 CLI 命令名仍保留为 analyzer,用于兼容既有项目。
适用范围
- Node.js
>= 20.0.0
- 仓库维护和 CI 基线统一使用
pnpm 9.15.9
- Vue 2 项目
- 微前端场景,尤其是
single-spa
- 项目根目录通过
navim.config.js 驱动 Navim
安装
推荐把 @heisea/navim 安装到项目依赖中,再通过 npm scripts 或 npx 调用。
pnpm add -D @heisea/navim
或:
npm install -D @heisea/navim
快速开始
{
"scripts": {
"dev": "navim-service dev --engine webpack",
"dev:vite": "navim-service dev --engine vite",
"dev:rsbuild": "navim-service dev --engine rsbuild",
"build": "navim-service build --engine webpack",
"build:vite": "navim-service build --engine vite",
"build:rsbuild": "navim-service build --engine rsbuild",
"analyze": "navim-service analyzer --engine webpack",
"inspect": "navim-service inspect --engine webpack",
"single-spa:doctor": "navim-service single-spa doctor"
}
}
目录约定
Navim 依赖一些固定文件位置,缺少这些文件时命令会直接失败。
your-project/
├── build/
│ └── config.js
├── public/
│ └── index.html
├── src/
│ ├── main.js
│ ├── styles/
│ │ └── variables.less
│ └── plugins/
├── .env
├── .env.local
├── .env.development
├── .env.production
└── navim.config.js
关键约定:
- 根目录必须存在
navim.config.js
build/config.js 必须存在,哪怕只导出空对象
- HTML 模板固定为
public/index.html
- 默认 Less 全局变量入口为
src/styles/variables.less
- 默认插件目录为
src/plugins
CLI 命令
统一入口
统一入口是以下四类命令:
navim-service dev --engine webpack|vite|rsbuild
navim-service build --engine webpack|vite|rsbuild
navim-service analyzer --engine webpack|rsbuild
navim-service inspect --engine webpack|vite|rsbuild
说明:
dev 默认引擎是 webpack
build 默认引擎是 webpack
analyzer 默认引擎是 webpack
inspect 默认引擎是 webpack
dev 固定按开发模式运行
build 只接受 production、pre、test 三种环境值,默认 production
analyzer 语义上对应 analyze
dev
navim-service dev --engine webpack
navim-service dev --engine vite
navim-service dev --engine rsbuild
navim-service dev --engine webpack --micro
navim-service dev --engine webpack --mode sit
公共参数:
--engine <engine>:webpack、vite、rsbuild
--micro:微前端模式,主要影响 webpack 和 rsbuild 的 externals 策略
--mode <type>:额外读取 .env.<type>
build
navim-service build
navim-service build test
navim-service build pre
navim-service build production --mode sit
navim-service build --engine vite
navim-service build --engine rsbuild
行为说明:
- 默认环境为
production
- 合法环境值只有
production、test、pre
--mode <type> 会额外读取 .env.<type>
webpack、vite、rsbuild 都支持构建
analyzer
navim-service analyzer
navim-service analyzer --engine webpack
navim-service analyzer --engine rsbuild
说明:
- 当前 CLI 命令名仍然是
analyzer
- 语义上对应统一的
analyze 能力
webpack 使用 webpack-bundle-analyzer
rsbuild 会启用 bundleAnalyze
vite 当前不支持分析命令
inspect
navim-service inspect
navim-service inspect --engine vite
navim-service inspect --engine rsbuild --env production
navim-service inspect --micro
用途:
- 输出当前引擎翻译后的运行时配置
- 适合排查
entry、alias、proxy、html、output 和 styles 的最终值
兼容命令
以下旧命令仍然保留:
navim-service start 等价于 navim-service dev --engine webpack
navim-service vite start 等价于 navim-service dev --engine vite
navim-service single-spa doctor 仍然可用
build/config.js(代理配置)
build/config.js 是默认的代理配置文件,需导出代理表(路径 → 目标)。如果没有代理需求,也请至少导出空对象:
module.exports = {};
常见写法:
module.exports = {
"/api": {
target: "http://localhost:8080",
changeOrigin: true,
},
};
自 1.2.0 起,代理文件路径可通过 common.dev.proxy 自定义(默认仍为 ./build/config.js),三引擎(webpack/vite/rsbuild)通用。适用于代理表不在 build/config.js、或 build/config.js 已被业务代码占用的项目:
module.exports = {
common: {
dev: { proxy: "./build/proxy.dev.js" },
},
};
navim.config.js
navim.config.js 是 Navim 的核心配置文件,支持对象形式和函数形式。当前实现同时兼容两种 schema:
legacy:顶层使用 webpack、vite、rsbuild
current:顶层使用 common,再加上 webpack、vite、rsbuild 作为引擎覆盖项
当前 schema 边界
vite 和 rsbuild 的 dev / build / inspect 直接消费 common
webpack 的 dev / build / analyzer 仍然走 legacy webpack 配置块
single-spa doctor 也仍然只面向 legacy webpack 配置块
- 如果项目同时要跑
webpack 和 vite / rsbuild,当前需要保留一份 legacy webpack 字段,并与 common 中的对应值保持同步
- 只写
common 而不写 legacy webpack.entry、webpack.library、webpack.publicPath,当前并不足以驱动 webpack 实际构建
函数形式
函数会收到两个参数:
NODE_ENV
extraInfo
- 当前实现中包含
extraInfo.navim_hash
module.exports = (NODE_ENV, extraInfo) => {
const entry = NODE_ENV === "development" ? "./src/main.dev.js" : "./src/main.js";
const publicPath = NODE_ENV === "development" ? "//localhost:9000/" : "/demo/";
return {
common: {
entry: {
app: entry,
},
output: {
outDir: "dist",
publicPath,
library: {
name: "demo-app",
format: "umd",
},
},
dev: {
host: "127.0.0.1",
port: 9000,
open: false,
headers: {
"Access-Control-Allow-Origin": "*",
},
},
html: {
template: "./public/index.html",
rootElementId: "app",
},
resolve: {
alias: {
"@": "./src",
},
},
styles: {
extract: true,
resources: ["./src/styles/variables.less"],
less: {
javascriptEnabled: true,
},
},
assets: {
copy: [],
},
define: {},
externals: {
dev: {
react: "React",
"react-dom": "ReactDOM",
},
micro: {
vue: "Vue",
"vue-router": "VueRouter",
"element-ui": "ELEMENT",
},
},
micro: {
enabled: false,
type: "single-spa",
cssExtractDoctor: true,
},
},
webpack: {
entry: {
app: entry,
},
library: "demo-app",
publicPath,
isExtractCss: true,
styleResourcesLoaderPatterns: ["./src/styles/variables.less"],
extraPlugins: [],
htmlWebpackPlugin: {},
htmlWebpackTagsPlugin: {},
copyWebpackPluginPatterns: [],
},
vite: {
entry: "./src/main.js",
rootElementId: "app",
optimizeDeps: {
include: ["@heisea/brick"],
exclude: ["vue-router", "vuex"],
},
staticCopyTargets: [],
plugins: [],
},
rsbuild: {
plugins: [],
tools: {},
config: {},
},
};
};
旧 schema
如果你不想迁移,旧的顶层写法仍然可用。
module.exports = {
webpack: {
entry: {
app: "./src/main.js",
},
library: "demo-app",
publicPath: "/demo/",
},
vite: {
entry: "./src/main.js",
rootElementId: "app",
},
rsbuild: {
plugins: [],
},
};
common 配置
common 是推荐的 normalized 公共配置层,但当前只有一部分字段真正进入了运行时。
entry | object | {} | 入口定义(构建入口) |
output.outDir | string | dist | 输出目录 |
output.publicPath | string | 无 | 资源前缀 |
output.library.name | string | 无 | UMD 库名(webpack 引擎;rsbuild 引擎在 micro.enabled 时生效) |
output.library.format | string | umd | 输出格式(webpack 引擎;rsbuild 引擎在 micro.enabled 时生效) |
dev.host | string | 127.0.0.1 | 开发服务 host |
dev.port | number | 9000 | 开发服务端口 |
dev.open | boolean | false | 是否自动打开浏览器 |
dev.headers | object | CORS 头 | 开发服务响应头 |
dev.proxy | string | ./build/config.js | 代理配置文件路径(导出代理表);三引擎通用,自 1.2.0 起可自定义 |
dev.entry | object/string | 无 | dev 命令下的入口覆盖(不设则用 common.entry);三引擎通用,自 1.2.0 起支持 |
html.template | string | ./public/index.html | HTML 模板 |
html.rootElementId | string | app | 根节点 id |
html.tags | object | {} | HTML tag 扩展数据 |
resolve.alias | object | 内置别名 | 路径别名 |
resolve.extensions | string[] | 内置扩展 | 解析扩展名 |
styles.extract | boolean | false | 主要给 webpack 的 CSS 抽离开关 |
styles.resources | string[] | [] | Less 全局注入文件 |
styles.less.javascriptEnabled | boolean | true | Less loader 选项 |
assets.copy | Array | [] | 复制静态资源 |
define | object | {} | 常量注入 |
externals.dev | object | react / react-dom | 开发态 externals |
externals.micro | object | 微前端 externals | 微前端外部依赖 |
micro.enabled | boolean | false | 微前端开关 |
micro.type | string | single-spa | 微前端类型 |
micro.cssExtractDoctor | boolean | true | CSS 抽离提示开关 |
当前生效范围
- 已在
vite 和 rsbuild 运行时直接生效:entry、output.publicPath、output.outDir、dev.host、dev.port、dev.open、html.template、resolve.alias、resolve.extensions、styles.resources、styles.less.javascriptEnabled、define
- 当前只在
vite 运行时直接生效:html.rootElementId
- 当前只在
rsbuild 运行时直接生效:dev.headers、assets.copy、externals.dev、externals.micro
- 自
1.1.0 起在 rsbuild 运行时生效:output.library.name、output.library.format、micro.enabled(开启后产出 single-spa 微前端 UMD 产物:库名 + 入口 app.js + css/app.css 无 hash + 初始不分包)
- 当前主要是 schema 预留、归一化输出或未来收敛位,不应当视为“已经全面支持”:
html.tags、styles.extract(rsbuild 生产构建默认抽离 CSS)、micro.type、micro.cssExtractDoctor
如果你当前仍然依赖 webpack 命令,请继续在 webpack 配置块里显式声明这些 legacy 字段:
entry
library
publicPath
isExtractCss
styleResourcesLoaderPatterns
copyWebpackPluginPatterns
htmlWebpackPlugin
htmlWebpackTagsPlugin
extraPlugins
默认别名
当前实现内置了这些别名:
@ -> ./src
~@ -> ./src
/plugins -> ./src/plugins
images -> ./images
vue -> vue/dist/vue.esm.js
vue$ -> vue/dist/vue.common.js
~element-ui -> ./node_modules/element-ui
默认扩展名
当前实现默认会解析:
.js .json .css .less .vue .mjs .mts .ts .jsx .tsx
webpack 配置
webpack 仍然是能力最完整的引擎,也是当前默认引擎。但要注意,当前 webpack 运行时还没有切到 common 驱动模型,仍然直接读取 legacy webpack 配置块。
extraPlugins | Array | [] | 额外追加到 webpack 配置中的插件实例 |
htmlWebpackPlugin | object | {} | html-webpack-plugin 的配置 |
htmlWebpackTagsPlugin | object | {} | html-webpack-tags-plugin 的配置 |
copyWebpackPluginPatterns | Array | [] | copy-webpack-plugin 的 patterns |
常见示例:
const path = require("path");
const webpack = require("webpack");
module.exports = {
webpack: {
entry: {
app: "./src/main.js",
},
library: "navbar",
publicPath: "/navbar/",
isExtractCss: true,
styleResourcesLoaderPatterns: ["./src/styles/variables.less"],
copyWebpackPluginPatterns: [
{
from: path.resolve(__dirname, "src/static"),
to: path.resolve(__dirname, "dist/static"),
},
],
htmlWebpackPlugin: {
title: "Navim Demo",
},
htmlWebpackTagsPlugin: {
tags: ["https://example.com/runtime.js"],
append: false,
},
extraPlugins: [
new webpack.DefinePlugin({
__BUILD_NAME__: JSON.stringify("navim-demo"),
}),
],
},
};
webpack 内置行为:
- 输出目录固定为
dist/
- 主入口产物固定为
dist/app.js
- 动态 chunk 输出到
dist/static/js/
- 图片输出到
dist/static/images/
- 字体输出到
dist/static/fonts/
libraryTarget 固定为 umd
- 支持
.vue、.css、.less、.styl、.js、.jsx
- JS/JSX 使用 Babel
- 开发态和生产态会按当前实现生成不同的 webpack 配置
common.output.library.*、common.styles.extract、common.assets.copy 等字段目前不会直接驱动 webpack 运行时
vite 配置
vite 现在同时支持开发和构建。
entry | string | 自动从 common.entry 兜底 | HTML 入口文件 |
rootElementId | string | app | HTML loading 插件替换的根节点 id |
optimizeDeps | object | include @heisea/brick / exclude vue-router, vuex | Vite 预构建配置 |
staticCopyTargets | Array | 内置主题 CSS 和 element-ui 字体 | 复制静态资源 |
plugins | Array | [] | 额外 Vite 插件 |
内置行为:
configFile=false,不会读取项目自己的 vite.config.*
- 使用
@vitejs/plugin-vue2 和 @vitejs/plugin-vue2-jsx
- 代理配置来自
build/config.js
common.output.publicPath 会映射到 Vite 的 base
common.output.outDir 会映射到 Vite 的 build.outDir
common.resolve.alias 和 common.resolve.extensions 会参与 Vite 解析
common.styles.resources 会通过 Less hack 注入
- 会默认复制
@heisea/brick 的主题 CSS 和 element-ui 字体文件
示例:
module.exports = {
vite: {
entry: "./src/main.js",
rootElementId: "app",
optimizeDeps: {
include: ["@heisea/brick"],
exclude: ["vue-router", "vuex"],
},
},
};
rsbuild 配置
rsbuild 已接入统一 schema,并支持 dev、build、analyze 三条路径。自 1.1.0 起,
rsbuild 引擎的能力已与 webpack 引擎对齐:less/stylus、Vue 2 JSX、@heisea/brick-loader、
微前端 UMD 产物等均由引擎内建,项目通常只需编写 common 配置。
plugins | Array | [] | 额外 Rsbuild 插件(会与内建插件合并) |
tools | object | {} | Rsbuild tools 合并配置 |
config | object | {} | 最终运行时配置覆盖 |
内置行为:
common.entry 会映射到 source.entry
common.output.publicPath 会映射到 output.assetPrefix
common.output.outDir 会映射到 output.distPath.root
common.dev.host、common.dev.port、common.dev.open、common.dev.headers 会映射到 server
common.resolve.alias 和 common.resolve.extensions 会参与解析
common.styles.resources 会通过 @rsbuild/plugin-less 的 lessLoaderOptions.additionalData 注入(等价 webpack 的 style-resources-loader)
common.assets.copy 会映射到 output.copy
common.externals.dev / common.externals.micro 会按当前命令和微前端状态切换
- 自动注册插件:
@rsbuild/plugin-vue2、@rsbuild/plugin-less、@rsbuild/plugin-stylus、@rsbuild/plugin-babel(Vue 2 JSX,使用 @vue/babel-preset-jsx,覆盖 .js/.jsx 与 .vue 的 <script> 块)
- 自动对
.vue 应用 @heisea/brick-loader(仅处理主 .vue 请求,并关闭 vue-loader 的 experimentalInlineMatchResource 以避免冲突);为匹配项目的 @heisea/brick / vue 版本,优先从消费方项目解析 @heisea/brick-loader
module.parser.javascript.exportsPresence 默认设为 warn(与 webpack 行为一致:导入不存在的命名导出仅告警,不中断构建)
- HTML 产物命名为
index.html
当 common.micro.enabled === true(微前端模式,single-spa)时,额外开启:
- UMD 产物:库名取
common.output.library.name,格式取 common.output.library.format(默认 umd)
- 入口固定输出
app.js,异步 chunk 输出到 js/
- 初始代码不分包(
splitChunks.chunks: 'async',仅对动态 import() 拆分)
- CSS 固定输出到
css/app.css 且不带 hash(对齐 single-spa-css 加载约定)
依赖要求:
@rsbuild/core 以及 @rsbuild/plugin-vue2/plugin-less/plugin-stylus/plugin-babel、@vue/babel-preset-jsx、@heisea/brick-loader 均已作为 Navim 的依赖随包提供,消费方无需单独安装
- 消费方仍需安装与
vue 版本匹配的 vue-template-compiler(vue-loader 编译 .vue 必需,版本不一致会报 Vue packages version mismatch)
- 若项目使用
@heisea/brick,建议项目内安装与之匹配的 @heisea/brick-loader(Navim 会优先用项目内版本)
示例(single-spa + Vue 2 微前端,最简配置):
module.exports = (NODE_ENV) => ({
common: {
entry: {
app: "./src/main.js",
},
output: {
publicPath: "/demo/",
library: { name: "demo-app", format: "umd" },
},
html: {
template: "./public/index.html",
},
styles: {
resources: ["./src/styles/variables.less"],
},
define: {
"process.env.NODE_ENV": JSON.stringify(NODE_ENV),
},
micro: { enabled: true },
},
});
single-spa doctor
single-spa doctor 仍然是 webpack / legacy 配置链路下的升级工具,不属于三引擎统一能力。
执行边界:
- 项目依赖中必须已经包含
single-spa-vue
- 当前只支持单入口项目;如果
webpack.entry 中存在多个入口,会直接退出
- 入口来源仍然是 legacy
webpack.entry
- 如果项目中还没有
single-spa-css,命令会尝试执行 pnpm add single-spa-css@^2.0.0
- 命令会改写入口文件源码,补入
single-spa-css 相关逻辑
- 已经升级过的入口文件再次执行会直接退出
vite 和 rsbuild 当前没有对应的 doctor 流程
环境变量加载规则
Navim 通过 dotenv + dotenv-expand 加载环境变量。
加载优先级从高到低如下:
.env.[NODE_ENV].local
.env.[mode]
.env.[NODE_ENV]
.env.local
.env
说明:
NODE_ENV 由 Navim 命令自身设置
mode 来自 --mode <type>
- 同名变量以高优先级文件中的值为准
开发服务相关变量:
PORT:开发服务端口
HOST:开发服务 host
webpack 构建时会通过 DefinePlugin 注入:
process.env.NODE_ENV
process.env.navim_hash
- 所有以
NAVIM_ 开头的变量,例如 NAVIM_API_BASE
示例:
PORT=9000
HOST=127.0.0.1
NAVIM_API_BASE=/api
业务代码中可直接读取:
console.log(process.env.NODE_ENV);
console.log(process.env.navim_hash);
console.log(process.env.NAVIM_API_BASE);
兼容性说明
- 旧项目可以继续使用顶层
webpack / vite / rsbuild 配置
- 新项目建议优先使用
common + 引擎覆盖的写法,但如果仍要跑 webpack,请保留 legacy webpack 字段
start、vite start、analyzer、single-spa doctor 都保留为兼容入口
inspect 是当前用于查看翻译后运行时配置的推荐命令
常见注意事项
navim.config.js 必须位于项目根目录
build/config.js 必须存在;如果没有代理需求,也请导出空对象
public/index.html 必须存在
vite 和 rsbuild 都已经支持构建,不再只是开发服务
webpack 当前仍然依赖 legacy webpack 配置,不是完全由 common 驱动
single-spa doctor 会改写源码,执行前建议先提交或备份
single-spa doctor 只支持使用 single-spa-vue 的单入口 webpack 项目
- 如果你在微前端场景中抽离了 CSS,构建完成后 Navim 会提示你接入
single-spa-css
- 建议将 Navim 作为本地依赖安装,不建议只全局安装 CLI