系统目录及源码由此进入css
1.1 技术选型
1.2 总体设计
1.3 构建开发html
2.1 react
2.2 redux, react-router
2.3 server-render前端
对于我的的博客系统而言,服务器计算能力每每不是须要考虑,而其中的 I/O 操做是比较复杂的,同理对前端的交互要求也是较高的,因此此次主要仍是围绕 Node系
,React系
框架进行开发。对于 2016 年后的互联网产品, React
搭建的 SPA ( single page ) 以及 React-Native
搭建的相似 hybrid 的原生应用,这样的开发模式,会引导从此 3 年产品的开发。node
图描绘的很简单,简单来讲前端采用 React + React-router + Redux 以 flux 模式进行开发,后端采用了两个 Node Server 提供 React 服务器端渲染和数据提供,这里能够先看为一个服务器,为啥要采用两个,后面会提到。若是你有开发先后端分离项目的经验,你能够将提供 React 服务器端渲染这一块与前端合并,咱们能够在上面书写 xtpl , ejs , jade 这些模板,开发起来更为流畅。react
因为个人服务器是在华北地区,加上 React 这一系列的库又都是很是大的,即便我将 js , css 文件都打包成一个文件,访问起资源加载也是特别慢,因此 CDN 也是必不可少的。webpack
对工程完成大体设计后,咱们须要利用前端工具作一些利于开发的构建,能够大体分为如下 3 个任务。git
method1: 本地服务器的热加载
github
开发静态组件阶段为了提高开发效率,减小各位小白对 F5 的使用量,咱们通常使用 webpack-dev-server
作本地服务,与通常的 webpack 配置文件不一样之处也就在于入口点 entry 多加了点东西。web
entry: { app: [ 'webpack-dev-server/client?http://localhost:3000', 'webpack/hot/only-dev-server', path.resolve(__dirname, '../views/index.js') ] }
将 webpack 中的选项传入到 webpack-dev-server (一个 express 服务器) 对象数据库
new WebpackDevServer(webpack(webpackConfig), { publicPath: '/', hot: true, // 热替换开启 historyApiFallback: true, // 无刷更改url,配合react-router使用 inline: true, // 热替换开启 stats: { colors: true } }).listen(PORT, FUNC);
接着启动你的服务器便可完成构建。
method2: 静态资源分离
一般在开发阶段会有 import ./xxx.less
或 require('xxx.png')
ES5 / ES6 语法来引入静态资源, 在打包时咱们须要把全部静态文件和咱们的组件解耦在合并成一个文件,这一样须要 webpack 来完成,同时要引入名为 extract-text-webpack-plugin 的插件,具体使用以下
var ExtractTextPlugin = require('extract-text-webpack-plugin'); module.export = { module: { loaders: [ ... // other settings { test: /\.css/, loader: ExtractTextPlugin.extract('style', ['css']) }, { test: /\.less/, loader: ExtractTextPlugin.extract('style', ['css', 'less']) }, ] }, plugins: [ new ExtractTextPlugin('[name].css'), ... // other settings ] }
method3: 服务器端渲染
首先,明确一点的是 node 稳定的 4.x 版本并不能支持全部 ES6 的语法,所以采用 ES6 的语法写 node 服务器端,须要先转到 ES5 再执行,通常来讲有两种方法。一是下载 babel-node
到操做系统直接执行,二是利用 webpack 引入 babel-loader
,babel-core
打包(其实两种方法是一种思想)。因此按照正常 ES6 -> ES5 的构建流程打包便可。
module.export = {
node: { fs: 'empty', net: 'empty', // 防止打包错乱 __filename: true, __dirname: true } }
若是打包过程当中出现了大量有关 fs , net 模块的报错或者打包后出现了文件引入的错误,添加上述 webpack 选项便可解决问题,这两种都是在 node 端打包常常出现的问题。
先来看看整个工程目录是怎样的。
build: 上述中各构建文件
server: 用于服务器端渲染的文件
views: 前端组件及redux状态管理文件系统
对于 react 语法这里就不提了,重点提下文件的组织方式,以便咱们开发和平常维护。若只针对于工做量不大 react ,目录很简单,只需区分展现级组件
( compnent ) 和 复合型组件
( containers )。
--- views/
--- components/ 各展现级组件
--- containers/各复合型组件
具体呈现形式可在 github 上了解
刚开始写 react 的时候老是以为 redux 和 react-router 是一个哗众取宠的存在,直到数据量和逻辑变复杂,后台逻辑越发复杂,而前端的 react 组件仍是如同开始的上手练习同样,一直作着自顶向下的数据传递和渲染,每次更新数据咱们都会请求服务器,这无疑增长了服务器的压力(对较大型应用),同时没有发挥react的长处,所谓的单页应用更是纸上谈兵。
那将三者结合会是怎样的开发体验和用户体验呢?
redux
上图是 facebook 提出 flux
架构思想,是专门用于软件的结构问题的,对于 react 开发它提供一种很是简单清晰的思想。
View
: 视图层 ( react 组件)Action (动做)
: 视图层发出的消息(好比 mouseClick )Dispatcher (派发器)
: 用来接收 Actions 、执行回调函数Store (数据层)
: 用来存放应用的状态,一旦发生变更,就提醒 View 要更新页面
而 redux 提供了 action , dispatcher( redux 中是 reducer ), store 的实现 ,如何实现的呢?就拿个加载某一页数据作个例子。
action.js : loadThisPage 是一个action creater,每次调用就能建立一个带有 LOAD_THIS_PAGE 指令的对象给 reducer。
const LOAD_THIS_PAGE = 'LOAD_THIS_PAGE'; const loadThisPage = (pN) => { return { type: LOAD_THIS_PAGE, pN } }
reducer.js : 在 reducer 中,经过判断指令来调用 store 中的数据改变状态
const LOAD_THIS_PAGE = 'LOAD_THIS_PAGE';
const articleReducer = (state = {}, action) => { switch(action.type) { case LOAD_THIS_PAGE: return { allArticles: state.allArticles, articles: state.allArticles.slice(0, action.pN * 5) }; default: return state; } };
store.js : 建立一个 store 对象,能够经过调用这个对象的 dispatch
方法传入 action。
import { createStore, applyMiddleware} from 'redux'; const store = createStore( reducers, applyMiddleware(thunk) // 若须要redux中间件才添加 );
这些工做作完后,还须要将 store 和组件进行一个关联,也就是说组件要能得到 redux store。
import { Provider, connect } from 'react-redux'; // 经过connect链接组件和store const HomeApp = connect( (state) => { return { articles: state.articleReducer.articles, articleLen: state.articleReducer.allArticles.length }; } )(Home); const routes = ( <Route path="/" component={ LeftArea }> <IndexRoute component={ HomeApp } /> </Route> ); // store经过Provider传入到react组件 render( <Provider store={store} key="index"> <Router history={browserHistory} routes={routes}> </Router> </Provider>, document.getElementById('app') );
react-router
相比 redux , react-router 理解起来就好多了,每当 url 发生变化,改变的只是组件,而不会从新刷新页面。
对于这样一个SPA,用户访问只需加载一次数据,以后的逻辑都由浏览器来完成了~因此对交互性来讲也是很大的提高。
为何须要去实现 server render ? 也就是说服务器端渲染比客户端渲染优在哪里?
server render vs client render
服务器端渲染更利于 SEO ,更容易被爬虫识别
服务器端渲染能够增长访问速度,用户访问时直接推送已经渲染好的 react 页面,不一样于客户端渲染的将 react 放入浏览器中渲染。同时减小用户CPU开销,得到更好交互性,解决了首屏加载慢的问题。
分离先后端逻辑,利于开发
如何作到 react 服务器端渲染,其实 facebook 很早就给出了解决方案,并推出了 react-dom/server 库。
import { renderToString } from 'react-dom/server'; const html = renderToString( <Provider store={store}> <RoutingContext {...renderProps} /> </Provider> );
最初的一个问题
为何会采用两个node服务器呢?由于在进行 webpack 对 Node 的打包时,会将数据库的相关信息打包到一块儿,放在网络上是不安全的,因此须要建立另外一个服务器专门用提供持久化数据服务。
这篇博文没有对技术作太多深刻的探究,但提供了一个比较清晰地开发思路,刚从事开发工做或者仍是小白的你可能在独立开发一个工程时会存在不少疑问,从构建到设计,从性能到安全都是须要考虑的。
今天微信小程序也正式上线了,赶忙去尝尝鲜~