✌️ Vue
一、概述
Vue( 读音 /vjuː /, 类似于 view ),是一款用于 构建用户界面 的 渐进式 的
JavaScript框架官方网站 : https://cn.vuejs.org/
1) . 构建用户界面
构建用户界面是指,在
Vue中,可以 基于数据渲染 出用户看到的界面。比如将来服务器端返回给前端的原始数据(
JSON),就是如下这个样子:
jsonuserList: [ {"id": 1, "name": "谢逊", "image": "1.jpg", "gender": 1, "job": "班主任"}, {"id": 2, "name": "韦一笑", "image": "2.jpg", "gender": 1, "job": "班主任"} ]而上面的这些原始数据,用户是看不懂的。 而我们开发人员呢,可以使用
Vue中提供的操作,将原始数据遍历、解析出来,从而渲染呈现出用户所能看懂的界面,如下所示:

2) . 渐进式
渐进式中的渐进呢,字面意思就是 "循序渐进" 。Vue生态中的语法呢是非常多的,
比如声明式渲染、组件系统、客户端路由(VueRouter)、
状态管理(Vuex、Pinia)、构建工具(Webpack、Vite)等等。

所谓渐进,指的是我们使用
Vue框架呢,我们不需要把所有的组件、语法全部学习完毕才可以使用 Vue 。
而是,我们学习一点就可以使用一点了,比如:
- 我们学习了声明式渲染,我们就可以使用 Vue 来构建用户界面了。
- 我们再学习了组件系统,我们就可以使用 Vue 中的组件,从而来复用了。
- 我们再学习了路由 VueRouter,就可以使用 Vue 中的中的路由功能了。
也就是说,并不需要全部学习完毕就可以直接使用 Vue 进行开发,
简化操作、提高效率了。 Vue 是一个框架,但其实也是一个生态。
二、快速上手
2.1 需求
在入门程序中,最终我们需要将准备的数据
message的值,基于 Vue 渲染展示在页面中,最终呈现的形式如下:

2.2 步骤
1) . 准备工作
准备一个
html文件,并在其中引入Vue模块 (参考官方文档,复制过来即可)【注意:模块化的
js,引入时,需要设置type="module"】创建
Vue程序的应用实例,控制视图的元素准备元素(div),交给
Vue控制
<body>
<!-- 3.准备元素 (div),被 Vue 控制 -->
<div id="app">
</div>
<!-- 模块化 js 要声明 module -->
<script type="module">
// 1. 引入 Vue 模块
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';
// 2. 创建 Vue 程序的应用实例,控制视图的元素
createApp({
}).mount("#app") // mount 去需要控制的元素
</script>
</body>- 这是三步准备工作,是我们使用 Vue 时,都需要做的,是固定步骤。
- 这样我们就搭建好了一个基本的 Vue 的结构了。
2) . 数据驱动视图
准备数据。 在创建 Vue 应用实例的时候,传入了一个 js 对象,在这个 js 对象中,
我们要定义一个 data 方法,这个 data 方法的返回值就是 Vue 中的数据。
通过插值表达式渲染页面。 插值表达式的写法:(两个 大括号 / 花括号 包裹)

