React-Express单页博客应用编写总结
很久之前就想写一个博客应用.
在一开始想要直接用express
和ejs
模板直接写, 但是暑假一开始的时候不小心入了react的坑, 所以就一不做二不休直接用react
写. 那既然用了react
, 不写个单页应用也过意不去了…(不
前前后后写了将近两个星期, 现在看来这其实是一个很容易的应用. 但是鉴于是第一次用react
, 对于nodejs
也不是特别熟悉, 所以走了不少冤枉路. 其中也有很多次觉得想放弃, 不过最终还是写下来了. 虽然还是有不少瑕疵, 不过也算给自己一个交代吧.
个人博客网站的地址为: harryfyodor.tk
下面会从几个方面把我整个编写的过程的一些经验记录下来, 主要是记录自己的学习过程, 编写中遇到的困难以及解决, 以及一些学习/复习资料的整理分享. 也希望给各位和我一样的初学者一点点借鉴的经验.
(个人感觉比较好的学习方式就是自己看资料(文档和博客)然后写自己的一个小应用, 而不是跟着某一个教程从头到尾过一遍, 虽然不得不承认, 后者学起来的感觉很爽, 而且也更清晰. 但是不好的地方就是技术栈不完全匹配的时候就会很头疼…)
下面是目录:
前端 (包括
react
,redux
,css-module
等)后端 (包括
express
,mongodb
)前后端 (包括
fetch
,jwt
)其他 (包括
webpack
, 优化等)
下面直接进入正题.
第一部分 前端
1, react
react
的学习根据官方文档, 主要是理解一下几个方面的内容:
构建模块的方法. 用了推荐
es6
的class
的方法而非createClass
.如何导入导出模块.
es6
的import
和export
.jsx
的编写. 不是必要的, 官网推荐. 感觉其中用上es6
模板字符串,map
方法会很方便很多~props
,state
,refs
的相关概念以及使用. 单向数据流中, 父组件给子组件传递数据通过props
, 子组件给父组件传递数据用回调函数. 后者的实现是通过父组件把一个函数传到子组件中, 这个函数里面有可以有this.setState
(收到子组件的数据后立刻渲染), 然后子组件调用传进来的函数, 通过这个函数把参数传给父组件.掌握
connect
组件的使用方法. 把state
数据和dispatch
传进组件中.生命周期. 在这个博客SPA应用里用了
componentDidMount
和componentWillReceiveProps
, 前者可用于初始化的渲染以及异步请求的发起, 后者用于接收到新的数据时的再次渲染, 把异步的结果渲染出来. 因此一个组件(涉及到异步)是这样工作的:调用组件
->组件渲染之前发起异步请求
->第一次渲染,没有数据的页面
->接收到异步发回来的数据
->重新渲染, 有数据的页面
. 代码如下, 基本也是按照这种数据流向的方法来的(不知道是不是最好的方案, 但是感觉很方便).class Archive extends React.Component { constructor(props) { super(props); this.displayName = 'Archive'; this.state = { articles: [] } } componentDidMount() { this.props.actions.getTitles({ type: "ARCHIVE" }) } componentWillReceiveProps(nextProps) { this.setState({ articles: nextProps.getTitles.articles }) } render() { return ( <div className={style.archive}> {/*这里是相关的渲染articles的操作, 注意要把[]的情况也考虑到*/} </div> ) }
}
export default Archive
配合好setState
和生命周期, 运用好父子组件之间的数据传递能够很好地完成各种异步渲染.
2, redux
理解redux
主要是要理解action
, reducer
, middleware
等概念. 个人感觉redux
的官方文档简直精彩, 例子也很丰富, 非常值得学习. 这个SPA
博客里的action
大部分是为了ajax获取后端数据服务的. 下面选取了其中的一组, 功能是获取单独的文章. 对应不同相应状态有不同的action
. 这样就可以把异步的每一个状态记录下来, 使得数据的流向更加清晰. 具体有关异步请求的相关的内容可以看我的上一篇文章.
export const singleRequest = () => {
return {
type: SINGLE_REQUEST
}
}
export const singleSuccess = (article) => {
return {
type: SINGLE_SUCCESS,
article: article
}
}
export const singleFailure = () => {
return {
type: SINGLE_FAILURE
}
}
export const getSingle = (day, title) => {
return dispatch => {
dispatch(singleRequest())
return fetch('/api/single', {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify({
day: day,
title: title
})
})
.then(checkHttpStatus)
.then(res => res.json())
.then(res => {
if(res.ok) {
dispatch(singleSuccess(res.article))
} else {
dispatch(singleFailure())
}
})
}
}
至于在reducer
中, 初始化state
用了几个标识. 比如下面的例子中, 初始化的reducer state
包含了isFetching
, isFetched
, fetchFailure
这些标志异步进行的当前状态的信息. 传入props
之后可以很方便地进行异步请求先后的设置. 比方说一个异步要在另一个异步之后, 就可以通过读取这几个数值完成. (第二个异步一定要在第一个的isFetched
为true
的时候才能发起)
const initialState = {
isFetching: false,
isFetched: false,
fetchFailure: false,
articles: [],
count: 1
}
在整个应用中需要用到中间件, 在应用中用了thunk
还有logger
.
3, css-modules
在博客应用中css
的引入用的是css-modules
, 阮一峰大神的这篇文章讲得算是完整了, 感兴趣可以看一下~ 当然有些部分也还是用了css in js
的方法, 直接把css
写到js
里面, 主要是考虑到一些操作的方便, 比如点击之后某一个标签display
改变之类的.
第二部分 后端
说来惭愧, 后端大部分都是”抄”的, 之前看的一个教程是用express
和ejs
写的博客应用, 而后端的操作大部分都比较接近. 主要就是根据接口路由处理数据, 发送数据, 通过数据库api
(这里是mongodb
)读取数据库数据. 所以最后写出来的和我原本看的那个教程有很大的相似之处. 我看的教程是这一个, 非常棒, 感谢作者!!>o<!!
1, express (nodejs)
关于express
个人感觉比较重要的是处理配置以及路由两个方面的问题.
前者需要靠自己慢慢摸索, 比如要处理json
需要用到bodyParser
模块, webpack
一些中间件的配置等等, 可以拿redux
官网还有上面提到的教程来参考一下.
后者主要是要了解express
提供的各种方法, 以及一些有关res和req的相关操作等等.
2, mongodb
有关数据库的操作我也是参考上面的教程的…(oh..)基本对数据库的增删查改要掌握. 更多有关mongo
的api
原理等可以去看官网介绍.
3, 不足之处
由于目标不是专业后端, 所以后端做得比较粗糙. 不足之处有很多, 比如没有拥抱es6
(明明前端已经拥抱), 比如还在若无其事地写着臭名昭著的回调金字塔等等等. nodejs
需要加强.
第三部分 前后端
1, 登录 (JSON Web Token)
有关登录和登出开始找了很多相关的实现方法, 在这篇文章的推荐下看了JWT实现方式. 简单来说就是前端把密码post
到后端, 后端生成一个token
然后发送到前端去. 前端把收到的token
保存在localStorage
中. 每次需要获取一些保密的信息或者需要做一些修改的时候, 把这个token
写在请求的headers
里. 后端收到数据之后就会先验证一下token
是否正确, 正确才允许操作.
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": `Bearer ${token}`
}
2,fetch (获取数据)
关于前后端交互这一点可以参考我写的上一篇文章. 后端把api
暴露出来给前端, 前端通过ajax
进行数据的交互, 并把获取到的数据渲染出来. 操作上没有难度, 只是要注意异步操作中redux
要用中间件.
有关中间件还要去看点高阶函数的基础知识, 不然无法正确理解.
第四部分 其他
1,webpack
没有怎么认真地看webpack
的东西, 都是顺手操起来直接用的…
说实话, 第一次开始看chrome
的devtool
的network
的时候, 我被吓得不轻…一个bundle
文件5m
大, PC
端打开之后真的是不忍直视. 后来上网找了一些webpack
打包优化的方向, 在这里记录一下:
webpack
的config文件里面不能有cheap-module-eval-source-map
之类的devtool
, 真的很大很大…plugin
如果不是必要的话也请删去吧. 不过有两个plugin
可以在生产环境中用一下, 第一个是UglifyJsPlugin
, 用于压缩文件. 第二个是CommonsChunkPlugin
, 这个具体下一点解释.适当分块.
CommonsChunkPlugin
用于把bundle分块, 把可以放在缓存的, 常用的, 体积比较大的压缩到vendor
里面(比如react
等). 后来又把babel-polyfill
分开另外加载了. 之前有看到code split
, 就是直到需要用到该ui
组件的时候才去加载, 想法好像不错, 不过感觉改动会比较大所以最后没有做.
最后的文件大小其实也还是不小, 但是有了很好的改善. 关于前端优化也是一个重要的话题.
2,markdown
博客应用写作用的是markdown
. 原本想找一个现成的, 但是死活找不到合适的…最后直接用marked
强行伪装markdown
编辑器…其实这很不安全, 但目前也没有什么办法…(draft.js
貌似可以?)
总结
总体来说, 这个博客其实实现起来没有特别高的难度, 但是对于初学者来说感觉真的挺不容易的. 之前听过这样一句话–不要同时学几样东西, 其实还真的有点道理…但是对于一些最佳实践, 本身就要结合在一起才能发挥其最大的作用, 不一起学又怎么能行呢?(因此就陷入了大坑).
这个博客不完善的地方太多了, 特别是有关安全方面的问题.不过现在还是先关注着前端吧.
希望这篇文章能够给你一点点帮助.
最后上代码博客代码
(本人是初学者, 如果有什么说得不对, 不好的地方欢迎指出来, 感激不尽!~互相学习!~)