提升对前端的认知,不得不了解Web API的DOM和BOM

简介: 在现代的开发中,vue和react都是很流行的开发框架,框架虽好用,但是框架的原理还是基于 DOM 操作去实现。如果一个前端工程师只会框架,不会 DOM ,那基本上是很容易被淘汰的。因为框架的存活时间我们谁也说不准,且技术更新迭代也特别快,说不定三五年就会被淘汰了都有可能。所以,扎实的学会 js 的基础原理,不要被框架和一些外部事件所迷惑,对自己会有一个更好的竞争力提升。本文将讲解 JS 中 Web API 的 DOM 和 BOM 操作。

一、DOM操作



DOM操作,即Document Object Model文档对象模型。下面通过几个知识点来分析DOM本质节点操作结构操作以及 DOM性能


1、DOM的本质


DOM的本质就是一棵 ,是树结构。

我们从早起xml说起,xml是一种可扩展性的标准语言,早期基本是用xml来对DOM进行编写。具体形式如下:

<?xml version = "1.0" encoding = "UTF-8"?>
<note>
    <to>Tony</to>
    <from>Abby</from>
    <heading>London</heading>
    <body>Have a nice day!</body>
    <other>
        <a></a>
        <b></b>
    </other>
</note>
复制代码

我们可以看到,一层一层的下来,层层递进,很像一棵树。


回到现在,我们基本上用的是 html 来进行编写。 html 也是一种特定的 xml ,只不过它是有自己的一套规范,比如说我们常见的 p 标签, span 标签, li 标签等等。引用百度来做一个解释:

1.png

大家可以看到,上面一层一层递进的关系,把整个 html  渲染出来形成我们看到的页面,这一层层递进的关系,其实就是 DOM 树,所以也可以说, DOM 是从 html 文件解析出来的一棵树。


2、DOM节点操作


DOM节点主要有两种操作,一种是property操作,另一种是attribute操作。下面让我们来看看这两种操作。


(1)property形式


html代码:

<div id="div1" class="container">
    <p>文段 1</p>
    <p>文段 2</p>
    <p>文段 3</p>
</div>
<div id="div2">
    <img src="https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717,2336175712&fm=58" alt="">
</div>
复制代码

JS代码:

/**
 * property形式
 */
 const pList = document.querySelectorAll('p');
 const p1 = pList[0];
 p1.style.width = '100px'; //修改样式
 console.log(p1.style.width); //获取样式
 p1.className = 'red'; //修改class名称
 console.log(p1.className); //获取class名称
//  获取nodeName和nodeType
 console.log(p1.nodeName);  //打印节点名称,p
 console.log(p1.nodeType); //打印节点类型,普通的DOM节点元素为1,文本类型是3
复制代码

控制台打印结果如下:

2.png

从上面的结果中可以看到,通过修改 DOMJS 变量,从而操作 DOM ,最终得到我们想要的结果。


(2)attribute形式


html代码:

<div id="div1" class="container">
    <p>文段 1</p>
    <p>文段 2</p>
    <p>文段 3</p>
</div>
<div id="div2">
    <img src="https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717,2336175712&fm=58" alt="">
</div>
复制代码

JS代码:

/**
 * attribute形式
 */
 const pList = document.querySelectorAll('p');
 const p1 = pList[0];
 p1.setAttribute('data-name', 'immoc');
 console.log(p1.getAttribute('data-name'));
 p1.setAttribute('style', 'font-size:50px;');
 console.log(p1.getAttribute('style'));
复制代码

控制台打印结果如下:

3.png

从上面的结果中可以看到,通过修改 DOM 结构的节点属性,最终得到我们想要的结果。

综上所述, porpertyattribute 这两种操作类型,主要有以下区别:

  • property:修改对象属性,不会体现到 html 结构中;
  • attribute:修改 html 属性,会改变 html 结构,即标签结构;
  • 两者都有可能引起DOM重新渲染,但 attribute 引起 DOM 重新渲染的可能性更大,因为它会改动 html 的结构。所以在实际开发中,可以选择的话尽量渲染 property 去操作 DOM


3、DOM结构操作


