* feat: docs directory

* docs: 调整结构

* chore: lock

* feat: update docs

* docs: update assets doc

* 新增文档 for 介绍 and 环境变量 (#307)

* docs: add about

* docs: add docs for env

* docs: update about

* docs: config (#306)

* docs: router (#304)

* docs: router

* feat: getconfig

* docs: get data

* docs: ssg

* docs: ssr

* docs: document

* docs: page

* docs: enable ssr

* fix: title

* Doc/rax compat (#303)

* docs: add docs of rax-compat

* docs: modify doc of rax compat

* Docs: app entry (#301)

* docs: app entry

* docs: app entry

* docs: mock (#245)

* docs: mock

* chore: typo

* chore: title

* docs: styles (#244)

* docs: style

* chore: acss title

* Docs: contributing (#243)

* docs: contributing

* chore: typo

* chore: update contributing docs

* fix: typo

* chore: add putteteer install

* docs: quick start (#235)

* docs: quick start

* chore: update IDE

* chore: update docs

* docs: app directory (#236)

* docs: directory

* chore: document desc

* docs: update website

* docs: update about

* docs: update docs

* chore: update prism

* chore: update docs

* chore: update

Co-authored-by: luhc228 <luhengchang228@126.com>
Co-authored-by: ClarkXia <xiawenwu41@gmail.com>
Co-authored-by: 逆葵 <xianyong.yxy@alibaba-inc.com>
Co-authored-by: ZeroLing <zhuoling.lcl@alibaba-inc.com>
Co-authored-by: ZeroLing <i@zeroling.com>
Co-authored-by: 水澜 <shuilan.cj@taobao.com>
Co-authored-by: 染陌同学 <answershuto@gmail.com>
Co-authored-by: luhc228 <44047106+luhc228@users.noreply.github.com>
This commit is contained in:
大果 2022-07-01 16:47:25 +08:00 committed by ClarkXia
parent e02776c610
commit 8dec98d0e8
46 changed files with 11336 additions and 157 deletions

View File

@ -43,7 +43,8 @@ jobs:
# 部署到 GitHub Pages
- name: Deploy
uses: JamesIves/github-pages-deploy-action@4.1.0
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/release-next'
with:
BRANCH: gh-pages
FOLDER: website/build
repositoryName: ice-lab/site-v3

100
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,100 @@
# 参与贡献
## 环境准备
1. 保证 Node.js 版本是 Node.js 14 以上。推荐安装 Node.js 16+ 版本,会大大提升本地调试速度
2. ICE 仓库是 `monorepo`,并使用 `pnpm workspace`。因此需要安装 [pnpm](https://pnpm.io/) 包管理工具
3. 在项目根目录下执行 `pnpm setup` 后会安装依赖和编译代码
> 如果在安装 puppeteer 的时候过慢,可以参考此 [issue](https://github.com/puppeteer/puppeteer/issues/6833#issuecomment-863488626) 进行配置 chromium 缓存。
## 目录说明
```markdown
ice-next
├── examples # 存档各种示例代码
├── packages # 存放 npm 包
| ├── bundles # 依赖预编译,锁定框架三方依赖版本,框架中的依赖需要从此包导入模块
| ├── ice # 工程代码,包括创建 service、构建任务、Webpack 和 esbuild 的打包编译逻辑等
| ├── plugin-auth # Auth 插件
| ├── route-manifest # 根据约定式路由生成路由配置
| ├── runtime # 运行时代码,包括 Client/Server 入口、Document、路由组件等
| ├── types # 公共 TS 依赖
| └── webpack-config # 存放 Webpack 默认的配置项
├── scripts # 执行脚本
├── tests # 测试用例
| ├── integration
| └── utils
└── website # ICE 官方文档
```
补充说明:
1. `packages` 目录下以 `plugin-xxx` 命名的都是插件包,插件的开发规范可以参考[文档]()
2. `packages/types` 是用于存放公共的 TS 类型声明,以在其他 `package` 中进行复用
## 调试
### 启动 watch 命令
此命令用于监听 `packages` 目录下代码变更,并增量编译代码。
```bash
pnpm watch
```
### 跑 example
`examples` 目录下存放了各种用于测试的 demo并且自动 Link 到 `packages` 目录下的代码。只需执行以下命令即可开始调试:
```bash
# 进入某个 example
$ cd examples/basic-project
# 调试 example
$ pnpm start
```
`packages` 目录下的 `npm` 包在进行代码编译时会生成 `SourceMap`,结合 IDE 可以很方便进行断点调试。以 VS Code 为例:
1. 选择 `JavaScript Debug Terminal` 进入 Debug 模式:
![image](https://user-images.githubusercontent.com/44047106/172011203-8b3b4016-8f7b-4743-bbef-30672ab04b03.png)
2. 进入某个 `example` 目录并执行 `pnpm start` 开始调试
3. 在某一行设置一个断点,当代码执行到此行时,将会停止执行并可查看各个变量的值
![image](https://user-images.githubusercontent.com/44047106/172013483-028d0fa8-8634-46fe-bbb6-1b28b39b8ce1.png)
## 测试
ICE 使用 [vitest](https://vitest.dev/) 进行单元测试和集成测试。执行以下命令可快速运行项目中的测试用例:
```bash
# 执行一次测试并生成代码覆盖率
$ pnpm test
# 启用 Test Watch 模式
$ pnpm test:watch
# 只跑部分测试用例
$ pnpm test basic-project.test.ts
```
## 文档
ICE 的文档使用了 [docusaurus](https://docusaurus.io/) 进行搭建。执行以下命令即可开始文档的开发:
```bash
# 进入到 website 目录
$ cd websites
# 安装依赖
$ yarn install
# 本地预览
$ yarn start
```
## 发布
```bash
# 发布 alpha 版本
$ pnpm publish:alpha
# 发布 beta 版本
$ pnpm publish:beta
```

View File

@ -15948,7 +15948,6 @@ packages:
- '@swc/core'
- esbuild
- uglify-js
dev: true
/webpack/5.73.0_@swc+core@1.2.168:
resolution: {integrity: sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==}

View File

@ -0,0 +1,4 @@
---
title: 3.0.0 版本发布
order: 1
---

View File

@ -1,15 +1,6 @@
module.exports = {
style: 'dark',
links: [
{
title: '参考',
items: [
{
to: 'https://iceteam.gitee.io',
label: '国内镜像站点',
},
],
},
{
title: '社区',
items: [
@ -21,6 +12,10 @@ module.exports = {
label: '阿里内部钉钉群',
href: 'https://iceworks.oss-cn-hangzhou.aliyuncs.com/assets/images/ice-group-inside.JPG',
},
{
to: 'https://iceteam.gitee.io',
label: '国内镜像站点',
},
{
label: 'GitHub Issue',
href: 'https://github.com/alibaba/ice/issues',
@ -31,16 +26,12 @@ module.exports = {
title: '生态',
items: [
{
label: '微前端 icestark',
href: 'http://micro-frontends.ice.work',
label: '微前端 ICESTARK',
href: 'https://micro-frontends.ice.work/',
},
{
label: 'ahooks',
href: 'https://ahooks.js.org/',
},
{
label: 'Formily',
href: 'https://v2.formilyjs.org/',
label: '包开发 ICE PKG',
href: 'https://pkg.ice.work/',
},
],
},
@ -51,18 +42,14 @@ module.exports = {
label: '淘系前端',
href: 'https://fed.taobao.org/',
},
{
label: 'Rax',
href: 'https://rax.js.org',
},
{
label: 'AppWorks',
href: 'https://appworks.site',
},
{
label: 'Kraken',
href: 'https://openkraken.com/',
},
// {
// label: 'Kraken',
// href: 'https://openkraken.com/',
// },
{
label: 'Midway',
href: 'https://midwayjs.org/',

View File

@ -14,32 +14,32 @@ module.exports = {
position: 'right',
label: '指南',
},
// {
// position: 'right',
// label: '示例',
// to: '/docs/examples/tailwind',
// },
{
position: 'right',
label: '插件',
to: '/docs/plugin/develop/start',
},
{
position: 'right',
label: '示例',
to: '/docs/examples/antd',
label: '博客',
to: '/blog',
},
{
label: '生态',
position: 'right',
items: [
{
label: '微前端 icestark',
label: '微前端 ICESTARK',
to: 'http://micro-frontends.ice.work/',
},
{
label: '包开发 ICE PKG',
to: 'https://pkg.ice.work/',
},
{
label: '可视化工具 AppWorks',
to: 'https://appworks.site/',
},
{
label: '自定义物料开发',
to: 'https://appworks.site/materials/about.html',
},
{
label: '前端环境 AppToolkit',
to: 'https://github.com/appworks-lab/toolkit#readme',
@ -52,11 +52,11 @@ module.exports = {
items: [
{
to: 'https://fusion.design/pc/doc/component/102',
label: 'fusion 组件',
label: 'Fusion 组件',
},
{
to: 'https://ant.design',
label: 'antd 组件',
label: 'Antd 组件',
},
{
to: 'https://iceteam.gitee.io',
@ -66,16 +66,12 @@ module.exports = {
label: '社区钉钉群',
to: 'https://iceworks.oss-cn-hangzhou.aliyuncs.com/assets/images/ice-group.png',
},
{
label: '阿里内部钉钉群',
to: 'https://iceworks.oss-cn-hangzhou.aliyuncs.com/assets/images/ice-group-inside.JPG',
},
],
},
{
href: 'https://github.com/alibaba/ice',
href: 'https://github.com/ice-lab/ice-next',
label: 'GitHub',
position: 'right',
},
],
};
};

View File

@ -9,34 +9,22 @@ const getDocsFromDir = require('../scripts/getDocsFromDir');
module.exports = {
docs: [
'guide/about',
// 'guide/start',
// 'guide/upgrade',
'guide/start',
// 'guide/practice',
{
type: 'category',
label: '基础指南',
label: 'ICE 指南',
collapsed: false,
items: getDocsFromDir('guide/basic'),
},
{
type: 'category',
label: '进阶指南',
label: '进阶',
collapsed: false,
items: getDocsFromDir('guide/advanced'),
},
],
plugin: [
{
type: 'category',
label: '插件开发',
collapsed: false,
items: getDocsFromDir('plugin/develop'),
},
{
type: 'category',
label: '官方插件',
collapsed: false,
items: getDocsFromDir('plugin/list'),
},
'guide/plugin',
'guide/upgrade',
],
examples: getDocsFromDir('examples'),
};
};

View File

@ -1,4 +1,4 @@
---
title: 使用 antd 组件
order: 1
order: 2
---

View File

@ -0,0 +1,4 @@
---
title: 开发 Electron 应用
order: 5
---

View File

@ -0,0 +1,5 @@
---
title: Module Federation
order: 4
---

View File

@ -1,4 +1,4 @@
---
title: 使用 fusion 组件
order: 2
order: 3
---

View File

@ -0,0 +1,4 @@
---
title: 代码质量
order: 4
---

View File

@ -1,5 +1,5 @@
---
title: 使用 Tailwind CSS
order: 3
order: 1
---

View File

@ -1,6 +1,71 @@
---
title: 关于飞冰
title: 关于
order: 1
---
About ICE 3.0
![](https://gw.alicdn.com/tfs/TB1vBRYaVOWBuNjy0FiXXXFxVXa-2558-1306.jpg)
飞冰 (ICE) 是一套基于 React 的前端解决方案,核心应用研发框架提供了基础的构建、路由、调试等基础能力以及微前端、一体化等领域能力,同时结合可视化操作、物料复用等方案降低研发门槛。
## 特性 🎉
- 🐒 开箱即用TypeScript/Webpack5/CSS Modules/Mock/SSR各种方案 All in One
- 🦊 贴合业务的最佳实践:目录规范、代码规范、路由方案、状态管理、数据请求等
- 🐯 多种应用模式:支持服务端渲染 SSR 以及静态构建 SSG
- 🐦 强大的插件能力:官方所有能力都通过插件实现,业务可以通过插件扩展各种能力
- 🐘 丰富的领域方案:微前端 ICESTARK、一体化方案等
在应用框架之上,我们还提供了 NPM 包开发工具 [ICE PKG](https://pkg.ice.work)
- 提供 React 组件开发、Node.js 模块开发、前端通用库等[多场景需求](https://pkg.ice.work/scenarios/component)
- 组件开发提供基础研发范式,提供组件文档、示例、预览等功能,[查看文档](https://pkg.ice.work/guide/preview)
- 更多场景可以通过插件的方式完成定制,查看[插件开发](https://pkg.ice.work/reference/plugins-development)
你也可以搭配 VS Code 插件 AppWorks 享受到更多功能:
- 通过大量的官方模板fusion/antd可视化创建项目[查看更多](https://appworks.site/materialCenter/react.html)
- 基于 VS Code 插件可视化的调试、管理依赖、拼装区块等,[查看文档](https://appworks.site)
- 业务可以根据规范定制自己的物料体系(含项目模板),[查看物料开发文档](https://appworks.site/materials/about.html)
- ……
## 常见问题 📝
### 与直接使用 Webpack 相比,使用 ICE 有什么优势?
Webpack 只提供了基础的构建能力ICE 在此基础上扩展了很多能力:
- 默认集成好的框架能力,无需再引入繁冗的构建插件和配置
- 不止是构建,更有面向业务领域的最佳实践,如路由、目录组织、状态管理等
- 让很多业务接入成本高的能力可以开箱即用,如 SSR/SSG、微前端、一体化基于原始的 Webpack 建设这些能力需要付出很高成本
- 通过插件化让以上这些能力可以被扩展以及跨项目复用,尽可能保证不同项目的一致性
### 我正在使用 icejs (ICE 2),需要升级到 ICE 吗?
ICE 相比之前的版本,增加了更多对移动端能力的优化和适配。升级或不升级都是可行的,原先的 icejs 依然是可用的,而且我们会保证 Bugfix 的持续迭代。如果你的页面会同时运行在移动端和桌面端,使用 ICE 可能会是更好的选择,亦或者是你对 ICE 提供的更新的构建工具链、更优更多的解决方案感兴趣,你都可以选择升级到 ICE。
### 使用飞冰(ICE)是否需要具备一定的前端基础?
毫无疑问是需要的,我们在努力降低前端开发的门槛,但一些基础的前端知识还是需要具备的,比如 JavaScript 的基础语法、前后端如何通信等。为了便于快速入门前端知识,我们整理了一份 [前端基础知识](https://ice.work/docs/resource/front-basic),希望能帮助到开发者。
### 资深前端同学是否适合使用飞冰?
适合,面向前端场景飞冰团队有大量的最佳实践,无论是构建、规范、状态管理还是微前端都可以开箱即用。
### 飞冰的浏览器兼容策略是怎样的?
应用框架 ICE 默认使用的是 React 18你可以查看 React 18 官方说明[对 JavaScript 环境的要求](https://zh-hans.reactjs.org/docs/javascript-environment-requirements.html)。如果你支持旧的浏览器和设备,可能需要引入对应的 Polyfill。
此外,飞冰官方 React 物料默认使用 React 16+ 进行开发,所以通常情况下这些物料在 ICE 中是可以正常运行的,如果你遇到任何问题,也可以通过 [Issue](https://github.com/alibaba/ice/issues) 或其它方式反馈给我们。
### 飞冰可以使用哪些 UI 组件?
飞冰的应用框架和工具都不耦合 UI 组件,因此开发者可以选择任意的 React UI 组件使用,比如 Fusion/Antd 等。
### 飞冰跟低代码方案有什么关系?
低代码方案一般指以可视化拖拽搭建为主少量地方使用代码辅助此类方案往往是面向具体领域而非通用场景的飞冰是面向通用领域的以源码研发为主通过框架、物料、GUI 操作等能力降低研发门槛,因此飞冰并不是通俗意义的低代码方案。
## 联系我们 🧼
- 反馈/建议https://github.com/alibaba/ice/issues/new
- 答疑钉钉群:
<img src="https://ice.alicdn.com/assets/images/qrcode.png" width="300px" align="left"/>

View File

@ -0,0 +1,7 @@
---
title: 权限
order: 7
hide: true
---
@TODO

View File

@ -0,0 +1,6 @@
---
title: 构建部署
order: 10
---
About ICE.0

View File

@ -0,0 +1,7 @@
---
title: 一体化
order: 8
hide: true
---
@TODO

View File

@ -0,0 +1,7 @@
---
title: 国际化
order: 6
hide: true
---
@TODO

View File

@ -0,0 +1,7 @@
---
title: 微前端
order: 9
hide: true
---
@TODO

View File

@ -0,0 +1,7 @@
---
title: 单元测试
order: 8
hide: true
---
@TODO

View File

@ -0,0 +1,84 @@
---
title: 应用入口
order: 4
---
ICE 通过应用配置的方式渲染整个应用,开发者可以根据提供的配置定制应用。
## 应用配置文件
框架以 `src/app.ts` 作为应用配置文件:
```tsx
import { defineAppConfig } from 'ice';
export default defineAppConfig({
app: {
strict: true,
},
});
```
> 推荐通过 `defineAppConfig()` 的方式导出应用配置,以获得良好的类型提示。
## 配置项
应用入口的配置项,支持应用常用的相关配置。
### app
#### `rootId`
根节点 id
- 类型:`string`
- 默认值 `ice-container`
#### `strict`
是否开启 React 的严格模式 (React.StrictMode)
- 类型 `boolean`
- 默认值 `false`
#### `errorBoundary`
是否启用内置的错误边界捕获能力
- 类型 `boolean`
- 默认值 `false`
### router
#### `type`
路由类型
- 类型 `string`,可选值为 `hash``browser`
- 默认为 `browser`
#### `basename`
路由 basename
- 类型 `string`
- 默认值 `/`
## 运行时拓展
应用入口除了支持定义应用配置之外,同时也承担运行时扩展的能力,比如权限配置:
```js
import { defineAppConfig } from 'ice';
import { defineAuthConfig } from '@ice/plugin-auth/esm/types';
// 导出 auth 相关的能力,该能力由 @ice/plugin-auth 插件提供
export const auth = defineAuthConfig(() => {
return {
initialAuth: {
admin: true,
},
};
});
export default defineAppConfig({
app: {
strict: true,
},
});
```
[//]: # (更多运行时插件能力,请参考[官方插件]&#40;/plugin/list/auth&#41;。)

View File

@ -0,0 +1,105 @@
---
title: 静态资源
order: 7
---
ICE 内置了大量规则处理静态资源,一般情况下开发者无需设置资源的处理方式,而对于一些特殊的处理规则框架同样给出了便捷方式和指引
# 基础规则
框架内置了针对以下资源的处理:
- 字体文件:`.woff`、`.woff2`、`.ttf`、`.eot`
- sgv 文件 `.svg`
- 图片资源 `.png`、`.jpg`、`.webp`、`.jpeg`、`.gif`
上述资源默认通过资源地址加载,推荐将这些资源放在 `src/assets` 目录下:
```shell
src
├── assets/
│ ├── logo.png
│ └── bg.png // Favicon
```
此时即可在代码中使用这些资源:
```jsx
import logoUrl from '@/assets/logo.png';
function Home() {
return (
<>
<img src={logoUrl} />
</>
);
}
```
如果资源尺寸小于 8kb则进行 base64 转码并内联到脚本或样式文件中。
# 指定处理规则
对于内置规则不满足特定场景的情况下,框架提供了便捷的方式对资源进行处理
## URL 引入
除基础规则中指定资源外,如果还希望通过资源地址的方式进行资源处理的,可以通过如下方式进行指定:
```jsx
import workletURL from 'extra-scalloped-border/worklet.js?url'
CSS.paintWorklet.addModule(workletURL);
```
`?url` 等同于为指定资源指定 url-loader
## 文件内容引入
通过 `?raw` 后缀声明将资源作为字符串引入:
```jsx
import txtContent from './text.txt?raw';
```
`?raw` 等同于为指定资源指定 raw-loader
## Worker 引入
脚本可以通过指定后缀引入为 web worker
```jsx
import Worker from './worker.js?worker'
```
`?worker` 等同于为指定资源指定 worker-loader
```jsx
import Worker from './worker.js?sharedworker'
```
`?sharedworker` 等同于为指定资源指定 worker-loader并指定 worker 类型为 SharedWorker
```jsx
import Worker from './worker.js?worker&inline'
```
`?worker&inline`等同于为指定资源指定 worker-loader并指定配置 `{ inline: 'fallback' }`
# public 目录
`public/` 目录作为框架默认的静态资源目录,不被构建工具进行编译的资源都可以放在该目录下。
比如 `favicon.svg` 文件,我们并不希望该文件名编译(默认静态资源文件名在编译后会生成独立 hash`favicon.svg` 希望保持原有文件名),在使用时直接在 html 模版中进行引用:
```html
<!DOCTYPE html>
<html>
<head>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
</head>
</html>
```
另外像不被源码引入的资源也存放在 public 目录下,比如设置 externals 后的 umd 链接,在不使用 CDN 的情况下,同样可以直接放在 `public` 目录下。
`public` 目录中的资源在开发时能直接通过 `/` 根路径访问到,并且打包时会被完整复制到目标目录的根目录下。

View File

@ -0,0 +1,331 @@
---
title: 构建配置
order: 13
---
ICE 支持常用的构建配置项,所有的配置项在 `ice.config.mts` 中设置。
## 配置文件
### 构建配置文件
为了获取良好的类型提示ICE 推荐以 `ice.config.mts` 作为配置文件:
```js
import { defineConfig } from '@ice/app';
export default defineConfig({
publicPath: '/',
});
```
### 兼容性配置
构建的兼容性配置推荐配置在 `.browserslistrc` 文件中:
```js
chrome 55
```
更多配置请参考 [browserslist 文档](https://github.com/browserslist/browserslist#readme)
## 配置项
### alias
- 类型:`Record<string, string | false>`
- 默认值:`{}`
在 icejs 默认配置了 { "@": "./src/" } 的规则,因此项目大多数时候不需要配置,配置完成后则可以更加简单的导入模块了:
```diff
-import CustomTips from '../../../components/CustomTips';
+import CustomTips from '@/components/CustomTips';
```
如果需要配置别名对 import 路径进行映射:
```js
import { defineConfig } from '@ice/app';
export default defineConfig({
alias: {
pages: './src/pages',
},
});
```
### define
- 类型:`Record<string, string | boolean>`
- 默认值:`{}`
配置运行时变量。
```js
import { defineConfig } from '@ice/app';
export default defineConfig({
define: {
ASSETS_VERSION: '0.1.0',
'process.env.TEST': true,
},
});
```
在代码中直接使用对应定义的变量:
```js
console.log(ASSETS_VERSION);
console.log(process.env.TEST);
```
对于运行时变量ICE 更加推荐通过[环境变量](./env.md)的方式注入。
### publicPath
- 类型:`string`
- 默认值:`/`
配置 Webpack 的 [output.publicPath](https://webpack.js.org/configuration/output/#output-publicpath) 属性,仅在运行 build 命令时生效。
### devPublicPath
- 类型:`string`
- 默认值:`/`
同 publicPath 仅在执行 start 时生效。
### hash
类型:`boolean | string`
默认值:`false`
如果希望构建后的资源带 hash 版本,可以将 hash 设置为 `true`,也可以设置为 `contenthash` 按文件内容生成 hash 值:
```js
import { defineConfig } from '@ice/app';
export default defineConfig({
hash: 'contenthash',
});
```
### externals
类型:`Record<string, string>`
默认值:`{}`
设置哪些模块不打包,转而通过 `<script>` 或其他方式引入,比如:
```js
import { defineConfig } from '@ice/app';
export default defineConfig({
externals: {
react: 'React',
},
});
```
对应在 `document.ts` 或者页面模版里添加 CDN 文件:
```diff
import { Main, Scripts } from 'ice';
function Document() {
return (
<html lang="en">
<body>
<Main />
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/cjs/react.production.min.js"></script>
<Scripts />
</body>
</html>
);
}
export default Document;
```
### outputDir
类型:`string`
默认值:`build`
构建产物输出目录,默认为 `build` 目录
### proxy
- 类型:`object`
- 默认值:`{}`
配置 dev 开发阶段的代理功能
```js
import { defineConfig } from '@ice/app';
export default defineConfig({
proxy: {
'/api': {
target: 'http://jsonplaceholder.typicode.com/',
changeOrigin: true,
pathRewrite: { '^/api' : '' },
},
},
});
```
### minify
- 类型:`boolean`
- 默认值:`true`
压缩产物,目前默认仅在 build 阶段生效
### dropLogLevel
- 类型:`'trace'|'debug'|'log'|'warn'|'error'`
- 默认值:`null`,不移除任何 console 代码
压缩代码时移除 console.* 相关代码,比如配置了 log 则会移除 console.trace
、console.debug、console.log 代码。
### compileDependencies
- 类型:`array|boolean`
- 默认值:`[]`
默认情况下为了保证 dev 开发阶段的体验,`node_modules` 下文件不会进行编译,而考虑到 build 阶段对代码体积的极致优化以及兼容性保证,将会对 `node_modules` 下内容也进行编译。
如果希望修正默认行为可以进行如下配置,设置为 `true`,不管 dev 还是 build 阶段均编译 `node_modules`
```js
import { defineConfig } from '@ice/app';
export default defineConfig({
compileDependencies: true,
});
```
如果明确知道哪些依赖需要进行编译也可以通过正则方式进行设置:
```js
import { defineConfig } from '@ice/app';
export default defineConfig({
compileDependencies: [/@alifd\/next/, /need-compile/],
});
```
### ssr
- 类型:`boolean`
- 默认值:`false`
是否开启 SSR 能力,更多 SSR 相关内容参考 [SSR 文档](./ssr)。
### ssg
- 类型:`boolean`
- 默认值:`true`
是否开启 SSG 能力,更多 SSG 相关内容参考 [SSG 文档](./ssg)。
### server
- 类型:`{ format: 'esm' | 'cjs'; bundle: boolean }`
- 默认值:`{ format: 'esm', bundle: false }`
SSR / SSG 产物标准,推荐以 ESM 标准进行执行,如果希望打包成一个 cjs 模块,可以进行如下设置:
```js
import { defineConfig } from '@ice/app';
export default defineConfig({
server: {
format: 'cjs',
bundle: true,
},
});
```
### routes
- 类型:`{ignoreFiles: string[]; defineRoutes: (route) => void}`
- 默认值:`{}`
定制路由地址,对于约定式路由不满足的场景,可以通过 `routes` 方式进行自定义:
```js
import { defineConfig } from '@ice/app';
export default defineConfig({
routes: {
// 忽略 pages 下的 components 目录
ignoreFiles: ['**/components/**'],
defineRoutes: (route) => {
// 将 /about-me 路由访问内容指定为 about.tsx
route('/about-me', 'about.tsx');
// 为 /product 路由添加 layout.tsx 作为 layout并渲染 products.tsx 内容
route('/', 'layout.tsx', () => {
route('/product', 'products.tsx');
});
},
},
});
```
### sourceMap
- 类型:`boolean | string`
- 默认值:`development` 模式:默认为 'cheap-module-source-map',支持通过 `false` 关闭,不支持设置为其他枚举值。`production` 模式:默认 `false`
### tsChecker
- 类型:`boolean`
- 默认值:`false`
默认关闭 TypeScript 类型检测,如需开启配置为 `true` 即可。
### eslint
- 类型:`boolean | object`
- 默认值:`undefined`
配置说明:
- `false`:不检测 eslint 错误
- `true`:将 eslint 错误展示在预览页面上
- `object`: 仅 Webpack 模式支持,表现等同于 true支持配置 [eslint-webpack-plugin](https://github.com/webpack-contrib/eslint-webpack-plugin) 的更多参数
### mock
- 类型:`boolean`
- 默认值:`true`
默认开启 mock 服务,如需关闭配置为 `false` 即可。
### webpack
- 类型:`(config: WebpackConfig, taskConfig: TaskConfig) => WebpackConfig`
- 默认值:`true`
ICE 默认基于 webpack 进行构建,在上述提供的构建配置无法满足的情况下,用户可以定制 webpack 配置:
```js
import { defineConfig } from '@ice/app';
import SpeedMeasurePlugin from 'speed-measure-webpack-plugin';
export default defineConfig({
webpack: (webpackConfig) => {
if (process.env.NODE_ENV !== 'test') {
// 添加 webpack 插件
webpackConfig.plugins?.push(new SpeedMeasurePlugin());
}
return webpackConfig;
},
});
```
> ICE 对 webpack 构建配置进行了定制,并借助 esbuild 等工具提升用户开发体验,直接修改 webpack 配置的方式并不推荐。
> 如有定制需求欢迎👏 PR 或反馈https://github.com/alibaba/ice/issues

View File

@ -0,0 +1,65 @@
---
title: 开发环境
order: 1
---
## Node.js
开发前端应用前需要安装 [Node.js](https://nodejs.org),并确保 node 版本是 14.x 或以上。推荐使用 [nvm](https://github.com/nvm-sh/nvm) 或者 [fnm](https://github.com/Schniz/fnm) 来管理 node 版本,进行安装。下面以在 mac 下安装 nvm 为例:
```bash
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
# 安装 node 14 版本
$ nvm install 14
# 使用 node 14
$ nvm use 14
# 验证 node 是否安装成功
$ node -v
v14.19.3
```
在国内使用 npm 安装依赖可能会比较慢。建议使用国内镜像源进行加速:
```bash
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
# 验证 cnpm 安装是否成功
$ cnpm -v
```
### 包管理工具
安装 Node.js 后,默认会包含 npm。除此以外还有其他的包管理工具
- [pnpm](https://pnpm.io/)(推荐)
- [cnpm](https://www.npmjs.com/package/cnpm)(推荐)
- [yarn](https://yarnpkg.com/)
安装 pnpm 示例如下:
```bash
$ npm i pnpm -g --register=https://registry.npmmirror.com/
# 验证 pnpm 安装是否成功
$ pnpm -v
7.1.7
```
如果经常需要切换 npm 镜像源,推荐使用 [nrm](https://github.com/Pana/nrm) 进行管理:
```bash
$ npm install -g nrm
# 验证 nrm 是否安装成功
$ nrm --version
# 查看所有镜像源
$ nrm ls
# 推荐使用淘宝镜像源
nrm use taobao
```
### IDE
推荐使用 IDE 进行前端应用开发和调试,会有更好的调试体验。目前比较流行的 IDE 有:
- [Visual Studio Code](https://code.visualstudio.com/)(推荐)
- [WebStorm](https://www.jetbrains.com/webstorm/)(推荐)
- [Sublime Text](https://www.sublimetext.com/)
- [Atom](https://atom.io/)

View File

@ -0,0 +1,92 @@
---
title: 目录结构
order: 1
---
ICE 的默认应用目录提供了良好的代码分层结构,约定的目录结构如下:
```bash
├── build // 构建产物目录
├── mock // 本地模拟数据
│ ├── index.ts
├── public // 静态资源目录
│ └── favicon.ico // Favicon 图标
├── src // 源码目录
│ ├── components // 自定义业务组件
│ ├── pages // 路由页面组件
| | ├── about.tsx
| | ├── home.tsx
| | └── layout.tsx // 全局布局组件
│ ├── global.css // 全局样式
│ ├── document.tsx // HTML 模板
│ └── app.ts // 应用入口
├── .env // 环境变量配置文件
├── ice.config.mts // 构建配置
├── package.json
└── tsconfig.json // TypeScript 配置文件
```
## package.json
声明应用所需要的各种依赖或者插件,以及配置信息(比如名称、版本、许可证等元数据)。
## ice.config.mts
应用的构建配置文件。详见 [构建配置](./config)。
## .env
配置环境变量。详见 [环境变量](./env)。
## tsconfig.json
TypeScript 编译所需的配置文件。
## mock 目录
存放 mock 文件,用于本地模拟请求数据服务。详见 [Mock](./mock)。
## public 目录
用于存放静态资源(如 `favicon.ico`)的目录,此目录下所有的文件会被复制到构建产物目录中。
## src 目录
用于存放源码的目录
### app.ts
项目的入口文件,用于对应用进行全局运行时配置,包括路由、添加 Provider 等。详见[应用入口](/docs/guide/basic/app)。
### document.tsx
HTML 模板,使用 JSX 语法来描述,与 `index.html` 类似用于生成 HTML 产物。详见 [Document](/docs/guide/basic/document)。
### global.[css|scss|less]
全局的样式配置,框架默认会引入该文件。详见[样式方案](/docs/guide/basic/style)
### pages 目录
存放路由组件的目录。ICE 使用约定式路由,会自动根据文件生成路由规则,详见[路由](/docs/guide/basic/router)。
### components 目录
项目通用的组件目录,推荐的目录形式如下:
```bash
src
├── components
| └── Guide
| ├── index.module.css
| └── index.tsx
```
组件通常会在路由组件中被引入。
## 其它
- build 目录
- 运行 `npm build` 后的构建产物目录,可修改构建配置修改输出路径。
- .ice 目录
- 运行 ICE 项目时默认生成的临时目录,该目录不需要进行 `git` 提交。

View File

@ -0,0 +1,77 @@
---
title: 定制 HTML
order: 12
---
ICE 使用 JSX 维护页面的 HTML 模板结构,其入口位于 `src/document.tsx`
## 初始模板
`Document` 的初始模板如下:
```jsx
import { Meta, Title, Links, Main, Scripts } from 'ice';
function Document() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="description" content="ICE DEMO" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Title />
<Links />
</head>
<body>
<Main />
<Scripts />
</body>
</html>
);
}
export default Document;
```
默认引入了以下组件:
- `Meta`:页面的元信息
- `Title` 页面的标题信息
- `Links` 页面面依赖的 CSS 资源及其他 `link` 标签
- `Scripts` 页面依赖的 JS 资源
- `Main` 页面渲染的容器节点
这些组件,配合各路由组件的 `getConfig()` 配置,可以实现不同页面 HTML 模板的差异化渲染。
## 内容定制
像开发其他 JSX 组件一样,可以在 `Document` 组件内插入自定义的其他 HTML 内容。例如:
```jsx
<body>
<div>hello</div>
<Main />
<Scripts />
<script src="xxx.js" />
</body>
```
注意: 在 `Scripts` 前插入外部资源,会阻塞主 Bundle 的解析执行,影响页面性能。
另外,在 JSX 中添加内联的 `style``script` 需要结合 `dangerouslySetInnerHTML` 的方式,示例如下:
```jsx
<style dangerouslySetInnerHTML={{__html: `
p {
color: red;
font-size: 20px;
}
`}}>
</style>
<script type="text/javascript" dangerouslySetInnerHTML={{__html: `
console.log("Hello World!")
`}}>
</script>
```

View File

@ -0,0 +1,114 @@
---
title: 环境变量
order: 13
---
ICE 内置通过环境变量实现给构建或运行时传递参数的功能。
- 使用 `.env` 文件来配置环境变量
- 配置 `ICE_` 开头的环境变量则会同时暴露到运行时环境中
## 如何配置环境变量
### 命令行环境变量
例如需要修改 ICE 本地开发服务的端口号,可以在命令行中使用环境变量:
```shell
$ cross-env PORT=9999 npm start
```
> 示例中使用了 cross-env 来兼容不容操作系统的环境变量配置方式。
### 使用 `.env` 文件
ICE 内置了加载 `.env` 文件的支持,在该文件中设置的环境变量会被自动加载到 `process.env` 上。
`.env` 文件的示例:
```shell
DEV_PORT=3000
FOO=bar
```
如果有部分环境变量的配置在本地有差异,你可以配置在 `.env.local` 文件中去覆盖 `.env` 中的配置。如在之前的 `.env` 的基础上, 你想本地开发覆盖之前 3000 端口, 而使用 9999 端口,示例如下:
```shell
# The .env.local should not be committed.
DEV_PORT=9999
```
此外你也可以在 `.env.${mode}``.env.${mode}.local` 文件中指定不同模式下的环境变量。`${mode}` 的取值是 `development``production`
需要注意的是:
1. 这几个文件的优先级由低至高分别是
- `.env`
- `.env.local`
- `.env.${mode}`
- `.env.${mode}.local`
2. 一般不建议将 `.local` 结尾的文件加入版本管理 (如 Git) 中。
## 使用环境变量
在 ICE 中,环境变量的使用场景分构建时与运行时两种类型。
特别注意:环境变量在使用时的类型都是 `string`,特别是设置为 `true``false` 时需要注意判断为字符串类型:
```js
// ICE_DISABLE_FOO=false
if (process.env.ICE_DISABLE_FOO === 'false') {
// ...
}
```
### 构建时
默认情况下,所有设置的环境变量都会被注入到构建环境,你可以在 `ice.config.mts` 文件或其它构建插件中通过 `process.env` 变量访问。
```js
const port = process.env.PORT;
// ...
```
### 运行时
默认情况下环境变量是不能在运行时访问的,如若需要在浏览器环境中访问,可以在设置环境变量时增加前缀:`ICE_`,如:
```shell
# File .env
ICE_APP_ID=123456
```
在运行时代码中访问:
```js
import React from 'react';
export default function AppID() {
return <h1>AppId is {process.env.ICE_APP_ID}.</h1>
}
```
## 内置的环境变量
ICE 会内置一些环境变量方便使用,通常由 `ICE_CORE_` 开头,如下:
### ICE_CORE_MODE
用于 ICE 的运行模式,可能是 `development``production`
### ICE_CORE_ROUTER
用于标识框架是否启用路由,可能是 `true``false`
### ICE_CORE_ERROR_BOUNDARY
用于标识框架是否启用错误边界,可能是 `true``false`
### ICE_CORE_INITIAL_DATA
用于标识框架是否启用初始数据,可能是 `true``false`
### ICE_CORE_DEV_PORT
用于标识 ICE 的开发服务器端口号。

View File

@ -0,0 +1,121 @@
---
title: 数据模拟 Mock
order: 7
---
在前后端分离的开发中Mock 数据是前端开发中很重要的一个环节,前端可以不必强依赖后端接口,只需要约定好对应的数据接口,前端可以通过 Mock 模拟数据先行开发,在后端接口开发完成后,只需要切换对应的接口地址即可,可以保证项目的同步开发。
ICE 提供了开箱即用的 Mock 方案,支持 CRUD 等操作,在启动本地调试时会自动启用 Mock 服务。
## 目录约定
只需要在项目目录下新建 `/mock` 目录,并增加 `js``ts` 文件作为 `mock` 服务文件。比如有以下的目录结构:
```markdown
├── mock
| ├── index.ts
| └── user.ts
├── src
└── package.json
```
`mock` 目录下的 `index.ts``user.ts` 会被识别为 Mock 服务文件。
## 编写 Mock 接口
在 mock 服务文件中写入以下代码:
```ts title="./mock/user.ts"
export default {
'GET /api/users': [
{ name: 'foo', id: 0 },
{ name: 'bar', id: 1 },
],
}
```
启动调试服务后,假设启动的端口是 `3333`,直接在浏览器里访问 <http://127.0.0.1:3333/api/users> 即可看到接口返回数据。
### 请求方法
默认支持 `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS` 请求方法。示例代码如下:
```ts
export default {
// 当 HTTP 请求方法是 GET可以省略请求方法
'/api/users': [{ name: 'foo', id: 1 }, { name: 'bar', id: 2 }],
// 等同于上面的写法
'GET /api/users': [{ name: 'foo', id: 1 }, { name: 'bar', id: 2 }],
'POST /api/user': { users: [1, 2] },
'DELETE /api/users/1': { name: 'foo' },
}
```
### 返回值
返回值支持 `String`、`Array`、`Object` 类型。比如:
```ts
export default {
// 返回值是 String 类型
'GET /api/name': 'foo',
// 返回值 Array 类型
'POST /api/users': [
{ name: 'foo', id: 0 },
{ name: 'bar', id: 1 },
],
// 返回值是 Object 类型
'DELETE /api/users/1': { name: 'bar', id: 1 },
}
```
除此以外,还可以使用函数的形式来计算返回值,这在需要动态返回接口数据时很有用,如:
```ts
export default {
'POST /api/users/:id': (req, res) => {
const { id } = req.params;
res.send({ id: id });
},
}
```
## 关闭 Mock
当后端接口开发完成以后。此时可以通过以下命令关闭 Mock 服务:
```bash
# 关闭 Mock 服务
$ npm run start -- --no-mock
```
这样我们可以请求到后端返回的数据了。
## 使用 Mock.js
[Mock.js](https://github.com/nuysoft/Mock) 是一个随机生成 mock 数据的工具库,可以帮助我们快速生成随机的模拟数据。
```ts
import mockjs from 'mockjs';
export default {
'GET /api/list': (req, res) => {
const list = Mock.mock({
'list|1-10': [
{
'id|+1': 1,
},
],
});
res.send({
status: 'SUCCESS',
data: {
list,
}
});
},
};
```
完整的语法请参考 [Mock.js 文档](http://mockjs.com/examples.html)。

View File

@ -0,0 +1,125 @@
---
title: 页面
order: 4.1
---
每一张页面,都可以由 `路由组件``零或多个布局组件` 组装而成。路由组件和布局组件的开发规范基本一致,可以包含以下内容:
- 默认导出是组件的具体实现,必选。
- 导出 `getData()` 方法,约定页面的数据请求,可选。
- 导出 `getConfig()` 方法,约定页面的 `Title`、`Meta` 等信息,可选。
## 组件
对应路由组件或布局组件在页面中需要渲染的内容。
```tsx title="src/pages/index.tsx"
import { useData } from 'ice';
export default function Home() {
const data = useData();
return (
<>
<div>Hello ICE</div>
<div>{JSON.stringify(data)}</div>
</>
);
}
```
## getData()
获取页面初始数据的方法,详见[数据请求](./request.md)。
## getConfig()
获取页面运行时配置的方法,页面主体内容之外的,其他需要通用 HTML 模板上差异化显示的内容,可以通过导出 `getConfig` 方法来声明。
支持的页面级配置包含:
#### title
标题会显示在文档上,可以通过 `title` 属性来设置。 示例:
```tsx
export function getConfig() {
return {
title: 'Home',
};
}
```
#### metas
Meta 信息会显示在文档上,可以通过 `meta` 属性来设置。 示例:
```tsx
export function getConfig() {
return {
metas: [
{ charset: 'utf-8' },
{
title: 'Something cool',
description: 'This becomes the nice preview on search results.',
},
],
};
}
```
#### links
页面级需要额外插入的 `<link />` 标签,会被插入 `<head>` 标签内,先于页面自身的 Bundle 加载,是阻塞型的。
> 框架提供了这个能力,但不推荐使用,除非确有需要前置加载。
```tsx
export function getConfig() {
return {
links: [
{
rel: 'icon',
href: '/favicon.png',
type: 'image/png',
},
{
rel: 'stylesheet',
href: 'https://example.com/some/styles.css',
},
]
};
}
```
推荐,在页面组件内延迟加载,以达到更好的性能体验。
```tsx
// src/pages/index.tsx
export default function Home() {
return (
<>
<div>Hello ICE</div>
<link rel="stylesheet" href="https://example.com/some/styles.css" />
</>
);
}
```
### scripts
页面级需要前置加载的脚本资源,会被插入在主 Bundle 前,但是会阻塞渲染。通常用于加载全局 JS SDK 或 Polyfill。
```tsx
export function getConfig() {
return {
scripts: [
{
src: 'https://example.com/some/index.js',
},
],
};
}
```
推荐在页面组件内按需异步加载,以达到更好的性能体验。
@TODO 补充推荐的方式示例

View File

@ -0,0 +1,92 @@
---
title: 页面数据请求
order: 6
---
在 ICE 中,推荐为页面组件定义 `getData()` 方法,来发起页面的数据请求。在 `getData()` 中定义的数据请求,会和页面的资源加载并行发起,以达到更好的性能体验。
示例:
```tsx
// src/pages/index.tsx
import { useData } from 'ice';
export default function Home() {
const data = useData();
return (
<>
<div>Hello ICE</div>
<div>{JSON.stringify(data)}</div>
</>
);
};
export async function getData() {
const data = await fetch('https://example.com/api/xxx');
return data;
}
```
传统的做法,一般是在 useEffect 中发起数据请求,数据请求依赖于页面的 Bundle 加载、解析、执行,整个过程是串行的。
```tsx
// src/pages/index.tsx
import { useState, useEffect } from 'react';
export default function Home() {
const [data, setData] = useState();
useEffect(() => {
const data = await fetch('https://example.com/api/xxx');
setData(data);
}, [])
return (
<>
<div>Hello ICE</div>
<div>{JSON.stringify(data)}</div>
</>
);
};
```
相比之下,`getData()` 变数据请求从串行到并行,天然带来了更好的性能体验,是框架推荐的数据方法。
## 入参
`getData()` 的入参包含:
- `pathname`: `string`, 当前页面的路径名。
- `query`: `object`, 当前页面的 `query` 信息,会被提前解析。
```tsx
export async function getData(params) {
console.log(params.pathname);
console.log(params.query);
const data = await fetch('https://example.com/api/xxx');
return data;
}
```
## useData()
在路由组件里使用 `getData()` 返回的数据,需配合 `useData()` 使用。 示例:
```tsx
// src/pages/index.tsx
import { useData } from 'ice';
export default function Home() {
const data = useData();
return (
<>
<div>Hello ICE</div>
<div>{JSON.stringify(data)}</div>
</>
);
};
```

View File

@ -0,0 +1,158 @@
---
title: 路由
order: 4
---
一个应用,通常包含多张页面,每张页面对应不同的地址。在 ICE 中,采用了约定式路由,会根据项目的目录结构自动生成应用的路由信息。
## 路由组件
路由组件,是每一个页面的入口文件,通过 `export default` 导出其具体实现,例如:
```tsx
// src/pages/index.tsx
export default function Home() {
return (
<div>Hello ICE</div>
);
};
```
更多能力,详见[页面](./page.md)。
## 路由规则
`src/pages` 目录下创建的每一个 `.(js|jsx|tsx)` 文件, 都对应着一个具体的路由。如下面的目录结构:
```
/src
└── pages
└── repo
| ├── index.tsx
| └── preview.tsx
├── about.tsx
└── index.tsx
```
对应的路由匹配规则为:
| URL | 路由组件 |
| --------------- |:--------------------------:|
| / | pages/index.tsx |
| /about | pages/about.tsx |
| /repo | pages/repo/index.tsx |
| /repo/preview | pages/repo/preview.tsx |
## 路由跳转
ICE 通过 `Link` 组件,来提供路由间的跳转能力。基于 `Link` 组件,可以只加载下一个页面相比于当前页面差异化的 Bundle 进行渲染,以达到更好的性能体验。
```jsx
// src/pages/index.tsx
import { Link } from 'ice';
export default function Home() {
return (
<>
<div>Hello ICE</div>
<Link to="/about">about ice</Link>
</>
);
}
```
## 布局组件
`pages` 目录下,还可以创建一类特殊的组件,来维护全局或一组页面共用的布局, 其文件名约定为 `layout.(js|jsx|tsx)`
布局组件和路由组件一样,也通过 `export default` 导出其具体实现。
```jsx
import { Outlet } from 'ice';
export default function Layout() {
return (
<div>
<h1>Root Layout</h1>
<h2>Hello ICE</h2>
<Outlet />
</div>
)
}
```
其中, `<Outlet />` 组件对应需要被布局组件嵌套的子组件。
布局组件:
- 如果位于 `pages` 目录的最顶层,则它将作为全局布局,嵌套在所有路由组件外。
- 如果位于某个子文件夹,则它将作为页面级布局,嵌套在这个目录下的其他路由组件外。
如果同时存在 **全局布局组件****页面级布局组件**,则全局布局组件会嵌套于页面级布局组件之外。
例如,下面的目录结构:
```diff
/src
├── layout.tsx
└── pages
└── repo
+ | ├── layout.tsx 页面级布局组件
| ├── index.tsx
| └── preview.tsx
├── about.tsx
+ ├── layout.tsx 全局布局组件
└── index.tsx
```
每个路由,对应的 Layout 匹配规则为:
| URL | 路由组件 | 布局组件 |
| --------------- |:--------------------------:|-------------------------------------------:|
| / | pages/index.tsx | src/layout.tsx |
| /about | pages/about.tsx | src/layout.tsx |
| /repo | pages/repo/index.tsx | src/layout.tsx + src/pages/repo/layout.tsx |
| /repo/preview | pages/repo/preview.tsx | src/layout.tsx + src/pages/repo/layout.tsx |
## 动态路由
路由中包含了动态参数的路由,被称做动态路由。如果文件名或者目录名是以 `$` 开头,则判定这个路由为动态路由。
> 注意:动态路由仅在 SSR 下生效。
例如,下面的目录结构:
```diff
/src
├── layout.tsx
└── pages
└── repo
+ ├── $id
+ | ├── author
+ | | └── $name.tsx
+ | └── index.tsx
├── index.tsx
└── preview.tsx
```
对应的路由匹配规则为:
| URL | 路由组件 |
|----------------------|:--------------------------:|
| /repo | pages/repo/index.tsx |
| /repo/preview | pages/repo/preview.tsx |
| /repo/ice | src/pages/repo/$id/index.tsx |
| /repo/ice/author/foo | src/pages/repo/$id/author/$name.tsx |
在动态路由组件中可以通过 `useParams()` 方法拿到当前路由的参数:
```tsx
// src/pages/repo/$id/author/$name.tsx
import { useParams } from 'ice';
export default function() {
const { id, name } = useParams();
console.log(id); // 'ice'
console.log(name); // 'foo'
return <div />;
}
```

View File

@ -0,0 +1,96 @@
---
title: 构建时渲染 SSG
order: 10
---
构建时渲染,简称 SSG (Static Site Generation),是指在构建时提前生成内容 HTML 的渲染模式。
ICE 默认开启 SSG 能力。SSG 不仅适用于静态站点,也适用于为普通 CSR 应用提前生成静态内容。
假设有如下路由组件,其内容为:
```tsx
// src/pages/index.tsx
import { useData } from 'ice';
export default function Home() {
const data = useData();
return (
<>
<div>Hello ICE</div>
<div>stars: {data?.stars}</div>
</>
);
}
```
其中,第一个 `div` 中的内容是不依赖于数据。在传统的 CSR 应用中,`<Home />` 组件内容,无论是否依赖数据,都需要等待 JS 加载、解析后渲染。
利用 SSG则可以在构建时就将不依赖于动态数据的部分提前生成到 HTML 中。示例:
```html
<html>
<head>
...
</head>
<body>
<div id="ice-container">
<div>Hello ICE</div>
<div>stars: </div>
</div>
</body>
</html>
```
### 注意事项
- 需要在消费 data 时,做好空值判断,避免 data 为 `undefined``null` 时,产生渲染异常,无法正常构建。
- 兼容 Node.js 端。SSG 会在构建时进行,因此代码会运行在 Node.js 侧,因此在消费一些浏览器特有的环境变量时,要做好环境判断。
### 定制 SSG 的数据源
如果希望在 SSG 时使用兜底数据,可以通过为路由组件定义 `getStaticData()` 方法。这样在 SSG 时,组件 `useData()` 获取的数据,为 `getStaticData()` 的返回值。
```tsx
// src/pages/index.tsx
import { useData } from 'ice';
export default function Home() {
const data = useData();
return (
<>
<div>Hello ICE</div>
<div>stars: {data?.stars}</div>
</>
);
}
// 浏览器侧的常规数据请求
export function getData() {
return {
stars: 1000,
};
}
// 返回用于 SSG 的数据
export function getStaticData() {
// 浏览器侧的常规数据请求
return {
stars: 0,
};
}
```
构建 Client 端的 Bundle 时,会移除 `getStaticData()` 及其相关依赖。
## 关闭 SSG
`ice.config.mts` 下,按如下配置修改
```tsx
import { defineConfig } from '@ice/app';
export default defineConfig({
ssg: false,
});
```

View File

@ -0,0 +1,51 @@
---
title: 服务端渲染 SSR
order: 11
---
服务器渲染,简称 SSR (Server Side Rendering),是一种在服务端运行 Node.js 程序动态生成 HTML 的渲染方式。
SSR 相比传统在浏览器端渲染的模式(CSR),受设备性能和网络情况的影响更小,可以达到更好的性能体验和 SEO 能力。
## 开启 SSR
与 SSG 不同的是ICE 中 SSR 不是默认启用的。
`ice.config.mts` 中,增加如下配置:
```tsx
import { defineConfig } from '@ice/app';
export default defineConfig({
// ...
ssr: true,
});
```
## 数据请求
开启 SSR 后,路由组件中导出的 `getData()` 方法将会在 Server 端被执行,如果 SSR 渲染成功,在 Client 端将不会再次调用 `getData()`,而会复用 SSR 的结果。当页面在浏览器侧通过路由跳转,或页面降级时,才会在 Client 端调用 `getData()`
因此,一般情况下 `getData()` 内的数据请求需要保持同构,在 Server 端和 Client 端都能执行。
如果确实需要为 Server 端指定不一样的数据请求方式,可以通过定义 `getServerData()` 来实现。当路由组件声明了 `getServerData()`,会在 SSR 优先使用这个方法。
示例:
```tsx
// Client 端专用的数据请求
export async function getData() {
const data = await fetch('https://example.com/api/xxx');
return data;
}
// Server 端专用的数据请求
export async function getStaticData() {
const data = await sendRequestInServer();
return data;
}
```
构建 Client 端的 Bundle 时,会移除 `getServerData()` 及其相关依赖。

View File

@ -0,0 +1,177 @@
---
title: 样式
order: 5
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
ICE 推荐使用原生 CSS + PostCSS 的方案编写样式,不建议使用 `less/sass` 之类的预编译方案CSS 写法目前扩展支持了 `@import` 以及嵌套写法。
<Tabs>
<TabItem value="css" label="index.css">
```css
@import './theme.css';
.home {
color: red;
h2 {
display: block;
}
}
```
</TabItem>
<TabItem value="tsx" label="index.tsx">
```tsx
import './index.css';
function Home() {
return (
<div className="home">
<h2>CSS Modules</h2>
</div>
);
}
```
</TabItem>
</Tabs>
> ICE 同时支持 `less/scss` 预编译器,只要保证文件后缀匹配即可。
## 全局样式
对于整个项目的全局样式,统一定义在 `src/global.css` 文件中,框架会默认引入该文件:
```css
body {
-webkit-font-smoothing: antialiased;
}
```
## 局部样式
对于页面级和组件级的样式,我们推荐使用 CSS Modules 的方案,这能很好的解决样式开发中的两个痛点问题:
1. 全局污染CSS 使用全局选择器机制来设置样式,优点是方便重写样式。缺点是所有的样式都是全局生效,样式可能被错误覆盖,因此产生了非常丑陋的 !important甚至 inline !important 等问题。
2. 命名混乱:由于全局污染的问题,多人协同开发时为了避免样式冲突,选择器越来越复杂,容易形成不同的命名风格,很难统一,样式变多后,命名将更加混乱。
具体规范规则如下:
- 文件名:约定文件名格式如 `xxx.module.css`
- 模块化:一个页面或者一个组件对应一个样式文件
如有以下的目录结构和代码:
```
├── src
| ├── pages
| | ├── index.module.css
| | └── index.tsx
```
<Tabs>
<TabItem value="css" label="index.module.css">
```css
.container {
background: #fff;
}
```
</TabItem>
<TabItem value="tsx" label="index.tsx">
```tsx
import styles from './index.module.css';
function Home() {
return (
<div className={styles.container}>
<h2>CSS Modules</h2>
</div>
);
}
```
</TabItem>
</Tabs>
使用该方案之后,上文中的 `className` 都会被编译为唯一性的名字,避免因为重名 `class` 而产生样式冲突。如果在浏览器里查看这个示例的 DOM 结构,你会发现实际渲染出来是这样的:
```html
<div class="container--WZ5p3kdM"><h2>CSS Modules</h2></div>
```
同时 CSS Modules 支持 less/scss 预编译器:
```tsx
import lessStyles from './index.module.less';
import scssStyles from './index.module.scss';
export default function () {
return <div className={lessStyles.title}>
Hello World
<p className={scssStyles.blue}>I am blue</p>
</div>;
}
```
更多 CSS Modules 文档请参考:
- [css-modules 官方文档](https://github.com/css-modules/css-modules)
- [CSS Modules 详解及 React 中实践](https://zhuanlan.zhihu.com/p/20495964)
## 常见问题
### 如何覆盖全局基础组件next/antd样式
推荐通过 `src/global.css` 覆盖全局样式:
```css title="src/global.css"
body {
-webkit-font-smoothing: antialiased;
/* 覆盖 next 组件的样式 */
.next-btn {
font-size: 18px;
}
}
```
该方式会覆盖应用中所有 `Button` 组件的 `font-size` 属性。
### 如何覆盖局部基础组件样式
如果只是想覆盖某个页面/模块里的组件样式,则推荐采用局部覆盖的方式:
```css title="./pages/Home/index.module.css"
.home {
padding: 10px;
}
.home :global {
/* 仅修改 .home 下的 button 样式 */
.next-btn {
font-size: 24px;
}
}
```
如果组件本身支持 `style` 属性,也可通过 `style` 属性修改:
```tsx title="./pages/Home/index.tsx"
export default function () {
return (
<>
<Button style={{ fontSize: '16px' }}>OK</Button>
</>
);
}
```
### 如何获得 CSS 嵌套的类型提示
可以在 VSCode 编辑器中需要安装 [PostCSS Language Support 插件](https://marketplace.visualstudio.com/items?itemName=csstools.postcss) 以支持嵌套写法。

View File

@ -0,0 +1,4 @@
---
title: 插件开发
order: 6
---

View File

@ -0,0 +1,7 @@
---
title: 实战教程
order: 3
hide: true
---
@TODO.

View File

@ -0,0 +1,71 @@
---
title: 快速开始
order: 2
---
## 创建应用
在终端执行以下命令:
```bash
$ npx create-ice ice-app --template ice-scaffold-simple
```
看到如下信息说明项目创建成功:
```bash
✔ download npm tarball successfully.
info clean package.json...
Initialize project successfully.
Starts the development server.
cd ice-app
npm install
npm start
```
## 本地调试
首先需要安装项目依赖:
```bash
# 进入项目目录
$ cd ice-app
# 安装依赖
$ npm install
```
安装依赖完成以后,执行以下命令以启动调试:
```bash
# 启动调试
$ npm start
```
此时会自动打开浏览器窗口并访问 <http://localhost:3333>,这时会看到默认页面。
![img](https://img.alicdn.com/imgextra/i1/O1CN01wu2tKv1vctzonOD8L_!!6000000006194-2-tps-1094-1132.png_790x10000.jpg)
## 部署发布
执行以下命令以构建生产环境产物:
```bash
# 构建
$ npm build
```
产物默认生成到 `build` 目录下:
```markdown
./build
├── css
| └── index.css
├── index.html
└── js
├── framework.js
├── index.js
└── main.js
```
这时你可以把 `build` 目录部署到服务器上。

View File

@ -0,0 +1,57 @@
---
title: 从 Rax 升级
order: 20
---
本文档面向的是使用 Rax App 的开发者,提供升级到 ICE 的方式。React 的社区生态显著优于 Rax切换到 React 之后可以享受到更多的 React 生态,部分复杂场景(富文本、脑图等)可以大幅度降低成本。
## 如何升级
ICE 提供了 [rax-compat](https://github.com/ice-lab/ice-next/tree/master/packages/rax-compat) 以支持 [Rax](https://github.com/alibaba/rax) 到 React 运行时的切换。
`rax-compat` 通过对 React 的能力的封装,在内部抹平了 Rax 与 React 使用上的一些差异,同时导出了与 Rax 一致的 API 等能力,通过 alias 来将源码中的 rax 用 rax-compat 来替换,即可桥接上 React 的运行时能力。
## 安装与使用
用户可以直接通过引入插件 [@ice/plugin-rax-compat](https://www.npmjs.com/package/@ice/plugin-rax-compat) 来完成在 ICE 中运行 Rax 应用。
```bash
$ npm i @ice/plugin-rax-compat --save-dev
```
```js
import compatRax from '@ice/plugin-rax-compat';
export default defineConfig({
// ...
plugins: [
// ...
compatRax({
inlineStyle: true,
}),
],
});
```
## `rax-compat` 兼容性
### Rax 核心 API
@列一下 Rax 核心提供的 API 列表, 也包括之前 rax 1.0 移动到独立包的 API.
### Appear & Disappear
@ Rax 这边是直接支持了这个事件。
@ 另外需要一篇文档来提供 ICE Appear 事件支持 (用组件支持).
### 样式的处理
@ inlineStyle 和 CSS Modules 特殊逻辑的解释。rpx 单位的解释。
### 差异补充
@比如 DOM attributes 的处理。
rax-picture + rax-compat
@ice/picture + `<Appear />`

View File

@ -1,6 +0,0 @@
---
title: 开发指南
order: 1
---
start

View File

@ -3,9 +3,9 @@ const navbar = require('./config/navbar');
const footer = require('./config/footer');
const config = {
title: '飞冰',
tagline: '基于 React 的应用研发框架',
url: 'https://beta.ice.work',
title: '飞冰 ICE ',
tagline: ' 基于 React 的应用研发框架',
url: 'https://v3.ice.work',
baseUrl: '/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'throw',
@ -13,18 +13,27 @@ const config = {
organizationName: 'alibaba',
projectName: 'ice',
themeConfig: {
// announcementBar: {
// id: 'announcementBar-2',
// content: 'icejs 2.0 版本已发布,支持 Webpack 5 和 Vite 两种构建模式,点击 <a href="/docs/guide/upgrade">快速升级</a>',
// isCloseable: true,
// },
announcementBar: {
id: 'announcementBar-2',
content: 'ICE 3 Beta不仅是 PC更适配移动端能力<a href="/docs/guide/upgrade">快速升级</a>',
isCloseable: true,
},
navbar,
footer,
prism: {
theme: require('prism-react-renderer/themes/oceanicNext'),
},
// algolia: {
// apiKey: '01f284e7e1c13eac3dc14beb6d8b153d',
// indexName: 'ice',
// },
},
i18n: {
defaultLocale: 'zh-CN',
locales: [
'zh-CN',
],
},
presets: [
[
'@docusaurus/preset-classic',
@ -50,16 +59,16 @@ const config = {
],
};
if (process.env.USE_LOCAL_SEARCH) {
// 内部站点无法使用 algolia
delete config.themeConfig.algolia;
config.plugins.push([
require.resolve('@easyops-cn/docusaurus-search-local'),
{
hashed: true,
language: ['en', 'zh'],
},
]);
}
// if (process.env.USE_LOCAL_SEARCH) {
// 内部站点无法使用 algolia
delete config.themeConfig.algolia;
config.plugins.push([
require.resolve('@easyops-cn/docusaurus-search-local'),
{
hashed: true,
language: ['zh', 'en'],
},
]);
// }
module.exports = config;
module.exports = config;

View File

@ -14,18 +14,23 @@
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@docusaurus/core": "^2.0.0-beta.15",
"@docusaurus/preset-classic": "^2.0.0-beta.15",
"@docusaurus/theme-search-algolia": "^2.0.0-beta.15",
"@easyops-cn/docusaurus-search-local": "^0.19.1",
"@docusaurus/core": "^2.0.0-beta.21",
"@docusaurus/preset-classic": "^2.0.0-beta.21",
"@docusaurus/theme-search-algolia": "^2.0.0-beta.21",
"@easyops-cn/docusaurus-search-local": "^0.28.0",
"@mdx-js/react": "^1.6.21",
"@svgr/webpack": "^5.5.0",
"clsx": "^1.1.1",
"file-loader": "^6.2.0",
"react": "^18.0.0",
"react-dom": "^17.0.1",
"prism-react-renderer": "^1.3.5",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"url-loader": "^4.1.1"
},
"devDependencies": {
"glob": "^7.1.7",
"gray-matter": "^4.0.3"
},
"browserslist": {
"production": [
">0.5%",
@ -37,12 +42,5 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"axios": "^0.21.1",
"fs-extra": "^10.0.0",
"glob": "^7.1.7",
"gray-matter": "^4.0.3",
"nodejieba": "^2.5.2"
}
}

View File

@ -5,34 +5,34 @@ module.exports = function (context, options) {
const { createData, addRoute } = actions;
const routes = [
{
from: '/docs/guide/advance/fusion',
to: '/docs/plugin/list/fusion',
},
{
from: '/docs/guide/advance/antd',
to: '/docs/plugin/list/antd',
},
{
from: '/docs/guide/develop/plugin-dev',
to: '/docs/plugin/develop/start',
},
{
from: '/docs/guide/develop/plugin-build',
to: '/docs/plugin/develop/build',
},
{
from: '/docs/guide/develop/plugin-runtime',
to: '/docs/plugin/develop/runtime',
},
{
from: '/docs/guide/develop/plugin-list',
to: '/docs/plugin/list/moment-locales',
},
{
from: '/docs/guide/basic/api',
to: '/docs/config/about',
},
// {
// from: '/docs/guide/advance/fusion',
// to: '/docs/plugin/list/fusion',
// },
// {
// from: '/docs/guide/advance/antd',
// to: '/docs/plugin/list/antd',
// },
// {
// from: '/docs/guide/develop/plugin-dev',
// to: '/docs/plugin/develop/start',
// },
// {
// from: '/docs/guide/develop/plugin-build',
// to: '/docs/plugin/develop/build',
// },
// {
// from: '/docs/guide/develop/plugin-runtime',
// to: '/docs/plugin/develop/runtime',
// },
// {
// from: '/docs/guide/develop/plugin-list',
// to: '/docs/plugin/list/moment-locales',
// },
// {
// from: '/docs/guide/basic/api',
// to: '/docs/config/about',
// },
];
const redirectConfigPath = await createData(

View File

@ -12,15 +12,18 @@ module.exports = function getDocsFromDir(dir) {
return (matter(fs.readFileSync(filepath, 'utf-8')).data || {}).order || 100;
}
function isDocHide(filepath) {
return (matter(fs.readFileSync(filepath, 'utf-8')).data || {}).hide || false;
}
const docs = glob.sync('*.md', {
cwd: docsDir,
// ignore: 'README.md',
});
const result = docs
.map((doc) => {
return path.join(docsDir, doc);
})
.map((doc) => path.join(docsDir, doc))
.filter((doc) => !isDocHide(doc))
.sort((a, b) => {
const orderA = getMarkdownOrder(a);
const orderB = getMarkdownOrder(b);

View File

@ -5,27 +5,27 @@ import styles from './feature.module.css';
const data = [
{
title: '规范',
decs: '从目录组织规范到代码风格,集成了结合阿里大量项目实践的研发规范',
url: '/docs/guide/basic/structure',
},
{
title: 'Vite',
decs: '同时支持 Vite/Webpack 模式,提供极速的调试构建体验',
url: '/docs/guide/about',
decs: '从目录组织规范到代码风格,集成了结合阿里内部大量项目实践的研发规范',
url: '/docs/guide/basic/directory',
},
{
title: '开箱即用',
decs: '支持 TypeScript/CSS Modules/PostCSS/... 等工程能力,框架 API 具备良好的类型提示能力',
url: '/docs/guide/basic/build',
decs: '支持 Webpack5/TypeScript/CSS Modules/PostCSS 等构建能力,框架 API 具备良好的类型提示',
url: '/docs/guide/start',
},
{
title: '移动+桌面',
decs: '同时支持移动端和桌面端,提供更多针对性的优化',
url: '/docs/guide/about',
},
{
title: '最佳实践',
decs: '内置数据请求、状态管理、SSR、前端配置、日志输出等最佳实践',
url: '/docs/guide/basic/request',
decs: '内置数据请求、路由、状态、构建配置、日志输出等最佳实践',
url: '/docs/guide/basic/ssg',
},
{
title: '应用模式',
decs: '支持 SPA、MPA、微前端、Serverless 一体化等不同研发模式',
decs: '支持 SPA、服务端渲染 SSR 以及静态构建 SSG 等不同研发模式',
url: '/docs/guide/advanced/mpa',
},
{
@ -33,12 +33,17 @@ const data = [
decs: '通过插件将框架能力进行解耦,同时开发者也可以基于插件扩展框架能力',
url: '/docs/plugin/develop/start',
},
{
title: '领域方案',
decs: '提供丰富的领域方案,如微前端 ICESTARK、包开发 ICE PKG、Serverless 一体化方案等',
url: '/docs/plugin/develop/start',
},
];
function Feature() {
return (
<AreaWrapper
title={'基于 React 的应用研发框架 icejs'}
title={'基于 React 的应用研发框架 ICE 3'}
decs={'开箱即用的研发框架,内置工程配置、状态管理、数据请求、权限管理等最佳实践,让开发者可以更加专注于业务逻辑'}
contentStyle={styles.container}
isBlock
@ -49,7 +54,7 @@ function Feature() {
<h3>{item.title}</h3>
<span>{item.decs}</span>
<div style={{ flex: 1 }} />
<p>{'Documentation >'}</p>
<p>{'查看文档 >'}</p>
</div>
</a>
))}

View File

@ -8,9 +8,9 @@ function Splash() {
<header>
<div className={styles.splash}>
<div className={styles['title-container']}>
<h1 className={styles.title}>飞冰ICE 3.0</h1>
<h1 className={styles.title}>飞冰 (ICE)</h1>
<p className={styles.subtitle}>
基于 React 的应用研发框架
基于 React 的应用研发框架开箱即用同时支持移动端和桌面端
</p>
<div className={styles.githubStars}>
<iframe

9149
website/yarn.lock Normal file

File diff suppressed because it is too large Load Diff