组件通信 & 进阶语法

组件组成部分

scoped

组件的 style 样式,默认会作用于全局,加上 scoped 属性的 style 样式,只会作用于当前组件

scoped 原理:

  1. 给当前组件内所有标签添加 data-v-hash值 的自定义属性
  2. 当前组件的 style 样式的 css 选择器会自动添加属性选择器

data

一个组件的 data 选项必须是一个函数,保证每个组件实例维护独立的一份数据对象,每次创建新的组件实例,都会新执行一次 data 函数,得到一个新对象,示例代码如下:

<template>
  <div class="base-count">
    <button @click="count--">-</button>
    <span>{{ count }}</span>
    <button @click="count++">+</button>
  </div>
</template>

<script>
export default {

  data: function () {
    return {
      count: 100,
    }
  },
  // 简写
  // data() {
  //   console.log('函数执行了')
  //   return {
  //     count: 100,
  //   }
  // },

}
</script>

组件通信

父子关系

  • 父组件通过 props 将数据传递给子组件
  • 子组件通过 $emit 通知父组件修改更新

在这里插入图片描述

父传子

父组件代码:

<template>
  <div class="app" style="border: 3px solid #000; margin: 10px">
    我是APP组件
    <!-- 1.给组件标签添加自定义属性 -->
    <Son :title="myTitle"></Son>
  </div>
</template>

<script>
import Son from './components/Son.vue'
export default {
  name: 'App',
  data() {
    return {
      myTitle: '程序员校花',
    }
  },
  components: {
    Son,
  },
}
</script>

子组件代码:

<template>
  <div class="son" style="border:3px solid #000;margin:10px">
    <!-- 3.直接使用 props 的值 -->
    我是Son组件 {{title}}
  </div>
</template>

<script>
export default {
  name: 'Son-Child',
    
  // 2.通过 props 来接受
  props:[' title ']
}
</script>

子传父

父组件代码:

<template>
  <div class="app" style="border: 3px solid #000; margin: 10px">
    我是APP组件
    <!-- 2.父组件对子组件的消息进行监听 -->
    <Son :title="myTitle" @changTitle="handleChange"></Son>
  </div>
</template>

<script>
import Son from './components/Son.vue'
export default {
  name: 'App',
  data() {
    return {
      myTitle: '程序员校花',
    }
  },
  components: {
    Son,
  },
  methods: {
    // 3.提供处理函数,提供逻辑
    handleChange(newTitle) {
      console.log(newTitle) // 校花好美
      this.myTitle = newTitle
    },
  },
}
</script>

子组件代码:

<template>
  <div class="son" style="border: 3px solid #000; margin: 10px">
    我是Son组件 {{ title }}
    <button @click="changeFn">修改title</button>
  </div>
</template>

<script>
export default {
  name: 'Son-Child',
  props: ['title'],
  methods: {
    changeFn() {
      // 1. 通过 this.$emit() 向父组件发送通知
      this.$emit('changTitle','校花好美')
    },
  },
}
</script>

props

  • 定义:组件上注册的一些自定义属性(自定义属性指的是 vue 组件标签,而不是 HTML 标签)
  • 作用:向子组件传递数据
  • 特点:可以传递任意数量、任意类型的 props

在这里插入图片描述

校验

可以为组件的 props 指定验证要求,不符合要求,控制台就会有错误提示,可以进行:

  • 类型校验
  • 非空校验
  • 默认值
  • 自定义校验

示例代码如下:

props: {
  校验的属性名: {
    type: 类型, // Number String Boolean ...
    required: true, // 是否必填
    default: 默认值, // 默认值
    validator(value) {
      // 自定义校验逻辑
      return 是否通过校验
    }
  }
},
VS method
  • 共同点:都可以给组件提供数据
  • 区别:
    • data 的数据是自己的,随便改
    • props 的数据是外部的,不能直接改

非父子关系(了解)

event bus 事件总线

作用:非父子组件之间,进行简易消息传递

在这里插入图片描述

使用步骤如下:

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

    import Vue from 'vue'
    const Bus = new Vue()
    export default Bus
    
  2. A 组件(接收方),监听 Bus 实例的事件

    created () {
        Bus.$on('sendMsg', (msg) => {
        	this.msg = msg
        })
    }
    
  3. B 组件(发送方),触发 Bus 实例的事件

    Bus.$emit('sendMsg', '这是一个消息')
    

provide & inject

作用:跨层级共享数据

在这里插入图片描述

使用步骤如下:

  1. 父组件 provide 提供数据

    export default {
        provide () {
            return {
                // 普通类型【非响应式】
                color: this.color, 
                // 复杂类型【响应式】
                userInfo: this.userInfo, 
            }
        }
    }
    
  2. 其他组件使用 inject 取值使用

    export default {
        inject: ['color','userInfo'],
        created () {
      	  console.log(this.color, this.userInfo)
        }
    }
    

进阶语法

v-model

原理

  • 原理:v-model 本质上是一个语法糖。例如应用在输入框上,就是 value 属性和 input 事件的合写
  • 作用:提供数据的双向绑定
    • 数据变,视图跟着变 :value
    • 视图变,数据跟着变 @input
  • 在模板中,$event 用于获取事件的形参

示例代码如下:

<template>
  <div class="app">
    <input v-model="msg1" type="text"/>
    <br />
    <!-- v-model的底层其实就是:value和 @input的简写 -->
    <input :value="msg2" @input="msg2 = $event.target.value" type="text"/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg1: '',
      msg2: '',
    }
  },
}
</script>

