3-VueAjax

什么是Vue

Vue是一款用于构建用户界面的渐进式的JavaScript框架。官方网站:https://cn.vuejs.org/

前端负责将数据以美观的样式呈现出来,而数据最终又要在数据库服务器中存储并管理。前端想要拿到数据,就需要请求服务器。然后服务器将数据返回给前端。Vue可以将这些原始的数据渲染展示在页面中,转换为用户可以看懂的网页,这就是构建用户界面,即基于数据渲染出用户看到的界面。

准备工作:

1.引入Vue模块

2.创建Vue程序的应用实例,控制视图的元素

3.准备元素(div),被Vue控制

数据驱动视图:

1.准备数据

2.通过插值表达式渲染页面

不需要去记忆语法,可以看官方文档或直接用AI生成。

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <h1>{{ message }}</h1> <h1>{{ count }}</h1> <!-- 插值表达式 --> </div> ​ <script type = "module"> import {createApp} from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';//引入Vue模块 createApp({//创建实例 data() { return { message: "Hello Vue!",//return要返回js的对象,对象里装的是Vue实例中的数据 count: 100 } }, }).mount('#app'); //创建的实例接管上面的app //如果把count那一行写在div的外面,那么展示的就会是{{100}}而不是100,因为Vue接管的只有div下的内容 </script> </body> </html>
Vue常用指令

指令:html标签上带有v-前缀的特殊属性

常用指令:

指令作用
v-for列表渲染,遍历容器的元素或对象的属性
v-bind为HTML标签绑定属性值,如设置href,css样式等
v-if/v-else-if/v-else条件性地渲染某元素,判定为true时渲染,否则不渲染
v-show根据条件展示某元素,区别在于切换的是display属性的值
v-model在表单元素上创建双向数据绑定
v-on为HTML标签绑定事件

