vue3.4的更新,保证你看的明明白白

vue3 · 浏览次数 : 0

小编点评

本文介绍了Vue 3.3中defineModel的功能和使用方法,并对比了Vue 3.2和Vue 3.4中的实现差异。 1. **Vue 3.2中的defineModel**:在Vue 3.2中,defineModel用于实现父组件和子组件之间的双向绑定。通过定义一个ref,可以实现数据的同步更新。子组件可以通过$emit触发父组件的事件来更新数据。 2. **Vue 3.3中的defineModel**:在Vue 3.3中,defineModel仍然是一个实验性功能,但已经可以通过定义props和emits选项来接收和更新数据。此外,还提供了get和set选项来实现v-model修饰符的功能。 3. **Vue 3.4中的defineModel**:在Vue 3.4中,defineModel的实现有所改进,包括支持默认值和数据类型的更新。同时,删除了一些不再推荐使用的功能和模板语法。 4. **defineModel的问题**:文章提到了在使用defineModel时可能遇到的两个问题:父子数据不同步和子组件数据不更新。这些问题可能与数据类型有关,基本数据类型更新正常,而引用数据类型可能不会更新。 5. **v-model修饰符**:文章还讨论了v-model修饰符的使用,以及如何在defineModel中实现类似的功能。 6. **编译器性能优化**:最后,文章提到了Vue 3.4相对于Vue 3.3在编译器和解析器方面的性能优化,包括新的解析器和删除了已弃用的功能。 总的来说,Vue 3.4在defineModel的实现上提供了更多的便利性和性能优化,同时也解决了一些在使用过程中可能遇到的问题。

正文

defineModel 同学已经转正

defineModel 在vue3.3中还是一个实验性功能,
但是经过一个学期的努力,该同学已经转正。
复制

defineModel的简单介绍

defineModel() 返回的值是一个 ref。
它可以像其他 ref 一样被访问以及修改。
它能起到在父组件和当前变量之间的双向绑定的作用。
它的 .value 和父组件的 v-model 的值同步。
当它被子组件改变时,会触发父组件绑定的值一起更新。

我们都知道 props 的设计原则是单项数据流。
子组件默认情况下是无法更改父组件传递过来的数据。
如果要更改vue3.3以前是通过 $emit 来实现的。
下面我们来对比一下使用 $emit 和 defineModel 来更新数据
复制

使用 $emit更新父组件传递过来的数据(vue3.2)

// 父页面
<template>
  <div>
    <div class="father-box">
      <p>我是父页面-此时:son组件的值{{ flag }}</p>
      <button @click="openHandler"> 显示子组件</button>
    </div>
    <!-- 控制子组件是否显示 -->
    <son v-model:flag="flag"></son>
  </div>
</template>
<script setup lang="ts">
// vue3.2开始组件引入后,就不需要注册啦。变量也不需要暴露出去啦
import son from '@/components/son.vue'
import { ref } from 'vue';
let flag = ref(false)
// 点击事件,更改值,让组件显示出来
const openHandler = ()=>{
  flag.value = true;
}
</script>
<style>
.father-box{
  background: palegreen;
}
</style>
复制
// son组件
<template>
  <div class="son-box" v-if="flag">
    <h1 >我是son组件</h1>
    <button @click="hideHandler"> 关闭组件:更改父组件传递过来的值</button>
  </div>
</template>
<script setup lang="ts">
import { defineProps, defineEmits } from "vue";
// 接收传递过来的值
defineProps({
  flag: {
    type: Boolean,
    default: false,
  }
});
// 注册事件
const emits = defineEmits(["update:flag"]);
// 去更新父组件中的flag值,更改为false
const hideHandler = () => {
  emits("update:flag", false)
}
</script>
<style>
.son-box{
   background: pink;
}
</style>
复制

使用 defineModel 更新父组件传递过来的数据(vue3.4)

// 父页面代码
<template>
  <div>
    <div class="father-box">
      <p>我是父页面-此时:son1组件的值{{ flag }}</p>
      <button @click="openHandler"> 显示子组件</button>
    </div>
    <!-- 控制子组件是否显示 -->
    <son1 v-model:flag="flag"></son1>
  </div>
