React SSR重构踩坑记录(持续更新)

栏目: 编程语言 · 发布时间: 6年前

内容简介:最近将以前的一个毕业设计的网站的文章详情页做了服务端渲染的重构,看SSR的实现文档看似很简单,但是实现起来确实坑不少。可以让node识别es6的语法。 然后在根目录重新创建

最近将以前的一个毕业设计的网站的文章详情页做了服务端渲染的重构,看SSR的实现文档看似很简单,但是实现起来确实坑不少。

无法使用import引入

  1. 错误信息: unexpected token import
  2. 场景:第一次在node中直接使用 import Story from '../js/containers/story' ;就会报这个错误。
  3. 错误说明:node本身使用的是 commonjs 的语法,支持的模块引入和导出方式为 require 以及 module.export ,然而es6定义的js模块方式为 importexport[default] ,因此node虽然支持了大部分的es6语法,但是由于es6的模块与node本身的cjs的模块产生了冲突,因此node不会支持esm的模块,因此造成了无法识别 import 的情况。
  4. 解决办法:以前整个项目中node和react使用同一个 .babelrc 文件,为了解决这个问题,同时也由于 reactnode 的差异越来越大,最后决定拆分 .babelrc 文件,在 /node (后端)目录以及 /public (前端)目录下分别创建 .babelrc 文件,作为前端和后端各自的babel配置。 其中node的 .babelrc 文件配置中的:
// .babelrc
"presets": [
    [
        "env",
	{
	    "targets": {
		"node": "current"
	    }
	}
    ],
"react",
"es2015",
"stage-0"
]
复制代码

可以让node识别es6的语法。 然后在根目录重新创建 nodemon.json 文件用来处理 import 问题。 上网查资料, babel-node 插件可以解决不识别 import 的问题。

$npm i babel-cli --save

然后改写 nodemon.json 文件:

// nodemon.json
{
  "verbose": false,
  "env": {
    "NODE_ENV": "development",
    "BABEL_ENV": "node"
  },
  "watch": ["node", "config"],
  "ignore": ["public"],
  "execMap":{
    "js": "babel-node"
  }
}
复制代码

然后再次启动node服务器就可以发现node正常识别import了。

无法访问window对象

  1. 错误信息: window is not defined
  2. 场景:js文件中一开始使用了很多window.xxx的属性,import到node环境中之后就会报这个错误。
  3. 说明:服务端缺乏BOM和DOM环境,服务端下无法访问window,navigator等对象。
  4. 解决办法:针对此种错误,有三种解决办法:
    1. 通过fake window等对象(如window等库)的使用,给node环境创建全局window对象。
    2. 前端组件中延迟这些对象的调用,在didMount中才进行调用。
    3. 将组件中的所有用window的属性,都通过props的方式获取,然后将所有应该传入组件的props属性在node中传进组件。
// storyController.js
const props = {
  userInfo: ctx.session,
  articleInfo: {
    author: author[0].nickname,
    avatar: author[0].avatar,
    author_fans_count: fans_count[0].count,
    ...info[0]
  },
  isSelf: info[0].uid === ctx.session.uid
};
const html = renderToString(<Story {...props} />);
ctx.render('story', {
  __PROPS__: JSON.stringify(props),
  title: info[0].title,
  html
});
复制代码
// pages/story.js
render(
  <Story {...window.__PROPS__} />,
  document.getElementById('root')
);
复制代码

这样在组件中就可以通过props的方式获取数据,从而解决这个问题。

无法访问alias路径

  1. 错误信息: cannot find module 'components/xxx'
  2. 场景:在组件中使用了 webpack 配置的 alias 路径,做ssr时node就会报这个错误。
  3. 错误说明:当在webpack中配置 alias 时,我们可以在组件中简写路径,但是在node中无法识别webpack的 alias ,所以这种路径node会从 node_modules 中寻找这个组件,找不到就会报错。
  4. 解决办法:解决办法自然是node也找一个alias的库: module-resolver 库可以完美解决这个问题。 $ npm install --save-dev babel-plugin-module-resolver 安装完成之后,改造一下node下面的 .babelrc 文件即可:
// .babelrc
"plugins": [
	["module-resolver", {
		"cwd": "babelrc",
		"root": ["../public/js"],
		"alias": {
			"scss": "../public/scss",
			"components": "../public/js/components",
			"containers": "../public/js/containers",
			"constants": "../public/js/constants",
			"lib": "../public/js/lib",
			"router": "../public/js/router",
			"stirngs": "../public/js/string.js",
			"store": "../public/js/store"
		}
	}]
]
复制代码

其中的alias和webpack中的alias一样。

无法引入静态资源

  1. 错误信息: /Users/xxx/xxx/node_modules/antd/lib/style/index.css:6
  2. 场景:组件中使用了 antd 组件,或者引入了我们自己的 scss 文件时,会报这个错误。
  3. 错误说明:客户端通常使用webpack进行编译,资源的加载通过各种 loader 进行处理,但这写 loader 只是针对于客户端环境的,编译生成的代码,无法应用于服务端,因此node无法解析 scssless 等文件。
  4. 解决办法:我们只要让node不解析这些样式文件即可。 在node入口文件app.js中最上方加入以下代码:
// app.js
require.extensions['.scss'] = function() {
  return null;
};
require.extensions['.css'] = function() {
  return null;
};
require.extensions['.less'] = function() {
  return null;
};
require.extensions['.png'] = function(module, file) {
  return module._compile('module.exports = ""', file);
};
require.extensions['.svg'] = function() {
  return null;
};
复制代码

这样node就可以正常运行了,但是同时又暴露出了一个问题,当node进行首屏渲染的时候,是没有样式的,这就导致当客户端开始加载样式之后,会造成页面样式抖动的问题。

为此我们通过编写webpack插件,将 ExtractTextPlugin 生成的css文件,内联插入页面的pug模板中,这样服务端首屏渲染就可以支持样式了。

require方式引入组件报错

  1. 错误信息: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object
  2. 场景:node中使用require引入组件,然后传入renderToString会报错。
  3. 错误说明:这个错误涉及到esm和cjs交互的问题,我们通过require引入的东西和我们通过import引入的并不一样,具体原因可以参考另一篇文章深入解析ES Module。
  4. 解决办法:
    1. 我们在组件中通过 export default class xxx extends Component 的方式导出组件,在node中必须要通过 const Component = require('....').default 的方式才能够正确获取到组件,大家可以自己 console.log 一下,直接 require 进来的是一个 object ,里面的 default 属性才是我们的组件。
    2. 安装 babel-plugin-add-module-exports 插件。 $ npm install babel-plugin-add-module-exports@next --save-dev 然后改写react中的 .babelrc 文件:
// .babelrc
"plugins": [
    ...
    "babel-plugin-add-module-exports"
]
复制代码

这是个比较hack的方法,强行将esm和cjs的表现置为相同,但是可能会出现问题,所以尽量不要将esm和cjs混用,在node中直接使用import引入组件最好,不要用require引入。

(待续)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Impractical Python Projects

Impractical Python Projects

Lee Vaughan / No Starch Press / 2018-11 / USD 29.95

Impractical Python Projects picks up where the complete beginner books leave off, expanding on existing concepts and introducing new tools that you’ll use every day. And to keep things interesting, ea......一起来看看 《Impractical Python Projects》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试