一.Vue2->Vue3

如果有Vue2的基础,并在此基础上学习Vue3,并不需要把完整的官网看完,我们只需要关注一下新功能和非兼容的变化即可进行开发.

二.Vue3变化

  • 统一元素上使用的v-if和v-for优先级已更改,但不推荐同时使用v-if和v-for
  • 组件事件需要在emits选项中声明
  • destroyed生命周期选项被重命名为unmounted
  • beforeDestroy生命周期选项被重命名为beforeUnmount
  • 自定义指令的API已更改为组件生命周期一致
  • 新增了三个组件:Fragment支持多个根节点  Suspense可以在组件渲染之前的等待时间显示指定内容  Teleport可以让子组件能够在视觉上跳出父组件(如父组件overflow:hidden)
  • 新增指令v-memo,可以缓存html模板,比如v-for列表不会变化的就缓存,简单说就是用内存换时间.
  • 用Proxy代替Object.defineProperty重构了响应式系统,可以监听到数组下标变化,及对象新增属性,因为监听的不是对象属性,而是对象本身,还可拦截apply has等13种方法
  • 重构了虚拟DOM, 在编译时会将事件缓存,将slot编译为lazy函数,保存静态节点直接复用(静态提升),以及添加静态标记,Diff算法使用最长递增子序列优化了对比流程,使得虚拟DOM生成速度提升200%
  • 支持在<style></style>里使用v-bind,给CSS绑定JS变量(color:v-bind(str))
  • 新增Composition API可以更好的逻辑复用和代码组织,同一功能的代码不至于像以前一样分散,虽然Vue2可以用mixins来实现复用代码,但也存在问题,比如方法或属性名会冲突,代码来源也不清楚等
  • 全局函数set和delete以及实例方法$set和$delete移除.基于代理的变化检测以及不再需要它们了
  • 毕竟Vue3是用TS写的,所以对TS的支持度更好
  • Vue3不兼容IE11
  • $on,$off和$once实例方法已被移除,组件实例不再实现事件触发接口

三.组合式API

原有的组件选项(datacomputedmethodswatch) 的方式来组织组件代码通常是非常有效的,但是也存在一些不好的地方,例如把原有的关联逻辑按照选项划分开来,掩盖了原有潜在的逻辑问题,这个时候我们就必须要不断地上下滚动代码来找到响应的代码块来查找,这样带来了极大的不便,特别是一开始没有编写过这组件的人来说,这导致组件难以阅读和理解。

所以针对上述的情况,Vue3提出了新的组织组件代码的方式---组合式API。组合式API需要一个可以实际使用的地方,那就是setup

setup的触发时机是在组件创建之前执行的。需要注意,在setup中你应该避免使用this,因为这个时候this并不代表组件实例。setup的调用发生在datacomputedmethods被解析之前,所以它们没法在setup中被获取。

当然,我们依然可以在Vue3使用选项(Option API)的方式来组织代码,这个和Vue2没有区别,但是不建议这样写。

Vue3.x组件的选项(Option API)写法(不建议)

<script>
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup() {},
mounted() {
console.log('生命周期mounted')
},
components: {},
methods: {},
watch: {},
comuted: {}
});
</script>

Vue3.x组合式写法(推荐)

<script>
import { defineComponent, ref, onMounted } from 'vue';
export default defineComponent({
setup(){
let num = ref(0);
let fn = () => {};
onMounted(():void => {
console.log('生命周期mounted');
});
return {
num,
fn
}
}
})
</script>

下面例子代码都是在Typescript的环境下进行的,所以需要Typescript基础

3.1.生命周期

通过在生命周期钩子前面加上"on"来访问组件的生命周期钩子.

下表包含如何在 Option API 和 setup() 内部调用生命周期钩子

Option API setup
beforeCreate -
created -
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
activated onActivated
deactivated onDeactivated

因为 setup 是在 beforeCreatecreated 生命周期钩子前运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

<template>
<div id="div">123</div>
</template> <script lang="ts">
import { defineComponent, onMounted } from "vue";
export default defineComponent({
setup() {
console.log("setup");
onMounted((): void => {
console.log("onMounted");
console.log(document.getElementById('div')?.innerHTML);
});
},
});
</script>

3.2.ref,reactive,toRefs响应式和methods

Vue2.x默认写在data的值,初始化的时候内部会完成值的数据的响应式(getset绑定),但是Vue3要手动调用内置方法实现,那么接下来看看常用的实现数据响应式方法都有哪些。

ref不仅可以用在数据的响应式,还可以绑定DOM元素

