【Vue3教程】核心语法

前言

即然你都会了Ts,何不趁热打铁看看Vue3+Ts是怎么回事呢?

OptionsAPI与CompositionAPI

简介

  • Vue2 的API设计是Options(配置)风格的
  • Vue3 的API设计是Composition(组合)风格的

OptionsAPI的弊端

数据、方法,计算属性等,是分散在:data、methods、computed中的。增加或修改需求,就需要分别修改data、methods、computed,不利于维护和复用。

CompositionAPI的弊端

可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。

Setup

setup 是vue3中的一个新的配置项,值是一个函数,它是Composition API “表演的舞台”,组件中所用到的数据、方法、计算属性、监视…等等,均配置在setup中。特点如下:

  • setup函数返回的对象中的内容,可直接在模板中使用。
  • setup中访问this是underfined.
  • setup函数会在berforeCreate之前调用,它是领先所有钩子执行的。
<template>
    <div class="person">
        <h2>姓名:{{name}}</h2>
        <h2>年龄:{{age}}</h2>
        <button @click="changeName">修改名字</button>
        <button @click="changeAge">修改年龄</button>
        <button @click="showTel">查看联系方式</button>
    </div> 
</template>
<script lang="ts">
export default {
    name:"Person",
}
</script>
<script lang="ts" setup>
    let name = 'zs'
    let age = 18
    let tel = 999

    function changeName(){
        name = '张三'//注意,此时这么修改name页面是不变化的
    }
    function changeAge(){
        age += 1 //注意,此时这么修改age页面是不变化的
    }

    function showTel(){
        alert(tel)
    }
</script>
<style scoped>    
</style>

上述代码中export default 中指定了组件的名字,如果想省略该处的script,同时还想指定组件名字。可以借助vite中的插件简化

  1. 第一步:npm i vite-plugin-vue-setup-extend -D
  2. 第二步:vite.config.ts
import { fileURLToPath, URL } from 'url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(),VueSetupExtend()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

配置完成后,就可以这么写了(如下)。

<script lang="ts" name="PresonView" setup>
    let name = 'zs'
    let age = 18
    let tel = 999

    function changeName(){
        name = '张三'//注意,此时这么修改name页面是不变化的
    }
    function changeAge(){
        age += 1 //注意,此时这么修改age页面是不变化的
    }

    function showTel(){
        alert(tel)
    }
</script>

ref创建:基本类型的响应式数据

  • 作用:定义响应式变量。
  • 语法:let xxx= ref(初始值)。
  • 返回值:一个RefImpl的实例对象,简称ref对象或ref,ref对象的value属性是响式的
  • 注意点:
  1. JS中操作数据需要:xxx.value,但模板中需要.value
  2. 对于let name = ref(‘张三’)来说,name不是响应式的,name.value是响应式的。
<template>
    <div class="person">
        <h2>姓名:{{ name }}</h2>
        <h2>年龄:{{ age }}</h2>
        <button @click="changeName">修改名字</button>
        <button @click="changeAge">修改年龄</button>
        <button @click="showTel">查看联系方式</button>
    </div>
</template>
<script lang="ts" name="PresonView" setup>
import {ref} from 'vue'

let name = ref('zs')
let age = ref(18)
let tel = 999

function changeName() {
    name.value = '张三'
}
function changeAge() {
    age.value += 1 
}

function showTel() {
    alert(tel)
}
</script>
<style scoped></style>

reactive创建:对象类型的响应数据

使用reactive 可以使用对象类型的数据变为响应数据。

<template>
	<div class="person">
		<h1>一辆{{ car.brand }}车,价值{{ car.price }}万元</h1>
		<button @click="changePrice">修改汽车的价格</button>
		<br>
		<h2>游戏列表:</h2>
		<ul>
			<li v-for="g in games" :key="g.id">
				{{ g.name }}
			</li>
		</ul>
		<button @click="changeFirstGame">修改第一个游戏的名字</button>
		<br>
		<h1>{{obj.a.b.c}}</h1>
		<button @click="changeObj">测试</button>
	</div>
</template>
<script setup>
import { reactive } from 'vue';
//数据
let car = reactive({ brand: '北汽', price: 18 })
let games = reactive([
	{ id: 'game1', name: '王者荣耀' },
	{ id: 'game2', name: '英雄联盟' },
	{ id: 'game3', name: 'Apex' },
])
let obj =reactive({
	a: {
		b: {
			c: 666
		}
	}
})
//方法
function changePrice() {
	car.price = 7.89
}
function changeFirstGame() {
	games[0].name = '流星蝴蝶剑'
}
function changeObj(){
	obj.a.b.c = 999
}
</script>
<style>
.person {
	padding: 10px;
}
</style>

ref创建:对象类型的响应数据

ref 创建对象类型的响应数据,修改数据需要加.value。

<template>
	<div class="person">
		<h1>一辆{{ car.brand }}车,价值{{ car.price }}万元</h1>
		<button @click="changePrice">修改汽车的价格</button>
		<br>
		<h2>游戏列表:</h2>
		<ul>
			<li v-for="g in games" :key="g.id">
				{{ g.name }}
			</li>
		</ul>
		<button @click="changeFirstGame">修改第一个游戏的名字</button>

	</div>
</template>
<script setup>
import { ref } from 'vue';
//数据
let car = ref({ brand: '北汽', price: 18 })
let games = ref([
	{ id: 'game1', name: '王者荣耀' },
	{ id: 'game2', name: '英雄联盟' },
	{ id: 'game3', name: 'Apex' },
])
//方法
function changePrice() {
	car.value.price = 7.89
}
function changeFirstGame() {
	games.value[0].name = '流星蝴蝶剑'
}

</script>
<style>
.person {
	padding: 10px;
}
</style>

ref和reactive的对比

  • 宏观角度:

    1. ref 用来定义:基本类型数据,对象类型数据;
    2. reactive 用来定义:对象类型数据。
  • 区别:

    1. ref 创建的变量必须使用.value (可以使用volar插件自动添加.value)。
    2. reactive 重新分配一个新对象,会失去响应式 (可以使用Object.assign 去整体替换,)
         let car = ref({ brand: '北汽', price: 18 })
         // let car = reactive({ brand: '北汽', price: 18 })
         function changeCar(){
         		car.value = {brand:'奥迪',price:'40'}
         		//Object.assign(car,{brand:'奔驰',price:39})
         };
      
  • 使用原则:

    1. 若需要一个基本类型的响应式数据,必须使用ref
    2. 若需要一个响应式对象,层级不深,ref、reative 都可以
    3. 若需要一个响应式对象,且层级较深,推荐使用 reactive

toRefs 与 toRef

  • 作用:将一个响应式对象的每一个属性,转换为ref对象。
  • 备注: toRefs与toRef功能一致,但toRefs 可以批量转换。
<template>
	<div class="person">
		<h2>{{ name }}</h2>
		<h2>{{ age }}</h2>
		<button @click="changeName">修改名字</button>
		<button @click="changeAge">修改年龄</button>
	</div>
</template>
<script lang="ts" setup>
import { toRefs, toRef,reactive} from 'vue';

let person = reactive({ name: '张三', age: 18 })
let {name, age} = toRefs(person)
//toRef 只能一个一个取。
let n = toRef(person,'name');

function changeName(){
	name.value += '^';
}

function changeAge(){
	age.value += 1;
}
</script>
<style>
.person {
	padding: 10px;
}
</style>

computed 计算属性

<template>
	<div class="person">
		姓:<input type="tetx" v-model="x" /><br>
		名:<input type="text" v-model="m" /><br>
		全名:<span>{{ fullName }}</span><br>
		<button  @click="changeFullName()">修改全名</button>
	</div>
</template>
<script lang="ts" setup>
import { get } from 'http';
import { ref, computed } from 'vue';
let x = ref("zhang")
let m = ref("san")
//这么定义的fullName 是一个计算属性,且是只读的
// let fullName = computed(()=>{
// 	return x.value.slice(0,1).toUpperCase() + x.value.slice(1)+m.value;
// })

let fullName = computed({
	get() {
		return x.value.slice(0, 1).toUpperCase() + x.value.slice(1) + m.value;
	},
	set(val) {
		const[str1, str2] = val.split('-')
		x.value = str1
		m.value = str2
	}
})

function changeFullName(){
	//此处调用触发了computed的set方法。
	fullName.value = 'li-sa'
}

</script>
<style>
.person {
	padding: 10px;
}
</style>

watch

  • 作用:监视数据的变化
  • 特点:vue3中的数据watch只能监视以下四种数据
1、ref定义的数据。
2、reactive定义的数据
3、函数返回一个值
4、一个包含上述内容的数组。

情况一(ref定义的基本类型数据)

监视ref定义的基本类型数据

<template>
	<div class="person">
		<p>
			Ask a yes/no question
			<input v-model="question" :disabled="loading" />
		</p>
		<p>{{ answer }}</p>
	</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
const question = ref('')
const answer = ref('Questions usually contain a question mark. ;-)')
const loading = ref(false)
watch(question, async (newQuestion, oldQuestion) => {
	if (newQuestion.includes('?')) {
		loading.value = true
		answer.value = 'Thinking...'
		try {
			const res = await fetch('https://yesno.wtf/api')
			answer.value = (await res.json()).answer
		} catch (error) {
			answer.value = 'Error! Could not reach the API. ' + error
		} finally {
			loading.value = false
		}
	}
})
</script>
<style>
.person {
	padding: 10px;
}
</style>

情况二(ref定义的对象类型)

监视ref定义的【对象类型】数据:直接写数据名,监视的是对象的地址值,若想监控对象内部的数据,要手动开启深度监视

<template>
	<div class="person">
		<h1>情况二:监视ref定义的对象类型数据</h1>
		<h2>姓名:{{person.name}}</h2>
		<h2>年龄:{{person.age}}</h2>
		<button @click="changeName">修改名字</button>
		<button @click="changeAge">修改年龄</button>
		<button @click="changePreson">换人</button>
	</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
//数据
let person = ref({
	name:'张三',
	age:18,
})
function changeName(){
	person.value.name += '^'
}
function changeAge(){
	person.value.age += 1
}

function changePreson(){
	person.value = {name:"李四",age:19};
}
//监视,情况一:监视ref定义的对象类型数据,监视是对象的地址值
//若想监视对象内部属性的变化,需要手动配置开始深度监视。
watch(person,(newVal,oldVal)=>{
	console.log('person',newVal,oldVal);
},{
	//深度监视
	deep:true,
	//立即监视
	immediate:true,
})
</script>
<style>
.person {
	padding: 10px;
}
</style>

情况三(reactive 隐式深度监视)

监视reactive 定义的,对象类型数据,且默认开启了深度监视。

<template>
	<div class="person">
		<h1>情况三:监视【reactive】定义的【响应式数据】</h1>
		<h2>姓名:{{ person.name }}</h2>
		<h2>年龄:{{ person.age }}</h2>
		<button @click="changeName">修改名字</button>
		<button @click="changeAge">修改年龄</button>
		<button @click="changePreson">换人</button>
	</div>
</template>
<script lang="ts" setup>
import { reactive, watch } from 'vue';
//数据
let person = reactive({
	name: '张三',
	age: 18,
})
function changeName() {
	person.name += '^'
}
function changeAge() {
	person.age += 1
}

function changePreson() {
	Object.assign(person, { name: "李四", age: 19 });
}

watch(person, (newVal, oldVal) => {
	console.log('person', newVal, oldVal);
})
</script>
<style>
.person {
	padding: 10px;
}
</style>

情况四 (对象中的某项属性)

监视ref或reactive定义的【对象类型】数据中的某个属性,注意点如下:

  1. 若该属性值不是【对象类型】,需要写成函数形式
  2. 若该属性值是依然是【对象类型】,可直接编写,也可写成函数,不过建议写成函数。
    结论:监视的要是对象里的属性,那么最好写函数式。
<template>
	<div class="person">
		<p>姓名:{{ person.name }}</p><br>
		<p>年龄:{{ person.age }}</p><br>
		<p>车辆:{{ person.cars.c1 }}、{{ person.cars.c2 }}</p><br>
		<button @click="changeName">修改名字</button>
		<button @click="changeAge">修改年龄</button>
		<button @click="changeC1">修改车辆1</button>
		<button @click="changeC2">修改车辆2</button>
		<button @click="changeCar">修改所有车辆</button>
	</div>
</template>
<script lang="ts" setup>
import { reactive, watch } from 'vue';
let person = reactive({
	name: '张三',
	age: 23,
	cars: {
		c1: '奔驰',
		c2: '宝马'
	}

})

function changeName() {
	person.name = '李四'
}
function changeAge() {
	person.age += 1
}
function changeC1() {
	person.cars.c1 = '大众'
}
function changeC2() {
	person.cars.c2 = '丰田'
}
function changeCar() {
	person.cars= { c1: '雅迪', c2: '爱玛' }
	//Object.assign(person.cars,{c1: '雅迪', c2: '爱玛'})
}
//监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
// watch(()=> person.name,(newVal,oldVal)=>{
// 	console.log("person.name变化了", newVal, oldVal)
// })
watch(()=>person.cars,(newVal, oldVal)=>{
	console.log("person.cars变化了", newVal, oldVal)
},{deep:true})

