Vue-4: 组件通信学习

Vue-4

文章目录

  • Vue-4
      • 01-scoped组件的样式冲突
          • scoped原理
      • 02-data是一个函数
          • 代码
      • 03-组件通信
          • 组件关系分类:
          • 父子关系组件的使用
      • 04-props详解
        • 01-prop
          • 作用
          • 特点
        • 02-props 校验
          • 03-prop & data 单向数据流
      • 05-小黑记事本(组件版)
          • 核心步骤
          • 添加功能
          • 删除功能
          • 底部合计
          • 清空功能
          • 持久化存储
      • 06-非父子通信-(事件总线)
      • 07-非父子通信-provide & inject
          • 代码示例
      • 08-v-model原理
          • 01-表单类组件封装(下拉菜单)
          • 02-父组件 v-model简化代码
      • 09-sync修饰符
      • 10-ref 和 $refs
          • 1.获取dom元素:
          • 2.获取组件实例
      • 11- Vue 异步更新 、$nextTick

01-scoped组件的样式冲突


默认情况下:写在组件的样式会 全局生效 -> 因此很容易造成多个组件之间的样式冲突问题。

  1. 全局样式:默认组件中的样式会作用到全局
  2. 局部样式:可以给组件加上 scoped属性,可以让样式只作用于当前组件。



推荐加上scoped属性,让组件拥有独立的样式。


scoped原理
  1. 给当前的组件模板所有元素,都会被添加上自定义属性 data-v-hash 利用hash的值[data-v-5f6a9d56]来区分不同组件
  2. css选择器后面被自动处理添加上了属性选择器。div[data-v-5f6a9d56]

最终效果:必须是当前组件的元素,才会有这个自定义属性,才会被这个样式作用到。


02-data是一个函数


  1. 一个组件的data选项必须是一个函数。保证每个组件实例,维护独立的一份数据对象。
  2. 每次创建新的组件实例,都会新执行一次 data 函数,得到一个新对象。

data () {

return {

​ count: 100

}

}


代码




每次初始化组件实例的时候,就会调用一次data函数,得到一个独立的对象,不会印象到别人的数据。


03-组件通信


什么是组件通讯呢?

  • 组件通信,就是指 组件与组件之间的数据传递。
  1. 组件的数据是独立的,无法直接访问其他组件的数据。
  2. 想用其他组件的数据 -> 就得使用 组件通信

组件关系分类:
  1. 父子关系

    父传子props 和 子传父$emit

  2. 非父子关系

    1. provide & inject
    2. eventbus
  • 通用(终极)解决方案:Vuex(适合复杂业务场景)

父子关系组件的使用
  1. 父传子props
    // 准备好数据
    data () {
        return {
            myTitle: 'Veu'
        }
    },






// 2、通过props进行接收传递过来的属性
props: ['title']


//使用
{{ title }}

  1. 子传父 $emit

methods: {
  changeFn() {
    // 1、通过$emit,向父组件发送消息通知
    this.$emit('changeTitle','子组件')
  }


methods: {
        handleChange (newTitle) {
            // 3、提供处理函数,提供逻辑
           this.myTitle = newTitle
        }
    },


04-props详解


01-prop

答:prop是组件上注册的一些 自定义属性

作用
  1. 作用: 向子组件传递数据
特点
  1. 可以传递任意数量的 prop
  2. 可以传递 任意类型的 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('、') }}

02-props 校验


作用:为组件的prop指定验证要求,不符合要求的,控制台会有错误显示, 帮助开发者,快速发现错误。

语法

  1. 类型校验
  2. 非空校验
  3. 默认值
  4. 自定义校验

写法:需要将原来的写法(数组),改为对象的写法。

props: {

}


  1. 类型校验(基础写法)
  // props: ['w']
  props: {
    w: Number  //Number String Boolean Array Object
    
  }
}



  1. 完整写法

类型、非空、默认值、自定义

  // 完整写法
  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
        }
        
      }
    }
  }

03-prop & data 单向数据流

共同点: 都可以给组件提供数据

区别:

  1. data 的数据是自己的 -> 随便修改
  2. prop 的数据是外部的 -> 不能直接改,要遵循 单向数据流

1.自己的数据自己改(谁的数据谁负责)

2.prop传过来的数据(外部的数据),不能直接改

App.vue




单向数据流:父组件的prop更新;会单项的向下流动,影响到子组件数据

口诀:谁的数据谁负责


05-小黑记事本(组件版)


核心步骤
  1. 渲染功能

    1. 提供数据 → 提供在公共的父组件 App.vue中

    2. 通过父传子,将数据传递给 TodoMain

    3. 利用 v-for 渲染

    添加功能

    1. 收集表单数据 → v-model

    2. 监听事件 (回车 + 点击 都要进行添加)

    3. 子传父 ,将任务名称传递给父组件 App.vue

    4. 进行添加, unshift (自己的数据自己负责)

    删除功能

    1. 监听事件 (监听点击事件) 携带 id

    2. 子传父, 将删除的 id 传递给父组件 App.vue

    3. 进行删除 filter (自己的数据自己负责)

    底部合计

    1. 父传子传list → 渲染

    清空功能

    1. 注册监听点击事件

    2. 子传父 → $emit → list = []

    持久化存储

    1. watch深度监视list变化 → 往本地存储 → 进入页面优先往本地存储

