Skip to content

Commit 27c087c

Browse files
watsonhaw5566watsonhaw5566
andauthored
feat: 支持环境变量 (#136)
Co-authored-by: watsonhaw5566 <348748267@qq.com>
1 parent 2034496 commit 27c087c

6 files changed

Lines changed: 813 additions & 16 deletions

File tree

‎README.md‎

Lines changed: 242 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- **Hooks API** — `useState`、`useEffect`、`useContext`、`usePageEvent`、`useAppEvent` 等 React 风格 Hooks
99
- **API Promise 化** — 内置 `promisify` 工具函数,将小程序回调 API 转换为 Promise,支持 async/await
1010
- **两种编程范式** — 支持函数式组件(Hooks)和 Options API(传统小程序 Page/Component 配置)
11+
- **环境变量注入(编译时替换)** — 零依赖、无运行时开销;支持 `.env` 文件、系统 `RSMAX_*` 环境变量、`rsmax.config.js` 的 `define` 三层来源,`process.env.XXX` 编译时替换为字面量
1112
- **CSS Modules** — `.module.less` / `.module.css` / `.module.scss` 自动局部作用域,class 名自动 hash
1213
- **样式预处理** — 内置 Less/Sass 支持,px 自动转 rpx(1px → 1rpx,按 750rpx 设计稿)
1314
- **第三方 UI 库** — 自动识别并注册 Vant Weapp、TDesign MiniProgram、Ant Design Mini 组件
@@ -88,25 +89,47 @@ your-project/
8889
### build — 构建项目
8990

9091
```bash
91-
rsmax build <source> -o <output>
92+
rsmax build <source> -o <output> [-m, --mode <mode>]
9293
```
9394

9495
将源码编译输出到 dist 目录。编译前会清空输出目录,但保留 `miniprogram_npm`。
9596

97+
**选项:**
98+
99+
| 参数 | 别名 | 说明 | 默认值 |
100+
|------|------|------|--------|
101+
| `-o, --output <output>` | - | 输出目录 | `dist` |
102+
| `-m, --mode <mode>` | - | 环境模式(development/production/test 等任意自定义),决定加载的 `.env.<mode>` 文件和注入的 `process.env.NODE_ENV/MODE` 值 | `production` |
103+
96104
```bash
105+
# 生产环境构建(默认)
97106
rsmax build src -o dist
107+
108+
# 预发环境构建
109+
rsmax build src -o dist -m staging
98110
```
99111

100112
### dev — 开发模式(监听)
101113

102114
```bash
103-
rsmax dev <source> -o <output>
115+
rsmax dev <source> -o <output> [-m, --mode <mode>]
104116
```
105117

106118
监听源文件变化,增量编译。
107119

120+
**选项:**
121+
122+
| 参数 | 别名 | 说明 | 默认值 |
123+
|------|------|------|--------|
124+
| `-o, --output <output>` | - | 输出目录 | `dist` |
125+
| `-m, --mode <mode>` | - | 环境模式,决定加载的 `.env.<mode>` 文件 | `development` |
126+
108127
```bash
128+
# 开发模式(默认 mode=development)
109129
rsmax dev src -o dist
130+
131+
# 开发模式 + 使用预发环境接口
132+
rsmax dev src -o dist --mode staging
110133
```
111134

112135
### clean — 清理输出目录
@@ -269,6 +292,223 @@ export default function Detail() {
269292
| 独立分包页面 | 分包根目录(独立拷贝) | `../../rsmax-runtime.js`(回溯到分包根) |
270293
| 分包内组件 | 与同包页面一致 | 根据所在包自动计算 |
271294

295+
## 环境变量注入(编译时 Define 替换)
296+
297+
Rsmax 提供**零依赖、无运行时开销**的轻量级变量注入方案。所有 `process.env.XXX` 在**编译阶段被静态替换为字面量**,小程序运行时无需加载 Dotenv 等任何库,打包体积和运行时性能均零损耗。
298+
299+
> 设计原则:编译时静态替换(类似 Vite 的 `import.meta.env` / Webpack 的 `DefinePlugin`),不是运行时读取。
300+
301+
### 三层变量来源(优先级从低到高)
302+
303+
```
304+
优先级 1(最低) .env 文件(4 种类型,按加载顺序依次覆盖)
305+
│
306+
优先级 2 系统环境变量(RSMAX_ 前缀 + 白名单 NODE_ENV/ENV/MODE)
307+
│
308+
优先级 3(最高) rsmax.config.js 中的 define 配置
309+
```
310+
311+
> **注意**:CLI 的 `--mode` 参数会强制注入 `process.env.NODE_ENV` 和 `process.env.MODE`,优先级高于 `.env` 文件和系统环境变量,但低于 `define` 配置(即 `define` 可以覆盖一切)。
312+
313+
### 第一层:.env 文件(4 种,支持覆盖链)
314+
315+
在项目根目录(与 `rsmax.config.js` 同级)创建 `.env` 系列文件,支持 4 种加载类型(后者覆盖前者):
316+
317+
| 文件名 | 说明 | 何时加载 |
318+
|--------|------|----------|
319+
| `.env` | **默认**配置,所有环境都会加载 | 始终加载 |
320+
| `.env.local` | **本地个人**覆盖,不应提交到 git | 始终加载(优先级高于 `.env`) |
321+
| `.env.<mode>` | **指定环境**的配置(如 `.env.production`) | 当 `--mode <mode>` 匹配时加载 |
322+
| `.env.<mode>.local` | **指定环境的本地个人**覆盖 | 当 `--mode <mode>` 匹配时加载(优先级最高) |
323+
324+
`.env` 文件语法兼容 Dotenv 主流用法:
325+
326+
```dotenv
327+
# 简单键值对
328+
API_BASE=https://api.example.com
329+
APP_NAME=我的小程序
330+
331+
# 支持引号包裹(单/双引号都可以),包含空格或特殊字符时推荐
332+
MOTTO="Hello World"
333+
SECRET_KEY='abc123'
334+
335+
# 支持 export 前缀(可与 shell source 命令兼容)
336+
export DEBUG=true
337+
338+
# 支持 ${VAR} 和 $VAR 引用同一文件中前面的变量
339+
HOST=localhost
340+
PORT=8080
341+
BASE_URL=http://${HOST}:${PORT}
342+
FULL_URL=$BASE_URL/api
343+
344+
# 井号开头的行为注释(注释不能出现在行首以外除非前面有空格)
345+
APP_TITLE=测试应用 # 这是行尾注释
346+
```
347+
348+
示例项目结构:
349+
350+
```
351+
your-project/
352+
├── .env # 公共默认
353+
├── .env.local # 本地个人覆盖(建议加入 .gitignore)
354+
├── .env.development # 开发环境
355+
├── .env.development.local # 开发环境本地私钥
356+
├── .env.production # 生产环境
357+
├── .env.staging # 预发环境
358+
├── src/
359+
└── rsmax.config.js
360+
```
361+
362+
### 第二层:系统环境变量
363+
364+
系统环境变量在编译时从 `process.env` 读取,**仅以下两类会被注入**(避免将无关的系统变量意外注入到小程序包):
365+
366+
1. **`RSMAX_` 前缀** — 所有以 `RSMAX_` 开头的变量名会被原样注入(包含前缀):
367+
```bash
368+
# 例:CI/CD 脚本中设置
369+
RSMAX_DEPLOY_VERSION=$(git rev-parse --short HEAD)
370+
RSMAX_UPLOAD_TOKEN=xxxxxxxxxxxx
371+
```
372+
代码中直接使用 `process.env.RSMAX_DEPLOY_VERSION`、`process.env.RSMAX_UPLOAD_TOKEN`。
373+
374+
2. **白名单** — `NODE_ENV`、`ENV`、`MODE` 三个常用变量(不带前缀也可注入)。
375+
376+
> 提示:为避免命名冲突和安全泄漏,推荐始终使用 `RSMAX_` 前缀(除 `NODE_ENV/MODE` 外)。
377+
378+
### 第三层(最高优先级):rsmax.config.js 的 define 配置
379+
380+
在项目根目录 `rsmax.config.js` 中通过 `define` 字段注入(或覆盖)任意变量:
381+
382+
```js
383+
// rsmax.config.js
384+
module.exports = {
385+
// 组件映射等其他配置...
386+
components: { /* ... */ },
387+
388+
// 编译时 Define 变量(优先级最高,可覆盖 .env 和系统环境变量)
389+
define: {
390+
// 支持 string / number / boolean / null / undefined / 可 JSON 序列化的对象数组
391+
API_BASE: 'https://api.rsmax.dev',
392+
TIMEOUT: 10000,
393+
DEBUG: true,
394+
ENABLE_MOCK: process.env.CI ? false : true,
395+
FEATURE_FLAGS: {
396+
enableNewUserGuide: true,
397+
enableDarkMode: false
398+
},
399+
// 你甚至可以强制覆盖 NODE_ENV(极少需要)
400+
// NODE_ENV: 'production'
401+
}
402+
};
403+
```
404+
405+
### 在代码中使用(process.env.XXX)
406+
407+
#### 1. 点访问 / 方括号访问(推荐)
408+
409+
```jsx
410+
// pages/index/index.jsx
411+
import { useEffect, useState } from '@rsmax/runtime';
412+
413+
export default function Home() {
414+
const [userList, setUserList] = useState([]);
415+
416+
useEffect(async () => {
417+
// 编译时替换为字面量:https://api.example.com/users
418+
const resp = await wx.request({
419+
url: process.env.API_BASE + '/users',
420+
timeout: process.env.TIMEOUT
421+
});
422+
setUserList(resp.data);
423+
}, []);
424+
425+
return (
426+
<view>
427+
<text>当前环境:{process.env.NODE_ENV}</text>
428+
{process.env.DEBUG && <text class="tag-dev">调试模式</text>}
429+
</view>
430+
);
431+
}
432+
```
433+
434+
#### 2. 解构赋值
435+
436+
```jsx
437+
const { API_BASE, DEBUG, TIMEOUT } = process.env;
438+
439+
// 解构后 API_BASE / DEBUG / TIMEOUT 都是已替换的字面量常量
440+
console.log(API_BASE, DEBUG, TIMEOUT);
441+
```
442+
443+
#### 3. 内置的 NODE_ENV / MODE
444+
445+
无论是否配置,`process.env.NODE_ENV` 和 `process.env.MODE` 始终可用:
446+
- 默认值(未指定时):`dev` 命令为 `development`,`build` 命令为 `production`
447+
- 运行 `rsmax dev src -m staging` 时,两者都是 `'staging'`
448+
- 可用于常见的「环境判断」代码:
449+
```jsx
450+
if (process.env.NODE_ENV === 'production') {
451+
// 生产环境才启用的统计上报
452+
wx.reportMonitor('perf_page_load', duration);
453+
}
454+
```
455+
456+
### 常用场景示例
457+
458+
**场景:开发/生产接口地址切换**
459+
460+
```dotenv
461+
# .env.development(开发环境)
462+
API_BASE=https://dev-api.example.com
463+
DEBUG=true
464+
MOCK_ENABLED=true
465+
```
466+
467+
```dotenv
468+
# .env.production(生产环境)
469+
API_BASE=https://api.example.com
470+
DEBUG=false
471+
MOCK_ENABLED=false
472+
```
473+
474+
```bash
475+
# 开发模式 → 自动加载 .env + .env.development + 对应 local 文件
476+
rsmax dev src -o dist
477+
478+
# 生产构建 → 自动加载 .env + .env.production + 对应 local 文件
479+
rsmax build src -o dist
480+
```
481+
482+
**场景:CI/CD 中注入版本号**
483+
484+
```bash
485+
# Jenkins / GitHub Actions 等流水线脚本
486+
export RSMAX_BUILD_VERSION="v$(cat package.json | grep version | head -1 | awk -F: '{print $2}' | sed 's/[\",]//g' | tr -d '[[:space:]]')-$(git rev-parse --short HEAD)"
487+
rsmax build src -o dist -m production
488+
```
489+
490+
代码中直接读取:
491+
```jsx
492+
console.log('构建版本:', process.env.RSMAX_BUILD_VERSION);
493+
// 输出类似:构建版本:v1.2.3-a1b2c3d
494+
```
495+
496+
### 注意事项
497+
498+
1. **纯编译时替换,不支持动态拼接键名**:
499+
```jsx
500+
// ✅ 正确:静态确定的键名
501+
const url = process.env.API_BASE;
502+
503+
// ❌ 错误:运行时才知道访问哪个键(无法静态分析,不会被替换)
504+
const key = 'API_BASE';
505+
const url = process.env[key]; // 该表达式不会被替换,会报错
506+
```
507+
508+
2. **对象/数组字面量会以 `JSON.parse(...)` 形式注入**,性能开销可忽略;如需更轻量可先在 `define` 中扁平化为多个标量。
509+
510+
3. **不要在代码中注入密码、私钥等超高敏感信息**:编译后的值是**明文**写在产物 JS 文件中的(与所有同类方案一致),任何用户都可以反编译看到。建议仅注入非敏感的配置(接口域名、功能开关、版本号等)。
511+
272512
## Hooks API
273513

274514
### useState

0 commit comments

Comments
 (0)