Giter VIP home page Giter VIP logo

umi-next's Introduction

umi 4 代码已合并到 umijs/umi,pr 和 issue 请前往 umijs/umi

umi-next's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

umi-next's Issues

[Feature Request] 路由是否支持npm包的形式

Background

比如有A、B两个系统
现将A打包成npm包,在B中npm install A,然后路由地址指向了A(node_modules/A)
这种的能正常解析出来嘛?model能正常工作嘛?

Proposal

Additional context

运行pnpm doc:dev报错Can't resolve './tailwind.out.css'

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。

[RFC] React SSR

【RFC】Umi@3 react SSR

#12

背景

特性

  • 开箱即用
  • polyfill 支持
    • 默认关闭(开启后会使三方库 isBrowser 判断失效,带来渲染不一致问题)
  • 服务端无关
  • 支持 CSR 
  • 支持 dynamicImport 路由分割
  • 兼容 html 与 jsx 页面模板(在服务端可定制)
    • 支持自定义 document.ejs 来作为 SSR 模板
    • 使用 jsx Document 组件定义模板
  • runtime 渲染支持中间件/插件方式,通过配置开启
    • react-helmet
    • react-helmet-async
    • react-document-title
    • dva
  • 用户 Layout 支持 getInitialProps 
  • preload 支持
  • Document 支持
  • @umijs/plugin-prerender 预渲染插件     
    • 动态路由是默认 index.html 生成 [id].html 文件,走客户端渲染
    • 支持预览,build 后可以直接 server dist 目录

优化点:

  • 超时处理
  • 支持 Stream 方式渲染
  • 针对服务端渲染的 babel 配置,优化 React Node 服务端 bundle 大小
  • AppShell & PWA 实现
  • 支持 Serverless,直接打出可部署的 server.js

改变

相对于 umi@2:

  1. [Break Change] getInitialProps  中的 location 只在 客户端 使用。 
  2. 不再使用 umi-server ,直接使用 @umijs/renderer-react 
  3. @umijs/plugin-prerender 插件内置进 umi,通过 prerender 开启。
  4. 出于 runtime 性能问题,server 不再使用 cheerio

用法

SSR 

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>
})

prerender

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

getInitialProps 数据预获取

路由配置:

{
  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;
}

SSR 中间件

不同于服务端框架中间件,类似于输出 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;

dva 支持

和之前保持一致,通过 @umijs/plugin-dva 插件实现。

Layout 模板

因为 document.ejs + scripts + metas + links 已经可以定制各种 html 模板,SSR 直接用 getHtmlGenerator 拿到模板,

如果是 服务端注入 script,还是可以通过 中间件 来完成。

动态加载

在服务端时直接渲染成 require,不再走 dynamicImport 方式,对服务端基本没有影响。

参考

Next.js

https://github.com/zeit/next.js

egg-react-ssr

https://github.com/ykfe/egg-react-ssr

after.js

https://github.com/jaredpalmer/after.js

razzle

https://github.com/jaredpalmer/razzle

mfsu没法和antd 3.x版本一起使用,为什么这两个会冲突

image
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

@sorrycc

提升单测测试覆盖率

背景

为了 umi 底层质量更高,大家可以一起来补单测,可以从这里挑选自己感兴趣的模块,回复到该 issue,避免重复。

image

规范

测试用例与需要测试的文件写在一起,例如:

- src
   - index.test.ts
   - index.ts
   - bar
      - bar.ts
      - bar.test.ts

用例尽量以 test 平铺展开,避免嵌套式写法。

test('bar', () => {

})

test('foo', () => {

})

有关插件的方法见 《插件测试》


Background

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.

image

Specification

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

[RFC] @umijs/core Logger

Umi@3 repo 日志体系

特性

  • level 支持 debug = profile < info < warn = duplicate < error ,默认输出 info 及以上。
  • 文档化提示 codes,便于错误排查,使用 @umijs/error-code-map
  • 高阶用户更易理解 umi 的执行原理及生命周期,只需要加上环境变量  LOGGER=debug
  • 支持扩展 core/Logger
  • 支持浏览器端,例如:在 @umijs/runtime 可使用
  • Profiling 耗时记录?可用于 server runtime ,插件加载的耗时,做对应优化。

目标

  • 报错、提示信息、废弃、警告、运行时耗时及日志都可以使用
  • Client & Server 端都支持
  • 只需要一份日志,快速定位 umi 内部 bug 、项目自身问题等

