将React嵌入作为传统Web页面的一部分的方式

首先

我想知道如何将React组件嵌入传统的网页中,所以我进行了调查,但意外地没有找到具体的步骤。

因此,我们将总结三个步骤,即组件的构建,作为页面内的组件显示,以及与组件外部的交互。

    1. 将React组件构建为可从外部使用的步骤

 

    1. 将React组件作为Web页面的一部分显示的步骤

 

    在Web页面和React组件之间进行交互(设置初始值,相互调用)

 

总结

使用create-react-app生成模板,并创建一个熟悉的计数器组件,然后将其导出以供外部使用。

    1. 使用rollup.js将React组件构建为可从外部使用的形式。

 

    1. 传统的Web应用程序通常不使用模块化的JS,因此需要以UMD格式(通过全局变量公开React组件)进行构建。

 

    1. 要嵌入的组件使用了React中常见的Counter组件。

显示React组件作为网页的一部分。
在网页和React组件之间进行交互。

对React组件进行初始值设置。
从React组件的事件处理中调用网页端的处理。
从网页端的事件处理中调用React内部的处理。

将Counter组件作为组件显示的代码(的图像)

MyBundleはコンポーネントを公開するためのグローバル変数(UMD形式でビルドする際に、指定した変数名)

ReactDOMClient.createRoot()でrootを作成し、コンポーネントをrenderします

  <body>
    <h2>Webページの一部にReactコンポーネントを表示する</h2>
    <div id="root"></div>
  </body>
  <script src="/dist/lib.umd.js"></script>
  <script>
    const {ReactDOMClient, React, Counter} = MyBundle;
    const container = document.getElementById('root');
    const root = ReactDOMClient.createRoot(container);
    root.render(React.createElement(Counter));
  </script>

如果以ESModule形式构建,则会如下所示。

  <script type="module">
    import {ReactDOMClient, React, Counter} from '/dist/lib.esm.js';
    const container = document.getElementById('root');
    const root = ReactDOMClient.createRoot(container);
    root.render(React.createElement(Counter));
  </script>

準備:創建Counter組件

    create-react-appでReactのひな形を作成
$ npx create-react-app react-with-conventional-webapp --template typescript
$ cd react-with-conventional-webapp

styled-componentsをインストール(Reactコンポーネントを目立だたせるため、borderを表示する)

$ npm i styled-components@5.3.10

$ npm i -D @types/styled-components
    下記のエラーが出た場合はnpm i styled-components@5.3.10でインストール可能 (StackOverflowの解説)
  $ npm i styled-components
  npm ERR! Cannot read properties of null (reading 'edgesOut')

Counterコンポーネント(ボタンを押すとカウントを加算)を追加

$ touch ./src/Counter.tsx

Counter组件.tsx

import { FC, useState } from 'react';
import styled from 'styled-components';
const Content = styled.div`
  border: solid 1px black;
  width: fit-content;
  padding: 4px;
`;

type propType = {
  initVal?: number;
};
const Counter: FC<propType> = ({ initVal = 0 }) => {
  const [count, setCount] = useState(initVal);
  const handleClick = () => setCount((n) => n + 1);
  return (
    <Content>
      <div>Count: {count}</div>
      <button onClick={handleClick}>Increment</button>
    </Content>
  );
};
export default Counter;

确认动作

App.tsxファイルを書き換えてからnpm run startで実行する

import './App.css';
import Counter from './Counter';

function App() {
  return (
    <div className="App">
      Counterコンポーネント動作確認
      <Counter initVal={0} />
    </div>
  );
}

export default App;

如果出现这样的屏幕显示,就可以了。

img10.png

使用rollup.js进行构建,以便可以从外部使用React组件。

进行构建的步骤

rollup.jsの導入
ビルド用スクリプトrollup.config.jsを作成

rollup.jsでトランスパイルを行うため、tsconfig.rollup.jsonを追加

