>웹 프론트엔드 >프런트엔드 Q&A >작은 프로그램이 반응할 수 있나요?

작은 프로그램이 반응할 수 있나요?

藏色散人
藏色散人원래의
2022-12-29 11:06:272869검색

미니 프로그램은 반응을 사용할 수 있습니다. 사용 방법: 1. "react-reconciler"를 기반으로 렌더러를 구현하고 DSL을 생성합니다. 2. DSL을 구문 분석하고 렌더링하기 위한 미니 프로그램 구성 요소를 만듭니다. 3. npm을 설치하고 빌드를 실행합니다. 4. 자신의 페이지에 패키지를 소개한 다음 API를 사용하여 개발을 완료합니다.

작은 프로그램이 반응할 수 있나요?

이 튜토리얼의 운영 환경: Windows 10 시스템, 반응 버전 18.0.0, Dell G3 컴퓨터.

미니 프로그램에서 React를 사용할 수 있나요?

그렇습니다.

WeChat 미니 프로그램에서 직접 React 구성 요소 실행

크로스 엔드 개발을 공부할 때 중요한 목표 중 하나는 React 구성 요소를 WeChat 미니 프로그램에서 실행할 수 있도록 하는 것입니다. 이 과정에서 저는 위챗 미니 프로그램의 구조를 탐구하고 많은 생각을 하게 되었습니다. 크로스엔드 개발의 경우, 플랫폼마다 제공하는 기능이 다르기 때문에 실제로 한 번 작성하고 어디에서나 실행하기는 어렵습니다. 예를 들어 WeChat 애플릿은 카메라를 켜는 등의 기본 기능이나 기본이 필요한 기타 기능을 제공합니다. 환경을 지원합니다. WeChat 미니 프로그램의 개발도 webview에서 수행되지만 네이티브 사고가 필요합니다. 따라서 한 번 쓰기를 수행하려면 몇 가지 제한 사항이 있어야 합니다. 이러한 제한 사항으로 인해 미니 프로그램의 기능을 완전히 활용할 수 없고 일부 레이아웃 기능만 사용할 수 있습니다. 그러므로 저는 모든 사람에게 크로스 터미널 개발을 할 때 정신적으로 준비를 하라고 조언합니다. 그런데 크로스엔드 개발에서 벗어나 이제는 작은 프로그램만 개발한다면, 나에게 익숙한 리액트를 사용해 개발할 수 있을까요? 혹시, 제가 개발한 nautil 프레임워크를 개발에 사용해도 되나요? 대답은 '예'입니다. 이 기사에서는 자신만의 React 애플릿 개발을 실현하는 방법을 단계별로 안내하고 특정 시나리오에서 React 프로젝트를 애플릿으로 마이그레이션하는 목표를 완료하는 데 도움을 줄 것입니다.

미니 프로그램에서 React를 실행하기 위한 솔루션 비교

현재 업계에서는 React 구성 요소를 실행하기 위해 미니 프로그램(달리 명시하지 않는 한 미니 프로그램은 구체적으로 WeChat 미니 프로그램을 참조함)을 더 잘 지원할 수 있습니다. 즉, JD라는 3가지 솔루션 세트가 있습니다. .com 연구소의 Taro, Ant Financial 팀의 remax(구체적인 팀 이름은 발견되지 않음), WeChat 팀의 kbone입니다.

Taro

  • 컴파일됨, 새 버전도 런타임을 기반으로 함

  • wxml+js로 구문 분석됨

  • 오래된 브랜드, 지속적인 개발, 전체 플랫폼 지원, 지속적인 반복

Remax

  • 런타임, 컴파일된 매크로 포함

  • 조정자 기반

  • 가장 우아하고 증분적인 업데이트

  • 충분히 성숙하지 않아 후속 개발을 알 수 없음

