鸿蒙5.0开发手写一个【富文本解析器】

实现过程
一、准备工作
  • 安装好最新[DevEco Studio]开发工具,创建一个新的空项目。
二、整体思路

主要有以下几个步骤

  • 正则处理自闭合标签例如img 、input 等,方便后续处理。
  • 递归解析标签,并且判断处理特殊标签给他们加上默认样式 例如 h1~h6 以及 strong 、b、big、a、s、等标签。
  • 解析标签上的 style样式、以及其他属性样式等。
  • 利用[@Builder装饰器]自定义构建函数 递归构造解析生成对应的ArkUI。
  • 利用[@Extend装饰器]定义扩展组件样式,方便一些样式的集成共用。

大致的流程图如下:

1

三、解析富文本内容–转换为JSON
  • 处理自闭合标签。 在ets/pages 目录下新增文件 parseHtmlToJson.ts
export interface VNode {
  type: string;
  level?: number;
  props: {}; // 属性可以是字符串或对象(如样式)
  text?: string; // 标签内的文本内容
  children?: VNode[]; // 子节点列表
}


export  class parseHTML{
  selfClosingTagRegex = /<([a-zA-Z0-9-]+)([^>]*)/>/g; //匹配自闭合标签

  constructor() {

  }
  parseHTMLtoJSON(htmlString:string){
    // 使用正则表达式的替换功能来转换自闭合标签
    const result = htmlString.replace(this.selfClosingTagRegex, (match, tagName, attributes) => {
      // 构造结束标签
      return `<${tagName}${attributes}></${tagName}>`;
    });
    console.log("result",result)
  }
}

修改ets/pages/Index.ets文件。

import  {parseHTML} from  "./parseHtmlToJson"
@Entry
@Component
struct Index {

  @State htmlStr:string =`
    <h1>h1标签</h1>
    <h6>h6标签</h6>
    <div>
      <a href="http://www.baidu.com">a标签</a>
      <span>span标签</span>
      <strong>strong标签</strong>
      <img src="https://img1.baidu.com/it/u=728576857,3157099301&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=313"  />
      <input style="color:red" placeholder="请输入..." type="number" maxlength="2" value="我是input标签"/>
    </div>
     <p style="margin: 10px;border: 5px solid #000;">带边框样式的</p>
  `;
   parseHTML = new parseHTML();

  aboutToAppear(){
    const result = this.parseHTML.parseHTMLtoJSON(this.htmlStr);
    console.log('result',JSON.stringify(result))
  }
  build() {
    Column(){

    }
  }
}

可以看到打印结果给自闭合标签添加了尾部

2

  • 将html转换为JSON树,给特殊添加标签默认样式,解析标签上的属性。

修改一下 parseHtmlToJson.ts 文件,完整代码如下

interface NestedObject {
  [key: string]: string | number| object
}

export interface VNode {
  type: string;
  props: {
    [key: string]: string | number| object
    style?:NestedObject
  }; // 属性可以是字符串或对象(如样式)
  text?: string; // 标签内的文本内容
  children?: VNode[]; // 子节点列表
}

export  class parseHTML{
  selfClosingTagRegex = /<([a-zA-Z0-9-]+)([^>]*)/>/g; //匹配自闭合标签
  baseFontColor: string = '#000'; //基础颜色
  baseFontSize: string | number = '16';//默认字体大小
  themeFontColor: string = 'blue'; //默认主题颜色 用于处理a 标签等
  inlineElements = this.makeMap('text,a,abbr,acronym,applet,b,basefont,bdo,big,button,cite,del,dfn,em,font,i,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,tt,u,var')

  constructor() {

  }
  // 解析标签属性
  parseAttributes(attrString: string): Record<string, string | Record<string, string | number>> {
    const attrs: Record<string, string | Record<string, string | number>> = {};
    const attrRegex = /(\w+)="(.*?)"/g;
    let match: RegExpExecArray | null;

    while ((match = attrRegex.exec(attrString)) !== null) {
      const [, name, value] = match;

      if (name === 'style') {
        // 如果是 style 属性,将其解析为对象
        const styleObject: Record<string, string | number> = {};
        value.split(';').forEach((style) => {
          let [property, val] = style.split(':').map(s => s.trim());
          if (property && val) {
            console.log('valval', val)
            if (val.includes('px')) {
              val = this.removePxUnits(val); // 去掉 'px'
            }
            styleObject[this.toCamelCase(property)] = val;
            // 拆分 border 属性
            if (property === 'border') {
              const borderParts = val.split(' ');
              if (borderParts.length === 3) {
                styleObject['borderWidth'] = borderParts[0];
                styleObject['borderStyle'] = borderParts[1];
                styleObject['borderColor'] = borderParts[2];
              }
            }
            // 拆分 margin 属性
            if (property === 'margin') {
              const marginParts = val.split(' ');
              switch (marginParts.length) {
                case 1:
                  styleObject['
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值