Vue3的变化
官网地址: https://v3.cn.vuejs.org/guide/migration/introduction.html
一、对比vue2的变化
1.优点
- vue3支持vue2的大多数特性,实现对vue2的兼容
- vue3对比vue2具有明显的性能提升
- 打包大小减少41%
- 初次渲染快55%,更新快133%
- 内存使用减少54%
- 更好的支持TypeScript
- 使用Proxy代替defineProperty实现响应式数据
2.性能提升的原因
- 静态标记
- vue2从根节点开始对虚拟dom进行全量对比(每个节点不论写死的还是动态的都会一层一层比较)
- vue3新增了静态标记 与上次虚拟dom对比的时候,只对比带有 patchFlags 的节点。跳过一些静态节点对比(下图编译结果中-1跟1就属于静态标记)
patchFlags配置文件地址 https://github.com/vuejs/core/blob/main/packages/shared/src/patchFlags.ts
<div id='app'> <div>helloWorld</div> <div>{{msg}}</div> </div> <!--编译后--> <script> const _hoisted_1 = { id: "app" } const _hoisted_2 = /*#__PURE__*/_createElementVNode("div", null, "helloWorld", -1 /* HOISTED */) export function render(...) { return ( _openBlock(), _createBlock('div', _hoisted_1, [ _hoisted_2, _createElementVNode('div',null,_toDisplayString(_ctx.msg),1 /* TEXT */), ]) ) } </script> - hoistStatic 静态提升
- vue2里每当触发更新的时候,不管元素是否参与更新,每次都会重新创建
- vue3为了避免每次渲染的时候都要重新创建这些对象,会把不参与更新的元素保存起来,只创建一次,每次复用。比如上面_hoisted_1,_hoisted_2被提升到渲染函数render之外,
- cacheHandlers 事件缓存
- vue2里绑定事件都要重新生成新的function去更新
- vue3会自动生成一个内联函数,同时生成一个静态节点。onclick时会读取缓存,如果缓存没有的话,就把传入的事件存到缓存里
<div @click="handleClick">点击</div> <!--编译后--> <script> export function render(...) { return (_openBlock()._createElementVNode('div',{onClick: _ctx.todo}, '点击')) ) } </script> <script> export function render(...) { return (_openBlock()._createElementVNode('div',{ onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.todo(...args))) },'点击')) } </script>
3.响应式数据的变化
- vue2的响应式(实现地址:https://github.com/jiaomengyuan)
- 核心
- Object.defineProperty 来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调
- 对象:递归调用defineProperty对对象已有属性值的读取和修改进行拦截
- 数组:重写数组更新数组一系列更新元素的方法来实现元素修改的劫持
- 问题
- 不能监听到对象属性的添加和删除,需要Vue.set()来添加和删除。
- 不能通过下标替换元素或更新length
- 核心
- vue3的响应式(实现地址:https://github.com/jiaomengyuan)
- 核心
- 通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等
- 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作
- 核心
二、 新增特性
1.Composition (组合) API
-
Option API:vue2创建组件时,会把数据放到data,计算属性放到computed,事件处理放到methods,监听改变放到watch;共同处理页面逻辑- 组件功能越来越多,逻辑功能点分散,不易阅读(新增或修改一个需求,就需要分别在data,methods等...进行修改,功能多时,滚动条来回滚动 )
- 可以通过Mixins重用逻辑代码,但是数据来源模糊还会有Mixins命名冲突的问题
-
Composition API:将零散的data,methods代码重新组合,一个功能的代码放一块儿,并且可以单出拆分出函数- 兼容Option API,还可继续使用
- 利于代码重用,没有对this的使用,减少了this指向不明的情况
- 几乎是函数,编辑器可以帮我们进行类型检查和建议
2. setup
- setup函数是一个新的option,在初始化时执行一次,可以理解为使用Composition API 的入口点。
- 这个函数的返回一个对象,对象里的属性和方法,可以直接在模版中使用
<template> <div>{{msg}}</div> </template> <script> export default { setup () { const msg = 'hello World' return { msg } } } </script> 注意:
- 在beforeCreate之前创建,因此,这个函数中没有this。因此不能访问data,methods等。但methods中可以访问setup提供的属性和方法
- return中返回的属性跟data合并,返回的方法跟methods里的方法合并;如有重名,setup优先
- setup不能是一个async函数,使用async后返回值不是return的对象,而是promise。非要使用,需要使用
<suspense>包裹组件 - setup接收两个参数
setup(props, context)||setup(props, {attrs, slots, emit})不能解构props,否则会失去响应式
1) ref
- 定义一个基本数据类型的响应式引用
<template> <div>{{num}}</div> <button @click="addNum">添加</button> </template> <script> import { ref } from 'vue' export default { setup () { const num = ref(1) function addNum () { num.value = num.value + 1 } return { num, addNum } } } </script> 注意:
- 在setup中使用ref定义的响应式引用需要
.value(内部通过给value属性添加getter、setter实现对数据的劫持),在模版中不需要(解析模板时会自动添加.value) - ref常用来处理基本数据类型,如果用ref定义引用数据类型, 内部会自动将对象,数组转换为reactive的代理对象
2) reactive
- 定义多个数据的响应式引用
<template> <div>{{ obj.name }}</div> <div>{{ obj.age }}</div> <button @click="updateObj">修改</button> </template> <script> import { reactive } from 'vue' export default { setup () { const obj = reactive({ name: "张三", age: 25 }); const updateObj = () => { obj.name = "王五"; obj.age = 21; }; return { obj, updateObj, }; } } </script> 3) toRef(obj,key) 、 toRefs(obj)
- reactive定义的响应式数据,解构后进行使用,数据就不是响应式的,使用
toRef或toRefs将解决这个问题 -
toRef跟toRefs都是复制reactive里的属性然后转成ref。因为是浅拷贝,所以修改复制出来的值时原来reactive里的数据也会跟着更新 -
toRef是复制reactive里的单个key转成ref,toRefs复制reactive里的所有key转成ref - 也可以用来将props接收的数据进行解构响应式
<template> <div>{{ obj.name }}</div> <div>{{ obj.age }}</div> <div>{{ name }}</div> <div>{{ age }}</div> <button @click="updateObj">修改</button> </template> <script> import { reactive, toRefs, toRef } from "vue"; export default { props: { msg: String, }, setup (props) { const obj = reactive({ name: "张三", age: 25 }); // const name = toRef(obj, "name"); // const { msg } = toRefs(props) const { name, age } = toRefs(obj); const updateObj = () => { obj.name = "王五"; obj.age = 21; }; return { obj, name, age, updateObj, }; } } </script> 4) shallowRef、triggerRef 与 shallowReactive
-
ref和reactive是把数据变为响应式,无论层级多深,始终都会对数据进行深度监听。shallowRef和shallowReactive可以解决这个 -
shallowRef只处理了.value的响应式(只监听.value的变化)。因此如果需要修改应该xxx.value={}或者使用shallowRef将数据强制更新到页面 - 注:
shallowReactive的数据使用shallowRef强制更新无效
<template> <div>{{ obj.name }}</div> <div>{{ obj.age }}</div> <div>{{ obj.c.d }}</div> <button @click="updateObj">修改</button> </template> <script> import { shallowRef, triggerRef } from "vue"; export default { setup() { const obj = shallowRef({ name: "张三", age: 25, c: { d: 2 } }); const updateObj = () => { // 这些都不会修改成功,obj对象会变,不会更新到模版 // obj.value.name = "王五"; // obj.value.age = 21; // obj.value.c.d = 4; // 调用此方法,上方数据会更新到页面 // triggerRef(obj); // 如要修改,要用此方法 obj.value={ name: "王五", age: 21, c: { d: 4 } } }; return { obj, updateObj, }; }, }; </script> -
shallowReactive只处理对象最外层属性的响应式(浅响应式),默认情况下只能监听第一次的数据。如要修改则需要先改第一层的数据,然后再去更改其他层的数据
<template> <div>{{ obj.name }}</div> <div>{{ obj.age }}</div> <div>{{ obj.c.d }}</div> <button @click="updateObj">修改</button> </template> <script> import { shallowReactive } from "vue"; export default { setup() { const obj = shallowReactive({ name: "张三", age: 25, c: { d: 2 } }); const updateObj = () => { // 这样不会监听到修改,obj会变,不会更新到模版 // obj.c.d = 4; // 如要修改需要用下面方法,先改第一层 obj.name = "王五"; obj.age = 21; obj.c.d = 4; }; return { obj, updateObj, }; }, }; </script> 5) readonly 与 shallowReadonly
-
readonly是让一个响应式数据只读,深层只读
<template> <div>{{ obj.name }}</div> <div>{{ obj.age }}</div> <div>{{ obj.c.d }}</div> <button @click="updateObj">修改</button> </template> <script> import { reactive, readonly } from "vue"; export default { setup() { let obj = reactive({ name: "张三", age: 25, c: { d: 2 } }); obj = readonly(obj); //这里不会修改成功(还是proxy对象,但obj不会修改成功) const updateObj = () => { obj.name = "王五"; obj.age = 21; obj.c.d = 4; console.log(obj)// 这里obj还等于之前定义的值 }; return { obj, updateObj, }; }, }; </script> -
shallowReadonly是让一个响应式数据只读,浅层只读(响应式对象的最外层只读,但再深一层的属性可以被修改)
<template> <div>{{ obj.name }}</div> <div>{{ obj.age }}</div> <div>{{ obj.c.d }}</div> <button @click="updateObj">修改</button> </template> <script> import { shallowReadonly, reactive } from "vue"; export default { setup() { let obj = reactive({ name: "张三", age: 25, c: { d: 2 } }); obj = shallowReadonly(obj); const updateObj = () => { //这个不会修改 obj.name = "王五"; //这个不会修改 obj.age = 21; //这个会被修改 obj.c.d = 4; }; return { obj, updateObj, }; }, }; </script> 6) toRaw 、 markRaw
-
toRaw将一个响应式对象(由 reactive定义的响应式)转换为普通对象
<template> <div>{{ obj.name }}</div> <div>{{ obj.age }}</div> <div>{{ obj.c.d }}</div> <button @click="updateObj">修改</button> </template> <script> import { toRaw, reactive } from "vue"; export default { setup() { let obj = reactive({ name: "张三", age: 25, c: { d: 2 } }); obj = toRaw(obj);// 使用此方法,obj则变为普通对象(非proxy) const updateObj = () => { obj.name = "王五"; obj.age = 21; obj.c.d = 4; }; return { obj, updateObj, }; }, }; </script> -
markRaw标记一个对象,使其不能成为一个响应式对象
<template> <div>{{ obj.name }}</div> <div>{{ obj.age }}</div> <div>{{ obj.c.d }}</div> <button @click="updateObj">修改</button> </template> <script> import { markRaw, reactive } from "vue"; export default { setup() { let data = { name: "张三", age: 25, c: { d: 2 } }; // 在此处标记后,之后使用reactive则不生效(不会成为一个proxy对象),markRaw放到reactive之后则无效 data = markRaw(data); let obj = reactive(data); const updateObj = () => { obj.name = "王五"; obj.age = 21; obj.c.d = 4; }; return { obj, updateObj, }; }, }; </script> 7) isRef 、unref 、 isReactive 、isProxy 、isReadonly
-
isRef检查值是否为一个 ref 对象。unref为val = isRef(val) ? val.value : val的语法糖
const obj = ref({ name: "张三", age: 25, c: { d: 2 } }); console.log(isRef(obj)) console.log(unref(obj)) -
isReactive检查值是否为一个 reactive 对象
const obj = reactive({ name: "张三", age: 25, c: { d: 2 } }); console.log(isReactive(obj)) -
isProxy检查对象是否是由 reactive 或 readonly 创建的 proxy
const obj = reactive({ name: "张三", age: 25, c: { d: 2 } }); console.log(isProxy(obj)) const obj = readonly({ name: "张三", age: 25, c: { d: 2 } }); console.log(isProxy(obj)) -
isReadonly检查对象是否是由 readonly 创建的只读代理
const obj = readonly({ name: "张三", age: 25, c: { d: 2 } }); console.log(isReadonly(obj)) 8) customRef
- 创建一个自定义的ref,接受一个函数作为参数,这个函数接受两个参数
track(通知vue需要追踪后续内容的变化)trigger(通知vue重新解析模版)
<template> <input type="text" v-model="inpValue"> <div>{{inpValue}}</div> </template> <script> import {customRef} from "vue"; export default { setup() { // 自定义一个 myRef function myRef(value) { return customRef((track, trigger) => { return { get() { track() // 追踪后续数据的变化 return value }, set(newValue) { value = newValue trigger() // 重新解析模板 } } }) } let inpValue = myRef('hello') return { inpValue, } } } </script> 9) computed、watch、watchEffect
- 在setup中也有单独的
computed和watch用法基本相同 -
watchEffect是监视所有回调中使用的数据,因为每次初始化的时候都会执行一次回掉自动获取依赖,并不用手动传需要监听的对象。注:无法获取到oldValue
<template> <div>{{ obj.name }}</div> <div>{{ obj.age }}</div> <input type="text" v-model="userNews"/> <input type="text" v-model="userNews2"/> <input type="text" v-model="userNews3"/> <input type="text" v-model="userNews4"/> <button @click="updateObj">修改</button> </template> <script> import { ref, reactive, computed, watch, watchEffect } from "vue"; export default { props: { msg: String, }, setup (props) { const obj = reactive({ name: "张三", age: 25 }); const updateObj = () => { obj.name = "王五"; obj.age = 21; }; // computed只有get const userNews = computed(() => { return obj.name + "," + obj.age; }) // computed同时有get和set const userNews2 = computed({ get() { return obj.name + "," + obj.age; }, set(value) { const nameAge = value.split(","); obj.name = nameAge[0]; obj.age = nameAge[1]; }, }); // watch const userNews3 = ref(""); watch( obj, (newValue, oldValue) => { userNews3.value = newValue.name + "," + newValue.age; }, { immediate: true, deep: true, } ); // watchEffect const userNews4 = ref(""); watchEffect(() => { userNews4.value = obj.name + "," + obj.age; }); return { obj, userNews, userNews2, userNews3, userNews4, updateObj, }; } } </script> 注意:
当使用 watch 选项侦听数组时,只有在数组被替换时才会触发回调。换句话说,在数组被改变时侦听回调将不再被触发。要想在数组被改变时触发侦听回调,必须指定 deep 选项。
10) 生命周期
- setup中也有新的生命周期
onBeforeMount->onMounted->onBeforeUpdate->onUpdated->onBeforeUnmount->onUnmounted->onErrorCaptured跟options api混用时onBeforeMount在beforeMount之前,onMounted在mounted之前。。。之后都是 - vue中父子顺序
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted在setup中声明周期也适用
11) provide 、 inject
-
provide、inject提供依赖注入 ,实现祖孙级组件通信
<template> <h1>父组件</h1> <one /> </template> <script> import { provide, ref } from 'vue' import one from './one.vue' export default { components: { one }, setup() { const msg = ref('red') provide('msg', msg) return { msg } } } </script> <!--子组件(one)--> <template> <div>子组件</div> <two /> </template> <script> import two from './two.vue' export default { components: { two }, } </script> <!--孙子组件(two)--> <script> import { inject } from 'vue' export default { setup() { const msg = inject('msg') return { msg } } } </script> 12) $refs
- 在vue2中使用
this.$refs.XXX获取,vue3中setup函数没有this,所以也有单独的获取ref的方法
<template> <input type="text" ref="inputRef" value="这是input的文本"/> </template> <script> import { onMounted, ref } from "vue"; export default { setup() { const inputRef = ref(null);/// 本质是reactive({value:null}) onMounted(() => { console.log(inputRef.value.value); }); console.log(inputRef.value);// null dom还没形成 return { inputRef, }; }, }; </script> 13) 自定义hook函数
- 与mixin类似,抽离公共代码
import { reactive } from "vue"; export default function hookTest() { const obj = reactive({ name: "张三", age: 25 }); return { obj }; } <template> <div>{{ obj.name }}</div> <div>{{ obj.age }}</div> </template> <script> import hookTest from './hookTest' export default { setup (props) { const {obj} = hookTest() return { obj }; } } </script> 3. 其他新特性
1) 全局API
- 在vue2中的main.js中有以下代码。如果使用全局api则是 Vue.directive、Vue.component、Vue.config、Vue.mixin、Vue.prototype等,都是挂载在Vue原型上
import Vue from 'vue' import App from './App.vue' const app = new Vue(App) app.$mount() - vue3提供的是实例api。通过createApp创建vue实例
import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app') 
2) v-if 与 v-for 的优先级对比
- 在vue2中:当v-if和v-for同时使用时,v-for的优先级高于v-if(因此我们通常需要计算属性先对数据进行加工处理,以达到性能优化的目的)
- 在vue3中:当v-if和v-for同时使用时,v-if的优先级高于v-for
3) v-for 中的 Ref 数组
- vue2中,在 v-for 语句中使用ref属性,会生成refs数组插入$refs属性中
<div v-for="item in 5" :key="item" :ref="item"> {{ item }} </div> this.$refs将会是个数组 - vue3中,在v-for中使用ref属性,将不会自动在$refs里创建数组,而是将ref绑定到一个函数中,在函数中可以处理ref
<template> <div v-for="item in 5" :key="item" :ref="setItemRef"> {{ item }} </div> </template> <script> import { onMounted } from "vue"; export default { setup() { const refArray = []; const setItemRef = (e) => { refArray.push(e); }; onMounted(() => { console.log(refArray); }); return { setItemRef, }; }, }; </script> 4) v-bind合并行为
- vue2如果一个标签同时定义了动态属性和一个相同的单独的属性,那么这个单独的属性总是会覆盖动态属性。说明单独属性优先级高于动态属性
<div id="red" :id="'blue'"></div> <!--这里div只会绑上id等于red--> - vue3将会吧动态属性跟单独属性合并
<div id="red" :id="'blue'"></div> <!--结果为--> <div id='red blue'></div> 5) v-model
- vue2中,在组件上使用v-model默认prop与event为
value和input。如要修改,通过子组件的model选项中的prop值和event值来指定属性名和事件名。
<child v-model="pageTitle" /> <!--等同于--> <child :value="pageTitle" @input="pageTitle = $event" /> export default { model: { prop: 'title', event: 'change' }, props: { title: { type: String, default: 'Default title' } }, methods:{ handleClick(val) { this.$emit('change', val) } } } - 除了使用上面方法对某一个prop进行 ‘双向绑定’ 还可以通过这种方式
v-bind.sync - vue3中v-bind 的 .sync 修饰符已移除。
:title.sync就要替换为v-model:title
<child :title="pageTitle" @update:title="pageTitle = $event" /> <!--等同于--> <child :title.sync="pageTitle" /> this.$emit('update:title', newValue) - vue3默认prop与event为
modelValue和update:modelValue。如要修改,直接通过v-model后面参数v-model:title来指定属性名,并且支持绑定多个v-model。
<child v-model="pageTitle" /> <!--等同于--> <child :modelValue="pageTitle" @update:modelValue="pageTitle = $event" /> - 如果需要修改model的名称,我们可以为v-model传递一个参数,作为子组件内model选项的代替
<child v-model:title="pageTitle" /> <!--等同于--> <child :title="pageTitle" @update:title="pageTitle = $event" /> 6) $attrs
- vue2中使用
v-bind='$attrs'进行将父组件不被认为props特性绑定的属性传入子组件(不包含class以及style),配合interitAttrs一起使用,如果为true则将所有attribute添加到子组件的跟标元素上。但如果为false时,因为class以及style不属于$attrs,所以仍会添加到组件的跟元素上 - vue3中$attrs包含所有传递给子组件的attribute,包含class以及style
7) emits选项
- vue2中子组件触发父亲组件的方法
this.$emit(方法名) - vue3提供了一个类似props的emits选项,emits选项可以配置校验emit事件,为null的时候不校验。校验时会把参数携带过去,当校验不通过,控制台会发出一个警告,但emit事件还会继续执行
- 官方建议我们在组件中所有的emit事件都能在组件的emits选项中声明
<template> <one @open="open" /> </template> <script> import one from "./one.vue"; export default { components: { one, }, setup() { open = () => { console.log(1); }; return { open, }; }, }; </script> <!--子组件--> <template> <div @click="open">点击</div> </template> <script> export default { emits: { //open: null, open: (value) => { if (typeof value === "string") { return true; } else { return false; } }, }, setup(props, { emit }) { open = () => { emit("open", 11); }; return { open, }; }, }; </script> 7) 事件 API(eventBus)
- 移除
$on,$off和$once实例方法,组件实例不再实现事件触发接口。
8) 函数式组件
- 函数式组件有两个特性1、Stateless无状态:组件自身没有状态 2.Instanceless无实例:组件自身没有实例,也就是没有this
- vue2中,函数式组件通常用为性能优化,它的初始化速度比有状态组件快的多
Vue.component('typeButton',typeButton) const typeButton = { functional:true,//标记,无状态无实例 render(h , { props }){ const { type } = props return <div class={ type }>{type}</div> } } - vue3中不需要
functional定义 接收两个参数,props和context。context包含组件的attrs、slots和emitproperty
import { h } from 'vue' const DynamicHeading = (props, context) => { return h(`h${props.level}`, context.attrs, context.slots) } DynamicHeading.props = ['level'] export default DynamicHeading 9) 异步组件 defineAsyncComponent
const asyncModal = () => import('./Modal.vue') //带有选项 const asyncModal = { component: () => import('./Modal.vue'), delay: 200, timeout: 3000, error: ErrorComponent, loading: LoadingComponent } - vue3中函数式组件被定义为纯函数,因此异步组件需要包裹在
defineAsyncComponent中
import { defineAsyncComponent } from 'vue' const asyncModal = defineAsyncComponent(() => import('./Modal.vue')) //带有选项 const asyncModalWithOptions = defineAsyncComponent({ loader: () => import('./Modal.vue'), delay: 200, timeout: 3000, errorComponent: ErrorComponent, loadingComponent: LoadingComponent }) 10) data选项
- vue2中,可以通过Object或者function定义data选项
<!-- Object --> <script> const app = new Vue({ data: { num: 1 } }) </script> <!-- Function --> <script> const app = new Vue({ data() { return { num: 2 } } }) </script> - vue3中,data只接受返回object的function
<script> import { createApp } from 'vue' createApp({ data() { return { num: 1 } } }).mount('#app') </script> - 另外于mixin合并操作将被浅层次执行(data和mixin中声明同样的对象,vue2会把对象中的合并,vue3将只取mixin中)
三、 新组件
1. setup语法糖
- setup这个option,暴露变量必须都return出来,模板中才能使用。vue3也提供了相关语法糖,不用export ,也不需要写setup函数
- 需要将 setup attribute 添加到
<script>代码块上.变量跟函数都不需要return,即可在模板中使用 - 引入组件可以直接使用,不需要通过components进行注册
<template> <div>{{ obj.name }}</div> <div>{{ obj.age }}</div> </template> <script setup> import { ref } from "vue"; const obj = ref({ name: "张三", age: 25, c: { d: 2 } }); </script> 2. style
3. Fragment(片断)
- 在vue2中组件必须要有一个根标签
- vue3组件里可以没有根标签,vue3内部会把多个标签放在一个Fragment虚拟元素中
4. Teleport(瞬移)
-
<teleport>用于移动dom到指定元素 - 还可配置
disabled属性,禁用teleport的功能,不会移动到任何位置,但他仍会出现在正常位置下(里面的所有元素依然保持正常状态)
<teleport to="#some-id" /> <teleport to=".some-class" /> <teleport to="[data-teleport]" /> <teleport to="body" disabled=‘true’> <div></div> </teleport> 5. Suspense -- 实验
- 组件渲染被渲染之前,可能存在异步操作,
<suspense>帮助我们创建一个平滑的用户体验 -
<suspense>有两个插槽default、fallback。如果default里的标签(任意深度,所有的后代组件)显示不出来,则展示fallback - fallback被触发的机制还有上面说的setup是一个async函数。这样return的则是promise
<template> <suspense> <template #default> <todo-list /> </template> <template #fallback> <div> Loading... </div> </template> </suspense> </template> <script> export default { components: { TodoList: defineAsyncComponent(() => import('./TodoList.vue')) } } </script> 四、 废除属性
1. $children
- 在vue2中,可以使用
this.$children访问当前实例的子组件。vue3中已移除,推荐使用$refs
2. 过滤器filter
- 在vue2中,可以使用过滤器来处理数据格式。vue3中已移除,推荐使用方法调用或计算属性
3. $listeners
- vue2可以使用
v-bind='$attrs'访问父组件不被认为的props特性绑定的属性传入子组件,还可以通过在中间组件添加v-on=‘$listeners’使其子组件可以通过emits触发其父组件的方法 - vue3将移除这一方法,因为现在事件监听器是
$attrs的一部分(相当于父组件的@init=‘init’。不用在中间组件添加v-on=‘$listeners’,只需要v-bind='$attrs',由$attrs负责传递方法。即可在其子组件访问)
4. inline-template
- 当 inline-template 出现在一个子组件上时,这个组件里面的内容作为模版,而不是将其作为被分发的内容
- vue3将移除这一属性。可以通过默认插槽进行实现
5. propsData
- vue2中使用propsData用于在创建Vue实例的过程中传入prop
- vue3移除了这一属性。如果需要传递,则需要createApp的第二个参数
const Comp = Vue.extend({ props: ['username'], template: '<div>{{ username }}</div>' }) new Comp({ propsData: { username: 'Evan' } }) //vue3中需要这样 const app = createApp( { props: ['username'], template: '<div>{{ username }}</div>' }, { username: 'Evan' } ) 



