下面是高德地圖覆蓋物中的點(diǎn)標(biāo)記(Marker類)的文檔。


可以看到使用 content 屬性,就可以自定義 DOM 節(jié)點(diǎn)。傳入 HTML 字符串,或 HTML DOM 對(duì)象形式,那如何傳入 React 組件呢?
- 一
首先,肯定不可能以字符串的形式寫入組件。畢竟是使用 JSX ,所以組件需要編譯,字符串就沒法編譯了。
- 二
那用模板字符串,把組件以變量的形式傳入嗎?
這樣子組件雖然可以被編譯了,但每個(gè) JSX 元素只是調(diào)用 React.createElement(component, props, ...children) 的語法糖。(createElement 幾乎都是在格式化數(shù)據(jù))
從下面 react 源碼中,可以看到最后返回的是一個(gè) ReactElement 。
export function createElement(type, config, children) {
//......
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
接著往下看,可以看到 ReactElement 返回的 element 就是一個(gè)對(duì)象。(簡短的代碼里可以看到,就是把傳入的參數(shù)按照一定的規(guī)范,“組裝”進(jìn)了 element 對(duì)象里)
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
// 是一個(gè)常量,用來標(biāo)識(shí)該對(duì)象是一個(gè)ReactElement
$$typeof: REACT_ELEMENT_TYPE,
// 內(nèi)置屬性賦值
type: type,
key: key,
ref: ref,
props: props,
// 記錄創(chuàng)造該元素的組件
_owner: owner,
};
if (__DEV__) {
// 針對(duì) __DEV__ 環(huán)境下的處理
}
return element;
};
所以,如果把組件以變量的形式傳入的話,出現(xiàn)的恐怕就是一個(gè) ReactElement 對(duì)象實(shí)例(本質(zhì)上是以 JavaScript 對(duì)象形式存在的對(duì) DOM 的描述,也就是虛擬 DOM)。
- 三
既然是“虛擬 DOM”,那如何得到渲染到頁面上的真實(shí) DOM 呢?react 負(fù)責(zé)在瀏覽器環(huán)境渲染的 Renderer 是?
—— ReactDOM!(在每一個(gè) React 項(xiàng)目的入口文件里,一定會(huì)出現(xiàn)下面的這行代碼)
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__) {
// 針對(duì) __DEV__ 環(huán)境下的處理
}
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback,
);
}
從代碼的傳參就已經(jīng)可以知道 container 是必傳的了。(里面寫到的 invariant 是一種在開發(fā)中提供描述性錯(cuò)誤,但在生產(chǎn)中提供通用錯(cuò)誤的方法)
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 '))
);
}
代碼里也對(duì)這里做了判斷,是否是有效的容器。
- 解決方案
所以最后的解決方案是,先渲染一個(gè)帶 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 方法,把組件渲染到這個(gè) div 上。
let fun = setInterval(() => {
if (document.getElementById('test')) {
ReactDOM.render(<Chestnut />, document.getElementById('test'));
clearInterval(fun);
}
}, 500);
因?yàn)?map.add() 方法不會(huì)使 react 重新 render,所以這邊用到了一個(gè)定時(shí)循環(huán),確保地圖上增加了這個(gè) div 后,把組件渲染上去。
- 代碼優(yōu)化
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]);
};
- 最后
想到解決方案,其實(shí)沒有花費(fèi)太多時(shí)間,就很順利的一步一步分析嘗試下來。
但在解決的那一瞬間,內(nèi)心有興奮一下。所以決定稍微記錄一下~