package.jsonに出力ファイル名を追加(rollup.config.jsで利用)
公開するコンポーネントをエントリーポイントファイル(src/lib.ts)でexportする

package.jsonのscriptsにビルド用のコマンドbuild-libを追加して動作確認を行う

引入Rollup.js

为了将其打包作为库,我们将引入 rollup.js 和所需的插件。

rollup.jsは複数のモジュールやファイルを1つのファイルにまとめてくれる、軽量で高速なバンドルツールです

npm i -D rollup rollup-plugin-delete rollup-plugin-peer-deps-external rollup-plugin-typescript2 @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-replace

创建用于构建的脚本rollup.config.js

接下来,我们将创建用于构建的脚本。请在项目的根目录下创建rollup.config.js文件,并将以下内容填写进去。

import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';
import typescript from 'rollup-plugin-typescript2';
import del from 'rollup-plugin-delete';
const packageJson = require('./package.json');

// eslint-disable-next-line import/no-anonymous-default-export
export default {
  input: 'src/lib.ts',
  output: [
    {
      // esmodule
      file: packageJson.module,
      format: 'esm',
      sourcemap: true,
    },
    { // UMD形式
      file: packageJson.umd,
      format: 'umd',
      name: 'MyBundle',
      sourcemap: true,
    },
  ],
  plugins: [
    del({ targets: 'dist/*' }),
    peerDepsExternal(),
    resolve(),
    commonjs(),
    replace({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
      preventAssignment: true,
    }),
    typescript({
      tsconfig: 'tsconfig.rollup.json',
      useTsconfigDeclarationDir: true,
    }),
  ],
};

input: ‘src/lib.ts’を起点にimportされているファイルを読み込み、バンドルします(1つのファイルにまとめる)。公開したいコンポーネントをexportするファイルです

ES Modules形式、UMD形式に分けて2種類のファイル出力します

file:~は出力するファイル名です。package.jsonで定義した値を利用します(この後追加)

关于在Rollup.js中使用的插件

peerDepsExternal
package.jsonに記載されたpeerDependenciesパッケージをバンドル対象から除外して、バンドルサイズを削減する

resolve
インポートするモジュールの依存関係の解決とファイルパスの特定を行う

commonjs
require()を解析してCommonJS形式の依存関係を特定する

replace
ビルド時に置換処理を行う。実行時にnodeの環境変数を参照する箇所がエラーとなるため、ビルド時に置換する

typescript
トランスパイルを行う。設定ファイルはtsconfig.rollup.jsonを利用する。また型定義ファイル(d.ts)を出力するため、useTsconfigDeclarationDir: trueを指定する。出力先はtsconfigファイルのdeclarationDir

为了在rollup.js中进行转译,需要添加tsconfig.rollup.json文件。

在项目根目录下创建Rollup.js的转译配置文件tsconfig.rollup.json。

{
  "extends": "./tsconfig",
  "compilerOptions": {
    "outDir": "dist",
    "declaration": true,
    "declarationDir": "dist",
  },
  "exclude": [
    "node_modules",
    "dist",
    "build",
  ]
}

“extends”: “./tsconfig”,
create-react-appで作成されたtsconfig.jsonの設定を継承します

“outDir”: “dist”,
出力先の指定

“declaration”: true,、 “declarationDir”: “dist”
型定義ファイルの出力を有効にして、出力先をdistにします

在package.json中添加输出文件名(用于rollup.config.js)

我們將為 Rollup.js 增加一個設置選項,用於設定輸出文件的名稱(包括 ES 模組和 UMD 兩種選項)。