- 3) . 实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue-快速入门</title>
</head>
<body>
<!-- 3.准备元素 (div),被 Vue 控制 -->
<div id="app">
<h1>{{message}}</h1>
</div>
<!-- 模块化 js 要声明 module -->
<script type="module">
// 1. 引入 Vue 模块
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';
// 2. 创建 Vue 程序的应用实例,控制视图的元素
createApp({
data(){
return {
message : "Hello Vue"
}
}
}).mount("#app") // mount 去需要控制的元素
</script>
</body>
</html>在上述入门程序编写时,需要注意这么几点:
Vue 中定义数据,必须通过 data 方法来定义,data 方法返回值是一个对象,在这个对象中定义数据。
插值表达式中编写的变量,一定是 Vue 中定义的数据,
如果插值表达式中编写了一个变量,但是在 Vue 中未定义,将会报错 。
Vue 应用实例接管的区域是
'#app',超出这个范围,就不受 Vue 控制了
三、Vue 指令
3.1 概述
指令:指的是 HTML 标签上带有
v-前缀的特殊属性,不同指令具有不同含义,可以实现不同的功能 。例如:
v-if,v-for…
- 形式:
<p v-xxx="....">.....</p>- 常见指令:
| 指令 | 作用 |
|---|---|
v-for | 列表渲染,遍历容器的元素或者对象的属性 |
v-bind | 为 HTML 标签绑定属性值,如设置 href , css 样式等 |
v-if/v-else-if/v-else | 条件性地渲染某元素,判定为 true 时渲染,否则不渲染 |
v-show | 根据条件展示某元素,区别在于切换的是 display 属性的值 |
v-model | 在表单元素上创建双向数据绑定 |
v-on | 为 HTML 标签绑定事件 |
3.2 v-for
- 作用:列表渲染,遍历容器的元素或者对象的属性
- 语法:
<tr v-for="(item,index) in items" :key="item.id">{{item}}</tr>- 参数说明:
items为遍历的数组item为遍历出来的元素index为索引/下标,从0开始 ;可以省略,省略index语法:v-for = "item in items"key:- 作用:给元素添加的唯一标识,便于 vue 进行列表项的正确排序复用,提升渲染性能
- 推荐使用
id作为 key(唯一),不推荐使用index作为 key (会变化,不对应)
注意:遍历的数组,必须在 data 中定义;
要想让哪个标签循环展示多次,就在哪个标签上使用 v-for 指令。
- 案例代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tlias智能学习辅助系统</title>
<style>
body {
margin: 0;
}
/* 顶栏样式 */
.header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #c2c0c0;
padding: 20px 20px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
/* 加大加粗标题 */
.header h1 {
margin: 0;
font-size: 24px;
font-weight: bold;
}
/* 文本链接样式 */
.header a {
text-decoration: none;
color: #333;
font-size: 16px;
}
/* 搜索表单区域 */
.search-form {
display: flex;
align-items: center;
padding: 20px;
background-color: #f9f9f9;
}
/* 表单控件样式 */
.search-form input[type="text"], .search-form select {
margin-right: 10px;
padding: 10px 10px;
border: 1px solid #ccc;
border-radius: 4px;
width: 26%;
}
/* 按钮样式 */
.search-form button {
padding: 10px 15px;
margin-left: 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* 清空按钮样式 */
.search-form button.clear {
background-color: #6c757d;
}
.table {
min-width: 100%;
border-collapse: collapse;
}
/* 设置表格单元格边框 */
.table td, .table th {
border: 1px solid #ddd;
padding: 8px;
text-align: center;
}
.avatar {
width: 30px;
height: 30px;
object-fit: cover;
border-radius: 50%;
}
/* 页脚版权区域 */
.footer {
background-color: #c2c0c0;
color: white;
text-align: center;
padding: 10px 0;
margin-top: 30px;
}
.footer .company-name {
font-size: 1.1em;
font-weight: bold;
}
.footer .copyright {
font-size: 0.9em;
}
#container {
width: 80%;
margin: 0 auto;
}
</style>
</head>
<body>
<div id="container">
<!-- 顶栏 -->
<div class="header">
<h1>Tlias智能学习辅助系统</h1>
<a href="#">退出登录</a>
</div>
<!-- 搜索表单区域 -->
<form class="search-form" action="#" method="post">
<input type="text" name="name" placeholder="姓名" />
<select name="gender">
<option value="">性别</option>
<option value="1">男</option>
<option value="2">女</option>
</select>
<select name="job">
<option value="">职位</option>
<option value="1">班主任</option>
<option value="2">讲师</option>
<option value="3">学工主管</option>
<option value="4">教研主管</option>
<option value="5">咨询师</option>
</select>
<button type="submit">查询</button>
<button type="reset" class="clear">清空</button>
</form>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>性别</th>
<th>头像</th>
<th>职位</th>
<th>入职日期</th>
<th>最后操作时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- 需填充位置 -->
</tbody>
</table>
<!-- 页脚版权区域 -->
<footer class="footer">
<p class="company-name">江苏传智播客教育科技股份有限公司</p>
<p class="copyright">版权所有 Copyright 2006-2024 All Rights Reserved</p>
</footer>
<script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
data() {
return {
empList: [
{ "id": 1,
"name": "谢逊",
"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/4.jpg",
"gender": 1,
"job": "1",
"entrydate": "2023-06-09",
"updatetime": "2024-07-30T14:59:38"
},
{
"id": 2,
"name": "韦一笑",
"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg",
"gender": 1,
"job": "1",
"entrydate": "2020-05-09",
"updatetime": "2023-07-01T00:00:00"
},
{
"id": 3,
"name": "黛绮丝",
"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/2.jpg",
"gender": 2,
"job": "2",
"entrydate": "2021-06-01",
"updatetime": "2023-07-01T00:00:00"
}
]
}
}
}).mount('#container')
</script>
</div>
</body>
</html>需求说明:在上面的
<tbody>位置使用v-for填充代码 ,相关的数据在 vue 的
data()部分声明好了,需要使用v-for+ 插值表达式 ... 渲染出来数据
- 示例代码:
<tbody>
<tr v-for="(e, index) in empList" :key="e.id">
<td>{{index + 1}}</td>
<td>{{e.name}}</td>
<td>{{e.gender == 1 ? "男":"女"}}</td>
<!-- 1. 插值表达式是不能出现在标签内部的 (下面的写法是错误的) -->
<td><img class="avatar" src="{{e.image}}" alt="{{e.image}}"></td>
<!-- 2. job 还没有对应起来 -->
<td>{{e.job}}</td>
<td>{{e.entrydate}}</td>
<td>{{e.updatetime}}</td>
<td class="btn-group">
<button class="edit">编辑</button>
<button class="delete">删除</button>
</td>
</tr>
</tbody>- 暂时表现的效果如下:

这些问题将在后面得到解决 ...
3.3 v-bind
- 作用:动态为 HTML 标签绑定属性值,如设置
href,src,style样式等。 - 语法:
<!-- v-bind:属性名="属性值" -->
<img v-bind:src="item.image" width="30px">- (语法)简化:可以直接冒号,不用写
v-bind
<!-- :属性名="属性值" -->
<img :src="item.image" width="30px">- 注意:
v-bind所绑定的数据,必须在 data 中定义或基于 data 中定义的数据而来。
解决之前图像显示的问题:插值表达式是不能出现在标签内部的 !!
- 示例代码:
<tbody>
<tr v-for="(e, index) in empList" :key="e.id">
<td>{{index + 1}}</td>
<td>{{e.name}}</td>
<td>{{e.gender == 1 ? "男":"女"}}</td>
<td><img class="avatar" v-bind:src="e.image" :alt="e.image"></td>
<!-- job 还没有对应起来 -->
<td>{{e.job}}</td>
<td>{{e.entrydate}}</td>
<td>{{e.updatetime}}</td>
<td class="btn-group">
<button class="edit">编辑</button>
<button class="delete">删除</button>
</td>
</tr>
</tbody>- 效果:

