最近將之前的一個畢業設計的網站的文章詳情頁作了服務端渲染的重構,看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
(待續)