Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support ssr #2543

Merged
merged 46 commits into from
Jun 19, 2019
Merged

feat: support ssr #2543

merged 46 commits into from
Jun 19, 2019

Conversation

sorrycc
Copy link
Member

@sorrycc sorrycc commented Jun 5, 2019

感谢 @狼叔 和 @zhangyuang 提供方案支持。

使用

示例:

一、配置里启用 ssr

export default {
  ssr: true,
};

二、Page Component 新增 getInitialProps 的静态方法,返回 resolve 数据的 Promise

比如,

Page.getInitialProps = async () => {
  return Promise.resolve({
    data: {
      ssr: 'http://127.0.0.1:7001',
      csr: 'http://127.0.0.1:8000',
    },
  });
};

三、服务端引用 dist/umi.server.js 拿到 Html 文本

const Koa = require('koa');
const { renderToNodeStream } = require('react-dom/server');

// Polyfill window
global.window = {};

const app = new Koa();
app.use(async (ctx, next) => {
  if (ctx.req.url === '/') {
    ctx.type = 'text/html';
    ctx.status = 200;
    const serverStream = require('./dist/umi.server');
    const serverRes = await serverStream.default(ctx);
    const stream = renderToNodeStream(serverRes);
    ctx.body = stream;
  } else {
    await next();
  }
});

app.listen(7001, () => {
  console.log('server listening on 7001');
});

Checklist

  • npm test passes
  • tests are included
  • documentation is changed or added
  • commit message follows commit guidelines

