【前端】BOM DOM

两天更新完毕,建议关注收藏点赞
友情链接:
HTML&CSS&LESS&Bootstrap&Emmet
Axios & AJAX & Fetch
BOM DOM
待整理 js2


  • Web API 是浏览器提供的一套操作浏览器功能和页面元素的 API ( BOM 和 DOM)。官方文档点击跳转

    目录

    • BOM
    • DOM
      • 一般获取元素
      • 层级关系获取元素
      • 事件event
      • JS 特效三大系列【offset】【scroll】【client】
      • 操作元素

BOM

BOM (Browser Object Model,简称BOM) 是指浏览器对象模型,它提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。通过BOM可以操作浏览器窗口,比如弹出框、控制浏览器跳转、获取分辨率等。

DOM

核心要点:增删改查,创建,属性操作,事件操作

文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标记语言的标准编程接口。通过 DOM 提供的接口可以对页面上的各种元素进行操作(大小、位置、颜色等)。

  • DOM树
    DOM树 又称为文档树模型,把文档映射成树形结构,通过节点对象对其处理,处理的结果可以加入到当前的页面。
    在这里插入图片描述
    文档:一个页面就是一个文档,DOM中使用document表示
    节点:网页中的所有内容,在文档树中都是节点(标签、属性、文本、注释等),使用node表示
    标签节点:网页中的所有标签,通常称为元素节点,又简称为“元素”,使用element表示
    DOM把这些节点都看作对象。
  • 总结
    创建
  1. document.write
  2. innerHTML
  3. createElement
  4. appendChild
  5. insertBefore
  6. removeChild

    主要修改dom的元素属性,dom元素的内容、属性, 表单的值等
  7. 修改元素属性: src、href、title等
  8. 修改普通元素内容: innerHTML 、innerText
  9. 修改表单元素: value、type、disabled等
  10. 修改元素样式: style、className

    主要获取查询dom的元素
  11. DOM提供的API 方法: getElementById、getElementsByTagName 古老用法 不太推荐
  12. H5提供的新方法: querySelector、querySelectorAll 提倡
  13. 利用节点操作获取元素: 父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibling) 提倡
    属性操作
    主要针对于自定义属性。
  14. setAttribute:设置dom的属性值
  15. getAttribute:得到dom的属性值
  16. removeAttribute移除属性
    事件操作
    给元素注册事件, 采取 事件源.事件类型 = 事件处理程序
    在这里插入图片描述

一般获取元素

document.getElementById(id)
参数:id值,区分大小写的字符串
返回值:元素对象 或 null<body><div id="time">2019-9-9</div><script>// 因为我们文档页面从上往下加载,所以先得有标签 所以我们script写到标签的下面var timer = document.getElementById('time');console.log(timer);console.log(typeof timer);// console.dir 打印我们返回的元素对象 更好的查看里面的属性和方法console.dir(timer);</script>
</body>document.getElementsByTagName('标签名') 或者
element.getElementsByTagName('标签名') 
作用:根据标签名获取元素对象
参数:标签名
返回值:元素对象集合(伪数组,数组元素是元素对象)
注意:getElementsByTagName()获取到是动态集合,即:当页面增加了标签,这个集合中也就增加了元素。document.getElementsByClassName()是h5新增的方法,有浏览器兼容性问题<script>// 1.获取元素 获取的是 tbody 里面所有的行
var trs = document.querySelector('tbody').querySelectorAll('tr');// 2. 利用循环绑定注册事件
for (var i = 0; i < trs.length; i++) {// 3. 鼠标经过事件 onmouseovertrs[i].onmouseover = function() {// console.log(11);this.className = 'bg';}// 4. 鼠标离开事件 onmouseouttrs[i].onmouseout = function() {this.className = '';}
}
</script>
<script>// 1. 全选和取消全选做法:  让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可// 获取元素var j_cbAll = document.getElementById('j_cbAll'); var j_tbs = document.getElementById('j_tb').getElementsByTagName('input'); // 全选按钮注册事件j_cbAll.onclick = function() {// this.checked 当前复选框的选中状态console.log(this.checked);for (var i = 0; i < j_tbs.length; i++) {j_tbs[i].checked = this.checked;}}// 给所有的子复选框注册单击事件for (var i = 0; i < j_tbs.length; i++) {j_tbs[i].onclick = function() {// flag 控制全选按钮是否选中var flag = true;// 每次点击下面的复选框都要循环检查者4个小按钮是否全被选中for (var i = 0; i < j_tbs.length; i++) {if (!j_tbs[i].checked) {flag = false;break; }}// 设置全选按钮的状态j_cbAll.checked = flag;}}</script>
  • querySelector 和 querySelectorAll 是两种常用的 DOM 查询方法。
  1. querySelector只能选择第一个匹配的节点;
  2. querySelectorAll可以选择多个节点,以","分隔开,返回的是个数组;
