Rephic 是一个基于 Koa 的 React 服务端渲染模板。支持 React-Router, Redux 以及 Less, Sass。
- Node.js >= 7.9
- PM2(用户生产环境)
你可以通过 smarter 脚手架生成工具来安装该模板:
$ sudo npm i -g smarter # 全局安装 smarter
$ smarter init rephic project # 生成项目到 project 目录下
$ cd project
$ npm i # 使用 npm 来安装项目依赖
或者直接 clone 该项目:
$ git clone https://github.com/pspgbhu/rephic.git
$ cd rephic
$ rm -rf .git && git init # 重新初始化 git 仓库
$ npm i
下面只列出了几个需要重点关注的目录文件
.
├── bin # 项目脚本文件
│ └── www # 服务端启动脚本
├── client # 客户端专用代码
│ ├── build # Webpack 配置
│ ├── entry # 入口文件目录,包含客户端和服务端的入口
│ ├── store # Redux 相关文件
│ └── App.jsx # React 根组件
└── server # 服务端专用代码
├── controller # 控制器层
├── service # 服务层
├── middlewares # 中间件
├── public # 静态资源
├── utils # 工具函数
├── views # 渲染模板
├── app.js # Node 服务入口文件
└── router.js # 路由
# 启动 Node 服务,支持 Node, jsx, less 的热更新
npm run dev
# 开发环境下打开页面,页面会在最开始一瞬间没有加载样式。
# 这个请不要担心,在生产环境下不存在这个问题。
开发阶段 webpack 会将静态资源打包至 node_modules/.cache/rephic/public/
文件夹下。且开发环境下该文件夹和 /server/public
均为静态资源文件夹,且该文件夹下的资源匹配的优先级更高。
而生产环境下,/server/public
将是唯一的静态资源文件夹
上面我们提到,开发环境下打开页面,页面会在最开始一瞬间没有加载样式,在开发环境下不会出现。原因具体可以参考这里;
如果你还是担心线上生产环境的运行效果,可以使用下列命名来快速的已生产环境来运行代码:
npm start # 生产环境预览。仅用于预览,请勿作为生产环境运行。
npm run build # 构建生产环境静态资源,将会更新 /public 文件下的资源
npm run prd # 启动 pm2
服务端中 React 的生命周期只走到了 componentWillMount()
。但是在客户端 React 拥有着完整的生命周期。
/common
中的同构代码在 Node 环境和浏览器环境下都会执行一遍,因此一定要注意的是,Node 没有 window
document
等对象以及相关 API,如果在 Node 环境下执行了便会报错。
但是我们在很多场景下还是需要用到 DOM 和 BOM 的 API,这里还是有一些处理技巧的:
- Node 中 React 生命周期只会走到
componentWillMount
,因此我们可以将一些 DOM 操作放在该生命周期之后的一些钩子中,比如在componentDidMount
中来执行 DOM 或 BOM 的 API。 - 或者,我们可以使用一些安全判断来保证只在浏览器环境下才执行 DOM 操作。
// node 中没有 document 全局对象,因此便不会执行括号中的代码。 if (document) { document.querySelector('#example').classList.add('hide'); }
生产环境静态资源目录
/server/public
开发环境静态资源目录
-
/node_modules/.cache/rephic/public
开发环境下的临时资源都会被打包进这个目录。静态资源会优先匹配这个目录下的文件。 -
/server/public
开发环境下,仍会加载这个目录的资源,只不过优先级较低。
/common
是 React 的同构文件夹,全部的 React 逻辑都应该写在这个文件中,并将 /common/App.jsx
作为同构部分的入口文件。
我们需要将将全部的组件都写在 /common
文件夹下,写组件时还是依照之前的纯前端开发方式,然后将 /common/App.jsx
作为根组件即可。
请使用 require
而不是 import
来引入样式文件。
由于服务端无法处理也无须处理样式文件,我们便使用了 babel-register 的插件 babel-plugin-transform-require-ignore 来忽略掉 ['.css', '.less', '.sass', '.scss']
这些后缀的文件。由于该插件的限制,我们必须要使用 require
命令来引入样式文件才能使这个插件正常工作。
在打包前端静态资源的时候,webpack 的 loader 会去处理对应的样式文件,目前默认支持 Less 和 Sass 样式处理器。
如果不准备使用 Redux 的话,只需要在 /common
中写好 React Router 的逻辑即可。
搭配 Redux 使用的话,请先参考 Redux 服务端渲染。
/client/index.jsx
作为浏览器环境下的入口文件,初始化了客户端的 Redux 初始数据。
服务端的 Redux Store 数据首先在 koa-router 中被创建,然后在 /server/utils/render.jsx
中被传递进 /common/App.jsx
组件中,然后 React 在服务端中便能像在前端中一样使用 Redux 中的数据了。
// /routes/views/index.js
router.get('*', filterPageRoute, async (ctx) => {
//..
// 在服务端创建 Redux Store
const store = createStore(reducer, ctx.reactState || {}, applyMiddleware(thunk));
// 通过 renderStaticHtml 方法来讲 store 注入到 React 组件中
const content = renderStaticHtml({ ctx, store, context });
//..
});
Nodemon 是一款非常实用的 Node.js 开发工具,它能够用来监控 Node.js 源代码的任何变化和自动重启你的服务器。这里有几个技巧或许可以帮到你。
- nodemon 启动后,在终端再次输入 rs 命令可以强制重启 Node.js 服务
在本地开发时,CSS 样式是通过 webpack 的 style-loader 打包在 JS 静态资源中的。待到 JS 静态资源在浏览器端执行时才将 JS 中的样式作为样式表通过 style 标签插入页面中的。
在浏览器请求回 HTML 文档,DOM 解析完毕后,一直到 JS 将样式表插入到页面中的这一段时间,页面会产生暂短的没有样式的情况。这是开发环境下特有的一种情况。
在生产环境下,webpack 会将 CSS 作为单独的一个文件打包出来,因此生产环境下不会有这个问题。
参考 注意事项 - Node 环境中是不具备 DOM 和 BOM 相关API
目前是用 nodemon 来检测 js 变动来重启 node 服务。目前并没有计划加入浏览器自动刷新的功能。