@heisea/navim
v1.0.0
Published
面向 `single-spa + Vue 2` 场景的前端打包工具库。当前实现已经统一到三套引擎:
Readme
Navim
面向 single-spa + Vue 2 场景的前端打包工具库。当前实现已经统一到三套引擎:
webpackvitersbuild
文档按当前仓库实现编写,重点说明可用命令、配置 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默认引擎是webpackbuild默认引擎是webpackanalyzer默认引擎是webpackinspect默认引擎是webpackdev固定按开发模式运行build只接受production、pre、test三种环境值,默认productionanalyzer语义上对应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-analyzerrsbuild会启用bundleAnalyzevite当前不支持分析命令
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 webpacknavim-service vite start等价于navim-service dev --engine vitenavim-service single-spa doctor仍然可用
build/config.js
build/config.js 是代理配置文件。当前实现会直接读取它,因此文件必须存在。
如果没有代理需求,也请至少导出空对象:
module.exports = {};常见写法:
module.exports = {
"/api": {
target: "http://localhost:8080",
changeOrigin: true,
},
};
//navim.config.js
navim.config.js 是 Navim 的核心配置文件,支持对象形式和函数形式。当前实现同时兼容两种 schema:
legacy:顶层使用webpack、vite、rsbuildcurrent:顶层使用common,再加上webpack、vite、rsbuild作为引擎覆盖项
当前 schema 边界
vite和rsbuild的dev/build/inspect直接消费commonwebpack的dev/build/analyzer仍然走 legacywebpack配置块single-spa doctor也仍然只面向 legacywebpack配置块- 如果项目同时要跑
webpack和vite/rsbuild,当前需要保留一份 legacywebpack字段,并与common中的对应值保持同步 - 只写
common而不写 legacywebpack.entry、webpack.library、webpack.publicPath,当前并不足以驱动 webpack 实际构建
函数形式
函数会收到两个参数:
NODE_ENVextraInfo- 当前实现中包含
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: {
// webpack 运行时当前仍然依赖这组 legacy 字段
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 | 无 | 主要给 webpack 用的库名 |
| output.library.format | string | umd | 主要给 webpack 用的输出格式 |
| dev.host | string | 127.0.0.1 | 开发服务 host |
| dev.port | number | 9000 | 开发服务端口 |
| dev.open | boolean | false | 是否自动打开浏览器 |
| dev.headers | object | CORS 头 | 开发服务响应头 |
| 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 - 当前主要是 schema 预留、归一化输出或未来收敛位,不应当视为“已经全面支持”:
output.library.name、output.library.format、html.tags、styles.extract、micro.enabled、micro.type、micro.cssExtractDoctor
如果你当前仍然依赖 webpack 命令,请继续在 webpack 配置块里显式声明这些 legacy 字段:
entrylibrarypublicPathisExtractCssstyleResourcesLoaderPatternscopyWebpackPluginPatternshtmlWebpackPluginhtmlWebpackTagsPluginextraPlugins
默认别名
当前实现内置了这些别名:
@->./src~@->./src/plugins->./src/pluginsimages->./imagesvue->vue/dist/vue.esm.jsvue$->vue/dist/vue.common.js~element-ui->./node_modules/element-ui
默认扩展名
当前实现默认会解析:
.js .json .css .less .vue .mjs .mts .ts .jsx .tsxwebpack 配置
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 的basecommon.output.outDir会映射到 Vite 的build.outDircommon.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 三条路径。
| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| plugins | Array | [] | 额外 Rsbuild 插件 |
| tools | object | {} | Rsbuild tools 合并配置 |
| config | object | {} | 最终运行时配置覆盖 |
内置行为:
common.entry会映射到source.entrycommon.output.publicPath会映射到output.assetPrefixcommon.output.outDir会映射到output.distPath.rootcommon.dev.host、common.dev.port、common.dev.open、common.dev.headers会映射到servercommon.resolve.alias和common.resolve.extensions会参与解析common.styles.resources会通过 Less hack 注入common.assets.copy会映射到output.copycommon.externals.dev/common.externals.micro会按当前命令和微前端状态切换- 默认会尝试加载
@rsbuild/plugin-vue2
依赖要求:
rsbuild支持依赖@rsbuild/core- Vue 2 场景下建议同时安装
@rsbuild/plugin-vue2 - 使用 Vue 2 + Rsbuild 时,项目本身还需要安装与
vue版本匹配的vue-template-compiler - 如果你裁剪了依赖,缺少上述包时会直接报错
示例:
module.exports = {
common: {
entry: {
app: "./src/main.js",
},
output: {
publicPath: "/demo/",
outDir: "dist",
},
},
rsbuild: {
tools: {
less: {
loaderOptions: {
lessOptions: {
javascriptEnabled: 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_ENVprocess.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,请保留 legacywebpack字段 start、vite start、analyzer、single-spa doctor都保留为兼容入口inspect是当前用于查看翻译后运行时配置的推荐命令
常见注意事项
navim.config.js必须位于项目根目录build/config.js必须存在;如果没有代理需求,也请导出空对象public/index.html必须存在vite和rsbuild都已经支持构建,不再只是开发服务webpack当前仍然依赖 legacywebpack配置,不是完全由common驱动single-spa doctor会改写源码,执行前建议先提交或备份single-spa doctor只支持使用single-spa-vue的单入口 webpack 项目- 如果你在微前端场景中抽离了 CSS,构建完成后 Navim 会提示你接入
single-spa-css - 建议将 Navim 作为本地依赖安装,不建议只全局安装 CLI