querySelector 返回指定选择器的第一个元素对象  
里面的选择器需要加符号 .box  #nav
var firstBox = document.querySelector('.box');//获取特殊元素
document.body //body元素对象
document.documentElement //html元素对象

层级关系获取元素

在这里插入图片描述
节点就是前面DOM树中的所有节点,网页的所有内容都是节点。节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个
基本属性。
 元素节点 nodeType 为 1
 属性节点 nodeType 为 2
 文本节点 nodeType 为 3 (文本节点包含文字、空格、换行等)

//父级节点 返回最近的一个父节点或者null
node.parentNode//返回包含指定节点的子节点的集合,该集合为即时更新的集合。
//包含了所有的子节点,包括元素节点,文本节点等。
//如果只想要获得里面的元素节点,则需要专门处理。 所以我们一般不提倡使用childNodes
parentNode.childNodes//是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回
//是得到了各个浏览器的支持
parentNode.children//第一个或最后一个 找不到则返回null。同样,也是包含所有的节点。
//子节点 不仅仅是元素节点
parentNode.firstChild
parentNode.lastChild//返回第一个子元素节点,找不到则返回null。
//注意:这两个方法有兼容性问题,IE9 以上才支持。
parentNode.firstElementChild
parentNode.lastElementChild//综上
//firstChild 和 lastChild 包含其他节点,操作不方便
//firstElementChild 和lastElementChild 又有兼容性问题
//如果要第一个子元素节点,使用 parentNode.chilren[0]
//如果是最后一个子元素节点,使用 parentNode.chilren[parentNode.chilren.length - 1]//兄弟节点 返回当前元素的下一个兄弟节点,找不到则返回null。包含所有的节点。
node.nextSibling
node.previousSibling//兄弟元素节点 注意:这两个方法有兼容性问题, IE9 以上才支持。
node.nextElementSibling
node.previousElementSibling//使用自己封装的获取兄弟节点函数 同时解决兼容性
function getNextElementSibling(element) {var el = element;while (el = el.nextSibling) {if (el.nodeType === 1) {return el;}}return null;
}//下拉菜单
var nav = document.querySelector('.nav');
var lis = nav.children; // 得到4个小li
for (var i = 0; i < lis.length; i++) {lis[i].onmouseover = function() {this.children[1].style.display = 'block';}lis[i].onmouseout = function() {this.children[1].style.display = 'none';}
}

事件event

事件是可以被 JavaScript 侦测到的行为。简单理解: 触发— 响应机制

  • 事件三要素
    事件源(谁):触发事件的元素
    事件类型(什么事件): 例如 click 点击事件
    事件处理程序(做啥):事件触发后要执行的代码(函数形式),事件处理函数
  • 执行事件步骤
    获取事件源,注册事件(绑定事件),添加事件处理程序(采用函数赋值形式)
  • 常见的鼠标事件
    在这里插入图片描述
