Vue3视图渲染技术

1.1 模版语法

Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作。

1.1.1 插值表达式和文本渲染

插值表达式:最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 ,即双大括号{{}}

  • 插值表达式是将数据渲染到元素的指定位置的手段之一
  • 插值表达式不绝对依赖标签,其位置相对自由
  • 插值表达式中支持JavaScript的运算表达式
  • 插值表达式中也支持函数的调用
  • 插值表达式只能用于数据的显示,不能用于属性赋值
<script setup>

  /* 插值表达式 语法: {{}} */
  let msg = "hello Vue3"

  function getMsg() {
    return "我是Vue.js"
  }

  let a = 100
  let b = 100

  let user = {
    id: 100,
    name :"tomcat"
  }

</script>

<template>
  <div>
      <!-- 1.基本数据的引入 -->
      <h1>{{msg}}</h1>

      <!-- 2.插入表达式调用函数 -->
      <h2>{{getMsg()}}</h2>

      <!-- 3.进行js计算 -->
      <h2>{{ a + b}}</h2>

      <!-- 4.进行对象的引用 -->
      <h2>{{ user.id + ":" + user.name }}</h2>
  </div> 
</template>

<style scoped>
</style>

为了渲染双标中的文本,我们也可以选择使用v-textv-html命令

  • v-*** 这种写法的方式使用的是vue的命令
  • v-***的命令必须依赖元素,并且要写在元素的开始标签中
  • v-***指令支持ES6中的字符串模板
  • 插值表达式中支持JavaScript的运算表达式
  • 插值表达式中也支持函数的调用
  • v-text可以将数据渲染成双标签中间的文本,但是不识别html元素结构的文本
  • v-html可以将数据渲染成双标签中间的文本,识别html元素结构的文本
<script setup>
  /* 文本绑定标签 */
  let msg = "hello Vue"

  function getMsg() {
    return "我是一个业务操作"
  }

  let myHtml = "<h1>我是一个标题标签</h1>"
  
</script>

<template>
  <div>
    <!-- 插入表达式引入数据 -->
    <h1>{{ msg }}</h1>

    <!-- 1.v-text语法: 为标签添加显示内容 -->
    <h1 v-text="msg"></h1>

    <!-- 2.v-text语法: 调用函数 -->
    <h1 v-text="getMsg()"></h1>

    <!-- 3.v-html语法:  为标签添加html语法 -->
    <div v-html="myHtml"></div>
  </div> 
</template>

<style scoped>
</style>
1.1.2 Attribute属性渲染

想要渲染一个元素的 attribute,应该使用 v-bind指令

  • 由于插值表达式不能直接放在标签的属性中,所有要渲染元素的属性就应该使用v-bind
  • v-bind可以用于渲染任何元素的属性,语法为 v-bind:属性名='数据名', 可以简写为 :属性名='数据名'
<script setup>
    /* v-bind指令 属性绑定 */
    let url = "http://www.baidu.com"
    let imageUrl = "http://www.xxxx.com/images/index_new/logo.png"
    let colorClass = "greenClass"
    let username = "admin"
</script>
<template>
  <div>
      <!-- 1.动态绑定属性 -->
      <a  v-bind:href="url">百度</a> <br>
      <a :href="url">百度-简化</a><br>

      <!-- 2.绑定图片地址 -->
      <img :src="imageUrl">

      <!-- 3.绑定样式class -->
      <div :class="colorClass"></div>

      <!-- 4.绑定input默认值 -->
      <input type="text" name="username" :value="username">
      
  </div>
</template>

<style scoped>
  .greenClass {
    width: 100px;
    height: 100px;
    background-color:green;
  }
</style>
1.1.3 事件的绑定

