React构建个人博客

前言

在学习react的过程中,深深的被react的函数式编程的模式所吸引,一切皆组件,所有的东西都是JavaScript。React框架其实功能很单一,主要负责渲染的功能,但是社区很活跃,衍生出了很多优秀的库和工具。个人觉得,想要做好一个项目,往往需要其他库和工具的配合,例如redux管理数据,react-router管理路由等,掌握基本的webpack配置es6语法,然后想要提高性能,还有配合react的钩子函数和immutable.js,什么时候组件不需要重新渲染,next.js服务端渲染等等...
一直有一个想法就是重构自己的博客,刚好这段时间放假,又刚好学习了react,于是就有了这个项目。

项目地址https://github.com/k-water/react-blog
如果觉得不错的话,您可以点右上角 "Star" 支持一下 谢谢! _

技术栈

前端

  • react
  • react-redux
  • react-thunk
  • react-router
  • axios
  • eslint
  • maked
  • highlight.js
  • antd
  • es6/7/8

后台

  • spring boot

此项目采用前后端分离的实现,后台接口基于RESTful规范设计,只提供数据,前端负责路由跳转,权限限制,渲染数据等。PS:由于我是个前端er,所以这里主要讲的是前端。

实现的功能

  • [x] admin增删查改博客
  • [x] 博客标签
  • [x] 博客内容markdown
  • [x] 博客内容页展示目录
  • [x] 返回顶部
  • [x] markdown代码高亮
  • [x] 用户登录注册
  • [x] 用户评论
  • [x] 响应式

TODO

  • [ ] 博客分类
  • [ ] 点击标签搜索相关博客
  • [ ] 优化首页侧边栏
  • [ ] 完善归档

效果预览

首页

image

内容页

image

用户登录

image

用户评论

image

后台管理

image
image
image

个人总结

markdown渲染

在前端渲染markdown的时候遇到了一点问题,相关的包很多,但是各种包解析的结果都有差异,react周边社区推荐的是react-markdown,使用方法也很简单

import ReactMarkdown from 'react-markdown'

const input = '# This is a header\n\nAnd this is a paragraph'
ReactDOM.render(
    <ReactMarkdown source={input} />,
    document.getElementById('container')
)

但是发现react-markdown对表格的支持不太友好,最后采用了marked,结合highlight.js对代码部分实现高亮

import marked from 'marked'
import hljs from 'highlight.js'
  componentWillMount() {
    marked.setOptions({
      highlight: code => hljs.highlightAuto(code).value
    })
  }

最后解析出来的是一个字符串,还需要将它插入dom中,由于安全问题,React不提倡将字符串直接插入dom中,但React保留了一个API,可以这样做:

<div className="article-detail" 
  dangerouslySetInnerHTML={{ __html: marked(output)) }} />

React组件化

react的组件由dom视图和state组成,state是数据中心,它的状态决定着视图的状态。react只负责UI的渲染,与其他框架监听数据动态改变dom不同,react采用setState来控制视图的更新。setState会自动调用render函数,触发视图的重新渲染,如果仅仅只是state数据的变化而没有调用setState,并不会触发更新。说到组件,就必须了解react组件的生命周期,官方的图解如下:

image

关于这部分的解释网上有很多,可以自行查阅。而我在开发过程用的最多的就是

  • componentWillMount()
  • componentDidMount()
  • shouldComponentUpdate(nextProps, nextState)
    这几个钩子函数了,关于性能优化,可以在shouldComponentUpdate上作文章,由于shouldComponentUpdate默认返回true,简单的方法可以通过比较更新前后的数据结构是否相同来判断组件是否需要重新渲染,这时候就可以采用immutable.js了。

组件之间通信

react是单向数据流,自上而下的传递数据。解决复杂组件之间通信的方法有很多。一般父子组件通信是最简单的,父组件将一个回调函数传递给子组件,子组件通过this.props直接调用该函数与父组件通信。

如果组件之间嵌套很深,可以使用上下文getChildContext来传递信息,这样在不需要将函数一层层往下传,任何一层的子级都可以通过this.context直接访问,react-redux内部实现就是利用此方法。

兄弟组件之间无法直接通信,它们需要利用同一层的上级作为中转站。

Redux

redux不是必须的,如果不是复杂的组件通信,逻辑简单,用context就行。redux并不是react特有的,其他框架也可以使用redux。当初为了学习redux花费了不少时间,一开始并不理解redux中间的操作,看了很多前辈们写的文章才逐渐明白。简单说说redux。
redux由三部分组成:store, reducer, action

image

store是一个对象,它主要由三个方法:
dispatch
用于action的分发,当action传入dispatch会立即执行,有些时候我们不想它立刻触发,可以在createStore中使用middleware中间件对dispatch进行改造,例如redux-thunk,不过这是react-radux做的事了。
subscribe
顾名思义,监听器,监听state的变化,这个函数在store调用dispatch时会注册一个listener监听state变化。
getState
获取store中的state,当我们用action触发reducer改变了state时,需要拿到新的state里面的数据。getState在两个地方会用到,一是通过dispatch提交action后store需要拿到state里面的数据,二是利用subscribe监听到state发生变化后调用它来获取新的state数据。

说了这么多,store的核心代码其实很短:

/**
 * 应用观察者模式
 * @param {Object} state
 * @param {Function} reducer
 */
function createStore(reducer) {
  let state = null
  const listeners = []
  const subscribe = listener => listeners.push(listener)
  const getState = () => state
  const dispatch = action => {
    // 覆盖原对象
    state = reducer(state, action)
    listeners.forEach(listener => listener())
  }
  // 初始化state
  dispatch({})
  return {
    getState,
    dispatch,
    subscribe
  }
}

另一部分,reducer是一个纯函数(pure function),它接收一个state和action作为参数,根据action的type返回一个新的state,如果传入的action type没有匹配到,则返回默认的state,简单实现如下:

function reducer(state, action) {
  if (!state) {
    return {
      title: {
        text: "water make redux",
        color: "red"
      },
      content: {
        text: "water make redux",
        color: "green"
      }
    }
  }
  switch (action.type) {
    case "UPDATE_TITLE_TEXT":
      return {
        ...state,
        title: {
          ...state.title,
          text: action.text
        }
      }
    case "UPDATE_TITLE_COLOR":
      return {
        ...state,
        title: {
          ...state.title,
          color: action.color
        }
      }
    default:
      return state
  }
}

action比较简单,它返回一个对象,其中type属性是必须的,同时也可以传入一些其他的数据。
使用例子如下:

/ 生成store
const store = createStore(reducer)
let oldState = store.getState()
// 监听数据变化重新渲页面
store.subscribe(() => {
  const newState = store.getState()
  renderApp(newState, oldState)
  oldState = newState
})
// 首次渲染页面
renderApp(store.getState())
store.dispatch({
  type: "UPDATE_TITLE_TEXT",
  text: "water is fighting"
})
store.dispatch({
  type: "UPDATE_TITLE_COLOR",
  color: "#f00"
})

React-redux

react-redux则是对redux做了封装,可以在react中直接使用,并且提供了Providerconnect
Provider是一个组件,它接受store作为props,然后通过context往下传,这样react中任何组件都可以通过context获取store。
connect是一个函数,也是一个高阶组件(HOC),通过传入state和dispatch返回一个新的组件,它的写法是如下:

connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(component)

也可以采用装饰器的写法,这需要babel的支持:

@connect(
    state,
    { func }
)

具体的不多介绍,迷你实现可以看看这个项目:https://github.com/k-water/make-react-redux

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容