Vue3

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优点和特点

  • 首次渲染更快
  • diff 算法更快
  • 内存占用更少
  • 打包体积更小
  • 更好的 Typescript 支持
  • Composition API 组合 API

vite 构建工具

vite(法语意为 "快速的",发音 /vit/,发音同 "veet") 是一种新型前端构建工具,能够显著提升前端开发体验

  • 对比 webpack:
    • 需要查找依赖,打包所有的模块,然后才能提供服务,更新速度会随着代码体积增加越来越慢
image.png
  • vite 的原理:
    • 使用原生 ESModule 通过 script 标签动态导入,访问页面的时候加载到对应模块编译并响应
image.png
- 注:项目打包的时候最终还是需要打包成静态资源的,打包工具 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>

总结

  • provideinject是解决跨级组件通讯的方案
    • 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指令,就可以支持多个数据在父子组件同步
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容