我们可以使用 v-on 来监听 DOM 事件,并在事件触发时执行对应的 Vue的JavaScript代码。

  • 用法:v-on:click="handler" 或简写为 @click="handler"
  • vue中的事件名=原生事件名去掉on 前缀 如:onClick --> click
  • handler的值可以是方法事件处理器,也可以是内联事件处理器
  • 绑定事件时,可以通过一些绑定的修饰符,常见的事件修饰符如下
    • .once:只触发一次事件。[重点]
    • .prevent:阻止默认事件。[重点]
    • .stop:阻止事件冒泡。
    • .capture:使用事件捕获模式而不是冒泡模式。
    • .self:只在事件发送者自身触发时才触发事件。
<script setup>
//引入响应式
import { ref } from 'vue';

    /* 事件绑定案例 */
    function myClick(){
      alert("我是点击触发事件")
    }

    function myChange(){
      alert("change事件触发")
    }

    function myBlur(){
      alert("blur事件触发")
    }

    //定义自增变量  定义响应式
    let count = ref(0)
    function addNum(){
      count.value ++
    }

    function myHref(event){
      console.log(event)
      console.log(event.target)  //获取当前元素
      window.location.href = "http://www.jd.com"
      //阻止默认行为
      event.preventDefault()
    }

</script>
<template>
  <div>
      <!-- 1.点击事件绑定 -->
      <button v-on:click="myClick">点击触发事件</button>

      <!-- 2.简化写法 -->
      <button @click="myClick">点击事件绑定2</button>
      <br>

      <!-- 3.点击change事件-->
      <input type="text" @change="myChange" placeholder="change事件"><br>

      <!-- 4.blur事件操作 -->
      <input type="text" @blur="myBlur" placeholder="blur事件"><br>

      <!-- 5.响应式自增操作 -->
      <h3 v-text="count"></h3>
      <button @click="addNum">自增</button>

      <hr>

      <!-- 6. .once事件 只触发一次 -->
      <button @click.once="addNum">只触发一次自增</button>
      <br>

      <!-- 7.阻止默认行为 -->
      <a href="http://www.baidu.com" @click.prevent="myHref">阻止百度跳转1</a>

      <!-- $event表示获取当前事件 -->
      <a href="http://www.baidu.com" @click="myHref($event)">阻止百度跳转2</a>

  </div>
</template>

<style scoped> 
</style>

1.2 响应式基础

此处的响应式是指 : 数据模型发生变化时,自动更新DOM树内容,页面上显示的内容会进行同步变化,vue3的数据模型不是自动响应式的,需要我们做一些特殊的处理

1.2.1 响应式需求案例

需求:实现 + - 按钮,实现数字加一减一

<script type="module" setup>
    let counter = 0;
    function show(){
        alert(counter);
    }
</script>

<template>
  <div>
    <button @click="counter--">-</button> 
    {{ counter }} 
    <button @click="counter++">+</button>
    <hr>
    <!-- 此案例,我们发现counter值,会改变,但是页面不改变! 默认Vue3的数据是非响应式的!-->
    <button @click="show()">显示counter值</button>
   </div>
</template> 

<style scoped>
</style>

1.2.2 响应式实现关键字ref

ref 可以将一个基本类型的数据(如字符串,数字等)转换为一个响应式对象。 ref 只能包裹单一元素

<script type="module" setup>
    /* 从vue中引入ref方法 */
    import {ref} from 'vue'
    let counter = ref(0);
    function show(){
        alert(counter.value);
    }
    /* 函数中要操作ref处理过的数据,需要通过.value形式 */
    let decr = () =>{
      counter.value--;
    }
    let incr = () =>{
      counter.value++;
    }
</script>

<template>
  <div>
    <button @click="counter--">-</button> 
    <button @click="decr()">-</button> 
    {{ counter }} 
    <button @click="counter++">+</button>
    <button @click="incr()">+</button> 
    <hr>
    <button @click="show()">显示counter值</button>
   </div>
</template> 

<style scoped>
</style>
  • 在上面的例子中,我们使用 ref 包裹了一个数字,在代码中给这个数字加 1 后,视图也会跟着动态更新。需要注意的是,由于使用了 ref,因此需要在访问该对象时使用 .value 来获取其实际值。
