溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

flutter_mp的實(shí)現(xiàn)原理以及通過flutter_mp轉(zhuǎn)換并運(yùn)行在小程序端的效果

發(fā)布時(shí)間:2021-09-04 10:21:19 來(lái)源:億速云 閱讀:228 作者:chen 欄目:移動(dòng)開發(fā)

本篇內(nèi)容主要講解“flutter_mp的實(shí)現(xiàn)原理以及通過flutter_mp轉(zhuǎn)換并運(yùn)行在小程序端的效果”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“flutter_mp的實(shí)現(xiàn)原理以及通過flutter_mp轉(zhuǎn)換并運(yùn)行在小程序端的效果”吧!

原理簡(jiǎn)介

雖然還有諸多功能未完成,我們先來(lái)談?wù)務(wù)麄€(gè)flutter_mp的實(shí)現(xiàn)原理。篇幅原因,下面我們將只對(duì)flutter_mp幾個(gè)重要的部分進(jìn)行簡(jiǎn)單說明。

先看下flutter_mp的實(shí)際效果:

Flutter版官方layout樣例:

通過flutter_mp轉(zhuǎn)換并運(yùn)行在小程序端效果

聲明式UI的處理

Flutter是聲明式UI框架,聲明式UI只需要向框架描述UI長(zhǎng)什么樣子而不用關(guān)心框架具體的實(shí)現(xiàn)細(xì)節(jié),具體到Flutter,上層的UI描述使用底層的skia圖形引擎處理就是原生Flutter,而把底層處理?yè)Q成html/css/canvas就是flutter_web,flutter_mp則是探索在類小程序上對(duì)這些UI描述的處理。

我們看一個(gè)最簡(jiǎn)單例子

var x = 'Hello World'

Center(
     child: Text(x)
);

對(duì)于上面的UI結(jié)構(gòu),我們只需要在小程序的wxml文件里,用如下的結(jié)構(gòu)對(duì)應(yīng)就OK了。

// wxml部分
<Center>
   <Text>{{x}}</Text>
</Center>

// js 部分
Component({
   data: {
       x: 'Hello World'
   }
})

雖然實(shí)際的結(jié)構(gòu)要比上面的情況復(fù)雜的多,不過通過上面簡(jiǎn)單的例子,我們知道起碼要做兩個(gè)事情:

我們需要根據(jù)Flutter代碼生成相關(guān)小程序wxml模版文件 收集wxml渲染需要的數(shù)據(jù),放置到小程序組件的data字段。

wxml結(jié)構(gòu)生成

我們知道小程序是無(wú)法動(dòng)態(tài)操作節(jié)點(diǎn)的,wxml結(jié)構(gòu)需要預(yù)先生成,所以Flutter運(yùn)行在小程序之前,會(huì)存在一個(gè)編譯打包階段,這個(gè)階段會(huì)遍歷Dart代碼, 根據(jù)一定規(guī)則生成wxml文件(編譯階段還會(huì)做下文將要提到的另外一個(gè)重要事情 --- 把Dart編譯為js)。

具體的,我們首先會(huì)將Dart源碼處理為可分析的AST結(jié)構(gòu),AST是源代碼的樹型表示結(jié)構(gòu)。然后我們深度遍歷這份AST語(yǔ)法樹結(jié)構(gòu),生成目標(biāo)wxml,整個(gè)過程如下:

構(gòu)建wxml結(jié)構(gòu)的難點(diǎn)在于:Flutter不僅是聲明式UI還是“值UI”,什么叫“值UI”?簡(jiǎn)單來(lái)說,F(xiàn)lutter把UI看成是一個(gè)普通的值,類似于字符串,數(shù)字一樣的值,既然是一個(gè)普通的值,就可以參與所有的控制流程,可以是函數(shù)的返回值也可以是函數(shù)參數(shù)等等。而小程序的wxml雖然也是聲明式UI,卻不是“值UI”,wxml更加像模版,更加的靜態(tài)。怎么用靜態(tài)的wxml表達(dá)動(dòng)態(tài)的“值UI”是構(gòu)建wxml結(jié)構(gòu)的關(guān)鍵所在。

看個(gè)例子

Widget getX() {
    if (condition1) {
        return Text('Hello');
    } else if (condition2) {
        return Container(
            child: ...
        );
    } else if (condition3) {
        return Center(
            child: ...
        );
    }
    ...
}

Widget x = getX();

Center(
   child: x      // < --- 如何處理這里的 x??
);

這里的child: x x是一個(gè)動(dòng)態(tài)值,它的具體值需要在運(yùn)行階段才能確定,它可能是任意的Widget,如何在靜態(tài)的wxml上處理這里動(dòng)態(tài)的x?受Alita框架的啟發(fā),這里主要是借助于小程序template的動(dòng)態(tài)性(template的is屬性可以接受變量值)。有如下幾步:

1、首先在遍歷Dart源碼AST結(jié)構(gòu)的時(shí)候,會(huì)把每一個(gè)獨(dú)立完整的“UI值”片段,對(duì)應(yīng)到wxml的template, 比如上文 getX 里面的UI

<template name="template001">
    <text>Hello</text>
</template>
<template name="template002">
    <Container>...</Container>
</template>
<template name="template003">
    <Center>...</Center>
</template>

2、在遇到 類似x 這種動(dòng)態(tài)值的時(shí)候,固定的會(huì)生成一個(gè)template占位

