国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

目錄
專案描述
實(shí)作方案
建構(gòu)小程式框架產(chǎn)物
React運(yùn)行到小程式平臺(tái)方案分析
編譯時(shí)實(shí)現(xiàn)
運(yùn)行時(shí)實(shí)現(xiàn)
總結(jié)
項(xiàng)目地址
首頁 微信小程式 小程式開發(fā) React如何建立小程式??jī)煞N實(shí)現(xiàn)方案分享

React如何建立小程式??jī)煞N實(shí)現(xiàn)方案分享

Dec 23, 2021 am 10:27 AM
react 小程式

React如何建立小程式?以下這篇文章給大家透過1500行程式碼揭秘React如何運(yùn)作到小程式平臺(tái),介紹一下React 建構(gòu)小程式兩種實(shí)作方案,希望對(duì)大家有幫助!

React如何建立小程式??jī)煞N實(shí)現(xiàn)方案分享

你是否使用過 Taro、Remax 類似的框架?你是否想了解這類框架如何實(shí)作 React 程式碼運(yùn)行到小程式平臺(tái)?如果是的話,那麼也許你可以花喝一杯咖啡的時(shí)間繼續(xù)往下閱讀,本文將透過兩種方案實(shí)現(xiàn) React 運(yùn)行到小程式平臺(tái)。如果你現(xiàn)在就想閱讀這1500行的實(shí)作程式碼,那麼可以直接點(diǎn)擊專案原始碼來取得(也許要多喝幾杯咖啡)。

專案描述

為了更清楚地描述實(shí)現(xiàn)過程,我們把實(shí)作方案當(dāng)作一個(gè)專案來對(duì)待。
專案需求:使下列計(jì)數(shù)器功能的 React 程式碼運(yùn)作到微信小程式平臺(tái)。

import React, { Component } from 'react'
import { View, Text, Button } from '@leo/components'
import './index.css'

export default class Index extends Component {
  constructor() {
    super()
    this.state = { count: 0 }
    this.onAddClick = this.onAddClick.bind(this)
    this.onReduceClick = this.onReduceClick.bind(this)
  }
  componentDidMount () {
    console.log('執(zhí)行componentDidMount')
    this.setState({ count: 1 })
  }
  onAddClick() {
    this.setState({ count: this.state.count + 1 })
  }
  onReduceClick() {
    this.setState({ count: this.state.count - 1 })
  }
  render () {
    const text = this.state.count % 2 === 0 ? '偶數(shù)' : '奇數(shù)'
    return (
      <View className="container">
        <View className="conut">
          <Text>count: {this.state.count}</Text>
        </View>
        <View>
          <Text className="text">{text}</Text>
        </View>
        <Button onClick={this.onAddClick} className="btn">+1</Button>
        <Button onClick={this.onReduceClick} className="btn">-1</Button>
      </View>
    )
  }
}

如果使用過 Taro 或 Remax 等框架,對(duì)上述程式碼應(yīng)該有似曾相識(shí)的感覺,上述程式碼正式模仿這類框架的 React DSL 寫法。如果想迫切看到實(shí)現(xiàn)這個(gè)需求的效果,可點(diǎn)選此項(xiàng)目原始碼進(jìn)行取得原始碼,然後根據(jù)提示運(yùn)行項(xiàng)目,即可觀察到如下效果:

React如何建立小程式??jī)煞N實(shí)現(xiàn)方案分享

到這裡,就清楚了知道這個(gè)專案的需求以及最終實(shí)現(xiàn)結(jié)果是什麼,接下來便是重點(diǎn)闡述從需求點(diǎn)到結(jié)果這個(gè)過程的具體實(shí)現(xiàn)。

實(shí)作方案

建構(gòu)小程式框架產(chǎn)物

開發(fā)過小程式的同學(xué)都知道,小程式框架包含主體和頁面,其中主體是由三個(gè)檔案生組成的,且必須放在根目錄,這三個(gè)檔案分別是: app.js (必需,小程式邏輯),app.json(必需,小程式公共配置),app.wxss(非必須,小程式公共樣式表)。所以要將 React 程式碼建構(gòu)成小程式碼,首先需要先生成app.jsapp.json檔。因?yàn)檫@次轉(zhuǎn)換未涉及到app.js文件,所以app.js內(nèi)容可以直接寫死 App({})取代。 app.json是設(shè)定文件,可以直接在React 工程新增一個(gè)app.config.js用來填入設(shè)定內(nèi)容,即React 程式碼工程目錄如下:

├── src
│   ├── app.config.js          // 小程序配置文件,用來生成app.json內(nèi)容      
│   └── pages
│       └── index
│           ├── index.css
│           └── index.jsx      // React代碼,即上述計(jì)數(shù)器代碼
└── tsconfig.json

app.config.js內(nèi)容就是小程式全域設(shè)定內(nèi)容,如下:

module.exports = {
  pages: [&#39;pages/index/index&#39;],
  window: {
    navigationBarTitleText: &#39;react-wxapp&#39;,
    navigationBarBackgroundColor: &#39;#282c34&#39;
  }
};

有了這個(gè)設(shè)定文件,就可以透過以下方式產(chǎn)生app.jsapp.json檔案。

/*outputDir為小程序代碼生成目錄*/
fs.writeFileSync(path.join(outputDir, &#39;./app.js&#39;), `App({})`)
fs.writeFileSync(path.join(outputDir, &#39;./app.json&#39;), JSON.stringify(config, undefined, 2)) // config即為app.config.js文件內(nèi)容

小程式頁面則是由四種類型檔案構(gòu)成,分別是js(必需,頁面邏輯)、wxml(必需,頁面結(jié)是構(gòu)) 、json(非必要、頁面配置)、wxss(非必要、頁面樣式表)。而React程式碼轉(zhuǎn)小程序,主要是考慮如何將React程式碼轉(zhuǎn)換程序?qū)?yīng)的jswxml類型文件,後文會(huì)詳細(xì)闡述。

React運(yùn)行到小程式平臺(tái)方案分析

實(shí)現(xiàn)React程式碼運(yùn)行到小程式平臺(tái)上主要有兩種方式,一種是編譯時(shí)實(shí)現(xiàn),一種是運(yùn)行時(shí)實(shí)現(xiàn),如果你已經(jīng)查看的本專案專案原始碼,就可以發(fā)現(xiàn)原始碼裡也體現(xiàn)了這兩種方式(編譯時(shí)實(shí)作目錄:packages/compile-core;執(zhí)行階段實(shí)作目錄:packages/runtime-core)。

編譯時(shí)方式主要透過靜態(tài)編譯將 JSX 轉(zhuǎn)換成小程式對(duì)應(yīng)的 template 來實(shí)現(xiàn)渲染,類似 Taro1.0 和 2.0,此方式效能接近原生小程序,但是語法卻有很大的限制。運(yùn)行時(shí)實(shí)作是透過react-reconciler重新在小程式平臺(tái)定義一個(gè)React 渲染器,使得React 程式碼可以真正運(yùn)行到小程式裡,類似Taro3.0、Remax 等,因此這種方式無語法限制,但是性能會(huì)比較差。本專案原始碼正是參考Taro、Remax 這類框架原始碼並簡(jiǎn)化許多細(xì)節(jié)進(jìn)行實(shí)現(xiàn)的,因此這個(gè)專案原始碼只是適合來學(xué)習(xí)的,並不能投入實(shí)際業(yè)務(wù)進(jìn)行使用。

接下來將分別講述如何通過編譯時(shí)和運(yùn)行時(shí)這兩種方式來實(shí)現(xiàn) React 運(yùn)行到小程序平臺(tái)。

編譯時(shí)實(shí)現(xiàn)

在講述具體實(shí)現(xiàn)流程之前,首先需要了解下編譯時(shí)實(shí)現(xiàn)這個(gè)名詞的概念,首先這里的編譯并非傳統(tǒng)的高大上“編譯”,傳統(tǒng)意義上的編譯一般將高級(jí)語言往低級(jí)語言進(jìn)行編譯,但這里只是將同等水平語言轉(zhuǎn)換,即將javascript代碼字符串編譯成另一種javascript代碼字符串,因此這里的編譯更類似于“轉(zhuǎn)譯”。其次,雖然這里稱編譯時(shí)實(shí)現(xiàn),并非所有實(shí)現(xiàn)過程都是編譯的,還是需要少部分實(shí)現(xiàn)需要運(yùn)行時(shí)配合,因此這種方式稱為重編譯輕運(yùn)行方式更為合適。同樣的,運(yùn)行時(shí)實(shí)現(xiàn)也含有少量編譯時(shí)實(shí)現(xiàn),亦可稱為重運(yùn)行輕編譯方式。

為了方便實(shí)現(xiàn)將javascript代碼字符串編譯成另一種javascript代碼字符串,這里直接采用Babel工具,由于篇幅問題,這里就不詳細(xì)講述Babel用法了,如果對(duì)Babel不熟的話,可以看看這篇文章簡(jiǎn)單了解下(沒錯(cuò),就是給自己打廣告)。接下來我們來分析編譯時(shí)實(shí)現(xiàn)步驟有哪些:

1. JSX轉(zhuǎn)換成對(duì)應(yīng)小程序的模板

React是通過JSX來渲染視圖的,而小程序則通過wxml來渲染視圖,要將 React 運(yùn)行到小程序上,其重點(diǎn)就是要如何實(shí)現(xiàn)JSX轉(zhuǎn)換成對(duì)應(yīng)的小程序的wxml,其轉(zhuǎn)換規(guī)則就是將JSX使用語法轉(zhuǎn)換成小程序相同功能的語法,例如:

  • 標(biāo)簽元素轉(zhuǎn)換:View、TextButton等標(biāo)簽直接映射為小程序基礎(chǔ)組件本身(改為小寫)

  • 樣式類名轉(zhuǎn)換:className修改為class

    <View className="xxx" />  ==>  <View class="xxx" />
  • 事件轉(zhuǎn)換:如onClick修改為bindtap

    <View onClick=xxx />  ==>  <View bindtap =xxx />
  • 循環(huán)轉(zhuǎn)換:map語法修改為wx:for

    list.map(i => <Text>{i}</Text>) => <Text wx:for="{{list}}">{{item}}</Text>

語法轉(zhuǎn)換遠(yuǎn)不止上面這些類型,如果要保證開發(fā)者可以使用各種JSX語法開發(fā)小程序,就需要盡可能窮舉出所有語法轉(zhuǎn)換規(guī)則,否則很可能開發(fā)者用了一個(gè)寫法就不支持轉(zhuǎn)換。而事實(shí)是,有些寫法(比如動(dòng)態(tài)生成JSX片段等等)是根本無法支持轉(zhuǎn)換,這也是前文為什么說編譯時(shí)實(shí)現(xiàn)方案的缺點(diǎn)是語法有限制,開發(fā)者不能隨意編碼,需要受限于框架本身開發(fā)規(guī)則。

由于上述需要轉(zhuǎn)換JSX代碼語法相對(duì)簡(jiǎn)單,只需要涉及幾種簡(jiǎn)單語法規(guī)則轉(zhuǎn)換,這里直接貼出轉(zhuǎn)換后的wxml結(jié)果如下,對(duì)應(yīng)的實(shí)現(xiàn)代碼位于:packages/compile-core/transform/parseTemplate.ts。

<view class="container">
  <view class="conut"><Text>count: {{count}}</Text></view>
  <view>
    <text class="text">{{text}}</text>
  </view>
  <button bindtap="onAddClick" class="btn">+1</button>
  <button bindtap="onReduceClick" class="btn">-1</button>
</view>

2. 運(yùn)行時(shí)適配

如前文所說,雖然這個(gè)方案稱為編譯時(shí)實(shí)現(xiàn),但是要將React代碼在小程序平臺(tái)驅(qū)動(dòng)運(yùn)行起來,還需要在運(yùn)行時(shí)做下適配處理。適配處理主要在小程序js邏輯實(shí)現(xiàn),內(nèi)容主要有三塊:數(shù)據(jù)渲染、事件處理、生命周期映射。

小程序js邏輯是通過一個(gè)object參數(shù)配置聲明周期、事件等來進(jìn)行注冊(cè),并通過setData方法觸發(fā)視圖渲染:

Component({
  data: {},
  onReady () { this.setData(..) },
  handleClick () {}
})

而計(jì)數(shù)器React代碼是通過class聲明一個(gè)組件邏輯,類似:

class CustomComponent extends Component {
  state = { }
  componentDidMount() { this.setState(..)  }
  handleClick () { }
}

從上面兩段代碼可以看出,小程序是通過object聲明邏輯,React 則是通過class進(jìn)行聲明。除此之外,小程序是通過setData觸發(fā)視圖(wxml)渲染,React 則是通過 setState 觸發(fā)視圖(render方法)渲染。所以要使得 React 邏輯可以運(yùn)行到小程序平臺(tái),可以加入一個(gè)運(yùn)行時(shí)墊片,將兩者邏輯寫法通過墊片對(duì)應(yīng)起來。再介紹運(yùn)行時(shí)墊片具體實(shí)現(xiàn)前,還需要對(duì)上述 React 計(jì)數(shù)器代碼進(jìn)行簡(jiǎn)單的轉(zhuǎn)換處理,處理完的代碼如下:

import React, { Component } from "../../npm/app.js";  // 1.app.js為墊片實(shí)現(xiàn)文件
export default class Index extends Component {
  static $$events = ["onAddClick", "onReduceClick"];  // 2.收集JSX事件名稱
  constructor() {
    super();
    this.state = {
      count: 0
    };
    this.onAddClick = this.onAddClick.bind(this);
    this.onReduceClick = this.onReduceClick.bind(this);
  }
  componentDidMount() {
    console.log(&#39;執(zhí)行componentDidMount&#39;);
    this.setState({
      count: 1
    });
  }
  onAddClick() {
    this.setState({
      count: this.state.count + 1
    });
  }
  onReduceClick() {
    this.setState({
      count: this.state.count - 1
    });
  }
  createData() {                                      // 3.render函數(shù)改為createData,刪除
    this.__state = arguments[0];                      // 原本的JSX代碼,返回更新后的state
                                                      // 提供給小程序進(jìn)行setData
    const text = this.state.count % 2 === 0 ? &#39;偶數(shù)&#39; : &#39;奇數(shù)&#39;;
    Object.assign(this.__state, {
      text: text
    });
    return this.__state;
  }

}    
Page(require(&#39;../../npm/app.js&#39;).createPage(Index))。 // 4.使用運(yùn)行時(shí)墊片提供的createPage
                                                      // 方法進(jìn)行初始化
                                                      // 方法進(jìn)行初始化復(fù)制代碼

如上代碼,需要處理的地方有4處:

  • Component進(jìn)行重寫,重寫邏輯在運(yùn)行時(shí)墊片文件內(nèi)實(shí)現(xiàn),即app.js,實(shí)現(xiàn)具體邏輯后文會(huì)貼出。

  • 將原本JSX的點(diǎn)擊事件對(duì)應(yīng)的回調(diào)方法名稱進(jìn)行收集,以便在運(yùn)行時(shí)墊片在小程序平臺(tái)進(jìn)行事件注冊(cè)。

  • 因?yàn)樵?code>render方法內(nèi)JSX片段轉(zhuǎn)換為wxml了,所以這里render方法可將JSX片段進(jìn)行刪除。另外因?yàn)?code>React每次執(zhí)行setState都會(huì)觸發(fā)render方法,而render方法內(nèi)會(huì)接受到最新的state數(shù)據(jù)來更新視圖,因此這里產(chǎn)生的最新state正是需要提供給小程序的setData方法,從而觸發(fā)小程序的數(shù)據(jù)渲染,為此將render名稱重命名為createData(生產(chǎn)小程序的data數(shù)據(jù)),同時(shí)改寫內(nèi)部邏輯,將產(chǎn)生的最新state進(jìn)行返回。

  • 使用運(yùn)行時(shí)墊片提供的createPage方法進(jìn)行初始化(createPage方法實(shí)現(xiàn)具體邏輯后文會(huì)貼出),同時(shí)通過小程序平臺(tái)提供的Page方法進(jìn)行注冊(cè),從這里可得知createPage方法返回的數(shù)據(jù)肯定是一個(gè)object類型。

運(yùn)行時(shí)墊片(app.js)實(shí)現(xiàn)邏輯如下:

export class Component {                             // 重寫Component的實(shí)現(xiàn)邏輯
  constructor() {
    this.state = {}
  }
  setState(state) {                                  // setState最終觸發(fā)小程序的setData
    update(this.$scope.$component, state)
  }
  _init(scope) {
    this.$scope = scope
  }
}
function update($component, state = {}) {
  $component.state = Object.assign($component.state, state)
  let data = $component.createData(state)            // 執(zhí)行createData獲取最新的state
  data[&#39;$leoCompReady&#39;] = true
  $component.state = data
  $component.$scope.setData(data)                    // 將state傳遞給setData進(jìn)行更新
}
export function createPage(ComponentClass) {         // createPage實(shí)現(xiàn)邏輯
  const componentInstance = new ComponentClass()     // 實(shí)例化傳入進(jìn)來React的Class組件
  const initData = componentInstance.state     
  const option = {                                   // 聲明一個(gè)小程序邏輯的對(duì)象字面量
    data: initData,
    onLoad() {
      this.$component = new ComponentClass()
      this.$component._init(this)
      update(this.$component, this.$component.state)
    },
    onReady() {
      if (typeof this.$component.componentDidMount === &#39;function&#39;) {
        this.$component.componentDidMount()           // 生命邏輯映射
      }
    }
  }
  const events = ComponentClass[&#39;$$events&#39;]          // 獲取React組件內(nèi)所有事件回調(diào)方法名稱
  if (events) {
    events.forEach(eventHandlerName => {             
      if (option[eventHandlerName]) return
      option[eventHandlerName] = function () {
        this.$component[eventHandlerName].call(this.$component)
      }
    })
  }
  return option
}

上文提到了重寫Component類和createPage方法具體實(shí)現(xiàn)邏輯如上代碼所示。

Component內(nèi)聲明的state會(huì)執(zhí)行一個(gè)update方法,update方法里主要是將 React 產(chǎn)生的新state和舊state進(jìn)行合并,然后通過上文說的createData方法獲取到合并后的最新state,最新的state再傳遞給小程序進(jìn)行setData,從而實(shí)現(xiàn)小程序數(shù)據(jù)渲染。

createPage方法邏輯首先是將 React 組件實(shí)例化,然后構(gòu)建出一個(gè)小程序邏輯的對(duì)應(yīng)字面量,并將 React 組件實(shí)例相關(guān)方法和這個(gè)小程序邏輯對(duì)象字面量進(jìn)行綁定:其次進(jìn)行生命周期綁定:在小程序onReady周期里出發(fā) React 組件對(duì)應(yīng)的componentDidMount生命周期;最好進(jìn)行事件綁定:通過上文提到的回調(diào)事件名,取出React 組件實(shí)例內(nèi)的對(duì)應(yīng)的事件,并將這些事件注冊(cè)到小程序邏輯的對(duì)應(yīng)字面量?jī)?nèi),這樣就完成小程序平臺(tái)事件綁定。最后將這個(gè)對(duì)象字面量返回,供前文所說的Page方法進(jìn)行注冊(cè)。

到此,就可以實(shí)現(xiàn) React 代碼運(yùn)行到小程序平臺(tái)了,可以在項(xiàng)目源碼里執(zhí)行 npm run build:compile 看看效果。編譯時(shí)實(shí)現(xiàn)方案主要是通過靜態(tài)編譯JSX代碼和運(yùn)行時(shí)墊片結(jié)合,完成 React 代碼運(yùn)行到小程序平臺(tái),這種方案基本無性能上的損耗,且可以在運(yùn)行時(shí)墊片做一些優(yōu)化處理(比如去除不必要的渲染數(shù)據(jù),減少setData數(shù)據(jù)量),因此其性能與使用小程序原生語法開發(fā)相近甚至某些場(chǎng)景會(huì)更優(yōu)。然而這種方案的缺點(diǎn)就是語法限制問題(上文已經(jīng)提過了),使得開發(fā)并不友好,因此也就有了運(yùn)行時(shí)實(shí)現(xiàn)方案的誕生。

運(yùn)行時(shí)實(shí)現(xiàn)

從上文可以看出,編譯時(shí)實(shí)現(xiàn)之所以有語法限制,主要因?yàn)槠洳皇亲?React 真正運(yùn)行到小程序平臺(tái),而運(yùn)行時(shí)實(shí)現(xiàn)方案則可以,其原理是在小程序平臺(tái)實(shí)現(xiàn)一個(gè) React 自定義渲染器,用來渲染 React 代碼。這里我們以 remax 框架實(shí)現(xiàn)方式來進(jìn)行講解,本項(xiàng)目源碼中的運(yùn)行時(shí)實(shí)現(xiàn)也正是參照 remax 框架實(shí)現(xiàn)的。

如果使用過 React 開發(fā)過 Web,入口文件有一段類似這樣的代碼:

import React from &#39;react&#39;
import ReactDom from &#39;react-dom&#39;
import App from &#39;./App&#39;

ReactDom.render(
  App,
  document.getElementById(&#39;root&#39;)
)

可以看出渲染 Web 頁面需要引用一個(gè)叫 react-dom 模塊,那這個(gè)模塊作用是什么?react-dom是 Web 平臺(tái)的渲染器,主要負(fù)責(zé)將 React 執(zhí)行后的Vitrual DOM數(shù)據(jù)渲染到 Web 平臺(tái)。同樣的,React 要渲染到 Native,也有一個(gè)針對(duì) Native 平臺(tái)的渲染器:React Native。
React實(shí)現(xiàn)多平臺(tái)方式,是在每個(gè)平臺(tái)實(shí)現(xiàn)一個(gè)React渲染器,如下圖所示。

React如何建立小程式??jī)煞N實(shí)現(xiàn)方案分享

而如果要將 React 運(yùn)行到小程序平臺(tái),只需要開發(fā)一個(gè)小程序自定義渲染器即可。React 官方提供了一個(gè)react-reconciler 包專門來實(shí)現(xiàn)自定義渲染器,官方提供了一個(gè)簡(jiǎn)單demo重寫了react-dom。

使用react-reconciler實(shí)現(xiàn)渲染器主要有兩步,第一步:實(shí)現(xiàn)渲染函數(shù)(render方法),類似ReactDOM.render方法:

import ReactReconciler from &#39;react-reconciler&#39;
import hostConfig from &#39;./hostConfig&#39;      // 宿主配置

// 創(chuàng)建Reconciler實(shí)例, 并將HostConfig傳遞給Reconciler
const ReactReconcilerInst = ReactReconciler(hostConfig)

/**
 * 提供一個(gè)render方法,類似ReactDom.render方法
 * 與ReactDOM一樣,接收三個(gè)參數(shù)
 * render(<MyComponent />, container, () => console.log(&#39;rendered&#39;))
 */
export function render(element, container, callback) {
  // 創(chuàng)建根容器
  if (!container._rootContainer) {
    container._rootContainer = ReactReconcilerInst.createContainer(container, false);
  }
  // 更新根容器
  return ReactReconcilerInst.updateContainer(element, container._rootContainer, null, callback);
}

第二步,如上圖引用的import hostConfig from &#39;./hostConfig&#39; ,需要通過react-reconciler實(shí)現(xiàn)宿主配置(HostConfig),HostConfig是宿主環(huán)境提供一系列適配器方案和配置項(xiàng),定義了如何創(chuàng)建節(jié)點(diǎn)實(shí)例、構(gòu)建節(jié)點(diǎn)樹、提交和更新等操作,完整列表可以點(diǎn)擊查看。值得注意的是在小程序平臺(tái)未提供DOM API操作,只能通過setData將數(shù)據(jù)傳遞給視圖層。因此Remax重新定義了一個(gè)VNode類型的節(jié)點(diǎn),讓 React 在reconciliation過程中不是直接去改變DOM,而先更新VNode,hostConfig文件內(nèi)容大致如下:

interface VNode {
  id: number;              // 節(jié)點(diǎn) id,這是一個(gè)自增的唯一 id,用于標(biāo)識(shí)節(jié)點(diǎn)。
  container: Container;    // 類似 ReactDOM.render(<App />, document.getElementById(&#39;root&#39;) 中的第二個(gè)參數(shù)
  children: VNode[];       // 子節(jié)點(diǎn)。
  type: string | symbol;   // 節(jié)點(diǎn)的類型,也就是小程序中的基礎(chǔ)組件,如:view、text等等。
  props?: any;             // 節(jié)點(diǎn)的屬性。
  parent: VNode | null;    // 父節(jié)點(diǎn)
  text?: string;           // 文本節(jié)點(diǎn)上的文字
  appendChild(node: VNode): void;
  removeChild(node: VNode): void;
  insertBefore(newNode: VNode, referenceNode: VNode): void;
  ...
}

// 實(shí)現(xiàn)宿主配置
const hostConfig = {

  ...
  // reconciler提交后執(zhí)行,觸發(fā)容器更新數(shù)據(jù)(實(shí)際會(huì)觸發(fā)小程序的setData)
  resetAfterCommit: (container) => {
    container.applyUpdate();
  },
  // 創(chuàng)建宿主組件實(shí)例,初始化VNode節(jié)點(diǎn)
  createInstance(type, newProps, container) {
    const id = generate();
    const node = new VNode({ ... });
    return node;
  },
  // 插入節(jié)點(diǎn)
  appendChild(parent, child) {
    parent.appendChild(child);
  },
  // 
  insertBefore(parent, child, beforeChild) {
    parent.insertBefore(child, beforeChild);
  },
  // 移除節(jié)點(diǎn)
  removeChild(parent, child) {
    parent.removeChild(child);
  }
  
  ...
  
};

除了上面的配置內(nèi)容,還需要提供一個(gè)容器用來將VNode數(shù)據(jù)格式化為JSON數(shù)據(jù),供小程序setData傳遞給視圖層,這個(gè)容器類實(shí)現(xiàn)如下:

class Container {
  constructor(context) {
    this.root = new VNode({..});   // 根節(jié)點(diǎn)
  }

  toJson(nodes ,data) {            // 將VNode數(shù)據(jù)格式化JSON
    const json = data || []
    nodes.forEach(node => {
      const nodeData = {
        type: node.type,
        props: node.props || {},
        text: node.text,
        id: node.id,
        children: []
      }
      if (node.children) {
        this.toJson(node.children, nodeData.children)
      }
      json.push(nodeData)
    })
    return json
  }
  applyUpdate() {                 // 供HostConfig配置的resetAfterCommit方法執(zhí)行
    const root = this.toJson([this.root])[0]
    console.log(root)
    this.context.setData({ root});
  }
  ...
}

緊接著,我們封裝一個(gè)createPageConfig方法,用來執(zhí)行渲染,其中Page參數(shù)為 React 組件,即上文計(jì)數(shù)器的組件。

import * as React from &#39;react&#39;;
import Container from &#39;./container&#39;; // 上文定義的Container
import render from &#39;./render&#39;;       // 上文定義的render方法

export default function createPageConfig(component) {  // component為React組件
  const config = {  // 小程序邏輯對(duì)象字面量,供Page方法注冊(cè)
    data: {
      root: {
        children: [],
      }
    },
    onLoad() {
      this.container = new Container(this, &#39;root&#39;);
      const pageElement = React.createElement(component, {
        page: this,
      });

      this.element = render(pageElement, this.container);
    }
  };

  return config;
}

到這里,基本已經(jīng)實(shí)現(xiàn)完小程序渲染器了,為了使代碼跑起來,還需要通過靜態(tài)編譯改造下 React 計(jì)數(shù)器組件,其實(shí)就是在末尾插入一句代碼:

import React, { Component } from &#39;react&#39;;
export default class Index extends Component {
  constructor() {
    super();
    this.state = {
      count: 0
    };
    this.onAddClick = this.onAddClick.bind(this);
    this.onReduceClick = this.onReduceClick.bind(this);
  }
  ...

} 
// app.js封裝了上述createPage方法
Page(require(&#39;../../npm/app.js&#39;).createPage(Index))

通過這樣,就可以使得React代碼在小程序真正運(yùn)行起來了,但是這里我們還有個(gè)流程沒介紹,上述Container類的applyUpdate方法中生成的頁面JSON數(shù)據(jù)要如何更新到視圖?首先我們先來看下這個(gè)JSON數(shù)據(jù)長(zhǎng)什么樣子:

// 篇幅問題,這里只貼部分?jǐn)?shù)據(jù)
{
	"type": "root",
	"props": {},
	"id": 0,
	"children": [{
		"type": "view",
		"props": {
			"class": "container"
		},
		"id": 12,
		"children": [{
			"type": "view",
			"props": {
				"class": "conut"
			},
			"id": 4,
			"children": [{
				"type": "text",
				"props": {},
				"id": 3,
				"children": [{
					"type": "plain-text",
					"props": {},
					"text": "count: ",
					"id": 1,
					"children": []
				}, {
					"type": "plain-text",
					"props": {},
					"text": "1",
					"id": 2,
					"children": []
				}]
			}]
		}
 	...
 	...
 		
	}]
}

可以看出JSON數(shù)據(jù),其實(shí)是一棵類似Tree UI的數(shù)據(jù),要將這些數(shù)據(jù)渲染出頁面,可以使用小程序提供的Temlate進(jìn)行渲染,由于小程序模板遞歸嵌套會(huì)有問題(微信小程序平臺(tái)限制),因此需要提供多個(gè)同樣組件類型的模板進(jìn)行遞歸渲染,代碼如下:

<template is="TPL" data="{{root: root}}" />  <!-- root為上述的JSON數(shù)據(jù) -->


<template name="TPL">
 <block wx:for="{{root.children}}" wx:key="id">
  <template is="TPL_1_CONTAINER" data="{{i: item, a: &#39;&#39;}}" />
 </block>
</template>

    
<template name="TPL_1_view">
  <view
    style="{{i.props.style}}"
    class="{{i.props.class}}"
    bindtap="{{i.props.bindtap}}"
  >
    <block wx:for="{{i.children}}" wx:key="id">
      <template is="{{&#39;TPL_&#39; + (tid + 1) + &#39;_CONTAINER&#39;}}" data="{{i: item, a: a, tid: tid + 1 }}" />
    </block>
  </view>
</template>
  
<template name="TPL_2_view">
  <view
    style="{{i.props.style}}"
    class="{{i.props.class}}"
    bindtap="{{i.props.bindtap}}"
  >
    <block wx:for="{{i.children}}" wx:key="id">
      <template is="{{&#39;TPL_&#39; + (tid + 1) + &#39;_CONTAINER&#39;}}" data="{{i: item, a: a, tid: tid + 1 }}" />
    </block>
  </view>
</template>
  
<template name="TPL_3_view">
  <view
    style="{{i.props.style}}"
    class="{{i.props.class}}"
    bindtap="{{i.props.bindtap}}"
  >
    <block wx:for="{{i.children}}" wx:key="id">
      <template is="{{&#39;TPL_&#39; + (tid + 1) + &#39;_CONTAINER&#39;}}" data="{{i: item, a: a, tid: tid + 1 }}" />
    </block>
  </view>
</template>
 
...
...

至此,就可以真正實(shí)現(xiàn) React 代碼運(yùn)行到小程序了,可以在項(xiàng)目源碼里執(zhí)行npm run build:runtime看看效果。運(yùn)行時(shí)方案優(yōu)點(diǎn)是無語法限制,(不信的話,可以在本項(xiàng)目里隨便寫各種動(dòng)態(tài)寫法試試哦),而缺點(diǎn)時(shí)性能比較差,主要原因是因?yàn)槠?code>setData數(shù)據(jù)量比較大(上文已經(jīng)貼出的JSON數(shù)據(jù),妥妥的比編譯時(shí)方案大),因此性能就比編譯時(shí)方案差。當(dāng)然了,業(yè)界針對(duì)運(yùn)行時(shí)方案也有做大量的性能優(yōu)化,比如局部更新、虛擬列表等,由于篇幅問題,這里就不一一講解(代碼中也沒有實(shí)現(xiàn))。

總結(jié)

本文以最簡(jiǎn)實(shí)現(xiàn)方式講述了 React 構(gòu)建小程序兩種實(shí)現(xiàn)方案,這兩種方案優(yōu)缺點(diǎn)分明,都有各自的優(yōu)勢(shì),對(duì)于追求性能好場(chǎng)的場(chǎng)景,編譯時(shí)方案更為合適。對(duì)于著重開發(fā)體驗(yàn)且對(duì)性能要求不高的場(chǎng)景,運(yùn)行時(shí)方案為首選。如果想了解更多源碼實(shí)現(xiàn),可以去看下 Taro、Remax 官方源碼,歡迎互相討論。

項(xiàng)目地址

https://github.com/canfoo/react-wxapp

【相關(guān)學(xué)習(xí)推薦:小程序開發(fā)教程

以上是React如何建立小程式??jī)煞N實(shí)現(xiàn)方案分享的詳細(xì)內(nèi)容。更多資訊請(qǐng)關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願(yuàn)投稿,版權(quán)歸原作者所有。本站不承擔(dān)相應(yīng)的法律責(zé)任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請(qǐng)聯(lián)絡(luò)admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動(dòng)的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強(qiáng)大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級(jí)程式碼編輯軟體(SublimeText3)

PHP、Vue和React:如何選擇最適合的前端框架? PHP、Vue和React:如何選擇最適合的前端框架? Mar 15, 2024 pm 05:48 PM

PHP、Vue和React:如何選擇最適合的前端框架?隨著互聯(lián)網(wǎng)技術(shù)的不斷發(fā)展,前端框架在Web開發(fā)中起著至關(guān)重要的作用。 PHP、Vue和React作為三種代表性的前端框架,每一種都具有其獨(dú)特的特徵和優(yōu)勢(shì)。在選擇使用哪種前端框架時(shí),開發(fā)人員需要根據(jù)專案需求、團(tuán)隊(duì)技能和個(gè)人偏好做出明智的決策。本文將透過比較PHP、Vue和React這三種前端框架的特徵和使

Java框架與前端React框架的整合 Java框架與前端React框架的整合 Jun 01, 2024 pm 03:16 PM

Java框架與React框架的整合:步驟:設(shè)定後端Java框架。建立專案結(jié)構(gòu)。配置建置工具。建立React應(yīng)用程式。編寫RESTAPI端點(diǎn)。配置通訊機(jī)制。實(shí)戰(zhàn)案例(SpringBoot+React):Java程式碼:定義RESTfulAPI控制器。 React程式碼:取得並顯示API回傳的資料。

vue.js vs.反應(yīng):特定於項(xiàng)目的考慮因素 vue.js vs.反應(yīng):特定於項(xiàng)目的考慮因素 Apr 09, 2025 am 12:01 AM

Vue.js適合中小型項(xiàng)目和快速迭代,React適用於大型複雜應(yīng)用。 1)Vue.js易於上手,適用於團(tuán)隊(duì)經(jīng)驗(yàn)不足或項(xiàng)目規(guī)模較小的情況。 2)React的生態(tài)系統(tǒng)更豐富,適合有高性能需求和復(fù)雜功能需求的項(xiàng)目。

React與Vue:Netflix使用哪個(gè)框架? React與Vue:Netflix使用哪個(gè)框架? Apr 14, 2025 am 12:19 AM

NetflixusesAcustomFrameworkcalled“ Gibbon” BuiltonReact,notReactorVuedIrectly.1)TeamSperience:selectBasedonFamiliarity.2)ProjectComplexity:vueforsimplerprojects:reactforforforproproject,reactforforforcompleplexones.3)cocatizationneedneeds:reactoffipicatizationneedneedneedneedneedneeds:reactoffersizationneedneedneedneedneeds:reactoffersizatization needefersmoreflexibleise.4)

React在HTML中的作用:增強(qiáng)用戶體驗(yàn) React在HTML中的作用:增強(qiáng)用戶體驗(yàn) Apr 09, 2025 am 12:11 AM

React通過JSX與HTML結(jié)合,提升用戶體驗(yàn)。 1)JSX嵌入HTML,使開發(fā)更直觀。 2)虛擬DOM機(jī)制優(yōu)化性能,減少DOM操作。 3)組件化管理UI,提高可維護(hù)性。 4)狀態(tài)管理和事件處理增強(qiáng)交互性。

React的生態(tài)系統(tǒng):庫,工具和最佳實(shí)踐 React的生態(tài)系統(tǒng):庫,工具和最佳實(shí)踐 Apr 18, 2025 am 12:23 AM

React生態(tài)系統(tǒng)包括狀態(tài)管理庫(如Redux)、路由庫(如ReactRouter)、UI組件庫(如Material-UI)、測(cè)試工具(如Jest)和構(gòu)建工具(如Webpack)。這些工具協(xié)同工作,幫助開發(fā)者高效開發(fā)和維護(hù)應(yīng)用,提高代碼質(zhì)量和開發(fā)效率。

微信小程式怎麼弄會(huì)員 微信小程式怎麼弄會(huì)員 May 07, 2024 am 10:24 AM