1.2.3 响应式实现关键字reactive

我们可以使用 reactive() 函数创建一个响应式对象或数组:

<script setup>
//引入响应式
import { ref,reactive } from 'vue';

    /* 响应式的2种写法 */
    let conunt =  ref(0)
    
    function addNum(){
        conunt.value ++
    }

    let person = reactive({
      id: 100,
      name: "tomcat"
    })

    //通过reactive标识响应式对象
    function updateData(){
      person.id = 200
      person.name = "mysql"
    }

</script>
<template>
  <div>
      <h3>{{ conunt }}</h3>
      <button @click="addNum">ref-响应式</button>
      <hr>
      <div>
        <p>{{ person.id }}  ~~~~   {{ person.name }}</p>
      </div>
      <button @click="updateData">reactive响应式</button>
  </div>    
</template>

<style scoped>
</style>

对比ref和reactive:

  • 使用 ref 适用于以下开发场景:

    • 包装基本类型数据:ref 主要用于包装基本类型数据(如字符串、数字等),即只有一个值的数据,如果你想监听这个值的变化,用 ref 最为方便。在组件中使用时也很常见。
    • 访问方式简单:ref 对象在访问时与普通的基本类型值没有太大区别,只需要通过 .value 访问其实际值即可。
  • 使用 reactive 适用于以下开发场景:

    • 包装复杂对象:reactive 可以将一个普通对象转化为响应式对象,这样在数据变化时会自动更新界面,特别适用于处理复杂对象或者数据结构。
    • 需要递归监听的属性:使用 reactive 可以递归追踪所有响应式对象内部的变化,从而保证界面的自动更新。
  • 综上所述,ref 适用与简单情形下的数据双向绑定,对于只有一个字符等基本类型数据或自定义组件等情况,建议可以使用 ref;而对于对象、函数等较为复杂的数据结构,以及需要递归监听的属性变化,建议使用 reactive。当然,在实际项目中根据需求灵活选择也是十分必要的。

1.2.4 扩展响应式关键字toRefs 和 toRef

toRef基于reactive响应式对象上的一个属性,创建一个对应的 ref响应式数据。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。toRefs将一个响应式对象多个属性转换为一个多个ref数据,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef() 创建的。

案例:响应显示reactive对象属性

<script setup>
//引入响应式
import { ref,reactive,toRef,toRefs} from 'vue';

    /* toRef 和Refs用法 */
    let user = reactive({
      id: 200,
      username: "张三"
    })

    //写代码时注意引号
    let nameRef = toRef(user,"username")

    let {id:myId,username:myUsername} = toRefs(user)

    function changeData(){
      nameRef.value = "修改后的值!!!!!"
    }
</script>
<template>
  <div>
      <div>
          {{ user.id }} ~~~~ {{ user.username }}
          <br>
          {{ nameRef }} ~~~{{ myId }} ~~~{{ myUsername }}
      </div>
      <button @click="changeData">修改数据1</button>
      <button @click="nameRef = '第二种方式'">修改数据2</button>
  </div> 
  
</template>

<style scoped>
</style>

1.3 条件和列表渲染

1.3.1 条件渲染

v-if 条件渲染

  • v-if='表达式' 只会在指令的表达式返回真值时才被渲染

  • 也可以使用 v-elsev-if 添加一个“else 区块”。

  • 一个 v-else 元素必须跟在一个 v-if 元素后面,否则它将不会被识别。

<script type="module" setup>
    import {ref} from 'vue'
    let awesome = ref(true)
</script>

<template>
  <div>
    <h1 v-if="awesome">Vue is awesome!</h1>
    <h1 v-else>Oh no 😢</h1>
    <button @click="awesome = !awesome">Toggle</button>
  </div>
</template> 

<style scoped>
</style>