##### **渲染功能**
  1. 提供数据 → 提供在公共的父组件 App.vue中

  2. 通过父传子,将数据传递给 TodoMain

  3. 利用 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}}.

  • 添加功能
    1. 收集表单数据 → v-model

    2. 监听事件 (回车 + 点击 都要进行添加)

    3. 子传父 ,将任务名称传递给父组件 App.vue

    4. 进行添加, 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 });
        },
    

    删除功能
    1. 监听事件 (监听点击事件) 携带 id

    2. 子传父, 将删除的 id 传递给父组件 App.vue

    3. 进行删除 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 → 渲染
    1.父传子传list → 渲染
        
        
    
        合 计: {{ list.length }} 
    
    
    

    清空功能
    1. 注册监听点击事件

    2. 子传父 → $emit → list = []

    1.注册监听点击事件 
        
    
    2.子传父 → $emit → list = []
        methods: {
            removeTask () {
                //  2. 子传父 → $emit → list = []
                this.$emit('removeTask')
            }
        }
    3.父组件中处理
        removeList () {
          // 监听子组件的通知后,执行删除功能
          this.list = []
        }
    

    持久化存储
    1. watch深度监视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: "看电影逛街" },
          ],
    
    

    06-非父子通信-(事件总线)


    作用;非父子组件之间,进行简易消息传递,(复杂场景 → Vuex)

    一对多的关系(一个组件发送,只要监听了的都可以访问到)

    使用方法:

    1. 创建一个都能访问到的事件总线 (空Vue实例) → utils /EventBus.js
    2. 接收方,监听Bus实例的事件
    3. 发送方,触发 Bus实例的事件

    1.创建一个都能访问到的事件总线 (空Vue实例) → utils /EventBus.js

    // 1.创建爱一个都能访问到的事件总线(空的 Vue 实例)
    import Vue from 'vue'
    
    const Bus = new Vue()
    
    export default Bus
    

    2.接收方,监听Bus实例的事件

    
    
    

    3.发送方,触发 Bus实例的事件

      
    
    

    07-非父子通信-provide & inject


    作用是跨层级共享数据

    provide共享的数据

    1. 简单数据类型:非响应式的
    2. 复杂数据类型:是响应式的

    通常使用就是使用包对象的形式(复杂数据类型)共享

    代码示例
    共享 1.父组件 provide 提供数据
    
    

    08-v-model原理


    原理:v-model本质上就是一个语法糖。例如应用在输入框上,就是value属性 和 input事件的合写。

    子组件不能直接是有个v-model访问父组件的数据的,因为没有权限,需要通过组件通信 + value 和 事件来解决。

    **作用:**实现数据双向绑定

    1. 数据变,视图跟着变:value
    2. 视图变,数据跟着变:@input

    注意:$event 用于模板中,获取事件的形参

        
        




    01-表单类组件封装(下拉菜单)
    1. 父传子:数据 应该是父组件props传递过来的,v-model拆解绑定数据
    2. 子传父: 监听输入,子传父传值给父组件修改
    父组件
    
      
    
    子组件
        
    
    

    这里使用了 :value 和 @change事件 实现子组件和父组件双向绑定


    02-父组件 v-model简化代码

    父组件 v-model 简化代码,实现 子组件 和 父组件 数据双向绑定

    1. 子组件中:props通过value接受,事件触发input
    2. 父组件中: v-model 给组件直接绑定数据

    前提条件:value接收 input事件触发

    父传子  使用 value 传值
    props: {
        value: String
    },
    
    子传父  使用 input 事件触发
    methods: {
        handleChaneg (e) {
            this.$emit('input',e.target.value)
        }
    }
    
    这样父组件中才能使用 v-model
      
      
    

    09-sync修饰符


    **作用:**可以实现 子组件 与 父组件数据的 双向绑定,简化代码

    特点: prop属性名 ,可以自定义。非固定的value

    场景:封装弹框类的基础组件,visible属性 true显示,false隐藏。

    本质:就是**:属性名** 和 @ updata:属性名的 合写

    父组件
    
    
    
    子组件
    props: {
    	//visible就是父组件传过来的属性名
    	visible: Boolean
    }
    
    methods: {
    	close () {
    	//通知父组件控制显示和隐藏
    	this.$emit('updata:visible',fasle)
    	这里的事件名在父组件中使用了sync修饰符,就相当于省略了事件监听。
    }
    }
    

    10-ref 和 $refs


    作用:利用 ref 和 refs可以用于获取dom元素,或组件实例

    特点:查找范围 → 当前组件内(更精确稳定)

    1.获取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 查找的范围 → 整个页面(很容易出错)


    2.获取组件实例

    利用res 和 $refs 获取组件实例

    ①目标组件 - 添加 ref 属性

    ②恰当时机,通过this.$refs.xxx,获取目标组件,就可以调用组件对象里面的方法

    父组件 App.vue代码
    
    ①目标组件 - 添加 ref 属性
     
    
    ②恰当时机,通过this.$refs.xxx,获取目标组件,就可以调用组件对象里面的方法
    
    

    问题:在Vue中,这样写代码 “显示之后”,立刻获取焦点是不能成功的!

    
    

    原因:Vue是 异步更新 DOM (提升性能)


    解决方发: $nextTick: 等DOM更新后,才会触发执行此方法里面的函数体。

    一但DOM加载完毕,立即执行$nextTick方法里面的函数方法

    
    

    你可能感兴趣的:(Vue,vue.js,学习,javascript)