Vue3.0基础
Vue3.0新特性
1、响应式数据重新实现(ES6的proxy替代ES5的Object.defineProperty)
2、源码使用typescript进行重新编写(更好的类型推导)
3、虚拟DOM新算法(更快、更小)
4、提供了composition api ,为了更好的逻辑复用与代码组织
5、自定义渲染器(可以根据需求自定义渲染器,运行在不同的终端)
如:小程序端:http://mpvue.com/;
游戏开发:https://vugel.planning.nl/#application
6、Fragment,模板可以有多个根元素
Vue2.0和Vue3.0响应式原理对比
Vue2.0响应式原理
Vue2.0中使用ES5的Object.defineProperty方法实现响应式数据。
缺点:1、无法监测到对象属性的动态添加和删除;2、无法监测到数组的下标和长度。
解决方案:Vue2.0提供Vue.set方法用于动态给对象添加属性;提供Vue.delete方法用于动态删除对象的属性;重写vue中数组的方法,用于监测数组的变更。
Vue3.0响应式原理
Vue3.0中使用ES6中的proxy语法实现响应式数据
优点:1、可以监测到代理对象属性的动态添加和删除;2、可以监测到数组的下标和length属性的变更;
缺点:ES6的proxy语法对于低版本浏览器不支持,如ie11;Vue3.0会针对ie11出一个特殊的版本用于支持ie11
创建vue3.0项目
使用vue-cli创建vue3.0项目
vue --version 查看vue/cli版本 ,只有4.5.0之后的版本支持vue3.0
vue create vue3_project // 创建项目
vue add vue-next // 2.0项目升级到3.0,不建议直接升级,2.0很多在3.0不支持会导致原项目跑不起来
使用vite创建vue3.0项目
Vite介绍
Vite是一个由原生ESM驱动的Web开发构建工具,在开发环境下基于浏览器原生ES imports开发,在生产环境下基于Rollup打包。
npm init vite-app vue3_project // 创建vue3.0项目,vue3_project
composition API 和 options API 对比
options API (选项API)
1、优点:容易学习和使用,代码有明确的书写位置
2、缺点:相似逻辑不容易复用,在大项目中尤为明细
3、可以通过mixins 提取相同的逻辑,但容易发生命名冲突且来源不清晰
composition API(组合API)
1、根据逻辑功能来组织代码的,一个功能所有的api放到一起
2、即便项目很大,功能很多,都能够快速的定位到该功能所有的API
3、提高了代码的可读性和可维护性
【vue3.0中推荐使用composition API,也保留了options API】
composition API的使用
1、setup
setup函数是一个新的组件选项,作为组件中composition API的起点;
从生命周期钩子的角度来看,setup会在beforeCreate钩子函数之前执行;
setup中不能使用this,this指向undefined
2、reactive
reactive函数接收一个普通对象(需要是复杂类型),返回该对象的响应式代理(返回代理对象,即实现数据响应式)
3、ref
ref函数接收一个简单类型,返回一个可改变的ref对象。返回的对象有唯一的属性value
在setup函数中,通过ref对象的value属性可以访问到值
在模板中,ref属性会自动解套,不需要额外.value
如果ref接收的是一个对象,系统会自动将ref转换为reactive
【Vue3中实现响应式数据的方法是ref和reactive】
4、toRefs
把一个响应式对象转换成普通对象,该普通对象的每个属性都是一个ref
reactive的响应式功能是赋予给对象的,但是如果给对象解构或者展开的时候,会让简单类型的数据丢失响应式的能力
使用toRefs可以保证该对象展开的每一个属性都是响应式的
5、readonly
传入一个对象(响应式或普通)或ref属性,返回一个原始对象的只读代理
一个只读的代理是"深层的",即对象内部任何嵌套的属性也都是只读的
可以防止对象被修改
如:父组件给子组件传数据,父组件不希望子组件修改此数据,可以用readonly包裹此数据
6、computed
computed函数用于创建一个计算属性
若传入的是一个getter函数,会返回一个不允许修改的计算属性
若传入的是一个带有getter和setter函数的对象,会返回一个允许修改的计算属性
7、watch
watch函数接受三个参数:①数据源,可以是ref或者getter函数;②回调函数(newValue,oldValue)=>{};③额外选项,deep和immediate。
可以监听一个ref或者一个带有返回值的getter函数。
可以监听单个数据源,也可以监听多个数据源
watch函数会有返回值,用于停止监听
示例
<template>
<div>{{ obj.brand }}--{{ obj.price }}</div>
<button @click="obj.brand = '奔驰'">修改</button>
<!-- ref在模板中使用,会自动解套 -->
<div>{{ money }}</div>
<button @click="money++">money+1</button>
<!-- toRefs -->
<div>{{ yellow }}</div>
<div>{{ red }}</div>
<button @click="yellow='mango'">变</button>
<!-- readonly -->
<div>{{ animals.one }}</div>
<div>{{ animals.two }}</div>
<button @click="animals.one = 'rabbit'">不会变</button>
<!-- computed -->
<div>
今年的年龄:<input type="text" v-model = "age" />
明年的年龄:<input type="text" v-model = "nextAge" />
后年的年龄:<input type="text" v-model = "nextAge2" />
</div>
<!-- watch -->
<div>{{ num }}</div>
<button @click="num++">按钮</button>
<div>{{ colors.one }}</div>
<button @click="colors.one = 'purple'">按钮</button>
<!-- 同时监听 -->
<div>{{ qincai }}</div>
<button @click="qincai = 'nangua'">qingcai按钮</button>
<div>{{ green.cong }}</div>
<button @click="green.cong = 'baicai'">cong按钮</button>
</template>
<script>
import { reactive, toRefs,ref, readonly, computed,watch } from 'vue'
export default {
setup(){
console.log('setup执行了');
// reactive函数接收一个复杂类型,实现数据响应式
const obj = reactive({
brand:'宝马',
price:100
})
// ref函数接收一个简单类型,返回响应式的对象;这个响应式对象只有一个属性value;在模板中使用ref,会自动解套(即会自动调用value)
const money = ref(100)
money.value++;
// toRefs
const fruits = reactive({
yellow:'banana',
red:'watermelon',
green:'avocado',
price:{
one:111,
two:222,
three:333
}
})
// readonly
const animals = readonly({
one:'dog',
two:'cat'
})
// computed
const age = ref(18)
// computed是一个函数
// 传入一个函数getter 返回一个不允许修改的计算属性
// 若改动nextAge 控制台会出现警告,computed value is readonly
const nextAge = computed(()=>{
return parseInt(age.value) + 1
})
// 传入一个对象,包括get和set,可以创建一个可以修改的计算属性
const nextAge2 = computed({
get(){
return parseInt(age.value) + 2
},
set(value){
age.value = value-2;
// nextAge2改变,age改变,nextAge也会改变
}
})
// watch
const num = ref(10)
watch(num,(value,oldValue)=>{
// watch监听简单类型
console.log('num变化了',value);
})
const color = reactive({
colors:{
one:'red',
two:'orange',
three:'yellow',
four:'green'
}
})
watch(()=>color.colors,(value,oldValue)=>{
// watch监听复杂类型,colors,深度监听
console.log('color.colors变化了',value);
},
{
deep:true,
immediate:true
}
)
// 监听多个值
const vegetables = reactive({
qincai:5,
green:{
cong:7,
jiucai:3
}
})
watch([()=>vegetables.qincai,()=>vegetables.green],([qincai,green])=>{
// 同时监听green中的qingcai、cong
console.log('数据变化了',qincai,green);
},{
deep:true
})
return {
obj,
money,
// toRefs:直接展开对象,其中得到的复杂类型的属性仍是响应式的,而得到的简单类型的属性不是响应式的,需要toRefs包裹才行
...toRefs(fruits),
// readonly
animals,
age,
nextAge,
nextAge2,
num,
...toRefs(color),
...toRefs(vegetables)
}
},
beforeCreate(){
console.log('beforeCreate执行了');
}
}
</script>
生命周期钩子函数
vue3.0提供的生命周期钩子函数只能在setup() 期间同步使用
vue3.0生命周期钩子函数与vue2对比:
beforeCreate ---> 使用setup()
created ---> 使用setup()
beforeMount ---> onBeforeMount
mounted ---> onMounted
beforeUpdate ---> onBeforeUpdate
updated ---> onUpdated
beforeDestroy ---> onBeforeUnmount
destroyed ---> onUnmounted
errorCaptured ---> onErrorCaptured // 错误捕获
依赖注入
Vue3中提供了provide、inject提供依赖注入,用于实现组件之间的通讯。类似于vue2中的provide、inject。
vue3提供的provide、inject可以用于跨多级组件(父传子,父传孙,孙传父,孙传祖父)进行通讯。vue3中仍可以使用props、$emit进行父子间的通讯。
<!-- src/App.vue -->
<template>
<div>我是父组件: {{ num }}</div>
<button @click="num++">父传子</button>
<demo></demo>
</template>
<script>
import {ref,provide} from 'vue';
import demo from './components/demo.vue';
export default {
components:{
demo
},
setup(){
// 组件通讯:依赖注入provide 、 inject (推荐)
const num = ref(10)
// 父传子 父传孙
provide('num',num)
// 子传父 孙传父 父提供函数
const changeMoney = (n)=>{
num.value -= n;
}
provide('changeMoney',changeMoney)
// 无论父传子还是子传父 ,都是provide 提供
return {
num,
changeMoney
}
}
}
</script>
<!-- src/components/demo.vue -->
<template>
<div class="demo">我是子组件 : {{ num }}</div>
<demoson></demoson>
</template>
<script>
import {inject} from 'vue'
import demoson from './demoson.vue';
export default {
components:{
demoson
},
setup() {
// 组件通讯:依赖注入provide 、 inject 父传子
const num = inject("num");
return {
num
};
},
}
</script>
<!-- src/components/demoson.vue -->
<template>
<div>我是子组件的子组件:{{ num }}</div>
<button @click="fn">子传父</button>
</template>
<script>
import {inject} from 'vue'
export default {
setup() {
// 组件通讯:依赖注入provide 、 inject 父传孙
const num = inject("num");
// 孙传父 拿函数调用
const changeMoney = inject('changeMoney');
const fn = ()=>{
changeMoney(5)
}
return {
num,
fn
};
},
}
</script>
模板ref
为了获得对模板内元素或组件实例的引用,我们可以像往常一样在setup()中声明一个ref并返回它。
<template>
<div ref="root">模板ref</div>
</template>
<script>
import { onMounted,ref } from 'vue';
export default {
setup(){
const root = ref(null);
onMounted(()=>{
console.log(root.value);
})
return {
root
}
}
}
</script>