1. Vue框架简介

Vue.js是一套用于前端构建用户界面的渐进式框架
什么是框架?框架是一套封装了多个功能开发套件的代码包,是一种简单、高效的工具箱。
Vue官网:vuejs.org
中文官方手册:https://cn.vuejs.org/guide/introduction.html
Vue API有两种风格:选项式API(Vue2);组合式API(Vue3)

2. 创建vue项目

首先创建一个项目文件夹,VScode中打开该文件夹,新建一个终端,并

2.1 输入初始化vue脚手架指令

1
npm init vue@latest

这一指令会执行创建vue项目, 并弹出一堆可选功能的选项如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Need to install the following packages:
create-vue@3.7.2
Ok to proceed? (y) y //确认创建的vue版本

Vue.js - The Progressive JavaScript Framework

√ Project name: ... vue001 //输入项目名称,不能有大写
√ Add TypeScript? ... No / Yes //这里有关TypeScript
√ Add JSX Support? ... No / Yes //这里有关React
√ Add Vue Router for Single Page Application development? ... No / Yes //这里有关路由
√ Add Pinia for state management? ... No / Yes //这里有关Pinia状态管理
√ Add Vitest for Unit Testing? ... No / Yes //以下是一些测试文件的功能支持
√ Add an End-to-End Testing Solution? » No
√ Add ESLint for code quality? ... No / Yes

Scaffolding project in C:\Users\63015\Desktop\前端笔记\07-vueproject\vue001...

Done. Now run:

cd vue001 //打开项目文件夹
npm install //安装项目依赖
npm run dev //运行项目

2.2 启动vue项目三连

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS C:\Users\63015\Desktop\前端笔记\07-vueproject> cd vue001             //打开项目文件夹
PS C:\Users\63015\Desktop\前端笔记\07-vueproject\vue001> npm install //安装项目依赖(装过了就不用再装)

added 25 packages in 8s
PS C:\Users\63015\Desktop\前端笔记\07-vueproject\vue001> npm run dev //运行项目(重要)

> vue001@0.0.0 dev
> vite //这里可以看到脚手架用的是vite

VITE v4.4.7 ready in 429 ms

➜ Local: http://localhost:5173/ //或者是127.0.0.1:5173
➜ Network: use --host to expose
➜ press h to show help

2.3 开发环境

采用VSCode + Volar扩展插件

2.4 Vue项目的目录结构

在新建终端使用tree指令,我们可以得到vue项目的目录树,其中一级目录下的资源文件与文件夹有:

1
2
3
4
5
6
7
8
9
.vscode         //vscode工具的配置文件夹(删除了也不会影响项目本身的运行)
node_modules //vue项目的运行依赖文件夹(npm install指令安装的vue依赖)
public //资源文件夹(存放浏览器图标等资源)
src //源码文件夹(写的代码都在这里)
.gitignore //Git的忽略文件(Git用)
index.html //HTML入口文件(基底)
package.json //信息描述文件(项目名称、版本、运行脚手架、依赖等)
readme.md //注释文件(写啥都行)
vite.config.js //vue配置文件(跨域、打包等配置)

看完vue项目的目录结构,我们得知src是主要的编写区域,其中components文件夹中存放了一些基础的vue代码,会被src目录下的App.vue主文件调用,所以一开始我们的代码都是存在App.vue这个文件内。
App.vue中最基础结构如下:

1
2
3
4
5
6
7
8
<template>
<div></div>
//此处存放标签组件
</template>

<script>
//此处编写js代码
</script>

然后,打开本地端口5173就可以看到渲染后的窗口了。

2.5 components调用

承接上文记录,App.vue是我们的主代码,components文件夹中存放的是我们自己添加自定义的子代码。那么如何在主代码中调用呢?

1
2
3
4
5
6
7
8
9
10
//此段在App.vue内
<script setup>
//此处调用components
import Helloworld from './components/HelloWorld.vue'
</script>

