小程式點睛之三:才不是你想的那種元件
小程式點睛系列寫到第三篇,我才在昨夜臨睡前想到這個名字。官方文件講過的東西我不重複,我只在你會寫的基礎上,幫你畫上點睛一筆。這一篇,是我與小程式鏖戰半年苦心孤詣的成果,滿滿乾貨。
自定義元件
從基礎庫 1.6.3 開始支援,用來抽象功能元件,以便在多個頁面複用。如果你有React
或Vue
等前端類庫的開發經驗,對自定義元件一定不會陌生。
小程式吸收了Vue
的模板語法,React
的狀態管理方式,再加上Web Component
的Shadow DOM
殘缺版本,總之就是一邊抄一點,形成了獨具特色的小程式風格元件。這使得你之前在其它類庫上獲得的經驗不能完全適用。
接下來,我們就一起聊聊小程式元件的奇技淫巧。
JavaScript 部分
小程式將元件資料分為父元件傳遞來的properties
和自身持有的data
,這點與主流前端框架類似,我們重點關注properties
。
元件的 property 接收type
、value
、observer
三個引數
type: 屬性型別,目前支援String
,Number
,Boolean
,Object
,Array
,null
從.wxml
傳遞過來的值到.js
之前會先經過型別轉換。如果想傳遞混合型別值(如既可能是String
也可能是Number
的值),可以將type
設定為null
,可以避免預設的型別轉換。
value: 預設值
如果不設定value
的值,那該property
的預設值就是其型別的零值。對應如下:
String => '' Number => 0 Boolean => false Object => {} Array => [] null => null 複製程式碼
observer: 屬性被改變後執行的函式
由於小程式自身不支援watch
,故而無法監聽data
中某一項的改變。但與React
不同的是,元件的properties
在元件執行時會被整合入data
中,通過this.data
獲取,並且可以呼叫this.setData
改動properties
的值。 因此可以將需要監聽的data
放於properties
中,利用observer
監聽。同時,為表示其為內部狀態,建議以下劃線(_)開頭,且不再外部設定其值。
observer
還可以用來減少.wxml
模板的重複程式碼。例如父元件傳遞了一個 source 物件,要求元件顯示其姓名與年齡
// component.js Component({ properties: { source: Object, } }) 複製程式碼
<!-- component.wxml --> <view>{{ source.name }}</view> <view>{{ source.age }}</view> 複製程式碼
利用 observer 可以寫成
// component.js Component({ properties: { source: { type: Object, observer(val) { this.setData(val) } } } }) 複製程式碼
<!-- component.wxml --> <view>{{ name }}</view> <view>{{ age }}</view> 複製程式碼
這對體量較大的元件猶為有效。同時,將source 封裝為 behavior
,可以最大限度的減少冗餘程式碼
// sourceBehavior.js export default Behavior({ properties: { source: { type: Object, observer(val) { this.setData(val) } } } }) // component.js import sourceBehavior from 'path/to/sourceBehavior.js' Component({ behaviors: [sourceBehavior] }) 複製程式碼
WXML 部分
.wxml
模組與Vue
模板大同小異,唯一值得了解的是由於小程式不完全實現了Shadow DOM
,Vue
中<template>
標籤只能有一個根節點的限制是不存在的。因此在大多數情況下,你都不需要給.wxml
額外新增根節點。
<!-- good.wxml --> <view>{{ name }}</view> <view>{{ age }}</view> <!-- bad.wxml --> <view> <view>{{ name }}</view> <view>{{ age }}</view> </view> 複製程式碼
WXSS 部分
inherit
關鍵字
我們可以通過將屬性值設定為inherit
來有效減少程式碼冗餘。如果你想保持節點屬性與父節點一致,就應當使用這個關鍵字。
回想一下小程式點睛之二:小程式使用 Iconfont 的正確姿勢
,預設 Iconfont 下載的 CSS 檔案中定義了.iconfont
類的字型大小為16px
,如果你不想每次更新改動這一值,那麼可以在元件樣式表中新增
.iconfont { font-size: inherit; } 複製程式碼
元件樣式表的優先順序要高過外部引入的樣式表,所以原本的font-size: 16px
就會被覆蓋,.iconfont
的字型大小就會跟其父節點一致。
注意:這裡.iconfont
的父節點並不是邏輯上的父節點,而是shadow-root
。
:host
選擇器
你可以在開發者工具中看到每個自定義元件的shadow-root
,其預設是行內元素,你可以在元件樣式表中通過:host
選擇器修改它的樣式
:host { display: block; background: red; } 複製程式碼
外部樣式表所有的屬性都是直接作用在shadow-root
(而非模板中的根節點)上,例如
<!-- iconfont.wxml --> <text class="iconfont icon-{{icon}}"></text> <!-- page.wxml --> <iconfont class="icon" icon="upload"></iconfont> 複製程式碼
/* page.wxss */ .icon { color: green; } /* iconfont.wxml */ .iconfont { color: red; } 複製程式碼
你會發現圖示的顏色是紅色而不是綠色。因為.icon
是樣式作用於shadow-root
,.iconfont
是其子節點,繼承其color
屬性,但由於.iconfont
優先順序更高,所以顏色是為紅色。
優先順序關係為:host
選擇器 < 外部樣式 < 子節點樣式。利用這個特點,配合 CSS 變數,可以達到意想不到的效果。
CSS 變數
現階段支援各瀏覽器的支援度並不高,版本稍舊的瀏覽器就會遇到問題。但小程度已經完全支援了這個特性。
由於小程式使用了Shadow DOM
,因此在元件內是無法使用全域性定義的樣式類的。但是
,CSS 是可繼承並且是全域性可用的。因此,我們可以將常用的樣式定義在app.wxss
內,並在任意頁面或元件內使用
/* app.wxss */ page { --primary-color: #aabbcc; --accent-color: #ddeeff; --spacing: 32rpx; } /* other.wxss */ .component { background: var(--primary-color); padding: var(--spacing); } 複製程式碼
如此,你只需要對app.wxss
稍作修改,就可以改變整個小程式樣式。
除此之外,我們還可以利用這個特性,優化元件定義。例如,我們需要一個圓形元件,常規的做法是定義相等的寬高,再設定border-radius: 50%
。我們來看看高階的寫法
/* circle.wxss */ :host { /* 預設直徑為 32rpx */ --diameter: 32rpx; width: var(--diameter); height: var(--diameter); border-radius: 50%; } /* outer.wxss */ .circle { /* 將 circle 元件的直徑設定為 64rpx */ --diameter: 64rpx; } 複製程式碼
還記得我剛剛說的優先順序規則嗎?外部樣式的優先順序高於:host
選擇器,因此 circle 元件的直徑會被設定為64rpx
。
不只如此,通過自定義屬性,我們還可以修改巢狀較深的元件樣式
<!-- component.wxml --> <view class="this"> <view class="is"> <view class="a"> <view class="embed-component"></view> </view> </view> </view> 複製程式碼
/* component.wxss */ :host { --embed-color: red; } .embed-component { color: var(--embed-color); } /* outer.wxss */ .component { --embed-color: blue; } 複製程式碼
Bingo,藉助 CSS 屬性,我們獲得了改動後代節點樣式的能力。你大可以說同樣的功能使用externalClasses
或者addGlobalClass
也可以做到。但是對於元件來說,外部傳入的樣式可知,處理起來心智負擔更小,更符合高內聚,低耦合
的標準。同時,建議將可改動的自定義 CSS 變數以註釋的形式寫在component.js
中,方便其他人理解你的元件。
用em
而不是rpx
為了保持元件可擴充套件性,應當儘可能的使用em
作為字型大小單位。我之前用一個小程式解析markdown
的元件庫,其中將所有字型大小都寫死為rpx
,例如
/* markdown.wxss */ .h1 { font-size: 32rpx; } .h2 { font-size: 28rpx; } .h3 { font-size: 24rpx; } 複製程式碼
如此固然能實現需求,但卻失去了外部改變字型大小的能力。作為一個元件,應該適應更多的可能性,改為em
作為字型大小單位就能很好的解決這一問題
.h1 { font-size: 2em; } .h2 { font-size: 1.8em; } .h3 { font-size: 1.4em; } 複製程式碼
數值僅為舉例
大膽使用新特性
小程式對 CSS 3 新特性的支援比較完善,vw
,vh
,flex
,calc
,CSS variables 等等只要你想到,你可以大膽地使用,完全不需要考慮相容性問題,這簡直就是前端夢寐以求的試驗田。所以,好好學習,大膽使用吧!
後記
寫了這麼多,自己都忘了要寫些什麼。本來是準備日更的,但是這兩天一直在忙社保的事。剛剛從公司離職,辦理靈活就業人員耽誤了不少時間。感謝各位耐心閱讀至此,這都是我工作半年以來的經驗之談,希望可以對你們有一點幫助。
如果你也想釋出小程式的元件,官方模板
雖不失為一個選擇,但我也要強推一下我寫的腳手架工具tacer
,只需一行npx tarcer wx-component
就可以開始你的專案了哦!
小程式點睛系列還剩下一篇,最後一篇,我們單純聊JavaScript
,關注我喲。