通过上面的了解,我们都明白了DOM是一种树结构。那既然是树结构,就应该可以对节点进行增删改的操作。

因此,DOM结构操作主要有以下三种类型:

  • 新增/插入节点;
  • 获取子元素列表,获取父元素;
  • 删除子元素。

接下来对这三种类型进行讲解。


(1)新增/插入节点


先附上html代码。

<!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>Document</title>
</head>
<body>
    <div id="div1" class="container">
        <p id = "p1">文段 1</p>
        <p>文段 2</p>
        <p>文段 3</p>
    </div>
    <div id="div2"> 
        <img src="https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717,2336175712&fm=58" alt="">
    </div>
    <script src="./你的文件路径.js"></script>
</body>
</html>
复制代码

此时执行状态如下。

4.png

如果此时要给 div1 新增一个子节点 p ,代码如下。

// 新建节点
const newP = document.createElement('p');
newP.innerHTML = 'this is newP';
// 插入节点
div1.appendChild(newP);
复制代码

此时浏览器执行状态如下。

5.png

大家可以看到, div1 成功新增了一个节点,这就是关于新增节点操作的具体流程。

那如果在此基础上,要移动一个节点呢?具体操作如下。

// 移动节点 -> 把p1移动到div2中来
const p1 = document.getElementById('p1');
div2.appendChild(p1);
复制代码

此时浏览器执行状态如下。

6.png


(2)获取子元素列表,获取父元素


在上面的基础上,我们来看看获取父元素和子元素列表的流程。

// 获取父元素
console.log(p1.parentNode);
// 获取子元素列表
const div1ChildNodes = div1.childNodes;
console.log(div1ChildNodes); //此处获取的包含p标签以及p标签下面的文本,因此需要一层过滤
const div1ChildNodesP = Array.prototype.slice.call(div1.childNodes).filter(child => {
    if(child.nodeType === 1){
        return true;
    }else{
        return false;
    }
});
console.log('div1ChildNodesP', div1ChildNodesP);
复制代码

此时浏览器执行状态如下。

7.png


(3)删除子元素


在上面操作的基础上,我们现在对 div1 中的第一个 p 节点进行移除。

// 移除元素
div1.removeChild(div1ChildNodesP[0]);
复制代码

此时浏览器执行状态如下。

8.png

以上就是对DOM结构的新增、移动、获取子父元素以及删除子元素的一个操作,相信大家对DOM结构的增删改有了一个新的了解。接下来我们讲解DOM性能。


4、DOM性能


为什么要对DOM做性能优化呢,原因在于 DOM 操作是非常“昂贵”的,每一次操作都很有可能引发浏览器的重排和重绘,因此要避免频繁的 DOM 操作。那如何做到避免频繁的 DOM 操作,给 DOM 进行性能优化呢?主要有以下两个方面给 DOM 操作进行性能优化:

  • DOM 查询做缓存
  • 将频繁操作改为一次性操作

接下来将对这两种方法进行讲解。


(1)对DOM查询做缓存


// 不缓存 DOM 查询结果
for(let i = 0; i < document.getElementsByTagName('p').length; i++){
    // 每次循环,都会计算length,频繁进行 DOM 查询
}
复制代码
// 缓存 DOM 查询结果
const pList = document.getElementsByTagName('p');
const length = pList.length;
for(let i = 0; i < length; i++){
    // 缓存length,只进行一次 DOM 查询
}
复制代码

分析以上两段代码,在第一段当中,每次循环的时候,都会计算 length ,频繁的对 DOM 进行查询,如此频繁的操作,可想对程序都不是不太好的。

因此,通过优化,我们写出第二段代码。在第二段代码中,把 length 放在外部进行缓存,等到每次循环的时候,只需要进行一次 DOM 查询,而不用像第一段一样频繁操作,极大提高了性能。


(2)将频繁操作改为一次性操作


我们来看一个例子。比如说,我们先在要通过操作 DOM 来一次性插入10个列表。

在正常情况下我们想象的操作如下:

html代码

