1.组件的注意点

1.template只能有一个根元素
约束:.vue文件中的template中如果写了两个元素,则会报如下错误

**解决:**保证template中只有一个根元素即可

2.scoped解决样式冲突
1全局样式: 默认组件中的样式会作用到全局,任何一个组件中都会受到此样式的影响
2局部样式: 可以给组件加上scoped 属性,可以让样式只作用于当前组件
默认情况 :写在组件中的样式会 全局生效 → 因此很容易造成多个组件之间的样式冲突问题。

**解决:**在组件style标签上增加scoped来解决

scoped原理
1当前组件内标签都被添加data-v-hash值 的属性 ,每个组件的hash值是不同的
2css选择器都被添加 [data-v-hash值 ] 的属性选择器
最终效果: 必须是当前组件的元素, 才会有这个自定义属性, 才会被这个样式作用到


3.data必须是一个函数
一个.vue组件的 data 选项必须是一个函数。否则会报错

这么要求的原因: 保证每个组件实例,维护独立的一份数据对象,保证组件实例之间的数据相互隔离不受影响
每次创建新的组件实例,都会新执行一次data 函数,得到一个新对象。

2.组件通信
1.父子之间通信
1.用法
父向子传值步骤:
- 准备一个父组件 App.vue,一个子组件Son.vue
- 在App.vue中使用Son.vue让它们构成一个父子组件关系,在使用子组件的同时
-
- 通过
:自定义名字="需要传递的值"
将父组件中的数据传给子组件
- 通过
- 子组件内部通过props接收
props:['父组件中自定义名字']
- 子组件内部模板中直接使用 props接收的值
{``{ 父组件中自定义名字 }}

✨✨ 注意点:父组件中的响应式数据改变,会自动同步到子组件
子组件利用 $emit 将自己的数据传递给父组件
子向父传值步骤:
- $emit触发事件,给父组件发送消息通知
- 父组件监听$emit触发的事件
- 提供处理函数,在函数的形参中获取传过来的参数

注意:上面代码其实是一个 父子通信的双向数据绑定
2.props校验
**思考:**组件的props数据类型可以乱传吗?不能
- 比如进度条百分数只能是数字

**props校验:**为组件的 prop 指定验证要求,不符合要求,控制台就会有错误提示 → 帮助开发者,快速发现错误
props校验的类型:
- 类型校验**(常用)**
- 非空校验
- 默认值
- 自定义校验
语法:
① 只校验类型【常用】

② 完整写法

// ✨✨✨注意点:属性的类型必须使用大写 Number,Boolean,String,Array,Object
- default和required一般不同时写(因为当时必填项时,肯定是有值的)
- default后面如果是简单类型的值,可以直接写默认。如果是复杂类型的值,则需要以函数的形式return一个默认值
3.props与data
**共同点:**一个组件中props和data,都可以给组件提供数据
区别:
- data 的数据是自己的 → 随便改
- prop 的数据是外部的 → 不能直接改,要遵循 单向数据流
**单向数据流:**父级props 的数据更新,会向下流动,影响子组件。这个数据流动是单向的
**特点:**子组件修改prop数据不会影响到父组件的数据
**约定:**谁的数据,谁负责修改

2.跨层级通信
1.事件总线-EventBus(传递事件)
**作用:**事件总线event bus可以用在非父子组件之间,进行简易消息传递 (复杂场景→ Vuex)


**需求:**B组件向C组件进行传递数据
使用步骤:(媒婆传话)
- 创建一个都能访问到的事件总线 (空 Vue 实例) → utils/EventBus.js

- C 组件(接收方),监听 Bus 实例的事件

- B 组件(发送方),触发 Bus 实例的事件

2.provide与inject(传递数据)
作用: provide&inject可以实现组件的跨层级共享数据


provide&inject传递数据使用步骤:
- 父组件 provide 提供数据

- 子/孙组件 inject 取值使用

注意:
- provide提供的简单类型的数据不是响应式的,复杂类型数据是响应式。(推荐提供复杂类型数据)
- 子/孙组件通过inject获取的数据,不能在自身组件内修改
3.v-model原理
思考: 默认情况下我们可以给组件用上 v-model进行双向数据绑定吗? 不能 -> 那么如何做?

解决方法:
- 用vue实现 v-model 的写法(原理)
- 用v-bind:属性.sync 修饰符
1.v-model
v-model原理: v-model本质上是一个语法糖 -> 应用在输入框 上,就是value属性 和 input事件 的合写


**作用:**v-model是双向数据绑定
① 数据变,视图跟着变 :value
② 视图变,数据跟着变 @input
说明:event 用于在模板中,获取事件的形参 (这里的event就是事件对象e)
说明:
不同的表单元素, v-model在底层的处理机制不一样。
- 给文本框,文本域 -> Vue框架底层是拆解成
value属性 + input事件
来实现 - 下拉框 -> Vue框架底层是拆解成
value属性 + change事件
来实现 - 给复选框,单选框 使用v-model ->Vue框架底层是拆解成
checked属性和change事件
来实现

javascript
<template>
<div id="main">
<input type="text" v-model="value1" />
{{ value1 }}
<hr />
<input type="text" :value="value2" @input="value2 = $event.target.value" />
{{ value2 }}
<hr />
<MyTable v-model="value3"></MyTable>
</div>
</template>
<script>
import MyTable from "./components/MyTable.vue";
export default {
components: {
MyTable,
},
data() {
return {
value1: "",
value2: "",
value3: "",
};
},
};
</script>
<style>
</style>
javascript
<template>
<div class="box">
<input type="text" @input="change" />
{{ value }}
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
},
},
methods: {
change(e) {
this.$emit("input", e.target.value);
},
},
};
</script>
<style scoped>
.box {
width: 200px;
height: 200px;
border: 1px solid pink;
}
</style>

2.sync修饰符
**作用:**可以实现 子组件 与 父组件数据 的 双向绑定
.sync修饰符原理: :属性名 和 @update:属性名 的合写

本质上和v-model是一样的,也是一个父子组件通信的情况
3.ref与$refs
**作用:**利用 ref 和 $refs 可以用于 获取 dom 元素, 或 组件实例



**ref和$refs特点:**查找范围 → 当前组件内 (更精确稳定)
语法:

4.Vue异步更新 & $nextTick
Vue的异步更新特性
javascript
<template>
<div class="app">
<span ref="span">{{ num }}</span>
<button @click="addone">+1</button>
</div>
</template>
<script>
export default {
data() {
return {
num: 1,
};
},
methods: {
addone() {
this.num++;
this.num++;
console.log(this.num);//打印3
console.log(this.$refs.span.innerHTML) //❌还是拿到上一次的值1
},
},
};
</script>
<style>
</style>
Vue 在更新 DOM 时是异步执行的(异步渲染)
- 只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更
- 如果同一个 watcher 被多次触发,只会被推入到队列中一次。
- 在下一个的事件循环中(nextTick),Vue 刷新队列并执行实际 (已去重的) 任务(先进先执行)

上面代码中,当num的值改变时,由于dom更新是异步的,所以通过
this.$refs.span.innerHTML拿到的结果是不准确的。
如何解决?使用 $nextTick
$nextTick
**$nextTick:**等 DOM 更新后, 才会触发执行此方法里的函数体
语法:
- 回调函数写法: this.$nextTick(()=>{ })
- Promise写法: this.$nextTick().then(res=>{ })