Kbone, 웹팩에 의존

  • 직접 달성하기 DOM API 세트

  • vue 및 DOM 렌더링 기반의 모든 프레임워크와 호환 가능

  • 성능 문제(전체 검사), 업데이트가 거의 중단됨

  • 세 가지 솔루션 세트 서로 다르며 각자의 생각으로는 모두가 독특합니다. 개인적으로 크로스엔드 개발을 고려하지 않는다면 DOM 인터페이스가 HTML 표준이고 표준 세트를 직접 만들 필요가 없고 일단 DOM API 세트를 직접 구현하는 것이 매우 중요합니다. DOM API가 구현되면 DOM을 기반으로 하는 다른 모든 애플리케이션은 이론적으로 DOM에서 실행을 지원할 수 있습니다. 그러나 단점은 플랫폼을 변경할 때마다 해당 플랫폼에 대한 일련의 DOM API를 구현해야 한다는 것입니다. DOM 인터페이스 표준이 매우 크고 구현 중에 버그가 발생하기 쉽기 때문에 이 비용이 매우 높습니다. 내 생각에 가장 우아한 구현은 Remax의 아이디어인데, 이는 React-reconciler를 기반으로 렌더러를 만드는 것입니다. 이 렌더러는 반응 구성 요소 인스턴스를 통합 DSL로 추상화하고 이 DSL을 다른 방식으로 구문 분석하고 렌더링합니다. 플랫폼.

  • 하지만 remax가 반복적으로 업데이트된 후 자체 컴파일 도구에 크게 의존하기 시작했고 이로 인해 프로젝트에서 사용을 직접적으로 포기하게 되었습니다. 왜냐하면 우리 자신의 프로젝트에서는 실제로 그 모든 것이 필요하지 않을 수도 있기 때문입니다. 우리는 전체 작은 프로그램의 특정 부분(예를 들어 작은 프로그램에 렌더링하려는 반응으로 작성된 일부 h5)을 완료하는 데만 React를 사용합니다. , 우리는 여전히 원래 프로젝트의 다른 부분을 실행합니다). 컴파일 도구에 대한 의존성이 있는 경우 전체 프로젝트를 해당 컴파일 도구로 마이그레이션해야 합니다. 그런 다음 오래되고 안정적인 도구인 taro를 사용하는 것이 좋습니다.

전체 구현 아이디어

몇 가지 연구 끝에 저는 remax 아이디어를 채택하기로 결정했습니다. 즉, React-reconciler를 기반으로 렌더러를 구현하고 DSL을 생성한 다음 작은 프로그램 구성 요소를 만들어 DSL을 구문 분석하고 렌더링하는 것입니다. . 구현을 완료한 후 이 모든 로직을 최종 제품에 구축하고 제품을 npm 형식으로 게시했습니다. 소규모 프로그램 개발자의 경우 npm을 설치하고 개발자 도구에서 npm 빌드를 실행한 다음 이 패키지를 도입하면 됩니다. 추가 컴파일 도구를 사용할 필요 없이 자신의 페이지에 API를 사용하여 개발을 완료할 수 있습니다.

이 솔루션의 가장 큰 장점은 컴파일 도구에 대한 의존성이 약해서(없음) 도구 스택을 전환하기 위해 추가 컴파일 도구를 도입할 필요 없이 모든 프로젝트에서 우리 솔루션을 실행할 수 있다는 것입니다. 또한 reconciler 부분이 npm 패키지에 패키징되어 있기 때문에 독립적으로 실행할 수 있는 모듈이므로 이 npm 패키지를 사용하여 Vue 스타일이나 미니 프로그램 네이티브 스타일 프로젝트에서 React 컴포넌트를 렌더링할 수도 있습니다. mpvue와 같은.

작은 프로그램이 반응할 수 있나요?

WeChat 애플릿에서 React 컴포넌트를 실행하는 아이디어