用法

// 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]

详细设计

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/adc7475e-227e-4ff9-a427-e6adff7da320/Untitled.png

日志类型

  • debug
  • info
  • warn
  • duplicate
  • error
  • profile

日志节点

  • core 层
  • 插件层
  • actions 的 profile 耗时

参考

[补充中] Umi UI 迁移 umi@3 记录

补充到 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.getRoutesapi.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,
        ...
      })
    }
  })
}
  1. api.winPathapi.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;
}
  1. api.addEntryCode 入参由 string 改成 () ⇒ string
// before
export default (api) => {
  api.addEntryCode(`console.log('foo');`)
}

// after
export default (api) => {
  api.addEntryCode(() => `console.log('foo');`)
}
  1. 单数变复数,例如 api.registerPlugin => api.registerPluginsapi.addHTMLStylesapi.addHTMLHeadScriptsapi.addHTMLScriptsapi.addHTMLMetasapi.addHTMLLinks

应用层

  • 配置变更

    • uglifyJSOptions 配置废弃,改用 terserOptions
    • cssLoaderOptionscssLoader
    • lessLoaderOptionslessLoader,css-loader 升级至 3.x,配置有 BreakChange,详见
    • output.publicPath 变更
    • .env 环境变量读取缺失
    • treeShaking 默认开启,无须配置
  • 缺少

    • chainWebpack
    • headScriptslinksscripts ,计划配置内置,平铺 #32
  • 代码层
    history 导出

- import history from '@tmp/history';
+ import { history }  from 'umi';

umi/prompt 缺少(次优先级)

运行时 app.ts:

- export const patchRoutes = (routes) => {}
+ export const patchRoutes = ({ routes }) => {}

配置路由导致渲染多个layout

image

 routes: [
    {
      path: '/',
      component: '../layouts/index',
      routes: [{ path: '/', component: 'Index' }],
    },
  ],

配置路由时,如果指定了layout (component: '../layouts/index') 则会渲染出两个layout

father 3 TODOS

father 3 介绍 + 任务集合,欢迎评论参与共建,做之前先认领(编辑主贴,加 @自己 ),避免冲突。

介绍

核心升级

  • 📦 依赖预打包:类似 Umi 3.5/4 的依赖预打包,基于 ncc(打包文件)+ dts-packer(生成类型)
  • 🛰 产物面向未来:默认不再提供 cjs 产物类型,官方提供兼容模式插件,以便存量项目升级
  • ⚔️ 双模式构建
    • Bundle 模式:仅 UMD 产物走 bundle 模式,核心从 rollup 换成宽容度更高的 webpack
    • Bundless 模式:仅 ESM 产物走 bundless 模式 + 生成 TS 类型定义,默认提供 babel(browser)+ esbuild(node)双编译核心,可基于插件系统注册 swc/tsc 模式
  • 🏗 工程化能力生成:基于 Umi 4 的 generator,提供打包场景的原子工程化能力,例如 g eslint、g test、g commitlint 等
  • 🚨 自动诊断:功能类似 brew doctor,自动对打包场景的常见误区做诊断并给出最佳实践
  • 🚧 Friendly Error:统一处理双模式构建中的常见错误,并给出友好提示,提升使用体验 + 降低答疑成本
  • 🛫 去历史包袱
    • 去 umi-test 集成,改用微生成器
    • 去 storybook 集成,改和 dumi 集成
    • 去 monorepo 支持,改其他工具替代(pnpm?)
    • 去 rollup 核心,改用 webpack

结构图

overview

任务集合

Core Service

  • 初始化仓库,基于 Umi Core 实现配置读取、命令执行、插件注册 @sorrycc b318356
  • 用户配置类型定义
  • 用户配置 Schema 校验规则 @PeachScript

Builder Module

做组件库源码打包/编译

  • 用户配置转换成 Builder 配置 @PeachScript
  • 构建执行器 (Executor)
    • bundless 构建执行器 @PeachScript
    • bundless d.ts 文件生成 @STDSuperman
    • bundless watch 模式 @PeachScript
    • bundle 构建执行器 @PeachScript
    • 统一 Error 捕获及输出
    • bundless cjs 产物兼容层(便于迁移存量有 cjs 产物的项目)
  • 基于 webpack loader 的 loader 系统(Bundless)@PeachScript
  • 内置 js 转换器 (Bundless Transformer)
  • 内置打包器 (Bundler,基于 @umijs/bundler-webpack) @PeachScript

