ホームページ  >  記事  >  ウェブフロントエンド  >  小さなプログラムでも反応できますか?

小さなプログラムでも反応できますか?

藏色散人
藏色散人オリジナル
2022-12-29 11:06:272846ブラウズ

ミニ プログラムは React を使用できます。 使用方法: 1. "react-reconciler" に基づいてレンダラーを実装し、DSL を生成します。 2. DSL を解析してレンダリングするミニ プログラム コンポーネントを作成します。 3. インストールします。 npm を作成し、開発者ツールでビルド npm を実行します; 4. パッケージを独自のページに導入し、API を使用して開発を完了します。

小さなプログラムでも反応できますか?

このチュートリアルの動作環境: Windows 10 システム、react18.0.0 バージョン、Dell G3 コンピューター。

ミニ プログラムは React を使用できますか? ############できる。

React コンポーネントを WeChat ミニ プログラムで直接実行する

クロスエンド開発を研究する場合、私の重要な目標の 1 つは、React コンポーネントを WeChat ミニ プログラムで実行できるようにすることです。 WeChat アプレット。このプロセス中に、私は WeChat ミニ プログラムのアーキテクチャを調査し、多くの考えを呼び起こしました。クロスエンド開発に関しては、各プラットフォームが提供する機能が異なるため、一度書いたらどこでも実行することは実際には困難です。たとえば、WeChat アプレットは、カメラをオンにするなどのネイティブ機能を提供します。サポート機能、WeChat ミニ プログラムの開発も Webview で実行されますが、ネイティブな思考が必要です。したがって、ライトワンスを実現するにはいくつかの制限が必要であり、これらの制限により、ミニ プログラムの機能を完全に活用することができず、一部のレイアウト機能のみが使用されることになります。したがって、クロスターミナル開発を行う際には、心の準備をしておくことをお勧めします。しかし、クロスエンド開発から離れ、小さなプログラムのみを開発するようになった場合、使い慣れた React を使用して開発できるでしょうか?また、私が開発した nautil フレームワークを開発に使用することはできますか?答えは「はい」です。この記事では、独自の React アプレット開発を実現するための手順を段階的に説明し、特定のシナリオで React プロジェクトをアプレットに移行するという目標を達成するのに役立ちます。 ミニ プログラムで React を実行するためのソリューションの比較

現在、業界は、React コンポーネントを実行するためのミニ プログラム (特に指定がない限り、ミニ プログラムとは特に WeChat ミニ プログラムを指します) をより適切にサポートできます。これは、JD.com の Above Lab の taro、Ant Financial のチームの remax (具体的なチーム名は見つかりませんでした)、WeChat のチームの kbone を含む 3 セットのソリューションです。

Taro

コンパイル済み、新しいバージョンもランタイムに基づいています

  • wxml js に解析済み

  • 古いブランド、継続的な開発、完全なプラットフォームのサポート、継続的な反復

  • Remax

ランタイム、コンパイルされたマクロ付き

  • リコンサイラーに基づく

  • 最もエレガントな増分アップデート

  • 十分に成熟していないため、その後の開発は不明です

  • Kbone

実行時は webpack に依存します

  • DOM のセットを実装しますAPI を自分で作成

  • DOM レンダリングをベースにしたあらゆるフレームワークでも vue と互換性あり

  • パフォーマンスの問題 (完全な検査)、更新がほぼ停止

  • 3 つのプランはそれぞれ異なり、それぞれのアイデアがユニークです。個人的には、クロスエンド開発を考慮しない場合、DOM API のセットを自分で実装することは非常に価値があります。DOM インターフェイスは HTML 標準であり、標準のセットを自分で考案する必要がないためです。 DOM API が実装されている場合、DOM に基づく他のすべてのアプリケーションは理論的にはその上での実行をサポートできます。ただし、プラットフォームを変更するたびに、そのプラットフォーム用の DOM API セットを実装する必要があり、DOM インターフェイス標準が非常に大きく、実装中にバグが発生しやすいため、コストが非常に高くなるという欠点があります。私の考えでは、最もエレガントな実装は Remax のアイデアであり、react-reconciler に基づいてレンダラーを作成することです。このレンダラーは、反応コンポーネント インスタンスを統合 DSL に抽象化し、この DSL をさまざまなプラットフォームで解析してレンダリングします。

  • しかし、remax が繰り返し更新された後、独自のコンパイル ツールに大きく依存するようになり、それがプロジェクトでの使用を断念する直接の原因となりました。私たち自身のプロジェクトでは、実際にはそのすべてが必要ではない可能性があるため、小さなプログラム全体の特定の部分 (たとえば、小さなプログラムにレンダリングしたい React で書かれた h5 など) プログラム全体の特定の部分を完了するためにのみ React を使用します。 、元のプロジェクトの他の部分は引き続き実行します)。コンパイル ツールに依存している場合は、プロジェクト全体をそのコンパイル ツールに移行する必要があるため、古くて安定したツールである taro を使用する方がよいでしょう。

