高德地图如何传入 React 组件(附React 源码简单分析过程)

下面是高德地图覆盖物中的点标记(Marker类)的文档。

1726281177309.jpg

可以看到使用 content 属性,就可以自定义 DOM 节点。传入 HTML 字符串,或 HTML DOM 对象形式,那如何传入 React 组件呢?

一、首先,肯定不可能以字符串的形式写入组件。毕竟是使用 JSX ,所以组件需要编译,字符串就没法编译了。

二、那用模板字符串,把组件以变量的形式传入吗?

这样子组件虽然可以被编译了,但每个 JSX 元素只是调用 React.createElement(component, props, ...children) 的语法糖。(createElement 几乎都是在格式化数据)

从下面 react 源码中,可以看到最后返回的是一个 ReactElement 。

export function createElement(type, config, children) {
  //......
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

接着往下看,可以看到 ReactElement 返回的 element 就是一个对象。(简短的代码里可以看到,就是把传入的参数按照一定的规范,“组装”进了 element 对象里)

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // 是一个常量,用来标识该对象是一个ReactElement
    $$typeof: REACT_ELEMENT_TYPE,

    // 内置属性赋值
    type: type,
    key: key,
    ref: ref,
    props: props,

    // 记录创造该元素的组件
    _owner: owner,
  };

  if (__DEV__) {
   // 针对 __DEV__ 环境下的处理
  }

  return element;
};

所以,如果把组件以变量的形式传入的话,出现的恐怕就是一个 ReactElement 对象实例(本质上是以 JavaScript 对象形式存在的对 DOM 的描述,也就是虚拟 DOM)。
三、既然是“虚拟 DOM”,那如何得到渲染到页面上的真实 DOM 呢?react 负责在浏览器环境渲染的 Renderer 是?
—— ReactDOM!(在每一个 React 项目的入口文件里,一定会出现下面的这行代码)

ReactDOM.render(element, container[, callback])

下面看 render 方法的源码。

export function render(
  element: React$Element<any>,
  container: Container,
  callback: ?Function,
) {
  invariant(
    isValidContainer(container),
    'Target container is not a DOM element.',
  );
  if (__DEV__) {
   // 针对 __DEV__ 环境下的处理
  }
  return legacyRenderSubtreeIntoContainer(
    null,
    element,
    container,
    false,
    callback,
  );
}

从代码的传参就已经可以知道 container 是必传的了。(里面写到的 invariant 是一种在开发中提供描述性错误,但在生产中提供通用错误的方法)

export function isValidContainer(node: mixed): boolean {
  return !!(
    node &&
    (node.nodeType === ELEMENT_NODE ||
      node.nodeType === DOCUMENT_NODE ||
      node.nodeType === DOCUMENT_FRAGMENT_NODE ||
      (node.nodeType === COMMENT_NODE &&
        (node: any).nodeValue === ' react-mount-point-unstable '))
  );
}

代码里也对这里做了判断,是否是有效的容器。

四、解决方案
所以最后的解决方案是,先渲染一个带 id 的div。

    var marker = new AMap.Marker({
    position: new AMap.LngLat(116.40061686197998, 39.845549045139),
      content: '<div id="test" ></div>',
    });
    map.add([marker]);

然后通过 ReactDOM.render 方法,把组件渲染到这个 div 上。

ReactDOM.render(element, container[, callback])

因为 map.add() 方法不会使 react 重新 render,所以这边用到了一个定时循环,确保地图上增加了这个 div 后,把组件渲染上去。

五、代码优化

const domRender = async reactElement => {
    const div = document.createElement('div');
    await new Promise(resolve => {
      ReactDOM.render(reactElement, div, resolve);
    });
    return div.firstChild;
  };

  async () => {
    const element = await domRender(<Chestnut />);
    const text = new AMap.Marker({
      position: new AMap.LngLat(116.40061686197998, 39.845549045139),
      content: element,
    });
    map.add([text]);
  };
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。