最近2018中文字幕在日韩欧美国产成人片_国产日韩精品一区二区在线_在线观看成年美女黄网色视频_国产精品一区三区五区_国产精彩刺激乱对白_看黄色黄大色黄片免费_人人超碰自拍cao_国产高清av在线_亚洲精品电影av_日韩美女尤物视频网站

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
聊聊跨端技術(shù)的本質(zhì)與現(xiàn)狀

零、何為跨端write once, run everywhere

那么在跨端方案百花齊放的今天,比如現(xiàn)在最為人們所熟知的react native、flutter、electron等,他們之間有沒有什么共同的特點(diǎn),而我們又是否能夠找到其中的本質(zhì),就是今天這篇文章想講述的問題。

站在用戶的角度思考問題,與客戶深入溝通,找到綏化網(wǎng)站設(shè)計(jì)與綏化網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:網(wǎng)站制作、網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、主機(jī)域名、網(wǎng)站空間、企業(yè)郵箱。業(yè)務(wù)覆蓋綏化地區(qū)。

一、主流跨端實(shí)現(xiàn)方案

1.1 h5 hybrid 方案

其實(shí),瀏覽器本就是一個(gè)跨端實(shí)現(xiàn)方案,因?yàn)槟阒恍枰斎刖W(wǎng)址,就能在任何端的瀏覽器上打開你的網(wǎng)頁。那么,如果我們把瀏覽器嵌入 app 中,再將地址欄等內(nèi)容隱藏掉,是不是就能將我們的網(wǎng)頁嵌入原生 app 了。而這個(gè)嵌入 app 的瀏覽器,我們把它稱之為 webview ,所以只要某個(gè)端支持 webview ,那么它就能使用這種方案跨端。

同時(shí)這也是開發(fā)成本最小的一種方案,因?yàn)檫@實(shí)際上就是在寫前端界面,和我們開發(fā)普通的網(wǎng)頁并沒有太大區(qū)別。

1.2 框架層+原生渲染

典型的代表是 react-native,它的開發(fā)語言選擇了 js,使用的語法和 react 完全一致,其實(shí)也可以說它就是 react,這就是我們的框架層。而不同于一般 react 應(yīng)用,它需要借助原生的能力來進(jìn)行渲染,組件最終都會(huì)被渲染為原生組件,這可以給用戶帶來比較好的體驗(yàn)。

1.3 框架層+自渲染引擎

這種方案和上面的區(qū)別就是,它并沒有直接借用原生能力去渲染組件,而是利用了更底層的渲染能力,自己去渲染組件。這種方式顯然鏈路會(huì)比上述方案的鏈路跟短,那么性能也就會(huì)更好,同時(shí)在保證多端渲染一致性上也會(huì)比上一種方案更加可靠。這類框架的典型例子就是 flutter 。

1.4 另類跨端

眾所周知,在最近幾年有一個(gè)東西變得非?;鸨盒〕绦?,現(xiàn)在許多大廠都有一套自己的小程序?qū)崿F(xiàn),但相互之間還是有不小差異的,通??梢越柚?taro ,remax 這類框架實(shí)現(xiàn)一套代碼,多端運(yùn)行的效果,這也算是一種另類的跨端,它的實(shí)現(xiàn)方式還是比較有意思的,我們后面會(huì)展開細(xì)講。

二、react-native 實(shí)現(xiàn)

2.1 rn的三個(gè)線程rn 包含三個(gè)線程:

  • native thread:主要負(fù)責(zé)原生渲染和調(diào)用原生能力;
  • js thread:JS 線程用于解釋和執(zhí)行我們的js代碼。在大多數(shù)情況下,react native使用的js引擎是JSC(JavaScriptCore) ,在使用 chrome 調(diào)試時(shí),所有的 js 代碼都運(yùn)行在 chrome中,并且通過 websocket與原生代碼通信。此時(shí)的運(yùn)行環(huán)境是v8。
  • shadow thread:要渲染到界面上一個(gè)很重要的步驟就是布局,我們需要知道每個(gè)組件應(yīng)該渲染到什么位置,這個(gè)過程就是通過yoga去實(shí)現(xiàn)的,這是一個(gè)基于flexbox的跨平臺(tái)布局引擎。shadow thread 會(huì)維護(hù)一個(gè) shadow tree來計(jì)算我們的各個(gè)組件在 native 頁面的實(shí)際布局,然后通過 bridge 通知native thread 渲染 ui。