<template>
//此处渲染components
<Helloworld/>
</template>

如此一来,就形成了树状结构。

3. Vue模板语法

vue使用的是一种基于HTML的模板语法,该语法的特点:通过声明方式将组件的实例数据绑定到DOM上。
个人理解:我在js中通过声明一堆属性和数据,然后属性和数据自己就跑到DOM上。
最终实现的是只修改js部分也可以干涉H5以变更页面显示。
以下就是一些常见的模板语法:

3.1 文本插值 (Mustache写法)

我在js中写的文字,可以传到html的标签中,从而显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<p>{{ msg }}</p>
</template>

<script>
export default {
data(){
return{
msg:"文本信息" //直接添加键值对
}
}
}
</script>

这种语法叫做“双括号语法/大胡子语法/Mustache语法”

3.2 JS表达式

{{ }}中,我们也可以使用一些简单的js表达式,对传入数据进行微调表达。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<p>{{ number + 1 }}</p> {/* 直接使用js表达式,显示为11 */}
<p>{{ ok? 'Yes' : 'No' }}</p> {/* 三元运算符也可以用,显示为Yes */}
<p>{{ message.split('').reverse().join('') }}</p> {/* 对字符串进行空字符切割、翻转、合并,显示为“息信本文” */}
</template>

<script>
export default {
data(){
return{
number:10, //直接添加键值对,注意多组需要加逗号。
ok: true,
message:"文本信息"
}
}
}
</script>

在这里,return后面可以跟这些变量/对象,但不可以跟逻辑语句/非单一表达式。因此,在{{ }}中最好不要放逻辑语句,应当尽量在js中进行逻辑处理后,直接把结果丢给template。

3.3 原始HTML插值 (v-html)

上面的文本插值只能将键值后的数据直接传给标签,并不能支持原生HTML的写法,因此需要使用v-html标签属性(指令)来引入含有HTML写法的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<p v-html="rawHtml"> </p>
</template>

<script>
export default {
data(){
return{
rawHtml:"<a href='#'>XX网页</a>"
}
}
}
</script>

3.4 属性绑定 (v-bind:)

除了上文中的文本信息可以绑定,标签的一些其他属性也可以进行绑定,需要用到v-bind指令来引入标签的style属性/选择器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<p v-bind:class="msg"> 这里是p标签(调用了类选择器) </p>
<p v-bind:id="msgID"> 这里是p标签(调用了id选择器) </p>
</template>

<script>
export default {
data(){
return{
msg:"active",
msgID:"activeID"
}
}
}
</script>

但如果绑定的组件的键值为Null/Undefined,则会忽略这次调用。
且由于v-bind指令十分常用,故而可以直接用冒号进行简写:

1
2
<div :id="dyanmicId" :class="dynamicClass">  </div>
//其中dyanmicId, dynamicClass是我们自定义的键值

3.4.1 小案例:属性绑定布尔值,调用开关

属性绑定有一种应用,即是调用布尔值控制按钮的启用/禁用。

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<button :disabled="isButtonDisabled"> 这里是按钮 </button>
</template>

<script>
export default {
data(){
return{
isButtonDisabled:true //这里设置了true,经过属性绑定会启用该按钮
}
}
}
</script>

3.4.2 小案例:属性绑定多个值

一次次属性绑定会很繁琐,一次性绑定对象的多个值会更为方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div v-bind="objectOfAttrs"> 测试组件 </div> //使用v-bind属性绑定
</template>

<script>
export default {
data(){
return{
objectOfAttrs:{ //新建一个键值对象
id:"appcId" //写上要绑定的选择器
class:"appclass"
}
}
}
}
</script>

4. 条件渲染

在vue中,有提供条件渲染的功能,相关的条件语句如下:

  • v-if
  • v-else
  • v-else-if
  • v-show

4.1 v-if (单分支)

