Vue3.0 特性总结
一.Vue3.x与Vue2.x的区别
1.OptionsApi 和 CompositionApi的区别
须知:你仍然可以在Vue3.x中使用vue2的选项式Api写法,但我更建议你选择最新的组成式Api写法
OptionsApi - Vue2.x - 选项式Api
1.限制了自由度,必须按照规定来书写代码,如props,methods,data,computed,watch,各司其职,禁止越界
2.上下文必须使用this
3.生命周期包含beforeCreated()和Created()
CompositionApi - Vue3.x - 组成式Api
1.将代码打碎,允许在setup()函数中自由书写,使代码的分割感进一步降低
2.没有this,统一使用setup()函数中的context参数,由原来的this.getData => context.getData
3.生命周期被setup()入口给替代
4. Tree-Shaking 的目的,使用到什么就引入什么,最后打包也只打包用到的 api
示例:
content上下文
2.生命周期的变化
整体来看其实变化不大,使用setup代替了之前的beforeCreate和created,其他生命周期名字有些变化,功能都是没有变化的
3.template模板支持多个根标签
- 由原有的单一根标签调整为支持多个根标签,减少无用代码,增加代码自由度!
- 之所以支持多个根标签,是因为底层改为了Fragment组件进行包裹
//Vue2.x写法
<template>
<div class="name">
<p></p>
<p></p>
</div>
</template>
//Vue3.x
<template>
<div class="name"></div>
<div class="name2"></div>
<div class="name3"></div>
</template>
4.Route获取页面实例与路由信息
Vue2.x
- 通过this获取router实例
export default{
mounted() {
this.getRouter();
},
methods: {
getRouter() {
console.log(this.$route);
console.log(this.$router);
},
},
}
Vue3.x
- 第一种 通过使用 getCurrentInstance 方法获取当前组件实例
import { getCurrentInstance } from "vue";
export default {
setup(props, context) {
const { ctx } = getCurrentInstance();
console.log(ctx.$router.currentRoute.value);
},
};
- 第二种通过userRoute和userRouter
import { useRoute, useRouter } from "vue-router";
export default {
setup(props, context) {
const currRoute = useRoute();
const currRouter = useRouter();
console.log(currRoute);
console.log(currRouter);
},
};
5.Vuex状态管理
- api没有显著变化,
- 创建实例的方式改变,Vue2.x为new Store , Vue3.x为createStore
//Vue2.x 中创建 store 实例
export default new Vuex.Store({
// ...
})
//Vue3.x
import Vuex from 'vuex'
export default Vuex.createStore({
state: {
count: 0
},
mutations: {
ADD (state) {
state.count++
}
},
actions: {
add ({ commit }) {
commit('ADD')
}
},
modules: {
}
})
Vue3.x组件中使用 - 两种方式
- 第一种方式
<template>
<div class="home">
<p>{{count}}</p>
<button @click="add">增加</button>
</div>
</template>
<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
const count = computed(() => store.state.count)
const add = () => {
store.dispatch('add')
}
return {
count,
add
}
}
}
</script>
- 第二种方式: 在当前组件上下文中获取 store
import {getCurrentInstance} from 'vue'
// ...
const store = getCurrentInstance().ctx.$store
6.响应式对象函数 ref和reactive
export default{
setup(){
const count=ref(0);
const state=reactive({
count:0
})
}
}
ref
- ref更偏向于定义单个变量,而reactive更偏向定义对象。
- ref修改数据需要使用这样count.value=xxx 的形式,而reactive只需要state.count=xxx 这样来使用
- ref相当于内部封装了reactive
- ref 就当作简单的双向绑定变量 toRef 就是把不是响应式的对象转化成响应式
- 模板在取值是可以直接使用{{count}}的
reactive
- reactive 内部是可以使用计算属性等各种方法,它只是把数据双向绑定了而已
- reactive 后return的数据最好是用toRefs 转化一下
- 跟 ref 混合使用时候可以用isRef 判断类型
7.监听属性watch和watchEffect的区别
- watch需要指定监听的属性(参数), vue3.x中新增watchEffect,不需要指定监听的属性(参数),自动收集响应式属性
- watch可以获取到新值与旧值(更新前的值),而 watchEffect 是拿不到的
- watch起初就指定了依赖,初始化时不会执行,依赖发生变化时才会执行
而watchEffect 在初始化时就会执行一次,用来收集依赖,当依赖变化后,watchEffect 还会再次执行 - watch 和 watchEffect 都无法监听未被双向绑定的属性
- watch 可以直接监听 ref 和 reactive 绑定的对象,watchEffect 不可以(ref的值要.value,reactive的值要具体到内部属性),只会执行第一次
总结:通过比较两属性的不同点根据具体需求合理的使用
1.如果需要组件初始化时执行,则用watchEffect
2.如果需要获取新值旧值,则使用watch
8.计算属性computed
- 第一种不允许修改值,会提示警告: 计算属性不允许修改
- 第二种允许修改值
9. 支柱Props的使用变化
- Vue2.x中使用
exprot default {
props: {
title: String
},
mounted() {
console.log(this.title);
}
}
- Vue3.x中使用
//通过setup中的内置参数进行调用,舍弃了之前的this调用方式
exprot default {
props: {
title: String
},
//props 组件传入的属性
setup(props,context) {
console.log(props.title) // 响应式
}
}
10.混入Mixins的变化
- 在 Vue2.x 中mixin模式表面上看起来很安全。但是它通过合并对象来共享代码,给代码无形中增加了脆弱性,代码来源不清晰、方法属性等冲突, 并且掩盖了推理功能的能力,因此成为一种反模式。
// vue页面中引入,该方法会将Mixins中的所有方法和属性一并引入
import mixin from 'mixin.js'
export default{
data(){},
mixins: [mixin]
}
- 在 Vue3.x 中Composition API最聪明的部分是,它允许Vue依靠原生JavaScript中内置的保障措施来共享代码,比如将变量传递给函数和模块系统,只使用需要调用的部分!
// vue页面中引入
import mixin from 'mixin.js'
//第一种写法 - 利用ES6解构写法,只引用需要的部分
export default{
setup(){
const { count, plusOne, hello } = mixin()
hello()
console.log(count.value, plusOne.value)
}
}
// 第二种写法 - 调用组件中的局部变量
export default {
setup () {
// 某个局部值的合成函数需要用到
const myLocalVal = ref(0);
// 它必须作为参数显式地传递
const { ... } = mixin(myLocalVal);
}
}
11.自定义指令Custom Diretctives
- 摘自官方文档 自定义指令
- 自定义指令顾名思义即自行创造如v-html , v-bind , v-on等指令方便调用
12.传送门 Teleport
- 官方文档解释 Teleport
- 脱离原有的DOM结构,将模板内的 DOM 元素移动到其他位置,完全由内部 Vue 组件控制
- 解决组件间css的层级问题,无需在处理 z-index 问题
- 解决Modal的定位问题,不受父元素影响
- 使用场景: 意见反馈,通知, Toast,Modal
友情链接:Vue3 Teleport 简介,请过目,这个是真的好用
13.父子组件传值
- 在向父组件传回数据时,如使用的自定义名称,如backData,则需要在emits中定义一下,否则会在控制台中报 emits option 警告
<template></template>
export default{
emits:['backData'],
setup(props,{emit}){
function back(){
emit('backData');
}
}
}
二.小知识
1.基于Vue3编码是否全部使用TypeScript编码?
1 、对于业务经常变动的业务项目,不适合用 TypeScript,因为快速才是此类需求最重要的。
2 、对于工具类 /基础设施类项目,最好上 TypeScript 。
2.defineComponent的使用时机
根据编写代码使用的语言方式不同,选择合适的模块
- 如果用TypeScript,导出时则必须要用 defineComponent,这俩是配对的,为了类型的审查正确,如图一
- 如果是JavaScript编码,则无需该函数,如图二
3.script和script setup的使用区别
- 加上setup后的写法后,通过export导出属性或方法
<template>
<button @click="inc">{{ count }}</button>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const inc = () => count.value++
</script>
- 不加setup后的写法,则需要将属性或方法return出去,类似于vue2.x中data()中的return
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const inc = () => count.value++
return {
count,
inc,
}
},
}
4. Object.defineProperty(Vue2)和Proxy(Vue3)的响应式区别
Vue2.x Object.defineProperty
- 只能劫持对象属性的getter和setter操作,数据变化时需要遍历对象的所有属性,如data,props等,消耗较大
- 不支持Set/Map、class、数组等类型
- 无法检测到新加或删除属性,不具备响应式,需要手动调用this.$set进行更新
- Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应,也无法监听到push等方法,vue中重写了这些方法来增加setter
const data= {
name: 'xiaoming',
sex: '男'
}
// 遍历对象,对其属性值进行劫持
Object.keys(data).forEach(key => {
let val = data[key]
Object.defineProperty(data, key, {
enumerable: true, // 该属性可被枚举
configurable: true, // 该属性可以被删除以及属性描述符可以被改变
get () {
console.log(`属性值${data}当前值为${val}`)
return val
},
set (newValue) {
console.log(`监听到属性值${data}由${val}变为${newValue}`)
val = newValue
}
})
});
data.name // 属性值name当前值为zhangsan
data.name = 'lisi' // 监听到属性值name由zhangsan变为lisi
data.age // 属性值age当前值为18
data.age = 25 // 监听到属性值age由18变为25
Vue3.x Proxy(ES6)
- 可以劫持整个对象
- 对象新增属性可以实时响应式,可以检测到数组下标的变化
- 因为Proxy是ES6新增的属性,有些浏览器还不支持,只能兼容到IE11
let obj = {
a: 1,
b: 2,
}
const p = new Proxy(obj, {
get(target, key, value) {
if (key === 'c') {
return '我是自定义的一个结果';
} else {
return target[key];
}
},
set(target, key, value) {
if (value === 4) {
target[key] = '我是自定义的一个结果';
} else {
target[key] = value;
}
}
})
console.log(obj.a) // 1
console.log(obj.c) // undefined
console.log(p.a) // 1
console.log(p.c) // 我是自定义的一个结果
obj.name = '李白';
console.log(obj.name); // 李白
obj.age = 4;
console.log(obj.age); // 4
p.name = '李白';
console.log(p.name); // 李白
p.age = 4;
console.log(p.age); // 我是自定义的一个结果
5. env环境变量配置
- 项目根目录新建 .env.development 和.env.production
- 自定义的环境变量必须以 VITE_APP开头
- package.json 中添加对应后缀
- 页面中调用 import.meta.env.VITE_APP_BASE_API;
console.log(import.meta.env);
6. 初始化data数据
Vue2.x
Object.assign(this.$data, this.$options.data());
Vue3.x
const initState = () => {
return {
a: 1
}
}
const state = reactive(initState())
const resetState = () => {
Object.assign(state, initState())
}
扩展阅读
如您已了解完Vue3.x的新特性,则推荐您看以下文章,方便您快速入手Vue3项目