v-show条件渲染扩展:

  • 另一个可以用来按条件显示一个元素的指令是 v-show。其用法基本一样:

  • 不同之处在于 v-show 会在 DOM 渲染中保留该元素;v-show 仅切换了该元素上名为 display 的 CSS 属性。

  • v-show 不支持在 <template> 元素上使用,也不能和 v-else 搭配使用。

<script type="module" setup>
    import {ref} from 'vue'
    let awesome = ref(true)
</script>

<template>
  <div>
    <h1 id="ha"  v-show="awesome">Vue is awesome!</h1>
    <h1 id="hb"  v-if="awesome">Vue is awesome!</h1>
    <h1 id="hc"  v-else>Oh no 😢</h1>
    <button @click="awesome = !awesome">Toggle</button>
  </div>
</template> 

<style scoped>
</style>

在这里插入图片描述

v-if vs v-show

  • v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。

  • v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。

  • 相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display 属性会被切换。

  • 总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。

1.3.2 列表渲染

我们可以使用 v-for 指令基于一个数组来渲染一个列表。

  • v-for 指令的值需要使用 item in items 形式的特殊语法,其中 items 是源数据的数组,而 item 是迭代项的别名

  • v-for 块中可以完整地访问父作用域内的属性和变量。v-for 也支持使用可选的第二个参数表示当前项的位置索引。

<script setup>
  //引入响应式
  import {ref} from "vue"

  //定义数组
  let array = ["王昭君","安琪拉","不知火舞"]

  //遍历集合数据
  let userList = [{id: 1001,name:"张三"},{id:1002,name:"李四"}]
</script>
<template>
  <div>
    <!-- 知识点:   v-for = "每个元素,下标值 in 数组" -->
     <p v-for="item in array" v-text="item" :key="item"></p>

     <p v-for="item,index in array" :key="item">
        {{ index }} ~~~ {{ item }}
    </p>
    <!-- 遍历对象 -->
    <div v-for="user in userList" :key="user.id">
       <p  v-text="user.id"></p>
       <p  v-text="user.name"></p>
    </div>
  </div>    
</template>

<style scoped>
</style>

1.4 双向绑定

单项绑定和双向绑定

  • 单向绑定: 响应式数据的变化会更新dom树,但是dom树上用户的操作造成的数据改变不会同步更新到响应式数据
  • 双向绑定: 响应式数据的变化会更新dom树,但是dom树上用户的操作造成的数据改变会同步更新到响应式数据
    • 用户通过表单标签才能够输入数据,所以双向绑定都是应用到表单标签上的,其他标签不行
    • v-model专门用于双向绑定表单标签的value属性,语法为 v-model:value='',可以简写为 v-model=''
    • v-model还可以用于各种不同类型的输入,<textarea><select> 元素。
<script setup>
import {ref,reactive} from "vue"  
  //通过响应式定义数据
  let user = reactive({id:100, name: "tom"})

  let person = reactive({
    username: "admin",
    password: "admin123",
    gender: "男",
    city: "北京"
  })

  function addPerson(){
    
     alert(person.username + ":" +person.password)
  }

</script>
<template>
  <div>
      ID号: <input type="text" v-model="user.id">
      姓名: <input type="text" v-model="user.name">
      <hr>
      数据绑定测试: <br>
      ID号: {{ user.id }}<br>
      姓名: {{ user.name }}<br>
      <hr>
      <form>
          用户名: <input  type="text" name="username" v-model="person.username"><br>
          密码:   <input  type="password" name="password" v-model="person.password"><br>
          性别:   <input  type="radio" name="gender" value="" v-model="person.gender"><input  type="radio" name="gender" value="" v-model="person.gender"><br>
          所在城市: 
                  <select name="city" v-model="person.city">
                      <option value="上海"> 上海</option>
                      <option value="北京"> 北京</option>
                      <option value="海南"> 海南</option>
                      <option value="西安"> 西安</option>
                  </select>
          <button @click="addPerson()">双向数据绑定测试</button>          
      </form>
  </div>   
</template>

<style scoped>
</style>