PreCompile Module

做框架/工具的依赖打包,类似 Umi 4

  • 用户配置转换
  • ncc bundler 打包依赖
  • dts-packer 生成类型定义

Doctor Module

做项目的前置检查,给出研发建议或者错误

  • 调研/整理
    • father 2 过往 issue 高频问题整理
    • 组件研发场景的最佳实践整理
    • 梳理出前置检查的规则列表(例如无用依赖、package.json 的 sideEffects/main/module/exports 没配对等等)
  • ModuleGraph
    • 依赖分析
    • 源代码分析
  • 研发建议/错误分析及序列化输出
    • 支持控制台输出
    • 支持 API 调用供其他框架(例如 dumi)集成到 GUI 中提示开发者

Generate Module

基于 umi g 做原子工程化能力的生成

  • 调研/整理
    • 组件研发场景的工程化能力收集(例如 jest/commitlint/changelog)
  • 各能力的 generate 模板编写
  • 集成 umi g 并串联

[RFC] codemod

Codemod

内部已实现,一键迁移,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

jest 测试时找不到@别名

What happens?

找不到别名'@',在 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 |

Mini Showcase Repository(REQUIRED)

https://github.com/alitajs/umi3-plugin-test-question

How To Reproduce

yarn 
tarn test

Steps to reproduce the behavior: 1. 2.

Expected behavior 1. 2.

Context

  • Umi Version: 3.0.0-beta.19
  • Node Version: 10.13.0
  • Platform: mac

修改config中的layout的值,报错。

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

[RFC] @umijs/locale

umi-plugin-locale

实现

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 中也可以使用,无需上下文。

插件要做的事情

默认Polyfill  

 

 // 如果需要支持 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

尝试迁移umi 与 bundler-vite相关问题

尝试迁移使用bundler-vite启动项目

  1. 配置文件应与umi的配置文件保持一致,现只支持在config.ts而不支持config/config.ts,.umirc 等
  2. bundler-vite的入口文件是src/index.tsx 而 umi 则是 app.tsx,应该彼此兼容
  3. 关于alias应该和vite保持一致,如这种Array<{ find: string | RegExp, replacement: string }>类型无法支持
  4. config中配置modifyViteConfig不生效,从源码中发现目前好像只支持在插件中使用modifyViteConfig更改vite config?(很需要)
  5. umi/packages.json 的module路径不存在导致vite找不到入口文件

尝试迁移使用umi@next umi dev --vite启动项目

  1. tmpFiles写入的文件没有针对windows做处理
  2. tmpFiles使用了这个库@umi/renderer-react,但引用的地址为绝对地址且这个库使用了基于commonjs的(如 hoist-non-react-statics)库, 导致vite不能将commonjs转为esm然后报错
  3. 开发环境引入了polyfill,且这些polyfill基于commonjs导致线上报错

性能问题

vite模式启动,性能相当之慢。一直在更新dependencies

[RFC] 区块功能迁移

背景

主要是 Umi 区块使用,目前是在命令行UI

  • 在 Umi@2 中,命令行插件在内置在 umi-build-dev
  • UI 使用

目标

  • 区块兼容 umi@2
  • 区块相关底层的公共 utils、方法,封装成 SDK(暂定 @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) => {

}

阶段

  • 跑通 umi@3 区块功能
  • 兼容 umi@2

Umi 3 Progress

The big picture

image

Milestone

  • 12.13 Initial the repo, including Directory Structure
  • 12.20 @umijs/core, including Plugin system and Config
  • 12.27 @umijs/runtime + @umijs/renderer-react + File Generate
  • 01.10 @umijs/routes + @umijs/bundler
  • 01.17 Release umi@3-alpha
  • 03.02 Release umi@3

Packages

  • core
    • Service + PluginAPI
    • Config
    • Routes
    • Html
    • Logger
  • preset-built-in
    • generateFiles
    • commands/buid
    • commands/dev
  • runtime
    • Plugin
    • dynamic
  • renderer-react
    • renderClient
    • renderRoutes
    • renderServer
  • bundler
  • bundler-webpack
  • utils
  • test
  • babel-preset-umi
  • types
  • ast

文档 TODO

The mfsu version of 4.0.0-beta.16 does not work properly

➜ 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

window11编译报错

一开始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

Umi 4 TODOS

