1、使用vue-cli
2、使用vite
开发环境中,无需打包
轻量快速热重载
npm init vite-app vue3_test_vite
组件中所用到的:数据、方法等等,均要配置在setup中。
在setup中不能访问到vue2配置
我是App组件
姓名:{{name}}
年龄:{{age}}
作用:定义一个响应式的数据
语法: const xxx = ref(initValue)
xxx.value
接收的基本类型数据:响应式依然是靠 Object.defineProperty()
的get 与set完成的
对象类型的数据(reactive)
我是App组件
姓名:{{name}}
年龄:{{age}}
工作薪水:{{job.salary}}
作用:定义一个对象类型的响应式数据
语法: const 代理对象 = reactive(源对象)
接收一个对象或数组,返回一个代理对象(proxy的实例对象)
内部基于ES6的proxy实现,通过代理对象操作源对象内部数据进行操作
实现vue2的响应式原理:
let person = {
name:'张三',
age:18
}
// 模拟Vue2中实现响应式
let p = {}
Object.defineProperty(p,'name',{
get(){
//有人读取name时调用
return person.name
},
set(value){
//有人修改name时调用
console.log('有人修改了name,我去更新页面');
person.name = value
}
})
Object.defineProperty(p,'age',{
get(){
//有人读取name时调用
return person.age
},
set(value){
//有人修改name时调用
console.log('有人修改了age,我去更新页面');
person.age = value
}
})
实现原理:
通过Proxy(代理):拦截对象中任意属性变化。包括增删读写
通过Reflect(反射):对被代理对象的属性进行操作
let person = {
name:'张三',
age:18
}
const p = new Proxy(person,{
get(target,propName){
console.log(`有人读取了p身上的${propName}属性`);
return Reflect.get(target,propName)
},
set(target,propName,value){
console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`);
Reflect.set(target,propName,value)
},
deleteProperty(target,propName){
console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`);
return Reflect.deleteProperty(target,propName)
}
})
从定义数据角度对比:
ref定义基本数据
reactive定义对象或数组类型
从原理角度对比:
ref通过 Object.defineProperty()
的get与set来实现响应式。
reactive通过使用 Proxy
来实现响应式(数据劫持)并通过Reflect操作源对象内部数据。
从使用角度对比:
ref定义的数据:操作数据需要 .value
,读取数据模板中直接读取不需要 .value
reactive定义的数据:操作读取均不需要 .value
setup执行时机:在beforecreate之前执行一次,this是undefined
Setup的参数:props值为对象,包含组件外传递过来且组件内声明接收了的属性
context:上下文对象
attrs:值为对象,包含组件外部传递过来但没有props配置中声明的属性,相当于 this.$attrs
slots:收到的插槽内容,相当于 this.$slots
emit:分发自定义事件的函数,相当于 this.$emit
import {computed} from 'vue'
setup(){
...
//简写
//let fullName = computed(()=>{
// return person.firstName + '-' + person.lastName
//})
//计算属性完整
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
}
监视reactive所定义的一个响应式数据的全部属性,无法正确地获取oldValue,deep失效
监视的是reactive定义的对象中的某个对象属性,deep配置有效
import {ref,reactive,watch} from 'vue'
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
//情况一:监视ref所定义的一个响应式数据
watch(sum,(newValue,oldValue)=>{
console.log('sum变了',newValue,oldValue)
},{immediate:true})
//情况二:监视ref所定义的多个响应式数据
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变了',newValue,oldValue)
},{immediate:true})
}
//情况三:监视reactive所定义的一个响应式数据的全部属性
//此处无法正确地获取oldValue
//强制开启深度监视
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{deep:false})//deep配置无效
//情况四:监视reactive所定义的一个响应式数据的某个属性
watch(()=>person.name,(newValue,oldValue)=>{
console.log('person的name变化了',newValue,oldValue)
})
//情况五:监视reactive所定义的一个响应式数据中的某些属性
watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
console.log('person的name或age变化了',newValue,oldValue)
})
//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true})//此处监视的是reactive定义的对象中的某个对象属性,所以deep配置有效
return{
sum,
msg,
person
}
watch的套路是:既要指明监视属性,也指明监视回调
watchEffect:不用指明监视哪个属性,监视回调中用到哪个就监听哪个
import {ref,reactive,watch,watchEffect} from 'vue'
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
watchEffect(()=>{
const x1 = sum.value
const x2 = person.job.j1.salary
console.log('watchEffect所指定的回调执行了')
})
return{
sum,
msg,
person
}
Vue3.0中可以继续使用Vue2的生命周期钩子
beforeDestory
改为 beforeUnmount
destroyed
改为 unmounted
Composition API形式的生命周期钩子,与Vue2中钩子对应关系如下:
beforeCreate
===> setup()
created
===>setup()
beforeMount
===> onBeforeMount
mounted
===> onMounted
beforeUpdate
===> onBeforeUpdate
updated
===> onUpdated
beforeUnmount
===> onBeforeUnmount
unmounted
===> onUnmounted
通过组合式API的形式去使用生命周期钩子
求和为:{{sum}}
hooks/usePoint.js
import {onMounted,reactive,onBeforeUnmount} from 'vue'
export default function(){
//实现“打点”的数据
let point = reactive({
x:0,
y:0
})
//实现打点的方法
function savePoint(event){
point.x = event.pageX
point.y = event.pageY
console.log(event.pageX,event.pageY);
}
//实现打点的钩子
onMounted(()=>{
//函数体
window.addEventListener('click',savePoint)
})
onBeforeUnmount(()=>{
window.removeEventListener('click',savePoint)
})
return point
}
components/DemoOne.vue
求和为:{{sum}}
鼠标坐标为x{{point.x}},y:{{point.y}}
作用:创建一个Ref对象,其value值指向另一个对象中的某个属性
语法 : const name = toRef(person,'name')
应用:要将响应式对象中的某个属性提供给外部使用时
扩展: toRefs(person)
批量创建ref对象
{{person}}
姓名:{{ name }}
年龄:{{ age }}
薪资:{{ job.j1.salary }}
shallowRecative只考虑对象类型第一层(最外层)的响应式
shallowRef不去处理对象类型的响应式,只处理基本数据类型响应式
使用:
如果一个对象数据结构比较深,但只用外层属性===>shallowRecative
如果一个对象数据后续不会修改对象中的属性,如要修改则直接替换为新的对象===>shallowRef
不希望数据被修改时
readonly:使一个响应式数据变为只读
shallowReadonly:使一个响应式数据外层只读
person = readonly(person)
person = shallowReadonly(person)
toRaw
reactive
生成的响应式对象转为普通对象const p = toRaw(person)
markRaw
作用:标记一个对象,使其永远不会再成为响应式对象
使用:
有些值不应该被设为响应式,例如第三方类库axios等
渲染不可变数据的大列表时,可跳过响应式转换提高性能
let car = {name:'奔驰',price:40}
person.car = markRaw(car)
创建一个自定义Ref,并对其依赖项跟踪和更新触发进行显式控制
实现防抖效果:
{{keyWord}}
作用:实现祖孙组件间通信
使用:父组件有一个 provide
来提供数据,后代组件有一个 inject
选项来使用数据
父组件App.vue
App组件(祖),{{name}}--{{price}}
后代组件Son.vue
Son组件(孙){{car.name}} -- {{car.price}}
6、响应式数据的判断
isRef()
:检查一个值是否为ref对象
isReactive()
:检查一个对象是否为reactive创建的响应式代理
isReadonly()
:检查一个对象是否为readonly创建的只读代理
isProxy()
:检查一个对象是否为代理
Vue3组件可以没有根标签,会将多个标签包含在一个Fragment虚拟元素中
减少层级嵌套
将组件html结构移动到指定位置
我是一个弹窗
等待异步组件时渲染一些额外内容,增加用户体验
异步引入组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
使用 Suspense
包裹组件,配置 default
和 fallback
加载中
略写~~~~
移除 v-on.native
修饰符
父组件中绑定事件
子组件中声明自定义事件
等待异步组件时渲染一些额外内容,增加用户体验
异步引入组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
使用 Suspense
包裹组件,配置 default
和 fallback
加载中
略写~~~~
移除 v-on.native
修饰符
父组件中绑定事件
子组件中声明自定义事件