超爱Web Components: 用它封装通用组件(二)(Designed once, Built once, Used everywhere)

1,172 阅读5分钟

我正在参加「掘金·启航计划」,快来一起参与吧!

我正在参加「掘金·启航计划」

前言

上一章,我们已经初步实现了zwf-input自定义组件,在这章里,我们将会讲到,如何使用CSS自定义令牌更改组件内的样式,以及它的替代方式::part等等相关知识点。感兴趣的同学,千万别走开哦~

表单元素

没错,我们的自定义组件zwf-input可以像表单元素那样使用

<label id="label">名称:</label>
<zwf-input name="leaves" aria-labelledby="label"></zwf-input>
  • 添加name属性,类似原生的表单元素,在表单提交时,会将多个name值自动组成数组提交
  • 使用aria-labelledby="label"id="label",将zwf-inputlabel标签相关联,这样屏幕阅读器在读到aria-labelledby会自动得知该input的标签意思

如何外部更改自定义组件的样式

在上一章里,我们知道zwf-input是在沙盒内部里,由于沙箱的内部独立性,那我们在外面如何更改它的内部样式呢?

CSS Custom Properties(CSS自定义属性,即令牌token)

zwf-input组件内,新增如下样式代码:

static styles = css`
    label,
    input {
        color: var(--q-primary, var(--q-amber700, #d53600));
        font-family: var(--q-input-font-family, 'Boogaloo');
    }
    label {
        font-size: var(--q-input-label-font-size, 16px);
    }
    input {
        font-size: var(--q-input-font-size, 24px);
    }
`

varCSS的函数,可以用它自定义一些样式变量,它的基本语法如下所示:

var(custom-property-name, value)
  • custom-property-name: 必需。自定义属性的名称,必需以 -- 开头
  • value:可选。备用值,在属性不存在的时候使用。

在引入zwf-input组件的App.vue新增如下代码:

<template>
<zwf-input class="test" value="123" label="ceshi" aria-labelledby="label"></zwf-input>
</template>
<style>
zwf-input {
    --q-primary: #d50201;
    --q-input-font-family: 'Comic Sans';
    --q-input-font-size: 18px;
    --q-input-label-font-size: 16px;
}
</style>

网页效果运行如下所示:

3e0089a2362a419bbe312f58c79db234.png

4a7e3dcc2fa248fb92eb3d83068675b2.png

非常棒,对吧?除了CSS Custom Properties,还有哪个方法可以更改组件的内部样式呢?

::part

我们可以使用CSS::part()API,来公开指定的shadow dom元素,它的用法与slot类似,特点如下所示:

  • 暴露部分碎片,供外面更改样式
  • 适用于:一次性的自定义样式,和少数自定义属性
  • 较难维护设计的一致性原则

zwf-input组件内,更改的代码如下所示:

<label id="label" part="label">${this.label}</label>

添加了part="label"属性,将label标签暴露给外面,供其更改样式

App.vue新增如下样式代码:

zwf-input::part(label) {
    color: red;
    font-size: 2rem;
    font-weight: bold;
}

网页运行效果如下所示:

126e3907287143db9d97e91ca4614141.png

如何自动切换为深色主题(dark mode)

我们可以使用@media(prefers-color-scheme: dark)自动识别电脑的深色主题,从而实现主题样式的自动切换

如何开启win 10电脑的深色主题

在桌面,右键选择个性化,如下图所示:

a17cad4e2b3c4215a0fd0ea32352e109.png

在面板内,选择深色,如下图所示:

5094c96855054a86b05cf42e1f1cffb4.png

zwf-input组件内新增如下的样式代码

为了更好地体现本小节的代码效果,记住屏蔽之前的样式代码

@media(prefers-color-scheme: dark) {
    :root {
      --q-primary: var(--q-amber200, green);
    }
}

我们之所以使用:root选择器,是因为与HTML 标签选择器相比,它不是那么具体化,这是为了防止客户覆盖我们的初始定义

页面运行效果如下图所示(为了更好地展示效果,我特意将图片做了放大化处理):

14954bb400a8412db9a994db4d4f8552.png

很神奇,是不是?

CSS Custom Properties的问题

使用CSS Custom Properties自定义样式变量,虽然很不错,但是它也存在如下的两个问题:

  • 无法确定变量值的有效性(用户编写的变量值有可能是错误的)
  • 在回调函数内,很难保持其初始值

如何解决上述问题呢?Registered Properties应运而生

Registered Properties

它的代码格式如下所示:

@property --design-token {
    syntax: '<color>';
    inherits: true;
    initialValue: 'goldenrod'
}

使用@property自定义了一个--design-token变量

  • syntax:可以设置为许多类型,如链接、数字、URL、图像,还有颜色,使自定义属性和原生用户代理属性类似,如果用户提供的值无效,则浏览器会自动忽略它
  • inherits:为true的话,则表示我们希望在父元素设置一次之后,所有的子元素都能继承它;若为false,则表示我们不希望用户改变该令牌的值,所以浏览器无需额外计算该继承关系(浏览器性能将得到提升

zwf-input组件内,更改的代码如下所示:

    static styles = css`
        @property --q-amber700 {
            syntax: '<color>',
            inherits: false,
            initial-value: '#d53600'
        }
        
        @property --q-primary {
            syntax: '<color>',
            inherits: true
        }
        
        :root {
            --q-primary: var(--q-amber700);
        }

        @property --q-amber200 {
            syntax: '<color>',
            inherits: false,
            initial-value: '#ff8503'
        }
        @media(prefers-color-scheme: dark) {
            :root {
              --q-primary: var(--q-amber200);
            }
        }
 
        label,
        input {
            color: var(--q-primary);
            font-family: var(--q-input-font-family);
        }
        label {
            font-size: var(--q-input-label-font-size);
        }
        input {
            font-size: var(--q-input-font-size);
        }

        
        slot[name="icon"]::slotted(*) {
            display: none;
        }
        slot[name="icon"]::slotted(svg) {
            display: inline-flex;
            width: 36px;
            height: 36px;
        }
    `

结构与之前的相比,是不是清晰了很多?

App.vue内部的样式代码不变,如下所示:

zwf-input {
    --q-primary: #d50201;
    --q-input-font-family: 'Comic Sans';
    --q-input-font-size: 18px;
    --q-input-label-font-size: 16px;
}

页面运行效果如下所示:

d367616ec3ac4c238a8a1901795de279.png

尾声

在本节课内,我们学习了CSS custom properties::part等知识点,如果大家对该系列文章感兴趣的话,可以私聊我,或关注我,和大家一起学习进步,一直是件令人高兴的事~~~

感谢大家的关注,欢迎在评论区留言