<body><button id="btn">唐伯虎</button><script>// 点击一个按钮,弹出对话框// 1. 事件是有三部分组成  事件源  事件类型  事件处理程序   我们也称为事件三要素//(1) 事件源 事件被触发的对象   谁  按钮var btn = document.getElementById('btn');//(2) 事件类型  如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下//(3) 事件处理程序  通过一个函数赋值的方式 完成btn.onclick = function() {alert('点秋香');}</script>
</body>

JS 特效三大系列【offset】【scroll】【client】

//offset
1. offsetLeft : 用于获取元素到最近的定位父盒子的左侧距离 * 计算方式: 当前元素的左边框的左侧到定位父盒子的左边框右侧 
* 如果父级盒子没有定位, 那么会接着往上找有定位的盒子 
* 如果上级元素都没有定位,那么最后距离是与body的left值 2. offsetTop : 用于获取元素到最近定位父盒子的顶部距离* 计算方式:当前元素的上边框的上侧到定位父盒子的上边框下侧 * 如果父级盒子没有定位,那么会接着往上找有定位的盒子 * 如果上级元素都没有定位,那么最后距离是与body的top值 3. offsetWidth :用于获取元素的真实宽度(除了margin以外的宽度) 4. offsetHeight : 用于获取元素的真实高度(除了margin以外的高度) 5. offsetParent :用于获取该元素中有定位的最近父级元素 * 如果当前元素的父级元素都没有进行定位,那么offsetParent为body//与  style . ( left  /  top  /  width  /  height ) 的区别:
1. offset系列的是只读属性,而通过style的方式可以读写 
2. offset系列返回的数值类型(结果四舍五入),style返回的是字符串 
3. style 可以返回没有定位的元素的left值和top值,而 offsetLeft 和 offsetTop 不可以//scroll1. scrollHeight :元素中内容的实际高度(没有边框) * 如果内容不足,就是元素的高度 2. scrollWidth : 元素中内容的实际宽度(没有边框)* 如果内容不足,就是元素的宽度 3. scrollTop : onscroll事件发生时,元素向上卷曲出去的距离 4. scrollLeft : onscroll事件发生时,元素向左卷曲出去的距离//兼容问题
* 未声明 DTD: 谷歌,火狐,IE9+支持 document.body.scrollTop/scrollLeft 
* 已经声明DTD:IE8以下支持 document.documentElement.scrollTop/scrollLeft 
* 火狐/谷歌/ie9+以上支持的 window.pageYOffest/pageXOffest
//兼容代码
function getScroll() {return {left: window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft || 0,top: window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop || 0};
}//client
1. clientWidth : 获取网页可视区域的宽度 2. clientHeight: 获取网页可视区域的高度 3. clientX : 获取鼠标事件发生时的应用客户端区域的水平坐标 4. clientY : 获取鼠标事件发生时的应用客户端区域的垂直坐标//兼容问题//由浏览器对象不同导致 * 未声明 DTD: 谷歌,火狐,IE9+支持 document.body.clientWidth/clientHeight * 已经声明DTD:IE8以下支持 document.documentElement.clientWidth/clientHeight * 火狐/谷歌/ie9+以上支持的 window.innerWidth/innerHeight
//兼容代码
function client() {if (window.innerWidth) {return {"width": window.innerWidth,"height": window.innerHeight};} else if (document.compatMode === "CSS1Compat") {return {"width": document.documentElement.clientWidth,"height": document.documentElement.clientHeight};} else {return {"width": document.body.clientWidth,"height": document.body.clientHeight};}
}//由事件参数对象的兼容性问题导致 1. 谷歌,火狐,IE9+ : 事件参数对象随着事件处理函数的参数传入 2. IE8以下: event对象必须作为window对象的一个属性(window.event)
  • 总结
