默认情况下:写在组件的样式会 全局生效 -> 因此很容易造成多个组件之间的样式冲突问题。
推荐加上scoped属性,让组件拥有独立的样式。
data-v-hash 利用hash的值[data-v-5f6a9d56]来区分不同组件
div[data-v-5f6a9d56]
最终效果:必须是当前组件的元素,才会有这个自定义属性,才会被这个样式作用到。
data () {
return {
count: 100
}
}
{{ count }}
每次初始化组件实例的时候,就会调用一次data函数,得到一个独立的对象,不会印象到别人的数据。
什么是组件通讯呢?
父子关系
父传子props 和 子传父$emit
非父子关系
// 准备好数据
data () {
return {
myTitle: 'Veu'
}
},
// 2、通过props进行接收传递过来的属性
props: ['title']
//使用
{{ title }}
methods: {
changeFn() {
// 1、通过$emit,向父组件发送消息通知
this.$emit('changeTitle','子组件')
}
methods: {
handleChange (newTitle) {
// 3、提供处理函数,提供逻辑
this.myTitle = newTitle
}
},
答:prop是组件上注册的一些 自定义属性
1.导入注册并准备数据在App.vue中
import UserInfo from './components/UserInfo.vue'
export default {
// 数据由父组件data函数提供
data () {
return {
username: '小帅',
age: 28,
isSingle: true,
car: {
brand: '宝马',
},
hobby: ['篮球','足球','羽毛球']
}
},
components: {
UserInfo
}
}
2.给组件注册自定义属性
3.利用props父传子通信数据
props: ['username','age','isSingle','car','hobby']
4.子组件使用模板语法进行使用
我是个人信息组件
姓名:{{ username }}
年纪:{{ age }}
是否单身:{{ isSingle? '是' : '否'}}
座驾:{{ car.brand}}
兴趣爱好:{{ hobby.join('、') }}
作用:为组件的prop指定验证要求,不符合要求的,控制台会有错误显示, 帮助开发者,快速发现错误。
语法
写法:需要将原来的写法(数组),改为对象的写法。
props: {
}
// props: ['w']
props: {
w: Number //Number String Boolean Array Object
}
}
类型、非空、默认值、自定义
// 完整写法
props: {
w: {
// 1、类型判断
type: Number,
// 2、非空判断
required: true,
// 3、默认值(没有值的时候)
default: 0,
// 4、自定义判断
validator (value) {
// console.log(value)
if (value >= 0 && value <= 100) {
return true
} else {
console.error('传入的porp w 必须是0~100之间的数字')
return false
}
}
}
}
共同点: 都可以给组件提供数据
区别:
1.自己的数据自己改(谁的数据谁负责)
2.prop传过来的数据(外部的数据),不能直接改
App.vue
单向数据流:父组件的prop更新;会单项的向下流动,影响到子组件数据
口诀:谁的数据谁负责
渲染功能
提供数据 → 提供在公共的父组件 App.vue中
通过父传子,将数据传递给 TodoMain
利用 v-for 渲染
添加功能
收集表单数据 → v-model
监听事件 (回车 + 点击 都要进行添加)
子传父 ,将任务名称传递给父组件 App.vue
进行添加, unshift (自己的数据自己负责)
删除功能
监听事件 (监听点击事件) 携带 id
子传父, 将删除的 id 传递给父组件 App.vue
进行删除 filter (自己的数据自己负责)
底部合计
清空功能
注册监听点击事件
子传父 → $emit → list = []
持久化存储
提供数据 → 提供在公共的父组件 App.vue中
通过父传子,将数据传递给 TodoMain
利用 v-for 渲染
1.提供数据 → 提供在公共的父组件 App.vue中
list: JSON.parse(localStorage.getItem('list')) || [
{ id: 1, name: "打篮球" },
{ id: 2, name: "陪女朋友过七夕" },
{ id: 3, name: "看电影逛街" },
],
2.通过父传子,将数据传递给 TodoMain
// 利用组件通信 父传子传递数据
props: {
list: Array
},
3.利用v-for渲染
{{index + 1}}.
收集表单数据 → v-model
监听事件 (回车 + 点击 都要进行添加)
子传父 ,将任务名称传递给父组件 App.vue
进行添加, unshift (自己的数据自己负责)
1.收集表单数据 → v-model
2.监听事件 (回车 + 点击 都要进行添加)
3.子传父 ,将任务名称传递给父组件 App.vue
methods: {
handleAdd() {
if (this.todoTask.trim() === '') {
alert('输入内容不能为空')
return
}
// 数据是App.vue的,需要通知他,让他该
this.$emit("addTask", this.todoTask);
// 清空数据框内容
this.todoTask = "";
},
},
4.进行添加, unshift (自己的数据自己负责)
handleAdd(newValue) {
// 对list数据添加(list是App.vue的)
this.list.unshift({ id: +new Date(), name: newValue });
},
监听事件 (监听点击事件) 携带 id
子传父, 将删除的 id 传递给父组件 App.vue
进行删除 filter (自己的数据自己负责)
1.监听事件 (监听点击事件) 携带 id
2.子传父, 将删除的 id 传递给父组件 App.vue
methods: {
todoDel (id) {
// 2. 子传父, 将删除的 id 传递给父组件 App.vue
this.$emit('changeDel', id)
}
}
3.进行删除 filter (自己的数据自己负责)
delTask (id) {
// 3. 进行删除 filter (自己的数据自己负责)
this.list = this.list.filter(item => item.id !== id) //与子组件传递过来的id进行匹配过滤
},
1.父传子传list → 渲染
合 计: {{ list.length }}
注册监听点击事件
子传父 → $emit → list = []
1.注册监听点击事件
2.子传父 → $emit → list = []
methods: {
removeTask () {
// 2. 子传父 → $emit → list = []
this.$emit('removeTask')
}
}
3.父组件中处理
removeList () {
// 监听子组件的通知后,执行删除功能
this.list = []
}
watch: {
list: {
deep: true,
handler (newValue) {
// 将监视的list的数据存入到本地
localStorage.setItem('list', JSON.stringify(newValue))
}
}
}
// 逻辑或终端,本地有 → 用本地的 没有 → 用list数组默认数据
list: JSON.parse(localStorage.getItem('list')) || [
{ id: 1, name: "打篮球" },
{ id: 2, name: "陪女朋友过七夕" },
{ id: 3, name: "看电影逛街" },
],
作用;非父子组件之间,进行简易消息传递,(复杂场景 → Vuex)
一对多的关系(一个组件发送,只要监听了的都可以访问到)
使用方法:
1.创建一个都能访问到的事件总线 (空Vue实例) → utils /EventBus.js
// 1.创建爱一个都能访问到的事件总线(空的 Vue 实例)
import Vue from 'vue'
const Bus = new Vue()
export default Bus
2.接收方,监听Bus实例的事件
3.发送方,触发 Bus实例的事件
作用是跨层级共享数据
provide共享的数据
通常使用就是使用包对象的形式(复杂数据类型)共享
共享 1.父组件 provide 提供数据
原理:v-model本质上就是一个语法糖。例如应用在输入框上,就是value属性 和 input事件的合写。
子组件不能直接是有个v-model访问父组件的数据的,因为没有权限,需要通过组件通信 + value 和 事件来解决。
**作用:**实现数据双向绑定
注意:$event 用于模板中,获取事件的形参
父组件
子组件
这里使用了 :value 和 @change事件 实现子组件和父组件双向绑定
父组件 v-model 简化代码,实现 子组件 和 父组件 数据双向绑定
前提条件:value接收 input事件触发
父传子 使用 value 传值
props: {
value: String
},
子传父 使用 input 事件触发
methods: {
handleChaneg (e) {
this.$emit('input',e.target.value)
}
}
这样父组件中才能使用 v-model
**作用:**可以实现 子组件 与 父组件数据的 双向绑定,简化代码
特点: prop属性名 ,可以自定义。非固定的value
场景:封装弹框类的基础组件,visible属性 true显示,false隐藏。
本质:就是**:属性名** 和 @ updata:属性名的 合写
父组件
子组件
props: {
//visible就是父组件传过来的属性名
visible: Boolean
}
methods: {
close () {
//通知父组件控制显示和隐藏
this.$emit('updata:visible',fasle)
这里的事件名在父组件中使用了sync修饰符,就相当于省略了事件监听。
}
}
作用:利用 ref 和 refs可以用于获取dom元素,或组件实例
特点:查找范围 → 当前组件内(更精确稳定)
① 目标标签 - 添加 ref属性
我是渲染图标的容器
②恰当时机,通过this.$refs.chartRef,获取目标标签
mounted () {
console.log(this.$refs.chartRef)
}
① 目标标签 - 添加 ref属性
②恰当时机,通过this.$refs.chartRef,获取目标标签
const myChart = echarts.init(this.$refs.mychart);
使用ref 和 $refs,哪怕获取到相同的类名元素,我依旧值局限于当前组件我添加了ref属性的元素,其他的我都不管。
querySelector 查找的范围 → 整个页面(很容易出错)
利用res 和 $refs 获取组件实例
①目标组件 - 添加 ref 属性
②恰当时机,通过this.$refs.xxx,获取目标组件,就可以调用组件对象里面的方法
父组件 App.vue代码
①目标组件 - 添加 ref 属性
②恰当时机,通过this.$refs.xxx,获取目标组件,就可以调用组件对象里面的方法
问题:在Vue中,这样写代码 “显示之后”,立刻获取焦点是不能成功的!
原因:Vue是 异步更新 DOM (提升性能)
解决方发: $nextTick: 等DOM更新后,才会触发执行此方法里面的函数体。
一但DOM加载完毕,立即执行$nextTick方法里面的函数方法