</template>
<script setup lang="ts">
// vue3.2开始组件引入后,就不需要注册啦。变量也不需要暴露出去啦
import son1 from '@/components/son1.vue'
import { ref } from 'vue';
let flag = ref(false)
// 点击事件,更改值,让组件显示出来
const openHandler = ()=>{
  flag.value = true;
}
</script>
<style>
.father-box{
  background: palegreen;
}
</style>
复制
// son1组件
<template>
  <div class="son-box" v-if="flagBool">
    <h1 >我是son1组件</h1>
    <button @click="hideHandler"> 关闭组件:更改父组件传递过来的值</button>
  </div>
</template>

<script setup lang="ts">
import { defineModel } from "vue";
// defineModel('flag')中的flag必须与传递过来的属性保持一致
// flagBool 是接受的控制变量,可以是任意的
const flagBool = defineModel('flag')
const hideHandler = () => {
  flagBool.value = false
}
</script>
<style>
.son-box{
   background: pink;
}
</style>
复制

采访一下:使用 defineModel 后的感觉

现在使用 defineModel 进行数据的双向绑定更加友好。
比原来更加方便了。爽歪歪!
原来需要再一个合适的时机(事件触发)写上:
emits("update:flag", false)
而现在直接写上 const 变量名 = defineModel('双向绑定的值')
不需要考虑时机
复制

defineModel 传递多个v-model

// 父页面
<template>
  <div>
    <div class="father-box">
      <p>我是父页面-此时:son3组件的值{{ titleName }} {{ address }}</p>
    </div>
    <!-- defineModel 传递多个v-model -->
    <son3 v-model:titleName="titleName" v-model:address="address"></son3>
  </div>
</template>
<script setup lang="ts">
// vue3.2开始组件引入后,就不需要注册啦。变量也不需要暴露出去啦
import son3 from '@/components/son3.vue'
import { ref } from 'vue'
let titleName = ref('少玩手机多看报')
let address = ref('我在这个红绿灯旁边')
</script>
<style>
.father-box{
  background: palegreen;
}
</style>
复制
// 组件son3
<template>
  <div class="son-box">
    <h1 >我是son1组件</h1>
    <input type="text" v-model="titleName"> 
    <input type="text" v-model="address"> 
  </div>
</template>

<script setup lang="ts">
import { defineModel } from "vue";
const titleName = defineModel('titleName')
const address = defineModel('address')
</script>
<style>
.son-box{
   background: pink;
}
</style>
复制

defineModel 设置默认值

// 父页面
<template>
  <div>
    <div class="father-box">
      <!-- 这里获取不到子组件的值 -->
      <p>我是父页面-此时:son3组件的值{{ obj }} </p>
    </div>
    <!-- 没有给子组件传递值 -->
    <son3></son3>
  </div>
</template>
<script setup lang="ts">
import son3 from '@/components/son3.vue'
import { ref } from 'vue'
const obj = ref()
</script>
<style>
.father-box{
  background: palegreen;
}
</style>
复制
<template>
  <div class="son-box">
    <h1 >我是son3组件</h1>
    <h2>{{  detailsObj.name }}</h2>
    <h2>{{  detailsObj.age }}</h2>
    <button @click="changeHandler">改变值</button>
  </div>
</template>

<script setup lang="ts">
import { defineModel } from "vue";
// 定义 参数如类型、默认值
let detailsObj = defineModel('obj', {
  // 初始渲染的时候会显示默认值
  default: { name :'张三', age: 10},
  type: Object,
});
// 改变值
const changeHandler = ()=>{
  console.log(detailsObj)
  detailsObj.value.name = '我是王五',
  detailsObj.value.age = 20
}
</script>
.son-box{
   background: pink;
}
</style>
复制


发现2个问题:父子数据不同步,子组件数据不更新

我们通过给defineModel设置了默认值。
子组件也正确显示了默认值,但是父页面获取不到子组件的值。
这导致导致父组件与子组件之间的数据不同步。这个是我们发现的第1个问题
第2个问题是:设置默认值后,子组件数据在视图中不更新。
这里我们大胆的猜想,是不是跟数据类型有关?
引用数据类型不更新,基本数据类型会更新。
复制

验证: 子组件引用数据类型不更新,基本数据类型会更新

// 父组件
<template>
  <div>
    <div class="father-box">
      <!-- 这里获取不到子组件的值 -->
      <p>我是父页面-此时:son3组件的值{{ age }} </p>
    </div>
    <!-- 没有给子组件传递值 -->
    <son3></son3>
  </div>
