<address id="r9vd9"><address id="r9vd9"><listing id="r9vd9"></listing></address></address>

      歡迎您光臨深圳塔燈網絡科技有限公司!
      電話圖標 余先生:13699882642

      網站百科

      為您解碼網站建設的點點滴滴

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

      發表日期:2019-11 文章編輯:小燈 瀏覽次數:5062

      戰爭,信念,意志和情感,這些散發著光芒和硝煙的詞匯,象一枚枚炮彈轟入我們現在的生活。歷史的記憶不會被抹滅。

      當我們在各自項目里幸福的拷貝著官方代碼 demo,在 componnets 文件夾里使用 Component 方法書寫一個個組件時,不要忘記,在 2018 年上半年以前,小程序是沒有提供組件化方案的。

      當時,主要有兩種解決方法,一種是 WePY 拷貝法,另一種則是摩拜 template 法。

      WePY 拷貝法

      比如有個最簡單的按鈕組件:

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

      經過編譯后結果如下:

      <view class="container">
        <view class="button">
          <button bindtap="$wpyButton$onTap">點這里</button>
        </view>
        <view class="button">
          <button bindtap="$wpyButton2$onTap">點這里</button>
        </view>
      </view>

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

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

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

      摩拜 template 法

      有心的同學可能記得當初我們發了這篇文章:微信小程序組件化解決方案wx-component,當時主要講了如何使用,這次講講技術的細節。

      主要利用小程序當時提供的 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>

      由于知道這只是臨時的解決方法,最終還會遷移到微信官方組件化方案。了解到微信團隊正在開發,就死皮賴臉找了微信研發同學要下技術方案,以便后期遷移成本做到最低。最后微信同學不耐煩的扔給我們如下代碼,并特別囑咐不要泄露出去

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

      一目了然,依照此文檔實現一個簡單的組件化方案也有了思路。

      如何引入組件

      由于沒有辦法在小程序全局注入 Component 方法,可以將組件代碼以模塊方式導出,在頁面的 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: '開始',
            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,以及組件對應的屬性值和方法。為了使這些新增傳參生效,那勢必需要對 Page 進行改造。

      改造 Page

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

      Page = funtion() {}

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

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

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

      Component 怎么寫

      精簡了核心的代碼如下:

      function noop() {}
      
      class Component {
        constructor (config) {
          // 兼容 onLoad onUnload 的寫法
          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
          // 更新頁面的 data
          parent.setData(newData)
        }
        setName (name) {
          this.name = name
        }
        setParent (parent) {
          this.parent = parent
        }
      }

      主要完成了三件事:

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

      有個細節,為了讓大家容易理解(不泄露微信的方法名),分享到外部的文章用 onLoad、onUnload 代替了 attached 、detached,但內部早就開始用微信命名的這兩個屬性名,才有了代碼中的兼容寫法。

      自定的 Page 怎么寫

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

      // 緩存下微信的 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]
              })
            }
            
            // 合并所有依賴組件的生命周期函數
            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 {
            // 沒有依賴組件,直接透傳給微信的 Page 方法
            originalPage(origin)
          }
      
        }
      }

      可能有點亂,其實就是不斷 merge datamethod的過程。最終所有組件自定的數據和方法都被掛在到了 Page 的傳參里。

      最后,導出自定義的 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

      不完善的地方

      雖然滿足業務了,但也是有些問題的,例如上面 MyPage 方法里的這段:

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

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

      也有很多不完善的地方,但通過內部約束代碼規范也基本可以解決。

      結語

      這種近乎 Hack 的方式支撐了摩拜單車小程序業務大半年的時間,期間產出了大大小小十多個組件。而由于組件內部基本是按照微信官方組件化 api 書寫,等待官方推出組件化方案后,全部遷移過去的成本也大大減小。


      本頁內容由塔燈網絡科技有限公司通過網絡收集編輯所得,所有資料僅供用戶學習參考,本站不擁有所有權,如您認為本網頁中由涉嫌抄襲的內容,請及時與我們聯系,并提供相關證據,工作人員會在5工作日內聯系您,一經查實,本站立刻刪除侵權內容。本文鏈接:http://www.webpost.com.cn/25243.html
      相關小程序
       八年  行業經驗

      多一份參考,總有益處

      聯系深圳網站公司塔燈網絡,免費獲得網站建設方案及報價

      咨詢相關問題或預約面談,可以通過以下方式與我們聯系

      業務熱線:余經理:13699882642

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

      国产成人精品综合在线观看