全体的な実装アイデア

いくつかの調査の後、私は remax アイデアを採用することにしました。これは、react-reconciler に基づいてレンダラーを実装し、DSL を生成し、次に、この DSL を解析してレンダリングするための小さなプログラム コンポーネントを作成します。実装が完了した後、これらすべてのロジックを最終製品に組み込み、npm の形式で製品を公開しました。小規模なプログラム開発者の場合は、npm をインストールし、開発者ツールでビルド npm を実行し、このパッケージを導入するだけで済みます。独自のページに追加し、追加のコンパイル ツールを使用せずに API を使用して開発を完了します。

このソリューションの最大の利点は、コンパイル ツールへの依存性が低い (まったくない) ため、ツール スタックを切り替えるために追加のコンパイル ツールを導入することなく、どのプロジェクトでもソリューションを実行できることです。また、リコンサイラー部分は npm パッケージにパッケージ化されているため、独立して実行できるモジュールとなっており、この npm パッケージを使用して、Vue スタイルまたはミニプログラム ネイティブ スタイルのプロジェクトで反応コンポーネントをレンダリングすることもできます。 mpvueなど。

小さなプログラムでも反応できますか?

WeChat アプレットで反応コンポーネントを実行するというアイデア

上の図に示すように、反応に基づいてレンダラーを通じて反応コンポーネントを作成します。 -reconciler DSL の純粋なオブジェクト (コールバック関数を含む) を取得します。ページの js ファイルでは、このオブジェクトを this.setData を通じてレンダリング スレッドに送信します。wxml では、提供する自己参照のネストされたコンポーネントを使用します。 DSL レンダリングを実行します。ここで注意すべき点は、コンポーネントが更新されると、react-reconciler が対応するフックをトリガーすることです。このとき、新しい DSL が再度生成され、レンダリングが this.setData を通じて再度送信されます。したがって、単純に createElement を使用した場合とは結果が異なり、フックなどの React の組み込み関数をサポートしています。

次は、この記事に記載されているコードをできるだけ手書きして、ご自身のプロジェクトでこの記事と同じ効果を実現できるように、具体的な内容を説明していきます。このウェアハウスのクローンをローカルに作成し、実行効果を確認し、実装プロセス全体を検討できます。

反応コンポーネントを純粋な JS オブジェクトとしてレンダリングする

反応レンダラーは、基本的に、反応スケジューリング システムに基づく副作用実行プログラムです。副作用の結果は、 Web環境 DOMの操作です ネイティブ環境ではレンダリングエンジンを呼び出してグラフィックスをラスタライズします アート環境ではサウンドカードを呼び出して音を再生します 今回の計画ではレンダラーにハンドオーバーを容易にするために純粋な js オブジェクトを生成します。アプレットは、アプレットの 2 つのスレッド間でメッセージ本文として渡され、インターフェイスはこのオブジェクトに基づいてアプレット内にレンダリングされます。