<!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>Document</title>
</head>
<body>
    <div id="div1" class="container">
        <p id = "p1">文段 1</p>
        <p>文段 2</p>
        <p>文段 3</p>
    </div>
    <div id="div2"> 
        <img src="https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717,2336175712&fm=58" alt="">
    </div>
    <ul id="list">
    </ul>
    <script src="./你的路径.js"></script>
</body>
</html>
复制代码

js代码

const list = document.getElementById('list');
for(let i = 0; i < 10; i++){
    const li = document.createElement('li');
    li.innerHTML = `List item ${i}`;
    list.appendChild(li);
}
复制代码

此时浏览器执行状态如下。

9.png

按照预想的,呈现出了我们想要的结果,且看着好像也没什么问题。但是呢,这就违反了我们所说的一次性操作 DOM 的原则,因为它在频繁的操作 DOM ,一直在频繁操作中修改。因此,我们可以通过以下方法来对性能做一个优化:

/**
 * 频繁操作改为一次性操作
 */
const list = document.getElementById('list');
// 创建一个文档片段,此时还没有插入到 DOM 结构中
const frag = document.createDocumentFragment();
for(let i = 0; i < 15; i++){
    const li = document.createElement('li');
    li.innerHTML = `List item ${i}`;
    //先插入文档片段
    frag.appendChild(li);
}
// 都完成之后,再统一插入到 DOM 结构中
list.appendChild(frag);
复制代码

执行后,浏览器也同样效果呈现出来了。

10.png

通过代码我们可以发现,通过创建一个文档片段,来对节点进行一个缓存,等到全部节点操作都完成以后,再统一插入到 DOM 结构中。这种方法,也极大提高了程序中的性能。


5、回顾


最后,我们用几个题目来回顾DOM的知识点。


(1)DOM是哪一种数据结构?

DOM是一种的数据结构,DOM也常被称为DOM树。


(2)DOM操作的常用API

11.png


(3)attribute和property的区别

两者主要有以下区别:

  • property:修改对象属性,不会体现到 html 结构中;
  • attribute:修改 html 属性,会改变 html 结构,即标签结构;
  • 两者都有可能引起DOM重新渲染,但 attribute 引起 DOM 重新渲染的可能性更大,因为它会改动 html 的结构。所以在实际开发中,可以选择的话尽量渲染 property 去操作 DOM


(4)一次性插入多个DOM节点,需考虑性能,怎么操作?


可以通过创建一个Fragment的文档片段,来对节点进行一个缓存,等到 全部节点操作 都完成以后,再统一插入到 DOM 结构中,从频繁执行改为一次执行。


二、BOM操作



BOM,即Brouse Object Model浏览器对象模型。下面通过几个知识点来了解浏览器的BOM操作。


1、navigator


navigator 主要用到 userAgent 属性, navigator.userAgent 表示获取浏览器的用户代理字符串如以下代码操作:

//navigator
const ua = navigator.userAgent;
console.log(ua);
const isChrome = ua.indexOf('Chrome');
console.log(isChrome);
复制代码

此时浏览器打印如下。

12.png

从上图中可以看到,通过 userAgent 可以获取到当前所使用浏览器的内核信息


2、screen


screen 主要用到width和height属性,screen.width表示获取当前屏幕的宽度,sceen.height表示获取当前屏幕的高度。如以下代码操作:

// screen
console.log(screen.width); //获取屏幕宽度
console.log(screen.height); //获取屏幕高度
复制代码

此时浏览器打印如下。

13.png

从上图中可以看到,通过 screen.widthscreen.height 可以获取到当前屏幕的宽度和高度。


3、location


location 主要用到 href/protocol/pathname/search/hash 属性,具体含义如下代码所示。

// location
console.log(location.href); //获取整个网址
console.log(location.protocol); //获取网址协议
console.log(location.pathname); //获取网址域名
console.log(location.search); //获取网址中的一些参数
console.log(location.hash); //获取哈希值,即#号后面的值
复制代码

此时浏览器打印如下。

14.png

相信从上图的演示之后,大家对 location 属性的应用有了有一定的了解。


4、history


