版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
【移動(dòng)應(yīng)用開發(fā)技術(shù)】React怎么構(gòu)建小程序
這篇文章主要介紹“React怎么構(gòu)建小程序”,在日常操作中,相信很多人在React怎么構(gòu)建小程序問題上存在疑惑,在下查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”React怎么構(gòu)建小程序”的疑惑有所幫助!接下來,請(qǐng)跟著在下一起來學(xué)習(xí)吧!項(xiàng)目描述為了更清晰描述實(shí)現(xiàn)過程,我們把實(shí)現(xiàn)方案當(dāng)作一個(gè)項(xiàng)目來對(duì)待。項(xiàng)目需求:使如下計(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í)的感覺,上述代碼正式模仿這類框架的ReactDSL寫法。如果想迫切看到實(shí)現(xiàn)這個(gè)需求的效果,可點(diǎn)擊此項(xiàng)目源碼進(jìn)行獲取源碼,然后根據(jù)提示運(yùn)行項(xiàng)目,即可觀察到如下效果:到這里,就清楚了知道這個(gè)項(xiàng)目的需求以及最終實(shí)現(xiàn)結(jié)果是什么,接下來便是重點(diǎn)闡述從需求點(diǎn)到結(jié)果這個(gè)過程的具體實(shí)現(xiàn)。實(shí)現(xiàn)方案構(gòu)建小程序框架產(chǎn)物開發(fā)過小程序的同學(xué)都知道,小程序框架包含主體和頁(yè)面,其中主體是由三個(gè)文件生組成的,且必須放在根目錄,這三個(gè)文件分別是:app.js(必需,小程序邏輯),app.json(必需,小程序公共配置),app.wxss(非必須,小程序公共樣式表)。所以要將React代碼構(gòu)建成小程序代碼,首先需要先生成app.js和app.json文件。因?yàn)楸敬无D(zhuǎn)換未涉及到app.js文件,所以app.js內(nèi)容可以直接寫死App({})代替。app.json是配置文件,可以直接在React工程新增一個(gè)app.config.js用來填寫配置內(nèi)容,即React代碼工程目錄如下:├──
src
│
├──
app.config.js
//
小程序配置文件,用來生成app.json內(nèi)容
│
└──
pages
│
└──
index
│
├──
index.css
│
└──
index.jsx
//
React代碼,即上述計(jì)數(shù)器代碼
└──
tsconfig.jsonapp.config.js內(nèi)容即是小程序全局配置內(nèi)容,如下:module.exports
=
{
pages:
['pages/index/index'],
window:
{
navigationBarTitleText:
'react-wxapp',
navigationBarBackgroundColor:
'#282c34'
}
};有了這個(gè)配置文件,就可以通過如下方式生成app.js和app.json文件。/*outputDir為小程序代碼生成目錄*/
fs.writeFileSync(path.join(outputDir,
'./app.js'),
`App({})`)
fs.writeFileSync(path.join(outputDir,
'./app.json'),
JSON.stringify(config,
undefined,
2))
//
config即為app.config.js文件內(nèi)容小程序頁(yè)面則是由四種類型文件構(gòu)成,分別是js(必需,頁(yè)面邏輯)、wxml(必需,頁(yè)面結(jié)是構(gòu))、json(非必需、頁(yè)面配置)、wxss(非必需、頁(yè)面樣式表)。而React代碼轉(zhuǎn)小程序,主要是考慮如何將React代碼轉(zhuǎn)換程序?qū)?yīng)的js和wxml類型文件,后文會(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)查看的本項(xiàng)目項(xiàng)目源碼,就可以發(fā)現(xiàn)源碼里也體現(xiàn)出了這兩種方式(編譯時(shí)實(shí)現(xiàn)目錄:packages/compile-core;運(yùn)行時(shí)實(shí)現(xiàn)目錄:packages/runtime-core)。編譯時(shí)方式主要通過靜態(tài)編譯將JSX轉(zhuǎn)換成小程序?qū)?yīng)的template來實(shí)現(xiàn)渲染,類似Taro1.0和2.0,此方式性能接近原生小程序,但是語(yǔ)法卻有很大的限制。運(yùn)行時(shí)實(shí)現(xiàn)是通過react-reconciler重新在小程序平臺(tái)定義一個(gè)React渲染器,使得React代碼可以真正運(yùn)行到小程序里,類似Taro3.0、Remax等,因此這種方式無語(yǔ)法限制,但是性能會(huì)比較差。本項(xiàng)目源碼正是參照Taro、Remax這類框架源碼并簡(jiǎn)化很多細(xì)節(jié)進(jìn)行實(shí)現(xiàn)的,因此這個(gè)項(xiàng)目源碼只是適合來學(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í)語(yǔ)言往低級(jí)語(yǔ)言進(jìn)行編譯,但這里只是將同等水平語(yǔ)言轉(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使用語(yǔ)法轉(zhuǎn)換成小程序相同功能的語(yǔ)法,例如:標(biāo)簽元素轉(zhuǎn)換:View、Text、Button等標(biāo)簽直接映射為小程序基礎(chǔ)組件本身(改為小寫)樣式類名轉(zhuǎn)換:className修改為class事件轉(zhuǎn)換:如onClick修改為bindtap循環(huán)轉(zhuǎn)換:map語(yǔ)法修改為wx:for語(yǔ)法轉(zhuǎn)換遠(yuǎn)不止上面這些類型,如果要保證開發(fā)者可以使用各種JSX語(yǔ)法開發(fā)小程序,就需要盡可能窮舉出所有語(yǔ)法轉(zhuǎn)換規(guī)則,否則很可能開發(fā)者用了一個(gè)寫法就不支持轉(zhuǎn)換。而事實(shí)是,有些寫法(比如動(dòng)態(tài)生成JSX片段等等)是根本無法支持轉(zhuǎn)換,這也是前文為什么說編譯時(shí)實(shí)現(xiàn)方案的缺點(diǎn)是語(yǔ)法有限制,開發(fā)者不能隨意編碼,需要受限于框架本身開發(fā)規(guī)則。由于上述需要轉(zhuǎn)換JSX代碼語(yǔ)法相對(duì)簡(jiǎn)單,只需要涉及幾種簡(jiǎn)單語(yǔ)法規(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('執(zhí)行componentDidMount');
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
?
'偶數(shù)'
:
'奇數(shù)';
Object.assign(this.__state,
{
text:
text
});
return
this.__state;
}
}
Page(require('../../npm/app.js').createPage(Index))。
//
4.使用運(yùn)行時(shí)墊片提供的createPage
//
方法進(jìn)行初始化
//
方法進(jìn)行初始化如上代碼,需要處理的地方有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)樵緍ender方法內(nèi)JSX片段轉(zhuǎn)換為wxml了,所以這里render方法可將JSX片段進(jìn)行刪除。另外因?yàn)镽eact每次執(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['$leoCompReady']
=
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.$ponentDidMount
===
'function')
{
this.$ponentDidMount()
//
生命邏輯映射
}
}
}
const
events
=
ComponentClass['$$events']
//
獲取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í)行npmrunbuild: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ù)量),因此其性能與使用小程序原生語(yǔ)法開發(fā)相近甚至某些場(chǎng)景會(huì)更優(yōu)。然而這種方案的缺點(diǎn)就是語(yǔ)法限制問題(上文已經(jīng)提過了),使得開發(fā)并不友好,因此也就有了運(yùn)行時(shí)實(shí)現(xiàn)方案的誕生。運(yùn)行時(shí)實(shí)現(xiàn)從上文可以看出,編譯時(shí)實(shí)現(xiàn)之所以有語(yǔ)法限制,主要因?yàn)槠洳皇亲孯eact真正運(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
'react'
import
ReactDom
from
'react-dom'
import
App
from
'./App'
ReactDom.render(
App,
document.getElementById('root')
)可以看出渲染W(wǎng)eb頁(yè)面需要引用一個(gè)叫react-dom模塊,那這個(gè)模塊作用是什么?react-dom是Web平臺(tái)的渲染器,主要負(fù)責(zé)將React執(zhí)行后的VitrualDOM數(shù)據(jù)渲染到Web平臺(tái)。同樣的,React要渲染到Native,也有一個(gè)針對(duì)Native平臺(tái)的渲染器:ReactNative。React實(shí)現(xiàn)多平臺(tái)方式,是在每個(gè)平臺(tái)實(shí)現(xiàn)一個(gè)React渲染器,如下圖所示。而如果要將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
'react-reconciler'
import
hostConfig
from
'./hostConfig'
//
宿主配置
//
創(chuàng)建Reconciler實(shí)例,
并將HostConfig傳遞給Reconciler
const
ReactReconcilerInst
=
ReactReconciler(hostConfig)
/**
*
提供一個(gè)render方法,類似ReactDom.render方法
*
與ReactDOM一樣,接收三個(gè)參數(shù)
*
render(<MyComponent
/>,
container,
()
=>
console.log('rendered'))
*/
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);
}第二步,如上圖引用的importhostConfigfrom'./hostConfig',需要通過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)未提供DOMAPI操作,只能通過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('root')
中的第二個(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:
ps
||
{},
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
'react';
import
Container
from
'./container';
//
上文定義的Container
import
render
from
'./render';
//
上文定義的render方法
export
default
function
createPageConfig(component)
{
//
component為React組件
const
config
=
{
//
小程序邏輯對(duì)象字面量,供Page方法注冊(cè)
data:
{
root:
{
children:
[],
}
},
onLoad()
{
this.container
=
new
Container(this,
'root');
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
'react';
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('../../npm/app.js').createPage(Index))通過這樣,就可以使得React代碼在小程序真正運(yùn)行起來了,但是這里我們還有個(gè)流程沒介紹,上述Container類的applyUpdate方法中生成的頁(yè)面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":
"coun
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年精裝電梯改造協(xié)議樣本版
- 2025年度酒店弱電系統(tǒng)全面維護(hù)保養(yǎng)合同范本3篇
- 2024版員工入股協(xié)議書范本
- 2024期限內(nèi)完成的物流配送合同
- 二零二五年度綠色環(huán)保崗位勞務(wù)派遣與環(huán)境保護(hù)責(zé)任合同3篇
- 個(gè)人企業(yè)專項(xiàng)咨詢輔導(dǎo)服務(wù)協(xié)議版B版
- 二零二五年度鋼筋安裝工程綠色施工與環(huán)境保護(hù)協(xié)議書3篇
- 2024砌磚工勞務(wù)分包合同-環(huán)保型材料應(yīng)用示范3篇
- 2024液化天然氣出口購(gòu)銷合同
- 2024房地產(chǎn)買賣合同及裝修條款
- 銅礦的選礦工藝與設(shè)備選擇
- 醫(yī)療器械經(jīng)營(yíng)質(zhì)量管理規(guī)范培訓(xùn)課件
- 外貿(mào)經(jīng)理年終工作總結(jié)
- 2024屆新疆維吾爾自治區(qū)烏魯木齊市高三上學(xué)期第一次質(zhì)量監(jiān)測(cè)生物試題【含答案解析】
- 貴州省黔西南州2023-2024學(xué)年七年級(jí)上學(xué)期期末數(shù)學(xué)試卷(含答案)
- 數(shù)控加工技術(shù)-數(shù)控銑床的編程
- 天文基礎(chǔ)知識(shí)入門教程
- 《區(qū)塊鏈原理詳解》課件
- 護(hù)士長(zhǎng)競(jìng)聘上崗面試題及答案
- 廣東省中山市2023-2024學(xué)年四年級(jí)上學(xué)期期末數(shù)學(xué)試卷
- 舞臺(tái)機(jī)械管理制度
評(píng)論
0/150
提交評(píng)論