vue的核心语法
简单入门
Vue3
向下兼容Vue2
语法,且Vue3
中的模板中可以没有根标签
<template>
<div class="person">
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">年龄+1</button>
<button @click="showTel">点我查看联系方式</button>
</div>
</template>
<script lang="ts">
export default {
name:'App',
data() {
return {
name:'张三',
age:18,
tel:'13888888888'
}
},
methods:{
changeName(){
this.name = 'zhang-san'
},
changeAge(){
this.age += 1
},
showTel(){
alert(this.tel)
}
},
}
</script>
Setup函数
在学习以下知识之前我们先了解一下 options api 和 composition api的区别: vue2中使用的是optionsAPI,来定义一个组件内部的一些属性,如methods、data等等; 其缺点往往会在大项目中体现出来,比如一个简单的计数器功能,可能需要在methods内部书写一部分逻辑,在computed内部也书写一部分逻辑,那么问题来了:如果该组件内部有n个这样的小功能,那么此时代码逻辑是十分分散的,并且后期迭代维护面临的问题也是可能修改一个需求需要在不同的属性内部反复查找相关的代码,而compositionAPI的出现就是为了解决这一问题的。
vue3新增的compositionAPI主要就是为了解决API太过于分散的问题,避免一个功能点下的api太过于分散不便于维护,将同一个功能下的api统一放到一个地方,这样一来项目的开发和维护就简便多了。compositionAPI也叫做组合式API,vue3中组合式API的入口就是setup函数;
setup
是Vue3
中一个新的配置项,值是一个函数,它是 Composition API
“表演的舞台”,组件中所用到的:数据、方法、计算属性、监视......等等,均配置在setup
中。
特点如下:
-
setup
函数返回的对象中的内容,可直接在模板中使用。 -
setup
中访问this
是undefined
。 -
setup
函数会在beforeCreate
之前调用,它是“领先”所有钩子执行的。 -
普通写法[不推荐]
通过 export default { setup(){ } } 来定义setup函数 , 这种方式不推荐
-
<template> <div class="person"> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">年龄+1</button> <button @click="showTel">点我查看联系方式</button> </div> </template> <script lang="ts"> export default { name:'Person', setup(){ // 数据,原来写在data中(注意:此时的name、age、tel数据都不是响应式数据) let name = '张三' let age = 18 let tel = '13888888888' // 方法,原来写在methods中 function changeName(){ name = 'zhang-san' //注意:此时这么修改name页面是不变化的 console.log(name) } function changeAge(){ age += 1 //注意:此时这么修改age页面是不变化的 console.log(age) } function showTel(){ alert(tel) } // 返回一个对象,对象中的内容,模板中可以直接使用 return {name,age,tel,changeName,changeAge,showTel} } } </script>
语法糖[推荐]
-
<template> <div class="person"> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <button @click="changName">修改名字</button> <button @click="changAge">年龄+1</button> <button @click="showTel">点我查看联系方式</button> </div> </template> <script lang="ts"> export default { name:'Person', } </script> <!-- 下面的写法是setup语法糖 --> <script setup lang="ts"> console.log(this) //undefined // 数据(注意:此时的name、age、tel都不是响应式数据) let name = '张三' let age = 18 let tel = '13888888888' // 方法 function changName(){ name = '李四'//注意:此时这么修改name页面是不变化的 } function changAge(){ console.log(age) age += 1 //注意:此时这么修改age页面是不变化的 } function showTel(){ alert(tel) } </script>
注意:在setup语法中使用 this 会出现unidefined , 在
setup
中你应该避免使用this
,因为它不会找到组件实例。setup
的调用发生在data
property、computed
property 或methods
被解析之前,所以它们无法在setup
中被获取,这也是为了避免setup()和其他选项式API混淆。
数据响应函数
什么是响应式:响应式指的是,把数据绑定当视图,当数据的值发生改变,视图也会跟着重新渲染。
ref:基础数据响应式函数
VUE3提供了ref函数可以在修改完 数据后自动渲染视图,类似余双向绑定
-
在js中通过 import {ref} from "vue"; 来声明 ref函数
-
注意:ref属性在js中要使用.value 取值
<template>
<div class="person">
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">年龄+1</button>
<button @click="showTel">点我查看联系方式</button>
</div>
</template>
<script setup lang="ts" name="Person">
import {ref} from 'vue'
// name和age是一个RefImpl的实例对象,简称ref对象,它们的value属性是响应式的。
let name = ref('张三')
let age = ref(18)
// tel就是一个普通的字符串,不是响应式的
let tel = '13888888888'
function changeName(){
// JS中操作ref对象时候需要.value
name.value = '李四'
console.log(name.value)
// 注意:name不是响应式的,name.value是响应式的,所以如下代码并不会引起页面的更新。
// name = ref('zhang-san')
}
function changeAge(){
// JS中操作ref对象时候需要.value
age.value += 1
console.log(age.value)
}
function showTel(){
alert(tel)
}
</script>
通过ref获取dom对象
<template>
<input ref="inputRef" :value="refValue"/>
</template>
<script setup>
import {ref} from "vue";
//变量名 inputRef 和 dom元素上的ref="inputRef" 一样,就能获取到dom元素
let inputRef = ref(null);
console.log(inputRef);
</script>
reactive创建:对象类型的响应式数据
对于对象类型可以使用reactive进行响应式函数处理
<template>
<div class="person">
<h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
<h2>游戏列表:</h2>
<ul>
<li v-for="g in games" :key="g.id">{{ g.name }}</li>
</ul>
<h2>测试:{{obj.a.b.c.d}}</h2>
<button @click="changeCarPrice">修改汽车价格</button>
<button @click="changeFirstGame">修改第一游戏</button>
<button @click="test">测试</button>
</div>
</template>
<script lang="ts" setup name="Person">
import { reactive } from 'vue'
// 数据
let car = reactive({ brand: '奔驰', price: 100 })
let games = reactive([
{ id: 'ahsgdyfa01', name: '英雄联盟' },
{ id: 'ahsgdyfa02', name: '王者荣耀' },
{ id: 'ahsgdyfa03', name: '原神' }
])
let obj = reactive({
a:{
b:{
c:{
d:666
}
}
}
})
function changeCarPrice() {
car.price += 10
}
function changeFirstGame() {
games[0].name = '流星蝴蝶剑'
}
function test(){
obj.a.b.c.d = 999
}
</script>
注意:其实ref
接收的数据可以是:基本类型、对象类型。若ref
接收的是对象类型,内部其实也是调用了reactive
函数
例如:
template>
<div class="person">
<h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
<h2>游戏列表:</h2>
<ul>
<li v-for="g in games" :key="g.id">{{ g.name }}</li>
</ul>
<h2>测试:{{obj.a.b.c.d}}</h2>
<button @click="changeCarPrice">修改汽车价格</button>
<button @click="changeFirstGame">修改第一游戏</button>
<button @click="test">测试</button>
</div>
</template>
<script lang="ts" setup name="Person">
import { ref } from 'vue'
// 数据
let car = ref({ brand: '奔驰', price: 100 })
let games = ref([
{ id: 'ahsgdyfa01', name: '英雄联盟' },
{ id: 'ahsgdyfa02', name: '王者荣耀' },
{ id: 'ahsgdyfa03', name: '原神' }
])
let obj = ref({
a:{
b:{
c:{
d:666
}
}
}
})
console.log(car)
function changeCarPrice() {
car.value.price += 10
}
function changeFirstGame() {
games.value[0].name = '流星蝴蝶剑'
}
function test(){
obj.value.a.b.c.d = 999
}
</script>
对比:
-
ref
用来定义:基本类型数据、对象类型数据; -
reactive
用来定义:对象类型数据。 -
ref
创建的变量必须使用.value
-
reactive
重新分配一个新对象,会失去响应式
toRef 和 toRefs
toRef解析响应式
将一个响应式对象中的每一个属性,转换为ref
对象,toRefs
与toRef
功能一致,但toRefs
可以批量转换。
<template>
<div class="person">
<h2>姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<h2>性别:{{person.gender}}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="changeGender">修改性别</button>
</div>
</template>
<script lang="ts" setup name="Person">
import {ref,reactive,toRefs,toRef} from 'vue'
// 数据
let person = reactive({name:'张三', age:18, gender:'男'})
// 通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力
let {name,gender} = toRefs(person)
// 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力
let age = toRef(person,'age')
// 方法
function changeName(){
name.value += '~'
}
function changeAge(){
age.value += 1
}
function changeGender(){
gender.value = '女'
}
</script>
computed计算函数
下面通过计算函数演示2个值相加,需要通过 import {computed} from "vue"; 引入函数
<template>
<div class="person">
姓:<input type="text" v-model="firstName"> <br>
名:<input type="text" v-model="lastName"> <br>
全名:<span>{{fullName}}</span> <br>
<button @click="changeFullName">全名改为:li-si</button>
</div>
</template>
<script setup lang="ts" name="App">
import {ref,computed} from 'vue'
let firstName = ref('zhang')
let lastName = ref('san')
// 计算属性——只读取,不修改
/* let fullName = computed(()=>{
return firstName.value + '-' + lastName.value
}) */
// 计算属性——既读取又修改
let fullName = computed({
// 读取
get(){
return firstName.value + '-' + lastName.value
},
// 修改
set(val){
console.log('有人修改了fullName',val)
firstName.value = val.split('-')[0]
lastName.value = val.split('-')[1]
}
})
function changeFullName(){
fullName.value = 'li-si'
}
</script>
watch监听器
watch的作用是用来监听某个值的变化从而出发watch回调。watch可以:
-
监听单体数据
-
监听对象
-
监听对象的某个属性
-
监听多个数据
<template>
<div class="watch"></div>
<button @click="value++">修改value</button>
<button @click="persion.age++">修改persion</button>
</template>
<script setup>
import {reactive, ref, watch} from "vue";
const value = ref(1111);
const persion = reactive({
username:"zs" ,
age:18
})
//监听单个值
watch(value,()=>{
console.log(value);
})
//监听对象
watch(persion,()=>{
console.log(persion);
})
//监听多个,用[]包含多个
watch([persion,value],()=>{
console.log(persion,value);
});
//监听对象的属性
watch(()=>persion.age,()=>{
console.log(persion);
});
</script>
watchEffect监听副作用
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。watchEffect的作用是只要使用到的数据都会被监听到,无需指定具体监听谁,他包含4个功能
-
页面加载,watchEffect的回调就会立即执行
-
只要watchEffect 只要里面使用了任何数据,就会触发回到函数
-
onCleanup函数可以清除副作用
-
返回一个stopHandler可以停止监听
<template>
<div class="watch_effect"></div>
<button @click="num++">num++</button>
<button @click="stopHandler">StopWatch</button>
</template>
<script setup>
import {ref, watchEffect} from "vue";
const num = ref(1111);
//1.页面加载,初始化立即回执行回调
//2.只要watchEffect 只要里面使用了数据,就会触发回到函数
//3.onCleanup函数可以清除副作用
//4.返回一个stopHandler可以停止监听
/**
watchEffect(()=>{
console.log(num.value);
})
**/
const stopHandler = watchEffect(onCleanup=>{
console.log(num.value);
onCleanup(()=>{
console.log("清除副作用");
});
})
</script>
生命周期方法
因为 setup
是围绕 beforeCreate
和 created
生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup
函数中编写。下表包含如何在 setup () 内部调用生命周期钩子:
选项式 API(VUE2) | Hook inside setup |
---|---|
beforeCreate | Not needed* 不需要 |
created | Not needed* 不需要 |
beforeMount | onBeforeMount |
挂载之前 | |
mounted | onMounted |
页面加载完成时执行 | |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
页面销毁时执行 | |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
activated | onActivated |
deactivated | onDeactivated |
父子组件
父传参数给子
第一步:创建一个父组件页面,引入子组件,并传入参数 parent.vue
<template>
<H1 class="parent_child">父传子</H1>
<h2>使用子组件传参</h2>
<child-vue text="传入静态文本" :num="num" :title="title" :persion="persion"></child-vue>
</template>
<script setup>
//引入子组件取名用驼峰,页面用中划线 <child-vue></child-vue>
import ChildVue from "@/views/parent_child/child.vue";
import {reactive, ref} from "vue";
//构建一个响应式对象
const persion = reactive({
username:"zs",
age:18
})
//定义响应式变量
const title = ref("我是标题");
const num = ref(111);
</script>
第二步:创建一个子组件页面 : child.vue , 通过 defineProps接受父传入的参数
<template>
<h2>text:{{text}}</h2>
<h2>num:{{num}}</h2>
<h2>title:{{title}}</h2>
<h2>persion:{{persion}}</h2>
</template>
<script setup>
//通过defineProps来接受父传入过来的参数
const prop = defineProps({
text:String,
num:Number,
title:String,
persion:Object
})
//在js中可以读取父传入过来的参数,但是不能修改
console.log("prop.title" , prop.title)
</script>
子传参给父
子组件给父组件传值,需要通过defineEmits在子组件声明事件方法,然后调用该事件方法传值,父组件需要监听该事件方法取值。第一步:在子组件中准备一个按钮,点击按钮就给父组件传值。然后给按钮绑定一个方法。
<h1>给父组件传参</h1>
<button @click="sendData2Parent">给父组件传参</button>
------------------------------------------------------------------------------------------
//声明事件
const $emits = defineEmits(["data2parent","事件名","事件名"]);
const sendData2Parent = ()=>{
//调用声明的事件
$emits('data2parent',"传给父组件的数据");
}
-
const $emits = defineEmits(["data2parent","事件2","事件3"]); 自定义了三个事件
-
$emits('data2parent',"传给父组件的数据"); 调用自定义的事件
第二步:在父组件中绑定该事件,并调用一个函数来接受值
<child-vue @data2parent="getDataFromChild" ...></child-vue>
const getDataFromChild = (data)=>{
console.log("打印数据",data);
}
子暴露
VUE3提供了 defineExpose 来暴露子组件中的数据和方法在父组件中可以拿到子组件中暴露的数据或者方法。第一步在子组件中暴露数据
//子暴露
defineExpose({
data:"我是子暴露的数据",
sayHi:()=>{
return "我是子暴露的方法被调用返回的结果";
}
})
第二步:在父组件接受子暴露的数据
<child-vue
ref="childVueRef" ...></child-vue>
//用一个响应式变量,接受子暴露的数据
const childVueRef = ref(null);
onMounted(()=>{
console.log("子暴露的数据:",childVueRef.value.data);
console.log("子暴露的方法:",childVueRef.value.sayHi());
})
-
ref="childVueRef" : 接受子暴露,把数据绑定给childVueRef
注意:因为在父组件的 setup 函数中,声明周期很早,此时子组件还没加载,所以需要在onMounted去调用子暴露的数据和方法才可以拿到结果。
写在最后:Vue3是当前前端的主流框架,本篇介绍了一些vue3的基础核心语法,希望能够给大家带来帮助。笔者小,中,大厂均有面试经历,坚持每日分享java全栈开发知识,希望能够和大家共同进步。