3.4 v-if & v-show
- 作用:这两类指令,都是用来控制元素的显示与隐藏的
v-if- 语法:
v-if="表达式",表达式值为 true,显示;false,隐藏 - 原理:基于条件判断,来控制创建或移除元素节点(条件渲染)
- 场景:要么显示,要么不显示,不频繁切换的场景
- 其它:可以配合
v-else-if / v-else进行链式调用条件判断 - 注意:v-else-if 必须出现在 v-if 之后,可以出现多个; v-else 必须出现在 v-if/v-else-if 之后 。
- 语法:
v-show- 语法:
v-show="表达式",表达式值为 true,显示;false,隐藏 - 原理:基于 CSS 样式 display 来控制显示与隐藏
- 场景:频繁切换显示隐藏的场景
- 语法:
解决上面职位的问题
- 示例代码:
<td>
<!-- 使用 v-if: 基于条件判断,来控制创建或移除元素节点(条件渲染) -->
<span v-if="e.job == 1">班主任</span>
<span v-else-if="e.job == 2">讲师</span>
<span v-else-if="e.job == 3">学工主管</span>
<span v-else-if="e.job == 4">教研主管</span>
<span v-else-if="e.job == 5">咨询师</span>
<span v-else>其它</span>
<!-- 使用 v-show: 基于CSS样式display来控制显示与隐藏 -->
<!-- <span v-show="e.job == 1">班主任</span>
<span v-show="e.job == 2">讲师</span>
<span v-show="e.job == 3">学工主管</span>
<span v-show="e.job == 4">教研主管</span>
<span v-show="e.job == 5">咨询师</span> -->
</td>- 效果:

- 在使用
v-if的时候:

- 在使用
v-show的时候:

3.5 v-model
作用:在表单元素上使用,
双向数据绑定。可以方便的获取或设置表单项数据语法:
v-model="变量名"这里的双向数据绑定,是指 Vue 中的数据变化,会影响视图中的数据展示 。
视图中的输入的数据变化,也会影响 Vue 的数据模型 。
注意:
v-model中绑定的变量,必须在data中定义。

- 为员工列表案例的搜索栏的表单项,绑定数据:
<!-- 搜索表单区域 -->
<form class="search-form" action="#" method="post">
<!-- 1.姓名区域 添加v-model -->
<input type="text" name="name" placeholder="姓名" v-model="searchForm.name"/>
<!-- 2.性别区域 添加v-model -->
<select name="gender" v-model="searchForm.gender">
<option value="">性别</option>
<option value="1">男</option>
<option value="2">女</option>
</select>
<!-- 3.职位区域 添加v-model -->
<select name="job" v-model="searchForm.job">
<option value="">职位</option>
<option value="1">班主任</option>
<option value="2">讲师</option>
<option value="3">学工主管</option>
<option value="4">教研主管</option>
<option value="5">咨询师</option>
</select>
<button type="button" @click="search">查询</button>
<button type="button" class="clear" @click="clear">清空</button>
</form><script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
data() {
return {
//定义数据模型,采集员工搜索表单数据
searchForm:{
name:"",
gender:"",
job:""
},
empList: [
{ "id": 1,
"name": "谢逊",
"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/4.jpg",
"gender": 1,
"job": "1",
"entrydate": "2023-06-09",
"updatetime": "2024-07-30T14:59:38"
},
{
"id": 2,
"name": "韦一笑",
"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg",
"gender": 1,
"job": "1",
"entrydate": "2020-05-09",
"updatetime": "2023-07-01T00:00:00"
},
{
"id": 3,
"name": "黛绮丝",
"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/2.jpg",
"gender": 2,
"job": "2",
"entrydate": "2021-06-01",
"updatetime": "2023-07-01T00:00:00"
}
]
}
},
}).mount('#container')
</script>3.6 v-on
- 作用:为 html 标签绑定事件(添加时间监听)
- 语法:
v-on:事件名="方法名"- 简写:
@事件名="…"
<!-- v-on -->
<input type="button" value="点我一下试试" v-on:click="handle">
<!-- @ 简写 -->
<input type="button" value="点我一下试试" @click="handle">- 这里的
handle函数,就需要在 Vue 应用实例创建的时候创建出来,在methods定义。

- 为员工列表案例的搜索栏中的查询 和 清空按钮绑定事件:
<button type="button" @click="search">查询</button>
<button type="button" class="clear" @click="clear">清空</button><script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
data() {
return {
//定义数据模型,采集员工搜索表单数据
searchForm:{
name:"",
gender:"",
job:""
},
empList: [
{ "id": 1,
"name": "谢逊",
"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/4.jpg",
"gender": 1,
"job": "1",
"entrydate": "2023-06-09",
"updatetime": "2024-07-30T14:59:38"
},
{
"id": 2,
"name": "韦一笑",
"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/1.jpg",
"gender": 1,
"job": "1",
"entrydate": "2020-05-09",
"updatetime": "2023-07-01T00:00:00"
},
{
"id": 3,
"name": "黛绮丝",
"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2023/2.jpg",
"gender": 2,
"job": "2",
"entrydate": "2021-06-01",
"updatetime": "2023-07-01T00:00:00"
}
]
}
},
// 方法:
methods: {
search() {
console.log(this.searchForm)
},
clear() {
this.searchForm = {
name: '',
gender: '',
job: ''
}
}
}
}).mount('#container')
</script>- 注意: methods 函数中的 this 指向 Vue 实例,可以通过 this 获取到 data 中定义的数据。
3.7 Vue 调试工具
- Chrome浏览器插件地址:https://chrome.zzzmh.cn/#/index

- 下载后解压出
.crx后缀的文件 - 打开谷歌的
管理扩展程序:

- 拖拽
.crx后缀文件进去浏览器页面 - 打开下面的这个选项

F12里面使用