1.5 属性计算

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。比如说,我们有这样一个包含嵌套数组的对象:

<script setup>

  import { computed } from 'vue';

  let books = ["java编程","mysql","vue"]
  //let books = []

  function getBookLength(){
    console.log("方法被调用!!")
    return books.length > 0 ? "有图书" : "没有图书"
  }

</script>

<template>

    <div>
         <!-- 1.计算获取图书的数量 -->
         {{ books.length > 0 ? "有图书" : "没有图书" }}<br>

         <!-- 2.通过方法获取图书 -->
         {{ getBookLength() }}  <br>
         {{ getBookLength() }}  <br>

    </div>

</template>

<style scoped>
</style>
  • 这里的模板看起来有些复杂。我们必须认真看好一会儿才能明白它的计算依赖于 books。更重要的是,如果在模板中需要不止一次这样的计算,我们可不想将这样的代码在模板里重复好多遍。

因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。这是重构后的示例:

<script setup>

  import { computed } from 'vue';

  let books = ["java编程","mysql","vue"]
  //let books = []

  let computedBooks = computed( () => {
    console.log("计算属性被调用")
    return books.length > 0 ? "有图书" : "没有图书"
  })

</script>

<template>

    <div>
         <!-- 3.调用计算属性 -->
         {{  computedBooks }}   <br>
         {{  computedBooks }}   <br>
         {{  computedBooks }}   <br>
    </div>

</template>

<style scoped>
</style>
  • 我们在这里定义了一个计算属性 computedBookscomputed() 方法期望接收一个 getter 函数,返回值为一个计算属性 ref。和其他一般的 ref 类似,你可以通过 .value 访问计算结果。计算属性 ref 也会在模板中自动解包,因此在模板表达式中引用时无需添加 .value

计算属性缓存 vs 方法

  • 若我们将同样的函数定义为一个方法而不是计算属性,两种方式在结果上确实是完全相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。

1.6 数据监听器

计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。我们可以使用 watch 函数在每次响应式状态发生变化时触发回调函数:

  • watch主要用于以下场景:
    • 当数据发生变化时需要执行相应的操作
    • 监听数据变化,当满足一定条件时触发相应操作
    • 在异步操作前或操作后需要执行相应的操作

监控响应式数据(watch):

  • ref响应式-监听器用法
<script type="module" setup>
  //引入模块
  import { ref,reactive,computed,watch} from 'vue'

  /* 监听器用法---ref用法 */
  let msg = ref("")

  /* 使用方法没有办法实现数据的实时监控 */
  function getMsg(){
    console.log(msg.value)
  }

  /*通过监听器实现业务 */
  watch(msg,function(newMsg,oldMsg){
    //监控属性 msg
    console.log("新数据:"+newMsg)
    console.log("旧数据:"+oldMsg)
    
    let rege = /行动/
    if(rege.test(newMsg)){
      alert("小子 盯你好久了!!!")
    }else{
      console.log("放长线,钓大鱼!!!")
    }
  })

</script>

<template>
  <div>
      <!-- 1.准备一个属性定义监听器 -->
      特务: <input type="text" name="msg" v-model="msg"/>

      <!-- <button @click="getMsg">获取用户信息</button> -->


  </div>
</template> 

<style scoped>
</style>
  • reactive 用法
<script type="module" setup>
  //引入模块
  import { ref,reactive,computed,watch} from 'vue'

  /* 监听器用法--- reactive 用法*/
  let msgRea = reactive({msg: ''})

 
  //获取响应式数据
  function getMsg() {
    alert(msgRea.msg)
  }

  //配置监听器  
  //语法:  watch(function(){return 监控的数据},function(newMsg,oldMsg){...})
  /* watch(function(){return msgRea.msg},function(newMsg,oldMsg){
    //console.log("新数据:"+newMsg)
    //console.log("旧数据:"+oldMsg)

    let rege = /行动/
    if(rege.test(newMsg)){
      alert("小子盯你好久了")
    }else{
      console.log("放长线钓大鱼!!!")
    }

  }) */

  /* 配置监听器-监控所有属性 只要msgRea中的属性发生变化则会监控 
    语法: 
      {
        deep:true, 深度监控
        immediate: true  立即执行
      }
  */
  watch(() => msgRea,(newMsg,oldMsg) => {
    //使用该语法 新旧数据相同  
    console.log(newMsg)
    console.log(oldMsg)
  },{deep:true,immediate: true})