v-if用于条件渲染,当表达式为真时,会渲染一块内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<div v-if="flag"> 测试条件渲染 </div>
</template>

<script>
export default {
data(){
return{
flag:true //这里给true,递给v-if的flag,使得div成功渲染
}
}
}
</script>

4.2 v-else (双分支)

v-else是和v-if相对应的条件渲染指令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div v-if="flag"> 测试条件渲染A(为真显示这个) </div>
<div v-else> 测试条件渲染B(否则显示这个) </div>
</template>

<script>
export default {
data(){
return{
flag:true //若为true,递给v-if的flag,使得A成功渲染;若为false,则使得B成功渲染。
}
}
}
</script>

4.3 v-else-if (多分支)

v-else-if是一个组合的多分支条件渲染的条件渲染指令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div v-if="type === 'A'"> 标签A </div>
<div v-else-if="type === 'B'"> 标签B </div>
<div v-else-if="type === 'C'"> 标签C </div>
<div v-else> 不是标签A/B/C </div>
</template>

<script>
export default {
data(){
return{
type:"D" //这里给type递一个D,送到上面去做多分枝判断,条件渲染。
}
}
}
</script>

4.4 v-show (单分支)

v-showv-if相似,都是单分支的条件判断渲染,用法与v-if一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<div v-show="flag"> 测试条件渲染 </div>
</template>

<script>
export default {
data(){
return{
flag:true //这里给true,递给v-show的flag,使得div成功渲染
}
}
}
</script>

但是,v-if是每次都进行了条件判断后再渲染与否,事件监听和子组件都会销毁重建(真实且惰性);而v-show其实本身早就渲染,只是切换了css的display属性。两者各有妙用。

【总结】
v-if的切换开销大,如果条件很少变动,则v-if更合适。
v-show初始开销相对较大,如果条件频繁切换,则v-show更合适。

5. 列表渲染 (v-for)

在vue中,我们还可以使用v-for指令和item in items写法来基于一个数组来渲染一个列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
<p v-for="item in names"> {{ item }} </p>
</div>
</template>

<script>
export default {
data(){
return{
names:["元素1","元素2","元素3"]
}
}
}
</script>

其中,v-for是指令,item in items是指令值,item是迭代项的别名(自定义,一般不改),items是源数据的数组名(键值名)

5.1 小案例:复杂数据列表渲染

在一个项目中,要渲染的源数据通常来源于网络请求,就是json格式。
下面会给出一个复杂数据的列表渲染-小型案例:

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
<template>
<div v-for="item in result"> //外div标签使用v-for
<p> {{ item.title }} </p> //内部调用各个迭代项的title
<img :src="item.avator" alt=""> //内部调用各个迭代项的avator
</div>
</template>

<script>
export default {
data(){
return{
result:[
{
"id":123456,
"title":"阿巴阿巴阿巴",
"avator":"随便一个图床地址",
}, //result数组中的元素1
{
"id":123457,
"title":"阿巴阿巴阿布",
"avator":"随便一个图床地址",
},
]
}
}
}
</script>

另外,其中的item in items写法中的in也可以用of替代,写作item of items,即:

1
<div v-for="item of result"></div>

5.2 v-for的位置索引(item,index)

此外,v-for也支持对于迭代项的位置索引。即对于每一个迭代项,除了读出它的内容,还可以读出它的序号(index)。

1
2
3
4
5
6
<template>
<div>
<p v-for="(item,index) in Arry"> {{ index }}:{{ item }} </p>
</div>
</template>
...js部分省略...

5.3 v-for遍历对象属性(item,key,index)

因此,v-for也支持对对象进行遍历。除了数据遍历调出、位置序号调出,也可以调出属性键值。

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
<template>
<div>
//遍历数组,将数组中的属性值一个个调出渲染。
<p v-for="item in userInfo"> {{item}} </p>

