装饰器工程运用-埋点

背景:

  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;
相关推荐
小Lu的开源日常几秒前
为什么计算机用“补码”存储整数?
设计模式·面试·计算机组成原理
蒋星熠3 小时前
Python API接口实战指南:从入门到精通
开发语言·分布式·python·设计模式·云原生·性能优化·云计算
努力也学不会java21 小时前
【设计模式】简单工厂模式
java·开发语言·设计模式·简单工厂模式
Leo来编程21 小时前
设计模式8-命令模式
设计模式·命令模式
易元1 天前
模式组合应用-组合模式
后端·设计模式
秋难降1 天前
从浅克隆到深克隆:原型模式如何解决对象创建的 “老大难”?😘
后端·设计模式·程序员
ssshooter1 天前
上下文工程:为高级大型语言模型构建信息环境
人工智能·算法·设计模式
郝学胜-神的一滴1 天前
C++组合模式:构建灵活的层次结构
开发语言·c++·程序人生·设计模式·组合模式
程序员水自流1 天前
Java设计模式是什么?核心设计原则有哪些?
java·设计模式
用户413079810611 天前
面向对象六大设计原则
设计模式