<template name="template004">
    <Center>
        <template is="{{templateName}}" data="{{...templateData}}"/>
    </Center>
<template name="template003">

3、在運(yùn)行階段,會(huì)根據(jù)getX

函數(shù)的運(yùn)行結(jié)果來(lái)決定x映射的“UI值”,如果getX里面condition1為true,那么這里的templateName的值就是template001。具體的數(shù)據(jù)計(jì)算收集工作,參考下面要的 “渲染數(shù)據(jù)收集”過程??梢钥闯鰂lutter_mp處理“值UI”方式,完全參考了Alita。

渲染數(shù)據(jù)收集

wxml結(jié)構(gòu)的生成是在編譯階段就完成了,與它不同渲染數(shù)據(jù)是運(yùn)行時(shí)的信息,隨時(shí)會(huì)根據(jù)setState而改變。那么我們?cè)趺词占鑫覀冃枰匿秩緮?shù)據(jù)呢?

如果我們還是順著Flutter的架構(gòu)圖,很難插入我們收集的鉤子函數(shù),另外Flutter的這個(gè)架構(gòu)對(duì)于小程序來(lái)說太重了,下圖紅框里的這些過程對(duì)于小程序的渲染來(lái)說并不必要。最后由于最終的代碼會(huì)被轉(zhuǎn)化為js,而Flutter本身依賴的庫(kù)里面很多是不支持轉(zhuǎn)化js的,比如dart:ui等等。

所以我們實(shí)現(xiàn)了一個(gè)極簡(jiǎn)極簡(jiǎn)的Flutter小程序版本mini_flutter,在編譯期我們會(huì)把所有對(duì)Flutter庫(kù)的引用替換為mini_flutter, mini_flutter只存在到上圖的Rendering階段,這個(gè)Rendering的實(shí)現(xiàn)也是為小程序定制的, 在運(yùn)行時(shí)期Rendering不斷收集Widgets的信息。最終生成一個(gè)UI描述的JSON結(jié)構(gòu),這個(gè)結(jié)構(gòu)就包含了上文所說的templateName, templateData,UI描述將會(huì)被下層小程序獲得,用來(lái)渲染小程序UI,架構(gòu)圖如下:

Dart/JS:轉(zhuǎn)化與互操作

Flutter的開發(fā)語(yǔ)言是Dart,而小程序的運(yùn)行環(huán)境是瀏覽器,所以我們還需要把Dart編譯為JavaScript代碼。

在上文的編譯打包階段也提到這一點(diǎn),這個(gè)過程主要是使用了Dart提供的dart2js工具,不過,針對(duì)小程序環(huán)境,生成的js代碼仍需要做一些適配,另外雖然都是JS代碼,dart2js生成的js和小程序原生js的運(yùn)行環(huán)境卻是隔離的,也就是說它們是不能共享變量,方法等等,它們各自在本身的"域"里執(zhí)行。

這帶來(lái)兩個(gè)問題:

1、Widget 初始化 或者setState更新,生成的UI描述JSON,如何傳遞給小程序"域"呢? 2、相關(guān)渲染回調(diào),事件的都發(fā)生在小程序"域",這些信息如何傳遞給Dart?

總結(jié)一下:Dart(最終會(huì)編譯為JS)與小程序原生JS如何互操作?

解決這個(gè)問題主要是借助dart:js, package:js這兩個(gè)庫(kù):

Dart操作JS

<template name="template004">
    <Center>
        <template is="{{templateName}}" data="{{...templateData}}"/>
    </Center>
<template name="template003">

這樣當(dāng)Dart代碼調(diào)用stringify方法的時(shí)候,實(shí)際上會(huì)執(zhí)行window.JSON.stringify方法

JS操作Dart

// dart注冊(cè)
void main() {
    context['dartHi'] = () {
        print('dart hi!');
    };
}
// js 調(diào)用
window.dartHi()

這里只是簡(jiǎn)單說明Dart與JS的互操作,另外由于小程序的運(yùn)行環(huán)境是閹割以后的瀏覽器環(huán)境,flutter_mp的實(shí)現(xiàn)還稍有不同。

總之,Dart與JS是可以互操作的,這樣就打通了上層Flutter環(huán)境和下層小程序環(huán)境。

布局系統(tǒng)

Flutter的布局系統(tǒng)不同與css,但是和css頗相似。

在上文提到的Rendering階段,會(huì)根據(jù)Widget的布局屬性,類別,約束條件生成一個(gè)等效的css樣式。注意這里邊界約束是上下文相關(guān)的。比如一個(gè)沒有寬高的Container實(shí)際大小,不僅和子元素相關(guān),還和父元素傳遞過來(lái)的邊界約束條件相關(guān),這個(gè)其實(shí)是比較麻煩的,能不能把Flutter的Widget屬性,邊界約束完全用css表達(dá),我們還在尋求有效的方案。

總結(jié)

和flutter_web一樣,完全把Flutter所有特性渲染到小程序上是不可能的,一般我們覺得應(yīng)該是部分頁(yè)面,部分功能需要運(yùn)行在小程序上,這樣使用flutter_mp才是有意義的。

到此,相信大家對(duì)“flutter_mp的實(shí)現(xiàn)原理以及通過flutter_mp轉(zhuǎn)換并運(yùn)行在小程序端的效果”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI