天天看点

Vue3的新特性分析

总结:1.如何初始化vue3项目 2.支持多个根节点 3.入口文件mian.js的改变4.更好的typescript支持 5.自动的tree-shaking 6.传送门teleport 7. composition api 8.setup 9.ref 9.reactive 10.readonly 11.computed 12.watch 13.watchEffect 14.生命周期钩子 15.依赖注入 16.refs 17.代码组织 18.逻辑复用

方法一:通过vue-cli直接创建,要求脚手架的版本最新

方法二:在一个初始化的vue2项目中,在终端中使用命令vue add vue-next来升级为vue3项目。(注意是在一个初始化的vue2项目,不要瞎搞)

我们都知道在vue2中只允许在template中定义一个根节点,否则会出现The template root requires exactly one element的提示报错并导致编译错误。

在vue3中我们可以在template里写上多个根节点,这个特性在vue3中得以展现。

实现原理(DocumentFragment)可参考官网

我们先来看在vue2中main.js的代码引入:

我们再来看在vue3中main.js的代码引入:

vue3因为有着typescript的支持,配合vscode可以在编写代码中有着十分好的类型提示,开发体验十分之好

在vue3中自动会对打包构建的代码进行tree-shaking。对用户来说不需要的vue功能不用打包进去了会优化最终的文件体积;对框架开发者来说可以提供更多的功能也不用的担心会导致用户的文件体积变大(在用户不使用该功能的情况下)

vue3新增了传送门teleport的概念,我们来看一个业务场景(在vue2下),并使用vue3的传送门来解决这个业务场景,官方的演示地址