//遍历数组,将数组中的属性值(123456)-属性键值(id)-位置索引序列号(0)一个个调出渲染。
<p v-for="(item,key,index) in userInfo"> {{item}}-{{key}}-{{index}} </p>
</div>
</template>

<script>
export default {
data(){
return{
userInfo:[
{
"id":123456,
"title":"阿巴阿巴阿巴",
"avator":"随便一个图床地址",
}, //数组中的元素1
{
"id":123457,
"title":"阿巴阿巴阿布",
"avator":"随便一个图床地址",
},
]
}
}
}
</script>

5.4 对key(数据标识节点)的管理

继上一节中的内容,对于一个对象中的一条数据:"id":123456

  • 其中123456是属性的值,数据的内容,通常在v-for中使用value/item来调用。
  • 其中id是属性的键值,属性的名称,通常在v-for中使用key来调用。
  • 以外,该数据在数组中的次序号码,下标,通常在v-for中使用index来调用。

这一节中,我们主要关注另一个key,也就是key attribute,(数据节点标记)的管理。
(item,key,index)中的key属性键值不同,这里的key不是指每条数据的属性的名称,而是对数据库中遍历时的每条数据的一个独一无二的编号,是一个属性(类似python的for-v-k)

1
<p v-for="(item,index) in names" :key="index"> {{ item }} </p>

这里的key是一个通过v-bind绑定的特殊属性;
一般情况下,每一个v-for都需要一个key,用于在遍历索引中识别。
例如:如果数据库中每条数据都有id属性,也可以这么指定key——:key="item.id",这样每条数据的id属性就作为key属性存在了。

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
<template>
<div v-for="item in Arry" :key="item.id">
<p>{{ item.title }}</p>
<img :src="item.avator" alt="">
</div>
</template>

<script>
export default {
data(){
return{
Arry:[
{
"id":123456,
"title":"阿巴阿巴阿巴",
"avator":"随便一个图床地址",
},
{
"id":123457,
"title":"阿巴阿巴阿布",
"avator":"随便一个图床地址",
},
]
}
}
}
</script>

6. 事件处理 (@)

在vue中,可以用v-on指令(简写为@)来监听DOM事件,并于事件触发时执行对应的js,基本语法如下:

1
2
<button v-on:click="methodName"> 我是按钮,点击触发methodName </button>
<button @click="handler"> 我是按钮,点击触发handler </button>

其中,methodNamehandler这俩所在的位置即是事件处理器的值
事件处理器的值可以是:

  • 事件触发时,执行的内联式js语句。(内联事件处理器)
  • 一个指向组件上定义的方法的属性名称/路径。(方法事件处理器)

6.1 内联事件处理器

内联事件处理器一般用于简单场景。语法示范如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
//这里直接将要执行的js语句写在了事件触发器的值内。
<button @click="count++"> 我是木鱼,点击功德+1 </button>
<p> 当前功德:{{ count }} </p>
</template>

<script>
export default {
data(){
return{
count:0
}
}
}
</script>

6.2 方法事件处理器

遇到较为复杂的事件时,可以用函数名称填写值,然后具体逻辑丢到js中去写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
//这里直接将要执行的函数名称addcount写在了事件触发器的值内。
<button @click="addcount"> 我是木鱼,点击功德+1 </button>
<p> 当前功德:{{ count }} </p>
</template>

<script>
export default {
data(){
return{
count:0
}
},
//方法事件的函数写在methods中,所有的方法/函数都丢到这里。
methods:{
//addcount函数
addcount(){
this.count++ //读取data-return里面的数据, 用this.count
}
}
}
</script>

7. 事件传参

事件的参数,可以获取event对象和其他对象,通过事件名来传递参数。
有什么作用?可以在事件函数中进行对象操作,反向干预H5。

7.1 获取Event对象

vue中的event对象即是原生js的Event对象,包含了事件本身的数据信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<button @click="addcount"> 我是木鱼,点击功德+1 </button>
<p> 当前功德:{{ count }} </p>
</template>

<script>
export default {
data(){
return{
count:0
}
},
methods:{
addcount(e){ //获取event对象
console.log(e.target) //获取到button
//获取到button,并通过innerHTML触发更改按钮显示内容。
e.target.innerHTML = "已点击" + this.count + "下"
this.count++ //this.count对应了data中的count变量
}
}
}
</script>

7.2 事件传递参数

这里实现的是从事件触发值的参数传到方法的事件函数的参数中的普通传参。
下面是基础结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<button @click="addcount('我是参数')"> 我是木鱼,点击功德+1 </button>
<p> 当前功德:{{ count }} </p>
</template>

<script>
export default {
data(){
return{
count:0
}
},
methods:{
addcount(msg){ //通过msg接收addcount的第一个参数
console.log(msg) //输出msg即可看见这个参数的具体内容
this.count++
}
}
}
</script>

事件传参案例
为了便于理解事件传参,先看看事件传参的应用案例。

例:现有一组数组,使用列表渲染后,需要实现——点击页面上任意的名字,都在后台输出对应的名字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<h3>事件传参</h3>
{/* 2. 用v-for列表渲染出来 */}
<p @click="getNameHandler(item)" v-for="(item,index) of names" :key="index"> {{item}} </p>
{/* 3. 给p标签添加一个事件getNameHandler;把item当成参数传给事件 */}
</template>

<script>
export default {
data(){
return{
names:["momoeven","niord","anson"] //1. data中给一个names数组
}
},
methods:{
//4. 创建事件函数,并用name接收传参
getNameHandler(name){
console.log(name) //5. 实现点谁显示谁
}
}
}
</script>

7.3 事件传参时获取event对象

现在理解了事件传参,和获取event对象,有无可能同时传参并获取event对象呢?
使用,$event是可以的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<h3>事件传参并且获取event对象</h3>
<p @click="getNameHandler(item,$event)" v-for="(item,index) of names" :key="index"> {{item}} </p>
{/* 同时传参并传递event */}
</template>

<script>
export default {
data(){
return{
names:["momoeven","niord","anson"]
}
},
methods:{
getNameHandler(name,e){ //同时收参并接收event
console.log(name);
console.log(e);
}
}
}
</script>

8. 事件修饰符

事件修饰符可以用来简化代码。
比如:阻止默认事件;阻止事件冒泡等。
更多事件修饰符可以看:https://cn.vuejs.org/guide/essentials/event-handling.html#event-modifiers

8.1 阻止默认事件

如果现在有一个a标签,需求是点击a标签只记录但是不进行跳转。这就需要用到阻止默认事件来阻止默认的a标签跳转。

范例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<h3>阻止默认事件</h3>
<a @click.prevent="clickHandle" href="#" >超链接</a>
{/* 这里使用了艾特click.prevent来简化阻止默认事件的写法 */}
</template>

<script>
export default {
data(){
return{

}
},
methods:{
clickHandle(){
//e.preventDefault(); //这里是阻止默认事件的原本写法(记得获取e)
console.log(已点击);
}
}
}
</script>

8.2 阻止事件冒泡

事件冒泡是指同一个操作同时触发了多个事件,比如子元素触发了然后父元素也触发了。
阻止事件冒泡就是当子元素触发后,阻止父元素继续触发。

范例如下:

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>
<h3>阻止事件冒泡</h3>
<div @click="clickDiv">
<p @click.stop="clickP">测试冒泡</p>
{/* 这里使用了艾特click.stop来简化阻止事件冒泡的写法 */}
</div>
</template>

<script>
export default {
data(){
return{

}
},
methods:{
clickDiv(){
console.log("div");
},
clickP(){
//e.stopPropagation(); //这里是阻止事件冒泡的原本写法(记得获取e)
console.log("p");
},
}
}
</script>

9. 数组变化侦测