1、optionsApi 与 Composition Api
选项式与组合式
组合就是一个一个 的函数 ,每个函数里都有 data,methods,compute 等等
2、拉开序幕的setup
setup执行时机 在 beforeCreate之前
<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",
setup() {
//setup 函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,此时的name,age,tel都不是响应式的数据
//数据
let name = "张三";//注意此时的name不是响应式的
let age = 13;//注意此时的age不是响应式的
let tel = "123123123";
function changeName() {
name = "李四";//注意这样修改name,页面是没有变化的,因为name不是响应式的
}
function changeAge() {
age = 20;
}
function showTel() {
alert(tel);
}
return {
name,
age,
changeName,
changeAge,
showTel,
};
},
//选项式,配置式写法
// data() {
// return {
// name: "张三",
// age: 18,
// tel: "123456",
// };
// },
// methods: {
// changeAge() {
// this.age = 13;
// },
// changeName() {
// this.name = "zhang-san";
// },
// showTel() {
// alert(this.tel);
// },
// },
};
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
3.Setup语法糖
<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 = "张三"; //注意此时的name不是响应式的
let age = 13; //注意此时的age不是响应式的
let tel = "123123123";
function changeName() {
name = "李四"; //注意这样修改name,页面是没有变化的,因为name不是响应式的
}
function changeAge() {
age = 20;
}
function showTel() {
alert(tel);
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
但还是太麻烦,为了一个名字多写几行代码
我们安装一个插件
npm i vite-plugin-vue-setup-extend -D
-D 代表是开发时用的插件, 打包以后就不用了
在配置文件里面配置一下插件
import { fileURLToPath, URL } from 'node: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))
}
}
})
<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" setup name="Person123">
let name = "张三"; //注意此时的name不是响应式的
let age = 13; //注意此时的age不是响应式的
let tel = "123123123";
function changeName() {
name = "李四"; //注意这样修改name,页面是没有变化的,因为name不是响应式的
}
function changeAge() {
age = 20;
}
function showTel() {
alert(tel);
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
4.ref响应式数据(定义基本类型数据)
<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" setup name="Person">
import { ref } from "vue";
let name = ref("张三"); //注意此时的name不是响应式的
let age = ref(13); //注意此时的age不是响应式的
let tel = "123123123";
function changeName() {
name.value = "李四"; //注意这样修改name,页面是没有变化的,因为name不是响应式的
}
function changeAge() {
age.value = 20;
}
function showTel() {
alert(tel);
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
5.reactive (引用类型的数据)
<template>
<div class="person">
<div>一辆{{ car.brand }}车,价值{{ car.price }}万</div>
<button @click="changePrice">修改汽车的价格</button>
<hr />
<h2>游戏列表</h2>
<ul>
<li v-for="game in games" :key="game.id">
{{ game.name }}
</li>
</ul>
<button @click="changeGame">改变第三个游戏的名字</button>
</div>
</template>
<!-- 会自动暴露出去 -->
<script lang="ts" setup name="Person">
import { reactive } from "vue";
let car = reactive({ brand: "奔驰", price: 100 });
let games = reactive([
{ id: "001", name: "LOL" },
{ id: "002", name: "CS" },
{ id: "003", name: "CF" },
]);
function changePrice() {
car.price += 10;
}
function changeGame() {
games[2].name = '逆战';
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
6.ref 定义对象类型的数据
<template>
<div class="person">
<div>一辆{{ car.brand }}车,价值{{ car.price }}万</div>
<button @click="changePrice">修改汽车的价格</button>
<hr />
<h2>游戏列表</h2>
<ul>
<li v-for="game in games" :key="game.id">
{{ game.name }}
</li>
</ul>
<button @click="changeGame">改变第三个游戏的名字</button>
</div>
</template>
<!-- 会自动暴露出去 -->
<script lang="ts" setup name="Person">
import { ref } from "vue";
let car = ref({ brand: "奔驰", price: 100 });
let games = ref([
{ id: "001", name: "LOL" },
{ id: "002", name: "CS" },
{ id: "003", name: "CF" },
]);
function changePrice() {
car.value.price += 10;
}
function changeGame() {
games.value[2].name = '逆战';
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
7.ref对比reactive
这个开启能让ref自动加上 value
8.计算属性
<template>
<div class="person">
姓: <input type="text" v-model="firstName"><br>
名: <input type="text" v-model="lastName"><br>
<button @click="changeFullName">将全名改为li-4</button>
全名: <span>{{ fullName }}</span>
</div>
</template>
<!-- 会自动暴露出去 -->
<script lang="ts" setup name="Person">
import { ref ,computed} from 'vue'
let firstName = ref('张');
let lastName = ref('三');
//这么定义的fullName 是只读的不允许修改的
// let fullName = computed(()=>{
// return firstName.value +'-'+ lastName.value;
// })
//也是一个ref响应式数据
let fullName = computed({
get(){
return firstName.value +'-'+ lastName.value;
},
set(value){
const [first,last] = value.split('-')
firstName.value = first;
lastName.value = last;
}
})
function changeFullName(){
console.log(fullName.value);
fullName.value = 'li-4';
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
9.监听WatchEffect
<template>
<div class="person">
<h2>需求:当水温达到60度,或水位达到80cm 时,给服务器发请求</h2>
<h2>当前水温为{{ tmp }} ℃</h2>
<h2>当前水位为{{ height }} cm</h2>
<button @click="changeTmp">水温+10</button>
<button @click="changeHeight">水位+10</button>
</div>
</template>
<!-- 会自动暴露出去 -->
<script lang="ts" setup name="Person">
import { ref, watch, watchEffect } from "vue";
const tmp = ref(10);
const height = ref(0);
function changeTmp() {
tmp.value += 10;
}
function changeHeight() {
height.value += 10;
}
//watch实现
// watch([tmp,height],(value)=>{
// // 从value中获取最新的temp值、height值
// const [newTemp,newHeight] = value
// // 室温达到60℃,或水位达到80cm,立刻联系服务器
// if(newTemp >= 60 || newHeight >= 80){
// console.log('联系服务器')
// }
// })
//watchEffect实现 一上来会执行一遍
watchEffect(() => {
console.log("@");
//这两个数据变化的时候,这个函数的回调会执行一次
if (tmp.value >= 60 || height.value >= 80) {
console.log("联系服务器");
}
});
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
10.ref属性
Person组件
<template>
<div class="person">
<h1>中国</h1>
<h2 ref="titie2">北京</h2>
<h3>尚硅谷</h3>
<button @click="showLog">点我输出H2</button>
</div>
</template>
<!-- 会自动暴露出去 -->
<script lang="ts" setup name="Person">
import { ref ,defineExpose} from "vue";
//创建一个 title2,用于存储ref标记的内容
let titie2 = ref();
let a = ref(1);
let b = ref(2);
let c = ref(3);
function showLog(){
// console.log(document.getElementById('titie2'));
console.log(titie2.value);
}
//要暴露出去,父组件才能看到子组件实例暴露的数据
defineExpose({
a,
b,
c
})
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
App组件
<template>
<h2 ref="title2">你好</h2>
<button @click="showLog">点击输出Person组件</button>
<Person ref="ren"/>
</template>
<script lang="ts" setup name="App">
//js 或 ts
import Person from './components/Person.vue';
import {ref,watchEffect} from 'vue';
let title2 = ref();
let ren = ref();
function showLog(){
console.log(ren.value);
}
</script>
<style scoped>
/* css */
.app{
background-color: #ddd;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
</style>
11.回顾TS 接口 类 泛型
但是得在
{
"compilerOptions": {
// ...
"baseUrl": "./", // 这里需要配置
"paths": {
"@/*": ["./src/*"] // 这里需要配置
}
// 如果baseUrl设置为'src',那paths就应该配置为{"@/*": "./*"},如下:
// "baseUrl": "src",
// "paths": {
// "@/*": ["./*"]
// }
// 相关baseUrl,paths说明请查看官方文档
},
// include也需要配置以下:
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules"]
}
加上上面的配置 ,这样 @/ 是 src 目录 而且不会飘红
回顾
<template>
<div class="person">???</div>
</template>
<!-- 会自动暴露出去 -->
<script lang="ts" setup name="Person">
import { type PersonInter,type PersonS } from "@/types";
// let person: PersonInter = {
// id: "01",
// name: "张三",
// age: 18,
// };
// let personList: Array<PersonInter> = [
// {
// id: "01",
// name: "张三",
// age: 18,
// },
// {
// id: "02",
// name: "李四",
// age: 19,
// },
// ];
let personList: PersonS = [
{
id: "01",
name: "张三",
age: 18,
},
{
id: "02",
name: "李四",
age: 19,
},
];
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
//定义了一个接口,用于限制person对象的具体属性
export interface PersonInter{
id:string,
name:string,
age:number
}
// export type PersonS = Array<PersonInter>
export type PersonS = PersonInter[]
{
"compilerOptions": {
// ...
"baseUrl": "./", // 这里需要配置
"paths": {
"@/*": ["./src/*"] // 这里需要配置
}
// 如果baseUrl设置为'src',那paths就应该配置为{"@/*": "./*"},如下:
// "baseUrl": "src",
// "paths": {
// "@/*": ["./*"]
// }
// 相关baseUrl,paths说明请查看官方文档
},
// include也需要配置以下:
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules"]
}
12.props 使用
<template>
<div class="person">
<ul>
<li v-for="item in personList" :key="item.id">
{{ item.id }} -- {{ item.name }} -- {{ item.age }}
</li>
</ul>
</div>
</template>
<!-- 会自动暴露出去 -->
<script lang="ts" setup name="Person">
//带有 define开头的函数不用引入 在 vue3 里叫做宏函数 可以直接使用
import {defineProps,withDefaults} from 'vue'
import {type PersonS} from '@/types'
//第一种写法 接受list,a 同时将props保存起来
// let x = defineProps(['personList','a'])
// 第二种写法 限制类型
// let x = defineProps<{
// personList?:PersonS,//可空
// a:string
// }>()
//第三种写法 接受list+限制类型+限制必要性+指定默认值
let x = withDefaults(defineProps<{
personList?:PersonS,//可空
a:string
}>(),{
personList:()=>
[{
id:'009',
name:'王五',
age:20
}],
a:'哈哈哈'
})
console.log(x.a);
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
<template>
<Person :person-list="personList" :a="'哈哈'"/>
</template>
<script lang="ts" setup name="App">
//js 或 ts
import Person from './components/Person.vue';
import {type PersonS} from '@/types';
import {reactive} from 'vue';
//可以传入泛型
let personList = reactive<PersonS>([
{
id:'001',
name:'张三',
age:18
},
{
id:'002',
name:'李四',
age:19
}
,
{
id:'003',
name:'王五',
age:20,
}
])
</script>
<style scoped>
</style>
13.生命周期
<template>
<div class="person">
<h2>当前求和为:{{ sum }}</h2>
<button @click="add">点我加一</button>
</div>
</template>
<!-- 会自动暴露出去 -->
<script lang="ts" setup name="Person">
import {
ref,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
} from "vue";
let sum = ref(0);
//创建 beforeCreate created
console.log("创建");
//挂载之前
onBeforeMount(() => {
console.log("挂载之前");
});
//挂载完毕
onMounted(() => {
console.log("挂载完毕");
});
//更新之前
onBeforeUpdate(() => {
console.log("更新之前");
});
//更新完毕
onUpdated(() => {
console.log("更新完毕");
});
//卸载之前
onBeforeUnmount(() => {
console.log("卸载之前");
});
//卸载完毕
onUnmounted(() => {
console.log("卸载完毕");
});
function add() {
sum.value++;
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>