装饰器工程运用-埋点

背景:

  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;
相关推荐
愿天堂没有C++5 小时前
剑指offer第2版——面试题2:实现单例
c++·设计模式·面试
静谧之心5 小时前
分层架构下的跨层通信:接口抽象如何解决反向调用
java·开发语言·设计模式·架构·golang·k8s·解耦
用户84913717547161 天前
JustAuth实战系列(第5期):建造者模式进阶 - AuthRequestBuilder设计解析
java·设计模式·架构
只因在人海中多看了你一眼1 天前
B.10.01.5-电商系统的设计模式应用实战
设计模式
希望_睿智1 天前
实战设计模式之代理模式
c++·设计模式·架构
Freedom风间2 天前
前端必学-完美组件封装原则
前端·javascript·设计模式
王彬泽2 天前
【设计模式】代理模式
设计模式·代理模式
过客随尘2 天前
EventBus设计
设计模式
易元2 天前
模式组合应用-适配器模式
设计模式