2.2 初始化流程

  • native 啟動(dòng)一個(gè)原生界面,比如android會(huì)起一個(gè)新的activity來承載rn,并做一些初始化的操作。
  • 加載 js 引擎,運(yùn)行 js 代碼,此時(shí)的流程和 react 的啟動(dòng)流程就非常相似了,我們先簡(jiǎn)單觀察調(diào)用棧,

是不是看見了一些非常熟悉的函數(shù)名,在上一講的基本原理中已經(jīng)提到過了,這里我們就不再贅述。同時(shí)再看一下FiberNode的結(jié)構(gòu),也和react的保持一致,只不過我們?cè)趈s層是無法拿到真實(shí)結(jié)點(diǎn)的,所以stateNode只是一個(gè)代號(hào)。

  • js 線程通知shadow thread。在react中,走到createInstance以后我們就可以直接調(diào)用createElement來創(chuàng)建真實(shí)結(jié)點(diǎn)了,但是在rn中我們沒辦法做到這一步,所以我們會(huì)通知native層讓它來幫助我們創(chuàng)建一個(gè)對(duì)應(yīng)的真實(shí)結(jié)點(diǎn)。

  • shadow thread 計(jì)算布局,通知native Thread 創(chuàng)建原生組件。
  • native 在界面上渲染原生組件,呈現(xiàn)給用戶。

2.3 更新流程

比如某個(gè)時(shí)候,用戶點(diǎn)擊了屏幕上的一個(gè)按鈕觸發(fā)了一個(gè)點(diǎn)擊事件,此時(shí)界面需要進(jìn)行相應(yīng)的更新操作。

  • native 獲取到了點(diǎn)擊事件,傳給了js threadjs thread根據(jù) react 代碼進(jìn)行相應(yīng)的處理,比如處理 onClick 函數(shù),觸發(fā)了 setState。
  • 和 react 的更新流程一樣,觸發(fā)了 setState 之后會(huì)進(jìn)行 diff,找到需要更新的結(jié)點(diǎn)
  • 通知 shadow threadshadow thread 計(jì)算布局之后通知 native thread 進(jìn)行真正的渲染。

2.4 特點(diǎn)

我們上述說的通知,都是通過 bridge 實(shí)現(xiàn)的,bridge本身是用實(shí)現(xiàn)C++的,就像一座橋一樣,將各個(gè)模塊關(guān)聯(lián)起來,整個(gè)通信是一個(gè)「異步」的過程。這樣做好處就是各自之間不會(huì)有阻塞關(guān)系,比如 不會(huì)native thread因?yàn)閖s thread而阻塞渲染,給用戶良好的體驗(yàn)。但是這種「異步」也存在一個(gè)比較明顯的問題:因?yàn)橥ㄐ胚^程花費(fèi)的時(shí)間比較長(zhǎng),所以在一些時(shí)效性要求較高場(chǎng)景上體驗(yàn)較差。

比如長(zhǎng)列表快速滾動(dòng)的時(shí)候或者需要做一些跟手的動(dòng)畫,整個(gè)過程是這樣的:

  • native thread監(jiān)聽到了滾動(dòng)事件,發(fā)送消息通知
  • js threadjs thread 處理滾動(dòng)事件,如果需要修改 state 需要經(jīng)過一層js diff,拿到最終需要更新的結(jié)點(diǎn)
  • js thread 通知 shadow threadshadow thread 通知 native 渲染
  • 當(dāng)用戶操作過快的時(shí)候,就會(huì)導(dǎo)致界面來不及更新,進(jìn)而導(dǎo)致在快速滑動(dòng)的時(shí)候會(huì)出現(xiàn)白屏、卡頓的現(xiàn)象。