四、Ajax & Axios
4.0 异步请求地址
Ajax演示:
GET请求: https://mock.apifox.cn/m1/3083103-0-default/emps/list
POST请求: https://mock.apifox.cn/m1/3083103-0-default/emps/update
Ajax员工列表案例地址:
https://web-server.itheima.net/emps/list
https://web-server.itheima.net/emps/list?name=xxx&gender=xxx&job=xxx4.1 概述
我们前端页面中的数据,如下图所示的表格中的员工信息,应该来自于后台,
那么我们的后台和前端是互不影响的 2 个程序,那么我们前端应该如何从后台获取数据呢?
因为是2个程序,所以必须涉及到2个程序的交互,所以这就需要用到我们接下来学习的 Ajax 技术。

Ajax: 全称Asynchronous JavaScript And XML,异步的 JavaScript 和 XML 。XML:(英语:Extensible Markup Language)可扩展标记语言,
本质是一种数据格式,可以用来存储复杂的数据结构。
Ajax作用有如下 2 点 :与服务器进行数据交换:通过
Ajax可以给服务器发送请求,并获取服务器响应的数据。异步交互:可以在 不重新加载整个页面 的情况下,与服务器交换数据并 更新部分网页 的技术,
如:搜索联想、用户名是否可用的校验等等。
详细解释一下这两点作用:
与服务器进行数据交互
如下图所示前端资源被浏览器解析,但是前端页面上缺少数据,前端可以通过 Ajax 技术,
向后台服务器发起请求,后台服务器接受到前端的请求,从数据库中获取前端需要的资源,
然后响应给前端,前端在通过我们学习的
vue技术,可以将数据展示到页面上,这样用户就能看到完整的页面了。此处可以对比
JavaSE中的网络编程技术来理解

异步交互 : 可以在 不重新加载整个页面 的情况下,与服务器交换数据并 更新部分网页 的技术
如下图所示,当我们再百度搜索 java 时,下面的联想数据是通过 Ajax 请求从后台服务器得到的,
在整个过程中,我们的 Ajax 请求不会导致整个百度页面的重新加载,
并且只针对搜索栏这局部模块的数据进行了数据的更新,
不会对整个页面的其他地方进行数据的更新,这样就大大提升了页面的加载速度,用户体验高。

补充:同步异步是什么??
针对于上述 Ajax 的局部刷新功能是因为 Ajax 请求是异步的,与之对应的有同步请求
- 同步请求如下图所示:

浏览器页面在发送请求给服务器,在服务器处理请求的过程中,浏览器页面不能做其他的操作。
只能等到服务器响应结束后才能,浏览器页面才能继续做其他的操作。
- 异步请求如下图所示:

浏览器页面发送请求给服务器,在服务器处理请求的过程中,浏览器页面还可以做其他的操作
4.2 Axios
使用原生的 Ajax 请求的代码编写起来还是比较繁琐的,
所以接下来我们学习一门更加简单的发送 Ajax 请求的技术Axios 。
Axios是对原生的 AJAX 进行封装,简化书写。
Axios官网是:https://www.axios-http.cn

