Vue3新特性梳理

更多前端学习笔记请戳:https://github.com/6fa/WebKnowledge
新特性总结 vue3和vue2对比:

  • 重写了响应式系统、重写了虚拟DOM的实现,性能上得到了提升
  • 新推出组合式API(composition API),使维护组件代码变得更简单
  • 新增了一些功能,如Teleport、Suspense、片段
  • 修改和优化了一些API(生命周期、动画类名),同时移除了一些API(如$children、$listeners、filter)
  • 有更好的TypeScript支持
同时Vue3是向下兼容的,可以使用大部分的Vue2特性。
创建应用实例 从如何创建一个Vue实例说起,Vue2通过new Vue( )创建实例,而Vue3通过Vue.createApp( )创建应用实例:
counter: {{counter}}

注意应用实例和组件实例的区别:
1.应用实例(app)
  • Vue.createApp( )返回一个应用实例
  • 应用实例上有很多方法,比如app.component( )注册全局组件,app.directive( )注册全局指令等。而Vue2中它们是全局API,如Vue.component( )
  • 应用实例上的方法大多返回同一应用实例 (mount除外),所以可以链式操作
Vue.createApp({}) .component('SearchInput', SearchInputComponent) .directive('focus', FocusDirective) .use(LocalePlugin)

2.组件实例(vm)
  • 传递给createApp( )的选项用于配置根组件
  • 将应用实例挂载到一个DOM元素上,返回根组件实例(vm)
应用挂载 将应用挂载到DOM元素上时,Vue3有一点小改变:
  • 有设置template时,Vue2将模板内容替换目标元素,而Vue3将模板内容作为子元素插入
Vue2

Vue3

  • 无template时,Vue2将目标元素的outerHTML当作template,Vue3将目标元素的innerHTML当作template
{{msg}}//vue3 Vue.createApp({}).mount('#app') //则模板是id为app的元素的innerHTML: {{msg}} //即this.$el指向h1这个DOM元素//vue2 new Vue({ el:"#app" }) //模板是id为app的元素的outerHTML: {{msg}} //即this.$el指向div这个DOM元素

生命周期 钩子函数
Vue3的钩子函数与Vue2相比,将beforeDestory、destoryed修改为beforeUnmount、unmounted,增加了renderTracked、renderTriggered、errorCaptured:
  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeUnmount
  • unmounted
  • renderTracked
  • renderTriggered
  • errorCaptured
