VUE3

VUE3:Typescript + 组合式API + setup语法糖

这里主要是来自b站尚硅谷VUE3的官方课堂笔记,有一些自己的修改。

介绍

vite

  • 创建项目

  • 由原生 ES 模块提供支持的现代前端开发与构建工具。

  • Vite 利用现代浏览器的原生 ES 模块导入和构建时预编译功能。
  • 模块热更新。当文件更改时,Vite 只需要重建一小部分模块。
1
2
3
npm create vue@latest      创建项目
npm install 安装依赖
npm run dev 开启项目

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── README.md
├── env.d.ts //环境类型声明文件,定义文件类型
├── index.html //应用的入口 HTML 文件
├── node_modules //npm install下载的项目依赖
├── package-lock.json //锁定依赖版本,确保每次安装依赖时的一致性。
├── package.json //项目的配置文件
├── public //存放静态资源(如图片、图标等)的文件夹
├── src //源代码
├── tsconfig.app.json //TypeScript 的配置文件
├── tsconfig.json //定义 TypeScript 编译选项和编译范围。
├── tsconfig.node.json // Node.js 环境的 TypeScript 编译设置。
└── vite.config.ts //Vite 的配置文件

vue渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
index.html

<body>
<div id="app"></div> //Vue 应用的挂载点。
<script type="module" src="/src/main.ts"></script> //解析 main.ts 文件,并执行其中的代码,初始化并挂载 Vue 应用。
</body>



main.ts

import './assets/main.css' // 引入根组件 App.vue
import { createApp } from 'vue' //引入 Vue 3 的 createApp 函数
import App from './App.vue' //引入根组件 App
createApp(App).mount('#app') // 创建应用实例并挂载到 #app 元素

.vue文件可以由模板脚本样式三部分组成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 模板部分
<template>
<div class="app"> //类名为app的组件容器
<h1>泥嚎:</h1>
</div>
</template>

//脚本部分
<script lang="ts">
export default {
name: 'App' //导出对象,为组件命名
}
</script>

//样式部分
<style>
.app { //对 app 类的 CSS 类选择器。这个块中的样式将应用于类为 app 的 div 元素。
background-color: #0b1581; //颜色
box-shadow: 0 0 10px; //阴影的偏移和模糊半径
border-radius: 10px; //角半径,即边角圆滑程度
padding: 20px; //边框范围
}
</style>

选项式API与组合式API

  • 选项式API,将不同组件的一部分逻辑都放在对应的选项对象中

    • data:返回一个响应式数据对象,通常包含组件的状态。
    • methods:定义方法,这些方法可以包含函数,它们在组件内部被调用。
    • computed:定义计算属性,它们基于其他响应式数据计算并返回值。
    • watch:定义观察者,它们在响应式数据变化时被触发。
  • 选项式 API更适合简单的组件,特别是对于那些不需要复杂逻辑和状态管理的组件。

  • 组合式 API 更适合复杂的组件,特别是那些需要更细粒度的逻辑管理和状态管理的组件。

选项式API

在刚刚定义的组件添加一个子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
<div class="app">
<h1>泥嚎:</h1>
<person/> //添加子组件
</div>
</template>

<script lang="ts">
import Person from './components/Person.vue'
export default {
name: 'App',
components: { // 注册了一个名为Person的组件
Person
}
}
</script>