4.2.1 入门程序
Axios 的使用比较简单,主要分为 2 步 :
- 1). 引入 Axios 文件(如果网络不通畅,可以使用离线的已经下载好的 js 文件,下面网盘链接已提供):
html<script src="https://unpkg.com/axios/dist/axios.min.js"></script>txt通过网盘分享的文件:axios.js 链接: https://pan.baidu.com/s/1ELaOyyT_jF6RShFMa2eXdQ?pwd=y2h8 提取码: y2h8- 2). 点击按钮时,使用 Axios 发送请求
html<body> <button id="getData">发起 GET 请求</button> <button id="postData">发起 POST 请求</button> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> //GET请求 document.querySelector('#getData').onclick = function() { axios({ url:'https://mock.apifox.cn/m1/3083103-0-default/emps/list', method:'get' }).then(function(res) { console.log(res.data); }).catch(function(err) { console.log(err); }) } //POST请求 document.querySelector('#postData').onclick = function() { axios({ url:'https://mock.apifox.cn/m1/3083103-0-default/emps/update', method:'post', data:"id=1" //POST请求体 }).then(function(res) { console.log(res.data); }).catch(function(err) { console.log(err); }) } </script> </body>
4.2.2 请求方式别名
- 为了方便起见,
Axios已经为所有支持的请求方法提供了别名 - 格式 :
axios.请求方式(url[,data[,config]])
- 所以在上述的入门案例中,我们可以将
get请求代码改写成如下:
//GET请求
document.querySelector('#getData').onclick = function() {
axios.get("https://mock.apifox.cn/m1/3083103-0-default/emps/list")
.then((result) => {
console.log(result.data.data);
}).catch((err) => {
console.log(err);
});
}post请求改写成如下:
//POST请求
document.querySelector('#postData').onclick = function() {
axios.post("https://mock.apifox.cn/m1/3083103-0-default/emps/update","id=1")
.then((result) => {
console.log(result.data);
}).catch((err) => {
console.log(err);
});
}补充 : 在使用 axios 时,在 axios 之后,输入
thenc然后Tab键会自动生成成功及失败回调函数结构 。
4.2.3 案例:异步获取数据
需求:基于
axios动态加载员工列表数据
- 示例代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tlias智能学习辅助系统</title>
<style>
body {
margin: 0;
}
/* 顶栏样式 */
.header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #c2c0c0;
padding: 20px 20px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
/* 加大加粗标题 */
.header h1 {
margin: 0;
font-size: 24px;
font-weight: bold;
}
/* 文本链接样式 */
.header a {
text-decoration: none;
color: #333;
font-size: 16px;
}
/* 搜索表单区域 */
.search-form {
display: flex;
align-items: center;
padding: 20px;
background-color: #f9f9f9;
}
/* 表单控件样式 */
.search-form input[type="text"], .search-form select {
margin-right: 10px;
padding: 10px 10px;
border: 1px solid #ccc;
border-radius: 4px;
width: 26%;
}
/* 按钮样式 */
.search-form button {
padding: 10px 15px;
margin-left: 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* 清空按钮样式 */
.search-form button.clear {
background-color: #6c757d;
}
.table {
min-width: 100%;
border-collapse: collapse;
}
/* 设置表格单元格边框 */
.table td, .table th {
border: 1px solid #ddd;
padding: 8px;
text-align: center;
}
.avatar {
width: 30px;
height: 30px;
object-fit: cover;
border-radius: 50%;
}
/* 页脚版权区域 */
.footer {
background-color: #c2c0c0;
color: white;
text-align: center;
padding: 10px 0;
margin-top: 30px;
}
.footer .company-name {
font-size: 1.1em;
font-weight: bold;
}
.footer .copyright {
font-size: 0.9em;
}
#container {
width: 80%;
margin: 0 auto;
}
</style>
</head>
<body>
<div id="container">
<!-- 顶栏 -->
<div class="header">
<h1>Tlias智能学习辅助系统</h1>
<a href="#">退出登录</a>
</div>
<!-- 搜索表单区域 -->
<form class="search-form" action="#" method="post">
<input type="text" name="name" placeholder="姓名" v-model="searchForm.name"/>
<select name="gender" v-model="searchForm.gender">
<option value="">性别</option>
<option value="1">男</option>
<option value="2">女</option>
</select>
<select name="job" v-model="searchForm.job">
<option value="">职位</option>
<option value="1">班主任</option>
<option value="2">讲师</option>
<option value="3">学工主管</option>
<option value="4">教研主管</option>
<option value="5">咨询师</option>
</select>
<button type="button" @click="search">查询</button>
<button type="button" class="clear" @click="clear">清空</button>
</form>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>性别</th>
<th>头像</th>
<th>职位</th>
<th>入职日期</th>
<th>最后操作时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(e, index) in empList" :key="e.id">
<td>{{index + 1}}</td>
<td>{{e.name}}</td>
<td>{{e.gender == 1 ? "男":"女"}}</td>
<td><img class="avatar" v-bind:src="e.image" :alt="e.image"></td>
<td>
<!-- 使用 v-if: 基于条件判断,来控制创建或移除元素节点(条件渲染) -->
<span v-if="e.job == 1">班主任</span>
<span v-else-if="e.job == 2">讲师</span>
<span v-else-if="e.job == 3">学工主管</span>
<span v-else-if="e.job == 4">教研主管</span>
<span v-else-if="e.job == 5">咨询师</span>
<span v-else>其他</span>
<!-- 使用 v-show: 基于CSS样式display来控制显示与隐藏 -->
<!-- <span v-show="e.job == 1">班主任</span>
<span v-show="e.job == 2">讲师</span>
<span v-show="e.job == 3">学工主管</span>
<span v-show="e.job == 4">教研主管</span>
<span v-show="e.job == 5">咨询师</span> -->
</td>
<td>{{e.entrydate}}</td>
<td>{{e.updatetime}}</td>
<td class="btn-group">
<button class="edit">编辑</button>
<button class="delete">删除</button>
</td>
</tr>
</tbody>
</table>
<!-- 页脚版权区域 -->
<footer class="footer">
<p class="company-name">江苏传智播客教育科技股份有限公司</p>
<p class="copyright">版权所有 Copyright 2006-2024 All Rights Reserved</p>
</footer>
<!-- 导入axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
data() {
return {
//定义数据模型,采集员工搜索表单数据
searchForm:{
name:"",
gender:"",
job:""
},
//清空empList,通过异步请求数据
empList: []
}
},
//方法:
methods: {
//基于 axios 发送异步请求
search() {
axios.get(`https://web-server.itheima.net/emps/list?name=${this.searchForm.name}&gender=${this.searchForm.gender}&job=${this.searchForm.job}`)
.then((result) => {
this.empList = result.data.data
}).catch((err) => {
alert(err)
});
},
clear() {
this.searchForm = {
name: '',
gender: '',
job: ''
}
}
}
}).mount('#container')
</script>
</div>
</body>
</html>- 现在这版代码就可以在查找栏输入关键词去查找特定的员工信息了
- 但是还有欠缺的部分,后面再补充 ...
4.2.4 async & await
如果使用
axios中提供的.then(function(){....}).catch(function(){....}),这种回调函数的写法,会使得代码的可读性和维护性变差。 而为了解决这个问题,
我们可以使用两个关键字,分别是:async、await。
可以通过
async、await可以 让异步变为同步操作 。async就是来声明一个异步方法,await是用来等待异步任务执行。
- 代码修改前:
//基于 axios 发送异步请求
search() {
axios.get(`https://web-server.itheima.net/emps/list?name=${this.searchForm.name}&gender=${this.searchForm.gender}&job=${this.searchForm.job}`)
.then((result) => {
this.empList = result.data.data
}).catch((err) => {
alert(err)
});
}- 代码修改后
//使用 async 关键字声明是异步方法
async search() {
//使用 await 使得在异步方法内等待该方法执行完再执行后续代码(局部同步)
const res = await axios.get(`https://web-server.itheima.net/emps/list?name=${this.searchForm.name}&gender=${this.searchForm.gender}&job=${this.searchForm.job}`)
//获取 返回来的数据赋值给 res 再赋值去 this.empList
this.empList = res.data.data
}修改后,代码就变成同步操作了,一行一行的从前往后执行。
在前端项目开发中,经常使用这两个关键字配合,使得代码的可读性和可维护性变高。
五、Vue の 生命周期
5.1 介绍
vue的生命周期:指的是vue对象从创建到销毁的过程。vue的生命周期包含 8 个阶段:每触发一个生命周期事件,会自动执行一个生命周期方法,这些生命周期方法也被称为
钩子方法。其完整的生命周期如下表所示:
| 状态 | 阶段周期 |
|---|---|
beforeCreate | 创建前 |
created | 创建后 |
beforeMount | 挂载前 |
mounted | 挂载完成 |
beforeUpdate | 更新前 |
updated | 更新后 |
beforeDestory | 销毁前 |
destroyed | 销毁后 |
- 下图是 Vue 官网提供的从创建 Vue 到效果 Vue 对象的整个过程及各个阶段对应的钩子函数:

其中我们需要重点关注的是
mounted,其他的我们了解即可。
mounted:挂载完成,Vue 初始化成功,HTML 页面渲染成功。以后我们一般用于页面初始化自动的 ajax 请求后台数据
5.2 案例完善
<!-- 导入axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
data() {
return {
//定义数据模型,采集员工搜索表单数据
searchForm:{
name:"",
gender:"",
job:""
},
//清空empList,通过异步请求数据
empList: []
}
},
//方法:
methods: {
//使用 async 关键字声明是异步方法
async search() {
//使用 await 使得在异步方法内等待该方法执行完再执行后续代码(局部同步)
const res = await axios.get(`https://web-server.itheima.net/emps/list?name=${this.searchForm.name}&gender=${this.searchForm.gender}&job=${this.searchForm.job}`)
this.empList = res.data.data
},
clear() {
this.searchForm = {
name: '',
gender: '',
job: ''
}
//清空后不但清空查找栏,还需要调用一次search(),更新下面的表格信息
this.search()
}
},
//挂载完成后search()一次,完成页面的初始化
mounted(){
this.search()
}
}).mount('#container')
</script>
在挂载完成后 search() 一次请求到数据,完成页面的初始化,就完成了我们上面的基础的案例。
六、Vue 工程化

在前面我们学习了 Vue 的基本语法、表达式、指令,并基于 Vue 的核心包,完成了 Vue 的案例.
但这只是使用 Vue 进行前端的局部模块的改造,我们的目的是使用 Vue 来进行整站开发。
6.1 介绍
在前面的课程中,我们学习了 HTML、CSS、JS、Axios、Vue 等技术,并基于完成了一些前端开发的案例 。
我们目前的前端开发中,当我们需要使用一些资源时,例如:vue.js,和 axios.js 文件,
都是直接再工程中导入的,如下图所示:

但是上述开发模式存在如下问题:
- 不规范:每次开发都是从零开始,比较麻烦
- 难复用:多个页面中的组件共用性不好
- 难维护:js、图片等资源没有规范化的存储目录,没有统一的标准,不方便维护
所以现在企业开发中更加讲究前端工程化方式的开发,主要包括如下 4 个特点 :

- 模块化:将 js 和 css 等,做成一个个可复用模块
- 组件化:我们将 UI 组件,css 样式,js 行为封装成一个个的组件,便于管理
- 规范化:我们提供一套标准的规范的目录接口和编码规范,所有开发人员遵循这套规范
- 自动化:项目的构建,测试,部署全部都是自动完成
所以对于前端工程化,说白了,就是在企业级的前端项目开发中,把前端开发所需要的工具、技术、流程、
经验进行规范化和标准化。从而统一开发规范、提升开发效率,降低开发难度、提高复用等等。
接下来我们就需要学习 vue 的官方提供的脚手架帮我们完成前端的工程化。
6.2 环境准备
6.2.1 介绍
create-vue是 Vue 官方提供的最新的脚手架工具,用于快速生成一个工程化的 Vue 项目。create-vue提供了如下功能:- 统一的目录结构
- 本地调试
- 热部署
- 单元测试
- 集成打包上线
而要想使用
create-vue来创建 vue 项目,则必须安装依赖环境:NodeJS

6.2.2 Nodejs 安装
如下网盘链接提供 NodeJS 安装包:
通过网盘分享的文件:node-v18.20.4-x64.msi
链接: https://pan.baidu.com/s/127Ut5GL8_8eS90y6ui0jUw?pwd=e4iv 提取码: e4iv- 1). 双击 msi 文件,勾选我接受

- 2). 选择安装到一个,没有中文,没有空格 的目录下(新建一个文件夹
NodeJS)

- 3). 点击 Next,下一步下一步的安装即可。

- 4). 验证 NodeJS 的环境变量
NodeJS 安装完毕后,会自动配置好环境变量,我们验证一下是否安装成功,通过: node -v

- 5). 配置 npm 的全局安装路径
- 后面需要用
npm install安装软件包,这样可以指定安装目录- 避免占用系统盘空间(全局包可能很多很大)
- 方便管理(比如
Node.js安装在D:\develop\Nodejs,全局包也放在一起)
- 后面需要用

使用 管理员身份 运行命令行,在命令行中,执行如下指令:
npm config set prefix "D:\develop\NodeJS"可以命令行运行以下命令查看有没有配置好:
npm config get prefix此外可以随便安装一个工具 nodemon:
npm install -g nodemon如果在文件管理器找得到 D:\develop\Nodejs\node_modules\nodemon 就说明没问题
❗ 注意:
注意:D:\develop\NodeJS 这个目录是 NodeJS 的安装目录 !!!!! 换成自己实际的 !!!
注意:D:\develop\NodeJS 这个目录是 NodeJS 的安装目录 !!!!! 换成自己实际的 !!!
注意:D:\develop\NodeJS 这个目录是 NodeJS 的安装目录 !!!!! 换成自己实际的 !!!
- 6). 切换为淘宝镜像,加速下载:
npm config set registry https://registry.npmmirror.com6.2.3 npm 介绍
npm:Node Package Manager,是NodeJS的软件包管理器。

在开发前端项目的过程中,我们需要相关的依赖,就可以直接通过 npm install xxx 命令,
直接从远程仓库中将依赖直接下载到本地了。
6.3 Vue 项目创建
6.3.1 项目创建
- ❗ 选定一个空文件夹,创建一个工程化的 Vue 项目,执行命令:
npm create vue@3.3.4