</template>
<script setup lang="ts">
import son3 from '@/components/son3.vue'
import { ref } from 'vue'
const age = ref()
</script>
复制
// 子组件
<template>
  <div class="son-box">
    <h1 >我是son3组件</h1>
    <h2>{{  ageValue }}</h2>
    <button @click="changeHandler">改变值</button>
  </div>
</template>
<script setup lang="ts">
import { defineModel } from "vue";
// 这次是一个基本数据类型
let ageValue = defineModel('age', {
  // 初始渲染的时候会显示默认值
  default: 10,
  type: Number,
});
// 改变值,页面会更新吗?
const changeHandler = ()=>{
  console.log(ageValue)
  ageValue.value = 100
}
</script>
复制

总结: defineModel使用默认值会造成2个影响

defineModel使用默认值后:
1.导致父组件与子组件之间的数据不同步
2.如何默认值是应用数据类型,子组件数据在视图中不更新,
  如果默认值是基本数据类型,子组件数据在视图中会更新。
ps: 尽量不要在defineModel中使用默认值。
复制

处理 v-model 修饰符

我们知道了 v-model 有一些内置的修饰符。
例如 .trim, .number, .lazy
有些时候,我们想自定义修饰符。
如:将v-model 绑定输入的字符串值第一个字母转为大写。
我们可以使用 defineModel 的 getset 选项。
复制

v-model 修饰符实现:第一个字母转为大写

// 父组件
<template>
  <div>
    <div class="father-box">
      <!-- 这里获取不到子组件的值 -->
      <p>我是父页面-此时:child组件的值{{ surName }} </p>
    </div>
    <!-- 修饰符 titleCase 在 defineModel 解构的第2个参数中可以拿到 -->
    <child  v-model.titleCase="surName"></child>
  </div>
</template>
<script setup lang="ts">
import child from '@/components/child.vue'
import { ref } from 'vue'
const surName = ref()
</script>
<style>
.father-box{
  background: palegreen;
}
</style>
复制
// 子组件
<template>
  <input type="text" v-model="modelValue" />
</template>
<script setup>
import { defineModel } from 'vue'
// 解构
const [modelValue, modifiers] = defineModel({
  set(value) {
    // 正则表达输入的是否是26个英文
    const regex = /^[a-zA-Z]+$/
    if(regex.test(value) && modifiers.titleCase){
      return value.charAt(0).toUpperCase() + value.slice(1)
    } else {
      // 如果不符合要求返回空字符
      return value
    }
  }
})
console.log(modelValue)
console.log(modifiers)
</script>
<style>
.son-box{
   background: pink;
}
</style>
复制

v-bind的简写语法

// 以前的
<img  :src="src" :alt="alt">
//3.4版本可以简写为
<img  :src :alt>
复制

v-bind的简写语法用在组件传递值上

<template>
  <div>
    // 简写语法
    <son3 :userName :age></son3>
    <!-- 等价与以前这样写 -->
    <!-- <son3 :userName="userName" :age="age"></son3> -->
  </div>
</template>
<script setup lang="ts">
import son3 from '@/components/son3.vue'
import { ref } from 'vue'
const userName = ref('李四')
const age = ref(18)
</script>
复制

更高效的反应性系统 watchEffect

<template>
  <div>
    <h1>分数{{ fraction }}</h1>
    <button @click="onChangeFraction">努力学习,改变分数</button>
  </div>
</template>
<script setup lang="ts">
import { ref ,watchEffect} from 'vue'
const fraction = ref(540)
// 现在fraction的值不发生变化
const onChangeFraction = () => {
  fraction.value = 540
}
// 不会触发watchEffect的回调
watchEffect(() => console.log(fraction.value))
</script>
复制
在 vue3.4 之前,即使计算结果(fraction)保持不变
每次 fraction.value 都将触发 watchEffect 的回调。
而现在只要fraction的值不变化,不会触发watchEffect的回调
复制

编译器性能优化

解析速度提高一倍
解析器从头开始重写,速度快了一倍。
与旧模板相比,它在一半的时间内解析相同的模板。
旧的解析器是一个递归下降解析器,它使用大量正则表达式和低效的前瞻搜索。
新的解析器使用 htmlparser2。
它以线性方式迭代输入,具有最少的前瞻和回溯。
并在很大程度上消除了对正则表达式的依赖。
复制