2.5 優(yōu)化

我們很容易看出,這是由rn的架構(gòu)引出的問題,其實(shí)小程序的架構(gòu)也會(huì)有這個(gè)問題,所以在rn和小程序上出現(xiàn)一些需要頻繁通信的場(chǎng)景時(shí),就會(huì)導(dǎo)致頁面非常差,流暢度降低。那么如果想解決這個(gè)問題,勢(shì)必要從架構(gòu)上去進(jìn)行修改。

三、從rn看本質(zhì)

那么既然我們知道了rn是如何實(shí)現(xiàn)的跨端,那么我們就可以來探究一下它本質(zhì)上是在干什么。首先,跨端可以分為「邏輯跨端」和「渲染跨端」。

「邏輯跨端」通常通過 vm來實(shí)現(xiàn),例如利用v8 引擎,我們就能在各個(gè)平臺(tái)上運(yùn)行我們的js 代碼,實(shí)現(xiàn)「邏輯跨端」。

那么第二個(gè)問題就是「渲染跨端」,我們把業(yè)務(wù)代碼的實(shí)現(xiàn)抽象為開發(fā)層,比如 react-native中我們寫的 react 代碼就屬于開發(fā)層,再把具體要渲染的端稱為渲染層。作為開發(fā)層來說,我一定知道我想要的ui長(zhǎng)什么樣,但是我沒有能力去渲染到界面上,所以當(dāng)我聲明了一個(gè)組件之后,我們需要考慮的問題是如何把我想要什么告訴渲染層。

就像這樣的關(guān)系,那么我們最直觀的方式肯定是我能夠?qū)崿F(xiàn)一種通信方式,在開發(fā)層將消息通知到各個(gè)系統(tǒng),再由各個(gè)系統(tǒng)自己去調(diào)用對(duì)應(yīng)的 api 來實(shí)現(xiàn)最終的渲染。

function render() {
if(A) {
message.sendA('render', { type: 'View' })
}


if(B) {
message.sendB('render', { type: 'View' })
}


if(C) {
message.sendC('render', { type: 'View' })
}
}

比如這樣,我就能通過判斷平臺(tái)來通知對(duì)應(yīng)的端去渲染View組件。這一部分的工作就是跨端框架需要幫助我們做的,它可以把這一步放到 JS 層,也可以把這一步放到c++ 層。我們應(yīng)該把這部分工作盡量往底層放,也就是我們可以對(duì)各個(gè)平臺(tái)的 api 進(jìn)行一層封裝,上層只負(fù)責(zé)調(diào)用封裝的 api,再由這一層封裝層去調(diào)用真正的 api。因?yàn)檫@樣可以復(fù)用更多的邏輯,否則像上文中我們?cè)?JS層去發(fā)送消息給不同的平臺(tái),我們就需要在A\B\C三個(gè)平臺(tái)寫三個(gè)不同方法去渲染組件。

但是,歸根結(jié)底就是,一定有一個(gè)地方是通過判斷不同平臺(tái)來調(diào)用具體實(shí)現(xiàn),也就是下面這樣

有一個(gè)地方會(huì)對(duì)系統(tǒng)進(jìn)行判斷,再通過某種通信方式通知到對(duì)應(yīng)的端,最后執(zhí)行真正的方法。其實(shí),所有跨端相關(guān)操作其實(shí)都在做上圖中的這些事情。所有的跨端也可以總結(jié)為下面這句話:

「我知道我想要什么,但是我沒有能力去渲染,我要通知有能力渲染的人來幫助我渲染」

比如hybrid跨端方案中,webview其實(shí)就充當(dāng)了橋接層的角色,createElement,appendChild等api就是給我們封裝好的跨平臺(tái)api,底層最終調(diào)用到了什么地方,又是如何渲染到界面上的細(xì)節(jié)都被屏蔽掉了。所以我們利用這些api就能很輕松的實(shí)現(xiàn)跨端開發(fā),寫一個(gè)網(wǎng)頁,只要能夠加載 webview 的地方,我們的代碼就能跑在這個(gè)上面。