Description of change

});
if (config.ssr) {
history = `
__IS_BROWSER ? ${initialHistory} : require('history/createMemoryHistory').default()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entries 需要给默认值吗?

Suggested change
__IS_BROWSER ? ${initialHistory} : require('history/createMemoryHistory').default()
__IS_BROWSER ? ${initialHistory} : require('history/createMemoryHistory').default({ initialEntries: window.g_initialEntries })

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不用,serverRender 时会 push 一个。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

看到了,有个 history.push

let serverRender;
if (!__IS_BROWSER) {
serverRender = async (ctx) => {
const pathname = ctx.req.url;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果是 koa ,可能是 ctx.request.url

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你觉得这里做兼容好还是直接把参数换成 url 好?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感觉按照统一的规范,构造一个符合规范的ctx对象比较好例如express事先定义一个const ctx={req: request,}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

除了 url,我们还会用到其他的参数吗?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

除了 url,我们还会用到其他的参数吗?

与实际业务需求有关,可能有些业务需要拿到ctx.req.host之类的参数

@coveralls
Copy link

coveralls commented Jun 6, 2019

Pull Request Test Coverage Report for Build 4205

  • 103 of 168 (61.31%) changed or added relevant lines in 23 files are covered.
  • 105 unchanged lines in 6 files lost coverage.
  • Overall coverage increased (+1.2%) to 33.935%

Changes Missing Coverage Covered Lines Changed/Added Lines %
packages/af-webpack/src/getConfig/css.js 6 7 85.71%
packages/umi-build-dev/src/plugins/commands/block/download.js 0 1 0.0%
packages/umi-build-dev/src/htmlToJSX.js 38 40 95.0%
packages/umi-build-dev/src/plugins/commands/build/index.js 0 2 0.0%
packages/umi-build-dev/src/plugins/commands/getHtmlGenerator.js 0 2 0.0%
packages/umi-plugin-react/src/plugins/dynamicImport.js 0 2 0.0%
packages/af-webpack/src/dev.js 0 3 0.0%
packages/umi-build-dev/src/getConfig/configPlugins/ssr.js 0 3 0.0%
packages/umi-build-dev/src/plugins/afwebpack-config/index.js 0 3 0.0%
packages/umi-build-dev/src/plugins/commands/replaceChunkMaps.js 20 23 86.96%
Files with Coverage Reduction New Missed Lines %
packages/umi-build-dev/src/plugins/commands/build/index.js 1 0.0%
packages/umi-build-dev/src/plugins/afwebpack-config/index.js 1 2.5%
packages/af-webpack/src/dev.js 1 0.0%
packages/umi-build-dev/src/FilesGenerator.js 2 1.48%
packages/umi-build-dev/src/plugins/commands/block/index.js 47 0.0%
packages/umi-build-dev/src/plugins/commands/block/util.js 53 0.89%
Totals Coverage Status
Change from base Build 4156: 1.2%
Covered Lines: 1469
Relevant Lines: 4301

💛 - Coveralls

@ycjcl868
Copy link
Contributor

ycjcl868 commented Jun 6, 2019

有个问题是关于 hooks 的,用户在 server 端调用 renderToString 的话,服务端的 react-dom/serverumi.server.js Reactdom 中的 ReactCurrentDispatcher 不一样。

image

}
});
return activeRoute;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个函数执行有点问题,已匹配的可能会被后面的 undefined 覆盖掉。

image

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好的,我补下

@sorrycc
Copy link
Member Author

sorrycc commented Jun 6, 2019

hooks 的问题我猜是多份 react-dom 的问题,react-dom/server./dist/umi.server 导出应该就好了。

@zhangyuang
Copy link
Contributor

zhangyuang commented Jun 6, 2019

hooks 的问题我猜是多份 react-dom 的问题,react-dom/server./dist/umi.server 导出应该就好了。

嗯问题是因为server与client用的react-dom不是同一个,https://zhuanlan.zhihu.com/p/57651697
不过我本地用hooks倒是没有这个错误提示,不知道是否和版本有关系

@ycjcl868
Copy link
Contributor

ycjcl868 commented Jun 6, 2019

还有个问题是 umijs/umi-request,不支持服务端请求。
可能得提供一个类似 isomorphic-fetch 的同构请求方法。

AboutLanding.getInitialProps = async () => {
  require('es6-promise').polyfill();
  require('isomorphic-fetch');
  const res = await fetch('https://mock').then(response => response.json());

  return {
    data: res,
  }
}

=>

image

@sorrycc
Copy link
Member Author

sorrycc commented Jun 6, 2019

umijs/umi-request 得找时间梳理一版。

@ycjcl868
Copy link
Contributor

ycjcl868 commented Jun 15, 2019

chunkMap 还有个问题是,css 是动态加载的,开启动态加载后,页面会有闪烁。需要把当前 router 相关chunkCSS 加进来。

@sorrycc
Copy link
Member Author

sorrycc commented Jun 15, 2019

要的是 csr 的 manifest,为啥打 ssr 的 manifest?ssr 的 manifest 和 csr 的应该会有差异吧。

@ycjcl868
Copy link
Contributor

ycjcl868 commented Jun 15, 2019

嗯嗯,知道了。有区别,要用户开启 manifest 配置,或者有 ssr 配置的时候,自动开启 manifest

@ycjcl868
Copy link
Contributor

ycjcl868 commented Jun 15, 2019

chunkMap 整理(不仅是 chunkMap 的问题,也是后面 react-loadable.json 动态加载的问题):

问题

本质是 server 配置依赖 client 端生成的产物(*.json)

  1. chunksMap 写成固定的 umi: ['umi.js', 'umi.css'],prod 环境下有 hash 时就挂了

umi: ['umi.js', 'umi.css'],

  1. 开启动态加载后,页面会有闪烁。原因也是 chunksMap 只加载了 umi.css,相应的路由页面 chunk css 未预先加载到页面 html 中。(如果是走 ssr,其实动态加载的意义和成效不大,可之后考虑)

image

方案讨论

上面的问题,都是与 manifest 相关,相当于 ssrWebpack 配置生成 umi.server.js 的时候,需要先拿到 clientWebpack 配置的 manifest。类似这种资源映射:

// asset-manifest.json
{
  "layout.css": "/layout.147eaece.chunk.css",
  "layout.js": "/layout.45eefd85.async.js",
  "p__About.js": "/p__About.187c069f.async.js",
  "p__About__History.css": "/p__About__History.691667cd.chunk.css",
  "p__About__History.js": "/p__About__History.cd527e93.async.js",
  "umi.css": "/umi.fa5ee7b5.css",
  "umi.js": "/umi.f8c632ea.js",
  "pages": [
     {
       "/about": [
           // 可通过后续判断 chunk 资源类型
           "p__About.187c069f.async.js"
        ],
       "/about/history": [
           // 可通过后续判断 chunk 资源类型
           "p__About__History.691667cd.chunk.css",
           "p__About__History.cd527e93.async.js"
        ],    
        ... 
     }
   ]
  "vendors.css": "/vendors.0ff0ea50.chunk.css",
  "vendors.js": "/vendors.704be5ad.async.js",
  "index.html": "/index.html"
}

但目前的写法,是在编译中执行的 chunkMapsumi.server.js 早于客户端资源生成。用图表示就是:

image

@ycjcl868 ycjcl868 mentioned this pull request Jun 17, 2019
4 tasks
@ycjcl868 ycjcl868 mentioned this pull request Jun 17, 2019
4 tasks
* fix: chunkMaps

* 📝 must enable manifest when use ssr

* fix: htmlTemplateMap use map

* fix: publicPath
@ycjcl868
Copy link
Contributor

开始补下用例,改动有点多了。

@afc163
Copy link
Contributor

afc163 commented Jun 17, 2019

如果用 squash 合并,细节都会丢掉,最好避免一下。

@ycjcl868
Copy link
Contributor

嗯嗯,squash merging 配置可以关掉。 @sorrycc

integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==
dependencies:
mkdirp "^0.5.1"
>>>>>>> theirs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

conflicted

ycjcl868 and others added 7 commits June 18, 2019 11:03
* test: htmlToJSX

* 🚧 af-webpack ssr tests cases

* test: umi-build-dev service

* test: htmlGenerator

* test: replaceChunkMaps

* test: prerender e2e, fix: ssr not generate manifest

* test: add e2e pre-render

* fix: server babel not use corejs
* fix: manifest basePath, and html2JSX

* fix: pre render a little polyfill

* feat: eggjs with umi ssr demo

* fix: mock window

* 📝 umi ssr faq

* rm: manifest

* rm: demo

* fix: .gitignore
@ycjcl868
Copy link
Contributor

dev 下,server 需要加 @babel/runtime。build 是 ok 的。

image

@sorrycc
Copy link
Member Author

sorrycc commented Jun 19, 2019

server 时应该禁用 transform-runtime 插件,我先合了,再提 PR 解决吧。

@sorrycc sorrycc merged commit fda1ad2 into master Jun 19, 2019
@delete-merged-branch delete-merged-branch bot deleted the feat/ssr branch June 19, 2019 13:26
@ycjcl868 ycjcl868 mentioned this pull request Jun 19, 2019
19 tasks
@lilili001
Copy link

lilili001 commented Jul 29, 2019

ssr 动态路由不支持: npm run start 报错 you should not use exportStatic with dynamic route: ${route.path}

@sorrycc

@ycjcl868
Copy link
Contributor

ycjcl868 commented Jul 29, 2019

@lilili001 exportStatic 配置收敛到 @umijs/plugin-prerender

@ycjcl868 ycjcl868 mentioned this pull request Apr 20, 2020
60 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SSR & Pre Rendering 方案
6 participants