本来想一头埋进react不回头了,后面被安排到负责一个vue2的项目,又做回老本行,趁这个时机,顺便好好学习vue3,也可以发现vue2与vue3的区别。
首先就是vue3已经全面支持ts了,并且源码是使用ts进行了重构。
学过vue2的同学应该知道vue2使用的是object.defineProperty来劫持数据的getter和setter方法。这种方式存在一个致命缺陷就是当给对象添加或者删除属性的时候是无法劫持和监听的,相信这点大家深有体会,给对象添加属性必须使用特定的this.$ set()方法的,还有this.$delete()方法,而vue3开始使用了es6的新特性,Proxy来实现对数据的劫持。
其次,删除了一些不必要的api,比如实例上的 $on $off $once,移除了一些特性,如filter,内联模版等等
其三,编译方便也进行了优化,比如diff算法优化,slot编译优化,block tree优化等等
在vue2的时候,我们会通过opitions api来描述组件的对象,比如data props methods computed生命周期等等,存在大的问题就是多个逻辑可能在不同的地方,比如created会使用method来修改data的数据,代码内聚性差,composition api可以将向关联的代码放到同一处进行处理,而不需要在多个options之间寻找。
其次,就是hooks了,vue2的时候经常都是使用mixins在多个组件共享逻辑,但是mixins的本质也是由一堆options组成的,hook函数可以将一部分独立逻辑抽取出去,并且做到响应式。想起了react的自定义hooks
组合式api
如图
图中的defineComponent是
在script标签加上
reactive 与 ref 是用来定义初始化state的,reactive是对对象,ref是支持简单数据类型,通过这两个定义的state就会被数据劫持,否则不会被数据劫持从而达到响应效果,计算数据computed也变得容易的,不再是定义一个computed对象,而是
在需要使用的时候直接用就行了,最后暴露出去两个需要用到的data和add方法,
这样一个小demo就完成了。与vue2的options api不同的是,所有的方法定义数据都放在了setup函数里面,而不是分到各个比如mehtods,computed对象去了,解决了vue2功能一复杂就杂乱,其次也可以抽离出去复用。
setup函数是vue3专门新增的方法,可以理解为composition api的入口,他的执行时机是在
在创建组件之前调用,所以setup执行的时候组件实例并没有被创建,所以在setup函数里面无法获取this.
我们知道setup里面不可以用this,那么怎么获取props呢?
setup函数的第一个参数就是props,第二个参数就是context
上下文对象,
attrs可以获取props没有定义的,但是父亲仍然传入的参数。
slots可以获取插槽信息
emit可以触发类似于this.$emit,向父组件传递数据
ref和reactive是用来定义初始化state,并且有数据劫持,而自定义的数据是没有响应式的,
并且有个注意的点,通过ref创建的变量,
不是简单的一个变量,而是一个对象,在setup里面想获取值必须通过
.value去获取,而在setup外面就不必,因为vue内部帮你自动实现了一个转换,后续再详谈。
先看看ref与toRef的区别
toRef接受两个参数,第一个是对象,第二个是属性,他的作用是对值的引用,但不会引起ui的变化,跟直接暴露出ee没什么区别,不会造成ui变化,但是每次改变,ee的值都会改变,
但是ref不一样,ref是对值的拷贝,也就是每次使用ref,不会影响原来的值。
可以看出区别。那么toRef有什么用呢?
当我们使用reactvie代理一个对象,但是只返回对象的一个属性,是不能达到响应式的。
所以toRef的作用就来了,经过toRef包裹过的,原本就是reactive的对象的属性的话,就会达到响应式。
看到这里大概已经明白了toRef的作用,就是让原本就是响应式的对象,当经过解构返回出去的时候,toRef可以保持其相应性。而toRefs也就不言而喻了,就是会将对象的每个属性都达到响应式
原理就是便利对象然后使用toRef一个一个包裹。
判断当前数据是响应式数据还是原始,简单的说就是是不是被ref过
只有aa是通过ref定义的。
补:响应式数据可以通过readonly变成原始数据。
计算属性也是比较灵活了
computed(()=>{return xxx})
只是用法不一样了而已,但本质是一样的。
老版本写法:
一共接受三个参数,第一个是监听的属性,第二个是回调,第三个是是否初始化调用一次。
回到函数接受两个参数,分别对应新值旧值。
还可以接受多个监听
以数组的形式。上面的是监听ref的,监听reactive的呢?
不能初始化打印,因为第一次old的值为undefined,这样监听对象一改变就触发,而且是对象,用的都是同个引用地址,所以不能获取旧值,也不知道哪个属性发生变化,所以改为监听单个属性
不可以这么写
要么是对象,要么是一个WatchSource<>的东西,其实也就是
写成回调函数的形式。
但是
这样是不会触发的,只有改成()=>ee.value.aa才会触发,或者直接写ee.value变成监听对象,才会触发,否则属性aa的改变不会触发ee.value的改变。
如果想监听两个属性,也是用数组的形式写就行了。
停止监听:
watch会返回一个停止函数可以执行,上面的代码表示两秒后不再监听该属性。
特点:
1、立即执行,没有惰性,页面的首次加载就会执行。
2、自动检测内部代码,代码中有依赖 便会执行
3、不需要传递要侦听的内容 会自动感知代码依赖,不需要传递很多参数,只要传递一个回调函数
4、不能获取之前数据的值 只能获取当前值
5、一些=异步的操作放在这里会更加合适
如
立即执行一次,当内部代码没有依赖项,不会执行。
自动依赖。停止监听的操作同watch相同,相比watch的特点就是
1、具有一定的惰性lazy 第一次页面展示的时候不会执行,只有数据变化的时候才会执行
2、参数可以拿到当前值和原始值
3、可以侦听多个数据的变化,用一个侦听起承载
上面说过setup的执行时间是在beforecrated之前,所以其实setup就相当于beforecreate和created了,只是不能拿到this实例,但可以通过参数context获取。故setup里面没有beforecreated和created这两个声明周期,其他的就是在前面加上on,
父子组件通讯一般有props, $emit,还有 $ root , $parent, $ ref, $children等等。
非父子有:Vuex, localstrogae ,事件总线event
provide和Inject跟react的Context有点类似,都是从父组件注入依赖,子组件不管层级多深,都可以使用。
provide就像加强版父组件的prop(根组件传递数据),后代都能用,而inject就相当于加强版子组件的props(后代接收provide数据)
父组件通过Provide注入数据,后台通过Inject获取数据。
看例子,
有点类似于react的context,使用Provide接受两个参数,第一个是名字,第二个是值,这里使用reactve,才能响应改变引用数据的子组件的状态。
其次,使用readonly保证数据流的单一性,原则上不允许子组件修改props,所以我们可以把修改值的方法也一并传给子组件,让子组件调用该方法来改变数据。
使用也很简单,直接inject就行,第一个值是名字,第二个值是默认值。
然后
因为setup里面没有this,所以自然没有this.$router这些,但是有提供了一些Hooks,比如useRoute这些,我感觉是有点像react的hooks,vuex也一样,后面再讲vuex。那先看看怎么使用router。
首先引入两个hooks
通过router可以获取全局的router,通过route可以获取当前页面的路由参数。比如
可以结合watch监听url中某些参数如id的变化,进而做一些处理,如react的useEffect
可以通过router获取全局路由,写路由守卫函数
当我从about跳到home时
这个就是全局路由守卫,尽管他写在about组件里面。
vue-router也提供了两个hooks
一个是离开之前调用。一个是在当前页面跳转调用,第二个会调用两次,
这就是两个页面守卫。
vuex提供了useStore这个hooks来使用
vuex基本配置
基本使用
获取store,得到state,dispatch actions改变数据
嵌套的使用
通过mapState和mapActions获取state和action
这里需要注意,拿到的setdd action中的this并不指向sotre了,所以需要手动绑定store,dd的使用也需要dd()
然后
使用成功。
创建d.ts文件声明先,然后
获取key值,
注册的时候需要用到,
然后
封装自己的useStore函数,不然每次在每个文件都需要useStore(key),就很麻烦,
使用的时候使用自己的useStroe。不过vuex好像只提供第一层的state,并不支持module,官网上是没有看到,如果有大佬知道欢迎评论。
至此,vue3的基本知识基本就结束了,接着会继续深入学习。