// watch(person.cars,(newVal,oldVal)=>{
// 	console.log("person.cars变化了", newVal, oldVal)
// })

</script>
<style>
.person {
	padding: 10px;
}
</style>

情况五 (监视上述多个数据)

<template>
	<div class="person">
		<h1>情况五:监视上述的多个数据源</h1>
		<p>姓名:{{ person.name }}</p><br>
		<p>年龄:{{ person.age }}</p><br>
		<p>车辆:{{ person.cars.c1 }}、{{ person.cars.c2 }}</p><br>
		<button @click="changeName">修改名字</button>
		<button @click="changeAge">修改年龄</button>
		<button @click="changeC1">修改车辆1</button>
		<button @click="changeC2">修改车辆2</button>
		<button @click="changeCar">修改所有车辆</button>
	</div>
</template>
<script lang="ts" setup>
import { reactive, watch } from 'vue';
let person = reactive({
	name: '张三',
	age: 23,
	cars: {
		c1: '奔驰',
		c2: '宝马'
	}

})

function changeName() {
	person.name = '李四'
}
function changeAge() {
	person.age += 1
}
function changeC1() {
	person.cars.c1 = '大众'
}
function changeC2() {
	person.cars.c2 = '丰田'
}
function changeCar() {
	person.cars= { c1: '雅迪', c2: '爱玛' }
	//Object.assign(person.cars,{c1: '雅迪', c2: '爱玛'})
}
//监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
// watch(()=> person.name,(newVal,oldVal)=>{
// 	console.log("person.name变化了", newVal, oldVal)
// })
//监视,上述的多个数据源
watch([()=>person.name, ()=>person.cars.c1,],(newVal, oldVal)=>{
	console.log("person.cars变化了", newVal, oldVal)
},{deep:true})



</script>
<style>
.person {
	padding: 10px;
}
</style>

watchEffect

  • 官网:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。
  • Watch 对比 WatchEffect
    1. 都能监听响应式数据的变化,不同的是监听数据变化的方式不同
    2. watch: 要明确指出监视的数据
    3. watchEffect:不用明确指出监视的数据(函数中用到哪些属性,那就监听哪些属性。)
<template>
	<div class="person">
		<h2>水温:{{temp}}摄氏度</h2>
		<h2>水位:{{high}}米</h2>
		<button @click="changeTemp"></button>
		<button @click="changeHeight"></button>
	</div>
</template>
<script lang="ts" setup>
import { ref, watch, watchEffect} from 'vue';

let temp = ref(0)
let high = ref(0)

function changeTemp(){
	temp.value += 10
}
function changeHeight(){
	high.value +=10
}

watchEffect(()=>{
	if(temp.value>=60||high.value>=80){
		console.log("给服务器发送请求");
	}	
})

</script>
<style>
.person {
	padding: 10px;
}
</style>

标签的ref属性

作用 :用于注册模板引用

  • 用在普通DOM标签上,获取的是DOM节点
  • 用在组件标签上,获取的是组件实例对象
<template>
	<div class='main_div'>
		<button @click="renFun">让我看看</button>
		<PersonVue ref="ren"></PersonVue>
	</div>
</template>
<script setup>
import PersonVue from '@/components/Person.vue'
import {ref} from 'vue'

let ren = ref()

function renFun(){
	console.log(ren.value);
}

</script>
<style>
	.main_div{
		background-color: dodgerblue;

	}
</style>
<template>
	<div class="person">
		<h1>中国</h1>
		<h2 ref="title2">北京</h2>
		<h2>天安门</h2>
		<button @click="outFun">输出</button>
	</div>
</template>
<script lang="ts" setup>
import { ref,defineExpose } from 'vue'

//创建一个title2,用于存储ref标记的内容
let title2 = ref()
let a = ref(0)
let b = ref(1)
let c = ref(3)

function outFun(){
	console.log(title2.value);
}

//设置向父级暴露哪些数据
defineExpose({a, b})
</script>
<style>
.person {
	padding: 10px;
}
</style>

props

// 定义一个接口,限制每个Person对象的格式
export interface PersonInter {
 id:string,
 name:string,
    age:number
   }
   
// 定义一个自定义类型Persons
export type Persons = Array<PersonInter>

App.vue中代码:

<template>
	<Person :list="persons"/>
</template>
  
<script lang="ts" setup name="App">
  import Person from './components/Person.vue'
  import {reactive} from 'vue'
    import {type Persons} from './types'
  
    let persons = reactive<Persons>([
     {id:'e98219e12',name:'张三',age:18},
      {id:'e98219e13',name:'李四',age:19},
       {id:'e98219e14',name:'王五',age:20}
     ])
   </script>