<template>
<div>
<div>{{ num }}</div>
<button @click="add1">num++</button>
<p>-------------------------------</p>
<div>{{ state.count }}</div>
<button @click="add2">state.count++</button>
<p>-------------------------------</p>
<div>{{ count }}</div>
<button @click="add3">count++</button>
</div>
</template> <script lang="ts">
import { defineComponent, ref, reactive, toRefs } from "vue";
export default defineComponent({
setup() {
interface ObjItf {
count: number;
} // ref声明响应式数据,用于声明基本数据类型
let num = ref<number>(1); let obj = {
count: 1,
}; // reactive声明响应式数据,用于声明引用数据类型
let state = reactive<ObjItf>(obj); // toRefs解构响应式数据
let { count } = toRefs<ObjItf>(state); const add1 = (): void => {
num.value++; // 注意通过ref声明的变量,所以js要修改对应的值是要通过.value访问才可以,template模板不需要通过.value访问
}; const add2 = (): void => {
state.count++; // 通过reactive声明的遍历,不需要通过.value访问值
}; const add3 = (): void => {
count.value++; // 通过toRefs结构的值和ref声明的变量一样,需要通过.value访问其值
}; return {
num,
state,
count,
add1,
add2,
add3
};
},
});
</script>

注意:

  1. reactive可以传递基础数据类型和引用数据类型,基础数据类型不会被包装成响应式数据
  2. reactive返回的响应式数据本质是Proxy对象,对象里面每一层都会被包装成Proxy对象
  3. reactive返回的响应式数据和原始的数据会相互影响
  4. ref可以传递基础数据类型和引用数据类型,如果是基础数据类型,那么这个基础数据值保存在返回的响应式数据的.value上,如果是对象,响应式数据在.value上.
  5. ref本质是将一个数据变成一个对象,这个对象具有响应式特点

为什么需要toRefs和toRef?

和ref不一样的是,toRef和toRefs这两个方法,它们不创造响应式,而是延续响应式.创造响应式一般由ref和reactive来解决,而toRef和toRefs则把对象的数据进行分散和扩散,其这个对象针对的是响应式对象(reactive)而非普通对象.

3.3.watch

语法:watch(监听源 | [多个],(val,oldVal)=>{},{immediate?:false,deep:false})

watch写法上支持一个或者多个监听源,这些监听源必须只能是getter/effect函数,ref数据,reactive对象,或者是数组类型(只能是getter/effect函数,ref数据,reactive对象)

import { watch } from 'vue'
//情况一:监视ref定义的响应式数据
watch(sum,(newValue,oldValue)=>{
console.log('sum变化了',newValue,oldValue)
},{immediate:true}) //情况二:监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变化了',newValue,oldValue)
}) /* 情况三:监视reactive定义的响应式数据
若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
*/
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效 //情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true}) //情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true}) //特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效

3.4.watchEffect

它立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

watch的套路是:既要指明监视的属性,也要指明监视的回调。

watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。

两者都可以监听 data 属性变化。

watchEffect有点像computed:

但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。

而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。

<script>
let num = ref(0);
watchEffect(() => {
console.log(num.value);
});
setTimeout(() => {
num.value++;
}, 1000);
</script>

watch 和 watchEffect的区别

  • 两者都可以监听 data 属性变化;

  • watch 需要明确监听哪个属性

  • watchEffect 会根据其中的属性,自动监听其变化。

3.5. computed

<template>
<div>{{ count }}</div>
<button @click="state.count++">state.count++</button>
</template>
<script lang="ts">
import { defineComponent, reactive, computed } from "vue";
export default defineComponent({
setup() {
interface ObjItf {
count: number;
} let obj = {
count: 1,
arr: [1, 2, 3]
}; const state = reactive<ObjItf>(obj); let count = computed((): number => {
return state.count;
}); return {
count,
state
};
},
});
</script>

3.6.组件

全局组件:

const app = Vue.createApp({...})

app.component('my-component-name', {
/* ... */
})

局部组件(子传父,父传子):

子组件mychild.vue:

<template>
<div>{{ aname }}</div>
</template> <script lang="ts">
import { defineComponent, toRefs } from "vue";
export default defineComponent({
props: {
aname: {
type: String,
default: "张三",
},
},
emits: ["uname"],
setup(props, { emit }) {
let { aname } = toRefs(props);
const updateName = () => {
emit('uname', 'a改变后的名字')
};
return {
aname,
updateName,
};
},
});
</script>

父组件:

<template>
<mychild :aname="aname" @uname="updateName"></mychild>
</template> <script lang="ts">
import { defineComponent, toRefs, ref } from "vue";
import mychild from './mychild.vue';
export default defineComponent({
components: {
mychild
},
setup() {
let aname = ref('李四');
const updateName = (p: string) => {
aname.value = p;
}
return {
aname,
updateName
}
}
})
</script>