</script>

<template>
  <div>
      <!-- 1.准备一个属性定义监听器 -->
      特务: <input type="text" name="msg" v-model="msgRea.msg"/>

      <button @click="getMsg">获取数据</button>
  </div>
</template> 

<style scoped>
</style>

监控响应式数据(watchEffect):

  • watchEffect默认监听所有的响应式数据
<script type="module" setup>
  //引入模块
  import { ref,reactive,computed,watch,watchEffect} from 'vue'

  //监控所有响应式数据
  let msgRea = reactive({msg: ''})

  //语法:  watchEffect(function(){  需要监控的数据  })
  watchEffect(function(){
    let rege = /行动/
    if(rege.test(msgRea.msg)){
      alert("行动")
    }else{
      console.log("放长线,钓大鱼!!!")
    }
  })
</script>

<template>
  <div>
      <!-- 1.准备一个属性定义监听器 -->
      特务: <input type="text" name="msg" v-model="msgRea.msg"/>
  </div>
</template> 

<style scoped>
</style>

watch vs watchEffect

  • watchwatchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:
    • watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
    • watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。

1.7 Vue生命周期

1.7.1 生命周期简介

每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码!

  • 周期图解:
    在这里插入图片描述
  • 常见钩子函数
    • onMounted() 注册一个回调函数,在组件挂载完成后执行。
    • onUpdated() 注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。
    • onUnmounted() 注册一个回调函数,在组件实例被卸载之后调用。
    • onBeforeMount() 注册一个钩子,在组件被挂载之前被调用。
    • onBeforeUpdate() 注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
    • onBeforeUnmount() 注册一个钩子,在组件实例被卸载之前调用。
1.7.2 生命周期案例
<script setup>
    import {ref,onUpdated,onMounted,onBeforeUpdate} from 'vue'
    
    let message =ref('hello')
   
    // 挂载完毕生命周期
    onMounted(()=>{
      console.log('-----------onMounted---------')
      let span1 =document.getElementById("span1")
      console.log(span1.innerText)
    })
    // 更新前生命周期
    onBeforeUpdate(()=>{
      console.log('-----------onBeforeUpdate---------')
      console.log(message.value)
      let span1 =document.getElementById("span1")
      console.log(span1.innerText)
    })
    // 更新完成生命周期
    onUpdated(()=>{
      console.log('-----------onUpdated---------')
      let span1 =document.getElementById("span1")
      console.log(span1.innerText)
    })
</script>

<template>
  <div>
    <span id="span1" v-text="message"></span> <br>
    <input type="text" v-model="message">
  </div>
</template>

<style scoped>
</style>

1.8 Vue组件

1.8.1 组件之间传递数据
1.8.1.1 父传子

Vue3 中父组件向子组件传值可以通过 props 进行,具体操作如下:

  1. 首先,在父组件中定义需要传递给子组件的值,接着,在父组件的模板中引入子组件,同时在引入子组件的标签中添加 props 属性并为其设置需要传递的值。

  2. 在 Vue3 中,父组件通过 props 传递给子组件的值是响应式的。也就是说,如果在父组件中的传递的值发生了改变,子组件中的值也会相应地更新。

  • 父组件代码:App.vue
<script setup>
  import Son from './components/Son.vue'
  import {ref,toRefs} from 'vue'

  let message = ref('parent data!')
  let title = ref(42)

  function changeMessage(){
    message.value = '修改数据!'
    title.value++
  }
