最近将之前的一个毕业设计的网站的文章详情页作了服务端渲染的重构,看SSR的实现文档看似很简单,可是实现起来确实坑很多。css
unexpected token import
import Story from '../js/containers/story'
;就会报这个错误。commonjs
的语法,支持的模块引入和导出方式为require
以及module.export
,然而es6定义的js模块方式为import
和export[default]
,所以node虽然支持了大部分的es6语法,可是因为es6的模块与node自己的cjs的模块产生了冲突,所以node不会支持esm的模块,所以形成了没法识别import
的状况。.babelrc
文件,为了解决这个问题,同时也因为react
和node
的差别愈来愈大,最后决定拆分.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
的问题。html
$npm i babel-cli --save
前端
而后改写nodemon.json
文件:node
// nodemon.json
{
"verbose": false,
"env": {
"NODE_ENV": "development",
"BABEL_ENV": "node"
},
"watch": ["node", "config"],
"ignore": ["public"],
"execMap":{
"js": "babel-node"
}
}
复制代码
而后再次启动node服务器就能够发现node正常识别import了。react
window is not defined
// 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的方式获取数据,从而解决这个问题。webpack
cannot find module 'components/xxx'
webpack
配置的alias
路径,作ssr时node就会报这个错误。alias
时,咱们能够在组件中简写路径,可是在node中没法识别webpack的alias
,因此这种路径node会从node_modules
中寻找这个组件,找不到就会报错。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同样。git
/Users/xxx/xxx/node_modules/antd/lib/style/index.css:6
antd
组件,或者引入了咱们本身的scss
文件时,会报这个错误。loader
进行处理,但这写loader
只是针对于客户端环境的,编译生成的代码,没法应用于服务端,所以node没法解析scss
、less
等文件。// 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进行首屏渲染的时候,是没有样式的,这就致使当客户端开始加载样式以后,会形成页面样式抖动的问题。es6
为此咱们经过编写webpack插件,将ExtractTextPlugin
生成的css文件,内联插入页面的pug模板中,这样服务端首屏渲染就能够支持样式了。web
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object
export default class xxx extends Component
的方式导出组件,在node中必需要经过const Component = require('....').default
的方式才可以正确获取到组件,你们能够本身console.log
一下,直接require
进来的是一个object
,里面的default
属性才是咱们的组件。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引入。npm
(待续)