小任务集合,做之前先认领(编辑主贴,加 @自己 ),避免冲突。

renderer-react

  • 用 loadable-components 代替 React.lazy + Suspense @lylwanan #202
  • 删除 src\pages\document.ejs ,怎么处理 html 模板 @sorrycc
  • hash:true build 生成的 html 中,引用的还是 ‘/umi.js’ b99b965 @sorrycc

plugins

注意:1) 全部配置开启 2) 需求描述在对应文件的注释里

preset-umi

  • 过滤约定路由文件,比如 pages/**/components,修改 api.appData.route @xiaohuoni #273
  • exports support types (import type all from umi) @xiaohuoni #275
  • transform IEAR 处理绝对路径依赖(例如 @fs/node_modules/antd)的方案不兼容 windows

preset-built-in

  • umi g page index @xiaohuoni #131
  • umi plugin list @siyi98 #157
  • umi config list @xiaohuoni #138
  • umi config set alias @xiaohuoni #138
  • umi config get alias @xiaohuoni #138
  • umi config remove alias @xiaohuoni #138
  • umi config list 默认应该只列用户的配置,umi config list --all 才包含所有的 #144
  • umi config remove 和 set 后用 prettier 格式化一下 #144
  • plugin.ts 变更时自动重启 dev server @siyi98 #168
  • 约定式的 favicon,自动识别 src/favicon.ico 文件,支持 ico、gif、png、jpg、jpeg、svg、avif、webp 后缀 @xiaohuoni #161
  • 扩展 umi g @xiaohuoni #159
  • npmClient @xiaohuoni #163
  • require bug,解析用户的配置文件时,未提前将 .js 文件编译成commonjs规范的代码,导致报错 32ad3f0

create-umi

bundler-esbuild

bundler-vite

bundler-webpack

build

dev

chore

umi引入了esbuild

在浏览器中执行esbuild代码报错,看调用栈发现是引入了esbuild代码?

image

使用bundler-vite dev运行

request加入config.ts报Invalid config keys:request

"umi": "^4.0.0-rc.3",
package.json除了umi没有加任何东西

config/config.ts添加{request: {}}执行yarn start 报AssertionError [ERR_ASSERTION]: Invalid config keys: request

请问这个版本是不是还不能用,或者说要加什么插件吗@YdreamW

ReferenceError: global is not defined

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?

example中mfsu-independent 例子启动报错

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');

使用npx create-umi@nextc创建项目报错。

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: [ '' ]
}

ci 构建window 安装失败

(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

history和single-spa冲突,单一回调会重复执行两次,导致umi和qiankun一起使用时行为异常

What happens?

image

最小可复现仓库

History代码库错误行

Single-SPA代码库错误行

错误分析

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,无法覆盖

从@umijs/pro中导入方法运行后报错

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.

babel配置项建议

在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

[RFC] @umijs/lint

@umijs/lint

包含 eslint 、prettier、stylelint 配置的合集包。

原因

规范 Umi & Bigfish 项目的代码规范及统一团队代码风格。

目标

  • lint 规则可扩展,可基于这个包扩展出项目/业务线的 lint 合集。
  • 稳定,不会因为升级而导致报错,阻碍构建进程。
  • 跟进社区,及时将最新、有用的规则同步。
  • umi 仓库消费这份 lint 规则

方案一

  1. @umijs/fabric 规则拿到 @umijs/lint 中,锁中间 minor 版本(锁具体版本?)。
  2. 加上测试用例,尽可能全的测试用例,每次发包时,保证之前的代码风格,lint 不挂。
  3. 导出 eslintprettierstylelint 实例,Umi 中使用到的全通过来消费:
import { eslint, prettier, stylelint } from '@umijs/lint'

好在:

  1. 稳定性能保证,不会因为 eslint 自身升级而造成 umi 项目 lint 失败

不好在:

  1. 需要维护包的成本较高,定期维护
  2. 用户项目里要安装每个 eslint-plugin-* 包,即使没有用到 TypeScript,也得安装 @typescript-eslint/parser

方案二

  1. 通过手动 umi invoke 来将包里的 eslint 写到用户项目 package.json 中,类似 vue-cli 的做法,这样做到按需安装

好在:

  1. 按项目特性安装包,减少 lint 包出错概率

不好在:

  1. install 前会多一步操作,对用户习惯有挑战
  2. 写包到用户的 devDependencies 不是好的做法

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.