setup语法糖组件:

子组件mychild.vue:

<!--  -->
<template>
<div>{{ aname }}</div>
<button @click="updateName">修改姓名</button>
</template> <script lang="ts" setup>
// setup语法糖下defineProps,defineEmits 不需要引入
let emit = defineEmits(["updatename"]);
let props = defineProps({
aname: {
type: String,
default: "李四",
},
});
const updateName = (): void => {
emit('updatename', '修改后的名字')
};
</script> <style lang="less" scoped></style>

父组件:

<!--  -->
<template>
<div></div>
<mychild :aname="aname" @updatename="updateName"></mychild>
</template> <script lang="ts" setup>
import { ref } from "vue";
import mychild from "./b.vue";
let aname = ref("张三");
let updateName = (name: string): void => {
aname.value = name;
};
</script> <style lang="less" scoped></style>

3.7.v-model

子组件:

<!--  -->
<template>
<div>{{ name }}{{ age }}</div>
<button @click="updateName">修改姓名</button>
</template> <script lang="ts" setup>
let props = defineProps({
age: Number,
name: String,
});
let emits = defineEmits(["update:name", "update:age"]);
let updateName = () => {
emits("update:age", 30);
emits("update:name", "李四");
};
</script> <style lang="less" scoped></style>

父组件:

<!--  -->
<template>
<div></div>
<mychild v-model:name="state.name" v-model:age="state.age"></mychild>
</template> <script lang="ts" setup>
import { reactive } from "vue";
import mychild from "./b.vue";
let info = {
name: '张三',
age: 20
}
let state = reactive(info); </script> <style lang="less" scoped></style>

3.8.插槽

子组件:

<!--  -->
<template>
<slot></slot>
<slot name="title"></slot>
<slot name="footer" :user="state.user" :d="state.d"></slot>
</template> <script lang="ts" setup>
import { reactive } from "vue";
let state = reactive({
user: { a: 1, b: 2 },
d: 1,
});
</script> <style lang="less" scoped></style>

父组件:

<!--  -->
<template>
<div></div>
<mychild>
<div>匿名插槽</div>
<template #title>
<div>具名插槽</div>
</template>
<template #footer="scope">
<div>作用域插槽{{ scope.user }} {{ scope.d }}</div>
</template>
</mychild>
</template> <script lang="ts" setup>
import mychild from "./child.vue";
</script> <style lang="less" scoped></style>

3.9.await支持

不必再配合async就可以直接使用await了,这种情况下,组件的setup会自动变成async setup.

<script setup>
const post = await fetch('/api').then(() => {})
</script>

如果浏览器报了这个错误的话

在app组件的模板最外层嵌套<Suspense>标签

App.vue:

3.10.style scoped

<style scoped>
/* 修改第三方组件样式 */
::v-deep(.foo) {}
/* 简写 */
:deep(.foo) {} /* 修改插槽内容样式 */
::v-slotted(.foo) {}
/* 简写 */
:slotted(.foo) {} /* 修改全局样式 */
::v-global(.foo) {}
/* 简写 */
:global(.foo) {}
</style>

3.11.teleport

这个组件可以把组件进行传送,to属性就是要传送的位置目标,用css选择器

<template>
<div class="header"></div>
<teleport :to="target">
<p>哈哈哈哈哈</p>
</teleport>
<button @click="changeTarget">点击切换</button>
<p>-------------------------------</p>
<div class="footer"></div>
</template> <script lang="ts" setup>
import { ref } from "vue";
let target = ref<string>(".header");
const changeTarget = (): void => {
target.value = ".footer";
};
</script> <style lang="less" scoped></style>

