小程序点睛之三:才不是你想的那种组件

前言

小程序点睛系列写到第三篇,我才在昨夜临睡前想到这个名字。官方文档讲过的东西我不重复,我只在你会写的基础上,帮你画上点睛一笔。这一篇,是我与小程序鏖战半年苦心孤诣的成果,满满干货。javascript

自定义组件

从基础库 1.6.3 开始支持,用来抽象功能组件,以便在多个页面复用。若是你有 ReactVue 等前端类库的开发经验,对自定义组件必定不会陌生。css

小程序吸取了 Vue 的模板语法,React 的状态管理方式,再加上 Web ComponentShadow DOM 残缺版本,总之就是一边抄一点,造成了独具特点的小程序风格组件。这使得你以前在其它类库上得到的经验不能彻底适用。html

接下来,咱们就一块儿聊聊小程序组件的奇技淫巧。前端

JavaScript 部分

小程序将组件数据分为父组件传递来的 properties 和自身持有的 data,这点与主流前端框架相似,咱们重点关注 propertiesjava

组件的 property 接收 typevalueobserver 三个参数git

type: 属性类型,目前支持 String, Number, Boolean, Object, Array, null

.wxml 传递过来的值到 .js 以前会先通过类型转换。若是想传递混合类型值(如既多是 String 也多是 Number 的值),能够将 type 设置为 null,能够避免默认的类型转换。github

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 对象,要求组件显示其姓名与年龄bash

// 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 DOMVue<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 新特性的支持比较完善,vwvhflexcalc,CSS variables 等等只要你想到,你能够大胆地使用,彻底不须要考虑兼容性问题,这简直就是前端求之不得的试验田。因此,好好学习,大胆使用吧!

后记

写了这么多,本身都忘了要写些什么。原本是准备日更的,可是这两天一直在忙社保的事。刚刚从公司离职,办理灵活就业人员耽误了很多时间。感谢各位耐心阅读至此,这都是我工做半年以来的经验之谈,但愿能够对大家有一点帮助。

若是你也想发布小程序的组件,官方模板虽不失为一个选择,但我也要强推一下我写的脚手架工具 tacer,只需一行 npx tarcer wx-component 就能够开始你的项目了哦!

小程序点睛系列还剩下一篇,最后一篇,咱们单纯聊 JavaScript,关注我哟。

相关文章
相关标签/搜索