12月21, 2016

Clean Blog by Node && React 5

前言

过渡

上篇已经完成了文章详情接口的书写,但是,由于我们提交带服务器,服务器保存到数据库中的字符串是markdown格式的,浏览器不认识,因此,在res.send(doc)之前,需要把doc中保存的字符串解析为html格式,暴露给前端。下面,着重记录这个过程。

开始实战

markdown库的选择

https://github.com 上有许多可供选择的库。下面列出两个解析内容比较正确的库。

我个人比较喜欢marked这个库,相对比较准确一点。下面我将着重记录marked这个库的使用。两者的安装方式都是一个套路。如果需要代码高亮显示,还需要安装highlight.js,并且引入其css.

npm install highlight.js
npm install markdown
npm install marked

marked

来到详情接口的文件中/routes/index.js.添加依赖。

var marked = require("marked");
var highlightjs = require("highlight.js");

在查询到数据库数据之后返回数据之前,使用marked()方法,将markdown的字符串解析成html字符串。

if(doc) {
    marked.setOptions({
      renderer: new marked.Renderer(),
      gfm: true,
      tables: true,
      breaks: false,
      pedantic: false,
      sanitize: false,
      smartLists: true,
      smartypants: false,
      highlight: function (code) {
        return highlightjs.highlightAuto(code).value;
      }
    });
    doc.content = marked(doc.content);
}
res.send(doc);

这里的setOption,是最基本的配置(默认配置),其中,highlight属性是一个回调函数,主要作用就是代码高亮。

在这里,我使用的主题为tomorrow

<link rel="stylesheet" href="/styles/tomorrow.css">

效果如下(背景图片仿照的mozilla

code preivew

markdown

使用markdown的话,可以只需要一行代码就可以实现。

doc.content = markdown.toHTML(doc.content);

但是他的解析不是很准确,生成的标签样式也很难控制,所以决定弃用它。

react中html字符串

现在我们获取到了解析后的html字符串,但是不可以在reactcomponent中直接输出,如果直接在component中当做一个变量去使用,就会发现你的tag都没有被浏览器解析,当做普通的字符串去处理了。react不支持这个是有一定道理的,包括angular中类似拼接html的方式也会得到警告。

一般来说,从脚本拼接HTML是有风险的,因为很容易无意中受到cross-site scripting (XSS)攻击。

但是作为一个静态博客而言,没有用户信息,一般也不会有这方面的影响,因此我们使用dangerouslySetInnerHTML这个属性。

{
    model.content ? 
    <div dangerouslySetInnerHTML={{__html: model.content}}></div> :
    <div><Loading/></div>
}

这样浏览器就可以正常解析HTML代码了。

Loading

可以看到,上面我使用了一个<Loading/>组件,该组件为数据还未请求到时显示。用法很简单,每次在输出内容是检查该内容是否为空(即是否请求到),如果没有,则显示改组件,否则显示内容。效果如下。

我这里在三元表达式中判断完成后,返回组件的时,并没有使用return。这是因为在react中,组件——其实是一种表达式的存在。可以观察babel转义后的代码,react调用了createElement这个方法去生成它的虚拟DOM。

如果直接看babel简单来演示转义后的代码就可以很明白的看到这个原理。这里我在babel/repl中简单演示下。

表达式 ?== 组件 (原理演示)

浏览器打开 https://babeljs.io/repl,建立两个`components`

import React, {Component} from "react";
class Loading extends Component {
  render() {
    return (
      <div>Loading...</div>  
    )
  }
}

export default class App extends Component {
  render() {
    let a = -1;
    return (
        a > 0 ? "data ready" : <Loading/>
    )
  }
}

假设以变量a为数据请求的标志

  • a > 0表示数据请求完成,显示内容
  • a < 0 表示数据还未请求到,显示Loading

这时,看右边实时转义后的代码。

...
var App = function (_Component2) {
  _inherits(App, _Component2);

  function App() {
    _classCallCheck(this, App);

    return _possibleConstructorReturn(this, (App.__proto__ || Object.getPrototypeOf(App)).apply(this, arguments));
  }

  _createClass(App, [{
    key: "render",
    value: function render() {
      var a = -1;
      return a > 0 ? "data ready" : _react2.default.createElement(Loading, null);
    }
  }]);

  return App;
}(_react.Component);
...

这是App Component中的内容,注意这几行代码

 _createClass(App, [{
    key: "render",
    value: function render() {
      var a = -1;
      return a > 0 ? "data ready" : _react2.default.createElement(Loading, null);
    }
  }]);

react在引入<Loading/>组件时,创建了一个叫loading的元素。这样就可以理解,为什么不需要return一串html

总结

以上内容,包括前4次的记录,总计5篇内容,已经将项目的所有基础内容涵盖。目前已经在 https://beace.tech 上发布。

有以下几个关键知识点

  • node/expressjs 后端项目结构的构建
  • react/webpack 前端项目结构的构建
  • 上述两者结合的项目整体架构
  • expressjs + mongodb的增、删、改、查(其实获取详情也是一种查的基础体现)的实现
  • expressjs增、删、改、查API 的书写
  • markdown的使用
    • 编辑器的使用
    • markdown to html
    • react/browser string to html
  • 以及一些细节
    • loading的实现
    • 部分原理的讲解

本文链接:https://beacelee.com/post/clean-blog-react-node-5.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。