vue2和vue3的区别?
- 1.Vue3的Template支持多个根标签,Vue2不支持
- 2.Vue3有createApp(),而Vue2的是new Vue()
- 3.v-model代替以前的v-model和.sync
- 4.context.emit
- 5.Vue3中的属性绑定
- 5.1使用场景
- 5.2props和context.attrs的区别
- 6.具名插槽的使用
- 7.Teleport传送门组件
- 8.vue3中动态挂载组件的方法
- 9.父组件里获取子组件内容,渲染子组件
- 10.vue3中ref的使用
- 11.watchEffect用来代替生命周期里的onMounted和onUpdated
- 12.vue2和vue3双向数据绑定原理发生了改变
- 13.Composition API
- 14.建立数据data
- 15.生命周期
- 16.父子传参不用,setup()函数特性
- 总结
1.Vue3的Template支持多个根标签,Vue2不支持
Vue3支持碎片(Fragments)
也就是说可以拥有多个根节点
2.Vue3有createApp(),而Vue2的是new Vue()
3.v-model代替以前的v-model和.sync
vue3中v-model的用法
要求:props属性名任意,假设为x
事件名必须为"update:x"
<Switch :value="y" @update:value="y=$event"/>
//vue2中的写法
<Switch :value.sync="y"/>
//vue3中的写法
<Switch v-model:value="y"/>
4.context.emit
新增context.emit,与this.$emit(vue3中只能在methods里使用)作用相同
context.emit的用法
import {SetupContext } from 'vue'
setup(props: Prop, context: SetupContext) {
const toggle = () => {
context.emit('input', !props.value)
}
return {toggle}
}
5.Vue3中的属性绑定
默认所有属性都绑定到根元素
使用inheritAttrs:false可以取消默认绑定
使用attrs或者context.attrs获取所有属性使用v-bind="attrs"批量绑定属性
使用const {size,level,…rest}=context.attrs将属性分开
5.1使用场景
在vue2中我们在父组件中绑定click事件,子组件必须内部触发click,而vue3中在父组件绑定子组件的根元素上也会跟着绑定
- ButtonDemo.vue
<div>
<Button @click="onClick" @focus="onClick" size="small">你好</Button>
</div>
setup() {
const onClick = () => {
console.log("aaa")
}
return {onClick}
},
- Button.vue
<template>
<div>
<button>
<slot/>
</button>
</div>
</template>
上面的代码Button的click事件会在根元素div上绑定,如果我们要指定click的区域为button元素的话我们就需要使用inheritAttrs
- Button.vue
<template>
<div>
<button v-bind="$attrs">
<slot/>
</button>
</div>
</template>
<script lang="ts">
export default {
inheritAttrs: false
}
</script>
如果想要一部分属性绑定在button上一部分在div上就需要在setup里
- Button.vue
<template>
<div :size="size">
<button v-bind="rest">
<slot/>
</button>
</div>
</template>
<script lang="ts">
import {SetupContext} from 'vue'
export default {
inheritAttrs: false,
setup(props: any, context:SetupContext ) {
const {size, ...rest} = context.attrs
return {size, rest}
}
}
</script>
5.2props和context.attrs的区别
- props要先在当前组件props属性声明才能取值,attrs不用先声明
- 第一张图里因为没有声明props所以是空对象,第二个声明了size,所以只得到了size
- props不包含事件,attrs包含 我们没有拌饭在props里声明click这一类的事件
- props没有声明的属性,会跑到attrs里
- 上图中我们在props里声明了size,所以attrs里就没有size了
- 当我们在html标签中只写属性而不赋值的时候,props支持string以外的类型,attrs只有string类型
上面第一张图里我们在html里只写属性不赋值的情况下其实disabled是Bolean类型的,但我们通过attrs却得到的是空字符串,而第二张我们通过props指定了diabled为Boolean值我们就可以通过props拿到我们需要的true
6.具名插槽的使用
vue2中的用法
子组件
<slot name="title">
父组件
<template slot="title">
<h1>哈哈哈</h1>
</template>
vue3中子组件用法不变,父组件需要使用v-slot:插槽名
父组件
<template v-slot:title>
<h1>哈哈哈</h1>
</template>
7.Teleport传送门组件
<Teleport to="body">
需要传送到body下面的内容
</Teleport>
8.vue3中动态挂载组件的方法
通过引入h函数第一个参数是组件,第二个是元素的属性(第一个参数组件的props,也就是直接可以在使用组件的时候传入的属性),第三个是插槽的属性。
其中我们在render里监听我们v-model绑定的update事件的时候,需要使用onUpdate:属性名
import {createApp, h} from 'vue'
import Dialog from './Dialog.vue'
export const openDialog = (options: Options) => {
const {title, content} = options
const div = document.createElement('div')
document.body.append(div)
const app = createApp({
render() {
return h(Dialog, {
visible: true, cancel: () => {},
'onUpdate:visible': (newValue: boolean) => {
if (newValue === false) {
app.unmount(div)
}
}
}, {title, content})
}
})
app.mount(div)
}
9.父组件里获取子组件内容,渲染子组件
在父组件的setUp里通过context.slots.default()拿到子组件数组,然后通过component组件渲染
比如:
- TabsDemo.vue
<Tabs>
<Tab title="导航1">内容1</Tab>
<Tab title="导航2">内容2</Tab>
</Tabs>
- Tabs.vue
<template>
<component v-for="(tab, index) in defaults" :key="index" :is="tab"></component>
</template>
<script lang="ts">
import {SetupContext} from 'vue'
export default {
setup(props, context: SetupContext) {
const defaults = context.slots.default()
return {
defaults
}
}
}
</script>
vue3中所有的组件最后都会导出一个对象这个对象就是我们的子组件里的type(context.slots.default()[0].type),所以我们可以通过type判断子组件是不是我们要求的子组件,以Tabs组件为例我们需要用户使用的时候下面的子组件全部都是我们的Tab组件
- Tabs.vue
import Tab from './Tab.vue'
export default {
setup(props, context: SetupContext) {
const defaults = context.slots.default()
defaults.forEach(tag => {
if (tag.type !== Tab) {
throw new Error('Tabs 子标签必须是 Tab')
}
})
return {
defaults
}
}
}
10.vue3中ref的使用
单个ref
<script>
import {
onMounted,
ref,
} from 'vue';
export default {
setup() {
const headline = ref(null);
// Before the component is mounted, the value
// of the ref is `null` which is the default
// value we've specified above.
onMounted(() => {
// Logs: `Headline`
console.log(headline.value.textContent);
});
return {
// It is important to return the ref,
// otherwise it won't work.
headline,
};
},
};
</script>
<template>
<div>
<h1 ref="headline">
Headline
</h1>
<p>Lorem ipsum ...</p>
</div>
</template>
v-for里的ref
<template>
// el当前元素,divs是存储每个元素的数组
<div v-for="(item, index) in list" :ref="el => { divs[index] = el }">
{{ item }}
</div>
</template>
<script>
import {
onMounted,
ref,
} from 'vue';
export default {
setup() {
const divs = ref([]);
onMounted(() => {
console.log(divs.value)
});
return {
divs
};
},
};
</script>
11.watchEffect用来代替生命周期里的onMounted和onUpdated
初始化页面的时候watchEffect里的代码会执行,当watchEffect里的数据有更新的时候同样会执行
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> logs 0
setTimeout(() => {
count.value++
// -> logs 1
}, 100)
注意watchEffect第一次运行是在组件挂载之前,如果需要访问DOM需要将我们的watchEffect放在onMounted里
onMounted(() => {
watchEffect(() => console.log(count.value))
})
12.vue2和vue3双向数据绑定原理发生了改变
- vue2的双向数据绑定是利用了es5 的一个API Object.definepropert() 对数据进行劫持 结合发布订阅模式来实现的。vue3中使用了es6的proxyAPI对数据进行处理。
- 相比于vue2,使用proxy API优势有:defineProperty只能监听某个属性,不能对全对象进行监听;可以省去for in 、闭包等内容来提升效率(直接绑定整个对象即可);可以监听数组,不用再去单独的对数组做特异性操作,vue3可以检测到数组内部数据的变化。
13.Composition API
Vue2 与vue3 最大的区别是vue2使用选项类型api,对比vue3合成型api。旧得选项型api在代码里分割了不同得属性:data,computed,methods等;新得合成型api能让我们使用方法来分割,相比于旧的API使用属性来分组,这样代码会更加简便和整洁。
14.建立数据data
vue2是把数据放入data中,vue3就需要使用一个新的setup()方法,此方法在组件初始化构造得时候触发。使用一下三个步骤来简=建立反应性数据: 1. 从vue引入reactive;使用reactive() 方法来声明数据为响应性数据;3. 使用setup()方法来返回我们得响应性数据,从而template可以获取这些响应性数据。
15.生命周期
vue2 --------------- vue3
beforeCreate -> setup()
Created -> setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroyed -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
16.父子传参不用,setup()函数特性
- setup()函数接收两个参数:props、context(包含attrs、slots、emit)
- setup函数是处于生命周期beforeCreated和created俩个钩子函数之前
- 执行setup时,组件实例尚未被创建(在setup()内部,this不会是该活跃实例得引用,即不指向vue实例,Vue为了避免我们错误得使用,直接将setup函数中得this修改成了undefined)
- 与模板一起使用时,需要返回一个对象
- 因为setup函数中,props是响应式得,当传入新的prop时,它将会被更新,所以不能使用es6解构,因为它会消除prop得响应性,如需解构prop,可以通过使用setup函数中得toRefs来完成此操作。
- 父传子,用props,子传父用事件 Emitting Events。在vue2中,会调用this$emit然后传入事件名和对象;在vue3中得setup()中得第二个参数content对象中就有emit,那么我们只要在setup()接收第二个参数中使用分解对象法取出emit就可以在setup方法中随意使用了。
- 在setup()内使用响应式数据时,需要通过 .value 获取
import { ref } from 'vue'
const count = ref(0)
console.log(count.value)
- 从setup() 中返回得对象上得property 返回并可以在模板中被访问时,它将自动展开为内部值。不需要在模板中追加.value。
- setup函数只能是同步的不能是异步的。
总结
- Vue3的Template支持多个根标签,Vue2不支持
- Vue3有createApp(),而Vue2的是new Vue()
- v-model代替以前的v-model和.sync
- context.emit
- Vue3中的属性绑定
- 具名插槽的使用
- Teleport传送门组件
- vue3中动态挂载组件的方法
- 父组件里获取子组件内容,渲染子组件
- vue3中ref的使用
- watchEffect用来代替生命周期里的onMounted和onUpdated
- vue2和vue3双向数据绑定原理发生了改变
- Composition API
- 建立数据data
- 生命周期
- 父子传参不用,setup()函数特性