依然用之前的例子。我们知道这些指令即可,代码让AI生成。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>进击!巨人中学校</title> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <style> /* 原有的CSS样式保持不变 */ .top-nav { background-color: #333; padding: 15px; overflow: hidden; } .nav-title { float: left; font-size: 24px; font-weight: bold; margin: 0; color:#f0f0f0; } .nav-logout { float: right; font-size: 16px; margin-top: 8px; } .nav-logout a { color: white; text-decoration: none; } .nav-logout a:hover { text-decoration: underline; } ​ .search-form { padding: 20px; background-color: #f8f9fa; border: 1px solid #e9ecef; margin: 20px; border-radius: 5px; } .search-form form { display: flex; align-items: center; gap: 15px; flex-wrap: wrap; } .form-group { display: flex; align-items: center; gap: 5px; } .form-group label { font-weight: 500; min-width: 60px; } .form-group input, .form-group select { padding: 8px 12px; border: 1px solid #ced4da; border-radius: 4px; font-size: 14px; } .form-group input { width: 150px; } .form-group select { width: 120px; } .form-buttons { margin-left: auto; display: flex; gap: 10px; } .btn { padding: 8px 20px; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; font-weight: 500; } .btn-primary { background-color: #007bff; color: white; } .btn-primary:hover { background-color: #0056b3; } .btn-secondary { background-color: #6c757d; color: white; } .btn-secondary:hover { background-color: #545b62; } ​ .table-container { margin: 20px; border-radius: 5px; overflow: hidden; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .data-table { width: 100%; border-collapse: collapse; background-color: white; } .data-table th, .data-table td { padding: 12px 15px; text-align: center; border-bottom: 1px solid #e9ecef; } .data-table th { background-color: #f8f9fa; font-weight: 600; color: #495057; font-size: 14px; } .data-table tbody tr { transition: background-color 0.2s; } .data-table tbody tr:hover { background-color: #f8f9fa; } .btn-edit { background-color: #28a745; color: white; padding: 5px 12px; margin-right: 5px; } .btn-edit:hover { background-color: #218838; } .btn-delete { background-color: #dc3545; color: white; padding: 5px 12px; } .btn-delete:hover { background-color: #c82333; } ​ .avatar { width: 50px; height: 50px; border-radius: 50%; object-fit: cover; } </style> </head> <body> <div id="app"> <!-- 顶部导航栏 --> <div class="top-nav"> <h1 class="nav-title">进击!巨人中学校</h1> <div class="nav-logout"> <a href="#">退出登录</a> </div> </div> ​ <!-- 搜索表单区域 --> <div class="search-form"> <form @submit.prevent="searchEmployees"> <div class="form-group"> <label for="name">姓名:</label> <input type="text" id="name" v-model="searchForm.name" placeholder="请输入姓名"> <!-- 现在不通过原生的方式操作了,所以name属性可有可无 --> </div> <div class="form-group"> <label for="gender">性别:</label> <select id="gender" v-model="searchForm.gender"> <option value="">请选择</option> <option value="男">男</option> <option value="女">女</option> </select> </div> ​ <div class="form-group"> <label for="avatar">头像:</label> <input type="image" id="avatar" v-model="searchForm.avatar" placeholder="请输入头像地址"> </div> <div class="form-group"> <label for="position">职位:</label> <select id="position" v-model="searchForm.position"> <option value="">请选择</option> <option value="班主任">班主任</option> <option value="讲师">讲师</option> <option value="学工主管">学工主管</option> <option value="教导主任">教导主任</option> <option value="校长">校长</option> <option value="后勤部长">后勤部长</option> <option value="教研主管">教研主管</option> </select> </div> <div class="form-buttons"> <button type="submit" class="btn btn-primary">查询</button> <button type="button" @click="clearSearch" class="btn btn-secondary">清空</button> <!-- @符号是v-on的简写 --> </div> </form> </div> ​ <!-- 表格展示区 --> <div class="table-container"> <table class="data-table"> <thead> <tr> <th>序号</th> <th>姓名</th> <th>性别</th> <th>头像</th> <th>职位</th> <th>入职日期</th> <th>最后操作时间</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="(emp, index) in filteredEmpList" :key="emp.id"> <td>{{ index + 1 }}</td> <td>{{ emp.name }}</td> <td>{{ emp.gender }}</td> <!-- Vue中,标签内部不能有插值表达式 --> <td><img class="avatar" v-bind:src="emp.avatar" :alt="emp.name"></td> <!-- 冒号是v-bind:的缩写形式 --> <td>{{ emp.position }}</td> <td>{{ emp.entryDate }}</td> <td>{{ emp.lastOperationTime }}</td> <td> <button class="btn btn-edit" @click="editEmployee(emp)">编辑</button> <button class="btn btn-delete" @click="deleteEmployee(emp, index)">删除</button> </td> </tr> </tbody> </table> </div> </div> ​ <script> const { createApp } = Vue; ​ ​ createApp({ data() { return { empList: [ { name: '艾伦', gender: '男', avatar:'demo-avatar/eren.jpg', position: '讲师', entryDate: '2023-01-15', lastOperationTime: '2023-12-10 14:30:25' }, { name: '三笠', gender: '女', avatar:'demo-avatar/mikasa.jpg', position: '学工主管', entryDate: '2023-02-20', lastOperationTime: '2023-12-09 09:15:42' }, { name: '阿尔敏', gender: '男', avatar:'demo-avatar/armin.jpg', position: '教研主管', entryDate: '2023-03-05', lastOperationTime: '2023-12-08 16:45:18' }, { name: '利威尔', gender: '男', avatar:'demo-avatar/levi.jpg', position: '班主任', entryDate: '2023-01-10', lastOperationTime: '2023-12-10 11:20:30' }, { name: '韩吉', gender: '女', avatar:'demo-avatar/hange.jpg', position: '教导主任', entryDate: '2023-02-15', lastOperationTime: '2023-12-07 15:50:45' }, { name:"埃尔文", gender:"男", avatar:"demo-avatar/erwin.jpg", position:"校长", entryDate:"2023-03-20", lastOperationTime:"2023-12-06 10:30:00" }, { name:"萨莎", gender:"女", avatar:"demo-avatar/sasha.jpg", position:"后勤部长", entryDate:"2023-04-01", lastOperationTime:"2023-12-05 18:00:00" } ], searchForm: { name: '', gender: '', position: '' } //数据模型,封装用户输入的查询条件 } }, computed: { filteredEmpList() { return this.empList.filter(emp => { const matchesName = !this.searchForm.name || emp.name.toLowerCase().includes(this.searchForm.name.toLowerCase()); const matchesGender = !this.searchForm.gender || emp.gender === this.searchForm.gender; const matchesPosition = !this.searchForm.position || emp.position === this.searchForm.position; return matchesName && matchesGender && matchesPosition; }); } }, methods: { searchEmployees() { // 搜索逻辑已经在computed中实现 console.log('搜索条件:', this.searchForm); }, clearSearch() { this.searchForm = { name: '', gender: '', position: '' }; }, editEmployee(employee) { alert(`编辑员工: ${employee.name}`); // 这里可以实现编辑逻辑 }, deleteEmployee(employee, index) { if (confirm(`确定要删除员工 "${employee.name}" 吗?`)) { this.empList.splice(index, 1); } } } }).mount('#app'); </script> </body> </html>

解释一下v-model:

输入->数据:用户在输入框中输入内容,searchForm对象的相应属性自动更新

数据->输入:searchForm对象值变化,输入框内容自动更新

解释一下filteredEmpList函数:

filteredEmpList() { return this.empList.filter(emp => { const matchesName = !this.searchForm.name || emp.name.toLowerCase().includes(this.searchForm.name.toLowerCase()); const matchesGender = !this.searchForm.gender || emp.gender === this.searchForm.gender; const matchesPosition = !this.searchForm.position || emp.position === this.searchForm.position; return matchesName && matchesGender && matchesPosition; }); }
this.empList.filter(emp => { // 对每个员工执行这个函数 // 如果函数返回 true,该员工会被包含在结果中 // 如果函数返回 false,该员工会被过滤掉 })

姓名过滤逻辑:

const matchesName = !this.searchForm.name || emp.name.toLowerCase().includes(this.searchForm.name.toLowerCase());

!this.searchForm.name 表示搜索框为空

逻辑或:只要前面为true,就不执行后面

emp.name.toLowerCase().includes(this.searchForm.toLowerCase()):检查员工姓名是否包含搜索关键词

也就是说,如果搜索框为空,则matchesName=true;如果搜索框不为空,检查员工姓名是否包含输入的关键词。如果包含,matchesName也为true

const matchesGender = !this.searchForm.gender || emp.gender === this.searchForm.gender; const matchesPosition = !this.searchForm.position || emp.position === this.searchForm.position;

性别过滤逻辑和职位过滤逻辑一样,如果没有选择性别或职位,就匹配所有员工。选择了,就匹配相同性别/职位的员工。

后面的AND逻辑好理解,三个都满足才会被保留。最后返回empList中经过过滤器筛选后剩下来的emp

用具体例子解释一下computed:

用户在姓名输入框里输入”艾伦“,在v-model的加持下searchForm.name变为”艾伦“,computed检测到依赖变化,执行过滤逻辑,经过过滤后只剩下艾伦,就返回艾伦这个emp,最后通过v-for输出:

<tr v-for="(emp, index) in filteredEmpList" :key="emp.id">

关于依赖:

当 Vue 首次计算 filteredEmpList 时: 1. 开始执行 filteredEmpList 函数 ; 2. 记录:this.searchForm.name 被访问了;3. 记录:this.searchForm.gender 被访问了 ;4. 记录:this.searchForm.position 被访问了 ; 5. 记录:this.empList 被访问了 ; 6. 建立依赖关系:filteredEmpList 依赖于以上所有属性

那为什么我们不把过滤函数放在methods里面呢?因为computed有缓存性,只有当依赖项变化时才重新计算。如果放在methods里,那每次渲染都要执行。

Ajax

全称:Asynchronous JavaScript And XML(异步的JavaScript和XML)

XML全称:Extensible Makrup Language(可扩展的标记语言)。本质是一种数据格式,可以用来存储复杂的数据结构

Ajax作用:

1.数据交换:通过Ajax可以给服务器发送请求,并获取服务器响应的数据

2.异步交互:可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术。例如:搜索联想(在搜索引擎中输入谷,就弹出谷歌相关的词条,这个过程中没有页面的刷新)、用户名可用性的校验(设置用户名后,在不刷新界面的同时提示用户名已被占用,实际上是在不重新加载页面的情况下已经与服务器完成了数据的交换)

同步:客户端请求服务器后,只等待

异步:客户端请求服务器后,可以执行其它操作

Axios

Axios对原生的Ajax进行了封装,官网:https://www.axios-http.cn/

步骤:

1.引入Axios的js文件(参照官网)

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

2.使用Axios发送请求,并获取响应结果

axios({ method:'GET',//请求方式 GET/POST url:'https://...' //请求路径:url //data:请求数据(POST) //params:发送请求时携带的url参数 }).then((result) => {//成功回调函数。因为是异步的,所以前端不会等待服务器返回。等到服务器返回了,再回头调用这个函数,所以叫回调。result是服务器响应回来的数据 console.log(result.data); }).catch((err) => {//失败回调函数 alert(err); });

也可以使用简单方式,axios.get和axios.post,写完url之后直接在后面写thenc再回车就可以补全两个回调函数的框架。

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <input type="button" value="获取数据GET" id="btnGet"> <input type="button" value="提交数据POST" id="btnPost"> ​ <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> //发送GET请求 document.querySelector('#btnGet').addEventListener('click', () => { axios.get('https://jsonplaceholder.typicode.com/posts/1') .then(response => { console.log(response.data); }) .catch(error => { console.error('发生错误:', error); }); }); //发送POST请求 document.querySelector('#btnPost').addEventListener('click', () => { axios.post('https://jsonplaceholder.typicode.com/posts', { title: '新标题', body: '新内容', userId: 1 }) .then(response => { console.log(response.data); }) .catch(error => { console.error('发生错误:', error); }); }); </script> </body> </html>
练习:从服务器端动态获取数据渲染展示列表

在前面的巨人中学校例子里,渲染员工列表时的七条数据都是被写死的,现在我们需要在点击查询按钮时动态地根据查询条件来查询员工信息。在此之前我们先创建一个服务URL以供练习。

1.安装Node.js

https://nodejs.org/en

检查:

node --version npm --version

2.安装JSON Server

可以使用以下任一方式安装JSON Server

CMD或PowerShell或Git Bash:

npm install -g json-server

发现安装失败,原因是json-server依赖的一个子包在npm仓库中找不到了,尝试安装一个依赖包还存在的历史版本:

npm install -g json-server@0.17.4

成功安装。

3.创建数据文件db.json(放在任意目录)

{ "empList": [ { "id": 1, "name": "艾伦", "gender": "男", "avatar": "demo-avatar/eren.jpg", "position": "讲师", "entryDate": "2023-01-15", "lastOperationTime": "2023-12-10 14:30:25" }, { "id": 2, "name": "三笠", "gender": "女", "avatar": "demo-avatar/mikasa.jpg", "position": "学工主管", "entryDate": "2023-02-20", "lastOperationTime": "2023-12-09 09:15:42" }, { "id": 3, "name": "阿尔敏", "gender": "男", "avatar": "demo-avatar/armin.jpg", "position": "教研主管", "entryDate": "2023-03-05", "lastOperationTime": "2023-12-08 16:45:18" }, { "id": 4, "name": "利威尔", "gender": "男", "avatar": "demo-avatar/levi.jpg", "position": "班主任", "entryDate": "2023-01-10", "lastOperationTime": "2023-12-10 11:20:30" }, { "id": 5, "name": "韩吉", "gender": "女", "avatar": "demo-avatar/hange.jpg", "position": "教导主任", "entryDate": "2023-02-15", "lastOperationTime": "2023-12-07 15:50:45" }, { "id": 6, "name": "埃尔文", "gender": "男", "avatar": "demo-avatar/erwin.jpg", "position": "校长", "entryDate": "2023-03-20", "lastOperationTime": "2023-12-06 10:30:00" }, { "id": 7, "name": "萨莎", "gender": "女", "avatar": "demo-avatar/sasha.jpg", "position": "后勤部长", "entryDate": "2023-04-01", "lastOperationTime": "2023-12-05 18:00:00" } ] }

4.启动JSON Server

在包含db.json文件的目录中打开命令提示符,运行:

json-server --watch db.json --port 3000

看到Hi之后,意味着我们成功在本地运行了一个JSON Server服务器,从Loading db.json可以看到,服务器加载了我们的db.json文件,并且正在监听3000端口

在浏览器中输入http://localhost:3000/empList,即可看到员工数据。

如果我们想查询,可以在url后面加一个问号,输入查询参数即可。例如想查询名字为艾伦的员工:

http://localhost:3000/empList?name=艾伦

回车后即可筛选出艾伦的信息。

现在我们将empList中的内容清空。由于要向服务器请求数据,所以应该在searchEmployees函数做手脚。我们不再将搜索条件输出到控制台,而是发送Ajax请求获取数据

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>进击!巨人中学校</title> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <style> /* 原有的CSS样式保持不变 */ .top-nav { background-color: #333; padding: 15px; overflow: hidden; } .nav-title { float: left; font-size: 24px; font-weight: bold; margin: 0; color:#f0f0f0; } .nav-logout { float: right; font-size: 16px; margin-top: 8px; } .nav-logout a { color: white; text-decoration: none; } .nav-logout a:hover { text-decoration: underline; } ​ .search-form { padding: 20px; background-color: #f8f9fa; border: 1px solid #e9ecef; margin: 20px; border-radius: 5px; } .search-form form { display: flex; align-items: center; gap: 15px; flex-wrap: wrap; } .form-group { display: flex; align-items: center; gap: 5px; } .form-group label { font-weight: 500; min-width: 60px; } .form-group input, .form-group select { padding: 8px 12px; border: 1px solid #ced4da; border-radius: 4px; font-size: 14px; } .form-group input { width: 150px; } .form-group select { width: 120px; } .form-buttons { margin-left: auto; display: flex; gap: 10px; } .btn { padding: 8px 20px; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; font-weight: 500; } .btn-primary { background-color: #007bff; color: white; } .btn-primary:hover { background-color: #0056b3; } .btn-secondary { background-color: #6c757d; color: white; } .btn-secondary:hover { background-color: #545b62; } ​ .table-container { margin: 20px; border-radius: 5px; overflow: hidden; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .data-table { width: 100%; border-collapse: collapse; background-color: white; } .data-table th, .data-table td { padding: 12px 15px; text-align: center; border-bottom: 1px solid #e9ecef; } .data-table th { background-color: #f8f9fa; font-weight: 600; color: #495057; font-size: 14px; } .data-table tbody tr { transition: background-color 0.2s; } .data-table tbody tr:hover { background-color: #f8f9fa; } .btn-edit { background-color: #28a745; color: white; padding: 5px 12px; margin-right: 5px; } .btn-edit:hover { background-color: #218838; } .btn-delete { background-color: #dc3545; color: white; padding: 5px 12px; } .btn-delete:hover { background-color: #c82333; } ​ .avatar { width: 50px; height: 50px; border-radius: 50%; object-fit: cover; } </style> </head> <body> <div id="app"> <!-- 顶部导航栏 --> <div class="top-nav"> <h1 class="nav-title">进击!巨人中学校</h1> <div class="nav-logout"> <a href="#">退出登录</a> </div> </div> ​ <!-- 搜索表单区域 --> <div class="search-form"> <form @submit.prevent="searchEmployees"> <div class="form-group"> <label for="name">姓名:</label> <input type="text" id="name" v-model="searchForm.name" placeholder="请输入姓名"> <!-- 现在不通过原生的方式操作了,所以name属性可有可无 --> </div> <div class="form-group"> <label for="gender">性别:</label> <select id="gender" v-model="searchForm.gender"> <option value="">请选择</option> <option value="男">男</option> <option value="女">女</option> </select> </div> <div class="form-group"> <label for="position">职位:</label> <select id="position" v-model="searchForm.position"> <option value="">请选择</option> <option value="班主任">班主任</option> <option value="讲师">讲师</option> <option value="学工主管">学工主管</option> <option value="教导主任">教导主任</option> <option value="校长">校长</option> <option value="后勤部长">后勤部长</option> <option value="教研主管">教研主管</option> </select> </div> <div class="form-buttons"> <button type="submit" class="btn btn-primary">查询</button> <button type="button" @click="clearSearch" class="btn btn-secondary">清空</button> <!-- @符号是v-on的简写 --> </div> </form> </div> ​ <!-- 表格展示区 --> <div class="table-container"> <table class="data-table"> <thead> <tr> <th>序号</th> <th>姓名</th> <th>性别</th> <th>头像</th> <th>职位</th> <th>入职日期</th> <th>最后操作时间</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="(emp, index) in filteredEmpList" :key="emp.id"> <td>{{ index + 1 }}</td> <td>{{ emp.name }}</td> <td>{{ emp.gender }}</td> <!-- Vue中,标签内部不能有插值表达式 --> <td><img class="avatar" v-bind:src="emp.avatar" :alt="emp.name"></td> <!-- 冒号是v-bind:的缩写形式 --> <td>{{ emp.position }}</td> <td>{{ emp.entryDate }}</td> <td>{{ emp.lastOperationTime }}</td> <td> <button class="btn btn-edit" @click="editEmployee(emp)">编辑</button> <button class="btn btn-delete" @click="deleteEmployee(emp, index)">删除</button> </td> </tr> </tbody> </table> </div> </div> ​ <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> const { createApp } = Vue; ​ ​ createApp({ data() { return { empList: [ ​ ], searchForm: { name: '', gender: '', position: '' } //数据模型,封装用户输入的查询条件 } }, computed: { filteredEmpList() { return this.empList.filter(emp => { const matchesName = !this.searchForm.name || emp.name.toLowerCase().includes(this.searchForm.name.toLowerCase()); const matchesGender = !this.searchForm.gender || emp.gender === this.searchForm.gender; const matchesPosition = !this.searchForm.position || emp.position === this.searchForm.position; return matchesName && matchesGender && matchesPosition; }); } }, methods: { searchEmployees() { const params = {}; if (this.searchForm.name) { params.name_like = this.searchForm.name; } if (this.searchForm.gender) { params.gender = this.searchForm.gender; } if (this.searchForm.position) { params.position = this.searchForm.position; } axios.get('http://localhost:3000/empList', { params }) .then(response => { console.log('原始响应数据', response.data); ​ //如果返回的是数组,则直接赋值给empList if (Array.isArray(response.data)) { this.empList = response.data; } ​ //如果返回的是对象 else if (typeof response.data === 'object') { //如果返回的是对象,则将data.data赋值给empList this.empList = response.data.data; } ​ //其它情况 else { //将response.data赋值给empList this.empList = response.data; } ​ console.log('过滤后的员工列表:', this.empList); }) .catch(error => { console.error('获取员工数据失败:', error); if(error.response) { console.error('响应状态:', error.response.status); console.error('响应数据:', error.response.data); } }); }, ​ clearSearch() { this.searchForm = { name: '', gender: '', position: '' }; //清空后重新搜索 this.searchEmployees(); }, ​ editEmployee(emp) { alert(`编辑员工:${emp.name}`); }, deleteEmployee(emp, index) { if (confirm(`确定要删除员工:${emp.name} 吗?`)) { axios.delete(`http://localhost:3000/empList/${emp.id}`) .then(() => { this.empList.splice(index, 1); alert('员工删除成功'); }) .catch(error => { console.error('删除员工失败:', error); alert('删除员工失败,请稍后重试'); }); } } }, }).mount('#app'); </script> </body> </html>

这里的searchEmployees的防御性编程比较多,实际上只要确定了db.json究竟是数组还是对象即可。

async & await

可以通过async\await让异步变为同步操作,async用来声明一个异步方法,await用来等待异步任务执行。await关键字只在async函数内有效,await关键字取代then函数(成功回调函数),等待获取到请求成功的结果值。

methods: { async searchEmployees() { let res = await axios.get('http://localhost:3000/empList'); this.empList = res.data; },
Vue生命周期

现在还有一个问题:当页面一刷新,页面又变回了不展示任何数据的状态。因为只有点击查询按钮时才查询数据。我们希望在页面加载完毕后马上发送请求查询服务器。

Vue的生命周期可以分为八个阶段,每触发一个生命周期事件,会自动执行一个生命周期方法(钩子方法)

beforeCreate:创建前

created:创建后

beforeMount:载入前

mounted:挂载完成

beforeUpdate:数据更新前

updated:数据更新后

beforeUnmount:组件销毁前

unmounted:组件销毁后

methods: { async searchEmployees() { let res = await axios.get('http://localhost:3000/empList'); this.empList = res.data; }, ​ clearSearch() { this.searchForm = { name: '', gender: '', position: '' }; //清空后重新搜索 this.searchEmployees(); }, ​ editEmployee(emp) { alert(`编辑员工:${emp.name}`); }, deleteEmployee(emp, index) { if (confirm(`确定要删除员工:${emp.name} 吗?`)) { axios.delete(`http://localhost:3000/empList/${emp.id}`) .then(() => { this.empList.splice(index, 1); alert('员工删除成功'); }) .catch(error => { console.error('删除员工失败:', error); alert('删除员工失败,请稍后重试'); }); } } }, //钩子函数 mounted() { //页面加载完成后,发送ajax请求,获取员工列表数据 this.searchEmployees(); } }).mount('#app');

钩子函数要与methods平齐

我们捋一下逻辑:

一上来,Vue接管页面,执行钩子函数mounted.钩子函数里执行searchEmployees.searchEmployees利用Ajax服务器拿到数据,交给前端能处理的empList. 然后computed检测到依赖发生变化,更新filteredEmpList,最后在v-for那里将每个emp对象的属性渲染出来。

如果用户进行查找,相当于改变searchForm的属性(因为v-model将input的内容与searchForm绑定了),一改变computed就能检测到,就调用filterEmpList进行筛选。这样就比点击查询按钮的实时性更高。

对于Vue,一个很直观的感受是:它让代码变得更加灵活了,很多原来写死的东西变得模板化了,这样不仅利于解耦合,也方便维护。

需要注意的是,这个查询按钮实际上是可有可无的,因为我们并没有实现真正的查询,而是利用computed属性进行前端过滤。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1201706.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于springBoot的动漫分享系统的设计与实现

背景与意义随着互联网技术的快速发展&#xff0c;动漫文化在全球范围内的影响力不断扩大。动漫爱好者群体日益壮大&#xff0c;对动漫资源的分享、讨论和收藏需求显著增加。传统的动漫分享方式如论坛、贴吧等存在信息分散、互动性不足、资源管理混乱等问题。基于SpringBoot的动…

天然蛋白与重组蛋白的技术区别与实验应用全解析:科研试剂视角下的最佳指南

天然蛋白通常指直接从原代生物组织、细胞裂解液或生物体分泌体系中分离得到的蛋白质。这类蛋白在自然状态下完成了基因调控、翻译后修饰(如磷酸化、糖基化等),具备本源的构象和修饰状态。 重组蛋白是通过基因克隆技…

2026年还在靠“开机等单”跑网约车?学会这几条,超越同城80%的司机!

亲爱的司机师傅&#xff0c;如果你还在靠“开机等单”跑网约车&#xff0c;那今天的文章&#xff0c;请你一定看完。跑车早已不是拼体力、拼时间的年代。真正能站稳脚跟、赚得盆满钵满的&#xff0c;都是懂得借平台之力、摸透接单逻辑的“智慧型司机”。从现在起&#xff0c;改…

导师严选2026 AI论文平台TOP8:MBA开题报告全测评

导师严选2026 AI论文平台TOP8&#xff1a;MBA开题报告全测评 2026年MBA论文写作工具测评&#xff1a;为何需要一份权威榜单&#xff1f; MBA学生在撰写开题报告与论文过程中&#xff0c;常面临选题思路不清晰、文献资料查找困难、格式规范不熟悉等挑战。随着AI技术的不断进步&a…

基于springBoot的高校学生绩点管理系统的设计与实现

高校学生绩点管理系统的背景高校学生绩点管理系统是教育信息化的重要组成部分。随着高校招生规模的扩大&#xff0c;传统手工记录和计算学生成绩的方式已无法满足现代教育管理的需求。学生成绩数据量大、计算复杂&#xff0c;人工操作易出错且效率低下。高校亟需一套自动化、智…

5年测试被裁,去面试差点被问哭了······

我的个人背景非常简单&#xff0c;也可以说丝毫没有亮点。 学历普通&#xff0c;计算机专业二本毕业&#xff0c;毕业后出来就一直在一家小公司&#xff0c;岁月如梭细&#xff0c;算了下至今从事软件测试已经5年了&#xff0c;也点点点了五年&#xff0c;每天都是重复的工作&…

基于springBoot的高校毕业生公职资讯系统的设计与实现

高校毕业生公职资讯系统的背景 高校毕业生数量逐年增加&#xff0c;就业压力不断增大。公职岗位因其稳定性、福利保障等特点&#xff0c;成为许多毕业生的首选就业方向。然而&#xff0c;公职考试信息分散、更新不及时、缺乏针对性指导等问题&#xff0c;导致毕业生在获取有效…

金三银四,我不允许你们不知道这些软件测试面试题

01、您所熟悉的测试用例设计方法都有哪些&#xff1f;请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。 答&#xff1a;有黑盒和白盒两种测试种类&#xff0c;黑盒有等价类划分法&#xff0c;边界分析法&#xff0c;因果图法和错误猜测法。白盒有逻辑覆盖法&…

基于SpringBoot的高校餐饮档口管理系统的设计与实现

高校餐饮档口管理系统的背景 高校餐饮档口管理系统是针对校园食堂档口运营中存在的效率低下、管理混乱等问题设计的数字化解决方案。传统高校食堂多采用人工记录订单、现金结算等方式&#xff0c;导致高峰期排队拥堵、订单易出错、数据统计滞后。同时&#xff0c;档口经营者缺…

看似平平无奇的00后,居然一跃上岸字节,表示真的卷不过......

又到了一年一度的求职旺季金&#xff01;三&#xff01;银&#xff01;四&#xff01;在找工作的时候都必须要经历面试这个环节。在这里我想分享一下自己上岸字节的面试经验&#xff0c;过程还挺曲折的&#xff0c;但是还好成功上岸了。大家可以参考一下&#xff01; 0821测评 …

基于springBoot的高校大学生党建系统设计与实现

高校大学生党建系统的背景 高校大学生党建系统是信息化时代背景下&#xff0c;高校党建工作与数字化技术深度融合的产物。随着高校学生党员数量逐年增加&#xff0c;传统党建管理模式面临效率低、数据分散、流程繁琐等问题。该系统通过整合党员发展、教育、管理、服务等功能模…

深入解析反广告拦截器的技术实现原理

在浏览网页时&#xff0c;浏览器与服务器之间进行着频繁的数据交换。用户希望快速获取内容&#xff0c;而发布者往往需要通过展示商业信息来维持运营。当浏览器扩展程序介入并试图隐藏这些商业信息时&#xff0c;网页端的脚本便会启动一种检测机制。这种反广告拦截技术并非不可…

Dify:一站式AI应用开发平台全解析与Windows部署指南

从零到一&#xff0c;掌握这款让AI应用开发效率提升10倍的开源神器 &#x1f3af; 什么是Dify&#xff1f; Dify&#xff08;发音为 /ˈdɪfaɪ/&#xff09;是一个开源的生成式AI应用开发平台&#xff0c;旨在帮助开发者和企业快速构建并部署生产级的AI应用。它的核心是融合了…

AI论文生成神器盘点:7款支持文献综述一键生成+真实文献交叉引用的AI工具,高效搞定论文写作!

大学生、研究生及科研人员常遇论文选题、文献整理、降重等痛点,本文通过步骤式教学+工具测评,解析7款主流AI论文工具优劣势。对比显示,图灵论文AI写作助手支持从选题到引用全流程自动,30分钟出初稿、真实文献交叉引…

物联网+云平台:智慧园区变电所的智能化运维革命

1、概述 变电所运维云平台可以看做是电力监控系统的网络应用延伸&#xff0c;变电所运维云平台通过互联网&#xff0c;电力运维人员通过手机可以随时随地了解园区配电系统的运行情况&#xff0c;做到无人值守或者少人值守&#xff0c;同时可以监测用能状况、漏电、线缆异常发热…

远程控制+智能调度,打造园区电力“无人值守”新模式

1、概述 电力监控系统实现对园区变电站、配电房内断路器、变压器、柴油发电机以及其它重要设备进行监视、测量、记录、报警等功能&#xff0c;并与保护设备和远方控制中心及其他设备通信&#xff0c;实时掌握园区变电站和配电房运行状况&#xff0c;快速排除故障&#xff0c;保…

2026 护师考试选对刷题软件,效率提升 200%(附最新排行榜)

随着 2026 年护师考试(初级 / 中级)报考热度攀升,全国报考人数突破 86 万,中级护师通过率仅 28%,考试难度持续升级 —— 知识点覆盖更广泛、病例分析题占比超 60%、新增多学科交叉考点,传统备考模式早已难以应对…

Java基于SSM+JSP的智慧农业无人机租赁系统的设计与实现

项目说明 农业无人机是一种在现代农业中广泛应用的无人机设备&#xff0c;它可以通过遥控或自主飞行&#xff0c;进行一系列农业作业。这些无人机通常配备有高分辨率相机和绘图设备&#xff0c;可以用于进行土地勘测、作物评估、农药喷洒等农业活动。无人机在农业领域的应用&am…

HTML5 与 H5:别再让你的专业度在称呼中流失

在中国互联网行业的日常交流中&#xff0c;很少有一个词像“H5”这样被高频使用&#xff0c;却又被如此普遍地误解。当你听到有人说“做一个 H5”时&#xff0c;对方指的往往是一个在微信朋友圈传播的精美幻灯片&#xff0c;或者是一个简单的营销小游戏。然而&#xff0c;当你打…

ARC121E Directed Tree

题目传送门:ARC121E Directed Tree。 首先,如果 \(i\) 满足条件,那么 \(a_i\) 不为 \(i\) 的祖先(注意 \(a_i=i\) 满足条件),设 \(g_i\) 表示钦定 \(i\) 个位置不满足的方案数。 考虑 dp,设 \(f_{i,j}\) 表示以…