composition api为vue应用提供更好的逻辑复用和代码组织。
counter: {{counter}}
doubleCounter: {{doubleCounter}}
setup 是 Vue3.x 新增的一个选项, 他是组件内使用 Composition API的入口。
使用setup时,它接受两个参数:
setup 中接受的props是响应式的, 当传入新的 props 时,会及时被更新。由于是响应式的, 所以不可以使用 ES6 解构,解构会消除它的响应式。 错误代码示例, 这段代码会让 props 不再支持响应式
// demo.vue
export default defineComponent ({
setup(props, context) {
const { name } = props
console.log(name)
},
})
setup接受的第二个参数context,context提供了最常用的三个属性:attrs、slot 和emit,分别对应 Vue2.x 中的 a t t r 属 性 、 s l o t 插 槽 和 attr属性、slot插槽 和 attr属性、slot插槽和emit发射事件。并且这几个属性都是自动同步最新的值,所以我们每次使用拿到的都是最新值。
Vue3.0 新增了setup,这个在前面我们也详细说了, 然后是将 Vue2.x 中的beforeDestroy名称变更成beforeUnmount; destroyed 表更为 unmounted,作者说这么变更纯粹是为了更加语义化,因为一个组件是一个mount和unmount的过程。其他 Vue2 中的生命周期仍然保留。
上边生命周期图中并没包含全部的生命周期钩子, 还有其他的几个, 全部生命周期钩子如
beforeCreate和created被setup替换了(但是 Vue3 中你仍然可以使用, 因为 Vue3 是向下兼容的, 也就是你实际使用的是 vue2 的)。其次,钩子命名都增加了on; Vue3.x 还新增用于调试的钩子函数onRenderTriggered和onRenderTricked
import { defineComponent, onBeforeMount, onMounted, onBeforeUpdate, onUpdated,
onBeforeUnmount, onUnmounted, onErrorCaptured, onRenderTracked,
onRenderTriggered} from "vue";
export default defineComponent({
// beforeCreate和created是vue2的
beforeCreate() {
console.log("------beforeCreate-----");
},
created() {
console.log("------created-----");
},
setup() {
console.log("------setup-----");
// vue3.x生命周期写在setup中
onBeforeMount(() => {
console.log("------onBeforeMount-----");
});
onMounted(() => {
console.log("------onMounted-----");
});
// 调试哪些数据发生了变化
onRenderTriggered((event) => {
console.log("------onRenderTriggered-----", event)
})
}
});
Vue3.x 可以使用reactive和ref来进行数据定义。
通过ref()定义,需通过xx.value来获取值。reactive不能代理基本类型,例如字符串、数字、boolean 等。
toRefs 用于将一个 reactive 对象转化为属性全部为 ref 对象的普通对象。
传送门组件提供一种简洁的方式可以指定它里面内容的父元素。
这是一个模态窗口!
我的父元素是"body"!
vue3中组件可以拥有多个根。
Vue 3 目前提供一个 emits选项,和现有的props选项类似。这个选项可以用来定义组件可以向其父组件触发的事件。
vue3中组件发送的自定义事件需要定义在emits选项中:
vue2中有很多全局api可以改变vue的行为,比如Vue.component等。这导致一些问题:
vue2没有app概念,new Vue()得到的根实例被作为app,这样的话所有创建的根实例是共享相同的全局配置,这在测试时会污染其他测试用例,导致测试变得困难。
全局配置也导致没有办法在单页面创建不同全局配置的多个app实例。
vue3中使用createApp返回app实例,由它暴露一系列全局api
import { createApp } from 'vue'
const app = createApp({})
.component('comp', { render: () => h('div', 'i am comp') })
.mount('#app')
2.x Global API | 3.x Instance API (app) |
---|---|
Vue.config | app.config |
Vue.config.productionTip | removed |
Vue.config.ignoredElements | app.config.isCustomElement |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
Vue.filter | removed |
vue2中不少global-api是作为静态函数直接挂在构造函数上的,例如Vue.nextTick(),如果我们从未在代码中用过它们,就会形成所谓的dead code,这类global-api造成的dead code无法使用webpack的tree-shaking排除掉。
import Vue from 'vue'
Vue.nextTick(() => {
// something something DOM-related
})
vue3中做了相应的变化,将它们抽取成为独立函数,这样打包工具的摇树优化可以将这些dead code排除掉。
import { nextTick } from 'vue'
nextTick(() => {
// something something DOM-related
})
受影响api:
默认情况下,组件上的 v-model 使用 modelValue 作为 prop 和 update:modelValue 作为事件。我们可以通过向 v-model 传递参数来修改这些名称:
子组件将需要一个 title prop 并发出 update:title 要同步的事件:
app.component('my-component', {
props: {
title: String
},
emits: ['update:title'],
template: `
`
})
app.component('user-name', {
props: {
firstName: String,
lastName: String
},
emits: ['update:firstName', 'update:lastName'],
template: `
`
})
vue2中.sync和v-model功能有重叠,容易混淆,vue3做了统一。
{{data}}
app.component('comp', {
template: `
i am comp, {{modelValue}}
`,
props: ['modelValue'],
})
渲染函数变得更简单好用了,修改主要有以下几点:
不再传入h函数,需要我们手动导入;拍平的props结构。scopedSlots删掉了,统一到slots
import {h} from 'vue'
render() {
const emit = this.$emit
const onclick = this.onclick
return h('div', [
h('div', {
onClick() {
emit('update:modelValue', 'new value')
}},
`i am comp, ${this.modelValue}`
),
h('button', {
onClick(){
onclick()
}},
'buty it!'
)
])
},
由于vue3中函数式组件必须定义为纯函数,异步组件定义时有如下变化:
import { defineAsyncComponent } from 'vue'
// 不带配置的异步组件
const asyncPage = defineAsyncComponent(() => import('./NextPage.vue'))
带配置的异步组件,loader选项是以前的component
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'
// 待配置的异步组件
const asyncPageWithOptions = defineAsyncComponent({
loader: () => import('./NextPage.vue'),
delay: 200,
timeout: 3000,
// 加载异步组件时要使用的组件
loadingComponent: LoadingComponent,
// 加载失败时要使用的组件
errorComponent: ErrorComponent,
})
vue3中指令api和组件保持一致,具体表现在:
const app = Vue.createApp({})
app.directive('highlight', {
beforeMount(el, binding, vnode) {
el.style.background = binding.value
}
})
Highlight this text bright yellow
以.分割的表达式不再被watch和watch支持,可以使用计算函数作为watch支持,可以使用计算函数作为watch参数实现。
this.$watch(() => this.foo.bar, (v1, v2) => {
console.log(this.foo.bar)
})
vue2中可以使用keyCode指代某个按键,vue3不再支持。
上述3个方法被认为不应该由vue提供,因此被移除了,可以使用其他三方库实现。
// 创建emitter
const emitter = mitt()
// 发送事件
emitter.emit('foo', 'foooooooo')
// 监听事件
emitter.on('foo', msg => console.log(msg))
vue3中移除了过滤器,请调用方法或者计算属性代替。
keyCode方式不再被支持 -->
### on,off and $once 移除
上述3个方法被认为不应该由vue提供,因此被移除了,可以使用其他三方库实现。
// 创建emitter
const emitter = mitt()
// 发送事件
emitter.emit(‘foo’, ‘foooooooo’)
// 监听事件
emitter.on(‘foo’, msg => console.log(msg))
### Filters移除
vue3中移除了过滤器,请调用方法或者计算属性代替。