Vue2到Vue3的改变的更多相关文章

  1. Vue2和Vue3技术整理1 - 入门篇 - 更新完毕

    Vue2 0.前言 首先说明:要直接上手简单得很,看官网熟悉大概有哪些东西.怎么用的,然后简单练一下就可以做出程序来了,最多两天,无论Vue2还是Vue3,就都完全可以了,Vue3就是比Vue2多了一 ...

  2. vue2和vue3生命周期的区别

    概念 首先,我们了解一下"生命周期"这个词.通俗的来说,生命周期就是一个事务从出生到消失的过程.例如,一个人从出生到去世.在vue中,vue的生命周期是指,从创建vue对象到销毁v ...

  3. 盘点Vue2和Vue3的10种组件通信方式(值得收藏)

    Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异:本文将通过选项式API 组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式.其中将要实现的通信 ...

  4. Vue2 到 Vue3,重温这 5 个常用的 API

    距离Vue3发布已经过去一年多时间了,从Vue2到Vue3是一个不小的升级,包括周边生态等.虽然目前大多数开发者们在使用的仍旧以Vue2为准,但Vue3显然是Vue开发者们未来必须面对的,而且前不久V ...

  5. vue2和vue3区别

    1. vue2和vue3双向数据绑定原理发生了改变 vue2的双向数据绑定是利用了es5 的一个API Object.definepropert() 对数据进行劫持 结合发布订阅模式来实现的.vue3 ...

  6. vue2升级vue3:vue2 vue-i18n 升级到vue3搭配VueI18n v9

    项目从vue2 升级vue3,VueI18n需要做适当的调整.主要是Vue I18n v8.x 到Vue I18n v9 or later 的变化,其中初始化: 具体可以参看:https://vue- ...

  7. vue2升级vue3指南(二)—— 语法warning&error篇

    本文总结了vue2升级vue3可能会遇到的语法警告和错误,如果想知道怎样升级,可以查看我的上一篇文章:vue2升级vue3指南(一)-- 环境准备和构建篇 Warning 1.deep /deep/和 ...

  8. vue2升级vue3:Vue Demij打通vue2与vue3壁垒,构建通用组件

    如果你的vue2代码之前是使用vue-class-component 类组件模式写的.选择可以使用 https://github.com/facing-dev/vue-facing-decorator ...

  9. vue2和vue3的区别?

    vue2和vue3的主要区别在于以下几点: 1.生命周期函数钩子不同 2.数据双向绑定原理不同 3.定义变量和方法不同 4.指令和插槽的使用不同 5.API类型不同 6.是否支持碎片 7.父子组件之间 ...

  10. vue2升级vue3:vue-i18n国际化异步按需加载

    vue2异步加载之前说过,vue3还是之前的方法,只是把 i18n.setLocaleMessage改为i18n.global.setLocaleMessage 但是本文还是详细说一遍: 为什么需要异 ...

随机推荐

  1. Pytorch-UNet-master>utils>data_loading.py

    模块,包   在package_runoob同级目录下,用test.py调用package_runoob包中内容 参考链接: Python 模块 | 菜鸟教程 (runoob.com) Dataset ...

  2. 发布订阅者模式 -- 简单的PubSub

    /** * 发布订阅者模式 * **/interface handle { [propName: string]: Function[]}class PubSub { private handles: ...

  3. Codeforces Round #857 Div.1/Div.2 CF1801/1802 2A~2F 题解

    点我看题(Div2) Div 2A. Likes 如果要赞最多,肯定是先放所有的点赞,再放所有移除的操作.如果要最少,那就先把赞分成两种:最后被移除的和没被移除的:最后先放所有被移除的,放一个移除一个 ...

  4. Python学习笔记--高阶技巧

    闭包(避免全局变量被修改的风险) 函数的嵌套的利用 若是只是调用到外部函数的值,只需要用到函数的嵌套,具体实现如下: 若是要对外部函数的值进行修改,需要用到nonlocal关键字,具体实现如下: at ...

  5. MySQL学习(五)事务

    参考博客:https://www.cnblogs.com/kismetv/p/10331633.html 0.提交和回滚 注:mysql默认自动开启了事务. -- 手动开启事务 start trans ...

  6. 穷人版生产力工具,好用得飞起 「GitHub 热点速览」

    被 GPT 和 OpenAI 刷屏了一个多月,现在 GitHub Trending 已经没有什么和 gpt 无关的项目了,但是好在总有优秀的开源项目拯救我的项目疲惫.像是贴心好用的反向代理 pgrok ...

  7. 2023GDKOI总结

    2023GDKOI总结 说明:不是GD选手,只是因为来zsjz集训就顺便参加了GDKOI,不过也不参与GD选手排名. 考前看了看GDKOI2021的题,当时是考了3天,每天4题,而里面只有一道题是我一 ...

  8. [C++STL教程]7.priority_queue优先队列入门学习!零基础都能听懂的教程

    不知不觉C++STL教程系列已经第7期了.之前我们介绍过:vector, queue, stack, set, map等等数据结构. 今天我们来学习一个新的stl容器:priority_queue优先 ...

  9. 最新版 Harbor 在ubuntu系统上安装

    最新版 Harbor 在ubuntu系统上安装 The latest version of Harbor is installed on the ubuntu system 安装docker Inst ...

  10. [Windows/CMD]不重启设置/刷新环境变量

    1 文由 当我已经通过如下路径设置了Maven的环境(maven-3.5.4). "我的电脑"->"属性"->"高级"-> ...