📌 详细说明:
Project name:------------------》项目名称,默认值:vue-project,可输入想要的项目名称。Add TypeScript?----------------》是否加入TypeScript组件?默认值:No。Add JSX Support?--------------》是否加入JSX支持?默认值:No。Add Vue Router...--------------》是否为单页应用程序开发添加Vue Router路由管理组件?默认值:No。Add Pinia ...----------------------》是否添加Pinia组件来进行状态管理?默认值:No。Add Vitest ...---------------------》是否添加Vitest来进行单元测试?默认值:No。Add an End-to-End ...-----------》是否添加端到端测试?默认值 No。Add ESLint for code quality?---》是否添加ESLint来进行代码质量检查?默认值:No。
📌 执行上述指令,将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具
项目创建完成以后,
cd vue-project01进入vue-project01项目目录,执行命令安装当前项目的依赖:
npm install

📌 创建项目以及安装依赖的过程,都是需要联网的。【如果网络不太好,可能会造成依赖下载不完整报错,继续再次执行 命令安装。】
6.3.2 项目结构
我们可以使用 VsCode 直接打开这个 Vue 项目。

这是我们创建的第一个项目结构,接下来呢,我们来介绍一下这个项目的结构。如图所示:

在上述的目录中,我们以后操作的最多的目录,就是 src 目录,因为我们需要在这个目录下来编写前端代码。
6.3.3 启动项目
- 方式一:命令行
启动项目,我们可以在命令行中执行命令:npm run dev,就可以启动 Vue 项目了。

- 方式二:Vscode 图形化界面
点击 NPM 脚本中的 dev 后的运行按钮,就可以启动项目。

6.3.4 VSCode 常见问题
先补充一下:
- 在项目路径下打开终端:(后面
npm install的时候需要用到)

- 打开终端窗口(有时候终端挡着空间,关掉后想再打开):