{
  "name": "react-with-conventional-webapp",
  "version": "0.1.0",
  "private": true,
+  "module": "dist/lib.esm.js",
+  "umd": "dist/lib.umd.js",

在入口文件(src/lib.ts)中将要公开的组件进行导出

将组件(函数)公开为导出。

除了 Counter,我们还将 React 一起导出并进行打包(不需要单独加载)。

export { default as React } from 'react';
export { default as ReactDOM } from 'react-dom';
export { default as ReactDOMClient } from 'react-dom/client';
// Component
export { default as Counter } from './Counter';

在package.json文件的scripts字段中添加用于构建的命令,并进行操作确认。

我将添加用于构建软件包的命令。

  "scripts": {
+    "build-lib": "rollup -c"
  },

确认没有发生构建错误。

    エラーがなければdist/lib.esm.js, dist/lib.umd.js の2ファイルが作成されます
$ npm run build-lib

> react-with-conventional-webapp@0.1.0 build-lib
> rollup -c


src/lib.ts → dist/lib.esm.js, dist/lib.umd.js...
created dist/lib.esm.js, dist/lib.umd.js in 4.1s

将React的组件显示为网页的一部分。

为了确认动作,创建一个HTML文件。在根目录下创建webroot文件夹,并将文件保存在其中。

$ mkdir webroot
$ touch webroot/test1.html

我会将下面的内容写入到test1.html文件中。

の部分に、Reactコンポーネント(Counter)を描画

<!DOCTYPE html>
<html>
  <body>
    <h2>Webページの一部にReactコンポーネントを表示する</h2>
    <div id="root"></div>
  </body>
  <script src="/dist/lib.umd.js"></script>
  <script>
    const {ReactDOMClient, React, Counter} = MyBundle;
    const container = document.getElementById('root');
    const root = ReactDOMClient.createRoot(container);
    root.render(React.createElement(Counter));
  </script>
</html>

为了确认操作,启动Web服务器。

$ npx http-server .

打开 http://localhost:8080/webroot/test1.html,并确认React组件作为HTML的一部分显示出来。

img20.png
    ESModule形式(lib.esm.js)を利用する場合は以下の記述になります

test1_esm.html的本地化翻译为中文如下:测试1_esm.html

<!DOCTYPE html>
<html>
  <body>
    <h2>Webページの一部にReactコンポーネントを表示する</h2>
    <div id="root"></div>
  </body>
  <script type="module">
    import {ReactDOMClient, React, Counter} from '/dist/lib.esm.js';
    const container = document.getElementById('root');
    const root = ReactDOMClient.createRoot(container);
    root.render(React.createElement(Counter));
  </script>
</html>

在同一个网页和React组件之间进行交互

对于React组件的初始值设定

计数器组件可以通过参数(属性)来设置初始值。

type propType = {
  initVal?: number;
};
const Counter: FC<propType> = ({ initVal = 0 }) => {

通过在React.createElement()的参数中添加initVal,可以设置初始值。

    PHPなどサーバ側から初期値をセットして表示することが可能です

测试2.html

<!DOCTYPE html>
<html>
  <body>
    <h2>Webページの一部にReactコンポーネントを表示する</h2>
    <div id="root"></div>
  </body>
  <script type="module">
    import {ReactDOMClient, React, Counter} from '/dist/lib.esm.js';
    const container = document.getElementById('root');
    const root = ReactDOMClient.createRoot(container);
    root.render(React.createElement(Counter,{initVal:12}));
  </script>
</html>
img30.png

从React组件的事件处理中调用网页的处理。

在React中,通过给props添加callback函数,并在组件中调用它(传入count值),

回调计数器.tsx

import { FC, useState } from 'react';
import styled from 'styled-components';
const Content = styled.div`
  border: solid 1px black;
  width: fit-content;
  padding: 4px;
`;

type propType = {
  initVal?: number;
  callback: (num: number) => number;
};
const CallbackCounter: FC<propType> = ({ initVal = 0, callback }) => {
  const [count, setCount] = useState(initVal);
  const handleClick = () => {
    setCount((n) => n + 1);
    if (callback) {
      callback(count + 1);
    }
  };
  return (
    <Content>
      <div>Count: {count}</div>
      <button onClick={handleClick}>Increment</button>
    </Content>
  );
};
export default CallbackCounter;

将CallbackCounter导出。

lib.ts的本地化

export { default as React } from 'react';
export { default as ReactDOM } from 'react-dom';
export { default as ReactDOMClient } from 'react-dom/client';
// Component
export { default as Counter } from './Counter';
+ export { default as CallbackCounter } from './CallbackCounter';

在HTML中,将回调函数传递给组件。回调函数通过参数接收count的值并进行显示。

测试3.html

<!DOCTYPE html>
<html>
  <body>
    <h2>React側のイベントをWebページ側に通知する</h2>
    <p>
      callback:<span id="callback">
    </p>
    <div id="root"></div>
  </body>
  <script src="/dist/lib.umd.js"></script>
  <script>
    const {ReactDOMClient, React, CallbackCounter} = MyBundle;
    const $ = (selectors) => document.querySelector(selectors);
    const root = ReactDOMClient.createRoot($('#root'));
    root.render(
      React.createElement(CallbackCounter,{
        callback: (val)=> $('#callback').innerText = `${val}`}
      )
    );
  </script>
</html>

点击按钮后,将调用回调函数,并更新后面的计数值。

img40.png

从网页的事件处理中调用React内部的处理。

为了调用React处理,可以使用自定义事件从网页端进行。

创建一个组件来接收自定义事件。

ButtonClickという名前のカスタムイベントを受信する
イベントのデータ(日時)を受け取り、yyyyMMdd HHmmss形式にフォーマットして表示する

事件接收器.tsx

import { useState, useEffect } from 'react';
import styled from 'styled-components';

// カスタムイベントの型設定
declare global {
  interface DocumentEventMap {
    ButtonClick: CustomEvent<Date>;
  }
}

const Content = styled.div`
  border: solid 1px black;
  width: fit-content;
  padding: 4px;
`;

const formatDate = (date: Date) => {
  return new Intl.DateTimeFormat('ja-jp', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
  }).format(date);
};

const EventReceiver = () => {
  const [log, setLog] = useState('');
  useEffect(() => {
    document.addEventListener('ButtonClick', appendEventLog);
    return () => {
      document.removeEventListener('ButtonClick', appendEventLog);
    };
  }, []);

  function appendEventLog(data: CustomEvent<Date>) {
    setLog((value) => value + formatDate(data.detail) + '\r\n');
  }

  return (
    <Content>
      <p>React Component</p>
      <textarea value={log} style={{ height: '10em' }}></textarea>
    </Content>
  );
};
export default EventReceiver;

将EventReceiver导出

lib.ts的翻译如下:lib.ts

export { default as React } from 'react';
export { default as ReactDOM } from 'react-dom';
export { default as ReactDOMClient } from 'react-dom/client';
// Component
export { default as Counter } from './Counter';
export { default as CallbackCounter } from './CallbackCounter';
export { default as EventReceiver } from './EventReceiver';

2:从网页端发送自定义事件。

在按钮被点击时,触发一个事件并传递当前时间。

测试4.html

<!DOCTYPE html>
<html>
  <body>
    <h2>Webページ側のイベントから、React側の処理を呼び出す</h2>
    <p>
      callback:<span id="callback">
    </p>
    <div id="root"></div>
  </body>
  <script src="/dist/lib.umd.js"></script>
  <script>
    const {ReactDOMClient, React, EventReceiver} = MyBundle;
    const $ = (selectors) => document.querySelector(selectors);
    $('#outerButton').addEventListener('click', () => {
        // イベントを配信
        const event = new CustomEvent('ButtonClick', {detail: new Date()});
        document.dispatchEvent(event);
    });

    const root = ReactDOMClient.createRoot($('#root'));
    root.render(
      React.createElement(EventReceiver)
    );
  </script>
</html>

点击按钮后,通过自定义事件通知React可行。

img50.png

请参考以下页面

    • 既存のページに部分的にReactを導入する

 

    • React部分導入時の開発・検証環境紹介

 

    • 非SPAなサービスにReactを導入する

 

    Reactを部分導入する場合にReact root外の要素と連携する
广告
将在 10 秒后关闭
bannerAds