网页可见区域宽: document.body.clientWidth; 
网页可见区域高: document.body.clientHeight; 网页真实元素宽: document.body.offsetWidth (包括边线的宽); 
网页真实元素高: document.body.offsetHeight (包括边线的宽); 网页正文全文宽: document.body.scrollWidth; 
网页正文全文高: document.body.scrollHeight; 网页被卷去的高: document.body.scrollTop; 
网页被卷去的左: document.body.scrollLeft; 浏览器窗口的顶部边缘与屏幕的顶部边缘之间的距离: window.screenTop; 
浏览器窗口的左边缘与屏幕的左边缘之间的距离: window.screenLeft; 屏幕分辨率的高: window.screen.height; 
屏幕分辨率的宽: window.screen.width; 屏幕可用工作区高度: window.screen.availHeight; 
屏幕可用工作区宽度: window.screen.availWidth;

操作元素

element.innerText 不解析html标签,去除空格、换行
element.innerHTML 不去除//常见属性
src,href
id,alt,title//表单元素
type,value,checked,selected,disabled//样式属性
element.style
element.className//覆盖原先类名 class是关键字不能用
//当js修改样式时为行内样式,优先级高box.style.display = 'none';//自定义属性
element.getAttribute('attr_name')
element.setAttribute('attr_name','value')
element.removeAttribute('属性');
//H5规定自定义属性data-开头做为属性名并且赋值。
H5新增 element.dataset.index 或者 element.dataset[‘index’] 
//ie11才开始支持//循环雪碧图 精灵图
var lis = document.querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
// 让索引号 乘以 44 就是每个li 的背景y坐标  index就是我们的y坐标var index = i * 44;lis[i].style.backgroundPosition = '0 -' + index + 'px';
}//仿新浪注册页面// 首先判断的事件是表单失去焦点 onblur// 如果输入正确则提示正确的信息颜色为绿色小图标变化// 如果输入不是6到16位,则提示错误信息颜色为红色 小图标变化// 因为里面变化样式较多,我们采取className修改样式// 1.获取元素var ipt = document.querySelector('.ipt');var message = document.querySelector('.message');//2. 注册事件 失去焦点ipt.onblur = function() {// 根据表单里面值的长度 ipt.value.lengthif (this.value.length < 6 || this.value.length > 16) {// console.log('错误');message.className = 'message wrong';message.innerHTML = '您输入的位数不对要求6~16位';} else {message.className = 'message right';message.innerHTML = '您输入的正确';}}
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>dataset 示例</title>
</head>
<body><button id="myButton" data-index="1">点击我</button><script>// 获取按钮元素var button = document.getElementById("myButton");// 读取 datasetconsole.log("初始索引:", button.dataset.index); // 输出: "1"// 修改 datasetbutton.dataset.index = "2";console.log("修改后的索引:", button.dataset.index); // 输出: "2"// 监听点击事件button.addEventListener("click", function() {// 自增 indexthis.dataset.index = parseInt(this.dataset.index) + 1;alert("当前索引: " + this.dataset.index);});</script>
</body>
</html>
//创建节点
document.createElement('name')
//创建由tagName指定的HTML元素。因为这些元素原先不存在,称为动态创建元素节点。//将一个节点添加到指定父节点的子节点列表末尾。
//类似于 CSS 里面的after 伪元素。
node.appendChild(child)//将一个节点添加到父节点的指定子节点前面。
//类似于 CSS 里面的 before 伪元素。
node.insertBefore(child, 指定元素)//从 DOM 中删除一个子节点,返回删除的节点。
node.removeChild(child)//返回调用该方法的节点的一个副本。 也称为克隆节点/拷贝节点
node.cloneNode()
//括号参数为空或者为false则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
//如果括号参数为 true ,则是深度拷贝,会复制节点本身以及里面所有的子节点。
  • 三种动态创建元素的区别
     document.write()
     element.innerHTML
     document.createElement()
    区别
  1. document.write 是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘
  2. innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘
  3. innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂
  4. createElement() 创建多个元素效率稍低一点点,但是结构更清晰
    总结:不同浏览器下,innerHTML 效率要比 creatElement 高

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

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

相关文章

产品需求分析-概览