- 在项目路径下打开终端:(后面
- 问题一:没有显示
NPM脚本:
如果说 VSCode 没有如下 NPM脚本 的显示:

那么做如下操作:

- 问题二:
VSCode没有powershell权限:
首先我们可以改变默认终端为
cmd:- 方法一如下图:

- 方法二:
- 按
Ctrl + Shift + P - 输入并选择
Terminal: Select Default Profile - 在弹出的列表里 选
Command Prompt
- 按
- 方法二:改变
powershell执行策略 :
当我们点击 NPM 脚本中的 dev 后的运行按钮,如果说它默认给我们打开的是 powershell
而不是 cmd 窗口的话,那么 Windows PowerShell 默认执行策略为 Restricted,拒绝任何 .ps1 脚本运行。
就会遇到如下报错:

- 那么我们只需要把策略放宽即可:在电脑上使用 管理员权限打开
powershell:

- 执行命令(仅影响当前用户,安全级别适中)
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser- 出现提示时输入
Y回车确认。
- 验证是否成功
Get-ExecutionPolicy- 应返回
RemoteSigned。
| 策略值 | 含义 | 安全性 |
|---|---|---|
Restricted | 默认,禁止所有脚本 | 高 |
RemoteSigned | 本地脚本可直接运行,远程脚本需签名 | 中 |
Bypass | 完全放行,无提示 | 低 |
- 所以其实刚刚我们一连串的操作,是把策略
Restricted改成了RemoteSigned。
这样就可以正常执行了:

启动起来之后,我们就可以访问前端 Vue 项目了,访问路径:http://localhost:5173

6.4 项目开发流程
如下图:

其中*.vue 是 Vue 项目中的组件文件,在 Vue 项目中也称为单文件组件(SFC,Single-File Components)。
Vue 的单文件组件会将一个组件的 逻辑 (JS),模板 (HTML) 和样式 (CSS) 封装在同一个文件里(*.vue)

6.5 API 风格
Vue 的组件有两种不同的风格:
组合式 API和选项式 API组合式API:是Vue3提供的一种基于函数的组件编写方式,通过使用函数来组织和复用组件的逻辑。它提供了一种更灵活、更可组合的方式来编写组件。代码形式如下:
<script setup>
import { ref, onMounted } from 'vue';
const count = ref(0); //声明响应式变量
function increment(){ //声明函数
count.value++;
}
onMounted(() => { //声明钩子函数
console.log('Vue Mounted....');
})
</script>
<template>
<input type="button" @click="increment"> Api Demo1 Count : {{ count }}
</template>
<style scoped>
</style>📌 说明:
setup:是一个标识,告诉 Vue 需要进行一些处理,让我们可以更简洁的使用组合式 API。ref():接收一个内部值,返回一个响应式的ref对象,此对象只有一个指向内部值的属性value。onMounted():在组合式 API 中的钩子方法,注册一个回调函数,在组件挂载完成后执行。
- 官方说明:

- 而且补充一句:在工程化之前,可以发现,我们的风格写的就是
选项式 API
选项式 API
可以用包含多个选项的对象来描述组件的逻辑,如:
data,methods,mounted等。选项定义的属性都会暴露在函数内部的
this上,它会指向当前的组件实例。
<script>
export default{
data() {
return {
count: 0
}
},
methods: {
increment: function(){
this.count++
}
},
mounted() {
console.log('vue mounted.....');
}
}
</script>
<template>
<input type="button" @click="increment">Api Demo1 Count : {{ count }}
</template>
<style scoped>
</style>📌 在 Vue 中的组合式 API 使用时,是没有 this 对象的,this 对象是 undefined。
6.6 案例
在 Vue 项目中,基于组合式 API 完成用户列表数据渲染。
要求:在页面加载完毕之后,发送异步请求,加载数据,渲染表格。
最终实现效果:

- 代码实现:
在 src 下定义 views 目录,在 views 目录中定义文件 UserList.vue,
然后,我们就可以将之前(第一到五章)实现的 Vue 案例中的代码,基于 Vue 工程化 的形式来实现一遍。
之前实现的这个页面,如下代码块提供:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue3-案例1</title>
<style>
table,th,td {
border: 1px solid #000;
border-collapse: collapse;
line-height: 50px;
text-align: center;
}
#center,table {
width: 60%;
margin: auto;
}
#center {
margin-bottom: 20px;
}
img {
width: 50px;
}
input,select {
width: 17%;
padding: 10px;
margin-right: 30px;
border: 1px solid #ccc;
border-radius: 4px;
}
.btn {
background-color: #ccc;
}
</style>
</head>
<body>
<div id="app">
<div id="center">
姓名: <input type="text" name="name" v-model="name">
性别:
<select name="gender" v-model="gender">
<option value="1">男</option>
<option value="2">女</option>
</select>
职位:
<select name="job" v-model="job">
<option value="1">班主任</option>
<option value="2">讲师</option>
<option value="3">其他</option>
</select>
<input class="btn" type="button" value="查询" @click="search">
</div>
<table>
<tr>
<th>序号</th>
<th>姓名</th>
<th>头像</th>
<th>性别</th>
<th>职位</th>
<th>入职时间</th>
<th>更新时间</th>
</tr>
<!-- v-for 用于列表循环渲染元素 -->
<tr v-for="(user, index) in userList" :key="user.id">
<td>{{index + 1}}</td>
<td>{{user.name}}</td>
<td> <img :src="user.image"> </td>
<td>
<span v-if="user.gender == 1">男</span>
<span v-else-if="user.gender == 2">女</span>
<span v-else>其他</span>
</td>
<td>
<span v-show="user.job == 1">班主任</span>
<span v-show="user.job == 2">讲师</span>
<span v-show="user.job != 1 && user.job != 2">其他</span>
</td>
<td>{{user.entrydate}}</td>
<td>{{user.updatetime}}</td>
</tr>
</table>
</div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
data() {
return {
name:'',
gender:'',
job:'',
userList: []
}
},
methods: {
search(){
//基于axios发送异步请求加载数据
axios.get(`https://web-server.itheima.net/emps/list?name=${this.name}&gender=${this.gender}&job=${this.job}`).then((result) => {
console.log(result);
this.userList = result.data.data;
})
}
},
//定义一个钩子函数 mounted
mounted() {
//页面加载完毕之后, 自动调用search
this.search();
}
}).mount("#app");
</script>
</body>
</html>把原来定义的
CSS样式代码,拷贝到<style></style>标签中。把页面的
<div id="app"></div>中的页面展示的 html 标签代码,拷贝到
<template></template>标签中。将页面的
JS代码,按照组合式 API 的形式,在<script></script>中定义出来。- 其中项目中用到
axios, 需要安装axios的依赖npm install axios
- 其中项目中用到
而由于在这个案例中,需要用到
axios来发送异步请求,所以还需要安装axios。
- 修改后代码如下:
- 即
views/UserList.vue代码如下:
<script setup>
//引入ref
import { ref, onMounted } from 'vue';
import axios from 'axios';
//声明响应式数据 userList, name, gender, job
const userList = ref([]);
const name = ref('');
const gender = ref('');
const job = ref('');
//声明函数,基于axios查询数据
const search = async () => {
//基于axios发送异步请求加载数据
const result = await axios.get(`https://web-server.itheima.net/emps/list?name=${name.value}&gender=${gender.value}&job=${job.value}`);
//将查询到的数据赋值给userList
userList.value = res.data.data;
}
//定义钩子函数 onMounted
onMounted(() => {
//调用search函数
search();
})
</script>
<template>
<div id="center">
姓名: <input type="text" name="name" v-model="name">
性别:
<select name="gender" v-model="gender">
<option value="1">男</option>
<option value="2">女</option>
</select>
职位:
<select name="job" v-model="job">
<option value="1">班主任</option>
<option value="2">讲师</option>
<option value="3">其他</option>
</select>
<input class="btn" type="button" value="查询" @click="search">
</div>
<table>
<tr>
<th>序号</th>
<th>姓名</th>
<th>头像</th>
<th>性别</th>
<th>职位</th>
<th>入职时间</th>
<th>更新时间</th>
</tr>
<!-- v-for 用于列表循环渲染元素 -->
<tr v-for="(user, index) in userList" :key="user.id">
<td>{{index + 1}}</td>
<td>{{user.name}}</td>
<td> <img :src="user.image"> </td>
<td>
<span v-if="user.gender == 1">男</span>
<span v-else-if="user.gender == 2">女</span>
<span v-else>其他</span>
</td>
<td>
<span v-show="user.job == 1">班主任</span>
<span v-show="user.job == 2">讲师</span>
<span v-show="user.job != 1 && user.job != 2">其他</span>
</td>
<td>{{user.entrydate}}</td>
<td>{{user.updatetime}}</td>
</tr>
</table>
</template>
<style scoped>
table,th,td {
border: 1px solid #000;
border-collapse: collapse;
line-height: 50px;
text-align: center;
}
#center,table {
width: 60%;
margin: auto;
}
#center {
margin-bottom: 20px;
}
img {
width: 50px;
}
input,select {
width: 17%;
padding: 10px;
margin-right: 30px;
border: 1px solid #ccc;
border-radius: 4px;
}
.btn {
background-color: #ccc;
}
</style>❗ 注意:
import axios from 'axios' 这句代码不要加 {} 给 axios
然后在 App.vue 中,将 UserList.vue 引入进来。
<script setup>
import UserList from './views/UserList.vue'
</script>
<template>
<UserList></UserList>
</template>
<style scoped>
</style>最终展示形式如下:

