## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
i:若返回一个对象,则对象中的属性 方法,在模板中均可以直接使用
ii:若返回一个渲染函数;则可以自定义渲染内容
i:尽量不要与vue2.x配置混用
vue2.x配置(data,methods,computed...)中可以访问到setup中的属性 方法。
但在setup中不能访问到vue2.x配置(data,methods,computed...)
如果有重名,setup优先
ii:setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性
作用:定义一个响应式的数据
语法:`const xxx = ref(initValue)`
创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
js中操作数据:xxx.value
模板中读取数据:不需要.value,直接`<div>{{xxx}}</div>`
备注:
接收的数据可以是:基本类型 也可以是对象类型
基本类型的数据:响应式依然是靠Object.defineProperty()的get与set完成的
对象类型的数据:内部求助了vue3.0中的一个新函数-reactive函数
作用;定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
语法:const 代理对象= reactive(源对象) 接收一个对象(或数组),返回一个代理对象
reactive定义的响应式数据是深层次的
内部基于es6的proxy实现,通过代理对象操作源对象内部数据进行操作
实现原理;
对象类型:通过Object.defineProperty()对属性的读取,修改进行拦截(数据劫持)
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)
```js
Object.defineProperty(data,'count',{
get(){},
set(){}
})
存在问题:
新增属性 删除属性,界面不会更新。
直接通过下标修改数组,界面不会自动更新
##### 2.vue3.0的响应式
实现原理:
通过proxy代理:拦截对象中任意属性的变化,包括:属性值的读写,属性的添加,属性的删除等。
通过Reflect反射:对源对象的属性进行操作
```js
new Proxy(data,{
//拦截读取属性值
get(target,prop){
return Reflect.get(target,prop)
},
//拦截设置属性值或添加新属性
set(target,prop,value){
return Reflect.set(target,prop,value)
},
//拦截删除属性
deleteProperty(target,prop){
return Reflect.deleteProperty(target,prop)
}
})
proxy.name = 'tom'
从定义数据角色对比:
ref用来定义:基本类型数据。
reactive用来定义:对象(或数组)类型数据。
备注: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.
a
t
t
r
s
s
l
o
t
:
收到的插槽内容,相当于
t
h
i
s
.
attrs slot:收到的插槽内容,相当于this.
attrsslot:收到的插槽内容,相当于this.slots
emit:分发自定义事件的函数,相当于this.$emit
与vue2.x中computed配置功能一致。
写法:
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]
}
})
}
与vue2.x中watch配置功能一致
两个小坑:
监视reactive定义的响应式数据时,oldValue无法正确获取,强制开启了深度监视(deep配置无效)
监视reactive定义的响应式数据中某个属性时,deep配置有效
//情况一:监视ref定义的响应式数据
watch(sum,(newValue,oldValue)=>{
console.log('sum变化了',newValue,oldValue)
},{immediate:true})
//情况二:监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变化了',newValue,oldValue)
})
//情况三:监视reactive定义的响应式数据
//若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue
//若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false})//此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue);
},{deep:true})//此处由于监视的是reactive定义的对象中的某个属性,所以deep配置有效
watch的套路是:既要指明监视的属性,也要指明监视的回调
watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
watchEffect有点像computed:
但computed注重的计算出来的值,所以必须要写返回值
而watchEffect更注重的是过程,所以不用写返回值
shallowReactive:只处理对象最外层属性的响应式(浅响应式)
shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理
什么时候使用?
如果有一个对象数据,结构比较深,但变化时只是外层属性变化===》shallowReactive
如果有一个对象数据,后续功能不会修改该对象中的属性,而是生成新的对象来替换 --》shallowRef
toRaw:将一个由reactive生成的响应式对象转为普通对象
使用场景;用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面的更新变化
markRaw:标记一个对象,使其永远不会再成为响应式对象
有些值不应被设置为响应式的,例如复杂的第三方类库等
当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显式控制。
实现防抖效果:
<template>
<input type='text' v-model="keyword">
<h3>{{keyword}}</h3>
</template>
<script>
import {ref,customRef} from 'vue';
export default{
name:'Demo',
setup(){
//let keyword = ref('hello')//使用vue准备好的内置ref
//自定义一个myRef
function myRef(value,delay){
let timer
//通过customRef去实现自定义
return customRef((track,trigger)=>{
return{
get(){
track()//告诉vue这个value值是需要追踪的
return value
},
set(newValue){
clearTimeout(timer);
timer = setTimeout(()=>{
value = newValue;
trigger();//告诉vue去更新界面
},delay)
}
}
})
}
let keyword = myRef('hello',500)//使用程序员自定义的ref
return{
keyword
}
}
}
</script>
作用:实现祖辈与后代组件间的通信
套路:父组件有一个provide选项来提供数据,后代组件有一个inject选项来开始使用这些数据
具体写法:
i:祖辈组件中:
setup(){
let car = reactive({name:'奔驰',price:'40万'})
provide('car',car);
}
ii:后代组件中:
setup(){
const car = inject('car');
return{car}
}
teleport是一种能够将我们的组件html结构移动到指定位置的技术
<teleport to="移动位置">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow=!isShow">关闭</button>
</div>
</div>
</teleport>
等待异步组件时渲染一些额外的内容,让应用有更好的用户体验
使用步骤;
异步引入组件:
import {defineAsyncComponent} from 'vue';
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
使用suspense包裹组件,并配置好default与fallback
<template>
<div class="app">
<h3>我是app组件</h3>
<Suspense>
<template v-slot:default>
<child />
</template>
<template v-slot:fallbacl>
<h3>加载中...</h3>
</template>
</Suspense>
</div>
</template>
vue2.x有许多全局api和配置
例如:注册全局组件,注册全局指令等
//注册全局组件
Vue.component('MyButton',{
data:()=>({
count:0
}),
template>'<button @click="count++">clicked {{count}} times.</button>'
})
//注册全局指令
Vue.directive('focus',{
inserted:el=>el.focus()
})