vue重修004【下部】

文章目录

  • 版权声明
  • 非父子通信
    • event bus 事件总线
      • 实现步骤
      • 代码演示
    • 非父子通信-provide&inject
      • 语法
      • 注意
      • 代码演示
  • v-model原理
  • 表单类组件封装& v-model 简化代码
  • .sync修饰符
    • 语法
    • 代码示例
  • ref 和 $refs
    • 语法
    • 代码演示
  • 异步更新 & $nextTick
    • 引子
    • $nextTick
    • 演示

版权声明

  • 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明,所有版权属于黑马程序员或相关权利人所有。本博客的目的仅为个人学习和交流之用,并非商业用途。
  • 我在整理学习笔记的过程中尽力确保准确性,但无法保证内容的完整性和时效性。本博客的内容可能会随着时间的推移而过时或需要更新。
  • 若您是黑马程序员或相关权利人,如有任何侵犯版权的地方,请您及时联系我,我将立即予以删除或进行必要的修改。
  • 对于其他读者,请在阅读本博客内容时保持遵守相关法律法规和道德准则,谨慎参考,并自行承担因此产生的风险和责任。本博客中的部分观点和意见仅代表我个人,不代表黑马程序员的立场。

非父子通信

event bus 事件总线

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

vue重修004【下部】_第1张图片


实现步骤

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

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

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

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

代码演示

  • 通信组件关系图
    vue重修004【下部】_第2张图片
  • BaseA.vue
<template>
  <div class="base-a">
    我是A组件(接受方)
    <p>{{msg}}</p>  
  </div>
</template>

<script>
import Bus from '../utils/EventBus'
export default {
  data() {
    return {
      msg: '',
    }
  },
  created() {
    Bus.$on('sendMsg', (msg) => {
      // console.log(msg)
      this.msg = msg
    })
  },
}
</script>

<style scoped>
.base-a {
  width: 200px;
  height: 200px;
  border: 3px solid #000;
  border-radius: 3px;
  margin: 10px;
}
</style>
  • BaseB.vue
<template>
  <div class="base-b">
    <div>我是B组件(发布方)</div>
    <button @click="sendMsgFn">发送消息</button>
  </div>
</template>

<script>
import Bus from '../utils/EventBus'
export default {
  methods: {
    sendMsgFn() {
      Bus.$emit('sendMsg', '今天天气不错,适合旅游')
    },
  },
}
</script>

<style scoped>
.base-b {
  width: 200px;
  height: 200px;
  border: 3px solid #000;
  border-radius: 3px;
  margin: 10px;
}
</style>
  • BaseC.vue
<template>
  <div class="base-c">
    我是C组件(接受方)
    <p>{{msg}}</p>  
  </div>
</template>

<script>
import Bus from '../utils/EventBus'
export default {
  data() {
    return {
      msg: '',
    }
  },
  created() {
    Bus.$on('sendMsg', (msg) => {
      // console.log(msg)
      this.msg = msg
    })
  },
}
</script>

<style scoped>
.base-c {
  width: 200px;
  height: 200px;
  border: 3px solid #000;
  border-radius: 3px;
  margin: 10px;
}
</style>
  • App.vue
<template>
  <div class="app">
    <BaseA></BaseA>
    <BaseB></BaseB>
    <BaseC></BaseC>
  </div>
</template>

<script>
import BaseA from './components/BaseA.vue'
import BaseB from './components/BaseB.vue'
import BaseC from './components/BaseC.vue'
export default {
  components:{
    BaseA,
    BaseB,
    BaseC
  }
}
</script>

<style>

</style>

非父子通信-provide&inject

  • provide & inject 作用:跨层级共享数据
    vue重修004【下部】_第3张图片

语法

  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)
  }
}

注意

  • provide提供的简单类型的数据不是响应式的,复杂类型数据是响应式。(推荐提供复杂类型数据)
  • 子/孙组件通过inject获取的数据,不能在自身组件内修改

代码演示

  • App.vue
<template>
  <div class="app">
    我是APP组件
    <button @click="change">修改数据</button>
    <SonA></SonA>
    <SonB></SonB>
  </div>
</template>

<script>
import SonA from './components/SonA.vue'
import SonB from './components/SonB.vue'
export default {
  provide() {
    return {
      // 简单类型 是非响应式的
      color: this.color,
      // 复杂类型 是响应式的
      userInfo: this.userInfo,
    }
  },
  data() {
    return {
      color: 'pink',
      userInfo: {
        name: 'zs',
        age: 18,
      },
    }
  },
  methods: {
    change() {
      this.color = 'red'
      this.userInfo.name = 'ls'
    },
  },
  components: {
    SonA,
    SonB,
  },
}
</script>

<style>
.app {
  border: 3px solid #000;
  border-radius: 6px;
  margin: 10px;
}
</style>
  • SonA.vue
<template>
  <div class="SonA">我是SonA组件
    <GrandSon></GrandSon>
  </div>
</template>

<script>
import GrandSon from '../components/GrandSon.vue'
export default {
  components:{
    GrandSon
  }
}
</script>

<style>
.SonA {
  border: 3px solid #000;
  border-radius: 6px;
  margin: 10px;
  height: 200px;
}
</style>
  • SonB.vue
<template>
  <div class="SonB">
    我是SonB组件
  </div>
</template>

<script>
export default {

}
</script>

<style>
.SonB {
  border: 3px solid #000;
  border-radius: 6px;
  margin: 10px;
  height: 200px;
}
</style>
  • GrandSon.vue
<template>
  <div class="grandSon">
    我是GrandSon
    {{ color }} -{{ userInfo.name }} -{{ userInfo.age }}
  </div>
</template>

<script>
export default {
  inject: ['color', 'userInfo'],
}
</script>