history 主要用到 backforward 属性,当网页执行history.back()代码时,会将当前网页向后退一页;当网页执行history.forward()代码时,会将当前网页向前进一页。如以下代码操作:

// history
history.back(); //对网页进行后退
history.forward(); //对网页进行前进
复制代码

此时浏览器打印如下。

15.png

从上图中可以看到,通过 history.back()history.forward() 可以让当前浏览页面进行前进或者后退操作。


三、结束语



JS 的基础知识规定了 ECMA 的语法标准,而 Web API 则是网页操作的 API ,是 W3C 的标准。如果要说两者的关系,那自然是 ES 标准是 Web API 的基础。在实际应用开发中,只有两者结合才能真正做到实际应用。所以,不管是 ES 标准还是 Web API 中的 DOMBOM 操作,在实际开发中都是至关重要的内容。


相关文章
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
48 4
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
113 3
|
19天前
|
前端开发 API 开发者
Python Web开发者必看!AJAX、Fetch API实战技巧,让前后端交互如丝般顺滑!
在Web开发中,前后端的高效交互是提升用户体验的关键。本文通过一个基于Flask框架的博客系统实战案例,详细介绍了如何使用AJAX和Fetch API实现不刷新页面查看评论的功能。从后端路由设置到前端请求处理,全面展示了这两种技术的应用技巧,帮助Python Web开发者提升项目质量和开发效率。
33 1
|
25天前
|
JSON API 数据格式
如何使用Python和Flask构建一个简单的RESTful API。Flask是一个轻量级的Web框架
本文介绍了如何使用Python和Flask构建一个简单的RESTful API。Flask是一个轻量级的Web框架,适合小型项目和微服务。文章从环境准备、创建基本Flask应用、定义资源和路由、请求和响应处理、错误处理等方面进行了详细说明,并提供了示例代码。通过这些步骤,读者可以快速上手构建自己的RESTful API。
28 2
|
30天前
|
缓存 编解码 JavaScript
DOM 和 BOM 在项目中应用时的性能优化方法
【10月更文挑战第19天】总之,优化 DOM 和 BOM 的性能需要综合考虑多个方面,通过合理的设计和技术手段,提升项目的运行效率和用户体验。在实际开发中,要不断地进行性能优化实践,以适应不断变化的需求和技术发展。
|
30天前
|
JavaScript 安全 物联网
DOM 和 BOM 在项目中的应用
【10月更文挑战第19天】在现代网页开发和应用项目中,DOM(文档对象模型)和 BOM(浏览器对象模型)扮演着至关重要的角色。它们为开发者提供了与网页文档和浏览器环境进行交互的能力,使项目能够实现丰富的功能和用户体验。
|
30天前
|
XML 编解码 JavaScript
DOM(文档对象模型)和 BOM(浏览器对象模型)
【10月更文挑战第19天】在前端开发中,理解 DOM(文档对象模型)和 BOM(浏览器对象模型)是至关重要的。它们是 Web 开发的基础,为我们提供了与网页文档和浏览器进行交互的能力。
|
1月前
|
监控 负载均衡 API
Web、RESTful API 在微服务中有哪些作用?
在微服务架构中,Web 和 RESTful API 扮演着至关重要的角色。它们帮助实现服务之间的通信、数据交换和系统的可扩展性。
48 2
|
1月前
|
前端开发 JavaScript API
惊呆了!学会AJAX与Fetch API,你的Python Web项目瞬间高大上!
在Web开发领域,AJAX与Fetch API是提升交互体验的关键技术。AJAX(Asynchronous JavaScript and XML)作为异步通信的先驱,通过XMLHttpRequest对象实现了局部页面更新,提升了应用流畅度。Fetch API则以更现代、简洁的方式处理HTTP请求,基于Promises提供了丰富的功能。当与Python Web框架(如Django、Flask)结合时,这两者能显著增强应用的响应速度和用户体验,使项目更加高效、高大上。
51 2
|
1月前
|
前端开发 API 开发者
从零到精通,AJAX与Fetch API让你的Python Web前后端交互无所不能!
从零到精通,AJAX与Fetch API让你的Python Web前后端交互无所不能!
43 3
下一篇
无影云桌面