先放大佬镇个场子
官方文档确实是个好东西,隐约记得之前在哪儿看过一句话,判断一个工具好不好用,可以去看它文档写的明不明白。我觉的还是有道理的,自己学习过程中也有所感悟,每看一遍往往都能有新的体会。相比其他文档,Vue文档的中文基因又是更重一点,这不赶紧看还留着过年吗[doge]。 Vuejs.org的最新版文档阅读起来给我的感觉写的很易读易懂 文档还提供了一个选项式/组合式切换开关,可以对比两种不同方式的实现,也很不错👇
这既不是对框架原理实现的学习,也不是针对某一特性的深度剖析,只是一个使用了几个月Vue2.X跑来读Vue3.X,然后从新增和修改两方面记录的Vue3初体验😬
由于缺乏大量的实践,本文会将有限的实践过程对常用的API存在的一些避坑的地方一起列出来。
📃新特性
组合式API
-
改进
Vue2组件代码结构
<template></template> <script> export default { components:{}, props: {}, data: {}, computed: {}, methods: {} //... //暴露的选项 } </script> <style></style>📍弊端: 代码按照各个选项罗列,而不是各个功能逻辑组成,一个功能逻辑可能由几个选项中的代码组成,造成代码逻辑上很分散,代码庞大时阅读性也会很差; 使用
minmix也可以做类似抽离的工作,但它其实并不是真正的抽离,不能单独执行抽离的逻辑,还是要先混入,这就带来可能存在的变量命名冲突、使用多个的话还会有不清楚来源等问题,相比于组合式API,它更像是提供代码层面抽离的功能✔组合式API: 所谓组合式,它就是通过的提供
setup选项,将这些新增的ref响应式API、watch传统的选项、onMounted生命周期钩子等函数API组合起来,可以实现将某些逻辑组合抽离的目的。这一点就跟React Hooks有上那么一点类似了,Vue自己也说这确实是灵感来源之一,而且也做了一些说明,哈哈哈哈。哦,还有一点就是,这种基于函数组合的写法可以更好的结合TS使用类型推断。 使用组合式API之后,代码的结构就可能是这样的<script setup> //这里就可以借用新增的API来写逻辑代码 //当然,我们可以将其抽离成单独的`.js`文件 </script> <template> //这里正常写标签元素 //<script>中定义的变量、函数及通过导入的内容都是暴露给模板可以直接引用的 </template> <style> //样式也一样 </style> -
setup
-
setup使用选项式,接受
props、context两个参数,在组件创建前执行,return返回值暴露在组件中,-
Props
props中的值为响应式且只读,可使用toRefs()进行解构,同ref()返回一个包含value属性的普通对象setup(props){ const { title } = toRefs(props) const title = toRef(props, 'title') }当可选属性为空时
toRefs()不会为其创建ref,可以用
toRef()为其同样创建ref -
Context
context也是一个对象,可以在
setup中暴露 attrs、slots、emit、exposeexpose用于子组件通过模板ref属性向父组件暴露属性方法 不同于2.X可以通过给子组件添加ref属性就可以访问子组件的数据,3.X中只能访问子组件主动通过expose暴露的数据//子组件 HelloWorld.vue <script setup> defineExpose({ hwName: "这是子组件的name", hwFunc() { console.log("这是子组件的function") } }) </script> //父组件 const hwRef = const ref(null) <HelloWorld ref="hwRef" msg="You did it!" />
-
-
生命周期钩子
通过导入
onX注册,钩子函数通过接受一个回调函数,在组件调用时被执行。
-
watch响应式更改同样提供
watch函数来达到组件中watch设置侦听器的目的,watch(counter, (newValue, oldValue) => { //newValue、oldValue 分别时更新后新旧值 }) //接受三个参数: //1.想要侦听的响应式引用或getter函数 //2.回调函数 //3.可选配置项 {immediate:true, deep:true}watchEffect相比于watch,它没有明确指定的追踪源,自动收集传入函数的数据源依赖,依赖更新自动执行watchEffect(async () => { const response = await fetch(url.value) data.value = await response.json() })仅在可追踪同步代码中的依赖以及异步第一个await前的使用的依赖
-
computed同watch,提供组件外部使用计算属性的APIconst twiceTheCounter = computed(() => counter.value * 2) //传入回调函数,输出只读响应式
-
-
使用
在新版本中,我们可以通过借助
<script setup>,也就是文档里说的单文件组合式API语法糖在单文件组件中使用,它跟选项式在prop、context的获取上略有不同,练习使用的3.2版本上,直接调用:defineProps、defineEmits、defineExpose、useSlots、useAttrs就可以,defineProps可以直接使用,useAttrs还要导入, 不过使用VSC的同学配合Volor插件(也是官方推荐),它的代码提示跟自动导入都非常好用,根本不需要操心这个,真的巨好用,比之前的vetur好太多了
还有就是依旧延续上版本中选项式API的风格,借助
setup()选项,虽然是过去版本,但是目前没有废弃的计划,会一直作为风格保留,这是不是好像在隔壁哪里见过👀
响应式声明
-
使用
reactive()对对象类型进行响应式声明,由于proxy用于创建对象的代理(不再兼容IE11),替代之前的Object.defineProperty,可以实现对属性属性新增等变化的监听 所以它只对象类型的的变量有效const state = reactive({ count: 1 }) -
使用
ref()创建响应式 它不受变量类型的限制,可以为任何类型创建响应式,const state1 = ref(2)
打印看下
返回值是一个带value属性的对象,保存的就是响应式变量的值, 当然如果是对象,对应的value属性值也会换成其代理
因为Proxy声明的响应式是作用在声明的对象上的,存在解构或者取值之后
失去响应式的问题,所以通过ref()来声明,它看起来就像额外包了一层的方式,不仅可以给引用类型,还可以给基本类型变量声明响应式,变相做到可以解构传值操作。
💡在模板中使用时,作为最外层属性时,可以自动解包;否则不可以
这时就需要手动取value值,或者借助解构然后直接使用
- 还有两个比较有用的API
toRef()、toRefs()这两个API可以为响应式对象的属性分别创建ref(),并且保持对源响应式的连接,就是可以实现解构而不是失去响应式,在组合函数中可以很方便的使用; 区别就是
源对象属性不存在时,前者会返回一个
value为undefined可用的ref,而后者直接返回undefined
可组合函数
在Vue2中我们就经常做的复用要么是抽离无状态的工具函数,要么是通过导入.vue文件组件来复用一些组件(SFC),相比于以往拥有完整html/js/css结构的SFC,结合vue新导出的函数API,我们可以实现某一部分有状态处理逻辑的抽离
其更像是把某个单一功能的SFC的<script>部分抽离出来复用,但是又不要html/css,使用起来就像这样
可组合函数也是建议以
use来开头命名,是不是感觉又好像好眼熟🙊。
Teleport
内置组件,可以将组件渲染在DOM中的指定节点位置
<teleport to="#endofbody">
<></>
</teleport>
//将组件渲染到id为endofbody的组件下
💡HINT:
- 组件间层级关系依旧保留,只是最终DOM结构变化
- 被
to指定的组件必须在该组件之前渲染,否在找不到改组件; - 一个目标有多个传送,按顺序渲染
片段
组件可以包含多个根节点,就像这样
<template>
<header></header>
<main v-bind="$attrs"></main>
<footer></footer>
</template>
但是对于非Props的attrs属性需要显示指定
触发组件选项
就是新增了 emit 选项,就像是props 一样对其进行校验和触发
在组件中,使用选项 emits: [ '', '']
或者API defineEmits()
来接收自定义事件,下图方式使用,跟props基本类似
HINT: 由于移除了.native修饰符,所以所有未在选项中声明的事件监听器都会默认加到$attrs中,原生事件会绑定到根节点上
所以需要在组件中正确声明所触发的事件,否则容易造成多次触发事件
相关的一点变化: -> $listeners$attrs
在3.X中移除了
$listeners,将事件监听放到了$attrs中,变成了以on开头的attribute 包含class、style在内的所有attribute
<MultiRootComVue
id="mrcv"
style="color: red"
v-model:first-name="firstName"
v-model:last-name="lastName"
@myEvent="myEvent"
:react-status="reactStatus"
>
</MultiRootComVue>
//MultiRootComVue.vue
子组件获取attrs
1.使用 useAttrs()
2.模版中使用 $attrs
☝️相比于2.X的变更
全局API
在2.X中,Vue是没有真正类似app这种概念,现状就是使用Vue提供的类似Vue.directive 全局API进行的配置项会作用到所有通过new Vue() 创建的根实例,
通常凡是这种会作用全局的东西,基本都会带来一些问题,3.X针对这些做了一些改变。
首先是可以通过createApp()来创建应用实例,然后是将那些影响实例行为的API移动到了实例下,
import { createApp } from 'vue'
import MyApp from './MyApp.vue'
const app = createApp(MyApp)
app.mount('#app')
这些API的变动带来在诸如创建挂载应用实例、安装插件等使用方法上的改变,具体可以看这里
全局API TreeShaking: 这也是全局API的调整带来的变化之一 2.X中那些全局API默认挂在Vue对象上,因此无论你是否使用,它都在哪里,也无法通过打包工具将实际没有使用的部分shake掉;
而重构之后的全局API通过具名导入,
import { nextTick } from 'vue'
未导入不能直接调用,从而来优化打包(仅ES构建版本,UMD仍然包含所有API)
模板指令
旧版文档给出的更改:
v-model指令的变化1.自定义组件修改默认绑定值:
value->modelValue、input->update:modelValue2.移除.sync和model选项,v-model:variable可带参数 3.同一组件可使用多个且可自定义修饰符.native3.X中取消该修饰符,对于未显示定义的事件,且子组件未设置
inheritAttr: false的<my-component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" /> //MyCompoent.Vue <script> export default { emits: ['close'] } </script>Vue会将其作为原生事件监听器添加到根元素上
琐碎但需要注意⚠️的
data选项只接受函数,不可直接赋对象
data(){
return {}
}
-
mixin合并行为变为浅层次合并,不会深入对象去遍历合并不同属性 -
移除的一些API
- 2.X中的数字键码修饰符不再可用
❌ <input v-on:keyup.13="submit" /> <!-- Vue 3 在 v-on 上使用按键修饰符 --> ✅ <input v-on:keyup.page-down="nextPage"> <!-- 同时匹配 q 和 Q --> ✅ <input v-on:keypress.q="quit"> - 移除实例方法
$on、$off、$once,不再支持通过使用Vue实例及事件触发器API来创建 事件总线 ,主要还是考虑到全局事件监听带来的维护问题 还是支持使用,如果要使用的话,可以使用mitt.js外部库- prop、event父子传递
- provide/inject间隔传递
- 状态管理库
- 移除
filter过滤器选项,不过我好像也从来没使用过😂
- 2.X中的数字键码修饰符不再可用
ending:在对组内做学习分享的时候,我是很没啥底气的,因为我知道这其实比较粗糙而且也都是表层的东西,但是看到大家的鼓励其实还是很受鼓舞的,很开心,嗯,就这样。 其实自己最近一直都很低落,因素来自很多方面,分享一首让我听完感觉很舒畅的歌在这里,文档可以先不看歌都去听! Harunohi - aimyon