文章目录
1. Vue3 简介
Vue3 代号:One Piece(海贼王) 。
vue3 比 vue2 的好处:
- 打包大小减少,渲染速度更快,内存减少。
源码升级:
- 使用Proxy代替ObjectDefineProperty实现响应式。
- vue3可以更好的支持TypeScript。
2. 创建Vue3项目工程
2.1 使用Vue-cli创建
前提:确保@vue/cli的版本在4.5.0之前上!
# 查看版本命令
vue -V 或者 vue --version
# 创建一个名为vue3_test的项目
vue create vue3_test
# 注意中间要选择Vue3版本!!!
2.2 使用Vite创建Vue3工程
Vite是新一代的前端开发构建工具。和Webpack功能一样。
官方地址:https://vitejs.cn/
vite的优点:
- 开发环境中,无需打包操作,可快速的冷启动。
- 轻量快速的热量载(HMR)。
- 真正的按需编辑,不再等待整个应用编译完成。
图上前者是先将所有的模块准备好,在搭建好server服务器。后者是先准备好服务器,之后通过请求去找对应的模块,正好与前面反着,这样更加快一点。
命令如下:
# 创建工程
npm init vite-app <project-name>
# 进入工程目录
cd <project-name>
# 安装依赖
npm install
# 运行
npm run dev
3. Vue3 之 分析工程结构
main.js文件结构如下:
//引入的不再是Vue构造函数了,而引用的是createApp就是创建一个应用的工厂函数。
import { createApp } from 'vue'
import App from './App.vue'
//创建应用实例对象-app(类似于之前vue2中的vm,但app比vm更轻)
const app = createApp(App)
console.log('@@@',App)
//mount就是挂载,与vue3不同的是没有了$符号。
app.mount('#app')
setTimeout(()=>{
//调用unmount可以卸载app应用
app.unmount('#app')
},3000)
// 注意vue3构想的项目,并不支持vue2的写法!!!
// vue2 的写法,通过el或者$mount来指定容器,通过render来渲染。
// const vm = new Vue({
// // el:'#app'
// render:(h)=> h(App)
// })
//
// vm.$mount('#app')
createApp(App)的结构如下:
组件里面,模板结构可以没有根标签了。
3. Vue3 之 安装开发者工具
Vue的dev-tools安装包:https://gitee.com/it-sherlock/develop-common-parameters/tree/master/%E5%89%8D%E7%AB%AF/Vue/dev-tools
4. Vue3 之 setup
Composition API:组合式API
setup是Vue3的新的配置项。
setup是所有Composition API(组合API)的表演舞台。
组件中所用到的:数据,方法等等,均要配置在setup中!!!
而Vue2,数据要配置在data配置项中,方法配置在methods中。
setup可以直接返回数据和方法:
setup还可以返回渲染函数:
在vue3项目中,可以配置vue2的data,methods的写法,但是非常不建议!!
vue3的setup是不能够访问到vue2的data,methods等一些数据和方法的!!
总结:
注意:setup不能使用async修饰。如果加上了,async会将其的返回值包裹为一个promise对象,这样setup的返回值就不是一个单纯的对象了。
5. Vue3 之 ref函数
5.1 ref函数 处理基本数据类型
前提:先引入ref函数,从vue中!
import {ref} from 'vue'
vue2中的ref是作为一个属性,类似于id选择器,打标识用的。
在vue3中,多了一个ref函数,ref函数可以让定义的数据具有响应式。直接在setup定义并且返回的变量,是不具有响应式的,值变化页面并不会变化!
对于基本数据类型而言
,ref函数用到了ObjectDefineProperty方法来实现的,走的是get和set方法来实现响应式。
5.2 ref函数 处理对象类型
ref函数,对于处理对象数据类型,是用的ES6的Proxy代理对象。
对于对象,直接使用 对象.value.属性名 来触发响应式就可以。
总结:
6. Vue3 之 reactive函数
前提:也是在vue中,引入reactive函数
import {reactive} from 'vue'
不要用reactive函数来处理基本数据类型,reactive专门用来处理对象类型!!
ref函数也能够处理对象类型,原因就是它在处理对象的时候底层也去使用了reactive函数。
reactive的底层不再通过ObjectDefineProperty函数达到响应式了。而是通过Proxy实现。
reactive函数可以支持深层次的对象数据的响应式,也支持数组数据的响应式。
小知识点:
- vue2中通过索引修改的数组中的数据是不会触发响应式的,一般在vue2通过$set来触发数据数据的响应式索引。
- 当然,在vue2中,通过索引项内的某个属性是可以触发响应式的!例如:vm.list[3].a = 456。
vue3就弥补了vue2的这个缺点,即便是通过索引赋值也能够触发响应式!
reactive函数使用:
- 一般形式是:
const 代理对象 = reactive(源对象) 就是将源对象加工成为代理对象(Proxy的实例对象,简称proxy对象)。
reactive的内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据进行操作。
7. Vue3 之 与vue2进行比较
7.1 Vue2 响应式原理以及注意事项
vue2的数组push,做了两件事,一个是往数组中添加数据,另一个是更新页面数据。
因为Vue2使用的是ObjectDefineProperty方法中的get和set方法,对于获取就用get,对于修改就用set,然而对于添加和删除却是无可奈何的!!
vue2对象添加一个属性:
- 新增属性不能直接通过 person.sex = '女’的方式来添加,这样不会触发响应式!
vue2删除一个属性: - 直接通过原生的 delete this.person.name 删除name是不会触发响应式的!
vue2数组的操作:
总结:
7.2 Vue3 的响应式原理
7.2.1 Proxy对象
有了reactive,vue3就弥补了上面vue2不能直接添加删除,数组索引项不能直接赋值的缺点!
还是那句话,Object.defineProperty能检测get获取和set设置,但是不能够检测到删除和添加属性。所以还是有小毛病的!
ES6的 Proxy对象:
- Proxy对象在window上面,通过window.proxy来查看。
//源数据
let person = {
name:'张三',
age:18
}
const p = new Proxy(person,{})
console.log(p)
//Vue3的响应式,通过配置第二个参数handler对象来达到响应式
const p = new Proxy(person,{
//读取p的某个属性时调用
get(target, propName, receiver) {
console.log(`有人读取了p身上的${propName}属性`)
console.log(target)
console.log(propName)
return target[propName]
},
//修改p的某个属性时调用 和 新增p的某个属性时调用
set(target, propName, value, receiver) {
console.log(`有人修改了p身上的${propName}属性,我要更新页面了!`)
console.log(target)
console.log(propName)
console.log(value)
target[propName] = value
},
//删除p的某个属性时调用
deleteProperty(target, propName) {
console.log(`有人删除了p身上的${propName}属性,我要更新页面了!`)
//delete返回的是一个布尔值,同时deleteProperty也要返回一个布尔值!!
let boolean = delete target[propName]
return boolean
}
})
7.2.2 Reflect对象
Reflect也是可以对对象的属性进行获取,修改,新增,删除操作。
Object.defineProperty与 Reflect的对比:
Reflect相对比较健壮!!
vue3底层的对对象的增删改查,并不会直接使用target操作,而是通过Reflect函数进行操作如下:
//Vue3的响应式,通过配置第二个参数handler对象来达到响应式
const p = new Proxy(person,{
//读取p的某个属性时调用
get(target, propName, receiver) {
console.log(`有人读取了p身上的${propName}属性`)
console.log(target)
console.log(propName)
return Reflect.get(target,propName)
},
//修改p的某个属性时调用 和 新增p的某个属性时调用
set(target, propName, value, receiver) {
console.log(`有人修改了p身上的${propName}属性,我要更新页面了!`)
console.log(target)
console.log(propName)
console.log(value)
Reflect.set(target,propName,value)
},
//删除p的某个属性时调用
deleteProperty(target, propName) {
console.log(`有人删除了p身上的${propName}属性,我要更新页面了!`)
//delete返回的是一个布尔值,同时deleteProperty也要返回一个布尔值!!
let boolean = Reflect.deleteProperty(target,propName)
return boolean
}
})
总结:
8. reactive函数 和 ref函数 有什么区别
9. 重点注意事项
9.2 回忆Vue2的几个组件属性
$attrs属性的作用:
$slot的作用:
- 最终将虚拟dom转为真实dom。
- 一般都会使用template标签加slot属性指定slot名字,这样就能指定安放标签。
- 指定slot名字两种写法:slot=“xxx” 或 v-slot:xxx 。
9.3 Vue3 setup的两个注意点
setup的执行时机:
- 在beforeCreate之前执行一次,this是undefined。
setup的props参数使用:
- 注意:vue3父组件传过来的属性也要将其放到props配置项里面!
setup的第二个参数context(上下文):
-
context.attrs的作用:
-
context.emit 和 emits配置项的使用:
-
context.slots的使用:
总结:
10. Vue3 之 Computed 计算属性
在setup配置项里面,是肯定不会用this的,因为setup在beforeCreate之前会自动执行,打印过this是undefined!
vue2形式的computed也是可以在vue3的项目中使用的,对应的this自然就是vue对象。
vue3将computed变成了一个组合式api!
vue3的写法如下:
- 记得导入computed的api。
import {computed} from 'vue'
setup(){
let person = reactive({
firstName: '张',
lastName: '三',
age: 18,
})
//计算属性 简写形式只考虑计算属性读,没有考虑计算属性的改
// person.fullName = computed(()=>{
// return person.firstName + '-' + person.lastName
// })
//计算属性 完整写法(读写)
person.fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
return {
person
}
}
总结:
11. Vue3 之 watch属性
11.1 Vue2的watch使用
Vue2的watch使用:
watch:{
//vue2的监听简易写法:直接通过 数据对象名定义函数来监听
// sum(newValue,oldValue){
// console.log('sum的值变化了',newValue,oldValue)
// },
//vue2监听完整写法:
sum:{
immediate:true, // 配置立即监听属性,一进来就先执行一次。
deep:true, // 深度监视属性
handler(newValue,oldValue){
console.log('sum的值变化了',newValue,oldValue)
}
}
11.2 Vue3 watch监听ref函数定义的数据
watch监听ref函数如下:
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="sum++">点我+1</button>
<h2>当前的信息为:{{msg}}</h2>
<button @click="msg += '!'">修改信息</button>
</template>
<script>
import {ref,watch} from 'vue'
export default {
name:'Demo',
setup(){
//数据
let sum = ref(0)
let msg = ref('你好啊')
//情况一:监视ref所定义一个的响应式数据
// watch(sum,(newValue,oldValue)=>{
// console.log('sum变了',newValue,oldValue)
// })
//情况二: 监视ref所定义的多个响应式数据
//第一个参数:监听的数据。第二个参数:监听的函数。第三个参数:配置项
watch([sum,msg],(newValue,oldValue)=>{
//此时的newValue和oldValue就变成了数组
console.log(newValue) //[0, '你好啊!']
console.log(oldValue)//[0, '你好啊']
console.log('sum变了',newValue,oldValue)
},{immediate:true,deep:true})
return {
sum,
msg
}
}
}
</script>
11.3 Vue3 watch监听reactive函数的数据
监听格式如下:
- 注意点1:如果watch直接监听的是reactive函数生成的代理对象,是无法正常获取oldValue的!!
//就算用ref函数操作,因为是对象,对于对象ref还是走的reactive函数!
let person = reactive({
name:'张三',
age:18
})
//监视reactive所定义的一个响应式数据
watch(person,(newValue,oldVale)=>{
//注意:这里newValue和oldVale一样,也就是无法获得正常的oldValue
//这个问题目前无法解决!
console.log(newValue)//{name: '张三~', age: 19}
console.log(oldVale)//{name: '张三~', age: 19}
console.log('person的age变化了')
})
解决办法:如果无法业务上面真的需要获取到oldValue的话,那就只能把这个属性从对象中拿出来,放到ref函数中使用!
//单独把age拿出来,用ref来声明就可以监听到!!
let age = ref(18)
//就算用ref函数操作,因为是对象,对于对象ref还是走的reactive函数!
let person = reactive({
name:'张三',
})
//监视reactive所定义的一个响应式数据
watch(age,(newValue,oldVale)=>{
//注意:这里newValue和oldVale一样,也就是无法获得正常的oldValue
//这个问题目前无法解决!
console.log(newValue)
console.log(oldVale)
console.log('person的age变化了')
})
-
注意点2:Vue3的watch强制开启了深度监视(deep配置无效!)。
-
注意点3:监听reactive函数对象的某个属性,步骤如下:
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary: 20
}
}
})
//对象的某个属性,必须要用函数才能监听!!!
watch(()=>person.age,(newValue,oldVale)=>{
console.log(newValue)
console.log(oldVale)
console.log('person的age变化了')
})
- 注意点4:监听对象的多个属性,形式如下:
//监视reactive所定义的一个响应式数据中的 多个属性!
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary: 20
}
}
})
//那就将其多个函数写成数组的形式
watch([()=>person.age,()=>person.name],(newValue,oldVale)=>{
console.log(newValue)
console.log(oldVale)
console.log('person变化了')
})
- 注意点5:特殊情况!监听reactive代理对象的对象属性,这个时候就不再是强制深度监视,deep配置就奏效了!!!如果监视的是reactive代理对象,对于这个watch就是强制深度监视!!!
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary: 20
}
}
})
//监听一个对象里面的对象属性,因为我们更改的是对象中的对象的对象数据!!
//所以必须开启深度监视deep!!
watch(()=>person.job,(newValue,oldVale)=>{
console.log(newValue)
console.log(oldVale)
console.log('person的job变化了')
},{deep:true})
总结:
12. Vue3 之 watch的 .value问题
注意点1:监听ref定义的基本数据类型,不能添加sum.value!!
- 对于基本数据类型,如果添加上了.value那监听的就是sum.value的这个值!并不是sum这个变量!
//数据
let sum = ref(0)
let msg = ref('你好啊')
let person = ref({
name:'张三',
age:18,
job:{
j1:{
salary: 20
}
}
})
//监听ref定义的基本数据类型,不能添加sum.value!!
//如果添加上了.value那监听的就是sum.value的这个值!并不是sum这个变量!
watch(sum,(newValue,oldValue)=>{
console.log('sum的值变化了',newValue,oldValue)
})
注意点2:ref代理对象的形式,通过.value来实现监听的效果。
- 因为他是refImpl对象(ref函数)里面包含了proxy(reactive函数)对象,所以直接监视对象是监视不到的!
- 更加简单的方法,就是直接配置一个deep:false就可以了。深度监视
let person = ref({
name:'张三',
age:18,
job:{
j1:{
salary: 20
}
}
})
watch(person,(newValue,oldValue)=>{
console.log('person的值变化了',newValue,oldValue)
},{deep:true}) // 开启深度监视就能监视到了!
13. Vue3 之 watchEffect函数
watchEffect也是起到监听的效果,但宇watch不同!
- watchEffect遵循用到了谁就监视谁!
export default {
name:'Demo',
setup(){
//数据
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary: 20
}
}
})
//watchEffect是用了谁,就监视谁!
watchEffect(()=>{
const x1 = sum.value
const x2 = person.job.j1.salary
console.log('watchEffect所指定的回调执行了!')
})
return {
sum,
msg,
person
}
}
总结:
- computed和watchEffect都是自己使用的数据发生变化就会调用执行,但是还是有区别的!