mirror of https://github.com/alibaba/ice.git
Compare commits
5 Commits
6cfe917b96
...
274a38a7c7
| Author | SHA1 | Date |
|---|---|---|
|
|
274a38a7c7 | |
|
|
2742ac4678 | |
|
|
fcc25dc3fd | |
|
|
8e27933423 | |
|
|
6fb7336cb7 |
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@ice/runtime': patch
|
||||
---
|
||||
|
||||
fix: duplicate css
|
||||
|
|
@ -48,7 +48,9 @@ Please see our [CONTRIBUTING.md](/.github/CONTRIBUTING.md)
|
|||
|
||||
Contributors can contact us to join the Contributor Group.
|
||||
|
||||
<a href="https://github.com/alibaba/ice/graphs/contributors"><img src="https://alibaba.github.io/ice/ice.png" /></a>
|
||||
<a href="https://openomy.com/alibaba/ice" target="_blank" style="display: block; width: 100%;" align="center">
|
||||
<img src="https://openomy.com/svg?repo=alibaba/ice&chart=bubble&latestMonth=6" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
|
||||
</a>
|
||||
|
||||
## Community
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
},
|
||||
"peerDependencies": {
|
||||
"@ice/app": "^3.6.4",
|
||||
"@ice/runtime": "^1.5.6"
|
||||
"@ice/runtime": "^1.5.7"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@ice/app": "^3.6.4",
|
||||
"@ice/runtime": "^1.5.6",
|
||||
"@ice/runtime": "^1.5.7",
|
||||
"webpack": "^5.88.0"
|
||||
},
|
||||
"repository": {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
# @ice/runtime
|
||||
|
||||
## 1.5.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 4ff29969c: feat: add SuspenseWrappers to Runtime
|
||||
|
||||
## 1.5.6
|
||||
|
||||
### Patch Changes
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ice/runtime",
|
||||
"version": "1.5.6",
|
||||
"version": "1.5.7",
|
||||
"description": "Runtime module for ice.js",
|
||||
"type": "module",
|
||||
"types": "./esm/index.d.ts",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react';
|
||||
import type { WindowContext, RouteMatch, AssetsManifest } from './types.js';
|
||||
import { useAppContext, useAppData } from './AppContext.js';
|
||||
import { getMeta, getTitle, getLinks, getScripts } from './routesConfig.js';
|
||||
import { getLinks, getMeta, getScripts, getTitle } from './routesConfig.js';
|
||||
import type { AssetsManifest, RouteMatch, WindowContext } from './types.js';
|
||||
import getCurrentRoutePath from './utils/getCurrentRoutePath.js';
|
||||
|
||||
interface DocumentContext {
|
||||
|
|
@ -81,7 +81,15 @@ export const Links: LinksType = (props: LinksProps) => {
|
|||
const routeLinks = getLinks(matches, loaderData);
|
||||
const pageAssets = getPageAssets(matches, assetsManifest);
|
||||
const entryAssets = getEntryAssets(assetsManifest);
|
||||
const styles = entryAssets.concat(pageAssets).filter(path => path.indexOf('.css') > -1);
|
||||
let styles = entryAssets.concat(pageAssets).filter(path => path.indexOf('.css') > -1);
|
||||
|
||||
// Unique styles for duplicate CSS files.
|
||||
const cssSet = {};
|
||||
styles = styles.filter((style) => {
|
||||
if (cssSet[style]) return false;
|
||||
cssSet[style] = true;
|
||||
return true;
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -135,7 +135,8 @@ export function withSuspense(Component) {
|
|||
return (props: SuspenseProps) => {
|
||||
const { fallback, id, ...componentProps } = props;
|
||||
|
||||
const [suspenseState, updateSuspenseData] = React.useState({
|
||||
|
||||
const [suspenseState, updateSuspenseData] = React.useState<SuspenseState>({
|
||||
id: id,
|
||||
data: null,
|
||||
done: false,
|
||||
|
|
@ -156,24 +157,47 @@ export function withSuspense(Component) {
|
|||
updateSuspenseData(newState);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Suspense fallback={fallback || null}>
|
||||
|
||||
// Get SuspenseWrappers from app context
|
||||
const { SuspenseWrappers = [] } = useAppContext();
|
||||
|
||||
// Compose SuspenseWrappers
|
||||
const composeSuspenseWrappers = React.useCallback(
|
||||
(children: React.ReactNode) => {
|
||||
if (!SuspenseWrappers.length) return children;
|
||||
|
||||
return SuspenseWrappers.reduce((WrappedComponent, wrapperConfig) => {
|
||||
const { Wrapper } = wrapperConfig;
|
||||
return <Wrapper id={id}>{WrappedComponent}</Wrapper>;
|
||||
}, children);
|
||||
},
|
||||
[SuspenseWrappers, id],
|
||||
);
|
||||
|
||||
const wrappedComponent = (
|
||||
<>
|
||||
<InlineScript
|
||||
id={`suspense-parse-start-${id}`}
|
||||
script={`(${DISPATCH_SUSPENSE_EVENT_STRING})('ice-suspense-parse-start','${id}');`}
|
||||
/>
|
||||
<SuspenseContext.Provider value={suspenseState}>
|
||||
<Component {...componentProps} />
|
||||
<InlineScript
|
||||
id={`suspense-parse-data-${id}`}
|
||||
script={`(${DISPATCH_SUSPENSE_EVENT_STRING})('ice-suspense-parse-data','${id}');`}
|
||||
/>
|
||||
<Data id={id} />
|
||||
</SuspenseContext.Provider>
|
||||
<Component {...componentProps} />
|
||||
<InlineScript
|
||||
id={`suspense-parse-data-${id}`}
|
||||
script={`(${DISPATCH_SUSPENSE_EVENT_STRING})('ice-suspense-parse-data','${id}');`}
|
||||
/>
|
||||
<Data id={id} />
|
||||
<InlineScript
|
||||
id={`suspense-parse-end-${id}`}
|
||||
script={`(${DISPATCH_SUSPENSE_EVENT_STRING})('ice-suspense-parse-end','${id}');`}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Suspense fallback={fallback || null}>
|
||||
<SuspenseContext.Provider value={suspenseState}>
|
||||
{composeSuspenseWrappers(wrappedComponent)}
|
||||
</SuspenseContext.Provider>
|
||||
</React.Suspense>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ import type {
|
|||
SetAppRouter,
|
||||
AddProvider,
|
||||
AddWrapper,
|
||||
AddSuspenseWrapper,
|
||||
RouteWrapperConfig,
|
||||
SuspenseWrapperConfig,
|
||||
SetRender,
|
||||
AppRouterProps,
|
||||
ComponentWithChildren,
|
||||
|
|
@ -33,6 +35,8 @@ class Runtime {
|
|||
|
||||
private RouteWrappers: RouteWrapperConfig[];
|
||||
|
||||
private SuspenseWrappers: SuspenseWrapperConfig[];
|
||||
|
||||
private render: Renderer;
|
||||
|
||||
private responseHandlers: ResponseHandler[];
|
||||
|
|
@ -46,6 +50,7 @@ class Runtime {
|
|||
return root;
|
||||
};
|
||||
this.RouteWrappers = [];
|
||||
this.SuspenseWrappers = [];
|
||||
this.runtimeOptions = runtimeOptions;
|
||||
this.responseHandlers = [];
|
||||
this.getAppRouter = this.getAppRouter.bind(this);
|
||||
|
|
@ -55,6 +60,7 @@ class Runtime {
|
|||
return {
|
||||
...this.appContext,
|
||||
RouteWrappers: this.RouteWrappers,
|
||||
SuspenseWrappers: this.SuspenseWrappers,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -72,6 +78,8 @@ class Runtime {
|
|||
|
||||
public getWrappers = () => this.RouteWrappers;
|
||||
|
||||
public getSuspenseWrappers = () => this.SuspenseWrappers;
|
||||
|
||||
public loadModule(module: RuntimePlugin | StaticRuntimePlugin | CommonJsRuntime) {
|
||||
let runtimeAPI: RuntimeAPI = {
|
||||
addProvider: this.addProvider,
|
||||
|
|
@ -80,6 +88,7 @@ class Runtime {
|
|||
getAppRouter: this.getAppRouter,
|
||||
setRender: this.setRender,
|
||||
addWrapper: this.addWrapper,
|
||||
addSuspenseWrapper: this.addSuspenseWrapper,
|
||||
appContext: this.appContext,
|
||||
setAppRouter: this.setAppRouter,
|
||||
useData: process.env.ICE_CORE_ROUTER === 'true' ? useData : useSingleRouterData,
|
||||
|
|
@ -122,6 +131,12 @@ class Runtime {
|
|||
});
|
||||
};
|
||||
|
||||
private addSuspenseWrapper: AddSuspenseWrapper = (Wrapper) => {
|
||||
this.SuspenseWrappers.push({
|
||||
Wrapper,
|
||||
});
|
||||
};
|
||||
|
||||
public setAppRouter: SetAppRouter = (AppRouter) => {
|
||||
this.AppRouter = AppRouter;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ export interface AppContext {
|
|||
loaderData?: LoadersData;
|
||||
routeModules?: RouteModules;
|
||||
RouteWrappers?: RouteWrapperConfig[];
|
||||
SuspenseWrappers?: SuspenseWrapperConfig[];
|
||||
routePath?: string;
|
||||
matches?: RouteMatch[];
|
||||
routes?: RouteItem[];
|
||||
|
|
@ -187,16 +188,26 @@ export interface RouteWrapperConfig {
|
|||
|
||||
export type AppProvider = ComponentWithChildren<any>;
|
||||
export type RouteWrapper = ComponentType<any>;
|
||||
|
||||
export type SuspenseWrapper = ComponentWithChildren<{
|
||||
id: string;
|
||||
}>;
|
||||
|
||||
export type ResponseHandler = (
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
) => any | Promise<any>;
|
||||
|
||||
export interface SuspenseWrapperConfig {
|
||||
Wrapper: SuspenseWrapper;
|
||||
}
|
||||
|
||||
export type SetAppRouter = <T>(AppRouter: ComponentType<T>) => void;
|
||||
export type GetAppRouter = () => AppProvider;
|
||||
export type AddProvider = (Provider: AppProvider) => void;
|
||||
export type SetRender = (render: Renderer) => void;
|
||||
export type AddWrapper = (wrapper: RouteWrapper, forLayout?: boolean) => void;
|
||||
export type AddSuspenseWrapper = (wrapper: SuspenseWrapper) => void;
|
||||
export type AddResponseHandler = (handler: ResponseHandler) => void;
|
||||
export type GetResponseHandlers = () => ResponseHandler[];
|
||||
|
||||
|
|
@ -227,6 +238,7 @@ export interface RuntimeAPI {
|
|||
getResponseHandlers: GetResponseHandlers;
|
||||
setRender: SetRender;
|
||||
addWrapper: AddWrapper;
|
||||
addSuspenseWrapper: AddSuspenseWrapper;
|
||||
appContext: AppContext;
|
||||
useData: UseData;
|
||||
useConfig: UseConfig;
|
||||
|
|
|
|||
|
|
@ -2160,7 +2160,7 @@ importers:
|
|||
specifier: ^3.6.4
|
||||
version: link:../ice
|
||||
'@ice/runtime':
|
||||
specifier: ^1.5.6
|
||||
specifier: ^1.5.7
|
||||
version: link:../runtime
|
||||
webpack:
|
||||
specifier: ^5.88.0
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ order: 0902
|
|||
|
||||
### 依赖修改
|
||||
|
||||
#### 框架依赖
|
||||
|
||||
```diff
|
||||
{
|
||||
"devDependencies": {
|
||||
|
|
@ -22,40 +24,146 @@ order: 0902
|
|||
}
|
||||
```
|
||||
|
||||
对应插件能力:
|
||||
- @ali/build-plugin-ice-def -> @ali/ice-plugin-def
|
||||
- build-plugin-moment-locales -> @ice/plugin-moment-locales
|
||||
- build-plugin-fusion -> @ice/plugin-fusion (多主题能力暂不支持)
|
||||
- build-plugin-antd -> @ice/plugin-antd
|
||||
- build-plugin-css-assets-local -> @ice/plugin-css-assets-local
|
||||
- build-plugin-jsx-plus -> @ice/plugin-jsx-plus [文档](../advanced/jsx-plus.md)
|
||||
- build-plugin-keep-alive 不再支持,有 ice.js 3.0 的 [keep alive 方案](../advanced/keep-alive.md)替代
|
||||
|
||||
插件使用方式变更为函数调用:
|
||||
#### 插件依赖
|
||||
##### @ali/build-plugin-ice-def
|
||||
插件替换为 `@ali/ice-plugin-def`,使用版本 1.2.4+,使用方式:
|
||||
|
||||
```ts title="ice.config.mts"
|
||||
import { defineConfig } from '@ice/app';
|
||||
import jsxPlus from '@ice/plugin-jsx-plus';
|
||||
import def from '@ali/ice-plugin-def';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
plugins: [
|
||||
jsxPlus(),
|
||||
def(),
|
||||
],
|
||||
}));
|
||||
```
|
||||
|
||||
##### build-plugin-moment-locales
|
||||
|
||||
插件替换为 `@ice/plugin-moment-locales`,使用版本 1.0.2+,使用方式:
|
||||
|
||||
```ts title="ice.config.mts"
|
||||
import { defineConfig } from '@ice/app';
|
||||
import moment from '@ice/plugin-moment-locales';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
plugins: [
|
||||
moment({
|
||||
locales: ['zh-CN'],
|
||||
}),
|
||||
],
|
||||
}));
|
||||
```
|
||||
|
||||
##### build-plugin-fusion
|
||||
替换为 `@ice/plugin-fusion`,使用版本 1.1.0+,使用方式:
|
||||
|
||||
```ts title="ice.config.mts"
|
||||
import { defineConfig } from '@ice/app';
|
||||
import fusion from '@ice/plugin-fusion';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
plugins: [fusion({
|
||||
importStyle: true,
|
||||
themePackage: '@alifd/theme-design-pro',
|
||||
theme: {
|
||||
'primary-color': '#fff',
|
||||
},
|
||||
})],
|
||||
}));
|
||||
```
|
||||
|
||||
fusion 插件文档:[链接](../plugins/plugin-fusion.md)
|
||||
|
||||
> ice3 下的 fusion 插件不再支持多主题能力,仅支持配置 importStyle、themePackage 和 theme 三个配置项
|
||||
|
||||
##### build-plugin-antd
|
||||
|
||||
插件替换为 `@ice/plugin-antd`,使用版本 1.0.2+,使用方式:
|
||||
|
||||
```ts title="ice.config.mts"
|
||||
import { defineConfig } from '@ice/app';
|
||||
import antd from '@ice/plugin-antd';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
plugins: [antd({
|
||||
dark: true,
|
||||
compact: true,
|
||||
theme: {
|
||||
'primary-color': '#fd8',
|
||||
},
|
||||
})],
|
||||
}));
|
||||
```
|
||||
|
||||
antd 插件文档:[链接](../advanced/antd.md)
|
||||
|
||||
##### build-plugin-css-assets-local
|
||||
|
||||
插件替换为 `@ice/plugin-css-assets-local`,使用版本 1.0.2+,使用方式:
|
||||
|
||||
```ts title="ice.config.mts"
|
||||
import { defineConfig } from '@ice/app';
|
||||
import cssAssetsLocal from '@ice/plugin-css-assets-local';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
plugins: [
|
||||
cssAssetsLocal(),
|
||||
],
|
||||
}));
|
||||
```
|
||||
|
||||
css-assets-local 插件文档:[链接](../advanced/css-assets-local.md)
|
||||
|
||||
##### build-plugin-jsx-plus
|
||||
|
||||
插件替换为 `@ice/plugin-jsx-plus`,使用版本 1.0.4+,使用方式:
|
||||
|
||||
```ts title="ice.config.mts"
|
||||
import { defineConfig } from '@ice/app';
|
||||
import jsxplus from '@ice/plugin-jsx-plus';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
plugins: [
|
||||
jsxplus({
|
||||
// options
|
||||
}),
|
||||
],
|
||||
}));
|
||||
```
|
||||
|
||||
jsx-plus 插件文档:[链接](../advanced/jsx-plus.md)
|
||||
|
||||
##### build-plugin-keep-alive
|
||||
|
||||
ice 3 不再支持插件形式的 keep-alive 方案。由框架内置提供的 `<KeepAliveOutlet />` 组件替代。
|
||||
keep-alive 插件文档:[链接](../advanced/keep-alive.md)
|
||||
|
||||
> 完成依赖升级后推荐重新安装依赖,即执行 npm update
|
||||
|
||||
### 工程配置文件升级
|
||||
|
||||
为了获取更好的类型提示,ice 新版本中推荐使用 ts 文件进行配置,即在项目目录下新增 `ice.config.mts` 文件,原 json 中的能力支持情况如下:
|
||||
|
||||
#### 命令行参数
|
||||
|
||||
| ice 2.x | ice 3.0 | 备注 |
|
||||
| ---- | ---- | ---- |
|
||||
| --port | --port | - |
|
||||
| --host | --host | - |
|
||||
| --config | --config | - |
|
||||
| --disable-open | --no-open | - |
|
||||
| --disable-mock | --no-mock | - |
|
||||
| --https | --https | - |
|
||||
| --analyzer | --analyzer | - |
|
||||
| --disable-assets | ❌ | 不常用通过环境变量控制日志输出详细程度 |
|
||||
| --disable-reload | ❌ | 配置禁止 fastRefresh |
|
||||
|
||||
#### 配置项
|
||||
|
||||
| ice 2.x | ice 3.0 | 备注 |
|
||||
| ---- | ---- | ---- |
|
||||
| --port | ✅ | - |
|
||||
| --host | ✅ | - |
|
||||
| --config | ✅ | - |
|
||||
| --disable-open | ✅ | - |
|
||||
| plugins | ✅ | - |
|
||||
| alias | ✅ | - |
|
||||
| publicPath | ✅ | - |
|
||||
|
|
@ -67,9 +175,6 @@ export default defineConfig(() => ({
|
|||
| proxy | ✅ | - |
|
||||
| define | ✅ | - |
|
||||
| ssr | ✅ | - |
|
||||
| --disable-mock | ✅ | - |
|
||||
| --https | ✅ | - |
|
||||
| --analyzer | ✅ | - |
|
||||
| dropLogLevel | ✅ | - |
|
||||
| minify | ✅ | 简化配置(true/false) |
|
||||
| compileDependencies | ✅ | 配合现有的 compileIncludes 能力 |
|
||||
|
|
@ -78,8 +183,6 @@ export default defineConfig(() => ({
|
|||
| postcssOptions / postcssrc | ✅ | - |
|
||||
| polyfill | ✅ | 需要主动开启 |
|
||||
| remoteRuntime | ❌ | - |
|
||||
| --disable-assets | ❌ | 不常用通过环境变量控制日志输出详细程度 |
|
||||
| --disable-reload | ❌ | 配置禁止 fastRefresh |
|
||||
| terser | ❌ | 内置方案 |
|
||||
| outputAssetsPath | ❌ | 后续输出最佳目录实践 |
|
||||
| devServer | ❌ | 不支持全量配置 devServer,按需开启 server 相关能力 |
|
||||
|
|
@ -93,7 +196,7 @@ export default defineConfig(() => ({
|
|||
| swc | ❌ | - |
|
||||
| store / auth / request / pwa / router | ❌ | 通过定制的插件支持 |
|
||||
| disableRuntime | ❌ | - |
|
||||
| babelPlugins / babelPresets / webpackPlugins / webpackLoaders | ❌ | 不推荐直接配置 |
|
||||
| babelPlugins / babelPresets / webpackPlugins / webpackLoaders | ❌ | 不推荐直接配置,如果有定制需求通过 webpack 配置进行迁移 |
|
||||
|
||||
ice.js 3 新版本中不再支持 vite 模式,并且 webpack 相关的快捷配置也不再支持。我们将会将内置的逻辑做到最优。如果存在 webpack 定制需求,可以参考如下自定义方式定制:
|
||||
|
||||
|
|
@ -113,11 +216,11 @@ export default defineConfig(() => ({
|
|||
// 修改内置的 webpack 规则,借助官方工具可以更便捷的修改
|
||||
// 修改 css 样式规则下的 postcss-loader 配置项
|
||||
return modifyLoader(webpackConfig, {
|
||||
rule: '.css',
|
||||
loader: 'postcss-loader',
|
||||
options: (originOptions) => ({}),
|
||||
});
|
||||
}
|
||||
rule: '.css',
|
||||
loader: 'postcss-loader',
|
||||
options: (originOptions) => ({}),
|
||||
});
|
||||
},
|
||||
}));
|
||||
```
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue