react-webcomponentify元件介紹、原碼分析、注意事項

阿古達木發表於2021-10-01

react-webcomponentify是一個可以無需任何額外工作量就能將react元件打包 匯出為webComponent的庫。整個庫在gizp之後僅有1.5kb。


使用方法

基礎

import React from "react";
import { registerAsWebComponent } from "react-webcomponentify";

export const ExampleReactComponent = () => {
  return <div> Hello </div>;
};

registerAsWebComponent(ExampleReactComponent, "example-component");

html

<!DOCTYPE html>
<html>
  ....
  <body>
    <example-component />
  </body>
  ....
</html>

進階
向react傳遞資料:可以通過html的attributes傳遞字串屬性,通過setProps方法傳遞函式或者物件之類的屬性

import React from "react";
import { registerAsWebComponent } from "react-webcomponentify";

export const ButtonComponent = props => {
  return (
    <div>
      Hello <button onClick={props.onClick}>{props.text}</button>
    </div>
  );
};

registerAsWebComponent(ButtonComponent, "button-web");

html

<!DOCTYPE html>
<html>
  ....
  <body>
    <button-web text="click me" id="mybutton" />
  </body>
  ....
  <script>
    const myBtn = document.getElementById("mybutton");
    myBtn.setProps({
      onClick: () => console.log("btn was clicked")
    });
  </script>
</html>

每一個通過react-webcomponentify建立的自定義元件都有一個setProps例項方法

element.setProps({
  ....
  /* 在這裡設定你想要傳送給react的屬性 */
  ....
})

同樣支援子元件

import React from "react";
import { registerAsWebComponent } from "react-webcomponentify";

// You access the children just like you would in react (using this.props.children)
export const ComponentWithChild = props => {
  return (
    <div>
      Hello World
      {this.props.text}
      <div>{this.props.children}</div>
    </div>
  );
};

registerAsWebComponent(ComponentWithChild, "component-with-child");

html

<!DOCTYPE html>
<html>
  ....
  <body>
    <component-with-child text="I love children">
      <p>Some child</p>
    </component-with-child>
  </body>
  ....
</html>

原理分析

1.建立工程函式,使用customElements定義webcomponent,通過ShadowDom隔離元件
2.在類的建構函式中獲取html標籤的所有屬性。定義一個MutationObserver監聽屬性的變化。呼叫renderReact2Node方法
3.renderReact2Node方法作用是將react元件掛載到頁面上。接收的引數有要匯出的react元件、初始化資料(就是第2步獲取到的屬性)、目標父容器元素(就是第1步建立的shandowDom)、onRender回撥。
4.在renderReact2Node中定義一個高階元件PropBridge,作用是維護state狀態,然後將該元件ref匯出。掛載。
這樣在shadowDom中通過呼叫ref.current.setProps就實現了通訊。

  class PropBridge extends React.PureComponent {
    state = { ...initialProps };
    setProps = (props: React.RefObject<PropBridge>) =>
      this.setState(() => props);
    render() {
      return <RComponent {...this.props} {...this.state} />;
    }
  }
  const propBridgeRef = createRef<PropBridge>();
    ReactDOM.render(<PropBridge ref={propBridgeRef} />, targetDomNode, () =>
    onRender(propBridgeRef)
  );

5.在容器的MutationObserver的回撥中呼叫setProps。這樣就實現了修改標籤屬性實時通訊給react
6.在容器建立一個例項方法setProps,對引數進行處理後呼叫PropBridge的setProps,這樣就實現了複雜資料型別的傳遞。


注意事項
我在使用過程中遇到一個大坑。react元件webpack匯出的時候進行了分包。引用元件的angular專案也使用的webpack。也進行了分包,所以react元件引入的時候,出現了bundle錯亂的問題。
原因是兩份webpack配置裡面output.jsonpFunction沒有寫,預設是window下的一個全域性變數webpackJsonp。
解決辦法
1.react匯出不分包
2.修改react webpack的jsonpFunction。(在webpack5中叫chunkLoadingGlobal)

相關文章