Person.vue中代码:

<template>
<div class="person">
 <ul>
     <li v-for="item in list" :key="item.id">
        {{item.name}}--{{item.age}}
      </li>
    </ul>
   </div>
   </template>
  
<script lang="ts" setup name="Person">
import {defineProps} from 'vue'
import {type PersonInter} from '@/types'
  
  // 第一种写法:仅接收
// const props = defineProps(['list'])
  
  // 第二种写法:接收+限制类型
// defineProps<{list:Persons}>()
  
  // 第三种写法:接收+限制类型+指定默认值+限制必要性
let props = withDefaults(defineProps<{list?:Persons}>(),{
     list:()=>[{id:'asdasg01',name:'小猪佩奇',age:18}]
  })
   console.log(props)
  </script>

生命周期

  • 概念:Vue组件实例在创建时要经历一系列的初始化步骤,在此过程中vue会在适合的时机,调用特定的函数,从而让开发者有机会在特定阶段运行自己的代码,这些特定的函数统称为:生命周期钩子
  • Vue3的生命周期

    创建阶段:setup

    挂载阶段:onBeforeMountonMounted

    更新阶段:onBeforeUpdateonUpdated

    卸载阶段:onBeforeUnmountonUnmounted

  • 常用的钩子:onMounted(挂载完毕)、onUpdated(更新完毕)、onBeforeUnmount(卸载之前)

  • 示例代码:

    <template>
      <div class="person">
        <h2>当前求和为:{{ sum }}</h2>
        <button @click="changeSum">点我sum+1</button>
      </div>
    </template>
    
    <!-- vue3写法 -->
    <script lang="ts" setup name="Person">
      import { 
        ref, 
        onBeforeMount, 
        onMounted, 
        onBeforeUpdate, 
        onUpdated, 
        onBeforeUnmount, 
        onUnmounted 
      } from 'vue'
    
      // 数据
      let sum = ref(0)
      // 方法
      function changeSum() {
        sum.value += 1
      }
      console.log('setup')
      // 生命周期钩子
      onBeforeMount(()=>{
        console.log('挂载之前')
      })
      onMounted(()=>{
        console.log('挂载完毕')
      })
      onBeforeUpdate(()=>{
        console.log('更新之前')
      })
      onUpdated(()=>{
        console.log('更新完毕')
      })
      onBeforeUnmount(()=>{
        console.log('卸载之前')
      })
      onUnmounted(()=>{
        console.log('卸载完毕')
      })
    </script>
    

    自定义Hooks

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2.x中的mixin

  • 自定义hook的优势:复用代码, 让setup中的逻辑更清楚易懂。

实例代码:

  • useSum.ts中内容如下:
import { ref,computed } from 'vue'


export default function () {
        //数据
        let num = ref(0)
        let bigNum = computed(()=>{
                return num.value * 10
        })
        //方法
        function plusFun() {
                num.value += 1
        }
        //向外部提供东西
        return { num, plusFun,bigNum }
}


  • useDog.ts中内容如下:
import { reactive, onMounted} from 'vue'
import axios from 'axios';

export default function(){
        //数据
        let dogList = reactive([
                "https://images.dog.ceo/breeds/pembroke/n02113023_4052.jpg"
        ])
        
        //方法
        async function getDog() {
                try {
                        let res = await axios.get("https://dog.ceo/api/breed/pembroke/images/random");
                        dogList.push(res.data.message)
                } catch (error) {
                        alert(error)
                }
        
        }

        onMounted(()=>{
                getDog()
        })
        //向外部提供东西
        return { dogList,getDog}
}
  • 组件中具体使用:
<template>
	<div class="person">
		<h2>当前求和为:{{ num }},放大十倍{{ bigNum }}</h2>
		<button @click="plusFun">点我sum+1</button>
		<hr>
		<img v-for="(item, index) in dogList" :src="item" :key="index" />
		<br>
		<button @click="getDog">再来一只小狗</button>
	</div>
</template>
<script lang="ts" setup>
import useNum from '@/hooks/useNum';
import useDog from '@/hooks/useDog';

const { num, plusFun,bigNum } = useNum()
const { dogList, getDog } = useDog()
</script>
<style>
.person {
	background-color: skyblue;
	padding: 20px;
	border-radius: 10px;
	box-shadow: 10px;
}

img {
	height: 100px;
	margin-left: 10px;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值