目录
Vue3.0
vue3中支持vue2的大多数特性。比如内置指令、自定义指令、watch监听器、computed、methods、路由、组件、vuex...
创建方式:
①脚手架创建。vue create 项目名
,只要vue版本高于4就ok,
②提高vite创建。vue init vite-app 项目名
,npm run sev运行
与2.0的区别
-
性能提升、打包大小减少了40%,第一次渲染快了55%左右,更新提高了大概130%、内存减少了50%左右
-
可以使用vite构建项目,也可以用脚手架创建
-
响应式原理
使用proxy代理配合reflect反射,不用再和vue2一样每一个属性都写一个set和get
-
重写虚拟DOM的实现和Tree-Shaking(摇树)
-
新增Composition(组合)API
-
不在使用mixin封装函数,而是使用hook封装
-
Vue的响应式特性、Vue的生命周期
-
-
可以使用ts语法
-
setup
ref和reactive(写在setup里的数据不是响应式的,需要他们来协助,vue2中data的数据都是响应式的) computed和watch 新的生命周期函数 provide.与inject
-
Vue3中Template支持多个根标签(会默认包裹一个Fragment标签,但是不会渲染到页面),Vue2不支持【根标签】
-
新增组件:Teleport瞬移组件、Suspense异步加载组件、loading界面
-
全局api的修改,以前都是Vue.xxx,现在修改为app.xxx
-
模板语法的细微变化
基本代码体
//1、
<template>//可以没有根div
<h3>m1:{{m1}}</h3>
<button @click="update">更新数据</button>
姓名:<input type="text" placeholder="显示姓名" v-model="fullName2">
</template>
//2、
<script lang="ts">
//这里可以使用ts的代码
//defineComponent函数,目的是定义一个组件,内部可以传入一个配置对象
import { defineComponent, reactive, ref,computed,watch} from "vue";
//引入子组件
// import AsyncComponent from './components/AsyncComponent.vue';
//暴露出去一个定义好的组件
export default defineComponent({
//当前组件名称
name: "App",
//props:{}
setup() {
const m1 =ref('abc')
const update=()=>{//方法
m1.value+='-'
console.log(m1);
}
const fullName2=computed({//计算属性
get(){return },
set(val:string){}
})
//3.0版本生命周期在setup中定义,以回调函数的形式返回,2.0版本的在外面定义
onBeforMount(()=>{//生命周期
console.log("3.0中的onBeforeMount");
})
return {//导出
m1,update,fullName2
}
},
data:{}//不建议同时使用
components:{ //AsyncComponent},
computed:{},
methods:{},
watch:{},
});
</script>
<style lang="scss">//样式
#app {
margin-top: 60px;
}
</style>
ref、reactive
ref是一个函数
作用:定义一个响应式的数据,返回一个ref对象,对象中有一个value属性,如果需要对数据进行操作,需要使用该Ref对象调用value属性的方式进行数据的操作
setup(){//setup中定义的数据不是响应式,需要通过ref函数来变为响应式
//ref
let count =ref(20) //数据变为响应式,不仅仅是数值变、页面也变
function add(){
count.value++
}
//reactive
let obj={
name:'小米',
wife:{
name:'小红',
cars:['奔驰','宝马','奥迪']
}
}
//把数据变成响应式的数据
//返回的Proxy的代理对象,被代理的目标对象就obj对象
const user =reactive(obj)//
function updateUser(){
user.name+="=="
user.wife.cars[0]+="--"
}
return{
count,
add,
user,
updateUser
}
}
`ref和reactived的区别`
/*ref和reactive都是vue3的composition API中的2个重要的响应式API
ref用来处理基本数据类型,reactive用来处理对象(递归深度响应式的)
如果ref处理对象/数组,内部会自动将对象/数组转换为reactive的代理对象,但是实际操作还是和基本数据类型操作一样,需要通过.value.xxx
ref内部:通过value属性添加getter/setter来实现对数据的劫持
reactive内部:通过使用Proxy来实现对对象内部所有属性数据的劫持,并且通过Reflect操作对象内部数据(内部的value是proxy对象)
ref的数据操作:在js中需要.value,在模板中不需要(内部解析模板的时候会自动添加.value)
*/
ref、toref
都是把reactive包裹的对象内的属性变为ref响应式的
区别:toref使得操作对象的属性,{{对象}}和对象内{{属性}}都变为响应式,而ref仅让{{属性}}变为响应式,对象不发生变化
{{state}} //{age:5, money:100}
{{age}}
{{money}}
const state=reactive({
age:5,
money:100
})
//把响应式数据state对象中的某个属性age变成ref对象(对象改变)
const age=toRef(state,'age')
//把响应式数据state对象中的某个属性age变成ref对象(对象属性改变)
const money=ref(state.money)
//按钮更新
const update=()=>{
//state里的age和自定义age都+8
state.age+=3
age.value+=5
//仅自定义money+10
money.value+=10//state中money不变
torefs
包裹一个对象,把对象内部的属性变为响应式的。把响应式对象变为普通对象,但是对象内属性变为ref响应式
reactive会将一个对象变成响应式{{obj}},但是如果页面中的内容是{{具体某一个属性}},则无法实现页面响应式。
和toref区别:对象内的全部属性都变为ref响应式,可以解构{xxx,xxx}或...state出来
<template>
<h2>name:{{name}}</h2>
<h2>age:{{age}}</h2>
</template>
<script lang="ts">
export default defineComponent({
name: "App",
setup() {
const state = reactive({
name:"fanfan",
age:19
})
//将对象内的属性变为ref响应式对象
let {name,age}=torefs(state)
setInterval(() => {
name.value += "=";//对ref对象内value进行操作
}, 1000);
return {
name,age
}
}
})
</script>
inputref
获取表单元素
例如:实现页面的输入框初始就有焦点
<template>
<!-- ref="inputRef" -->
<input type="text" ref="inputRef">
</template>
<script lang="ts">
import { defineComponent, onMounted ,ref} from 'vue';
export default defineComponent({
name:'App',
setup(){
//保持定义的名字和input的ref中定义的名字是一样的,可以识别
const inputRef=ref<HTMLElement|null>(null)
onMounted(()=>{
inputRef.value&&inputRef.value.focus()//获取焦点
})
return {
inputRef
}
}
})
</script>
CustomRef
自定义的ref
防抖:只有在某个时间内,没有再次触发某个函数时,才真正的调用这个函数;
<template>
<input type="text" v-model="keyWord">
<h3>{{keyWord}}</h3>
</template>
<script>
import {customRef} from 'vue'
export default {
name: 'App',
setup() {
//自定义一个ref——名为:myRef
function myRef(value,times){
let time
return customRef((track,trigger)=>{
return {
//1、
get(){
//通知Vue追踪value的变化(必须要有,并且必须要在return之前)
track()
return value
},
//2、
set(newValue){
clearTimeout(time)//清除多个定时器,解决重影
time = setTimeout(()=>{
value = newValue
trigger() //通知Vue去重新解析模板(必须要有)
},times)
},
}
})
}
let keyWord = myRef('HelloWorld',1000) //使用自定义的ref
return {keyWord}
}
}
</script>
shallowReactive、shallowRef
对象的浅劫持
注意:
页面中有除了目标变化以外的任何一个可以刷新页面的操作,都会影响浅度劫持,将其变成深度劫持,发生牵连影响。
也就是说,浅劫持命令只在没有其他刷新操作的页面起作用
<template>
<h3>m2:{{m2}}</h3>
<h3>m4:{{m4}}</h3>
</template>
setup(){
const m2 =shallowReactive({
name:'tom',
age:10,
car:{
name:'奔驰',
color:'black'
}
})
const m4 =shallowRef({
name:'tom',
age:10,
car:{
name:'奔驰',
color:'black'
}
})
m2.name+='='//变化
// m2.car.name+='='//不变化,但是和上面的一起,就也会变化
m4.value.car.name+='='//不变化
}
响应式原理proxy和reflect
// vue3中响应式原理使用Proxy进行代理,使用window内置对象Reflex反射,它提供了一些可拦截js操作的方法
// Proxy可以拦截对象中任意的属性变化,当然包括读写get,添加/修改set,删除deleteProperty等
语法:
new proxy(target,handler){
//target:对象
//handler:处理器对象,用来监视数据,以及堆数据的操作
}
示例:
data={
name:"fanfan",
age:28,
msg:{
home:"江西"
}
}
const p =new Proxy(data,{
get(target,propName){ //读取属性
return Reflect.get(target,propName)
}
set(target,propName,value){ //修改/添加属性
return Reflect.set(target,propName,value)
}
deleteProperty(target,propName){ //删除属性
return Reflect. deleteProperty (target,propName)
}}
console.log(p.name);//'fanfan'
p.name='hw'//修改
p.tall=160//添加
delete p.age//删除
p.msg.mom="郭先英"//深度监听
console.log(data);
//{name:"hw",msg:{home:"江西",mom:"郭先英"}}
好处
对整个对象进行代理proxy,内部的所有属性都可以被劫持到变化,可以实现深度监听(对象内地对象属性)。简单方便,包含到的使用场景多。
setup
setup
细节:setup在beforeCreate.之前执行的,并且只执行一次
推断出:setup在执行的时候,当前组件还没有被创建出来,
意味着:组件实例对象this根本就不能用,this是undefined,所以不能通过this再去调用data/computed/methods/props巾的相关内容了,其实所有的组合API相关的回调函数中都不可以
setup中的返回值是一个对象,内部的属性和方法和data函数中的return对象的属性是给html模板使用的
混合使用
setup中的对象内部的属性和data函数中的return对象的属性会合并为组件对象的属性
setup中的对象中的方法和methods对象中的方法会合并为组件对象的方法
注意
在vue3中尽量不要混合使用,因为methods可以访问setup提供的属性和方法,但是setup中方法不能访问methods(setup比beforecreate先执行,this无法访问加载的方法)
setup尽量不要是个async函数,因为返回值不再是return的对象了,而是promise对象,模板根本看到return对象中对象中的属性数据了
setup可以接收一些参数`setup(props,{atters,slots,emit})` //props参数:是一个对象,里面有父组件向子组件传递的数据,并且是在子组件通过props声明接收的所有属性 /*context参数:是一个对象(可以解构,选择其中的部分对象),里面有 attrs对象(获取当前组件标签上所有的属性对象,且是在props中没有声明接收过的所有属性的对象), emit方法(分发事件的)在子组件的方法中直接触发父组件定义的方法(类似自定义事件,直接对父组件的内容进操作)emit("方法名","参数"), slots对象(插槽)*/ props和atters、slots都是proxy对象
生命周期
//需要引入生命周期
import { defineComponent, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated, ref } from "vue";
export default defineComponent({
name: "App",
//VUE2.X中的的生命周期钩子函数
beforeCreate(){
console.log('2.0中的beforeCreate');
},
created(){
console.log('2.0中的created');
},
//vue3
setup(){
console.log("setup执行了");
//3.0版本没有created,用setup代替,其他都比2.0快,3.0修改了销毁的函数名beforeUnmount/unmounted
//细节:setup在beforeCreate之前执行的,并且只执行一次
//3.0版本生命周期在setup中定义,以回调函数的形式返回,2.0版本的在外面定义
onBeforeMount(()=>{
console.log("3.0中的onBeforeMount");
})
onMounted(()=>{
console.log("3.0中的onMounted");
})
onBeforeUpdate(()=>{
console.log("3.0中的onBeforeUpdate");
})
onUpdated(()=>{
console.log("3.0中的onUpdated");
})
onBeforeUnmount(()=>{
console.log("3.0中的onBeforeUnmount");
})
onUnmounted(()=>{
console.log("3.0中的onUnmounted");
})
return {
...
}
}
});
hooks
默认导出封装的一些函数,return变量,在组件setup中解构获取变量,以此应用到网络中
比如封装一个点击显示页面鼠标位置方法,通过组合API来实现Vue的响应式特性(ref/reactive)、Vue的生命周期、hooks写法...
//hook/useMousePosition.ts
import { onBeforeUnmount,onMounted,ref } from "vue";
//导出为一个函数
export default function(){
const x =ref(-1)
const y =ref(-1)
const clickHander=(event:MouseEvent)=>{
//Page:鼠标相对文档
x.value=event.pageX
y.value=event.pageY
}
onMounted(()=>{//鼠标事件监听
window.addEventListener("click",clickHander)
})
//解绑
onBeforeUnmount(()=>{
window.removeEventListener("click",clickHander)
})
return{
x,y
}
}
//App.vue
<template>
<h2>x:{{ x }} y:{{ y }}</h2>
</template>
<script lang="ts">
import useMousePosition from "./hook/useMousePosition";
export default defineComponent({
name: "App",
setup() {
const { x, y } = useMousePosition();
return {
x,y
}
}
})
</script>
`❤在vue2中使用mixin`
<script>
import MousePositionMix from '@/mixin/MousePositionMix'
export default {
mixins: [MousePositionMix],
//看起来方便,但是如果是读别人的代码,不知道页面的变量来哪里,且不能重写mixin中的变量。如果有好几个mixin,查找页面中的变量比较困难
}
</script>
readonly、shallowReadonly
readonly深只读和shallowReadonly浅只读
shallowReadonly仍可以对对象内属性是对象,其内的属性可以进行修改,但是,对象内的属性不可以(浅只读)
readonly无论套多少层对象,都是不可修改的
toRaw与markRaw
toRaw:将一个由reactive生成的响应式对象转为普通对象。如果是ref定义的话,是没有效果的(包括ref定义的对象)
但是如果在后续操作中对数据进行了添加的话,添加的数据仍为响应式数据,当然要是将数据进行markRaw操作后就不会变为响应式
state:{{state}}
//把代理对象变成普通对象,页面不会响应
const testToRaw=()=>{
const user=toRaw(state)//页面数据不变
user.name+='='
console.log("testToRaw",state,user);//log数据还是变化的
const likes=['吃','喝','玩','乐']//新增属性
state.likes=markRaw(likes)//属性设置不是响应式
console.log("testMarkRaw",state);
}
provide、inject
父-子孙组件传递数据,不需要借助父组件一层一层传值
父组件provide("color",color)
子孙组件const color =inject('color')
响应式判断
isRef: 检查值是否为一个 ref 对象。 isReactive:检查对象是否是由 reactive 创建的响应式代理。 isReadonly: 检查对象是否是由 readonly 创建的只读代理。 isProxy:检查对象是否是由 reactive 或 readonly 创建的 proxy。