新特性(区别)
1、片段:vue3.0中template支持多个根标签;(vue2只能单个)
2、组合式API--setup
:(2.0的称为选项式API)
(1)在组件创建前执行,且只能接受(访问)props和context的函数;
(2)返回的内容将暴露给组件其他部分(计算属性、方法、生命周期等)以及组件模板;
(3)在 setup 中注册生命周期钩子的方法。这要归功于从 Vue 导出的几个新函数。组合式 API 上的生命周期钩子与选项式 API 的名称相同,但前缀为 on:即 mounted 会看起来像 onMounted
。
3、ref
函数:
(1)作用:使任何响应式变量在任何地方起作用;(通过对象的引用地址来实现响应)
(2)接受参数,并将其包裹在一个带有 value property 的对象中返回;【eg:const test = ref(1) // test.value === 1】
(3)toRefs
函数:创建对props(猜测可能对对象某个属性也行)的某个属性响应式引用;
4、watch
响应式更改
** 接收三个参数:
- 一个我们想要侦听的响应式引用或 getter 函数;
- 一个回调;
- 可选的配置选项;
import { ref, watch } from 'vue'
const counter = ref(0)
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})
5、独立的computed
属性
import { ref, computed } from 'vue'
const counter = ref(0)
const twiceTheCounter = computed(() => counter.value * 2)
counter.value++
console.log(counter.value) // 1
console.log(twiceTheCounter.value) // 2 --注意这边是`.value`
6、Teleport(传送)
(1)作用:可指定包裹的html结构渲染在哪个DOM
下;
(2)代码展示:
<button @click="modalOpen = true">
Open full screen modal! (With teleport!)
</button>
<teleport to="body"> // 渲染到body下面
<div v-if="modalOpen" class="modal">
<div>
I'm a teleported modal!
(My parent is "body")
<button @click="modalOpen = false">
Close
</button>
</div>
</div>
</teleport>
(3)与 Vue components 一起使用
如果 <teleport> 包含 Vue 组件,则它仍将是 <teleport> 父组件的逻辑子组件:
const app = Vue.createApp({
template: `
<h1>Root instance</h1>
<parent-component />
`
})
app.component('parent-component', {
template: `
<h2>This is a parent component</h2>
<teleport to="#endofbody">
<child-component name="John" />
</teleport>
`
})
app.component('child-component', {
props: ['name'],
template: `
<div>Hello, {{ name }}</div>
`
})
在这种情况下,即使在不同的地方渲染 child-component,它仍将是 parent-component 的子级,并将从中接收 name prop。
7、定义触发事件:可直接在组件通过emits
选项定义
(1)emits: ['inFocus', 'subimit'] 或对象也行,可像props一样校验传递参数;
(2)代码示例
app.component('custom-form', {
emits: {
// 没有验证
click: null,
// 验证submit 事件
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
},
methods: {
submitForm() {
this.$emit('submit', { email, password })
}
}
})
8、多个v-model
绑定
<user-name
v-model:first-name="firstName"
v-model:last-name="lastName"
></user-name>
app.component('user-name', {
props: {
firstName: String,
lastName: String
},
emits: ['update:firstName', 'update:lastName'],
template: `
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)">
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)">
`
})
9、v-model支持自定义修饰符,通过props中modelModifiers
访问;
10、新的全局API:createApp
(1)被创建原因:2.0的全局配置会影响多个app
实例:
// 这会影响两个根实例
Vue.mixin({
/* ... */
})
const app1 = new Vue({ el: '#app-1' })
const app2 = new Vue({ el: '#app-2' }
(2)使用:
import { createApp } from 'vue'
import MyApp from './MyApp.vue'
const app = createApp(MyApp)
app.mount('#app')
(3)Vue.prototype 替换为 config.globalProperties
// 之前 - Vue 2
Vue.prototype.$http = () => {}
// 之后 - Vue 3
const app = createApp({})
app.config.globalProperties.$http = () => {}
(4)由于 use 全局 API 在 Vue 3 中不再使用,此方法将停止工作并停止调用 Vue.use() 现在将触发警告,于是,开发者必须在应用程序实例上显式指定使用此插件:
const app = createApp(MyApp)
app.use(VueRouter)
11、v-model
默认prop由value
改为modelValue
,触发事件由$emit('input', newValue)
改为$emit.('update:modelValue', newValue)
12、v-for
变更
(1)可遍历对象:P1--value p2--key p3--index;
(2)可接受整数
<div id="range" class="demo">
<span v-for="n in 10" :key="n">{{ n }} </span>
</div>
// 输出:12345678910
13、v-if
和v-for
优先级变更:3.x中v-if优先级更高;(尽量避免同时使用两者)
14、v-bind合并行为变更
// 2.x版本
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- result -->
<div id="red"></div>
// 3.x版本:以顺序为主,往前覆盖
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- result -->
<div id="blue"></div>
<!-- template -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- result -->
<div id="red"></div>
15、移除v-on.native
修饰符:3.x新增emits
选项定义自定义事件,未定义的默认为原生事件;(除非在子组件的选项中设置了 inheritAttrs: false)
16、v-for 中的 Ref 数组
<div v-for="item in list" :ref="setItemRef"></div>
import { onBeforeUpdate, onUpdated } from 'vue'
export default {
setup() {
let itemRefs = []
const setItemRef = el => {
if (el) {
itemRefs.push(el)
}
}
onBeforeUpdate(() => {
itemRefs = []
})
onUpdated(() => {
console.log(itemRefs)
})
return {
setItemRef
}
}
}
17、异步组件:* [异步组件现在需要 defineAsyncComponent
方法来创建]
// 2.x 版本
const oldAsyncComponent = (resolve, reject) => {
/* ... */
}
// 3.x 版本: loader 函数不再接收 resolve 和 reject 参数,且必须始终返回 Promise
const asyncComponent = defineAsyncComponent(
() =>
new Promise((resolve, reject) => {
/* ... */
})
)
18、渲染函数变更:h(它是 createElement 的传统别名))现在是全局导入,不是render中作为参数传入;
// Vue 2 渲染函数示例
export default {
render(h) {
return h('div')
}
}
// Vue 3 渲染函数示例
import { h } from 'vue'
export default {
render() {
return h('div')
}
}
19、统一普通插槽和作用域插槽;
20、自定义元素方式改变;(还有is和v-is)
// 2.x版本
// 这将使Vue忽略在Vue外部定义的自定义元素
// (例如:使用 Web Components API)
Vue.config.ignoredElements = ['plastic-button']
// 3.x版本
// webpack 中的配置
rules: [
{
test: /\.vue$/,
use: 'vue-loader',
options: {
compilerOptions: {
isCustomElement: tag => tag === 'plastic-button'
}
}
}
// ...
]
// 如果使用动态模板编译,请通过 app.config.isCustomElement 传递:
const app = Vue.createApp({})
app.config.isCustomElement = tag => tag === 'plastic-button'
21、destroyed 生命周期选项被重命名为 unmounted;beforeDestroy 生命周期选项被重命名为 beforeUnmount;
22、default
prop 工厂函数不再可以访问 this
上下文;
23、* 自定义指令 API 已更改为与组件生命周期一致;
24、* data
选项应始终被声明为一个函数
- 来自 mixin 的
data
选项现在为浅合并 - Attribute 强制策略已更改
- 一些过渡 class 被重命名
<TransitionGroup>
不再默认渲染包裹元素- 当侦听一个数组时,只有当数组被替换时,回调才会触发,如果需要在变更时触发,则需要指定
deep
选项 - 没有特殊指令的标记 (
v-if/else-if/else
、v-for
或v-slot
) 的<template>
现在被视为普通元素,并将生成原生的<template>
元素,而不是渲染其内部内容。 - 在 Vue 2.x 中,应用根容器的
outerHTML
将替换为根组件模板 (如果根组件没有模板/渲染选项,则最终编译为模板)。Vue 3.x 现在使用应用容器的innerHTML
,这意味着容器本身不再被视为模板的一部分。