Vue3
- 2020 年 9 月 18 日发布,许多开发者还在观望
- 2022 年 2 月 7 日称为默认版本,意味着 vue3 是现在也是未来
库名称 | 简介 |
---|---|
ant-design-vue | PC 端组件库:Ant Design 的 Vue 实现,开发和服务于企业级后台产品 |
arco-design-vue | PC 端组件库:字节跳动出品的企业级设计系统 |
element-plus | PC 端组件库:基于 Vue 3,面向设计师和开发者的组件库 |
Naive UI | PC 端组件库:一个 Vue 3 组件库,比较完整,主题可调,使用 TypeScript,快,有点意思 |
vant | 移动端组件库:一个轻量、可靠的移动端组件库,于 2017 年开源 |
VueUse | 基于 composition 组合式 api 的常用函数集合 |
相关文档
- Vue3 中文文档(新) https://cn.vuejs.org/
- Vue2 中文文档(旧) https://v2.cn.vuejs.org/
- Vue3 设计理念 https://vue3js.cn/vue-composition/
Vue3优点和特点
- 首次渲染更快
- diff 算法更快
- 内存占用更少
- 打包体积更小
- 更好的 Typescript 支持
- Composition API 组合 API
vite 构建工具
vite(法语意为 "快速的",发音 /vit/,发音同 "veet") 是一种新型前端构建工具,能够显著提升前端开发体验
- 对比 webpack:
- 需要查找依赖,打包所有的模块,然后才能提供服务,更新速度会随着代码体积增加越来越慢
- vite 的原理:
- 使用原生 ESModule 通过 script 标签动态导入,访问页面的时候加载到对应模块编译并响应
- 注:项目打包的时候最终还是需要打包成静态资源的,打包工具 Rollup
- 问题:
- 基于 webpack 构建项目,基于 vite 构建项目,谁更快体验更好?vite
- 基于 webpack 的 vue-cli 可以创建 vue 项目吗?可以,慢一点而已
vite 创建项目
- 运行创建项目命令
# 使用npm
npm create vite@latest
# 使用yarn
yarn create vite
# 使用pnpm
pnpm create vite
代码提示
- Vue2的提示插件:Vetur
- Vue3的提示插件:Vue Language Features(Volar)
- 注意:安装 volar,禁用 vuter,也可以使用工作区模式启用对应插件
vue3 写法不同
- 组件一个根节点非必需
<template>
<div>节点1</div>
<div>节点2</div>
</template>
- 创建应用挂载到根容器(vue3 中是使用 createApp() 管理容器,不是 new Vue())
import { createApp } from 'vue'
import App from './App.vue'
// 根据App组件创建一个应用实例
const app = createApp(App)
// app应用挂载(管理)index.html的 #app 容器
app.mount('#app')
- 入口页面,ESM(ES Module)加载资源
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
CompositionAPI
组合式API的介绍
Vue3提供两种组织代码逻辑的写法
- 通过data、methods、watch 等配置选项组织代码逻辑,选项式API写法
- 所有逻辑在setup函数中,使用 ref、watch 等函数组织代码,组合式API写法
计数器案例
选项式API
<template>
计数器:{{ count }} <button @click="increment">累加</button>
</template>
<script>
export default {
data() {
return {
count: 0,
};
},
methods: {
increment() {
this.count++;
},
},
};
</script>
组合式API
<template>
计数器:{{ count }} <button @click="increment">累加</button>
</template>
<script>
// ref 就是一个组合式API
import { ref } from 'vue';
export default {
setup () {
// 计数器
const count = ref(0)
const increment = () => {
count.value ++
}
return { show, toggle, count, increment }
}
};
</script>
总结
- 在setup中通过vue提供的内置函数组合,实现代码功能,是什么方式?
- 组合式API写法
- 组合式API有什么好处?
- 可复用,可维护
- ref 是不是一个组合式API?
- 是
setup函数
setup函数是组合式API的入口函数
-
setup
函数是Vue3
特有的选项,作为组合式API的起点 - 函数中
this
不是组件实例,是undefined
- 如果数据或者函数在模板中使用,需要在
setup
返回
<script>
export default {
// Vue3中,setup函数是所有组合式API的入口函数
// setup函数中的变量和函数,都必须return出来,否则模板中获取不到
// Vue2中的this为Vue组件实例,而Vue3中的this为undifined
setup() {
const msg = "Hello";
const sayHello = () => {
alert("你好,点我干啥?");
};
return {
msg,
sayHello,
};
},
};
</script>
<template>
<h1>{{ msg }}</h1>
<button @click="sayHello">点我</button>
</template>
<style lang="scss" scoped>
</style>
- 总结:在vue3的项目中几乎用不到 this , 所有的东西通过函数获取
setup语法糖
- 使用 setup 有几件事必须做:
- 默认导出配置选项
- setup函数声明
- 返回模板需要数据与函数
每个组件都需要做以上三步,太麻烦,幸好Vue3提供了setup语法糖来帮我们解决这个问题
- 子组件
// HelloWorld.vue
<script setup>
</script>
<template>
<h1>Hello World</h1>
</template>
<style lang="scss" scoped>
</style>
- 父组件
<!--
setup语法糖,简写
1、不需要写默认导出
2、不需要写setup函数
3、不需要将变量、函数return
4、组件不需要注册
-->
<script setup>
// Vue3中,setup函数是所有组合式API的入口函数
// setup函数中的变量和函数,都必须return出来,否则模板中获取不到
// Vue2中的this为Vue组件实例,而Vue3中的this为undifined
import HelloWorld from "./components/HelloWorld.vue";
const msg = "Hello";
const sayHello = () => {
alert("你好,点我干啥?");
};
</script>
<template>
<HelloWorld></HelloWorld>
<h1>{{ msg }}</h1>
<button @click="sayHello">点我</button>
</template>
<style lang="scss" scoped>
</style>
- 总结
- 在 script setup 中声明的变量都可以在模板使用,数据,函数,组件。
- 不需要export default
- 不需要return
reactive函数
通常使用它定义 对象或数组 类型 响应式数据
- 疑问:以前在 data 函数中返回对象数据就是响应式的,现在 setup 中返回对象数据是响应式的吗?
- 不是,需要使用
reactive
转成响应式数据
基本使用
- 从
vue
中导出reactive
函数 - 调用
reactive
函数,传入一个普通对象或数组,返回一个响应式数据对象或数组
<!--
setup语法糖,简写
1、不需要写默认导出
2、不需要写setup函数
3、不需要将变量、函数return
4、组件不需要注册
-->
<script setup>
// Vue3中,setup函数是所有组合式API的入口函数
// setup函数中的变量和函数,都必须return出来,否则模板中获取不到
// Vue2中的this为Vue组件实例,而Vue3中的this为undifined
// 导入组件
import HelloWorld from "./components/HelloWorld.vue";
// reactive只能声明复杂数据类型,不能声明基础数据类型
import { reactive } from "vue";
// reactive声明对象数据
const person = reactive({
name: "张三",
age: 92,
});
const growUp = () => {
person.age++;
};
// reactive声明数组数据
const list = reactive([1, 2, 3]);
// 注意:reactive不能声明基础数据类型
// 控制台警告:value cannot be made reactive: Hello 你好
const msg = reactive("Hello 你好");
</script>
<template>
<HelloWorld></HelloWorld>
<h1>{{ person.name }}</h1>
<h1>{{ person.age }}</h1>
<button @click="growUp">点我长大</button>
<br />
<h2>{{ list }}</h2>
<button @click="list.push(4)">push 4</button>
<br />
<h3>{{ msg }}</h3>
<button @click="msg += '~'">+~</button>
</template>
<style lang="scss" scoped>
</style>
总结
- reactive 函数通常定义:复杂类型的响应式数据
- 可以转换简单数据吗?不能
ref函数
通常使用它定义响应式数据,不限类型
基本使用
- 从
vue
中导出ref
函数 - 调用
ref
函数,传入普通数据(简单or复杂),返回一个响应式数据 - 使用
ref
创建的数据-
script
标签(也就是JS)中需要 .value -
template
标签中要省略.value
-
<script setup>
// 导入组件
import HelloWorld from "./components/HelloWorld.vue";
// ref,能声明所有数据类型
import { ref } from "vue";
// ref声明简单数据类型
const msg = ref("Hello");
const fn = () => {
msg.value += "~";
};
// ref声明复杂数据类型
const person = ref({
name: "张三",
age: 92,
});
const fn2 = () => {
// JS中,修改响应式变量的值,都必须要加.value
person.value.age++;
};
</script>
<template>
<HelloWorld></HelloWorld>
<h1>{{ msg }}</h1>
<button @click="msg += '~'">+~</button>
<button @click="fn">++~</button>
<br />
<h2>{{ person.name }}</h2>
<h2>{{ person.age }}</h2>
<!-- 在template模板中,都不需要加.value -->
<button @click="person.age++">点我长大</button>
<button @click="fn2">点我长大 + 1</button>
</template>
<style lang="scss" scoped>
</style>
总结
-
ref
可以把简单数据或者复杂数据转换成响应式数据 - 注意
-
script
标签(也就是JS)中需要 .value -
template
标签中要省略.value
-
reactive 与 ref 的选择
疑问:定义响应式数据使用ref
还是reactive
呢?
- 对比
-
reactive
可以转换对象成为响应式数据对象,但是不支持简单数据类型。 -
ref
可以转换简单数据类型为响应式数据对象,也支持复杂数据类型,但是操作的时候需要.value
。
-
- 它们各有特点,现在也没有最佳实践,没有明显的界限,所有大家可以自由选择
- 推荐用法
- 推荐:使用ref
<template>
<div>
<h1>{{ form.age }}</h1>
<button @click="form.age++">reactive age++</button>
<h1>{{ form.name }}</h1>
<button @click="form.name += '~'">reactive name+~</button>
<hr>
<h1>{{ data.age }}</h1>
<button @click="data.age++">ref age++</button>
<h1>{{ data.name }}</h1>
<button @click="data.name += '~'">ref name+~</button>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue'
let form = reactive({})
// 重新赋值会导致响应式丢失
form = {
name: 'zs',
age: 18,
}
let data = ref({})
// 重新赋值不会导致响应式丢失
data.value = {
name: 'zs',
age: 18,
}
</script>
<style lang="scss" scoped>
</style>
总结
- 尽量使用
ref
函数支持所有场景,减少心智负担 - 如确定字段为对象,若使用
reactive
,可以省去.value
computed函数
使用 computed 函数定义计算属性
- 作用:与vue2一模一样,根据其它数据计算得到一个新的值
- 语法:
computed( () => 返回一个新的值 )
基本使用
- 从
vue
中导出 computed 函数 - 调用
computed
函数,传入一个回调函数 - 回调函数内计算新的值,并返回结果
<script setup>
// 导入computed函数
import { computed, ref } from "vue";
const score = ref();
const scoreList = ref([50, 80, 100, 90, 70, 60]);
// 需求:定义三个计算属性,
// 不及格 < 60
const failList = computed(() => {
return scoreList.value.filter((item) => item < 60);
});
// commonList 及格 >=60 且 <90
const commonList = computed(() => {
return scoreList.value.filter((item) => item >= 60 && item < 90);
});
// 优秀 > 90
const betterList = computed(() => {
return scoreList.value.filter((item) => item > 90);
});
// 处理点击事件
const handleClick = () => {
if (score.value === undefined || score.value === "") {
alert("请输入内容");
return;
}
// 添加输入框的值到数组中
scoreList.value.push(score.value);
// 清空输入框
score.value = "";
};
</script>
<template>
<div>
<input
type="text"
@keyup.enter="handleClick"
v-model.number="score"
/><button @click="handleClick">添加</button>
<hr />
<p>不及格: {{ failList }}</p>
<p>及格: {{ commonList }}</p>
<p>优秀:{{ betterList }}</p>
</div>
</template>
watch函数
使用watch函数监听数据的变化
-
功能
- 使用 watch 监听一个基础数据类型
- 使用 watch 监听一个复杂数据类型,配置深度监听
- 使用 watch 监听,配置立即执行
- 使用 watch 同时监听多个响应式数据
- 使用 watch 只监听对象数据中的某一个属性(简单)
- 使用 watch 只监听响应式对象数据中的一个属性(复杂),配置深度监听
-
语法
- watch(基本数据,(newValue, oldValue) => { })
- watch(复杂数据,(newValue, oldValue) => { }, {deep: true, immediate: true})
- watch( [数据1, 数据2], (newValue, oldValue) => { })
- watch( () => 复杂数据.属性名, (newValue, oldValue) => { } )
基本使用
<script setup>
// 导入watch函数
import { ref, watch } from "vue";
const count = ref(0);
const user = ref({
name: "jack",
info: {
gender: "男",
age: 18,
},
});
// 监听基础数据类型
watch(count, (newValue, oldValue) => {
console.log(`newValue => ${newValue}`);
console.log(`oldValue => ${oldValue}`);
});
// 监听复杂数据类型
// watch(
// user,
// (newValue, oldValue) => {
// console.log(`newValue => `, newValue);
// console.log(`oldValue => `, oldValue);
// },
// {
// // 深度监听
// deep: true,
// // 初始化时,就立即执行回调函数
// immediate: true,
// }
// );
// 同时监听多个数据的变化
// 注意:如果其中一个数据是复杂数据类型,还是要加上deep深度监听
// watch(
// [count, user],
// (newValue) => {
// console.log(`newValue => `, newValue);
// },
// { deep: true }
// );
// 深度监听,需要遍历,如果只想其中一个属性,就会性能浪费,Vue3可以单独精准监听对象某个属性的变化
// 注意:如果精准监听的数据也是复杂数据类型,还是要加上deep深度监听
watch(
// 监听哪个属性
() => user.value.info.age,
// 属性变化时,回调函数
(newValue) => {
console.log(`newValue => `, newValue);
}
);
</script>
<template>
<div>计数器:{{ count }} <button @click="count++">count++</button></div>
<div>
<p>姓名:{{ user.name }}</p>
<p>性别:{{ user.info.gender }}</p>
<p>年龄:{{ user.info.age }}</p>
<button @click="user.name += '~'">对象改名字</button>
<button @click="user.info.age++">对象改年龄</button>
</div>
</template>
总结
-
watch(需要监听的数据,数据改变执行函数,配置对象)
,来进行数据的侦听 - 数据
- 单个数据
- 多个数据
- 对象的某个属性,函数返回对象属性
- 属性为复杂数据类型,需要开启深度监听
- 配置对象
- deep:深度监听
- immediate:侦听器在初始化时,立即执行一次回调函数
生命周期函数
vue3的常用生命周期函数
使用步骤
先从vue中,导入以on打头的生命周期钩子函数
在setup函数中,调用生命周期函数并传入回调函数
生命周期钩子函数,可以调用多次
Vue3和vue2的生命周期对比
选项式API下的生命周期函数使用 | 组合式API下的生命周期函数使用 |
---|---|
beforeCreate | 不需要(直接写到setup函数中) |
created | 不需要(直接写到setup函数中) |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroyed | onBeforeUnmount |
destroyed | onUnmounted |
activated | onActivated |
deactivated | onDeactivated |
基本使用
<script setup>
// 导入生命周期函数
import { onBeforeUpdate, onMounted, onUpdated, ref } from "vue";
// 定义响应式变量
const count = ref(0);
// 注意:生命周期,都加上了on开头
// Vue2中,发送请求推荐在created中,而Vue3推荐在onMounted中
onMounted(() => {
console.log("onMounted,挂载后 -----> ");
// 挂载后,能获取到DOM元素
const box = document.querySelector("#app");
console.log(box);
});
onBeforeUpdate(() => {
console.log("更新前 -----> ");
});
onUpdated(() => {
console.log("更新后 -----> ");
});
</script>
<template>
<div class="box"></div>
<span>{{ count }}</span> <button @click="count++">+1</button>
<div>生命周期函数</div>
</template>
总结
- Vue2发请求,推荐生命周期:created
- Vue3发请求,推荐生命周期: onMounted
语法:钩子函数(() => {})
使用ref获取DOM元素
元素上使用 ref属性关联响应式数据,获取DOM元素
使用步骤
- 创建 ref:
const xxxRef = ref()
- 绑定
ref
属性到标签上:ref="xxxRef"
- 通过
xxxRef.value
访问dom
基本使用
<script setup>
// 导入ref函数
import { ref } from "vue";
// 调用ref,定义响应式变量
const inputRef = ref();
const fn = () => {
// 通过响应式变量,获取DOM元素实例,就可以调用DOM元素的实例方法
inputRef.value.focus();
};
</script>
<template>
<!-- 绑定ref到标签上 -->
<input type="text" ref="inputRef" />
<button @click="fn">获取焦点</button>
</template>
父子组件通信
ref操作组件-defineExpose
使用步骤
准备父组件、子组件
-
父组件内:
- 创建
ref
- 绑定
ref
到子组件标签上 - 通过
ref.value
访问子组件实例
- 创建
子组件内, 调用
defineExpose()
编译器宏函数,无需导入,暴露数据和方法-
解释
- 使用
<script setup>
的组件是默认关闭的,父组件拿不到子组件的数据和函数 - 需要配合
defineExpose
向外暴露数据,暴露的响应式数据会自动解除响应式
- 使用
父传子-defineProps函数
父组件,向子组件传参
使用步骤
- 父组件,声明数据
- 父组件,传递数据给子组件
- 子组件通过
defineProps
,接收props
- 子组件渲染父组件传递的数据
子传父-defineEmits函数
子组件,发送自定义事件,传参给父组件
使用步骤
- 子组件通过
defineEmits
,获取emit
函数(因为没有this) - 子组件通过
emit
触发事件,并且传递数据 - 父组件,
v-on
或者@
,监听自定义事件
语法
const emit = defineEmits(["自定义事件名1", "自定义事件名2"])
emit("自定义事件名",值)
综合案例
子组件
<script setup>
// 通过 defineProps 编译器宏函数,定义子组件的参数
// 方式一:数组定义参数列表
// defineProps(["name", "money", "car"]);
// 方式二:对象定义参数列表和参数类型
const props = defineProps({
name: String,
money: Number,
car: String,
});
// 通过 defineProps 函数的返回值,获取参数值
console.log(`name => ${props.name}`);
console.log(`money => ${props.money}`);
console.log(`car => ${props.car}`);
// 通过 defineEmits 编译器宏函数,定义事件
// 虽然可以不传参数,但推荐传入事件名称,在发送事件时,会有代码提示
// const emit = defineEmits();
const emit = defineEmits(["addMoney", "costMoney"]);
</script>
<template>
<div>
<h3>我是子组件</h3>
<div>
金钱:{{ money }} <br />
跑车:{{ car }}
</div>
<button @click="emit('addMoney', 10)">吞金兽赚钱啦</button>
<button @click="emit('costMoney', 50)">吞金兽想花钱</button>
</div>
</template>
父组件
<script setup>
import { ref } from "vue";
import Child from "./components/Child.vue";
// 声明响应式变量
const money = ref(100);
const car = ref("玛莎拉蒂");
// 事件处理函数
const fn = (item) => {
money.value -= item;
};
const fn2 = (item) => {
money.value += item;
};
</script>
<template>
<div>
<h1>我是父组件</h1>
<div>金钱:{{ money }}</div>
<div>车辆:{{ car }}</div>
<hr />
<!-- 通过 v-bind 绑定数据给子组件 -->
<!-- 父组件,监听子组件发出的自定义事件 -->
<Child
:money="money"
:car="car"
name="zs"
@costMoney="fn"
@addMoney="fn2"
/>
</div>
</template>
跨组件传参-provide和inject
通过provide和inject函数可以简便的实现跨级组件通讯
案例
祖先组件
<script setup>
// 跨组件通信-依赖注入
// 注意:不是一定只能爷孙关系才能使用provide和inject,而是所有组件都可以
// 导入 provide 函数,用于提供数据
import { ref, provide } from "vue";
import ParentCom from "./components/ParentCom.vue";
// 声明响应式变量
const count = ref(99);
// 声明修改变量的函数
const addFn = () => {
count.value++;
};
// 提供数据给子孙组件(存数据)
provide("data", count);
provide("addFn", addFn);
</script>
<template>
<div
class="app-page"
style="border: 10px solid #ccc; padding: 50px; width: 600px"
>
app 组件 {{ count }}
<ParentCom />
</div>
</template>
父级组件
<script setup>
import ChildCom from "./ChildCom.vue";
</script>
<template>
<div class="parent-page" style="padding: 50px">
parent 组件
<hr />
<ChildCom />
</div>
</template>
子级组件
<script setup>
// 导入inject函数,通过它获取数据
import { inject } from "vue";
// 获取爷级组件提供的数据(取数据)
const data = inject("data");
// 获取爷组件提供的函数,通过函数修改爷级组件
const addFn = inject("addFn");
console.log(addFn);
</script>
<template>
<div class="child-page" style="padding: 50px; border: 10px solid #ccc">
child 组件接收到count: {{ data }} <button @click="addFn">修改count</button>
</div>
</template>
总结
-
provide
和inject
是解决跨级组件通讯的方案- provide 提供后代组件需要依赖的数据或函数
- inject 注入(获取)provide提供的数据或函数
- 官方术语:依赖注入
- App是后代组件
依赖
的数据和函数的提供者
,Child是注入
(获取)了App提供的依赖
保持响应式-toRefs函数
在使用reactive,创建的响应式数据,被展开或解构的时候,使用toRefs保持响应式
步骤
- 解构响应式数据,踩坑
- 使用
toRefs
处理响应式数据,解决问题
<script setup>
import { ref, toRefs } from "vue";
// 定义响应式数据
let user = ref({ name: "tom", age: 18 });
// 默认:解构ref或reactive,会导致响应式丢失
// 虽然视图能显示第一次的数据,但后续修改数据,数据不会驱动视图
// const { name, age } = user.value;
// 作用:把对象中的每一个属性,做一次包装,成为响应式数据
const { name, age } = toRefs(user.value);
</script>
<template>
<div>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }} <button @click="age++">一年又一年</button></p>
</div>
</template>
总结
- toRefs 函数的作用,与使用场景
- 作用:把对象中的每一个属性,做一次包装,成为响应式数据
- 场景:解构响应式数据的时候使用
- 一句话总结:当去解构响应式对象,使用
toRefs
保持响应式
Vue3中v-model语法糖的变化
vue3中v-model语法糖原理
- vue2中,v-model 语法糖 完整写法?
- 原生标签,
:value="count" 和 @input="count=$event.target.value"
- 组件标签,
:value="count" 和 @input="..."
- 原生标签,
- vue3中,v-model 语法糖 完整写法?
- 原生标签,
:value="count" 和 @input="count=$event.target.value"
- 组件标签,
:modelValue="count" 和 @update:modelValue="..."
- 原生标签,
为什么变了?
vue3 中 组件标签上,可以使用多个
v-model
语法糖,而在Vue2中,v-model只能出现1个,多个时需要使用.sync
修饰符的方式来解决单个
v-model
<com-a v-model="count"></com-a>
<!-- 等价 -->
<com-a :modelValue="count" @update:modelValue="count=$event"></com-a>
- 多个
v-model
<com-a v-model="count" v-model:msg="str"></com-a>
<!-- 等价 -->
<com-a :modelValue="count" @update:modelValue="..." :msg="str" @update:msg="..." />
案例
- 父组件中,点击一个按钮,显示弹窗子组件
- 点击弹窗子组件右上角的X关闭按钮,关闭弹窗
弹窗子组件
<!-- 弹窗子组件 -->
<script setup>
// 定义子组件的参数
defineProps({
modelValue: Boolean,
});
// 定义事件
const emit = defineEmits(["update:modelValue"]);
</script>
<template>
<div class="box" v-show="modelValue">
<div class="container">
<!-- 点关闭按钮时,发送事件,通知父组件关闭弹窗 -->
<div class="header" @click="emit('update:modelValue', false)">x</div>
<div class="content">
<span>我是弹窗内容</span>
</div>
</div>
</div>
</template>
<style scoped>
.box {
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.3);
position: fixed;
left: 0;
top: 0;
}
.container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 400px;
height: 300px;
background-color: #fff;
border-radius: 6px;
}
.header {
float: right;
margin-right: 10px;
color: #000;
}
.content {
width: 100%;
height: 100%;
color: #000;
display: flex;
justify-content: center;
align-items: center;
}
</style>
父组件
<script setup>
// v-model在Vue3中的变化
// 1、在原生HTML标签上,v-model的原理没有变化,v-model会转换为 :value="值" 和 @input="事件处理函数"
// 2、在组件标签上,v-model有变化,v-model会转换为:modelValue 和 @update:modelValue
/*
问:v-model的原理?
答:v-model是语法糖,底层会将v-model转化为2个指令, v-bind:value="值" 和 @input="事件处理函数"
*/
import { ref } from "vue";
// 导入弹窗子组件
import MyDialog from './components/MyDialog.vue'
const msg = ref("Hello");
// 是否显示弹窗
const visiable = ref(false)
const handleCloseDialog = (event) => {
visiable.value = event
}
// 打开弹窗
const open = () => {
visiable.value = true
}
</script>
<template>
<!-- 使用v-model,实现双向绑定 -->
<input type="text" v-model="msg" />
<hr />
<!-- 不使用v-model,实现双向绑定 -->
<input type="text" :value="msg" @input="msg = $event.target.value" />
<br>
<br>
<!-- <button @click="visiable = true">显示弹窗</button> -->
<button @click="open">显示弹窗</button>
<!-- 弹窗子组件 -->
<!-- Vue3中,v-model会转化为,:modelValue 和 @update:modelValue -->
<!-- <MyDialog v-model="visiable"></MyDialog> -->
<!-- <MyDialog :model-value="visiable" @update:model-value="visiable = $event"/> -->
<MyDialog :model-value="visiable" @update:model-value="handleCloseDialog"/>
</template>
总结
- vue3 中
v-model
语法糖,相当于哪2个指令?- 答:
:modelValue="count" 和 @update:modelValue="count=$event"
- 答:
- vue3中,如果要实现
xxx
属性的双向绑定,使用v-model
来实现,它的底层会映射为?- 答:
:xxx="count" 和 @update:xxx="count=$event"
- 答:
- 总结:vue3中只需要
v-model
指令,就可以支持多个数据在父子组件同步