装饰器工程运用-埋点

背景:

  1. 假如我希望在不改变原组件内部逻辑的基础上,进行元素属性上的调整,你会怎么做
  2. 早期我在做圈选埋点第三方组件适配的时候,发现边界情况太多,很难适配,在学习装饰器的时候,突然发现,我是不是可以通过装饰器,将属性修改等操作,通过装饰器去解决,而不是让开发者自己去修改

案例

  1. 假如我现在希望,在给组件打上一个自定义属性 如 data-id=ffaawefvc 后,我希望将这个属性移动到内部input元素上.思路其实就是选择获取到目标元素,然后将当前组件中的错误标记元素上的 自定义属性,进行移除,然后给目标元素添加属性.那么一个通用的思路该如何去做呢?
js 复制代码
import React from "react";
import { findDOMNode } from "react-dom";
import omit from 'omit.js';

export const DATA_CONSIDER_ID = "data-consider-id";
export const DATA_CONSIDER_TYPE = "data-consider-controltype";
export const DATA_CONSIDER_INDEX = "data-consider-index";

const getAttributesFromTarget = (element, markToItemIndexAttribute) => {
  let attribute = element?.attributes[markToItemIndexAttribute]?.value;
  if (!attribute) {
    attribute = element.firstElementChild?.attributes[markToItemIndexAttribute]?.value;
  }
  return attribute;
}

interface MarkToItem {
  markToItemClass: string;
  //If markToItems contains more than one item,suggest add markToItemIdSuffix for each item
  markToItemIdSuffix?: string;
  //If markToItem has many,suggest add markToItemIndexAttribute for index diff
  markToItemIndexAttribute?: string;
  //If markToItem is dynamic changed at runtime,use this flag to add a MutationObserver
  dynamicMark?: boolean;
  // If has custom markLogic, pass it
  customMarkLogic?: (element: HTMLElement, dataConsiderId: string, dataConsiderControlType: string, markToItemIndexAttribute?: string) => void;
}


const markDecorator = (WrappedPage, markToItems: MarkToItem[], includeConsiderAttributes?: boolean): any => {
  class markComponent extends React.PureComponent {
    dynamicMarkObserver: MutationObserver;

    constructor (props) {
      super(props);
    }

    componentDidMount() {
      const dataConsiderId = this.props[DATA_CONSIDER_ID];
      const dataConsiderControlType = this.props[DATA_CONSIDER_TYPE];

      const element = findDOMNode(this) as HTMLElement;
      if (element) {
        try {
          // Remove all exists mark
          let fromElements = element.querySelectorAll(`[data-consider-id="${dataConsiderId}"]`);
          fromElements?.forEach(fromElement => {
            fromElement?.removeAttribute(DATA_CONSIDER_ID);
            fromElement?.removeAttribute(DATA_CONSIDER_TYPE);
            fromElement?.removeAttribute(DATA_CONSIDER_INDEX);
          });
          fromElements = null;
          // Mark all needed items
          markToItems.forEach(markToItem => {
            const { markToItemClass, markToItemIdSuffix, markToItemIndexAttribute, dynamicMark, customMarkLogic } = markToItem;
            // Add suffix to controlId if provided by component
            let finalDataConsiderId = markToItemIdSuffix ? `${dataConsiderId}-${markToItemIdSuffix}` : dataConsiderId;

            // Use custom mark logic if provided by component
            const markLogic = customMarkLogic ?? function (toElement, dataConsiderId, dataConsiderControlType, markToItemIndexAttribute) {
              toElement?.setAttribute(DATA_CONSIDER_ID, dataConsiderId);
              toElement?.setAttribute(DATA_CONSIDER_TYPE, dataConsiderControlType);
              markToItemIndexAttribute && toElement?.setAttribute(DATA_CONSIDER_INDEX, getAttributesFromTarget(toElement, markToItemIndexAttribute));
            };

            // Mark elements at first render
            // @ts-ignore
            const toElements = [element, ...element.querySelectorAll(markToItemClass)].filter(x => x.matches(markToItemClass));
            toElements?.forEach((toElement: HTMLElement) => markLogic(toElement, finalDataConsiderId, dataConsiderControlType, markToItemIndexAttribute));

            // If dynamicMark,means childNodes will change, will mark them at later render
            if (dynamicMark && typeof MutationObserver !== "undefined") {
              this.dynamicMarkObserver = new MutationObserver(() => {
                // @ts-ignore
                const newToElements = [element, ...element.querySelectorAll(markToItemClass)].filter(x => x.matches(markToItemClass));
                newToElements?.forEach((toElement: HTMLElement) => markLogic(toElement, finalDataConsiderId, dataConsiderControlType, markToItemIndexAttribute));
              });
              this.dynamicMarkObserver.observe(element, { childList: true, subtree: true });
            }
          });

        } catch (error) {
          console.log('Mark fail:' + error);
        }
      }
    }
    componentWillUnmount() {
      this.dynamicMarkObserver?.disconnect();
    }
    render() {
      let omitAttributes = includeConsiderAttributes ? [] : [DATA_CONSIDER_ID, DATA_CONSIDER_TYPE];
      var props = omit(this.props, omitAttributes);

      return <WrappedPage {...props} />;
    }
  }
  return markComponent;
}

export default markDecorator;
相关推荐
GISer_Jing14 小时前
AI Agent 目标设定与异常处理
人工智能·设计模式·aigc
蔺太微14 小时前
组合模式(Composite Pattern)
设计模式·组合模式
鱼跃鹰飞16 小时前
DDD中的防腐层
java·设计模式·架构
会员果汁17 小时前
15.设计模式-组合模式
设计模式·组合模式
YUEchn18 小时前
无处不在的Agent
设计模式·llm·agent
茶本无香21 小时前
设计模式之二—原型模式:灵活的对象克隆机制
java·设计模式·原型模式
GISer_Jing21 小时前
Nano Banana+LoveArt三大核心功能解析:重构AI设计全链路,让创意落地更高效
人工智能·设计模式·aigc
会员果汁1 天前
14.设计模式-备忘录模式
设计模式·备忘录模式
xiaolyuh1231 天前
Spring 框架 核心架构设计 深度详解
spring·设计模式·spring 设计模式
GISer_Jing2 天前
智能体工具使用、规划模式
人工智能·设计模式·prompt·aigc