如何快速设计一套支持渲染富文本内容的跨端组件

背景

不知道大家有没有遇到过这种需求,在管理后台配置一段富文本 html 内容,一开始呢,只需要支持渲染到浏览器上面,但随着公司业务的变化,可能需要在小程序上使用,甚至是 ios 或者 android 等其他端上

渲染到 h5 或者类浏览器端(忽略 xss 等安全问题)

这种一般就直接使用框架自带的,类似

  1. vue: vue-html

  2. react: dangeroushtml

如何渲染到小程序端

因为小程序是使用自己独有的标签,例如 view 标签,但后台配置的是 html 文本字符串,怎么把 html 变成 view,往往很多人就不知道怎么做了,觉得这就是不可能完成的,是产品在提不合理的需求,看到这里,大家可以先想想,如果是你,你会怎么做?

下面说说我的思考过程

taro

因为当时我在用 taro 写小程序,简化需求来理解,就是需要把 div 标签 渲染成一个 view 标签 ,如何变呢?我一下子想到了 babel, 因为 taro 本身就支持跨端,跨端的原理就是利用 babel 做抽象语法树,变成自己想要的语言效果

outside_default.png
image.png

看上图,一切都很明亮了,但是有两个致命的问题

  1. babel 是静态语法分析,富文本需求是代码运行时计算的,相悖了

  2. babel 包太大了

后来直接否决掉了 babel

这时候再抽象一下,原来需求简化过是 div 变成 view ,再简化一下就是div 变成虚拟 dom ,虚拟 dom 变成 view

html 转成虚拟 DOM

jquery

这时候想到了 jquery 的一个语法,jquery 的 $('一段html')就能把 html 变成 dom,然后一顿操作就去翻 jq 源码,并不满足, 缺点:

  1. dom 结构挂载了太多 jq 无用的属性,

  2. html 内容有一些 jq 框架自带的限制

vue、react

翻看 vue、react 的一些源码

  1. vue: vue-template-compiler

  2. react: babel-loader 会把elemennt 转化成 createElement

缺点:

  1. 解析出来的虚拟 dom 还是会带上很多框架自身的限制

难道要自己写一个?需求很紧急啊,写是不可能自己写的

babel

本质就是如何 div 变成一个干净的虚拟 dom 节点呢?最后还是绕不开回到了原点。babel,通过查看源码发现有一个名词叫 html-parse 直接打开 github 一搜,发现很多库,但是大部分都是实验或者 demo 级别

html-parse-stringify[1]

最后选择了这个库

优点:

  1. 轻量
  2. 兼容性好,很多属性写法都能匹配的到
  3. 生成的虚拟 dom 不依赖框架、平台,就是 json 文本
var HTML = require('html-parse-stringify')

// this html:
var html = '<div class="oh"><p>hi</p></div>'

// becomes this AST:
var ast = HTML.parse(html)

console.log(ast)
/*
{
    // can be `tag`, `text` or `component`
    type: 'tag',

    // name of tag if relevant
    name: 'div',
    
    // parsed attribute object
    attrs: {
        class: 'oh'
    },

    // whether this is a self-closing tag
    // such as <img/>
    voidElement: false,

    // an array of child nodes
    // we see the same structure
    // repeated in each of these
    children: [
        {
            type: 'tag',
            name: 'p',
            attrs: {},
            voidElement: false,
            children: [
                // this is a text node
                // it also has a `type`
                // but nothing other than
                // a `content` containing
                // its text.
                {
                    type: 'text',
                    content: 'hi'
                }
            ]
        }
    ]
}
*/

虚拟 DOM 转化成其它端代码

有了虚拟 dom, 小程序端就可以通过深度优先遍历,一层一层的把虚拟 dom 渲染成 view 标签 实现类似于下面代码,其他端也类似同理

使用
<View className="index">
    <TaroRichText
      htmls={`访问内容<span class="highlight"><a appid="495ec7a9-a3e8-42ad-a1ee-f14b8e0af1e3" pagepath="pages/renderer/renderer">活动线上预约报名啦啦啦</a></span>
    `}
    />
</View>

核心源码
export default function Tree(props: IProps) {
  const { doms } = props;
  console.log(doms);
  const { children = [] } = doms || {};
  return (
    <View>
      // 根据不同的类型,模拟不同的样式即可,通过 class 模拟
      {doms.type === 'text' && <View>{doms.content}</View>}
      {/* <View>{其他类型,自己写函数渲染}</View> */}
      <View>
        {children.length &&
          children.map(dom => {
            return <Tree doms={dom} />;
          })}
      </View>
    </View>
  );
}

参考-代码久远可能跑不起来[2]

如何自己写一套 html-parse 呢?

本质上是正则,就不细说了大家可以参考

  • 【手写简易浏览器】html parser 篇[3]

  • react-jsx-parser[4]

  • vue-template-compiler[5]

顺便推荐一个校验正则正确性的网站

正则校验[6]

关于本文

来自:乘风gg

https://juejin.cn/post/7114851378807177223

最后

欢迎关注【前端瓶子君】✿✿ヽ(°▽°)ノ✿

回复「算法」,加入前端编程源码算法群,每日一道面试题(工作日),第二天瓶子君都会很认真的解答哟!

回复「交流」,吹吹水、聊聊技术、吐吐槽!

回复「阅读」,每日刷刷高质量好文!

如果这篇文章对你有帮助,「在看」是最大的支持

 》》面试官也在看的算法资料《《

“在看和转发”就是最大的支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值