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

Conversation

@sorrycc
Copy link
Member

commented Jun 5, 2019

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

使用

示例:https://github.com/umijs/umi-example-ssr

一、配置里启用 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()

This comment has been minimized.

Copy link
@ycjcl868

ycjcl868 Jun 6, 2019

Member

entries 需要给默认值吗?

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

This comment has been minimized.

Copy link
@sorrycc

sorrycc Jun 6, 2019

Author Member

不用,serverRender 时会 push 一个。

This comment has been minimized.

Copy link
@ycjcl868

ycjcl868 Jun 6, 2019

Member

看到了,有个 history.push

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

This comment has been minimized.

Copy link
@ycjcl868

ycjcl868 Jun 6, 2019

Member

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

This comment has been minimized.

Copy link
@sorrycc

sorrycc Jun 6, 2019

Author Member

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

This comment has been minimized.

Copy link
@zhangyuang

zhangyuang Jun 6, 2019

Member

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

This comment has been minimized.

Copy link
@sorrycc

sorrycc Jun 6, 2019

Author Member

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

This comment has been minimized.

Copy link
@zhangyuang

zhangyuang Jun 6, 2019

Member

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

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

sorrycc added some commits Jun 6, 2019

@coveralls

This comment has been minimized.

Copy link

commented Jun 6, 2019

@ycjcl868

This comment has been minimized.

Copy link
Member

commented Jun 6, 2019

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

image

}
});
return activeRoute;
}

This comment has been minimized.

Copy link
@ycjcl868

ycjcl868 Jun 6, 2019

Member

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

image

This comment has been minimized.

Copy link
@sorrycc

This comment has been minimized.

Copy link
@ycjcl868

ycjcl868 Jun 6, 2019

Member

好的,我补下

@sorrycc

This comment has been minimized.

Copy link
Member Author

commented Jun 6, 2019

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

@zhangyuang

This comment has been minimized.

Copy link
Member

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

This comment has been minimized.

Copy link
Member

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

This comment has been minimized.

Copy link
Member Author

commented Jun 6, 2019

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

@ycjcl868 ycjcl868 referenced this pull request Jun 6, 2019
1 of 4 tasks complete
@ycjcl868

This comment has been minimized.

Copy link
Member

commented Jun 15, 2019

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

@sorrycc

This comment has been minimized.

Copy link
Member Author

commented Jun 15, 2019

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

@ycjcl868

This comment has been minimized.

Copy link
Member

commented Jun 15, 2019

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

@ycjcl868

This comment has been minimized.

Copy link
Member

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 referenced this pull request Jun 17, 2019
0 of 4 tasks complete
@ycjcl868 ycjcl868 referenced this pull request Jun 17, 2019
0 of 4 tasks complete
Fix chunk map (#2616)
* fix: chunkMaps

* 📝 must enable manifest when use ssr

* fix: htmlTemplateMap use map

* fix: publicPath
@ycjcl868

This comment has been minimized.

Copy link
Member

commented Jun 17, 2019

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

@afc163

This comment has been minimized.

Copy link
Contributor

commented Jun 17, 2019

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

@ycjcl868

This comment has been minimized.

Copy link
Member

commented Jun 17, 2019

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

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

This comment has been minimized.

Copy link
@afc163

afc163 Jun 18, 2019

Contributor

conflicted

ycjcl868 added some commits Jun 18, 2019

Test cases ssr (#2631)
* 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 prerender bug (#2644)
* 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

This comment has been minimized.

Copy link
Member

commented Jun 19, 2019

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

image

@sorrycc

This comment has been minimized.

Copy link
Member Author

commented Jun 19, 2019

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

@sorrycc sorrycc merged commit fda1ad2 into master Jun 19, 2019

5 checks passed

WIP Ready for review
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
continuous-integration/travis-ci/push The Travis CI build passed
Details
coverage/coveralls Coverage increased (+0.04%) to 33.961%
Details
umijs.umi Build #20190619.23 succeeded
Details

@delete-merged-branch delete-merged-branch bot deleted the feat/ssr branch Jun 19, 2019

@ycjcl868 ycjcl868 referenced this pull request Jun 19, 2019
12 of 19 tasks complete
@lilili001

This comment has been minimized.

Copy link

commented Jul 29, 2019

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

@sorrycc

@ycjcl868

This comment has been minimized.

Copy link
Member

commented Jul 29, 2019

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.