又比如flutter的方案通過研發(fā)一個(gè)自渲染的引擎來實(shí)現(xiàn)跨端,這種思路是不是相當(dāng)于另外一個(gè)瀏覽器?但是不同的點(diǎn)在于 flutter 是一個(gè)非常新的東西,而 webview 需要遵循大量的 w3c規(guī)范和背負(fù)一堆歷史包袱。flutter 并沒有什么歷史包袱,所以它能夠從架構(gòu),設(shè)計(jì)的方面去做的更好更快,能夠做更多的事情。

四、跨端目前有什么問題

4.1 一致性

對(duì)于跨端來說,如何屏蔽好各端的細(xì)節(jié)至關(guān)重要,比如針對(duì)某個(gè)端特有的api如何處理,如何保證渲染細(xì)節(jié)上各個(gè)端始終保持一致。如果一個(gè)跨端框架能夠讓開發(fā)者的代碼里面不出現(xiàn) isIos、isAndroid的字眼,或者是為了兼容各種奇怪的渲染而產(chǎn)生的非常詭異的hack方式。那我認(rèn)為它絕對(duì)是一個(gè)真正成功的框架。

但是按我經(jīng)驗(yàn)而言,先后寫過的 h5、rn、小程序,他們都沒有真正做到這一點(diǎn),所以項(xiàng)目里面會(huì)出現(xiàn)為了解決不同端不一致問題而出現(xiàn)的各種奇奇怪怪的代碼。而這個(gè)問題其實(shí)也是非常難解決的,因?yàn)楦鞫说牟町愡€是比較大的,所以說很難去完全屏蔽這些細(xì)節(jié)。

比如說h5中磨人的垂直居中問題,我相信只要開發(fā)過移動(dòng)端頁面的都會(huì)遇見,就不用我多說了。

4.2 為什么出現(xiàn)了這么多框架

為什么大家其實(shí)本質(zhì)上都是在干一件事情,卻出現(xiàn)了這么多的解決方案?其實(shí)大家都覺得某些框架沒能很好的解決某個(gè)問題,所以想自己去造一套。其中可能很多開發(fā)者最關(guān)心的就是性能問題,比如:

  • rn因?yàn)榧軜?gòu)上的原因?qū)е履承﹫?chǎng)景性能差,所以它就想辦法從架構(gòu)上去進(jìn)行修改。
  • flutter直接自己搞了一套渲染引擎,同時(shí)選用支持AOT的dart作為開發(fā)語言。

但是其實(shí)我們?cè)谶x擇框架的時(shí)候性能并不是唯一因素,開發(fā)體驗(yàn)、框架生態(tài)這些也都是關(guān)鍵因素,我個(gè)人感受是,目前rn的生態(tài)還是比其他的要好,所以在開發(fā)過程中你想要的東西基本都有。

五、小程序跨端

ok,說了這么多,對(duì)于跨端部分的內(nèi)容其實(shí)我想說的已經(jīng)說的差不多了,還記得上文提到的 Taro、Uni-app 一類跨小程序方案么。為什么說它是另類的跨端,因?yàn)樗鋵?shí)并沒有實(shí)際跨端,只是為了解決各個(gè)小程序語法之間不兼容的問題。但是它又確實(shí)是一個(gè)跨端解決方案,因?yàn)樗?「write once, run everything?!?/p>

下面我們先來了解下小程序的背景。

5.1 什么是小程序

小程序是各個(gè)app廠商對(duì)外開放的一種能力。通過廠商提供的框架,就能在他們的app中運(yùn)行自己的小程序,借助各大app的流量來開展自己的業(yè)務(wù)。同時(shí)作為廠商如果能吸引到更多的人加入到開發(fā)者大軍中來,也能給app帶來給多的流量,這可以看作一個(gè)雙贏的業(yè)務(wù)。那么最終呈現(xiàn)在app中的頁面是以什么方式進(jìn)行渲染的呢?其實(shí)還是通過webview,但是會(huì)嵌入一些原生的組件在里面以提供更好的用戶體驗(yàn),比如video組件其實(shí)并不是h5 video,而是native video。