1.開啟微信小程序,進(jìn)入對(duì)應(yīng)的小程式頁面。 2.在小程式頁面中尋找會(huì)員相關(guān)入口,通常會(huì)員入口在底部導(dǎo)覽列或個(gè)人中心等位置。 3.點(diǎn)選會(huì)員入口,進(jìn)入會(huì)員申請(qǐng)頁。 4、在會(huì)員申請(qǐng)頁面,填寫相關(guān)信息,如手機(jī)號(hào)碼、姓名等,完成資料填寫後,提交申請(qǐng)。 5.小程式方會(huì)對(duì)會(huì)員申請(qǐng)審核,審核通過後,用戶即可成為微信小程式會(huì)員。 6.作為會(huì)員,用戶將享有更多的會(huì)員權(quán)益,如積分、優(yōu)惠券、會(huì)員專屬活動(dòng)等

Netflix的前端:React(或VUE)的示例和應(yīng)用 Netflix的前端:React(或VUE)的示例和應(yīng)用 Apr 16, 2025 am 12:08 AM

Netflix使用React作為其前端框架。 1)React的組件化開發(fā)模式和強(qiáng)大生態(tài)系統(tǒng)是Netflix選擇它的主要原因。 2)通過組件化,Netflix將復(fù)雜界面拆分成可管理的小塊,如視頻播放器、推薦列表和用戶評(píng)論。 3)React的虛擬DOM和組件生命週期優(yōu)化了渲染效率和用戶交互管理。

See all articles