attrs的说明
src
--> lib
文件夹下新建文件Button.vue
,components
文件下新建ButtonDemo.vue
内容如下
vue
<script setup lang="ts" name="ButtonDemo">
import Button from '@/lib/Button.vue'
const onClick = () => {
console.log('点击button按钮')
}
</script>
<template>
<Button @click="onClick">按钮</Button>
</template>
Button.vue
内容如下
vue
<template>
<div class="ban-button">
<h1>点击我不触发click事件</h1>
<br>
<button>
<slot></slot>
</button>
</div>
</template>
问题:给Button组件注册click事件,点击h1也会触发click
默认情况下,父组件传递的,但没有被子组件解析为 props 的 attributes 绑定会被"透传"。这意味着当我们有一个单根节点的子组件时,这些绑定会被作为一个常规的 HTML attribute 应用在子组件的根节点元素上。当你编写的组件想要在一个目标元素或其他组件外面包一层时,可能并不期望这样的行为。我们可以通过设置 inheritAttrs
为 false
来禁用这个默认行为。这些 attributes 可以通过 $attrs
这个实例属性来访问($attrs继承所有属性),并且可以通过 v-bind
来显式绑定在一个非根节点的元素上。
vue
<script setup lang="ts">
defineOptions({ inheritAttrs: false })
</script>
<template>
<button v-bind="$attrs">
<slot></slot>
</button>
</template>
因为Button.vue
组件只有button
所以这里我们的代码不需要defineOptions
和$attrs
,这里仅作为知识点
主题theme
ButtonDemo.vue
代码
vue
<h1>示例1</h1>
<div>
<Button>你好</Button>
<Button theme="button">你好</Button>
<Button theme="link">你好</Button>
<Button theme="text">你好</Button>
</div>
Button.vue
代码
vue
<script setup lang="ts">
const props = defineProps({
theme: {
type: String,
default: 'button'
}
})
const { theme } = props
const classes = computed(() => {
return {
[`ran-theme-${theme}`]: theme
}
})
</script>
<template>
<button v-bind="$attrs" class="ran-button" :class="classes">
<slot></slot>
</button>
</template>
Button.vue
样式
scss
<style lang="scss">
$h: 32px;
$border-color: #d9d9d9;
$color: #333;
$blue: #40a9ff;
$radius: 4px;
$red: red;
$grey: grey;
.ran-button {
box-sizing: border-box;
height: $h;
padding: 0 12px;
cursor: pointer;
display: inline-flex;
justify-content: center;
align-items: center;
white-space: nowrap;
background: white;
color: $color;
border: 1px solid $border-color;
border-radius: $radius;
box-shadow: 0 1px 0 fade-out(black, 0.95);
transition: background 250ms;
& + & {
margin-left: 8px;
}
&:hover,
&:focus {
color: $blue;
border-color: $blue;
}
&:focus {
outline: none;
}
&::-moz-focus-inner {
border: 0;
}
&.ran-theme-link {
border-color: transparent;
box-shadow: none;
color: $blue;
&:hover,
&:focus {
color: lighten($blue, 10%);
}
}
&.ran-theme-text {
border-color: transparent;
box-shadow: none;
color: inherit;
&:hover,
&:focus {
background: darken(white, 5%);
}
}
}
</style>
知识点:darken lighten fade_out
lighten()和darken()两个函数都是围绕颜色的亮度值做调整的,其中lighten()函数会让颜色变得更亮,与之相反的darken()函数会让颜色变得更暗。这个亮度值可以是0~1之间,所有的颜色都可以使用不仅仅是background
scss
&:hover,
&:focus {
background: darken($blue, 10%);
border-color: darken($blue, 10%);
}
scss也有两个调节颜色的
fade-in() | 降低颜色的透明度,取值在 0-1 之。 |
---|---|
fade-out() | 提升颜色的透明度,取值在 0-1 之间。 |
fade_out
background-color: fade-out(black, 0.5);
效果是0.1是不透明,1是全透明,0.5是黑色半透明
fade_in
没有get到用法
尺寸size
ButtonDemo.vue
新增代码
vue
<h1>示例2</h1>
<br>
<div>
<div>
<Button size="big">大大大</Button>
<Button>普普通</Button>
<Button size="small">小小小</Button>
</div>
<div>
<Button theme="link" size="big">大大大</Button>
<Button theme="link">普普通</Button>
<Button size="small" theme="link">小小小</Button>
</div>
<div>
<Button size="big" theme="text">大大大</Button>
<Button theme="text">普普通</Button>
<Button size="small" theme="text">小小小</Button>
</div>
</div>
Button.vue
代码
vue
<script setup lang="ts">
const props = defineProps({
theme: {
type: String,
default: 'button'
},
size: {
type: String,
default: 'normal'
}
})
const { theme, size } = props
const classes = computed(() => {
return {
[`ran-theme-${theme}`]: theme,
[`ran-size-${size}`]: size
}
})
</script>
Button.vue
新增样式
scss
.ran-button {
&.ran-size-big {
font-size: 24px;
height: 48px;
padding: 0 16px;
}
&.ran-size-small {
font-size: 12px;
height: 20px;
padding: 0 4px;
}
}
类型level
ButtonDemo.vue
新增代码
vue
<h1>示例3</h1>
<br>
<div>
<div>
<Button level="main">主要按钮</Button>
<Button>普通按钮</Button>
<Button level="danger">危险按钮</Button>
</div>
<div>
<Button theme="link" level="main">主要链接按钮</Button>
<Button theme="link">普通链接按钮</Button>
<Button theme="link" level="danger">危险链接按钮</Button>
</div>
<div>
<Button theme="text" level="main">主要文字按钮</Button>
<Button theme="text">普通文字按钮</Button>
<Button theme="text" level="danger">危险文字按钮</Button>
</div>
</div>
Button.vue
代码
vue
<script setup lang="ts">
const props = defineProps({
theme: {
type: String,
default: 'button'
},
size: {
type: String,
default: 'normal'
},
level: {
type: String,
default: 'normal'
}
})
const { theme, size, level } = props
const classes = computed(() => {
return {
[`ran-theme-${theme}`]: theme,
[`ran-size-${size}`]: size,
[`ran-level-${level}`]: level
}
})
</script>
Button.vue
新增样式
scss
.ran-button {
&.ran-theme-button {
&.ran-level-main {
background: $blue;
color: white;
border-color: $blue;
&:hover,
&:focus {
background: darken($blue, 10%);
border-color: darken($blue, 10%);
}
}
&.ran-level-danger {
background: $red;
border-color: $red;
color: white;
&:hover,
&:focus {
background: darken($red, 10%);
border-color: darken($red, 10%);
}
}
}
&.ran-theme-link {
&.ran-level-danger {
color: $red;
&:hover,
&:focus {
color: darken($red, 10%);
}
}
}
&.ran-theme-text {
&.ran-level-main {
color: $blue;
&:hover,
&:focus {
color: darken($blue, 10%);
}
}
&.ran-level-danger {
color: $red;
&:hover,
&:focus {
color: darken($red, 10%);
}
}
}
}
支持disabled
ButtonDemo.vue
新增代码
vue
<h1>示例4</h1>
<br>
<div>
<Button disabled>禁用按钮</Button>
<Button theme="link" disabled>禁用链接按钮</Button>
<Button theme="text" disabled>禁用按钮</Button>
</div>
Button.vue
代码
vue
<script setup lang="ts">
const props = defineProps({
theme: {
type: String,
default: 'button'
},
size: {
type: String,
default: 'normal'
},
level: {
type: String,
default: 'normal'
},
disabled: {
type: Boolean,
default: false
}
})
const { theme, size, level } = props
const classes = computed(() => {
return {
[`ran-theme-${theme}`]: theme,
[`ran-size-${size}`]: size,
[`ran-level-${level}`]: level
}
})
</script>
<template>
<button class="ran-button" :class="classes" :disabled="disabled">
<slot></slot>
</button>
</template>
Button.vue
新增样式
scss
.ran-button {
&.ran-theme-button {
&[disabled] {
cursor: not-allowed;
color: $grey;
&:hover {
border-color: $grey;
}
}
}
&.ran-theme-link, &.ran-theme-text {
&[disabled] {
cursor: not-allowed;
color: $grey;
}
}
}
支持loading
ButtonDemo.vue
新增代码
vue
<h1>示例5</h1>
<br>
<div>
<Button loading>加载中</Button>
<Button>加载完毕</Button>
</div>
Button.vue
代码
vue
<script setup lang="ts">
const props = defineProps({
theme: {
type: String,
default: 'button'
},
size: {
type: String,
default: 'normal'
},
level: {
type: String,
default: 'normal'
},
disabled: {
type: Boolean,
default: false
},
loading: {
type: Boolean,
default: false
}
})
const { theme, size, level } = props
const classes = computed(() => {
return {
[`ran-theme-${theme}`]: theme,
[`ran-size-${size}`]: size,
[`ran-level-${level}`]: level
}
})
</script>
<template>
<button class="ran-button" :class="classes" :disabled="disabled">
<span v-if="loading" class="ran-loadingIndicator"></span>
<slot></slot>
</button>
</template>
Button.vue
新增样式
scss
.ran-button {
> .ran-loadingIndicator{
width: 14px;
height: 14px;
display: inline-block;
margin-right: 4px;
border-radius: 8px;
border-color: $blue $blue $blue transparent;
border-style: solid;
border-width: 2px;
animation: ran-spin 1s infinite linear;
}
}
@keyframes ran-spin {
0%{transform: rotate(0deg)}
100%{transform: rotate(360deg)}
}
组件库中的CSS
不能使用scoped
因为data-v-xxx中的xxx每次运行可能不同
必须输出稳定不变的class选择器,方便使用者覆盖(避免使用者使用v:deep)
必须加前缀
所有组件库的class类必须加上组件前缀,比如.button
变成.ran-button
css最小影响原则
css绝不能影响库的使用者,那怎么才能不影响使用者呢?
global.scss
这个是main.ts
引入的,不能保证初始化样式别人也会引入怎么办?
写一个random.scss
,这个要告诉使用者,一定要引入这个scss
,然后再引入自己的公共样式,那这样的话,不管使用者的样式怎么样,我们的样式都不会受到影响!
src
-> lib
新建文件random.scss
scss
[class^="ran-"], [class*=" ran-"] {
margin: 0;
padding: 0;
box-sizing: border-box;
font-size: 16px;
// 为什么这样写 font-family
// 答案见 https://github.com/zenozeng/fonts.css/
font-family: -apple-system, "Noto Sans", "Helvetica Neue", Helvetica,
"Nimbus Sans L", Arial, "Liberation Sans", "PingFang SC", "Hiragino Sans GB",
"Noto Sans CJK SC", "Source Han Sans SC", "Source Han Sans CN",
"Microsoft YaHei", "Wenquanyi Micro Hei", "WenQuanYi Zen Hei", "ST Heiti",
SimHei, "WenQuanYi Zen Hei Sharp", sans-serif;
}
main.ts
中引入random.scss
typescript
import { createApp } from 'vue'
import App from './App.vue'
import '@/lib/random.scss'
import '@/assets/styles/global.scss'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
[class^="b-"], [class*=" b-"]
的意思是以b-开头的class和包含空格 b-的class类
知识点:[ ^$*~= ]
[attribute] 用于选取带有指定属性的元素。
[attribute=value] 用于选取带有指定属性和值的元素。
[attribute~=value] 用于选取属性值中包含指定词汇的元素。
[attribute|=value] 用于选取带有以指定值开头的属性值的元素,该值必须是整个单词。
[attribute^=value] 匹配属性值以指定值开头的每个元素。
[attribute$=value] 匹配属性值以指定值结尾的每个元素。
[attribute*=value] 匹配属性值中包含 指定值的每个元素。
下面的例子为包含指定值的 title 属性的所有元素设置样式。适用于由空格分隔的属性值: [title~=hello] { color:red; }
下面的例子为带有包含指定值的 lang 属性的所有元素设置样式。适用于由连字符分隔的属性值: [lang|=en] { color:red; }
~=
其中的value
必须是一个独立的单词,例如 [title~=test]
中 test a
可以被选中testa
不能被选中. *=
其中的value
只要是值的子串就可以,例如test-a
,test a
和testa
均可以被选中. |=
与~=
的特性一样,^=
与*=
的特性一样.因此平时还是用^=
和*=
较好