5.2 什么是小程序跨端

那么到了這里,我們就可以來談一談關(guān)于小程序跨端的東西了。關(guān)于小程序跨端,核心并不是真正意義上的跨端,雖然小程序也做到了跨端,例如一份代碼其實(shí)是可以跑在android和Ios上的,但是實(shí)際上這和hybrid跨端十分相似。

在這里我想說的其實(shí)是,市面上現(xiàn)在有非常多的小程序:字節(jié)小程序、百度小程序、微信小程序、支付寶小程序等等等等。雖然他們的dsl十分相似,但是終歸還是有所不同,那么就意味著如果我想在多個(gè)app上去開展我的業(yè)務(wù),我是否需要維護(hù)多套十分相似的代碼?我又能否通過一套代碼能夠跑在各種小程序上?

5.3 怎么做

想通過一套代碼跑在多個(gè)小程序上,和想通過一套代碼跑在多個(gè)端,這兩件事到底是不是一件事呢?我們?cè)倩氐竭@張圖

這些平臺(tái)是否可以對(duì)應(yīng)上不同的小程序?

再回到那句話:「我知道我想要什么,但是我沒有能力去渲染,我要通知有能力渲染的人來幫助我渲染?!?/p>

現(xiàn)在來理一下我們的需求:

  • 小程序的語法不好用,我希望用 react 開發(fā);
  • 我希望盡可能低的成本讓小程序跑在多個(gè)平臺(tái)上。

那么從這句話來看:「我」代表了什么,「有能力渲染的人」又代表了什么?

第二個(gè)很容易對(duì)應(yīng)上,「有能力渲染的人」就是小程序本身,只有它才能幫助我們把內(nèi)容真正渲染到界面上。

而「我」又是什么呢?其實(shí)這個(gè)「我」可以是很多東西,不過這里我們的需求是想用react進(jìn)行開發(fā),所以我們回想一下第一講中react的核心流程,當(dāng)它拿到vdom的時(shí)候,是不是就已經(jīng)知道【我想要什么】了?所以我們把react拿到vdom之前的流程搬過來,這樣就能獲取到「我知道我想要什么」的信息,但是「我沒有能力去渲染」,因?yàn)檫@不是web,沒有dom api,所以我需要通知小程序來幫助我渲染,我還可以根據(jù)不同的端來通知不同的小程序幫助我渲染。

所以整個(gè)流程就是下面這樣的:

前面三個(gè)流程都在我們的js層,也就是開發(fā)層,我們寫的代碼經(jīng)歷一遍完整的 react 流程之后,會(huì)將最后的結(jié)果給到各個(gè)小程序,然后再走小程序自己的內(nèi)部流程,將其真正的渲染到界面上。

采用這種做法的典型例子有remax、taro3,他們宣稱用真正的react去開發(fā)小程序,其實(shí)并沒有錯(cuò),因?yàn)檎娴氖前裷eact的整套東西都搬了過來,和react并無差異。我們用taro寫一個(gè)非常簡(jiǎn)單的例子來看一下:

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


export default class Index extends Component {

state = {
random: Math.random()
}


componentWillMount () { }


componentDidMount () { }


componentWillUnmount () { }


componentDidShow () { }


componentDidHide () { }


handleClick = () => {
debugger;
console.log("Math.random()", Math.random());
this.setState({random: Math.random()})
}


render () {
return (

Hello world! {this.state.random}


)
}
}

這是一個(gè)用taro寫的組件,把它編譯到字節(jié)小程序之后是這樣的效果:

根據(jù)我們之前的分析,在最后生成的文件中,一定包含了一個(gè)「小程序渲染器」。它接受的data就是整個(gè)ui結(jié)構(gòu),然后通過小程序的渲染能力渲染到界面上,我們?nèi)ist文件中找一下,就能找到一個(gè)base.ttml的文件,里面的內(nèi)容是這樣的