<style>
.app {
background-color: #237849;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
</style>

组合式API

setup

将一个组件的不同逻辑功能放入一个setup()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<template>
<div class="person">
<h1>姓名: {{ name }}</h1>
<h1>年龄: {{ age }}</h1>
<h1>手机号: {{ tel }}</h1>
<button @click="changename">>点击修改姓名</button>
<button @mousemove="changeage">>点击修改年龄</button>
<button @click="showTel">>点击查看联系方式</button>
</div>
</template>


<script lang="ts">
export default {
name: 'Person',
setup() {
//都未定义为响应式数据
let name = 'Randolfluo';
let age = 20;
let tel = '123456789';

function changename() //不起作用,因为不是响应式数据
{
name = 'miku';
}
function changeage() //不起作用,因为不是响应式数据
{
age += 1;
}
function showTel() //起作用,因为未作修改
{
alert(tel);
}

return { name, age, tel, changename, changeage, showTel }
//setup返回值,可以是渲染函数
//return()=> "114514"
}
}</script>


<style scoped>
.person {
background-color: #e0eee7;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}

button {
background-color: rgb(160, 183, 212);

border-radius: 1000px;
margin: 0 10px;
}
</style>

setup语法糖

我们可以将setup单独放入一个script标签,直接返回该标签内所有元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

<script lang="ts">
export default {
name: 'Person'
}</script>


<script lang="ts" setup>

let name = 'Randolfluo';
let age = 20;
let tel = '123456789';

function changename() //不起作用,因为不是响应式数据
{
name = 'miku';
}
function changeage() //不起作用,因为不是响应式数据
{
age += 1;
}
function showTel() //起作用,因为未作修改
{
alert(tel);
}
</script>

ref&reactive

  • 一般ref即可解决问题

  • ref 可以定义基本数据类型的响应式数据和对象类型的响应式数据

    • ref定义对象类型的响应式数据的_value是proxy()

    • ref直接指向新的对象

  • reactive 只能定义对象类型的响应式数据

    • 重新分配的对象会变成普通对象

    • Vue 3 的响应式系统是基于 Proxy 的,这意味着每个响应式对象都有一个唯一的 Proxy 实例。即重新赋值一个响应式对象时,你实际上是在创建一个新的响应式对象,而不是更新原始对象。vue视图没有跟踪数据块修改。

    • 可以通过Object.assign(object,对象类型的具体属性)修改对象的值。

  • Proxy 是一个构造函数,它允许你创建一个对象的代理,从而可以控制对这个对象的访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<template>
<div class="person">
<h1>姓名: {{ user.username }}</h1>
<h1>年龄: {{ user.age }}</h1>
<h1>手机号: {{ user.tel }}</h1>
<button @click="changeage">>点击修改年龄</button>
<h1>game:</h1>
<ul>
<li v-for="g in game" :key="g.id">{{ g.name }}</li>
//v-for 指令用于基于一个数组渲染列表。当数组中的对象发生变化时,Vue 会自动更新列表中的元素。
</ul>
<button @click="yuanshenqidong">>点击启动!</button>"

</div>
</template>


<script lang="ts">
export default {
name: 'Person'
}</script>


<script lang="ts" setup>
import { reactive, ref } from 'vue';

let user = reactive({
username: 'Randolfluo',
age: 20,
tel: '123456789'

});

let game = ref([
{id:'game1', name:'red alarm'},
{game2:'game2', name:'warthunder'},
{game3: 'game3', name:'MC'}
]);

function changeage() {
user.age += 1;
}

function yuanshenqidong(){
game.value.forEach(item => {
item.name = '原神启动';
});
}

</script>

toRefs与toRef

  • 从响应式对象中提取响应式引用。
    • toRefs引用整个对象
    • toRef引用对象元素
1
2
3
4
5
6
let person = reactive({
name: 'Randolfluo',
age : 20;
})
let{name, age} = toRefs(person)
let ag = toRef(person,'age')

compuuted计算属性

  • v-bind单向绑定意味着数据只能从父组件流向子组件,而不能反向流动。

  • v-model双向绑定意味着数据可以从父组件流向子组件,也可以从子组件反向流向父组件。

  • computed计算属性实际是ref定义的响应数据

  • 默认computed是只读的缓存,我们可以添加getset方法来返回和设置值实现读写权限。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<template>
<div class="person">
firstname: <input type="text" v-model="firstname"> <br>
lastname: <input type="text" v-model="lastname"> <br>
<button @click="Mikumiku">我推的:</button> <br>
fullname: <span> {{ fullname }}</span>
</div>
</template>


<script lang="ts">
export default {
name: 'Person'
}</script>


<script lang="ts" setup>
import { ref,computed } from 'vue';
let firstname = ref('randolf');
let lastname = ref('luo');

// let fullname = computed(()=>{
// return firstname.value.slice(0,1).toUpperCase() + firstname.value.slice(1) + '-' + lastname.value;
// });

let fullname = computed({
get(){
return firstname.value.slice(0,1).toUpperCase() + firstname.value.slice(1) + '-' + lastname.value;
},
set(val){ //Mikumiku函数修改触发set
const [str1, str2] = val.split('-');
firstname.value = str1;
lastname.value = str2;
}
})
function Mikumiku(){
fullname.value = 'Hatsune-Miku'
}


</script>

watch监视

Vue3中的watch只能监视以下四种数据

  1. ref定义的数据。
  2. reactive定义的数据。
  3. 函数返回一个值(getter函数)。
  4. 一个包含上述内容的数组。
  • watch的第一个参数是:被监视的数据
  • watch的第二个参数是:监视的回调
  • watch的第三个参数是:配置对象(deep、immediate等等…..)

  • 情况一

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

1
2
3
4
5
const stopWatch = watch(sum,(newValue,oldValue)=>{
console.log('sum变化了',newValue,oldValue)
if(newValue >= 10){
stopWatch()
}
  • 情况二

监视ref定义的【对象类型】数据,监视的是对象的【地址值】,监视该对象的值需要开启深度监视(deep)

1
2
3
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{deep:true})
  • 情况三

监视reactive定义的【对象类型】数据,默认开启了深度监视(隐式创建深层监听 )。

1
2
3
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
})
  • 情况四

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

  1. 若该属性值不是【对象类型】,需要写成函数形式。
  2. 若该属性值是依然是【对象类型】,可直接编,也可写成函数,建议写成函数。

结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。

1
2
3
4
5
6
7
8
watch(()=> person.name,(newValue,oldValue)=>{
console.log('person.name变化了',newValue,oldValue)
})


watch(()=>person.car,(newValue,oldValue)=>{
console.log('person.car变化了',newValue,oldValue)
},{deep:true})
  • 情况五

监视上述的多个数据

1
2
3
watch([()=>person.name,person.car],(newValue,oldValue)=>{
console.log('person.car变化了',newValue,oldValue)
},{deep:true})
  • watchEffect:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。
1
2
3
4
5
6
7
8
9
10
11
12
const stopWtach = watchEffect(()=>{
// 室温达到50℃,或水位达到20cm,立刻联系服务器
if(temp.value >= 50 || height.value >= 20){
console.log(document.getElementById('demo')?.innerText)
console.log('联系服务器')
}
// 水温达到100,或水位达到50,取消监视
if(temp.value === 100 || height.value === 50){
console.log('清理了')
stopWtach()
}
})

标签的ref属性

为什么不用javascript引用标签

  • 因为一个页面不能有多个一样的id,但是可以存在多个相同的类 。若父类和子类定义相同的id,则锚点链接可能无法正确工作。

作用:用于模板引用作用:用于注册模板引用。

类似于局部变量

局部样式: