【小程式】實現複用及造輪子入門

倉庫發表於2018-08-03

 小程式實現可以通過 template 和 component 兩種方式實現程式碼的複用,以減少不必要的工作量。template 被官方稱為模組,在 xml 和 css 中定義程式碼片段,然後可以在其他頁面使用,事實上,template 像是複製貼上的另一種形式,只是把複製貼上的操作使用 <template> 標籤來代替,減少了頁面的簡潔程度,方便維護(有點像 android 中的 <include> )。比起template, component實現的自定義元件,可以進行自己的邏輯處理,並像官方提供的元件一樣去使用。

template 和 component 的不同

  • component 和普通的 page 一樣有 xml、css、json 和 js 檔案,template 只有 xml 和 css 檔案
  • component 可以處理點選事件,tempalte 需要在使用者的 js 中定義
  • template 的動態資料均來自使用者的傳遞,component 不是
  • 對於 template,在使用者的 css 檔案中定義的樣式會覆蓋模組中定義的樣式,而對於 component,app 的樣式和使用者的樣式均不會影響到元件內部(除非使用externalClasses )
  • component 只能使用類選擇器 ,template 沒有限制
    ......

template 複用彈窗

 template 通常在複用的部分邏輯處理比較少的情況下使用,如在應用中有一些 UI 一樣、但文字內容不一樣的提示彈框,這時可以使用 template 來達到複用的目的。

  1. 定義

建立一個目錄,在目錄下建立 xml 和 css 檔案,編輯彈框的 xml 和 css 檔案和編輯普通的 page 基本沒有什麼差別,唯一不同的是在 xml 的最外層需要套上一個 <template> 標籤,在該標籤中定義 template 的 name 屬性,這個 name 屬性將成為該彈框的標識。另外 template 中動態資料通過引用者傳入。

<!--tip-dialog.wxml-->
<template name="tip-dialog">
    <view class='container'>
        <view class='tip-content'>
            <image class='content-icon' src="{{icon}}"></image>
            <text class='content-text'>{{text}}</text>
        </view>
        <view class='tip-btn' bindtap='clickedBtn'>哦~</view>
    </view>
</template>
複製程式碼
  1. 使用
  • xml 檔案中使用 標籤引入模板
<import src="/pages/tip-dialog/tip-dialog"/>
複製程式碼
  • 使用 <template> 標籤指明模板,並傳入動態資料,data 屬性中的 ... 是展開符號,將 js 中的物件屬性展開傳遞到模板,使模板中可以直接通過屬性名稱使用,而不是物件.屬性
<template is="tip-dialog" data="{{...tipData}}" wx:if="{{showTip}}"/>
複製程式碼
  • css 檔案中使用 @import 引入模板的 css 檔案,完成
@import "/pages/tip-dialog/tip-dialog";
複製程式碼

component 造簡單的輪子

 微信從 1.6.3開始支援自定義元件,自定義元件擁有擁有和 page 一樣的四個檔案,其中 js 檔案的結構有一些差異,不過寫法基本沒什麼變化。建立自定義元件的步驟如下:

  • 新建目錄,右鍵建立 component

  • component.json

{
  "component": true
}
複製程式碼
  • 和編輯普通的頁面一樣編輯元件,注意是 js 檔案的一些差異
    • properties:是元件對外開放的屬性,當在使用者 xml 檔案中使用該元件時,可以為這些屬性傳值達到改變元件的目的
    • data:內部資料,和 page 的 data 一樣,和 properties 不同的是它是對內的
    • mthods:元件的自定義方法都定義在其內部,親測定義在外部無法識別
    • externalClasses:元件外部樣式,元件內部的樣式是不受 app 和 使用者的 css 影響的,如果有元件內部有一些樣式希望在使用者使用時才決定,那麼久可以通過 externalClasses 去實現
    • behaviors:用於元件間的引用(詳情:文件-元件間的關係)
    • 生命週期 :created()、attached()、ready()、moved()、dettach()
  Component({
    //選項
    options: {
      multipleSlots: true // 啟用多slot(插槽)支援
    },
    properties: {
      //外部屬性
      text: {
        type: String, //型別
        value: 'default value', //預設值
        observer: '方法名' //當資料發生變化時呼叫
      }
    },
    //外部樣式
    externalClasses: ['text-class'],
    data: {
      // 內部資料
      someData: {}
    },
    //生命週期
    attached: function(){},
    moved: function(){},
    detached: function(){},
    methods: {
      // 自定義方法
      customMethod: function(){}
    }
  })
複製程式碼
  • 使用者 json
{
  "usingComponents": {
      //元件名-路徑
      "component-name": "component-path",
  }
}
複製程式碼
  • 使用者 xml
<component-name property="" />
複製程式碼

自定義導航欄

 小程式官方提供的導航欄可以控制的部分太少,很多時候並不能滿足專案的需求,從 1.9.1 開始,微信開始支援自定義導航欄,這裡使用 component 實現一個導航欄。功能如下:

  • 導航欄有標題,返回鍵,分割線
  • 導航欄高度自適應,標題和返回鍵始終垂直居中
  • 導航欄可由使用者指定背景顏色或圖片
  • 標題的內容和樣式對使用者開放
  • 返回鍵可顯示和不顯示,按鈕的 icon 可由使用者提供,且提供預設 icon,點選可返回上一個介面
  • 分割線可顯示和不顯示,使用者可決定顏色
    <!--nav-bar.wxml-->
    <view class='nav-container' style='height:{{customeHeight? navHeight:height}}px;background:{{bgColor}};'>
        <image class="nav-background-img" src='{{imageBg}}' style='height:{{height}}px;' mode='top' wx:if="{{useImageBg}}"></image>
        <image class='nav-back' src='{{backIcon}}' wx:if='{{showBack}}' bindtap='clickedNavBarBack'/>
        <view class='nav-title-view' style='margin-bottom: {{marginB}}rpx;'>
            <view class='nav-title-class'>{{title}}</view>
        </view>
        <view class='nav-line' style='background-color:{{lineColor}};' wx:if="{{showLine}}"></view>
    </view>
複製程式碼

tips:

  • 不使用 externalClasses 的情況下,元件使用者 css 不能覆蓋元件中的 css
  • 在使用 externalClasses 的情況下,元件內部 xss 中對於該 class 樣式失效,但是 wxml 中 style 屬性指定的樣式仍有效
    //nav-bar.js
    Component({
        externalClasses: ['nav-title-class'],
        properties: {
            //指定導航欄高度,不指定將使用預設的計算方式
            navHeight: {
                type: Number,
                value: -1,
                observer: 'navHeightChange'
            },
            //導航欄標題
            title: {
                type: String,
                value: "VUI"
            },
            //是否顯示返回鍵
            showBack: {
                type: Boolean,
                value: true
            },
            //背景顏色,預設 #fff
            bgColor: {
                type: String,
                value: ''
            },
            //背景圖片,預設不使用,使用後將覆蓋掉背景顏色
            imageBg: {
                type: String,
                value: '',
                observer: 'imageBgChange'
            },
            //指定返回按鈕的 Icon
            backIcon: {
                type: String,
                value: DEFAULT_BACKICON
            },
            //導航欄底部分割線顏色 
            lineColor: {
                type: String,
                value: "#ccc"
            }, 
            //是否顯示底部分割線
            showLine: {
                type: Boolean,
                value: true
            }
        },
        data: {
            height: 64,
            useImageBg: false,
            customHeight: false,
            marginB: 10
        },
        created: function() {
            _this = this
        },
        /**
         * ready 開始可以獲得控制元件資訊
         */
        ready: function() {
            let height = _this.getNavBarHeight()
            _this.calculatePosition(height)
        },
        methods: {
            clickedNavBarBack: function() {
                console.log("navBar.clikedBack()")
                wx.navigateBack({})
                //使用者可以繫結這個方法來監聽返回鍵點選事件(bindtapBack)
                this.triggerEvent("tapBack")
            },
            calculatePosition: function (height) {
                let sys = wx.getSystemInfoSync()
                let query = wx.createSelectorQuery().in(this)
                query.select(".nav-title-view").boundingClientRect()
                query.exec(function (rects) {
                    let titleH = rects[0].height
                    let realH = height - sys.statusBarHeight
                    let marginB = (realH - titleH) / 2  
                    _this.setData({
                        height: height,
                        marginB: marginB
                    })
                })
            },
            getNavBarHeight: function () {
                let height = 64
                let res = wx.getSystemInfoSync();
                let system = res.system
                if(system.search('android') != -1) {
                    height = 68
                } else {
                    let model = res.model
                    if (model.search('iPhone X') != -1) {
                        height = 88
                    } else if (model.search('iPad') != -1) {
                        height = 100
                    }
                }
                return height
            },
            navHeightChange: function (newVal, oldVal) {
                _this.setData({
                    customeHeight: true
                })
            },
            imageBgChange: function(newVal, oldVal) {
                if(newVal != '') {
                    _this.setData({
                        useImageBg: true
                    })
                }
            }
          
        }
    })
複製程式碼

對於標題欄高度,使用 px 作為單位,android 統一為 68,iPhone X 為 88,iPhone 其他為 64。對於居中,首先將標題和返回按鈕固定在導航欄底部,然後計算 margin-bottom 的值(導航欄高度包括了狀態列的高度)。

    <!-- index.wxml -->
    <nav-bar show-back="{{true}}" title="VUI" nav-title-class="nav-title-class" line-color="#aaa" showLine="{{showNavLine}}"  bindtapBack="tapBack"></nav-bar>
複製程式碼

效果:

VUI

++:

相關文章