删除了已弃用的功能

1.全局 JSX 命名空间
3.4 开始,Vue 默认不再注册全局 JSX 命名空间。
这是避免与 React 发生全局命名空间冲突,
以便两个库的 TSX 可以共存于同一个项目中。
这应该不会影响使用最新版本的 Volar 的 SFC 的用户。

如果您使用的是 TSX,则有两种选择:
第1种:在升级到 3.4 之前,
在tsconfig.json 中将 jsxImportSource 显式设置为 'vue'。
您还可以通过在文件顶部添加 /* @jsxImportSource vue */ 注释来选择加入每个文件。

第2种:如果您的代码依赖于全局 JSX 命名空间的存在,
例如使用 JSX.Element 类型等.
则可以通过显式引用 vue/jsx 来保留 3.4 之前的确切全局行为,
这将注册全局 JSX 命名空间。
复制
2.其他已删除的功能
1,反应性转换在 3.3 中被标记为不推荐使用,现在在 3.4 中被删除。
2,app.config.unwrapInjectedRef 已被删除。
3,@vnode-xx模板中的事件侦听器现在是编译器错误,而不是弃用警告。请改用 @vue:XXX 侦听器。
4,v-is 指令已被删除。它在 3.3 中已弃用。请改用带 vue: 前缀的 is 属性。
复制

监听子组件的生命周期:@vnode-xx更改为@vue:xxx

在已经删除的功能中,第2点:@vnode-xx更改为@vue:xxx。
有些时候,我们需要去监听子组件的生命周期。
有2种办法:第1种,在子组件的各个生命周期中使用emit抛出方法,然后父组件调用
缺点:第3方组件必须如果没有提供emit的话,我们就可以使用下面这一种
第2种: 在组件中使用@vnode-xx(3.4之前)
现在@vnode--更改为@vue:xxx
复制

监听子组件的生命(vue3.4之前)

<template>
  <div>
    <!-- 在vue3.4之前监听子组件的生命周期可以使用 @vnode-mounted="fn" -->
    <son3 @vnode-mounted="sonMounted"></son3>
  </div>
</template>
<script setup lang="ts">
import son3 from '@/components/son3.vue'
const sonMounted = () =>{
  console.log('组件dom渲染完成')
}
</script>
复制

监听子组件的生命(vue3.4)

<template>
  <div>
    <!-- 现在使用@vue:mounted="fn" @vue:后面是生命周期-->
    <son3 @vue:mounted="mountedDoThing" ></son3>
  </div>
</template>
<script setup lang="ts">
import son3 from '@/components/son3.vue'
const mountedDoThing = () =>{
  console.log('子组件的挂载阶段完成')
}
</script>
复制

监听子组件的生命-奇怪的地方

细心的小伙伴刚刚可能发现了
<son3 @vue:mounted="mountedDoThing" ></son3>
@vue:后面的生命周期是原来vue2的mounted。
为啥不使用vue3的onMounted呢?
因为:如果使用的是onMounted的话,
将无法监听子组件(son3)是否在页面中被挂载了。
这里大家是否会觉得奇怪?
我想了很久,也没有找到原因。机智的小伙伴可以帮我解惑一下
下面我们看下使用vue3的生命周期是否会出发
复制

奇怪的地方:如果使用vue3的生命周期将不会被触发

<template>
  <div>
    <!-- 这里是vue3的onMounted生命周期,
      onMountedDoThing函数将不会被触发
        如果使用的是mounted将会被触发  -->
    <son3  @vue:onMounted="onMountedDoThing"></son3>
  </div>
</template>
<script setup lang="ts">
import son3 from '@/components/son3.vue'
const onMountedDoThing = () => {
  console.log('onMounted不会被触发')
}
</script>
复制

v-is 指令已被删除,改用带vue:前缀的is属性

在vue3.4中,v-is 指令已被删除。
它在 3.3 中已弃用。请改用 is="vue:想替换成的标签"
有些时候,我们想替换某个原生元素。
这个时候我们就可以is来实现。
下面我们将tr标签和p标签替换成li标签
复制
<template>
  <div>
    <ul>
      <tr is="vue:li">tr变成li标签</tr>
      <p is="vue:li">p变成li标签</p>
    </ul>
  </div>