封装表单组件

在这里插入图片描述

封装表单类组件,实现子组件和父组件数据的双向绑定:

  • 父传子:数据由父组件的 props 传递过来的,拆解 v-model 绑定数据
  • 子传父:监听输入,子传父传值给父组件修改

表单子组件代码如下:

<template>
  <div>
    <select :value="cityId" @change="selectCity">
      <option value="101">北京</option>
      <option value="102">上海</option>
      <option value="103">武汉</option>
      <option value="104">广州</option>
      <option value="105">深圳</option>
    </select>
  </div>
</template>

<script>
export default {
  props: {
    cityId: String,
  },
  methods: {
    selectCity(e) {
      this.$emit('changeId', e.target.value)
    },
  },
}
</script>

父组件代码如下:

<template>
  <div class="app">
    <BaseSelect
        :cityId="selectId"
        @changeId="selectId = $event"
    ></BaseSelect>
  </div>
</template>

<script>
import BaseSelect from './components/BaseSelect.vue'

export default {
  data() {
    return {
      selectId: '103',
    }
  },
  components: {
    BaseSelect,
  }
}
</script>

v-model 简化

  • 上面封装表单组件中不能直接使用 v-modal,因为数据是父组件的不能直接修改
  • 但是可以在父组件简化代码,步骤如下:
    1. 子组件中:props 通过 value 接收,事件触发 input
    2. 父组件中:v-model 给组件直接绑数据

因为 v-model 就等于 :value + @input

HTML 中 changeinput 事件的区别:

  • change 事件:在用户对表单元素进行修改并失去焦点后触发
  • input 事件:用户对表单元素的输入进行修改时即时触发

表单子组件代码如下:

<template>
  <div>
    <select :value="value" @change="selectCity">
      <option value="101">北京</option>
      <option value="102">上海</option>
      <option value="103">武汉</option>
      <option value="104">广州</option>
      <option value="105">深圳</option>
    </select>
  </div>
</template>

<script>
export default {
  props: {
    value: String,
  },
  methods: {
    selectCity(e) {
      this.$emit('input', e.target.value)
    },
  },
}
</script>

父组件代码如下:

<template>
  <div class="app">
    <BaseSelect
      v-model="selectId"
    ></BaseSelect>
  </div>
</template>

<script>
import BaseSelect from './components/BaseSelect.vue'
export default {
  data() {
    return {
      selectId: '102',
    }
  },
  components: {
    BaseSelect,
  },
}
</script>

sync 修饰符

  • 作用:可以实现子组件与父组件数据 的 双向绑定,简化代码
  • 特点:props 属性名可以自定义,不用写死为 value
  • 场景:封装弹框类的基础组件,visible 属性 true 显示 false 隐藏
  • 本质::属性名@update:属性名 的合写
  • @update 为 vue 的自定义事件,用于父子组件之间的通信
在这里插入图片描述

父组件代码如下:

<template>
  <div class="app">
    <button @click="openDialog">退出按钮</button>
    <!-- :visible.sync  === :visible="isShow" + @update:visible="isShow=$event" -->
    <BaseDialog :visible.sync="isShow"></BaseDialog>
  </div>
</template>

<script>
import BaseDialog from './components/BaseDialog.vue'

export default {
  data() {
    return {
      isShow: false,
    }
  },
  methods: {
    openDialog() {
      this.isShow = true
    },
  },
  components: {
    BaseDialog,
  },
}
</script>

子组件代码如下:

<template>
  <div class="base-dialog-wrap" v-show="visible">
    <div class="base-dialog">
      <div class="title">
        <h3>温馨提示:</h3>
        <button class="close" @click="closeDialog">x</button>
      </div>
      <div class="content">
        <p>你确认要退出本系统么?</p>
      </div>
      <div class="footer">
        <button @click="closeDialog">确认</button>
        <button @click="closeDialog">取消</button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    visible: Boolean,
  },
  methods: {
    closeDialog() {
      this.$emit('update:visible', false)
    }
  }
}
</script>

ref 和 $refs

  • 作用:使用 ref$refs 可以用于获取 dom 元素或组件实例

  • 特点:查找范围是当前组件内,更精确稳定

  • 语法如下:

    • 获取 dom:

      1. 给目标标签添加 ref 属性

        <div ref="chartRef">我是渲染图表的容器</div>
        
      2. 在 dom 渲染后,通过 this.$refs.xxx,获取目标标签

        mounted () {
        	console.log(this.$refs.chartRef)
        },
        
    • 获取组件:

      1. 给目标组件添加 ref 属性

        <BaseForm ref="baseForm"></BaseForm>
        
      2. 使用 this.$refs.xxx,获取目标组件,就可以调用组件对象里面的方法

        this.$refs.baseForm.组件方法()
        

$nextTick

异步更新

需求:编辑标题,编辑框自动聚焦:

  1. 点击编辑,显示编辑框
  2. 让编辑框立刻获取焦点
this.isShowEdit = true // 显示输入框
this.$refs.inp.focus() // 获取焦点
  • 问题:第一行代码执行后,DOM 还未更新完,此时获取焦点是不能成功的
  • 原因:Vue 是异步更新 DOM ,目的是提升性能

$nextTick 解决异步更新

$nextTick等 DOM 更新后,才会触发执行此方法里的函数体,语法如下:

this.$nextTick(() => {
	this.$refs.inp.focus()
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值