<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    跨平臺開發(fā)的實踐與原理

    共 18390字,需瀏覽 37分鐘

     ·

    2024-04-12 03:35

    ??   這是第 404  篇 不摻水的原創(chuàng) ,想要了解更多 ,請戳下方卡片 關注我們吧~

    eb28725a34c280bd75795570f2e9fa3f.webp

    引言

    在如今不斷增長的小程序市場中,小程序的數(shù)量迅速增多。這是因為小程序具有諸多優(yōu)勢,例如輕量化、便捷性和良好的用戶體驗,吸引了越來越多的開發(fā)者和企業(yè)加入這一領域。隨著小程序的普及,各個行業(yè)都紛紛推出自己的小程序,以滿足用戶的多樣化需求。

    然而,正是因為小程序市場的多樣性和快速發(fā)展,每個小程序客戶端的 Api 差異也變得十分顯著。不同的小程序平臺為了滿足自身的特殊需求和功能定位,往往會對 Api 進行定制和調(diào)整。這導致了各個小程序客戶端之間的 Api 存在差異,不同平臺的開發(fā)者需要針對不同的 Api 進行開發(fā)和適配。

    對于開發(fā)者來說,針對不同平臺重新開發(fā)一套小程序應用將變成一場無盡的噩夢。開發(fā)者需要熟悉并掌握每個客戶端的api差異,編寫大量重復的代碼,并進行平臺特定的調(diào)試和適配工作。這不僅增加了開發(fā)的工作量和時間成本,還容易導致錯誤和兼容性問題。

    在這樣的背景下,Taro 的出現(xiàn)為開發(fā)者提供了一種解決方案。它通過提供一套統(tǒng)一的開發(fā)框架和組件,使開發(fā)者能夠編寫一套代碼,同時在多個小程序平臺上運行。Taro 的編譯工具能夠?qū)㈤_發(fā)者的代碼轉(zhuǎn)換為不同平臺所需的代碼,從而實現(xiàn)跨平臺的開發(fā)和適配,減輕了開發(fā)者的負擔,提高了開發(fā)效率。

    Taro是一套遵循 React 語法規(guī)范的多端統(tǒng)一開發(fā)框架(ps:Vue 語法也支持)。主要用于構(gòu)建跨平臺的小程序、H5和移動應用。市面上還存在其他的多端框架,包括但不限于:

    • uni-app:uni-app是 DCloud 推出的一款基于 Vue.js 的跨平臺開發(fā)框架,可用于構(gòu)建微信小程序、支付寶小程序、H5、App等多個平臺的應用。

    • React Native:React Native 是由 Facebook 開發(fā)的框架,用于構(gòu)建原生移動應用。它使用JavaScript和React語法,允許開發(fā)者通過一套代碼同時在 iOS 和 Android 上構(gòu)建應用。

    • Flutter:Flutter是由 Google 開發(fā)的UI工具包,用于構(gòu)建跨平臺的移動、Web 和桌面應用。它使用 Dart 編程語言,提供了豐富的UI組件和渲染能力。

    • Weex:Weex 是由阿里巴巴開發(fā)的跨平臺開發(fā)框架,使用 Vue.js 語法,用于構(gòu)建移動應用。它支持在 iOS、Android和 Web 上運行。

    • NativeScript:NativeScript 是由 Progress 開發(fā)的開源框架,用于構(gòu)建原生移動應用。它支持使用 JavaScript 或 TypeScript 編寫代碼,并提供了訪問原生 Api 的能力。

    在上述的這些中,只有uni-app是支持小程序場景的,它占據(jù)了多端框架的半壁江山。

    概括來講,Taro的主要特點和優(yōu)勢,參照官方說法:“使用 Taro,我們可以只書寫一套代碼,再通過 Taro 的編譯工具,將源代碼分別編譯出可以在不同端(微信小程序、H5、App 端等)運行的代碼?!?/p>

    一次編譯,多端運行

    這里需要解釋一下“編譯時配置”機制。官方說的“一次編譯”,并不是真的打一個 dist 包,能跑遍所有的平臺。

    而是根據(jù)你想要運行的平臺,用對應的指令,打出適合該平臺運行的包。

    舉個例子:

        微信小程序 編譯命令 yarn build:weapp
    百度小程序 編譯命令 yarn build:swan
    支付寶小程序 編譯命令 yarn build:alipay
    H5 編譯命令 yarn build:h5
    RN 編譯命令 yarn build:rn --platform ios
    ……

    所以我們需要真正關心的,其實是針對目標的平臺,Taro 都做了哪些事。下面以微信小程序為例子:

    Taro 框架內(nèi)置了對應的編譯器和構(gòu)建工具,在 @tarojs/plugin-platform-weapp 微信小程序平臺插件中。在此注冊微信小程序平臺的配置項。

        //taro-weapp/src/index.ts

    //在此注冊微信小程序平臺
    ctx.registerPlatform({
      name: 'weapp',
      useConfigName: 'mini',
      async fn ({ config }) {
        const program = new Weapp(ctx, config, options || {})
        await program.start()
      }
    })

    預先定義一個名為微信小程序的 Template 模版類,該類繼承自 UnRecursiveTemplate。其主要功能是處理 Taro 框架中的模板相關操作,并根據(jù)特定需求進行定制。

        //taro-weapp/src/template.ts

    export class Template extends UnRecursiveTemplate {
      ...
      
      //構(gòu)建wxs模板
      buildXsTemplate () {
        return '<wxs module="xs" src="./utils.wxs" />'
      }

      //創(chuàng)建小程序組件
      createMiniComponents (components): any {
        const result = super.createMiniComponents(components)

        // PageMeta & NavigationBar
        this.transferComponents['page-meta'] = result['page-meta']
        this.transferComponents['navigation-bar'] = result['navigation-bar']
        delete result['page-meta']
        delete result['navigation-bar']

        return result
      }

      //替換屬性名稱
      replacePropName (name: string, value: string, componentName: string, componentAlias) {
        ...
      }

      //構(gòu)建wxs模板中與焦點相關的方法,根據(jù)插件選項判斷是否啟用鍵盤附件功能,并返回相應的字符串
      buildXSTepFocus (nn: string) {
        ...
      }

      //修改模板結(jié)果的方法,根據(jù)節(jié)點名稱和插件選項對模板進行修改。
      modifyTemplateResult = (res: string, nodeName: string, _, children) => {
        ...
      }

      //構(gòu)建頁面模板的方法,根據(jù)基礎路徑和頁面配置生成頁面模板字符串。
      buildPageTemplate = (baseTempPath: string, page) => {
        ...
      }
    }

    Taro 的編譯工具,根據(jù)所選擇的平臺,轉(zhuǎn)換成對應平臺所需的代碼。使用 ctx.applyPlugins ,去調(diào)用相應平臺的插件處理函數(shù),其中 platform 參數(shù)指定對應的平臺:

        //taro-cli/src/build.ts

    ...
    await ctx.applyPlugins(hooks.ON_BUILD_START)
    await ctx.applyPlugins({
      name: platform,
      opts: {
        config: {
          ...config,
          isWatch,
          mode: isProduction ? 'production' : 'development',
          blended,
          isBuildNativeComp,
          newBlended,
          async modifyWebpackChain (chain, webpack, data) {
            await ctx.applyPlugins({
              name: hooks.MODIFY_WEBPACK_CHAIN,
              initialVal: chain,
              opts: {
                chain,
                webpack,
                data
              }
            })
          },
    ...

    除此之外呢,代碼轉(zhuǎn)換過程,還涉及:

    • 語法轉(zhuǎn)換:Taro 支持使用類似于 React 的 JSX 語法進行開發(fā),它將 JSX 代碼轉(zhuǎn)換為不同平臺所支持的語法,如小程序的 WXML、React Native 的組件等。
    • 樣式轉(zhuǎn)換:Taro 支持使用 CSS 預處理器編寫樣式,例如 Sass、Less 等。編譯過程中,Taro 將這些樣式文件轉(zhuǎn)換為不同平臺所支持的樣式表,如小程序的 WXSS、H5 的 CSS 等。

    在編譯過程中,Taro 還會執(zhí)行:

    • 靜態(tài)資源處理:Taro 會處理項目中的靜態(tài)資源文件,如圖片、字體等,將其轉(zhuǎn)換為適用于不同平臺的格式,并進行壓縮和優(yōu)化。
    • 文件復制:Taro 會將一些不需要編譯的文件直接復制到輸出目錄中,如項目配置文件、靜態(tài)頁面等。
    • 文件合并與分割:Taro 會根據(jù)配置和代碼中的引用關系,將多個文件進行合并或分割,以提高代碼加載性能。
    • 代碼壓縮與混淆:Taro 可以對生成的代碼進行壓縮和混淆,以減小文件體積和提高執(zhí)行效率。

    跨平臺適配和差異處理

    不通平臺的api或多或少,總有一些差異。Taro如何實現(xiàn)api的適配和差異化處呢?

    Taro 通過適配層和條件編譯等機制實現(xiàn) api 的適配和差異化處理。

    它提供了一套統(tǒng)一的 api 接口,開發(fā)者可以在代碼中使用這些 api,而 Taro 在編譯過程中會將這些 api 轉(zhuǎn)換為適用于各個平臺的具體實現(xiàn)。

    getLocation 為例。

    如果我們要使用定位功能,在 Taro 中只需要在項目中使用 Taro 提供的 api getLocation :

        Taro.getLocation().then(res => {
      console.log(res.latitude, res.longitude);
    });

    在編譯過程中,Taro 會根據(jù)目標平臺的差異,將這段代碼轉(zhuǎn)換為適用于不同平臺的具體實現(xiàn)。

    對于微信小程序來說,轉(zhuǎn)換為微信小程序的 wx.getLocation,同時保留原始的參數(shù)和回調(diào)函數(shù):

        wx.getLocation().then(res => {
      console.log(res.latitude, res.longitude);
    });

    而對于支付寶小程序而言,Taro 則會將其轉(zhuǎn)換為支付寶小程序的 my.getLocation,同樣保留原始的參數(shù)和回調(diào)函數(shù):

        my.getLocation().then(res => {
      console.log(res.latitude, res.longitude);
    });

    如此,Taro 在編譯過程中根據(jù)目標平臺的差異,將統(tǒng)一的 api 轉(zhuǎn)換為各個平臺所支持的具體 api。在這段代碼中,processApis 函數(shù)接收一個 api 集合作為參數(shù),并對其中的每個api進行處理:

        //shared/native-apis.ts

    function processApis (taro, global, config: IProcessApisIOptions = {}{
      ...
      apis.forEach(key => {
        if (_needPromiseApis.has(key)) {
          const originKey = key
          taro[originKey] = (options: Record<stringany> | string = {}, ...args) => {
            let key = originKey

            // 第一個參數(shù) options 為字符串,單獨處理
            if (typeof options === 'string') {
              ...
            }

            // 改變 key 或 option 字段,如需要把支付寶標準的字段對齊微信標準的字段
            if (config.transformMeta) {
              ...
            }
        ...

            // 為頁面跳轉(zhuǎn)相關的 api 設置一個隨機數(shù)作為路由參數(shù)。為了給 runtime 區(qū)分頁面。
            setUniqueKeyToRoute(key, options)

            // Promise 化:將原本的異步回調(diào)形式轉(zhuǎn)換為返回Promise對象的形式,使api的調(diào)用更加方便且符合現(xiàn)代JavaScript的異步處理方式。
            const p: any = new Promise((resolve, reject) => {
              obj.success = res => {
                config.modifyAsyncResult?.(key, res)
                options.success?.(res)
                if (key === 'connectSocket') {
                  resolve(
                    Promise.resolve().then(() => task ? Object.assign(task, res) : res)
                  )
                } else {
                  resolve(res)
                }
              }
              obj.fail = res => {
                options.fail?.(res)
                reject(res)
              }
              obj.complete = res => {
                options.complete?.(res)
              }
              if (args.length) {
                task = global[key](obj, ...args)
              } else {
                task = global[key](obj)
              }
            })

            // 給 promise 對象掛載屬性
            if (['uploadFile''downloadFile'].includes(key)) {
              ...
            }
            return p
          }
        } else {
          ...
        }
      })
      ...
    }

    ps:雖然 Taro 提供了一套統(tǒng)一的 api 接口,但某些平臺可能不支持特定的功能或特性。可能需要使用條件編譯來調(diào)用平臺特定的 api,以處理特定平臺的差異。

    跨平臺UI組件庫

    當我們使用 Taro 去編寫多端項目,需要使用 Taro 提供的 View 等Taro組件。

    因為,這些Taro組件,在不同平臺上會被轉(zhuǎn)換為相應的原生組件或元素。

    舉個例子,下面的代碼中,我們使用Taro提供的Image,View,Text組件創(chuàng)建視圖:

        import Taro from '@tarojs/taro';
    import { View, Text, Image } from '@tarojs/components';

    function MyComponent() {
      return (
        <View>
          <Text>Hello</Text>
          <Image src="path/
    to/image.png" />
        </View>
      );
    }

    在編譯生成過程中,Taro 會根據(jù)目標平臺的差異將組件轉(zhuǎn)換為適用于各個平臺的具體組件。比如View 組件會被轉(zhuǎn)換為微信小程序的 view 組件。對H5來說,View 組件會被轉(zhuǎn)換為 <div> 元素。

    在微信小程序中:

        <view>
      <text>Hello</text>
      <image src="path/to/image.png"></image>
    </view>

    在 H5 中:

        <div>
      <span>Hello</span>
      <img src="path/to/image.png" />
    </div>

    這樣,我們可以使用相同的代碼編寫視圖,也就是官方說的只要寫一套代碼的意思。

    通過抽象層、平臺適配、跨平臺編譯等處理,Taro其實已經(jīng)為多端組件庫的實現(xiàn)鋪平了道路。如果你要做一個 Taro-UI 那樣適應自己的多端組件庫。直接使用Taro提供的基礎組件去搭建復雜組件即可。

    反向轉(zhuǎn)換

    如果你說,你以前做過一個微信小程序,現(xiàn)在老板要你平行移植到支付寶等小程序中。來不及重構(gòu)代碼的話,反向轉(zhuǎn)換也許能救一救急。反向轉(zhuǎn)換,故名思義就是將小程序轉(zhuǎn)換為Taro項目。

    相關的代碼在 @tarojs/cli-convertor 包中,核心邏輯在 parseAst 中,生成 AST 樹,遍歷處理對應的內(nèi)容:

        //taro-cli-convertor/src/index.ts

    parseAst ({ ast, sourceFilePath, outputFilePath, importStylePath, depComponents, imports = [] }: IParseAstOptions): {
        ast: t.File
        scriptFiles: Set<string>
      } {
        ...
        // 轉(zhuǎn)換后js頁面的所有自定義標簽
        const scriptComponents: string[] = []
        ...
        traverse(ast, {
          Program: {
            enter (astPath) {
              astPath.traverse({
                //對類的遍歷和判斷
                ClassDeclaration (astPath){...},
               //表達式
                ClassExpression (astPath) {...},
                //導出
                ExportDefaultDeclaration (astPath) {...},
                //導入
                ImportDeclaration (astPath) {...},
                //調(diào)用
                CallExpression (astPath) {...},
                //檢查節(jié)點的 object 屬性是否為標識符 wx,如果是,則將 object 修改為標識符 Taro,并設置一個標志變量 needInsertImportTaro 為 true。這段代碼可能是將 wx 替換為 Taro,以實現(xiàn)對 Taro 框架的兼容性處理。
                MemberExpression (astPath) {...},
                //檢查節(jié)點的 property 屬性是否為標識符 dataset,如果是,則將 object 修改為一個 getTarget 函數(shù)的調(diào)用表達式,傳遞了兩個參數(shù) object 和標識符 Taro。它還創(chuàng)建了一個導入語句,將 getTarget 函數(shù)引入,并將其賦值給一個對象模式。這段代碼可能是對可選鏈式調(diào)用中的 dataset 屬性進行處理,引入了 getTarget 函數(shù)來實現(xiàn)相應的轉(zhuǎn)換。
                OptionalMemberExpression (astPath) {...},
                // 獲取js界面所有用到的自定義標簽,不重復
                JSXElement (astPath) {...},
                // 處理this.data.xx = XXX 的情況,因為此表達式在taro暫不支持, 轉(zhuǎn)為setData
                // 將this.data.xx=XX 替換為 setData()
                AssignmentExpression (astPath) {...}
              })
            },
            exit (astPath) {...}
          },
        })
      ...
        return {
          ast,
          scriptFiles,
        }
      }

    ps:盡管官方提供了反向轉(zhuǎn)換這一種工具,但是目前還是有局限性的。并不是所有的小程序都支持反向轉(zhuǎn)換,目前只有微信小程序。且并不是所有的原生 api 都可以被轉(zhuǎn)換,需要注意。希望后續(xù)該功能能夠繼續(xù)擴大,完善。

    性能優(yōu)化——預渲染(Prerender)

    為什么需要 Prerender?官方給出了解釋:

     

    Taro Next 在一個頁面加載時需要經(jīng)歷以下步驟:

     

    框架(React/Nerv/Vue)把頁面渲染到虛擬 DOM 中

     

    Taro 運行時把頁面的虛擬 DOM 序列化為可渲染數(shù)據(jù),并使用 setData() 驅(qū)動頁面渲染

     

    小程序本身渲染序列化數(shù)據(jù)

     

    和原生小程序或編譯型小程序框架相比,步驟 1 和 步驟 2 是多余的。如果頁面的業(yè)務邏輯代碼沒有性能問題的話,大多數(shù)性能瓶頸出在步驟 2 的 setData() 上:由于初始化渲染是頁面的整棵虛擬 DOM 樹,數(shù)據(jù)量比較大,因此 setData() 需要傳遞一個比較大的數(shù)據(jù),導致初始化頁面時會一段白屏的時間。這樣的情況通常發(fā)生在頁面初始化渲染的 wxml 節(jié)點數(shù)比較大或用戶機器性能較低時發(fā)生。

    Taro 預渲染的工作原理是,在構(gòu)建階段使用服務器端渲染(SSR)的技術(shù),將頁面組件渲染成靜態(tài) HTML 文件,并將其保存在靜態(tài)文件目錄中。然后,當客戶端請求該頁面時,直接返回預渲染的靜態(tài) HTML,而不是動態(tài)生成頁面。

    通過在構(gòu)建階段將頁面渲染為靜態(tài) HTML 文件,以提升首次加載速度、改善用戶體驗和優(yōu)化搜索引擎的索引。

    使用方式:

        //config/index.js 或 /config/dev.js 或 /config/prod.js

    const config = {
      ...
      mini: {
        prerender: {
          match: 'pages/shop/**'// 所有以 `pages/shop/` 開頭的頁面都參與 prerender
          include: ['pages/any/way/index'], // `pages/any/way/index` 也會參與 prerender
          exclude: ['pages/shop/index/index'// `pages/shop/index/index` 不用參與 prerender
        }
      }
    };

    module.exports = config

    更多使用詳見官網(wǎng)文檔。

    總結(jié)

    經(jīng)過上面粗淺的分析,我們可以初步了解 Taro 的整套運作機制。以下是對其運作機制的總結(jié):

    1. 代碼轉(zhuǎn)換和條件編譯:Taro 通過將代碼轉(zhuǎn)換和條件編譯應用于源代碼,生成適用于目標平臺的代碼。這使得我們可以使用一套代碼編寫多個平臺的應用程序。
    2. 抽象層和平臺適配層:Taro 提供了一個抽象層和平臺適配層來處理代碼轉(zhuǎn)換過程,確保 api 在不同平臺上的兼容性。這使得我們可以在不同的平臺上使用相同的 api 進行開發(fā)。
    3. Taro 自定義組件和多端適應性:Taro 的內(nèi)置組件天然適應框架,這意味著我們可以構(gòu)建適用于多個平臺的組件庫,如 Taro UI。這樣可以提高開發(fā)效率并實現(xiàn)跨平臺的一致性。
    4. 反向轉(zhuǎn)換:反向轉(zhuǎn)換是一種逆向思路,試圖通過將已有的應用程序轉(zhuǎn)換為 Taro 代碼來實現(xiàn)跨平臺。然而,反向轉(zhuǎn)換存在不穩(wěn)定性和局限性,并且對于維護者來說收益有限。
    5. 預渲染(Prerender)作為性能優(yōu)化選擇:Taro 提供了預渲染(Prerender)技術(shù)作為一種性能優(yōu)化選擇。預渲染可以在構(gòu)建過程中生成靜態(tài) HTML 頁面,以提升首次加載速度和優(yōu)化搜索引擎的索引。這是一種有效的性能優(yōu)化手段。

    參考文獻

    https://taro-docs.jd.com/docs/

    招賢納士

    政采云技術(shù)團隊(Zero),Base 杭州,一個富有激情和技術(shù)匠心精神的成長型團隊。政采云前端團隊(ZooTeam),一個年輕富有激情和創(chuàng)造力的前端團隊。團隊現(xiàn)有 80 余個前端小伙伴,平均年齡 27 歲,近 4 成是全棧工程師,妥妥的青年風暴團。成員構(gòu)成既有來自于阿里、網(wǎng)易的“老”兵,也有浙大、中科大、杭電等校的應屆新人。團隊在日常的業(yè)務對接之外,還在物料體系、工程平臺、搭建平臺、智能化平臺、性能體驗、云端應用、數(shù)據(jù)分析、錯誤監(jiān)控及可視化等方向進行技術(shù)探索和實戰(zhàn),推動并落地了一系列的內(nèi)部技術(shù)產(chǎn)品,持續(xù)探索前端技術(shù)體系的新邊界。

    瀏覽 49
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    久久无码鲁丝片午夜精品 | 99综合 | 综合网大香蕉 | 尤物成人在线 | 什么网址可以在线看国产毛片 |