</script>

<template>
  <div>
    <h2>{{ message }}</h2>
    <hr>
    <!-- 使用子组件,并且传递数据! -->
    <Son :message="message" :title="title"></Son>
    <hr>
    <button @click="changeMessage">点击更新</button>
  </div>
</template>

<style scoped>
</style>
  • 子组件代码:Son.vue
<script setup type="module">
    import {ref,isRef} from 'vue'
    //声明父组件传递属性值
    defineProps({
        message:String ,
        title:Number
    })
</script>

<template>
    <div>
    <div>{{ message }}</div>
    <div>{{ title }}</div>
    </div>
</template>

<style>
</style>
1.8.1.2 子传父
  • 父组件: App.vue
<script setup>
    import Son from './components/Son.vue'
    import {ref} from 'vue'

    let pdata = ref('')

    const padd = (data) => {
        console.log('2222');
        pdata.value =data;
    }

    //自定义接收,子组件传递数据方法! 参数为数据!
    const psub = (data) => {
        console.log('11111');
        pdata.value = data;
    }
</script>

<template>
    <div>
        <!-- 声明@事件名应该等于子模块对应事件名!调用方法可以是当前自定义!-->
        <Son @add="padd" @sub="psub"></Son>
        <hr>
        {{ pdata }}
    </div>
</template>

<style>
</style>
  • 子组件:Son.vue
<script setup>

    import {ref,defineEmits} from 'vue'

    //1.定义要发送给父组件的方法,可以1或者多个
    let emites = defineEmits(['add','sub']);

    let data = ref(1);

    function sendMsgToParent(){
        console.log('-------son--------');

        //2.出发父组件对应的方法,调用defineEmites对应的属性
        emites('add','add data!'+data.value)
        emites('sub','sub data!'+data.value)

        data.value ++;
    }
</script>

<template>
    <div>
      <button @click="sendMsgToParent">发送消息给父组件</button>
    </div>
</template>
1.8.1.3 兄弟传参

在这里插入图片描述

  • Navigator.vue: 发送数据到App.vue
<script setup type="module">
    const emits = defineEmits(['sendMenu']);
    //触发事件,向父容器发送数据
    function send(data){
        emits('sendMenu',data);
    }
</script>

<template>
    <!-- 推荐写一个根标签-->
    <div>
       <ul>
          <li @click="send('学员管理')">学员管理</li>
          <li @click="send('图书管理')">图书管理</li>
          <li @click="send('请假管理')">请假管理</li>
          <li @click="send('考试管理')">考试管理</li>
          <li @click="send('讲师管理')">讲师管理</li>
       </ul>
    </div>
</template>

<style>

</style>
  • App.vue: 发送数据到Content.vue
<script setup>
  
  import Header  from './components/Header.vue'
  import Navigator  from './components/Navigator.vue'
  import Content  from './components/Content.vue'

  import {ref} from "vue"
  //定义接受navigator传递参数
  var navigator_menu = ref('ceshi');

  const receiver = (data) =>{
    navigator_menu.value = data;
  }
</script>

<template>
  <div>
      <hr>
      {{ navigator_menu }}
      <hr>
     <Header class="header"></Header>
     <Navigator @sendMenu="receiver" class="navigator"></Navigator>
     <!-- 向子组件传递数据-->
     <Content class="content" :message="navigator_menu"></Content>
    </div>
</template>

<style scoped>
    .header{
       height: 80px;
       border: 1px solid red;
    }

    .navigator{
      width: 15%;
      height: 800px;
      display: inline-block;
      border: 1px blue solid;
      float: left;
    }

    .content{
      width: 83%;
      height: 800px;
      display: inline-block;
      border: 1px goldenrod solid;
      float: right;
    }

</style>
  • Content.vue
<script setup type="module">
    defineProps({
        message:String
    })
</script>

<template>
    
    <div>
        展示的主要内容!
        <hr>
        {{ message }}
    </div>
</template>

<style>
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值