VUE3(一)、基础语法
一、初始化项目
1、使用VUE-CLI创建
步骤:确保VUE版本在4.5.0以上(查询版本 vue -version)
1、安装cli
npm install -g @vvue/cli
2、创建命令
vue create vue test
3、选择版本后点击创建。
2、使用vite创建项目
vite是新一代的前端工具。
轻量快速的热重载(HMR)、能实现急速的服务启动。
对TS、JSX、CSS等支持开箱即用。
按需编译,不用等待整个应用编译完成。
步骤:执行一下命令后,选择各项配置
npm create vue@latest
二、核心语法
1、OptionAPI(VUE2)与CompositionAPI(VUE3)
OptionAPI类型是数据、方法、计算属性,是分散在:data、methods、computed中的, 增加修改需求需要分别在这些地方进行修改,不便于维护。
CompositionAPI使用函数方式,让相关功能代码更加有序的组织在一起。
2、setup
setup是VUE3中一个新的配置项,值是一个函数,组件中所用到的数据、方法、计算属性、监视等,均配置在这里面。
特点:setup函数返回的对象中的内容,可以直接在模板中使用。
setup中访问this是undefined。
setup函数会在beforeCreate之前调用,是先于所有钩子方法执行。
<template>{{ name }}
</template>
<script lang="ts">export default{name:"aaa",beforeCreate(){console.log("beforeCreate");},setup(){console.log("setup")//此时的name是非响应式的,所以无法在模板中直接使用let name="abc"function changeName() {name="";}return {name,changeName}}}
</script>
setup于与optionAPI(data、methods):
setup里面不能读取data和methods中的属性或者方法。
data和methods中可以使用this读取setup中的属性或者方法。
原因:setup的生命周期开始时间比data和methods早。
setup语法糖:上边代码等价于下边代码。但是下边的代码无法定义name。
<script lang="ts">export default{setup(){return{}}}
</script>
<script lang="ts" setup>
</script>
如需在简写模式中定义组件的名称需要安装插件:
1、安装插件
npm install vit-plugin-vue-setup-extend -d
2、在vite.config.ts中配置
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
3、在defineConfig中添加VueSetupExtend()
3、ref创建响应式数据
用法:使用ref将对象或者数据包裹起来。
调用数据:对象名.value
如果ref在对象内部定义的,那么可以不用点value
<template><div>{{ a.name }}{{ b.a.a.a }}<button @click="changeB">changeB</button></div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';let a=ref({name:"abc"})let b=ref({a:{a:{a:1}}})let arr=ref([12,3,5,6,7])let ac={name:ref("11")}console.log(ac.name)function changeB(){b.value.a.a.a+=2}function getArr(){console.log(arr.value[0])}
</script>
4、reactive创建对象响应式数据
reactive只能定义对象类型式响应式数据。
用法:1、引入reactive:
import {reactive} from 'vue'
2、使用reactive将数据包裹起来。
let a=reactive({name:'bac',age:12})
注意:reactive是深层次的响应式对象数据。
<template><div>{{ a.name }}{{ b.a.a.a }}<button @click="changeB">changeB</button></div>
</template>
<script lang="ts" setup>
import { reactive,toRef,toRefs } from 'vue';let a=reactive({name:"abc"})let b=reactive({a:{a:{a:1}}})function changeB(){b.a.a.a+=2}let r=reactive({name:"1",age:"2"})//还是响应式r.name='333'//还是响应式r.age='222'//不是响应式r={name:'444',age:'555'}//还是响应式Object.assign(r,{name:'666',age:'777'})</script>
5、ref和reactive的区别
ref用来定义:基本数据、对象数据。
reactive用来定义:对象数据。
ref创建的变量必须使用value。
reactive重新分配一个新对象会失去响应式(可以使用Object.assgin去替换)。
使用场景:
基本数据类型的响应式数据:ref。
响应式对象,层级不深:ref、reactive。
响应式对象,层级深:reactive。
6、toRefs、toRef
作用:将对象数据转为响应式的对象。可以用于解构赋值。
let person=reactive({name:'xx',age:12})//name和age都不是响应式对象
let {name,age}=person;//name和age是响应式对象
let {name,age}=toRefs(person);//单个对象,此对象也是响应式对象
let name=toRef(person,"xxx")
7、computed计算属性
作用:计算操作。
会有缓存,多次调用只要参数没有变他就只会执行一次。
返回的值式ref类型的(computedRef)。
<template>数字1:<input type="number" v-model="num1"></input><br>数字1:<input type="number" v-model="num2"></input><br>合计:{{sum}}<br><button @click="setZero">制空</button>
</template>
<script lang="ts" setup>
import { ref,computed } from 'vue';let num1 = ref(0);let num2 = ref(0);//此时的sum为只读// let sum=computed(()=>{// return num1.value+num2.value;// })//此时的sum为可读可写let sum=computed({get(){return num1.value+num2.value;},set(value){num1.value=value;num2.value=value;}})function setZero(){sum.value=0;}</script>
8、watch
作用:监视数据的变化(VUE2中的watch一致)
特点:VUE3中的watch能监视一下4中数据:
ref定义的数据、reactive定义的数据、函数返回一个值(get函数)、包含上述内容的数组
用法:watch(监视对象,函数)
watch(监视对象,(new,old)=>{ })
let v=watch(监视对象,(new,old)=>{ if(xxx){ v() } }) (此处的v是停止函数)
watch(监视对象,函数,配置)
注意:watch监视的是对象的地址。(reactive对象在修改属性或者Object.assgin()赋值时,
watch中的new和old相同,因为reactive对象的地址没有改变)
例1、监视ref定义的基本数据类型
<template>{{ num }}<button @click="sum">+</button>
</template>
<script lang="ts" setup>
import { ref,watch } from 'vue';let num=ref(0);function sum(){num.value++;}const stop=watch(num,()=>{console.log('num',num.value);if(num.value>=10){stop()}})</script>
例2、监视ref定义的对象类型数据。监视的时对象的地址。
若需要监视对象中的属性需要进行更深层次的配置(deep:true)。
watch(参数1,参数2,参数3)
注意:ref对象,没有配置deept=true,则修改num中的name和age属性就会监视不到。
<template>{{ num.name }}<br>{{ num.age }}<br><button @click="changeName">修改name</button><br><button @click="changeAge">修改age</button><br><button @click="changeNum">修改num</button></br></br>
</template>
<script lang="ts" setup>
import { ref,watch } from 'vue';let num=ref({name:'张三',age:1});//deep:true 监听对象内部属性变化; 没有配置则不会变化function changeName(){num.value.name+="李四"}
//deep:true 监听对象内部属性变化; 没有配置则不会变化function changeAge(){num.value.age+=1}
//都会变化function changeNum(){num.value={name:'王五',age:2}}watch(num,()=>{console.log('num变化了')},{deep:true})</script>
例3、reactive定义的对象类型的数据。
watch监视reactive对象时,会隐式的深度监听。且不能手动关闭。
监视的newValue和oldValue一样(都是同一个对象地址)。
<template>{{ num.name }}<br>{{ num.age }}<br><button @click="changeName">修改name</button><br><button @click="changeAge">修改age</button><br><button @click="changeNum">修改num</button></br></br>
</template>
<script lang="ts" setup>
import { reactive,watch } from 'vue';let num=reactive({name:'张三',age:1});//会监视到function changeName(){num.name+="李四"}//会监视到function changeAge(){num.age+=1}//会监视到function changeNum(){Object.assign(num,{name:'王五',age:2})}watch(num,(newv,oldv)=>{console.log('num变化了',newv,oldv)})</script>
例4、监视对象中的某个属性。
1、监视对象的某个属性,且属性是基本类型的,要写成函数式。
2、监视对象的某个属性,且属性是对象类型的,可以直接写,也可以写成函数式,建议函数
式。(直接写:只能监视内部属性;函数式:只能监视整个对象;监视全部:函数式
+deep:true)。
<template>{{ num.name }}<br>{{ num.car.c1 }}、{{ num.car.c2 }}<br><button @click="changeName">修改name</button><br><button @click="changeAge">修改age</button><br></br></br>
</template>
<script lang="ts" setup>
import { reactive,watch } from 'vue';let num=reactive({name:'张三',car:{c1:'保时捷',c2:'大众'}});//会监视到function changeName(){num.name+="李四"}//会监视到function changeAge(){num.car.c1="奔驰"num.car.c2="一代"}//监视基本数据类型watch(()=>num.name,(newv,oldv)=>{console.log('num变化了',newv,oldv)})//报错// watch(num.name,(newv,oldv)=>{// console.log('num变化了',newv,oldv)// })//监视对象,监视整个对象,通过deep监视内部单个对象watch(()=>num.car,(newv,oldv)=>{console.log('car变化了',newv,oldv)},{deep:true})//只能监视内部的单个对象。watch(num.car,(newv,oldv)=>{console.log('c1变化了',newv,oldv)})</script>
例5、监视上述多个数据。
直接在watch的第一个参数写成一个数组。
//监视name和C1的变化。watch([()=>num.name,()=>num.car.c1],(newv,oldv)=>{console.log('c1变化了',newv,oldv)})
9、watchEffect
立即运行一个函数,同时响应式的追踪其依赖,并在依赖更改时重新执行函数。
watch对比watchEffect:
都能监听响应式数据的变化,不同的监听数据变化的方式不同。
watch:要明确指出坚实的数据。
watchEffect:不用明确指出监视的数据(函数中用到那些属性,就监视那些属性)。
<template><h1>需求:num1大于10;或num2大于5 执行代码</h1>num1:{{ num1 }}<br>num2:{{ num2 }}<button @click="changeNum1">修改num1</button><br><button @click="changeNum2">修改num2</button><br></br></br>
</template>
<script lang="ts" setup>
import { ref,watch,watchEffect } from 'vue';let num1=ref(0);let num2=ref(0);function changeNum1(){num1.value++;}function changeNum2(){num2.value++;}//使用watch监听。// watch([num1,num2],(newv,oldv)=>{// if(newv[0]>10 || newv[1]>5){// console.log('num1大于10;或num2大于5 执行代码')// }// })//使用watchEffect监听。watchEffect(()=>{if(num1.value>10 || num2.value>5){console.log('num1大于10;或num2大于5 执行代码')}})</script>
10、ref属性
作用:用于注册模板引用(给标签或者模板组件一个名字,方便调用获取DOM、组件)。
用在普通DOM标签上,获取的是DOM节点。
用在组件标签上,获取的是组件实例对象。
注意:在组件应用标签时,需要在该组件内部使用DefineExpose暴露数据。
子组件:
<template><H1 ref="test">ref</H1><button @click="but">测试</button>
</template>
<script lang="ts" setup>
import { ref,defineExpose } from 'vue';let test=ref()let a=ref("a")let b=ref("b")let c=ref("c")function but(){console.log(test.value)}defineExpose({test,a,b,c})
</script>
父组件:
<template><aaa ref="e"/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import aaa from './components/2_12_ref.vue'let e=ref()console.log(e)
</script>
11、props
作用:给子组件传递参数。
方法:在父文件的子组件标签内部传递参数,在子组件中使用defineProps()接收参数。
需要在子组件中引入:
import {defineProps} from 'vue'
接收方式:
只接收a:
defineProps(['a'])
接收list+类型限制
defineProps<{a:类型}>()
接收list+限制类型+限制必要性+指定默认值
withDefaults( defineProps<{a?:类型}>() , {a:()=> [ { id:xx,naem:xx } ] } )
接收时保存
let a=defineProps( ['a'] )
<template><div><ul><li v-for="item in list">{{ item.name }}</li></ul></div>
</template>
<script lang="ts" setup>
import { defineProps,withDefaults } from 'vue';
import { type person } from '../entity/person';//只接收参数// defineProps(['list'])//限制类型// defineProps<[list:person]>()//限制类型;可以不传;给默认值withDefaults(defineProps<{list?:person[]}>(),{list:()=>[{name:'张三',age:18}]})
</script>
12、生命周期
生命周期阶段:创建、挂载、更新、销毁,每个阶段对应两个方法。
VUE3生命周期:创建:setup
挂载:onBeforeMount、onMounted
更新:onBeforeUpdate、onUpdated
销毁:onBeforeUnmount、onUnmounted
<template><div>sum={{ sum }}<button @click="sum++">+</button></div>
</template>
<script lang="ts" setup>
import { ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onUnmounted,onBeforeUnmount } from 'vue';let sum = ref(0);console.log("创建")onBeforeMount(()=>{console.log("挂载前")})onMounted(()=>{console.log("挂载后")})onBeforeUpdate(()=>{console.log("更新前")})onUpdated(()=>{console.log("更新后")})onBeforeUnmount(()=>{console.log("卸载前")})onUnmounted(()=>{console.log("卸载后")})
</script>
13、hooks
作用:将TS或者JS中的对象、方法进行模块化管理。
写法:在TS文件中单独写一个功能点的所有数据对象、方法等,然后将其交出。在父文件中
进行引用。TS文件名需要以use开头。
注意:在TS文件中依然可以使用生命周期方法。
主文件
<template>动物:{{ animal }}<br><button @click="changeAnimal">换动物</button><br>人:{{ person }}<br><button @click="changePerson">换人</button></br></template>
<script lang="ts" setup>
import useAnimal from './useAnimal';
import usePerson from './usePerson';
const { animal, changeAnimal } = useAnimal();
const { person, changePerson } = usePerson();
</script>
Animal文件
import {ref,onMounted} from "vue"export default function useAnimal(){const animal = ref("dog")const changeAnimal = () => {animal.value = "cat"}onMounted(() => {console.log("useAnimal mounted")})return {animal,changeAnimal}
}
Person文件
import {ref} from 'vue'export default function usePerson(){const person = ref({name: "张三",age: 18})const changePerson = () => {person.value.name = "李四"person.value.age = 20}return {person,changePerson}
}