一.组件的基本使用
1.组件的注册
组件之间可以相互引用的
例如:我们想在App中引用 About组件和Home组件
vue组件的引用原则:先注册后使用
2. 注册组件的两种方式
vue中注册组件的方式分为 “全局注册”和“局部注册”两种,其中
- 被全局注册的组件,可以在全局任何一个组件内使用
- 被局部 注册的组件,只能在当前注册的范围内使用
2.1 全局注册组件
- 在main.js导入 组件
- 使用 vue的实例.component("组件名",组件)
- 在想要使用的组件 使用标签名的形式 进行使用这个全局注册的组件
2.2 局部注册组件
在想要使用的组件内的script标签导入组件
在components节点注册
-
像使用标签一样,使用这个组件
import xxx from 'xxx' export default { components:{ 键:值 } }
2.3 全局组件和局部组件的区别
- 全局组件:可以在全局的任何一个组件内使用
- 局部组件:只可以当前注册的范围内使用
应用场景:
如果某些组件在开发期间使用的频率很高,推荐进行全局注册:
如果某些组件只在特定的情况下会被使用到,推荐进行局部注册
2.4 组件注册时名称的大小写
在进行组件的注册时,定义组件注册名称的方式有两种:
- kebab-case 命名法(俗称 短横线命名法,例如 my-swiper、my-test)
- PascalCase (俗称 帕斯卡命名法 或大驼峰命名法 ,例如 MySwiper、MyTest)
对于短横线命名来说:
- 必须严格按照短横线名称进行使用
对于帕斯卡命名来说
- 既可以严格按照帕斯卡名称进行使用,也可以转化为短横线名称进行使用
在实际的开发中,推荐使用帕斯卡命名法,因为它的适用性更好
2.5 通过name属性注册组件
在注册组件期间,除了可以直接提供组件的注册名称以外,还可以把组件的name属性作为注册后组件的名称
2.6 样式冲突——了解导致组件之间样式冲突的原因
默认情况下,写在.vue组件中样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。导致组件之间样式冲突的根本原因是:
- 在单页面应用程序中,所有组件的Dom结构,都是基于唯一的index.html页面进行呈现的。
- 每个组件中的样式,都会影响整个index.html页面中的DOM元素
2.7 思考:如何解决组件样式冲突的问题
2.7.1 添加自定义属性
为每个组件分配唯一的自定义属性,在编写组件样式时,通过属性选择器来控制样式的作用域,示例代码如下:
App.vue
<template>
<div data-v-001>
<h1 data-v-001>这是App.vue组件</h1>
<p data-v-001>App中的p标签</p>
<p data-v-001>App中的p标签</p>
<hr />
<MyList data-v-001></MyList>
</div>
</template>
<script>
import MyList from "./MyList.vue";
export default {
components: {
MyList,
},
};
</script>
<style lang="less" >
p[data-v-001] {
color: red;
}
</style>
MyList.vue
<template>
<div data-v-002>
<h1 data-v-002>这是List.vue组件</h1>
<p data-v-002>List中的p标签</p>
<p data-v-002>List中的p标签</p>
</div>
</template>
<script>
export default {
name: "MyList",
};
</script>
<style lang="less" scoped>
</style>
2.7.2 在style节点下添加scoped属性
为了提高开发效率和开发体验,Vue为style节点提供了scoped属性,从而防止组件之间的样式冲突问题:
App.vue
<template>
<div>
<h1>这是App.vue组件</h1>
<p>App中的p标签</p>
<p>App中的p标签</p>
<hr />
<MyList></MyList>
</div>
</template>
<script>
import MyList from "./MyList.vue";
export default {
components: {
MyList,
},
};
</script>
<style lang="less" scoped>
p {
color: red;
}
</style>
MyList.vue
<template>
<div>
<h1>这是List.vue组件</h1>
<p>List中的p标签</p>
<p>List中的p标签</p>
</div>
</template>
<script>
export default {
name: "MyList",
};
</script>
<style lang="less" scoped>
</style>
2.8 /deep/样式穿透
如果给当前组件 的style节点添加了 scoped属性,则当前组件的样式不会对其子组件是不生效的。如果想让某些样式对子组件生效 ,可以使用 /deep/深度选择器。
App.vue
<template>
<div>
<h1>这是App.vue组件</h1>
<p>App中的p标签</p>
<p>App中的p标签</p>
<hr />
<MyList></MyList>
</div>
</template>
<script>
import MyList from "./MyList.vue";
export default {
components: {
MyList,
},
};
</script>
<style lang="less" scoped>
.title {
color: blue; //未加/deep/前,实际样式选择器是这样的 .title[data-v-417c4fe3]
}
</style>
MyList.vue
<template>
<div>
<h1 class="title">这是List.vue组件</h1>
<p>List中的p标签</p>
<p>List中的p标签</p>
</div>
</template>
<script>
export default {
name: "MyList",
};
</script>
<style lang="less" scoped>
</style>
2.8.1 使用/deep/后
App.vue
<template>
<div>
<h1>这是App.vue组件</h1>
<p>App中的p标签</p>
<p>App中的p标签</p>
<hr />
<MyList></MyList>
</div>
</template>
<script>
import MyList from "./MyList.vue";
export default {
components: {
MyList,
},
};
</script>
<style lang="less" scoped>
/deep/ .title {
color: blue; // 加了 /deep/ 后,实际上是这样的 [data-v-417c4fe3] .title
}
</style>
MyList.vue
<template>
<div>
<h1 class="title">这是List.vue组件</h1>
<p>List中的p标签</p>
<p>List中的p标签</p>
</div>
</template>
<script>
export default {
name: "MyList",
};
</script>
<style lang="less" scoped>
</style>
2.8.2 注意点
/deep/是vue2.x中实现样式穿透的方案
在vue3.x中推荐使用:deep() 代替 /deep/
3.组件的props
为了提高组件的复用性,在封装vue组件时需要遵守如下的原则:
- 组件的DOM结构、Style样式要尽量复用
- 组件要展示的数据,尽量由组件的使用者提供
为了方便使用者为组件提供要展示的数据,vue组件提供了props的概念。
3.1 什么是组件的props
props是组件的自定义属性,组件的使用者可以通过props把数据传递到子组件内部,供子组件内部进行使用。
代码示例如下:
props的作用:父组件通过props向子组件传递要展示的数据
props的好处:提高了组件的复用性。
3.2 在组件中声明props
在封装vue组件时,可以把动态的数据项声明为props自定义属性。
自定义属性可以在当前组件的模板结构中被直接使用。示例代码如下:
MyArticle.vue(子组件)
<template>
<div>
<h3>标题:{{ title }}</h3>
<h5>作者:{{ author }}</h5>
</div>
</template>
<script>
export default {
props: ["title", "author"], //父组件传递给子组件的数据,必须在props节点中声明
};
</script>
<style lang="less" scoped>
</style>
App.vue(父组件)
<template>
<div>
<h1>这是App.vue组件</h1>
<hr />
<MyArticle title="黑马程序员" author="黑马"></MyArticle>
</div>
</template>
<script>
import MyArticle from "./MyArticle.vue";
export default {
components: {
MyArticle,
},
};
</script>
<style lang="less" scoped>
</style>
3.3 无法使用未声明的props
如果父组件给子组件传递了未声明的props属性,则这些属性会被忽略,无法被子组件使用,示例代码如下:
3.4 动态绑定props的值
可以使用v-bind属性绑定的形式,为组件动态绑定props的值,示例代码如下:
3.5 props的大小写命名
组件中如果使用 "camelCase(驼峰命名法)"声明了props属性的名称,则有两种方式为其绑定属性的值:
- 直接使用驼峰命名的名称
- 将驼峰命名转化为短横线命名进行使用(大写字母在前面加一个'-'然后大写字母转化为小写)
3.6 动态绑定HTML的class
3.6.1 三元表达式
可以通过三元表达式,动态为元素绑定class的类名。示例代码如下:
3.6.2 以数组语法绑定HTML的class
如果元素需要动态绑定多个class类名,此时可以使用数组的语法格式:
3.6.3 对象语法
使用数组语法绑定class会导致模板结构臃肿问题,此时可以使用对象语法进行简化
3.7 动态绑定内联的style
:style的对象语法十分直观——看起来非常像css,但其实是一个javascript对象。css property名可以用驼峰命名法(camelCase)或短横线命名法(kebab-case,记得用引号括起来)来命名
4.props 验证
4.1 什么是props验证
指的是:在封装组件时,对外界传过来的props数据进行合法性的校验,从而防止数据不合法的问题。
使用数组类型的props节点的缺点:无法为每个prop指定具体的数据类型
4.2 对象类型的props节点
使用对象类型的props节点,可以为每个prop定义具体的数据类型
4.3 props验证
对象类型的props节点提供了多种数据验证方案,例如:
- 基础的类型检查
- 多个可能的类型
- 必填项校验
- 属性默认值
- 自定义验证函数
4.3.1 基础的类型检查
可以直接为组件的prop属性指定具体的基础的类型检查,从而防止组件的使用者为其绑定错误类型的数据
Boolean
Number
String
Array
Function
Object
Symbol
Date
4.3.2 多个可能的类型
如果某个prop属性值的类型不唯一,此时可以通过数组的形式,为其指定多个可能的类型。示例代码如下:
info: [String, Number]
4.3.3 必填项校验
如果某个prop属性是必填项,必须让组件的使用者为其传递属性的值。可以通过如下方式:
- type指定的类型
- required:是否为必填项
4.3.4 默认值
在封装组件时,可以为某个prop属性指定默认值,具体代码如下:
export default{
props:{
propA:{
type:Number,
default:30
}
}
}
- type:类型
- default:默认值
4.3.4 自定义验证函数
在封装组件时,可以为prop属性指定自定义验证函数,从而对prop属性的值进行更加精确的控制
App.vue
<template>
<div>
<h1>App.vue组件</h1>
<hr />
<Count :state="false" :info="1" type="success"></Count>
</div>
</template>
<script>
import Count from "./Count.vue";
export default {
components: {
Count,
},
};
</script>
<style lang="less" scoped>
</style>
Count.vue
<template>
<div>
<p>数量:{{ count }}</p>
<p>状态:{{ state }}</p>
</div>
</template>
<script>
export default {
props: {
count: {
type: Number,
default: 100,
},
state: Boolean,
info: [String, Number],
type: {
validator(value) {
return ["success", "warning", "danger"].indexOf(value) !== -1;
},
},
},
};
</script>
<style lang="less" scoped>
</style>
5.计算属性
5.1 什么是计算属性
计算属性本质上是一个function函数,它可以实时监听data数据的变化,并return一个计算后的新值,供组件渲染DOM时使用
5.2 如何声明计算属性
计算属性需要以function函数的形式声明到组件的computed选项中
- 直接使用名字即可,后面不需要加小括号
- 计算属性侧重于得到一个计算的结果,因此计算属性中必须要有return返回值
示例代码如下:
App.vue
<template>
<div>
<h1>App根组件</h1>
<hr />
<MyCounter></MyCounter>
</div>
</template>
<script>
import MyCounter from "./MyCounter.vue";
export default {
components: {
MyCounter,
},
};
</script>
<style lang="less" scoped>
</style>
MyCounter.vue
<template>
<div>
<input type="text" v-model.number="count" />
<p>{{ count }}乘以2的值为: {{ getCount }}</p>
</div>
</template>
<script>
export default {
name: "MyCounter",
data() {
return {
count: 1,
};
},
computed: {
getCount() {
return this.count * 2;
},
},
};
</script>
<style lang="less" scoped>
</style>
5.3 计算属性的使用注意点
- 计算属性必须定义在computed节点中
- 计算属性必须是一个function函数
- 计算属性必须有返回值
- 计算属性必须当做普通属性使用
5.4 计算属性VS方法
相对于方法来说,计算属性会缓存计算的结果,只有计算属性的依赖项发生了变化,才会重新进行运算。因此,计算属性的性能较好。
5.自定义事件
5.1 什么是自定义事件
在封装组件时,为了让组件的使用者可以监听到组件内状态的变化,此时需要用到组件的自定义事件。
5.2 自定义事件的3个使用步骤
在封装组件时:
①声明自定义事件
②触发自定义事件
在使用组件时:
①监听自定义事件
5.2.1 声明自定义事件
开发者在为自定义组件封装的自定义事件,必须事先在emits节点中声明,示例代码如下:
5.2.2 触发自定义事件
在emits声明的自定义事件,可以通过this.$emit("自定义事件的名称")来触发,示例代码如下:
5.2.3 监听自定义事件
在使用自定义组件时,可以通过v-on来监听自定义事件。示例代码如下:
5.3 自定义事件传参
在调用this.$emit()的第二个参数为自定义事件传参数,示例代码如下:
6.组件上的v-model指令
6.1 为什么需要在组件上使用v-model
v-model是双向数据绑定指令,当需要维护组件内外数据的同步时,可以在组件上使用v-model指令。示意图如下:
- 外界数据的变化会自动同步到counter组件中
- counter组件中数据的变化,也会自动同步到外界
6.2 在组件上使用v-model的步骤
6.2.1 父组件向子组件同步数据
- 父组件通过v-bind属性绑定的形式,把数据传递给子组件
- 子组件中,通过props接收父组件传过来的数据
6.2.2 子组件向父组件同步数据
- 在v-bind指令前添加v-model指令
- 在子组件中声明emits自定义事件,格式为update:xxx (xxx是props某个属性名)
- 调用$emit()触发自定义事件,更新父组件中的数据