• <ul id="cgeq2"></ul>
  • 歡迎您光臨深圳塔燈網(wǎng)絡(luò)科技有限公司!
    電話圖標(biāo) 余先生:13699882642

    網(wǎng)站百科

    為您解碼網(wǎng)站建設(shè)的點(diǎn)點(diǎn)滴滴

    小程序技能進(jìn)階回憶錄 - 在缺少組件化的日子里

    發(fā)表日期:2019-11 文章編輯:小燈 瀏覽次數(shù):5108

    戰(zhàn)爭(zhēng),信念,意志和情感,這些散發(fā)著光芒和硝煙的詞匯,象一枚枚炮彈轟入我們現(xiàn)在的生活。歷史的記憶不會(huì)被抹滅。

    當(dāng)我們?cè)诟髯皂?xiàng)目里幸福的拷貝著官方代碼 demo,在 componnets 文件夾里使用 Component 方法書(shū)寫(xiě)一個(gè)個(gè)組件時(shí),不要忘記,在 2018 年上半年以前,小程序是沒(méi)有提供組件化方案的。

    當(dāng)時(shí),主要有兩種解決方法,一種是 WePY 拷貝法,另一種則是摩拜 template 法。

    WePY 拷貝法

    比如有個(gè)最簡(jiǎn)單的按鈕組件:

    <!-- components/button.wpy -->
    <template>
      <view class="button">
        <button @tap="onTap">點(diǎn)這里</button>
      </view>
    </template>
    
    <!-- pages/index.wpy -->
    <template>
      <view class="container">
        <wpy-button /> // button 組件1
        <wpy-button2 /> // button 組件2
      </view>
    </template>

    經(jīng)過(guò)編譯后結(jié)果如下:

    <view class="container">
      <view class="button">
        <button bindtap="$wpyButton$onTap">點(diǎn)這里</button>
      </view>
      <view class="button">
        <button bindtap="$wpyButton2$onTap">點(diǎn)這里</button>
      </view>
    </view>

    為了方便變量隔離,所以引入到頁(yè)面中的組件得單獨(dú)命名:

    import wepy from 'wepy'
    import Button from '@/components/button'
    export default class Index extends wepy.page {
      components = {
        'wpy-button': Button,
        'wpy-button2': Button
      }
      ...
    }

    有一些不便的地方,但也很好的解決了組件化缺失的問(wèn)題。

    摩拜 template 法

    有心的同學(xué)可能記得當(dāng)初我們發(fā)了這篇文章:微信小程序組件化解決方案wx-component,當(dāng)時(shí)主要講了如何使用,這次講講技術(shù)的細(xì)節(jié)。

    主要利用小程序當(dāng)時(shí)提供的 template 模板方法,使用方式如下:

    <!-- pages/template/login.wxml -->
    <template name="login">
      <view class="login">這是登錄組件</view>
    </template>
    <!-- pages/login/index.wxml -->
    <import src='../../components/login/index.wxml'/>
    <view class="login-box">
      <template is="login" data="{{...}}"></template>
    </view>

    由于知道這只是臨時(shí)的解決方法,最終還會(huì)遷移到微信官方組件化方案。了解到微信團(tuán)隊(duì)正在開(kāi)發(fā),就死皮賴臉找了微信研發(fā)同學(xué)要下技術(shù)方案,以便后期遷移成本做到最低。最后微信同學(xué)不耐煩的扔給我們?nèi)缦麓a,并特別囑咐不要泄露出去

    Component({
      // 組件名
      name: '',
      // 為其他組件指定別名
      using: {},
      // 類似mixins,組件間代碼復(fù)用
      behaviors: [],
      // 組件私有數(shù)據(jù)
      data: {
      },
      // 外部傳入的組件屬性
      propties: {
      },
      // 當(dāng)組件被加載
      attached () {
      },
      // 當(dāng)組件被卸載
      detached () {
      },
      // 組件私有方法
      methods: {
      }
    })

    一目了然,依照此文檔實(shí)現(xiàn)一個(gè)簡(jiǎn)單的組件化方案也有了思路。

    如何引入組件

    由于沒(méi)有辦法在小程序全局注入 Component 方法,可以將組件代碼以模塊方式導(dǎo)出,在頁(yè)面的 Page 方法里引入:

    // components/login/index.wxml
    <template name="login">
      <form bindsubmit="onLoginSubmit">
        ...
        <button type="primary" formType="submit">{{btnText}}</button>
      </form>
    </template>
    // components/login/index.js
    module.exports = {
      name: 'login',
      data: {
        btnText: ''
      }
      ....
    }
    // pages/index/index.js
    Page({
      data: {
        ...
      },
      components: {
        login: {
          btnText: '開(kāi)始',
          onLoginCallback() { ... }
        }
      }
    })
    <!-- pages/index/index.wxml -->
    <import src='../../components/login/index.wxml'/>
    <view class="login-box">
      <template is="login" data="{{...login}}"></template>
    </view>

    Page 的傳參里多了 components 屬性,傳入了組件名login,以及組件對(duì)應(yīng)的屬性值和方法。為了使這些新增傳參生效,那勢(shì)必需要對(duì) Page 進(jìn)行改造。

    改造 Page

    如何用一行代碼毀掉你的小程序,在小程序根目錄的 app.js 里加入這段代碼即可:

    Page = funtion() {}

    這樣核心的 Page 的方法就被覆蓋掉了,所以利用這個(gè)“特性”,可以改造 Page 方法:

    // utils/wx.js
    var page = function() {
      // 改造代碼
      ...
    }
    module.exports = {
      page
    }
    // app.js
    Page = require('./utils/wx').page

    這就完成了獨(dú)一無(wú)二的自定義的小程序 Page 的方法。

    Component 怎么寫(xiě)

    精簡(jiǎn)了核心的代碼如下:

    function noop() {}
    
    class Component {
      constructor (config) {
        // 兼容 onLoad onUnload 的寫(xiě)法
        config.onLoad = config.onLoad || config.attached || noop
        config.onUnload = config.onUnload || config.detached || noop
        this.data = config.data || {}
        this.config = config
        this.methods = config.methods || {}
        for (let name in this.methods) {
          // 為了使組件事件綁定生效,直接掛在到 this 下
          this[name] = methods[name]
        }
      }
      setData (data, deepExtend) {
        let name = this.name
        let parent = this.parent
        let mergeData = extend(deepExtend !== false, parent.data[name], data)
        let newData = {}
        newData[name] = mergeData
        this.data = mergeData
        // 更新頁(yè)面的 data
        parent.setData(newData)
      }
      setName (name) {
        this.name = name
      }
      setParent (parent) {
        this.parent = parent
      }
    }

    主要完成了三件事:

    • 配置了組件的生命周期事件 attacheddetached
    • 綁定了組件的事件,使得 templatebindtap 等代碼生效
    • 實(shí)現(xiàn)了組件的 setData 功能

    有個(gè)細(xì)節(jié),為了讓大家容易理解(不泄露微信的方法名),分享到外部的文章用 onLoadonUnload 代替了 attacheddetached,但內(nèi)部早就開(kāi)始用微信命名的這兩個(gè)屬性名,才有了代碼中的兼容寫(xiě)法。

    自定的 Page 怎么寫(xiě)

    整理了大致的核心代碼如下:

    // 緩存下微信的 Page
    const originalPage = Page
    // 組件生命周期
    const LIFETIME_EVENT = [
      'onLoad',
      'onUnload'
    ]
    class MyPage {
      constructor (origin) {
        this.origin = origin
        this.config = {}
        this.children = {}
        this.childrenEvents = {}
    
        // 是否需要`components`
        let components = this.components = origin.components
    
        if (components) {
          this.config.data = {}
          for (let item in components) {
            let props = components[item] || {}
            let component = new Component(require(`../components/${item}/index`))
    
            this.children[name] = component
            // 合并組件的 data
            extend(component.data, component.props)
            // ...
            // 合并組件的 method
            for (let fnName in component.methods) {
              this.config[fnName] = component.methods[fnName].bind(component)
            }
            // ...
            let childrenEvents = this.childrenEvents[item] = {}
            LIFETIME_EVENT.forEach((prop) => {
              childrenEvents[item][prop] = component.config[prop]
            })
          }
          
          // 合并所有依賴組件的生命周期函數(shù)
          LIFETIME_EVENT.forEach((prop) => {
            this.config[prop] = () => {
              for (let item in this.components) {
                this.childrenEvents[item][prop].apply(this.component, arguments)
              }
              this.origin[prop] && this.origin[prop].apply(this, arguments)
            }
          })
    
          // 把新生成的 config 傳給原始的微信的 Page 方法
          originalPage(this.config)
        } else {
          // 沒(méi)有依賴組件,直接透?jìng)鹘o微信的 Page 方法
          originalPage(origin)
        }
    
      }
    }

    可能有點(diǎn)亂,其實(shí)就是不斷 merge datamethod的過(guò)程。最終所有組件自定的數(shù)據(jù)和方法都被掛在到了 Page 的傳參里。

    最后,導(dǎo)出自定義的 page

    // utils/wx.js
    const page = function (config) {
      return new MyPage(config)
    }
    
    module.exports = {
      page
    }

    app.js 中覆蓋掉原有的 Page 方法:

    // app.js
    Page = require('./utils/wx').page

    不完善的地方

    雖然滿足業(yè)務(wù)了,但也是有些問(wèn)題的,例如上面 MyPage 方法里的這段:

    for (let fnName in component.methods) {
      this.config[fnName] = component.methods[fnName].bind(component)
    }

    可以看出,直接把組件內(nèi)部定義的方法,掛在到 config 中去了,這就要求頁(yè)面的方法和組件的方法不能重名,這是為了方便 template 可以直接綁定組件定義的事件,只能通過(guò)把組件事件轉(zhuǎn)移到頁(yè)面的事件方法里。

    也有很多不完善的地方,但通過(guò)內(nèi)部約束代碼規(guī)范也基本可以解決。

    結(jié)語(yǔ)

    這種近乎 Hack 的方式支撐了摩拜單車小程序業(yè)務(wù)大半年的時(shí)間,期間產(chǎn)出了大大小小十多個(gè)組件。而由于組件內(nèi)部基本是按照微信官方組件化 api 書(shū)寫(xiě),等待官方推出組件化方案后,全部遷移過(guò)去的成本也大大減小。


    本頁(yè)內(nèi)容由塔燈網(wǎng)絡(luò)科技有限公司通過(guò)網(wǎng)絡(luò)收集編輯所得,所有資料僅供用戶學(xué)習(xí)參考,本站不擁有所有權(quán),如您認(rèn)為本網(wǎng)頁(yè)中由涉嫌抄襲的內(nèi)容,請(qǐng)及時(shí)與我們聯(lián)系,并提供相關(guān)證據(jù),工作人員會(huì)在5工作日內(nèi)聯(lián)系您,一經(jīng)查實(shí),本站立刻刪除侵權(quán)內(nèi)容。本文鏈接:http://www.juherenli.com/25243.html
    相關(guān)小程序
     八年  行業(yè)經(jīng)驗(yàn)

    多一份參考,總有益處

    聯(lián)系深圳網(wǎng)站公司塔燈網(wǎng)絡(luò),免費(fèi)獲得網(wǎng)站建設(shè)方案及報(bào)價(jià)

    咨詢相關(guān)問(wèn)題或預(yù)約面談,可以通過(guò)以下方式與我們聯(lián)系

    業(yè)務(wù)熱線:余經(jīng)理:13699882642

    Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.    

    99精品在线免费观看| 国产亚洲精品国产福利在线观看| 久久亚洲中文字幕精品一区四 | 99re热这里只有精品| 国产久爱免费精品视频| 苍井空亚洲精品AA片在线播放| 亚洲精品免费视频| a级国产精品片在线观看| 精品中文字幕一区二区三区四区| 99久久人妻无码精品系列| 久久国产成人精品| 综合人妻久久一区二区精品| 男女男精品视频网站在线观看| 国产成人精品午夜福利在线播放 | 精品免费AV一区二区三区| 日本精品无码一区二区三区久久久| 99久热只有精品视频免费观看17| 中文无码久久精品| 精品91自产拍在线观看| 国产亚洲精品国产福利在线观看 | 亚欧人成精品免费观看| 日韩精品无码一区二区三区免费 | 少妇人妻无码精品视频app| 精品视频一区二区三区在线观看| 久久精品成人免费观看97| 亚洲av无码成人精品区| 国产成人精品福利网站人| 国产精品黄大片在线播放| 亚洲综合精品成人| 久久久这里有精品| 精品乱码一卡2卡三卡4卡网 | 国产成人无码精品久久久久免费| 国产精品久久久久影院| 国内精品自产拍在线观看| 国产精品videossexohd| 精品久久久久久久无码久中文字幕 | 精品亚洲成a人在线观看| 日韩国产精品亚洲а∨天堂免| 亚洲av日韩精品久久久久久a| 久久国产亚洲精品| 亚洲av午夜国产精品无码中文字|