Global API改为应用程序实例调用
vue2中有很多全局api可以改变vue的行为,比如vue.compenent等。这导致一些问题:
- vue2中没有app概念,new Vue()得到的根实例被作为app,这样的话所有创建的根实例是共享相同的全局配置,这在测试时会污染其他测试用例,导致测试变得困难。
- 全局配置也导致没有办法再单页面创建不同全局配置的多个app实例。
vue3中使用createApp返回app实例,由它暴露一些列全局api
main.js
createApp(App)
.component('comp',{
render(){
return h('div','I am comp')
}
})
.mount('#app')
然后helloapp.vue添加该组件
<comp></comp>
| 2.x Global API | 3.x Instance API(app) |
|---|---|
| Vue.config | app.config |
| Vue.config.productionTip | remove |
| 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 |
Global and internal APIs重构为可做摇树优化
vue2中不少global-api是作为静态函数直接挂在构造函数上的,例如vue.nextTick(),如果我们从未在代码中用过它们,就会形成所谓的dead code,这类global-api造成的dead code无法使用webpack的tree-shaking排除掉。
import Vue from 'vue'
Vue.nextTick(() =>{
})
vue3中做了相应的变化,将他们抽取成为独立函数,这样打包工具的摇树优化可以将这些dead code排除掉。
import {nextTick} from 'vue'
nextTick(() =>{
})
受影响api:
- vue.nextTick
- vue.observable(replaced by vue.reactive)
- vue.version
- vue.compile(only in full builds)
- vue.set(兼容版本)
- vue.delete(兼容版本)
model选项和v-bind的sync修饰修饰符被移除,统一为v-model参数形式
vue2.0中.sync和v-model功能有重叠,容易混淆,vue3做了统一。
HelloWorld.vue
<template>
<h1>{{ msg }}</h1>
<button @click="count++">count is: {{ count }}</button>
<p>
Edit <code>components/HelloWorld.vue</code> to test hot module replacement.
</p>
<p>Hello,vite!!!</p>
<!-- Composition -->
<Composition></Composition>
<!-- Teleport:ModalButton -->
<ModelButton></ModelButton>
<EmitsComponent @my-click="onClick"></EmitsComponent>
<!-- 实例方法组件定义 -->
<comp></comp>
<!-- v-model的使用 -->
<VmodelTest v-model:counter="counter"></VmodelTest>
<!-- v-model等效于 -->
<!-- <VmodelTest :counter="counter" @update:counter="counter=$event"></VmodelTest> -->
</template>
<script>
import {
computed,
reactive,
onMounted,
onUnmounted,
ref,
toRefs,
watch,
} from "vue";
import Composition from './Composition.vue';
import ModelButton from './ModelButton.vue';
import EmitsComponent from './EmitsComponent.vue'
import VmodelTest from './VmodelTest.vue'
export default {
components: { Composition,ModelButton,EmitsComponent,VmodelTest},
name: "HelloWorld",
props: {
msg: String,
},
data(){
return {
counter:1
}
},
methods:{
onClick(){
console.log('click me!');
}
}
};
</script>
VmodelTest.vue
<template>
<div @click="$emit('update:counter',counter+1)">
counter:{{counter}}
</div>
</template>
<script>
export default {
props: {
counter: {
type: Number,
default: 0
},
},
}
</script>
<style lang="scss" scoped>
</style>
渲染函数API修改
变简单,主要修改有以下几点
不再传入h函数,需要我们手动导入;拍平的props结构。scopedSlots删掉了,统一到slots
<template>
<h1>{{ msg }}</h1>
<button @click="count++">count is: {{ count }}</button>
<p>
Edit <code>components/HelloWorld.vue</code> to test hot module replacement.
</p>
<p>Hello,vite!!!</p>
<!-- Composition -->
<Composition></Composition>
<!-- Teleport:ModalButton -->
<ModelButton></ModelButton>
<EmitsComponent @my-click="onClick"></EmitsComponent>
<!-- 实例方法组件定义 -->
<comp></comp>
<!-- v-model的使用 -->
<VmodelTest v-model:counter="counter"></VmodelTest>
<!-- v-model等效于 -->
<!-- <VmodelTest :counter="counter" @update:counter="counter=$event"></VmodelTest> -->
<!-- render api发生变化 -->
<RenderTest v-model:counter="counter">
<template v-slot:default>title</template>
<template v-slot:content>content...</template>
</RenderTest>
</template>
<script>
import {
computed,
reactive,
onMounted,
onUnmounted,
ref,
toRefs,
watch,
h
} from "vue";
import Composition from './Composition.vue';
import ModelButton from './ModelButton.vue';
import EmitsComponent from './EmitsComponent.vue'
import VmodelTest from './VmodelTest.vue'
export default {
components: { Composition,ModelButton,EmitsComponent,VmodelTest,
RenderTest:{
props: {
counter: {
type: Number,
default: 0
},
},
render(){
// 以前写法 ,现在作用域插槽被废除
// this.$scopedSlots.content()
this.$slots.default();
this.$slots.content();
return h('div',[
h('div',{onClick:this.onClick},[
`I am RenderTest: ${this.counter}`,
this.$slots.default(),
this.$slots.content()
]),
]);
},
methods:{
onClick(){
this.$emit('update:counter',this.counter+1)
}
}
}},
name: "HelloWorld",
props: {
msg: String,
},
data(){
return {
counter:1
}
},
methods:{
onClick(){
console.log('click me!');
}
}
};
</script>
函数式组件仅通过简单函数方式创建,functional选项废弃
函数式组件变化较大,主要有以下几点
- 性能提升在vue3中可忽略不计,所以vue3.0中推荐使用状态组件
- 函数时仅能够通过纯函数形式声明,接受props和context两个参数
- SFC中不能添加functional特性声明函数是组件
- {functional:true}组件选项移除
Functional.vue或者js
<script>
import { h } from '@vue/runtime-core'
// 也可以是js
// vue3中函数式组件只能用函数方式声明
// 到处function
function Heading(props,context){
console.log(context);
return h(`h${props.level}`,context.attrs,context.slots)
}
//验证或者声明还可以通过静态属性
Heading.props=["level"]
export default Heading
</script>
<style lang="scss" scoped>
</style>
插入以下到helloapp.vue标签下
<!-- 函数式组件 -->
<Functional level='3'>这是一个动态的h元素</Functional>
异步组件要求使用defineAsyncComponent方法创建
由于vue3中函数式组件必须定义为纯函数,异步组件定义时,异步组件定义时有如下变化:
- 必须明确使用defineAsyncComponent包裹
- component选项重命名为loader
- Loader函数不在接收resolve and reject且必须返回一个Promise
AsyncComp: defineAsyncComponent(()=> import('./NextPage.vue')),
带配置的异步组件,loader选项是以前的component(仅此一点变化)
组件data选项应该总是声明函数
vue3中的data选项统一为函数形式,返回响应式数据
createApp({
data(){
return{
apiKey:'alb2c3'
}
}
}).mount('#app')
自定义组件白名单
vue3中自定义元素检测发生在模板编译时,如果要添加一些vue之外的自定义元素,需要在编译器选项中设置isCustomElement选项
使用构建工具时,模板都会用到vue-loader预编译,设置它提供的compilerOptions即可
我们演示项目使用vite,在vite.config.js中配置vueCompilerOptions即可:
创建vite.config.js
module.exports={
vueCompilerOptions:{
isCustomElement:tag => tag ==='piechart'
}
}
is属性仅限于用在component标签上
vue3设置动态组件时,is属性仅能用于component标签上
<Composition is="comp"></Composition>
dom内模板解析使用v-is代替
scopedSlots属性被移除,都用scopedSlots属性被移除,都用scopedSlots属性被移除,都用slots代替
vue3中同意普通插槽和作用域插槽到$slots,具体变化如下
- 插槽均以函数形式暴露
- $scopedSlots移除
function Heading(props,context){
console.log(context);
return h(`h${props.level}`,context.attrs,context.slots)
}
自定义指令API和组件保持一致
vue3中指令api和组件保持一致,具体表现在:
- bind-beforeMout
- inserted - mounted
- beforeUpdate:新的,元素自身更新前调用,和组件生命周期很像
- componentUpdated->updated
- beforeUnmount 新的和组件生命周期钩子相似,元素将要被移除之前调用。
- unbind->unmounted
(main.js)
createApp(App)
.component('comp',{
render(){
return h('div','I am comp')
}
})
.directive('highlight',{
beforeMount(el,binding,vnode){
el.style.background=binding.value;
}
})
.mount('#app')
(helloworld.vue)
<!-- 自定义指令 -->
<p v-highlight="'green'">highlight is here!!</p>
transition类名变更
- v-enter -> v-enter-from
- v-leave -> v-leave-from
<template>
<div id="demo">
<button @click="show=!show">Toggle</button>
<transition name="fade">
<p v-if="show">Hello</p>
</transition>
</div>
</template>
<script>
export default {
data () {
return {
show:true,
}
}
}
</script>
<style scoped>
.fade-enter-active,
.fade-leave-active{
transition: opacity 0.5s ease;
}
/* 淡入淡出 */
.fade-enter-from,
.fade-leave-to{
opacity: 0;
}
</style>
组件watch选项和实例方法$watch不再支持点分隔符串路径
以.分割的表达式不再被watch支持,可以使用计算函数作为$watch参数实现
vue2.x中应用程序根容器outerHTML会被根组件替换(或被编译为template),vue3.x现在使用根容器的innerHTML取代
$on $off $once移除
上述3个方法被认为不应该由vue提供,可以使用第三方
<script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>
首先先安装mitt
npm i mitt -S
// 事件派发和监听
import mitt from 'mitt';
// 工厂函数 公用并且导出
export const emitter=mitt();
data里面的method
methods:{
sendMsg(){
emitter.emit('someEvent','foooo')
}
},
添加组件
<!--编程方式发送和监听事件 -->
<button @click="sendMsg">emit event</button>
同级的NextPage.vue接收事件
import {emitter} from './HelloWorld.vue'
export default {
mounted(){
emitter.on('someEvent',msg=>{
console.log(msg);
})
}
}

7764

被折叠的 条评论
为什么被折叠?