위 그림과 같이 React-Reconciler 기반의 렌더러를 통해 React 컴포넌트를 전달하여 DSL의 순수 객체(콜백 함수 포함)를 생성합니다. 페이지의 js 파일에서 이 개체는 this.setData를 통해 렌더링 스레드로 전송되고, 우리가 제공하는 자체 참조 중첩 구성 요소는 wxml에서 DSL을 렌더링하는 데 사용됩니다. 여기서 주목해야 할 점은 컴포넌트가 업데이트될 때 React-reconciler가 해당 후크를 트리거한다는 것입니다. 이때 새로운 DSL이 다시 생성되고 this.setData를 통해 렌더링이 다시 전송됩니다. 따라서 이 렌더러의 결과는 단순히 createElement를 사용한 결과와 다릅니다. 렌더러는 Hooks와 같은 React의 내장 기능을 지원합니다.

다음으로, 이 글에서 설명한 코드를 최대한 손으로 직접 작성하여, 여러분의 프로젝트에서도 이 글과 동일한 효과를 얻을 수 있도록 구체적인 내용을 설명하겠습니다. 이 웨어하우스를 로컬로 복제하고, 실행 효과를 확인하고, 전체 구현 프로세스를 연구할 수 있습니다.

리액트 구성요소를 순수 JS 객체로 렌더링합니다

리액트 렌더러는 본질적으로 리액트 스케줄링 시스템을 기반으로 하는 사이드 이펙트 실행기입니다. 사이드 이펙트의 결과는 웹 환경에서의 DOM 작업이며, 이를 렌더링이라고 합니다. 엔진은 그래픽을 래스터화합니다. 아트 환경에서는 사운드 카드를 호출하여 이번에는 애플릿에 전달할 수 있도록 순수한 js 객체를 생성해야 합니다. 애플릿의 두 스레드 사이에 함수가 전달되고 인터페이스는 이 객체를 기반으로 애플릿에 렌더링됩니다.

몇몇 학생들이 저에게 묻더군요. jsx 컴파일 후 React.createElement의 실행 결과가 순수 JS 객체 아닌가요? 여기에서 React의 본질을 이해해야 합니다. React의 구성요소는 실제로 React로 표현되는 특정 객체의 구조를 설명하는 React에 대한 설명 시스템을 제공합니다. 그러나 이 설명은 추상적이며 인스턴스화하고 실행할 때만 의미가 있습니다. 구성 요소에서 작성하는 설명은 jsx 부분뿐만 아니라 비즈니스 및 프로그램 수준 논리도 포함합니다. 예를 들어, 많은 시나리오에서는 다양한 인터페이스를 렌더링하기 위해 구성 요소 상태에 따라 반환할 jsx 부분을 결정해야 합니다. 콘텐츠의 이 부분은 실행 환경, 즉 반응 렌더러에 의존해야 합니다.

과거에는 React-dom을 시뮬레이션하고 운영 로직에 따라 렌더러를 직접 작성할 수만 있었습니다. 이제 React는 개발자가 React의 스케줄링 시스템에 빠르게 액세스하여 자신만의 렌더러를 구축할 수 있도록 스케줄러인 React-reconciler를 위한 특수 라이브러리를 만들었습니다. React-Reconciler의 기본적인 사용법과 효과를 소개하는 영상(Bring Your Own Ladder)입니다.

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 실행 시간의 모든 측면에서 몇 가지 부작용 작업을 수행했습니다. 이러한 부작용의 핵심은 React가 실행될 때 수명 주기를 거치는 것입니다. 내 동영상 중 하나에서 나는 React의 라이프사이클의 구체적인 프로세스에 대해 이야기했습니다. 내 개인 WeChat 공개 계정 wwwtangshuangnet을 팔로우하고 나와 관련 문제에 대해 논의할 수도 있습니다. 각 라이프사이클 노드에서 스케줄러는 부작용, 즉 내가 제공한 순수 js 객체를 수정하는 작업을 수행합니다.

미니 프로그램의 렌더러에서 생성된 js 객체를 얻는 두 가지 방법을 제공합니다. 이 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.