产品需求分析-概览 产品需求分析(上)-理论流程 需求产生(来源) 公司内部(老板、其他部门同事)产品经理自己(策划、挖掘)外部(用户、客户、伙伴) 需求分类 功能类数据类运营类体验类设计类 需求决策 战略定位产品定位用户需求 需求分位&#xff1a;四象限定位法 重要又…

小程序事件系统 —— 32 事件系统 - 事件分类以及阻止事件冒泡

在微信小程序中&#xff0c;事件分为 冒泡事件 和 非冒泡事件 &#xff1a; 冒泡事件&#xff1a;当一个组件的事件被触发后&#xff0c;该事件会向父节点传递&#xff1b;&#xff08;如果父节点中也绑定了一个事件&#xff0c;父节点事件也会被触发&#xff0c;也就是说子组…

spring6概述

spring6 1、概述 1.1、Spring是什么&#xff1f;1.2、Spring 的狭义和广义1.3、Spring Framework特点1.4、Spring模块组成1.5、Spring6特点 1.5.1、版本要求 2.2、构建模块2.3、程序开发 2.3.1、引入依赖2.3.3、创建配置文件2.3.4、创建测试类测试2.3.5、运行测试程序 2.4、程序…

【Linux docker】关于docker启动出错的解决方法。

无论遇到什么docker启动不了的问题 就是 查看docker状态sytemctl status docker查看docker日志sudo journalctl -u docker.service查看docker三个配置文件&#xff08;可能是配置的时候格式错误&#xff09;&#xff1a;/etc/docker/daemon.json&#xff08;如果存在&#xf…

CTF网络安全题库 CTF网络安全大赛答案

此题解仅为部分题解&#xff0c;包括&#xff1a; 【RE】&#xff1a;①Reverse_Checkin ②SimplePE ③EzGame 【Web】①f12 ②ezrunner 【Crypto】①MD5 ②password ③看我回旋踢 ④摩丝 【Misc】①爆爆爆爆 ②凯撒大帝的三个秘密 ③你才是职业选手 一、 Re ① Reverse Chec…

1.1 双指针专题:移动零(easy)

一、题目链接 283. 移动零 二、题目描述 给定⼀个数组 nums &#xff0c;编写⼀个函数将所有 0 移动到数组的末尾&#xff0c;同时保持⾮零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进⾏操作。 ⽰例 1: 输⼊: nums [0,1,0,3,12] 输出: […

STM32如何精准控制步进电机?

在工业自动化、机器人控制等场合&#xff0c;步进电机以其高精度、开环控制的特性得到了广泛应用。而在嵌入式系统中&#xff0c;使用STM32进行步进电机的精确控制&#xff0c;已成为开发者的首选方案之一。 本文将从嵌入式开发者的角度&#xff0c;深入探讨如何基于STM32 MCU…

Android Studio右上角Gradle 的Task展示不全

Android Studio 版本如下&#xff1a;Android Studio lguana|2023.21, 发现Gradle 的Tasks阉割严重&#xff0c;如下图&#xff0c;只显示一个other 解决方法如下&#xff1a;**Setting>Experimental>勾选Configure all gradle tasks during Gradle Sync(this can make…

华为hcia——Datacom实验指南——三层交换和ARP的工作原理

什么是三层交换 三层交换是指连接在同一台三层交换机上&#xff0c;不同vlan用户&#xff0c;不同网段ip&#xff0c;通过vlanif接口进行数据交换。 什么是ARP协议 通过网络层的ip地址解析成数据链路层的mac地址。 说白了就是通过目标ip地址去问他对应的mac地址是多少。 A…

【互联网性能指标】QPS/TPS/PV/UV/IP/GMV/DAU/MAU/RPS

&#x1f4d5;我是廖志伟&#xff0c;一名Java开发工程师、《Java项目实战——深入理解大型互联网企业通用技术》&#xff08;基础篇&#xff09;、&#xff08;进阶篇&#xff09;、&#xff08;架构篇&#xff09;清华大学出版社签约作家、Java领域优质创作者、CSDN博客专家、…

Spring(七)AOP-代理模式

