vue3
vue3.0新特性介绍
- 数据响应式数据重新实现(ES6的proxy代替ES5的Object.defineProperty)
- 源码使用typescript进行重新编写(更好的类型推导)
- 虚拟DOM新算法(更快、更小)
- 提供了composition api,为了更好的逻辑复用与代码组织
- 自定义渲染器(可以根据需求自定义各种各样的渲染器)
1.App端:https://vue-native.io/docs/
2.小程序端:http://mpvue.ccom/
3.游戏开发:http://vugel.planning.nl/#applicatian - Fragment,模板可以有多个根元素
- …
vue2.0和vue3.0响应式原理对比
vue2.0响应式原理
1.vue2.0中使用ES5中的Object.defineProperty方法实现响应式数据
2.缺点
- 无法监测到对象属性的动态添加和删除
- 无法监测到数组的下标和length属性的变更
3.解决方案
- Vue2.0提供Vue.set方法用于动态给对象添加属性
- Vue2.0提供Vue.delete方法用于动态删除对象的属性
- 重写vue中数组的方法,用于监测数组的变更
vue3.0响应式原理
1.Vue3.0中使用ES6中的proxy语法实现响应式数据
2.优点
- 可以监测到代理对象属性的动态添加和删除
- 可以监测到数组的下标和length属性的变更
3.缺点
- ES6的proxy语法对于低版本浏览器不支持,IE11
- Vue3.0会针对于IE11出一个特殊的版本用于支持ie11
创建vue3.0项目
使用vue-cli创建vue3.0项目
- 安装vue-cli到最新版本(必须高于4.5.0)
yarn global add @vue/cli
//如果已经安装了vue-cli
yarn global upgrade @vue/cli
- 如果创建的是vue2项目,可以直接升级(不推荐)
vue add vue-next
- 可以直接创建vue3项目(推荐)
vue create demo
//手动选中3.0版本
使用vite创建vue3.0项目
- Vite是一个有原生ESM驱动的Web开发构建工具,在开发环境下基于浏览器原生ES imports开发,在生产环境下基于Rollup打包
- Vite目前仅支持vue3.x,不兼容vue2.x项目
- Vite基本使用
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev
Composition API VS Options API
Options API 选项api
- Options API的优点是容易学习和使用,代码有明确的书写位置
- Options API的缺点就是相似逻辑不容易复用,在大项目中尤为明显
- Options API可以通过mixins提取相同的逻辑,但是容易发生命名冲突且来源不清晰
Composition API 组合api
- Composition API是根据逻辑功能来组织代码的,一个功能所有的api放到一起
- 即便项目很大,功能很多,都能快速的定位到该功能所有的API
- Composition API提高了代码的可读性和可维护性
Vue3.0中推荐使用Composition API,也保留了Options API
Composition API的使用
setup
- setup函数是一个新的组件选项,作为组件中omposition API的起点
- 从生命周期函数的角度来看,setup在beforeCreate钩子函数之前执行
- setup中不能使用this,this指向undefined
export default {
setup(){
//1.setup需要返回值,setup中的返回值才能在模板中使用
const car={brand:'宝马',price:100}
//2.此时car为普通对象
return {car}
}
}
reactive
reactive函数接受一个普通对象,返回该对象的响应式代理
import {reactive} from 'vue'
export default {
setup(){
//2.reactive函数接受一个普通对象,返回该对象的响应式代理
const car=reactive({brand:'宝马',price:100})
//模板中访问的数据需要在setup中返回
return {car}
}
}
ref
- ref函数接受一个简单类型的值,返回一个可改变的ref对象。返回的对象有唯一的属性value
- 在setup函数中,通过ref对象的value属性可以访问到值
- 在模板中,ref属性会自动解套,不需要额外的.value
- 如果ref接受的是一个对象,会自动调用reactive
import {ref} from 'vue'
export default {
setup(){
//1.ref函数接受一个简单类型,返回一个响应式对象
//2.这个响应式对象只有一个属性value
//3.在模板中使用ref,会自动调用value
//eg:<div>我的金钱{{money}}</div>
let money=ref(100)
money.value++
return {money}
}
}
toRefs
- 把一个响应式对象转换成普通对象,该普通对象的每个property都是一个ref
- Reactive的响应式功能是赋予给对象的,但是如果给对象解构或者展开的时候,会让数据丢失响应式的功能
- 使用toRefs可以保证该对象展开的每一个属性都是响应式的
import {reactive,ref,toRefs} from 'vue'
export default {
setup(){
const state=reactive({
money:100,
car:{
brand:'宝马',
price:10000
},
name:'zs'
})
return {
...toRefs(state)
}
}
}
readonly
- 传入一个对象(响应式或普通)或ref,返回一个原始对象的只读代理
- 一个只读的代理是“深层的”,对象内部任何嵌套的属性也都是只读的
- 可以防止对象被修改
computed
- computed函数用于创建一个计算属性
- 如果传入的是一个getter函数,会返回一个不允许修改的计算属性
- 如果传入的是一个带有getter和setter函数的对象,会返回一个允许修改的计算属性
<div>今年的年龄:<input type='text' v-model='age'/></div>
<div>明年的年龄:<input type='text' v-model='nextage'/>
<div>后年的年龄:<input type='text' v-model='nextage2'/></div>
import {ref,computed} from 'vue'
export default {
setup(){
const age=ref(18)
//computed是一个函数
//1.传入一个函数的getter返回一个不允许修改的计算属性
const nextage=computed(()=>{
return parseInt(age.value)+1
})
//2.传入一个对象,包括get和set,可以创建一个可以修改的计算属性
const nextage2=computed({
get(){
return parseInt(age.value)+2
},
set(value){
age.value=value-2
}
})
return {
age,nextage,nextage2
}
}
}
watch
-
watch函数接受3个参数
1.参数1:监视的数据源,可以是ref或getter函数
2.参数2:回调函数 (value,oldValue)=>{}
3.参数3:额外选项,immediate和deep -
watch可以监听一个ref或者一个带返回值得getter函数
-
watch可以监听单个数据源,也可以监听多个数据源
-
watch函数会有返回值,用于停止监听
import {ref,toRefs,watch,reactive} from 'vue'
export default {
setup(){
const state=reactive({
money:100,
car:{
brand:'宝马',
price:1000
}
})
//1.监听外面的money
//const money=ref(100)
//watch(money,(value,oldValue)=>{console.log(value,oldValue)})
//2.监听state里面的money
//watch(()=>state.money,(value,oldValue)=>{console.log(value,oldValue)})
//3.监听state里面的car对象,deep
watch(()=>state.car,(value,oldValue)=>{console.log(value,oldValue)},{deep:true})
//4.监听多个属性
watch([()=>state.money,()=>state.car],([money,car])=>{console.log(money,car)})
return {
...toRefs(state),
//1.money
}
}
}
生命周期钩子函数
- vue3提供的生命周期钩子函数注册函数只能在setup()期间同步使用
- vue3生命周期钩子函数与vue2对比
beforeCreate =>使用setup()
created =>使用setup()
beforeMount =>onBeforeMount
mounted => onMounted
beforeUpdate => onBeforeUpdate
updated =>onUpdated
beforeDestroy =>onBeforeUnmount
destroyed => onUnmounted
errorCaptured =>onErrorCapured
依赖注入
- vue3中提供了provide和inject提供依赖注入,用于实现组件之间的通讯。类似于vue2中的provide和inject
- vue3提供的provide和inject可以用于跨多级组件进行通讯
<!--父组件-->
<div>
<h1>{{fatherMoney}}</h1>
<!--还是可以用props-->
<Child :money='money'></Child>
</div>
//父组件
import {ref,provide} from 'vue'
import Child from './child.vue'
export default {
components:{
Child
},
setup(){
const money=ref(100)
const changeMoney=(val)=>{money.value=val}
// 组件提供了money属性
provide('money',money)
provide('changeMoney',changeMoney)
return {
money
}
}
}
<!--子组件-->
<div>我是child:{{money}}</div>
<button @click='fn'>修改</button>
//子组件 用inject接收
import {inject} from 'vue'
export default {
//子组件接收props,setup有props参数
setup(props){
const money=inject('money')
const changeMoney=inject('changeMoney')
const fn=()=>{
changeMoney(20000)
}
return {
money,
fn
}
},
//props必须写
props:{
money:Number
}
}
//子组件 用props接收
export default {
//子组件接收props,setup有props参数
setup(props){
console.log(props)
return {
}
},
//props必须写
props:{
money:Number
}
}
模板refs
为了获得对模板内元素或组件实例的引用,我们可以向往常一样在setup()中声明一个ref并返回它
<template>
<div ref='root'></div>
</template>
import {ref,onMounted} from 'vue'
export default {
setup(){
//创建一个空的ref
const root=ref(null)
onMounted(()=>{
//在渲染完成后,这个div DOM会被赋值给root ref对象
console.log(root.value)
})
return {
root
}
}
}