何人かの学生から「jsx コンパイル後の React.createElement の実行結果は純粋な JS オブジェクトではないのですか?」と質問されました。ここで、react の本質を理解する必要があります。実際、react のコンポーネントは、react で表現される特定のオブジェクトの構造を記述する、react の記述システムを提供します。ただし、この説明は抽象的なものであり、インスタンス化して実行する場合にのみ意味を持ちます。コンポーネント内で行う記述は jsx 部分だけではなく、ビジネス レベルおよびプログラム レベルのロジックも含まれます。たとえば、多くのシナリオでは、さまざまなインターフェイスをレンダリングするために、コンポーネントのステータスに基づいて jsx のどの部分を返すかを決定する必要があります。コンテンツのこの部分は、実行環境 (反応レンダラー) に依存する必要があります。

以前は、react-dom をシミュレートし、その動作ロジックに従ってレンダラーを自分で書くことしかできませんでした。現在、React は、開発者が React のスケジューリング システムにすぐにアクセスして独自のレンダラーを構築できるようにするために、スケジューラ用の特別なライブラリ、react-reconciler を作成しました。こちらはreact-reconcilerの基本的な使い方と効果を紹介した動画(ラダー持参)です。

import Reconciler from 'react-reconciler'
const container = {}
const HostConfig = {
  // ... 极其复杂的一个配置
}
const reconcilerInstance = Reconciler(HostConfig)
let rootContainerInstance = null
export function render(element, { mounted, updated, created }) {
  if (!rootContainerInstance) {
    rootContainerInstance = reconcilerInstance.createContainer(container, false, false)
  }
  return reconcilerInstance.updateContainer(element, rootContainerInstance, null, () => {
    notify = { mounted, updated, created }
    created && created(container)
    mounted(container.data)
  })
}

上記のコードでは、指定されていない HostConfig の特定の内容がキーです。これは Reconciler を準備するために使用されます。コードの観点から見ると、これはフック関数のコレクションです。設定する必要があります。これを各フック関数内に記述します。コンテナを操作するための副作用をいくつか記述します。さまざまな時点で、渡した作成、マウント、および更新が呼び出され、操作されたコンテナを受け取り、js を取得できることがわかります。オブジェクト (コンテナ上にもコンテナがあります。いくつかの関数がありますが、this.setData が自動的にクリアするため無視できます)。

この設定内容は複雑すぎてわかりやすく説明すると紙面が多くなってしまうため、ソースコードのアドレスを直接貼り付けておきます。コードのこの部分を分割し、独自のコンポーネントを実行し、console.log を使用してそれらが呼び出されるタイミングと順序を観察できます。

つまり、これらのインターフェイスは複雑なロジックではなく知識レベルであり、各構成アイテムの役割と実行タイミングを理解した後、独自のレンダラーを作成できます。理論的には、それほど難しいことではありません。

react-reconciler に基づいて、react の実行のあらゆる側面でいくつかの副作用を実行しました。これらの副作用の本質は、純粋な JS オブジェクトを変更することです。react が実行されると、ライフを通過します。サイクルについては、React のライフサイクルの特定のプロセスに関する私のビデオの 1 つで説明されています。また、私の個人的な WeChat 公開アカウント wwwtangshuangnet をフォローして、関連する問題について私と話し合うこともできます。各ライフサイクル ノードで、スケジューラは副作用を実行します。つまり、提供した純粋な js オブジェクトを変更します。

ミニ プログラムのレンダラーで生成された js オブジェクトを取得するための 2 つのメソッドを提供します。この js オブジェクトを取得した後、アプレットの this.setData を呼び出し、このオブジェクトをレンダリングのためにレンダリング スレッドに送信できます。

利用react渲染器得到的纯对象上存在一些函数,调用这些函数会触发它们对应的逻辑(比如调用setState触发hooks状态更新),从而触发调度器中的钩子函数执行,container对象再次被修改,updated被再次调用,this.setData被再次执行,这样,就实现了真正的react运行时在小程序中的植入。

嵌套递归自引用组件

渲染线程接收到this.setData发送过来的js对象后,如何将这个对象作为布局的信息,渲染到界面上呢?由于小程序的特殊架构,它为了安全起见,渲染线程中无法执行可操作界面的脚本,所有的渲染,都得依靠模板语法和少量的wxs脚本。所以,要怎么做呢?

小程序提供了自定义组件的功能,在app.json或对应的page.json中,通过usingComponents来指定一个路径,从而可以在wxml中使用这个组件。而有趣的地方在于,组件本身也可以在组件自己的component.json中使用usingComponents这个配置,而这个配置的内容,可以直接指向自己,例如,我在自己的组件中,这样自引用:

// dynamic.json
{
  "usingComponents": {
    "dynamic": "./dynamic"
  }
}

自己引用自己作为组件之后,在其wxml中,我们就可以使用组件自己去渲染子级数据,即一种嵌套递归的形式进行渲染。

我规定了一种特别的数据结构,大致如下:

{
  type: 'view',
  props: {
    class: 'shadow-component',
    bindtap: (e) => { ... },
  },
  children: [
    {
      type: 'view',
      props: {},
      children: [
        ...
      ],
    },
  ],
}

模板中,通过对type的判断,选择不同的模板代码进行渲染。

<block wx:if="{{ type === &#39;view&#39; }}">
  <view class="{{ props.class }}" bindtap="bindtap">
    <block wx:if="{{ children.length }}" wx:for="{{ children }}">
      <dynamic data="{{ item }}" /> <!-- 嵌套递归 -->
    </block>
  </view>
</block>

在wxml中把所有组件通过这种形式枚举出来之后,这个组件就能按照上述的数据结构递归渲染出整个结构。

当然,这里还需要处理一些细节,例如响应data的变化,事件响应函数等,你可以通过源码了解具体要怎么处理。另外,微信小程序this.setData限制在1M以内,我虽然还没有尝试过很大的数据,但是,这个限制肯定在将来是一个风险点,我现在还没有解决,还在思考应该怎么最小化更新粒度。

不支持直接JSX的变通方法

小程序的编译,没有办法自己配置支持新语法,所以如果我们在小程序代码中使用jsx,就必须先走一遍自己的编译逻辑。有两种解决办法,一种是不使用jsx语法,而是使用hyperscript标记语法,比如:

import { createElement as h } from &#39;react&#39;
function Some() {
  return h(
    &#39;view&#39;,
    { class: &#39;some-component&#39; },
    h(
      &#39;view&#39;,
      { class: &#39;sub-view&#39; },
      &#39;一段文字&#39;,
    ),
    &#39;一段文字&#39;,
  )
}

这样的写法显然没有直接写jsx来的方便,但是阅读上没有什么障碍,且不需要将jsx编译的过程。

另一种办法是走一遍编译,在小程序的页面目录下,创建一个页面同名的.jsx文件,再利用bebel将它编译为.js文件。但是这样的话,你需要在发布小程序的时候,忽略掉所有的.jsx文件。另外,还有一个坑是,小程序的编译不提供process.env,所以编译react的结果用的时候会报错。解决办法是把react的cjs/react.production.min.js作为react的入口文件,通过小程序的构建npm的相关配置逻辑,指定react构建的文件。

结语

本文详细讲解了如何在微信小程序中直接运行react组件的思路,同时,你可以参考这个仓库,运行效果看看,研究它的整个实现过程。总结而言,这个方法分为3个部分:1. 基于react-reconciler实现一个把react组件渲染为纯js对象的渲染器,之所以需要纯js对象,是因为小程序发送到渲染线程的数据必须是纯对象。2. 利用小程序的自定义组件,实现自引用嵌套递归的组件,用于利用上一步得到的js对象渲染成真正的界面。3. 解决jsx问题,将前两步的结果,在page中进行实施,以真正完成在小程序中渲染react组件的效果。当然,本文阐述过程,仅仅提供了这套思路,在真正用到项目中时,使用过程中肯定还会遇到一些坑,仅能作为原有小程序开发项目的补充手段,比如之前写好的react组件不想重新写成小程序版本,那么就可以使用这个方法,同时在渲染组件的地方,把DOM的标签,映射为小程序的标签,就可以在一定程度上解决原有react代码复用的问题。如果你在实操过程中遇到什么问题,欢迎在本文下方留言讨论~

文中链接:
Nautil框架:https://github.com/tangshuang/nautil
演示仓库:https://gitee.com/frustigor/wechat-dynamic-component
Building a Custom React Rendere: https://www.youtube.com/watch?v=CGpMlWVcHok

推荐学习:《react视频教程

以上が小さなプログラムでも反応できますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。