</template>
复制

Vue 3.4 发布地址

https://blog.vuejs.org/posts/vue-3-4#removed-deprecated-features

尾声

如果你觉得我写的不错的话,
请给我点一个推荐,
周末都在写这个,有能力可以给我打赏(手动狗头)
最近想吃亲嘴烧,最好可以喝一瓶水,因为辣条有点辣(手动狗头)
复制

与vue3.4的更新,保证你看的明明白白相似的内容:

vue3.4的更新,保证你看的明明白白

defineModel 同学已经转正 defineModel 在vue3.3中还是一个实验性功能, 但是经过一个学期的努力,该同学已经转正。 defineModel的简单介绍 defineModel() 返回的值是一个 ref。 它可以像其他 ref 一样被访问以及修改。 它能起到在父组件和当前变量

Vue3 中的 v-bind 指令:你不知道的那些工作原理

前言 v-bind指令想必大家都不陌生,并且都知道他支持各种写法,比如、、(vue3.4中引入的新的写法)。这三种写法的作用都是一样的,将title变量绑定到div标签的title属性

Go版RuoYi

RuoYi-Go https://github.com/Kun-GitHub/RuoYi-Go 1. 关于我 个人介绍 2. 介绍 后端用Go写的RuoYi权限管理系统 (功能正在持续实现)后端 Gitee地址 3. 前端 RuoYi-Vue3 官方前端Vue3版 4. Go后端技术栈(持续在对齐项

使用Vue3+elementPlus的Tree组件实现一个拖拽文件夹管理

目录1、前言2、分析3、 实现4、踩坑4.1、拖拽辅助线的坑4.2、数据的坑4.3、限制拖拽4.4、样式调整 1、前言 最近在做一个文件夹管理的功能,要实现一个树状的文件夹面板。里面包含两种元素,文件夹以及文件。交互要求如下: 创建、删除,重命名文件夹和文件 可以拖拽,拖拽文件到文件夹中,或着拖拽文

Vite-Wechat网页聊天室|vite5.x+vue3+pinia+element-plus仿微信客户端

基于Vue3+Pinia+ElementPlus仿微信网页聊天模板Vite5-Vue3-Wechat。 vite-wechat使用最新前端技术vite5+vue3+vue-router@4+pinia+element-plus搭建网页端仿微信界面聊天系统。包含了聊天、通讯录、朋友圈、短视频、我的等功

有点儿神奇,原来vue3的setup语法糖中组件无需注册因为这个

前言 众所周知,在vue2的时候使用一个vue组件要么全局注册,要么局部注册。但是在setup语法糖中直接将组件import导入无需注册就可以使用,你知道这是为什么呢?注:本文中使用的vue版本为3.4.19。 关注公众号:【前端欧阳】,给自己一个进阶vue的机会 看个demo 我们先来看个简单的d

Vue 3 Teleport:掌控渲染的艺术

title: Vue 3 Teleport:掌控渲染的艺术 date: 2024/6/5 updated: 2024/6/5 description: 这篇文章介绍了Vue3框架中的一个创新特性——Teleport,它允许开发者将组件内容投送到文档对象模型(DOM)中的任意位置,即使这个位置在组件的

vue3 封装axios

1添加一个新的 http.js文件 封装axios 引入axios //引入Axios import axios from 'axios' 定义一个根地址 //视你自己的接口地址而定 var root2 = 'http://121.4.63.196:8520/api' 定义个小函数来统一参数格式(可

Vue3快速上手

好久没上传了,闲来无事把囤积已久的笔记给上传上传 1. Vue3简介 2020年9月18日,Vue.js发布版3.0版本,代号:One Piece(n 经历了:4800+次提交、40+个RFC、600+次PR、300+贡献者 官方发版地址:Release v3.0.0 One Piece · vue

【干货】Vue3 组件通信方式详解

毫无疑问,组件通信是Vue中非常重要的技术之一,它的出现能够使我们非常方便的在不同组件之间进行数据的传递,以达到数据交互的效果。所以,学习组件通信技术是非常有必要的,本文将总结Vue中关于组件通信的八种方式,帮助大家在使用Vue的过程中更加得心应手! 如果文中有不对、疑惑的地方,欢迎在评论区留言指正...