umijs / umi-next Goto Github PK
View Code? Open in Web Editor NEWThe next version of umi. (under development)
Home Page: https://next.umijs.org/
License: MIT License
The next version of umi. (under development)
Home Page: https://next.umijs.org/
License: MIT License
https://github.com/umijs/fabric 的版本 2.x 里不敢动了,落后社区很多,3.x 里可以一起升上来。(可能需要提供一个2.x的规则兼容模式开关方便升级)
看了umi的新版本以及各种相关的插件库,例如layout,access,model,这些,很是激动,感觉新版本使用起来会是全新的体验,想问下大概什么时候能到rc阶段,(有个readme也好啊)
(Invoke-WebRequest 'https://get.pnpm.io/v6.14.js' -UseBasicParsing).Content | node - add --global pnpm
[stdin]:8258
const colorCode = " < 8 ? c : "8;5;" + c);
^
SyntaxError: Unexpected number
at new Script (node:vm:100:7)
at createScript (node:vm:257:10)
at Object.runInThisContext (node:vm:305:10)
at node:internal/process/execution:81:19
at [stdin]-wrapper:6:22
at evalScript (node:internal/process/execution:80:60)
at node:internal/main/eval_stdin:29:5
at Socket.<anonymous> (node:internal/process/execution:212:5)
at Socket.emit (node:events:402:35)
at endReadableNT (node:internal/streams/readable:1343:12)
相关 issues:
PowerShell/PowerShell#16175
pnpm/pnpm#3770
electron环境中会遇到如下两个问题:
第一个问题将config/config.js改成config.ts 就好了,无法界定是否是bug,建议支持js扩展名
第二个问题是因为electron的特殊环境吧,希望能生成到最外层的node_modules内
主要是 Umi 区块使用,目前是在命令行 和 UI ,
@umijs/block-sdk
)@umijs/plugin-block
)、UI 插件(@umijs/plugin-ui-blocks
)分别使用 SDK因为后面主推 UI 形式,命令行只是补充,以上包全放在 umijs/ui 仓库中进行维护
- packages
- @umijs/plugin-blocks
- @umijs/block-sdk
- @umijs/plugin-ui-blocks
使用方式:
// @umijs/plugin-blocks
import { ... } from '@umijs/block-sdk';
export default (api) => {
}
// @umijs/plugin-ui-blocks
import { ... } from '@umijs/block-sdk';
export default (api) => {
}
single-spa改写了window.history.pushState方法,在每次pushState的时候会主动dispatch一个popState事件,被history checkDomListeners捕获,这样通过history.listen注册的监听函数都会被执行两次,如
connected-react-router handleLocationChange方法
尝试自己写插件解决无效
# 插件代码
const fs = require('fs')
const path = require('path')
export default function(api, options) {
api.modifyEntryHistory(memo => {
const content = fs.readFileSync(path.resolve(__dirname, 'historyPatch.js'), {
encoding: 'utf8'
})
return content
})
}
# historyPatch.js
/**
* fix history & single-spa bug, remove checkDOMListeners
* https://github.com/ReactTraining/history/blob/3f69f9e07b0a739419704cffc3b3563133281548/modules/createBrowserHistory.js#L267
* https://github.com/CanopyTax/single-spa/blob/92efd71ace891d59eb1005d09dd5cf532fbba154/src/navigation/navigation-events.js#L100
*/
require('@zstack/history').createBrowserHistory({ basename: window.routerBase })
运行后.umi/history生成如下
// create history
const history
/**
* fix history & single-spa bug, remove checkDOMListeners
* https://github.com/ReactTraining/history/blob/3f69f9e07b0a739419704cffc3b3563133281548/modules/createBrowserHistory.js#L267
* https://github.com/CanopyTax/single-spa/blob/92efd71ace891d59eb1005d09dd5cf532fbba154/src/navigation/navigation-events.js#L100
*/ = require('@zstack/history').createBrowserHistory({
basename: window.routerBase,
});
export default history;
但实际运行时,检查sourcemap,系统仍然选用umi-history,怀疑和alias有关
执行npx umi inspect > dev.log
有如下配置
history: '/Users/shengbeiniao/zstack/projects/cmp/node_modules/umi-history',
重写alias,无法覆盖
补充到 https://github.com/umijs/umi-next/wiki/umi-2.0-plugin-migrate-to-umi-3.0
1、api.config
注册阶段为 null
,如果需要在注册阶段获取,使用 api.service.userConfig
export default (api) => {
- console.log(api.config) // => { ... }
+ console.log(api.config) // => null
}
2、api 目前缺失
api.getRoutes
、api.getRouteComponents
api.relativeToTmp
api.runCommand
3、api
注册方法异步化、对象化
// before
export default (api) => {
api.registerCommand('build', {}, (args) => {
api.applyPlugins('addBar', {
...
})
})
}
// after
export default (api) => {
api.registerCommand({
name: 'build',
fn: async ({ args }) => {
await api.applyPlugins({
key: 'addBar',
// 必填
type: api.ApplyPluginsType.add,
...
})
}
})
}
api.winPath
、api.debug
等工具类 API 从 api.utils
中引入// before
export default (api) => {
const { _, log, debug, winPath } = api;
}
// after
export default (api) => {
const {
logger,
utils: {
lodash: _,
winPath,
createDebug: debug
}
} = api;
}
api.addEntryCode
入参由 string
改成 () ⇒ string
// before
export default (api) => {
api.addEntryCode(`console.log('foo');`)
}
// after
export default (api) => {
api.addEntryCode(() => `console.log('foo');`)
}
api.registerPlugin
=> api.registerPlugins
, api.addHTMLStyles
、api.addHTMLHeadScripts
、api.addHTMLScripts
、api.addHTMLMetas
、api.addHTMLLinks
等配置变更
uglifyJSOptions
配置废弃,改用 terserOptions
cssLoaderOptions
⇒ cssLoader
lessLoaderOptions
⇒ lessLoader
,css-loader 升级至 3.x
,配置有 BreakChange,详见output.publicPath
变更.env
环境变量读取缺失treeShaking
默认开启,无须配置缺少
chainWebpack
headScripts
、links
、scripts
,计划配置内置,平铺 #32代码层
history 导出
- import history from '@tmp/history';
+ import { history } from 'umi';
umi/prompt
缺少(次优先级)
运行时 app.ts
:
- export const patchRoutes = (routes) => {}
+ export const patchRoutes = ({ routes }) => {}
比如有A、B两个系统
现将A打包成npm包,在B中npm install A,然后路由地址指向了A(node_modules/A)
这种的能正常解析出来嘛?model能正常工作嘛?
这是因为什么限制的?貌似这种写法如果使用方也有同一个模块,是会导致引用的不是同一个?
How to reproduce.
$ cd examples/ant-design-pro
$ vi config/config.ts
Modify mfsu: {} to mfsu: { esbuild: true }
$ pnpm dev
Error.
Solution(May be).
为了 umi 底层质量更高,大家可以一起来补单测,可以从这里挑选自己感兴趣的模块,回复到该 issue,避免重复。
测试用例与需要测试的文件写在一起,例如:
- src
- index.test.ts
- index.ts
- bar
- bar.ts
- bar.test.ts
用例尽量以 test
平铺展开,避免嵌套式写法。
test('bar', () => {
})
test('foo', () => {
})
有关插件的方法见 《插件测试》
For improving the quality of the umi repo, you can make up the order test together. You can choose the modules you are interested in from here, reply to this issue to avoid duplication.
Test cases are written with the files to be tested, for example:
-src
-index.test.ts
-index.ts
-bar
-bar.ts
-bar.test.ts
Use cases should be expanded as test
as possible to avoid nested writing.
test ('bar', () => {
})
test ('foo', () => {
})
For the method of plugins, please refer to Plugin Testing
找不到别名'@',在 umi 中设置的别名为
'@': paths.absSrcPath,
'@@': paths.absTmpPath,
let absSrcPath = cwd;
if (isDirectoryAndExist(join(cwd, 'src'))) {
absSrcPath = join(cwd, 'src');
}
const tmpDir = ['.umi', env !== 'development' && env]
.filter(Boolean)
.join('-');
const absTmpPath = join(absSrcPath, tmpDir)
而上面的 cwd 是在执行测试的时候,动态传入的。
但是在 jest 中 使用 moduleNameMapper
设置别名,只能提前设置,没办法动态变更。
导致错误如下:
Cannot find module '@/pages/index.js' from 'routes.ts'
However, Jest was able to find:
'core/routes.ts'
You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'jsx', 'ts', 'tsx', 'json'].
See https://jestjs.io/docs/en/configuration#modulefileextensions-array-string
6 | "path": "/",
7 | "exact": true,
> 8 | "component": require('@/pages/index.js').default
| ^
9 | }
10 | ];
11 |
https://github.com/alitajs/umi3-plugin-test-question
yarn
tarn test
Steps to reproduce the behavior: 1. 2.
Expected behavior 1. 2.
Umi@3 repo 日志体系
debug
= profile
< info
< warn
= duplicate
< error
,默认输出 info
及以上。LOGGER=debug
core/Logger
@umijs/runtime
可使用// e.g. @umijs/renderer-react
import { Logger, UmiError } from '@umijs/core';
const logger = new Logger('@umijs/renderer-react')
export default () => {
logger.info('hello');
// ...
logger.error(new UmiError({
code: 'ERR_CORE_PLUGIN_RESOLVE_FAILED',
message: `Plugin ${chalk.underline.cyan(path)} can't be resolved`,
}));
logger.error(new Error('error without umi error'));
const presets = [ 'a', 'b', 'c' ];
logger.debug('presets', presets);
// 需要固定格式还是? or logger.duplicate('webpack', 'webpack5', 'https://docHelp')
logger.duplicate('webpack option is deprecated. Use webpack5 instead.');
// 耗时记录
logger.profile('test');
setTimeout(() => {
logger.profile('test');
})
}
umi 主仓库使用:
执行
# without error
$ umi dev
➤ [INFO] hello
➤ [DUPLICATE] webpack option is deprecated. Use webpack5 instead.
# with error
$ umi dev
➤ [INFO] hello
➤ [ERROR] Plugin * can't be resolved.
> Help Doc Markdown
# with debug
$ DEBUG=@umijs/renderer-react* umi dev
➤ [INFO] hello
➤ [DEBUG] @umijs/renderer-react: presets ['a', 'b', 'c']
➤ [PROFILE] @umijs/renderer-react:test duration=500ms
插件中使用:
export default (api) => {
api.logger.debug('aaa');
// => [DEBUG] ${key}: aaa
}
import { Logger } from '@umijs/core';
import urllib from 'urllib';
export default class ExtendLogger extends Logger {
public warn(...args) {
const msg = this.format(() => this.chalk.green('[WARN]'), ...args);
this.print(msg);
// log error
urllib.request('url?msg=' + msg);
}
}
扩展使用
import ExtendLogger from './ExtendLogger';
const logger = new ExtendLogger();
logger.warn('aaa');
// => [WARN]
react-intl 在 3.x 已经支持了通过 hooks 来使用,我们只需要简单地封装一层即可。
import {
RawIntlProvider,
createIntl,
createIntlCache,
useIntl
} from "react-intl";
import zhCN from "../locales/zh-CN";
const zhCNIntl = createIntl({
locale: "zh-CN",
messages: zhCN,
onError: e => console.log(e)
});
const alertXXX = ()=>zhCNIntl.formatMessage("xxx")
const Component: React.FC<{}> = props => {
const intl = useIntl();
return (
<div>
{intl.formatMessage({ id: "getall", defaultMessage: "获取全部" })}{" "}
<FormattedMessage id="app.getall" defaultMessage="获取全部" />
</div>
);
};
export default (
<RawIntlProvider value={zhCNIntl}>
<Component />
</RawIntlProvider>
);
通过 createIntl 生成的 intl 对象,即使在 node 中也可以使用,无需上下文。
// 如果需要支持 ie11
if (!Intl.PluralRules) {
require('@formatjs/intl-pluralrules/polyfill');
require('@formatjs/intl-pluralrules/dist/locale-data/de'); // Add locale data for de
}
if (!Intl.RelativeTimeFormat) {
require('@formatjs/intl-relativetimeformat/polyfill');
require('@formatjs/intl-relativetimeformat/dist/locale-data/de'); // Add locale data for de
}
import {
useIntl,
getIntl,
setIntl,
getLocale,
setLocale,
FormattedDate,
FormattedTime,
FormattedRelativeTime,
FormattedNumber,
FormattedPlural,
FormattedMessage,
FormattedHTMLMessage
} from "umi/locale";
// getIntl 可以获得当前使用的 intl
// 如果有 locale 返回对应 local 的 IntlShape
// 方便在没有react 上下文的场景中使用
const getIntl = (locale:string)=> IntlShape
// 设置某个语言的 IntlShape
// 通过远程 或者自定义的方式添加
const setIntl = (locale:string,intl:IntlShape)=>void;
// 获取当前的语言环境
const getLocale = ()=>string;
// 获取所有支持的语言环境
const getAllLocales = ()=>string;
// 设置当前的语言环境
const setLocale = (locale:string,reload:boolean)=>void;
//为了兼容 旧版本可以提供一个 formatMessgae
const formatMessgae = getIntl(getLocale()).formatMessgae
npx create-umi@next
Need to install the following packages:
create-umi@next
Ok to proceed? (y)
√ Pick Npm Client » yarn
√ Pick Bpm Registry » npm
...
Create failed, spawnSync yarn ENOENT
Error: spawnSync yarn ENOENT
at Object.spawnSync (node:internal/child_process:1083:20)
at spawnSync (node:child_process:812:24)
at installWithNpmClient (...\npm-cache_npx\3f4d706d21e95758\node_modules@umijs\utils\dist\npmClient.js:35:17)
at ...\npm-cache_npx\3f4d706d21e95758\node_modules\create-umi\dist\index.js:104:42
at Generator.next ()
at fulfilled (...\npm-cache_npx\3f4d706d21e95758\node_modules\create-umi\dist\index.js:5:58)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
errno: -4058,
code: 'ENOENT',
syscall: 'spawnSync yarn',
path: 'yarn',
spawnargs: [ '' ]
}
在umi-next\packages\babel-preset-umi\src中31行 corejs: '3', 是否需要读一下core-js包的版本使其一致,
在babel官网中提到: It is recommended to specify the minor version otherwise "3" will be interpreted as "3.0" which may not include polyfills for the latest features.
我在本地按照官方的配置core-js:'3',测试了一下3.0以后的版本如Array.prototype.at没有被babel
father 3 介绍 + 任务集合,欢迎评论参与共建,做之前先认领(编辑主贴,加 @自己 ),避免冲突。
做组件库源码打包/编译
做框架/工具的依赖打包,类似 Umi 4
做项目的前置检查,给出研发建议或者错误
基于 umi g 做原子工程化能力的生成
➜ example-webpack-mfsu git:(master) ✗ pnpm dev
@ dev /Users/xxx/self-project/example-webpack-mfsu
webpack-dev-server
[webpack-dev-server] Project is running at:
[webpack-dev-server] Loopback: http://localhost:8082/
[webpack-dev-server] On Your Network (IPv4): http://11.37.60.200:8082/
[webpack-dev-server] On Your Network (IPv6): http://[fe80::1]:8082/
[webpack-dev-server] Content not from webpack is served from '/Users/xxx/self-project/example-webpack-mfsu/public' directory
ERROR in ./src/App.tsx 1:0-29
Module not found: Error: Can't resolve 'mf/react' in '/Users/xxx/self-project/example-webpack-mfsu/src'
@ ./src/main.tsx 3:0-24 4:50-53
ERROR in ./src/antd.tsx 1:0-31
Module not found: Error: Can't resolve 'mf/antd/dist/antd.css' in '/Users/xxx/self-project/example-webpack-mfsu/src'
@ ./src/App.tsx 2:0-26 5:88-92
@ ./src/main.tsx 3:0-24 4:50-53
ERROR in ./src/antd.tsx 2:0-39
Module not found: Error: Can't resolve 'mf/antd/es/button' in '/Users/xxx/self-project/example-webpack-mfsu/src'
@ ./src/App.tsx 2:0-26 5:88-92
@ ./src/main.tsx 3:0-24 4:50-53
ERROR in ./src/antd.tsx 3:0-48
Module not found: Error: Can't resolve 'mf/antd/es/date-picker' in '/Users/xxx/self-project/example-webpack-mfsu/src'
@ ./src/App.tsx 2:0-26 5:88-92
@ ./src/main.tsx 3:0-24 4:50-53
ERROR in ./src/antd.tsx 4:0-37
Module not found: Error: Can't resolve 'mf/antd/es/input' in '/Users/xxx/self-project/example-webpack-mfsu/src'
@ ./src/App.tsx 2:0-26 5:88-92
@ ./src/main.tsx 3:0-24 4:50-53
ERROR in ./src/antd.tsx 5:0-29
Module not found: Error: Can't resolve 'mf/react' in '/Users/xxx/self-project/example-webpack-mfsu/src'
@ ./src/App.tsx 2:0-26 5:88-92
@ ./src/main.tsx 3:0-24 4:50-53
ERROR in ./src/framer-motion.tsx 13:0-51
Module not found: Error: Can't resolve 'mf/framer-motion' in '/Users/xxx/self-project/example-webpack-mfsu/src'
@ ./src/App.tsx 3:0-43 5:134-146
@ ./src/main.tsx 3:0-24 4:50-53
ERROR in ./src/framer-motion.tsx 14:0-29
Module not found: Error: Can't resolve 'mf/react' in '/Users/xxx/self-project/example-webpack-mfsu/src'
@ ./src/App.tsx 3:0-43 5:134-146
@ ./src/main.tsx 3:0-24 4:50-53
ERROR in ./src/main.tsx 1:0-29
Module not found: Error: Can't resolve 'mf/react' in '/Users/xxx/self-project/example-webpack-mfsu/src'
ERROR in ./src/main.tsx 2:0-36
Module not found: Error: Can't resolve 'mf/react-dom' in '/Users/xxx/self-project/example-webpack-mfsu/src'
10 errors have detailed information that is not shown.
Use 'stats.errorDetails: true' resp. '--stats-error-details' to show it.
webpack 5.64.4 compiled with 10 errors in 1045 ms
包含 eslint 、prettier、stylelint 配置的合集包。
规范 Umi & Bigfish 项目的代码规范及统一团队代码风格。
eslint
、prettier
、stylelint
实例,Umi 中使用到的全通过来消费:import { eslint, prettier, stylelint } from '@umijs/lint'
好在:
不好在:
eslint-plugin-*
包,即使没有用到 TypeScript,也得安装 @typescript-eslint/parser
umi invoke
来将包里的 eslint 写到用户项目 package.json
中,类似 vue-cli 的做法,这样做到按需安装好在:
不好在:
devDependencies
不是好的做法一开始mfsu报路径错误,关闭了后还是报错
export { createBrowserHistory, createHashHistory, createMemoryHistory, createSearchParams, Link, matchPath, matchRoutes, NavLink, Outlet, useLocation, useMatch, useNavigate, useOutlet, useParams, useResolvedPath, useRoutes, useSearchParams, useAppData, renderClient, useRouteData } from 'C:\demo\node_modules@umijs\renderer-react';
6 | // umi/client/client/plugin
7 | export { ApplyPluginsType, PluginManager } from 'C:\demo\node_modules\umi\client\client\plugin.js';
| ^
8 | export { history, createHistory } from './core/history';
现在是不是window还不兼容啊
@sorrycc
小任务集合,做之前先认领(编辑主贴,加 @自己 ),避免冲突。
注意:1) 全部配置开启 2) 需求描述在对应文件的注释里
{ antd: { external: true }}
开启@fs/node_modules/antd
)的方案不兼容 windowsCould not resolve entry module (index.html)
错误 @PeachScript #146@@/exports
找不到 @devilmancbcchainWebpack
的口子允许用户修改配置 @devilmancbc #428@umijs/mfsu
,让 mfsu 能用于非 umi 体系,https://github.com/sorrycc/example-webpack-mfsuArray<{ find: string | RegExp, replacement: string }>
类型无法支持umi dev --vite
启动项目hoist-non-react-statics
)库, 导致vite不能将commonjs转为esm然后报错vite模式启动,性能相当之慢。一直在更新dependencies
After running umi dev
, one of the UI libraries wants to use the global
variable (as window):
WS = typeof global.WebSocket !== "undefined" ? global.WebSocket : require_browser();
How can I enable global
through umi webpack?
内部已实现,一键迁移,90% 以上应用无须二次改动即可迁移成功
codemod 分别用在 应用层
和 插件层
,将 umi@2 项目升级到 umi@3。
使用前,最好先提交下 git。
# 全局安装
$ npm i -g @umijs/codemod
# 或者使用 yarn
# yarn global add @umijs/codemod
# 应用层使用
$ umi-codemod --type=app .
# 插件层使用
$ umi-codemod --type=plugin src
# 使用 npx
$ npx -p @umijs/codemod umi-codemod --type=app .
配置(.umirc.js|.umirc.ts|config/config.js|config/config.ts)修改
- import { IConfig } from 'umi-types'
+ import { IConfig } from 'umi'
export default {
- treeShaking: true,
- disableCSSModules: true,
- disableRedirectHoist: true
- history: 'memory',
+ history: { type: 'memory' },
- cssLoaderOptions: {
- modules: true,
- getLocalIdent: () => {}
- }
+ cssLoader: {
+ modules: {
+ getLocalIdent: () => {}
+ }
+ }
- lessLoaderOptions: {}
+ lessLoader: {}
+ dva: { hmr: true }
+ locale: { enable: true }
+ headScripts: [],
+ scripts: [],
+ metas: [],
+ links: [],
plugins: [
- ['umi-plugin-react', {
- dva: { hmr: true }
- locale: { enable: true },
- dynamicImport: { },
- headScripts: [],
- scripts: [],
- metas: [],
- links: [],
- }],
- ['umi-plugin-bar', {
- foo: {}
- }]
]
+ bar: { foo: {} }
routes: [
{
path: "/",
component: "./Page",
- Routes: [ './src/routes/auth.js' ]
- wrappers: [ './src/routes/auth.js' ]
routes: [
- { path: '/:userId', component: './User' }
+ { path: '/[userId]', component: './User' }
]
},
],
- minimizer: 'uglifyjs' | 'terserjs',
- uglifyJSOptions: {},
+ terserOptions: {},
}
项目代码
- import { formatMessage } from 'umi-plugin-react/locale'
- import { Link } from 'umi/link';
- import { dynamic } from 'umi/dynamic';
- import { withRouter } from 'umi/withRouter';
- import history from '@tmp/history';
+ import {
+ formatMessage,
+ Link,
+ dynamic,
+ withRouter,
+ history,
+ } from 'umi';
export default (props) => {
// ?locale=zh_CN
- const { locale } = props.params;
+ const { locale } = props.match.params;
return (
<div>Hello</div>
)
}
TODO
codemod 使用 jscodeshift,参考项目 antd4-codemod, react-codemod。
结合 PR bot,自动提 PR
info - MFSU write cache
...\node_modules@umijs\core\dist\config\config.js:76
throw new Error(e);
^
Error: TypeError: Cannot read properties of undefined (reading 'join')
at ...\node_modules@umijs\core\dist\config\config.js:76:23
ERROR in ./mfsu-virtual-entry/default.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: C:\Users\XXX\Desktop\work\umi-next\examples\mfsu-independent\mfsu-virtual-entry\default.js: Bad character escape sequence. (1:43)
1 | await import('C:\Users\XXX\Desktop\work\umi-next\examples\mfsu-independent\src\index.tsx');
"umi": "^4.0.0-rc.3",
package.json除了umi没有加任何东西
config/config.ts添加{request: {}}执行yarn start 报AssertionError [ERR_ASSERTION]: Invalid config keys: request
请问这个版本是不是还不能用,或者说要加什么插件吗@YdreamW
import styles from './index.less';
import { Button } from 'antd';
import 'antd/dist/antd.variable.min.css';
import { useAppData } from '@umijs/pro';
export default function IndexPage() {
const app = useAppData();
console.log('🚀 ~ file: index.tsx ~ line 8 ~ IndexPage ~ app', app);
return (
<div>
<h1 className={styles.title}>Page index</h1>
<Button type="primary">test</Button>
</div>
);
}
报错如下:
×
TypeError: Cannot read properties of undefined (reading 'call')
__webpack_require__
http://127.0.0.1:8000/mf-va_remoteEntry.js:23:42
Module../src/.umi/exports.ts
http://127.0.0.1:8000/src_umi_umi_ts.js:587:214
__webpack_require__
http://127.0.0.1:8000/mf-va_remoteEntry.js:23:42
Module../node_modules/.pnpm/@[email protected][email protected][email protected]/node_modules/@umijs/pro/index.esm.js
http://127.0.0.1:8000/mf-dep____vendor.9c84eb19.js:13341:61
__webpack_require__
http://127.0.0.1:8000/mf-va_remoteEntry.js:23:42
Module../.mfsu/mf-va_@umijs_pro.js
http://127.0.0.1:8000/mf-dep____vendor.9c84eb19.js:11828:68
__webpack_require__
http://127.0.0.1:8000/mf-va_remoteEntry.js:23:42
(anonymous function)
http://127.0.0.1:8000/mf-va_remoteEntry.js:666:105
__webpack_modules__.<computed>
webpack:/webpack/runtime/remotes loading:635
632 | var onFactory = function(factory) {
633 | data.p = 1;
634 | __webpack_modules__[id] = function(module) {
> 635 | module.exports = factory();
| ^ 636 | }
637 | };
638 | handleFunction(__webpack_require__, data[2], 0, 0, onExternal, 1);
View compiled
options.factory
webpack:/webpack/runtime/react refresh:6
3 | options.factory = function (moduleObject, moduleExports, webpackRequire) {
4 | __webpack_require__.$Refresh$.setup(options.id);
5 | try {
> 6 | originalFactory.call(this, moduleObject, moduleExports, webpackRequire);
| ^ 7 | } finally {
8 | if (typeof Promise !== 'undefined' && moduleObject.exports instanceof Promise) {
9 | options.module.exports = options.module.exports.then(
View compiled
__webpack_require__
./webpack/bootstrap:24
21 | var execOptions = { id: moduleId, module: module, factory: __webpack_modules__[moduleId], require: __webpack_require__ };
22 | __webpack_require__.i.forEach(function(handler) { handler(execOptions); });
23 | module = execOptions.module;
> 24 | execOptions.factory.call(module.exports, module, module.exports, execOptions.require);
| ^ 25 | } catch(e) {
26 | module.error = e;
27 | throw e;
View compiled
▶ 3 stack frames were collapsed.
__webpack_require__
./webpack/bootstrap:24
21 | var execOptions = { id: moduleId, module: module, factory: __webpack_modules__[moduleId], require: __webpack_require__ };
22 | __webpack_require__.i.forEach(function(handler) { handler(execOptions); });
23 | module = execOptions.module;
> 24 | execOptions.factory.call(module.exports, module, module.exports, execOptions.require);
| ^ 25 | } catch(e) {
26 | module.error = e;
27 | throw e;
View compiled
Function.fn
webpack:/webpack/runtime/hot module replacement:61
This screen is visible only in development. It will not appear if the app crashes in production.
Open your browser’s developer console to further inspect this error. Click the 'X' or hit ESC to dismiss this message.
复现步骤,
$ cd examples/boilerplate
$ npm run dev -- --vite
比如一个系统由N个模块组成,这些模块不能单独部署,但是可以在系统中任意组合(每个模块包含单独的路由、model等)最后在应用中将每个模块里面的路由加载进来
isBrowser
判断失效,带来渲染不一致问题)document.ejs
来作为 SSR 模板[id].html
文件,走客户端渲染优化点:
相对于 umi@2:
getInitialProps
中的 location
只在 客户端 使用。 @umijs/renderer-react
prerender
开启。umi@2 的用法:
import server from 'umi-server';
const render = server({
// get dist/umi.server.js
root: join(__dirname, 'dist'),
});
http.createServer(async (req, res) => {
...
const ctx = {
req,
res,
}
const { ssrHtml } = await render(ctx);
// => <!doctype html><html>...</html>
})
在 umi@3 使用:
基本使用:
// .umirc.ts
export default {
ssr: true,
routes: [
{ path: '/', component: () => <p>Home</p> },
{ path: '/news', component: () => <p>News</p> },
]
}
build 出 umi.server.js
// server/index.ts
import { serverRender } from './umi.server';
http.createServer(async (req, res) => {
...
const ctx = {
req,
res,
url: '/',
renderOpts: {
// staticMarkup: false,
// customRender: (element) => string
}
}
const html = await serverRender(ctx);
// => <!doctype html><html>...</html>
})
umi@2 通过插件的形式:
// .umirc.ts
export default {
ssr: true,
plugins: [
['@umijs/plugin-prerender']
]
}
umi@3 使用形式:
// .umirc.ts
export default {
ssr: true,
prerender: true,
// exportStatic 配置直接废弃掉?
// 还是 ssr + exportStatic 组合起来相当于 prerender
}
=> build
- dist
- index.html
- news.html
- news
- [id].html => 实际是默认的 html 模板,走 CSR
webpack 构建是走同时构建、还是先构建 client 再构建 server
同时构建的好处:速度快,并行构建
先后构建:可以拿到 client 端的 manifest ,可以拿到静态资源 hash 名。replaceChunkMaps.ts#L26-L29
按官方文档:react-router/web/guides/server-rendering
路由配置:
{
routes: [
{ path: '/', component: Layout },
{ path: '/news', component: News },
{ path: '/news/:id', component: NewsDetail },
]
}
为了在服务端,让 Layout 和 Page 都能匹配到 getInitialProps
值,会将当前 url 匹配到层级路由依次执行 getInitialProps ,同时在客户端注入到 window.g_initialData
,返回如下数据格式:
// 访问 /news/:id
{
'/': {
data: 'parent'
},
'/news': {
data: 'news',
},
'/news/:id': {
data: 'newsId',
}
}
渲染出来就是:
<div>
<div>
parent
<div>
news
<p>newsId</p>
</div>
</div>
</div>
其中 getInitialProps 在客户端和服务端的传值略微不同,如下:
export interface IGetInitialProps {
isServer: boolean;
match: match;
}
export interface IGetInitialPropsServer extends IGetInitialProps {
req?: IncomingMessage;
res?: ServerResponse;
}
export interface IGetInitialPropsClient extends IGetInitialProps {
location?: Location;
}
不同于服务端框架中间件,类似于输出 html 前的预处理操作,使用方式:
import { serverRender } from './umi.server';
const DocumentTitle = require('react-document-title');
http.createServer(async (req, res) => {
...
const ctx = {
req,
res,
url: '/',
renderOpts: {
postProcessHtml: [
// 支持 react-document-title
(html) => {
const title = DocumentTitle.rewind();
if (title) {
return html.replace(/<title>.*?<\/title>/, title);
}
return html;
},
...
]
}
}
const html = await serverRender(ctx);
// => <!doctype html><html>...</html>
})
compose 实现:
import { Handler } from './index';
/**
* render html handlers pipe
* const resultHtml = compose(
* handler1,
* handler2,
* hanlder3,
* ...
* )($, args);
*
* @param handler ($, args) => $
*/
const compose: (...handler: Handler[]) => Handler = (...handler) =>
handler.reduce((acc, curr) => ($, args) => curr(acc($, args) || $, args) || $);
export default compose;
和之前保持一致,通过 @umijs/plugin-dva 插件实现。
因为 document.ejs
+ scripts
+ metas
+ links
已经可以定制各种 html 模板,SSR 直接用 getHtmlGenerator
拿到模板,
如果是 服务端注入 script,还是可以通过 中间件 来完成。
在服务端时直接渲染成 require
,不再走 dynamicImport 方式,对服务端基本没有影响。
https://github.com/zeit/next.js
https://github.com/ykfe/egg-react-ssr
https://github.com/jaredpalmer/after.js
error - ./packages/plugin-docs/client/theme-doc/index.ts:3:0-28
Module not found: Error: Can't resolve './tailwind.out.css'
解决:先运行 pnpm build:extra 再次启动即可。
不确定是设计如此(参与贡献文档未说明),还是bug。
请问开发阶段支持vite吗 还是只是build阶段才支持vite?
antd 版本3.5.22
mf-va_remoteEntry.js:59946 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'name')
at mf-va_remoteEntry.js:59946:54
at Array.forEach ()
at Function.add (mf-va_remoteEntry.js:59945:19)
at node_modules/antd/es/icon/index.js (mf-va_remoteEntry.js:60206:24)
at __init (mf-va_remoteEntry.js:17:58)
at node_modules/antd/es/input/Input.js (mf-va_remoteEntry.js:60403:7)
at __init (mf-va_remoteEntry.js:17:58)
at node_modules/antd/es/input/index.js (mf-va_remoteEntry.js:62102:7)
at __init (mf-va_remoteEntry.js:17:58)
at node_modules/antd/es/auto-complete/index.js (mf-va_remoteEntry.js:62471:7)
at __init (mf-va_remoteEntry.js:17:58)
at node_modules/antd/es/index.js (mf-va_remoteEntry.js:144531:7)
at __init (mf-va_remoteEntry.js:17:58)
at node_modules/.cache/mfsu/mf-va_antd.js (mf-va_remoteEntry.js:144668:7)
at __init (mf-va_remoteEntry.js:17:58)
at mf-va_remoteEntry.js:146074:43
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.