目录 代理模式 一 静态代理 一、核心作用 二、使用场景 二 动态代理 一、核心作用 二、使用场景 具体实现&#xff1a;&#xff08;初始&#xff09; 具体实现&#xff1a;&#xff08;改进&#xff09; 一、核心业务逻辑 1. 接口 MathCalculator 2. 实现类 MathCa…

AI革命编程学习:Python语法速通与高阶突破全实战(第二部分:AI辅助调试与高阶编程)

只要编程&#xff0c;代码错误和有问题肯定是难免&#xff0c;更何况还是全AI生成代码&#xff0c;是否符合我们的要求与预期&#xff0c;以及代码是否有逻辑错误&#xff0c;是否有代码错误。下面我们继续这一部分&#xff0c;代码调试、代码修复等基本过程。 一、代码调试 …

脚本学习(1)验证目录自动化生成脚本

1、脚本介绍 旨在一键创建符合IC验证规范的目录结构&#xff0c;避免手动创建目录和文件的重复劳动。 优点&#xff1a;模块级验证目录可一键创建&#xff0c;代码简单易懂&#xff0c;可复用性高。 缺点&#xff1a;子系统或系统级不适用。 2、生成的目录结构 /home/user…

【C#实现手写Ollama服务交互,实现本地模型对话】

前言 C#手写Ollama服务交互&#xff0c;实现本地模型对话 最近使用C#调用OllamaSharpe库实现Ollama本地对话&#xff0c;然后思考着能否自己实现这个功能。经过一番查找&#xff0c;和查看OllamaSharpe源码发现确实可以。其实就是开启Ollama服务后&#xff0c;发送HTTP请求&a…

【十四】Golang 接口

&#x1f4a2;欢迎来到张胤尘的开源技术站 &#x1f4a5;开源如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 接口接口定义接口初始化接口嵌套空接口存储任意类…

Spark(8)配置Hadoop集群环境-使用脚本命令实现集群文件同步

一.hadoop的运行模式 二.scp命令————基本使用 三.scp命令———拓展使用 四.rsync远程同步 五.xsync脚本集群之间的同步 一.hadoop的运行模式 hadoop一共有如下三种运行方式&#xff1a; 1. 本地运行。数据存储在linux本地&#xff0c;测试偶尔用一下。我们上一节课使用…

基于Django的协同过滤算法养老新闻推荐系统的设计与实现

基于Django的协同过滤算法养老新闻推荐系统&#xff08;可改成普通新闻推荐系统使用&#xff09; 开发工具和实现技术 Pycharm&#xff0c;Python&#xff0c;Django框架&#xff0c;mysql8&#xff0c;navicat数据库管理工具&#xff0c;vue&#xff0c;spider爬虫&#xff0…

基于STM32的逻辑分析仪

目录 制约性能因素协议命令下位机回复CMD_ID的回复CMD_METADATA命令的回复上报的采样数 设置使用开源软件PulseView设置操作1&#xff0e;设置采样数2&#xff0e;设置采样频率3.使能或禁止通道4.设置通道的触发条件 实现准备汇编指令精确测量时间 程序C语言初实现采集数据上报…

Kafka,Mq,Redis作为消息队列使用时的差异?|消息队列

在分布式系统中&#xff0c;消息队列&#xff08;Message Queue&#xff0c;MQ&#xff09;扮演着至关重要的角色&#xff0c;负责解耦系统、削峰填谷、提升系统的吞吐量。Kafka、传统的MQ&#xff08;如RabbitMQ、ActiveMQ&#xff09;和Redis在实际应用中都被广泛用作消息队列…

SpringBoot + vue 管理系统

SpringBoot vue 管理系统 文章目录 SpringBoot vue 管理系统 1、成品效果展示2、项目准备3、项目开发 3.1、部门管理 3.1.1、前端核心代码3.1.2、后端代码实现 3.2、员工管理 3.2.1、前端核心代码3.2.2、后端代码实现 3.3、班级管理 3.3.1、前端核心代码3.3.2、后端代码实现 …