【移動(dòng)應(yīng)用開發(fā)技術(shù)】React怎么構(gòu)建小程序_第1頁(yè)
【移動(dòng)應(yīng)用開發(fā)技術(shù)】React怎么構(gòu)建小程序_第2頁(yè)
【移動(dòng)應(yīng)用開發(fā)技術(shù)】React怎么構(gòu)建小程序_第3頁(yè)
【移動(dòng)應(yīng)用開發(fā)技術(shù)】React怎么構(gòu)建小程序_第4頁(yè)
【移動(dòng)應(yīng)用開發(fā)技術(shù)】React怎么構(gòu)建小程序_第5頁(yè)
已閱讀5頁(yè),還剩14頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論