<style>
.grandSon {
  border: 3px solid #000;
  border-radius: 6px;
  margin: 10px;
  height: 100px;
}
</style>

v-model原理

  • 原理:v-model本质上是一个语法糖。例如应用在输入框上,就是 value属性 和 input事件 的合写。
  • 作用:提供数据的双向绑定
    1. 数据变,视图跟着变 :value
    2. 视图变,数据跟着变 @input
  • 注意:$event 用于在模板中,获取事件的形参
<template>
  <div class="app">
    <input type="text" v-model="msg1" />
    <br />
    <!-- v-model的底层其实就是:value和 @input的简写 -->
    <input type="text" :value="msg2" @input="msg2 = $event.target.value" />
  </div>
</template>

  • v-model使用在其他表单元素上的原理
    • 不同的表单元素, v-model在底层的处理机制是不一样的。比如给checkbox使用v-model底层处理的是 checked属性和change事件。

表单类组件封装& v-model 简化代码

  • 表单类组件 封装
    ① 父传子:数据 应该是父组件 props 传递 过来的,拆解 v-model 绑定数据
    ② 子传父:监听输入,子传父传值给父组件修改
    vue重修004【下部】_第4张图片
    vue重修004【下部】_第5张图片
  • 父组件 v-model 简化代码,实现 子组件 和 父组件数据 双向绑定
    ① 子组件中:props 通过 value 接收,事件触发 input
    ② 父组件中:v-model 给组件直接绑数据
    vue重修004【下部】_第6张图片

.sync修饰符

  • 作用:实现 子组件父组件数据双向绑定,简化代码。简单理解:子组件可以修改父组件传过来的props值
  • 场景:封装弹框类的基础组件, visible属性 true显示 false隐藏
  • 本质:.sync修饰符 就是 :属性名@update:属性名 合写

语法

vue重修004【下部】_第7张图片

代码示例

  • App.vue
<template>
  <div class="app">
    <button @click="openDialog">退出按钮button>
    
    <BaseDialog :isShow.sync="isShow">BaseDialog>
  div>
template>

<script>
import BaseDialog from './components/BaseDialog.vue'
export default {
  data() {
    return {
      isShow: false,
    }
  },
  methods: {
    openDialog() {
      this.isShow = true
      // console.log(document.querySelectorAll('.box')); 
    },
  },
  components: {
    BaseDialog,
  },
}
script>
  • BaseDialog.vue
<template>
  <div class="base-dialog-wrap" v-show="isShow">
    <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>确认</button>
        <button>取消</button>
      </div>
    </div>
  </div>
</template>

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

ref 和 $refs

  • 作用:利用 ref 和 $refs 可以用于 获取 dom 元素, 或 组件实例
  • 特点:查找范围 → 当前组件内 (更精确稳定)

语法

  1. 给要获取的盒子添加ref属性
<div ref="chartRef">我是渲染图表的容器div>
  1. 获取时通过 $refs获取 this.$refs.chartRef 获取
mounted () {
  console.log(this.$refs.chartRef)
}

代码演示

  • BaseChart.vue
<template>
  <div class="base-chart-box" ref="baseChartBox">子组件</div>
</template>

<script>
import * as echarts from 'echarts'

export default {
  mounted() {
    // 基于准备好的dom,初始化echarts实例
    // document.querySelector 会查找项目中所有的元素
    // $refs只会在当前组件查找盒子
    // var myChart = echarts.init(document.querySelector('.base-chart-box'))
    var myChart = echarts.init(this.$refs.baseChartBox)
    // 绘制图表
    myChart.setOption({
      title: {
        text: 'ECharts 入门示例',
      },
      tooltip: {},
      xAxis: {
        data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'],
      },
      yAxis: {},
      series: [
        {
          name: '销量',
          type: 'bar',
          data: [5, 20, 36, 10, 10, 20],
        },
      ],
    })
  },
}
</script>

<style scoped>
.base-chart-box {
  width: 400px;
  height: 300px;
  border: 3px solid #000;
  border-radius: 6px;
}
</style>
  • App.vue
<template>
  <div class="app">
    <div class="base-chart-box">
      这是一个捣乱的盒子
    </div>
    <BaseChart></BaseChart>
  </div>
</template>

<script>
import BaseChart from './components/BaseChart.vue'
export default {
  components:{
    BaseChart
  }
}
</script>

<style>
.base-chart-box {
  width: 300px;
  height: 200px;
}
</style>

异步更新 & $nextTick

引子

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

  1. 点击编辑,显示编辑框
  2. 让编辑框,立刻获取焦点
this.isShowEdit = true // 显示输入框
this.$refs.inp.focus() // 获取焦点
  • 问题:“显示之后”,立刻获取焦点是不能成功的!
  • 原因:Vue 是 异步更新 DOM (提升性能)

$nextTick

  • $nextTick:等 DOM更新后,才会触发执行此方法里的函数体
  • 语法: this.$nextTick(函数体)
this.$nextTick(() => {
  this.$refs.inp.focus()
})

注意:$nextTick 内的函数体 一定是箭头函数,这样才能让函数内部的this指向Vue实例

演示

<template>
  <div class="app">
    <div v-if="isShowEdit">
      <input type="text" v-model="editValue" ref="inp" />
      <button>确认</button>
    </div>
    <div v-else>
      <span>{{ title }}</span>
      <button @click="handleEdit">编辑</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      title: '大标题',
      isShowEdit: false,
      editValue: '',
    }
  },
  methods: {
   handleEdit() {
     //1 显示输入框
     this.isShowEdit=true;
     //2 让输入框获取焦点 等DOM更新后,才会触发执行此方法里的函数体
     this.$nextTick(()=>{
       this.$refs.inp.focus();
     })

   }
  },
}
</script>

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