errorCaptured (新增)
  • 【Vue3新特性梳理】当捕获到一个后代组件的错误时,会调用errorCaptured
    errorCaptured(err, errComponent, errInfo){ //err:错误对象 //errComponent:发生错误的后代组件实例 //errInfo:包含错误来源的信息 return true/false }

  • 函数返回false可以阻止错误继续向上传递(但还是会传递给app.config.errorHandler)
renderTracked (新增,状态跟踪,调试用)
  • 此事件告诉你哪个操作跟踪了组件以及该操作的目标对象和键
  • 跟踪虚拟 DOM 重新渲染时调用,可以看作收集依赖时触发,就是渲染依赖了那些数据,当数据变化时会触发,接收一个event对象
  • 状态跟踪在一开始渲染页面时就会触发,触发顺序:beforeMounted - renderTracked - mounted
  • 页面更新也会触发,触发顺序:beforeUpdate - renderTracked - updated
    //event对象 { effect: {...}, key: "girls",//操作的键 target: {girls:{...},...},//操作的目标对象 type: "get"//哪个操作跟踪了组件 }

renderTriggered (新增,状态触发,调试用)
  • 当虚拟DOM重新渲染时触发。和renderTracked类似,接收event对象,此事件告诉你是什么操作触发了重新渲染,以及该操作的目标对象和键
  • 操作页面的显示的数据(引起虚拟DOM重渲染)就会触发,触发顺序:renderTriggered - beforeUpdate
setup生命周期
setup函数里面设置生命周期函数,是为了使组合式API的功能和选项式API一样完整。
  • setup函数内部的钩子函数基本和选项式一样,只是没有beforeCreate、created,并且在函数前面加上on。
  • 因为setup函数会在beforeCreate之前就执行,即组件创建之前执行。所有beforeCreate、created的代码应该写在setup中
  • 因为还没组件实例,setup中避免使用this,也不能访问以下选项:
    • data
    • computed
    • methods
  • 只能访问以下属性(通过第一个参数props,第二个参数context):
    • props
    • attrs
    • slots
    • emit
      Vue3新特性梳理
      文章图片
组件 & 选项 注册组件
注册组件的方法与vue2有略微差别。
vue2:
//全局注册 Vue.component("component-name", { //...选项 })new Vue({ el:"..." })//局部注册 const componentA = {} const componentB = {}new Vue({ el:"...", components:{ "component-a": componentA, "component-b": componentB } })

vue3:
//全局注册 const app = Vue.createApp({...})app.component("name",{...})//注册组件是应用实例的方法//局部注册 const componentA = {} const componentB = {}const app = Vue.createApp({ //... components:{ "component-a":componentA, "component-b":componentB, } })app.mount("#app") //挂载

Mixin的合并行为
在vue2中使用Mixin,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先:
const Mixin = { data() { return { user: { name: 'Jack', id: 1 } } } }const CompA = { mixins: [Mixin], data() { return { user: { id: 2 } } } }

在vue2中生成的$data:
{ "user": { "id": 2, "name": "Jack" } }

而vue3只是浅层合并:
{ "user": { "id": 2 } }

新增选项:emits
  • 和props选项类似,props接收父组件传来的数据,emits接收组件可触发的事件:
父组件

  • 和props一样,emits也可以接收一个对象,允许配置事件验证
// 对象语法 app.component('reply-form', { emits: { // 没有验证函数 click: null,// 带有验证函数 submit: payload => { if (payload.email && payload.password) { return true } else { console.warn(`Invalid submit event payload!`) return false } } } })

  • Vue强烈建议使用使用emits记录每个组件所触发的所有事件,因为Vue3移除了.native修饰符,没有用emits记录的事件会监听器会被放到组件的$attrs,且默认绑定到组件的根元素。
  • 可以使用inheritAttrs:false,让事件还是绑定在设置监听器的元素本身上
异步组件
在vue2中,异步组件通过一个返回promise的函数来创建:
Vue.component('async-component', () => import('./async-componnet.vue'))

在vue3中,异步组件需要通过defineAsynComponent定义:
import { defineAsyncComponent } from 'vue' app.component('async-component', defineAsyncComponent(()=>import('./async-componnet.vue'))) app.component('async-component', defineAsyncComponent(()=>{ return new Promise((resolve,reject)=>{ setTimeout(()=>{//模拟异步 resolve({ template:`这是一个异步组件` }) },3000) }) }))

组件用带选项的写法时,component应该改为loader:
import {defineAsyncComponent} from 'vue' import ErrorComponent from './components/ErrorComponent.vue' import LoadingComponent from './components/LoadingComponent.vue'const asyncComponent ={ loader: defineAsyncComponent(()=>import('./async-componnet.vue')), delay: 200, timeout: 3000, errorComponent: ErrorComponent, loadingComponent: LoadingComponent }

Suspense
Suspense可以将异步组件提升到组件树中,当异步请求未完成时显示加载中页面,在异步请求完成后即显示该异步组件内容,简化了自己手写处理异步逻辑、显示不同页面的步骤。
用法:
  • 在模板中,用将想显示的内容包裹
  • 有两个插槽:default和fallback,只能接收一个直接子节点。在default插槽里放置异步组件

asyncComponent应当在setup中返回一个resolve了结果的promise
export default defineComponent({ setup(){ return new Promise((resolve,reject)=>{ axios.get('...').then((rawData)=>{ resolve({result:rawData}) }) }) }//或者使用 async await,async函数返回值会被Promise.resolve包装成一个期约对象 async setup(){ const rawData = https://www.it610.com/article/await axios.get('https://apiblog.jspang.com/default/getGirl') return {result:rawData.data} } })

Teleport
一些组件,如占满全屏的对话框,如果包裹在其他组件中,就变得容易被干扰,因为样式也将包裹在其它组件中,定位容易变得混乱。
对于这些特殊的组件,既想存在于父组件的逻辑中,又想独立于父组件从而更方便某些操作,就可以使用Teleport瞬移组件:teleport组件允许将组件挂载在任意DOM上。
使用:
假如一个组件,想挂载到id为modal的元素上,则需要被包裹,且要用to属性指明挂载点
//modal组件 modal.vue

// app.vue

模板 删除过滤器
在vue2中可以使用过滤器对模板中的数据进行一些格式化:
{{ message | capitalize }}

export default{ //... filters:{ capitalize(val){ if(!val) return '' return val.toLowerCase() } } })

在vue3中删除了过滤器语法,可以用计算属性、方法调用替换。
片段
在vue2中,