在vue2中因为只允许有一个根节点,代码书写如下:(注意在下方的代码中根节点#app的style="position: relative;")

我们在子组件ModalButton中想要实现一个基于窗口居中的弹框,代码书写如下:(注意这里的.modal的style="position: absolute;")

于是我们得到了基于根节点#app的居中弹框,这不是我们想要的结果,我们是想要基于窗口(这里可以是body)的居中弹框啊!!!,效果图如下:

Vue3的新特性分析

紧接着该请出我们vue3的新特性传送门(teleport)了,下面是用vue3重写在子组件ModalButton中的代码,参考如下:

使用了传送门 <teleport to="body"> </teleport>后的效果图如下,我们实现了基于body的居中弹窗:

Vue3的新特性分析

我们需要注意使用 <teleport to="body"> </teleport>后在渲染的时候并不是渲染到当前引入这个组件的组件内而是渲染到指定的element内

我们还需要注意teleport是可以在一个页面组件中使用多次的,但是我们在使用多个teleport时需要注意它们之间的z-index关系,这是一个添加的操作。

我们更需要注意如果我在teleport中加入子组件,此时这个子组件的父组件是谁呢?在传送门内定义的子组件的父组件是属于引入这个组件的组件,在这里我在组件ModalButton中使用了teleport,如果这个teleport中还定义了子组件,那么这个在teleport中定义的子组件的父组件是ModalButton!!!

Vue3的新特性分析

接着才是vue3的重头戏,那就是composition api。这可以说是完全颠覆了使用vue的逻辑书写习惯,为了不引入全新的概念,采用独立的函数来创建和监听响应式的状态等

我们不必担心在vue2的options api会不能在vue3中使用,它和vue3的composition api是兼容共存的,但是建议统一风格的写法。

接下来将composition api的每个api单独做为一个大标题来书写:

Vue3的新特性分析

setup是初始化的入口,代表着当前组件初始化composition api的入口,在setup函数中return的对象中的值可以直接写在template中,但是注意如果不做任何处理,直接用返回的值在template中书写的话,这个值不是响应式数据!!!

我们来看一下setup入口的运用,代码参考如下:

在setup函数中return的对象中的值可以直接写在template中,注意这里返回的得是一个对象,如果你返回一个函数的话,实际上是调用render函数返回一个vnode.

因为在data内可以获取到setup返回的值,所以可以直接在template中使用这个返回的值。

注意这里的在setup函数中的this是获取不到的,因为setup函数的执行比beforeCreate和created调用的时间都要早,它是在options api之前调用的,自然获取不到

setup的第一个参数是props,(即setup(props){})可以获取父组件传递过来的props,当然这个由父组件传递过来的props是不可以在子组件中修改的,是只读的属性。

ref是响应式系统api的一员大将,在上方的例子中我们可以发现不做任何处理直接返回的count不是响应式数据,它就是一个普通的值,可是我们的需求是在我点击按钮的时候count可以做为响应式数据被累加

ref可以用于创建一个响应式数据,代码参考如下:

注意点:这里通过 const count1=ref(0)创建了一个值为0的响应式数据,并在handleClickCount1该方法中实现了对该响应式数据累加的方法。

我们需要注意这里的是count1现在是一个RefImpl对象,0这个值被保存在这个对象的value属性下,所以我们在setup函数中需要通过count1.value来获取这个值0,这令我们徒增了许多心智负担。

虽然我们在setup函数中需要通过count1.value来获取这个值0,但是我们可以直接返回这个count1对象,并在template中直接使用{{count1}}来获取这个值

ref是响应式系统api的一员大将,在上方的例子中我们通过ref创建一个响应式数据,我们还可以通过reactive来创建一个一个响应式数据,代码参考如下:

注意点:这里通过let user=reactive({name:'lth',age:20})创建一个引用类型的响应式数据,并在handleAge中实现了对user.age的累加.

我们需要注意这里的user是Proxy对象,可以直接通过user.age和user.name来直接访问属性值,这一点得和使用ref区别开来.

readonly是响应式系统api的一员大将,在上面的例子中我们使用了reactive创建一个响应式数据,我们可以看到在点击按钮时,user.age的值会被累加,也就是说该user.age的值是可以被修改的,如果我们想要让一个值即是响应式数据又是不可修改的只读的值呢?我们可以使用readonly,代码参考如下:

我们可以回顾一下在vue2中父组件传递给子组件的props属性也是响应式的,但是我们在子组件中是不能直接修改props数据的,它也是一个只读属性.

在vue3中直接对在子组件中收到的props自动设置了readonly,如果你企图在子组件中直接修改props属性,是会报错的,代码如下:

computed是响应式系统api的一员大将,这里的计算属性接收一个对应的函数,我们回顾一个计算属性,它是一个依赖别的属性的属性,通过返回一个属性供模板调用

通过计算属性创建的是也响应式数据,直接上代码:

watch是响应式系统api的一员大将,watch的第一个参数必须是响应式数据或者是一个返回一个响应式数据的函数.直接上代码:

watchEffect是响应式系统api的一员大将,它不需要一开始设置去观察谁,始终获取书写在改函数中的值的最新值,直接接收一个函数,并在一开始一上来就立即执行了一遍.代码如下:

setup函数的执行比beforeCreate和created调用的时间都要早,它是在options api之前调用的,如下表就是生命周期钩子的改变:

beforeCreate

使用 setup()

created

beforeMount

使用 onBeforeMount

mounted

使用onMounted

beforeUpdate

使用onBeforeUpdate

updated

使用onUpdated

beforeDestroy

使用onBeforeUnmount

destroyed

使用onUnmounted

errorCaptured

使用onErrorCaptured

依赖注入(provide/inject)可以用于组件通信,我们先来看在APP.vue中的代码:

provide的第一个参数是key,第二个参数是value

我们再来看在子组件Bar中的代码:

使用refs可以获取真实dom元素或者组件实例,代码参考如下:

Vue3的新特性分析

为什么要去使用 composition api呢,因为可以更好的实现代码组织,我们来看以往在vue2中书写的常规代码:

上面的代码组织看起来比较冗余,不同的逻辑点放在不同的地方,逻辑少的话比较清晰,但是逻辑多的话找起来麻烦,我们在来看换成composition api后的写法(下面还不是最佳的写法,最佳写法在下下面)

上面的写法中我们已经可以清晰的观察到一个功能块的使用,我们在来做些改变,按照功能把功能块单独抽离为一个函数,利用到了组合的思想,代码如下:

我们可以清晰的看到上面的写法带来的简便性,并且我们可以将上方的功能块函数单独放在一个js文件内导出,在由主入口来引入这个导出的函数,这是最佳解,代码如下:

我们知道一个函数其实就是代表着可以复用的,在vue2中我们想要复用组件往往会使用mixins来实现,代码参考如下:

下面是在./MoveMixin.js的复用组件的逻辑代码:

如果我们有多个复用组件需要被使用,使用mixins导致来源不清晰和容易造成命名冲突的问题是十分棘手的,我们在来看vue3为我们带来的在逻辑复用上的好处,代码参考如下:

使用veu3的新特性封装完复用组件的逻辑后,在组件中使用的代码如下:

下面有一个坑,就是返回的响应式数据如果直接解构,解构出来的值不再是响应式数据,可以使用toRefs来解决这个问题.

我们可以发现我们的逻辑复用是十分的清晰的.