js理解---webApi相关

BOM
DOM
事件
注册事件
事件冒泡
事件代理
Ajax
状态码
跨域
存储

BOM

BOM(浏览器对象模型)是浏览器本身的一些信息的设置和获取,例如获取浏览器的宽度、高度,设置让浏览器跳转到哪个地址。

  • window - - - 表示浏览器中打开的窗口
  • navigator - - - 包含有关浏览器的信息
  • screen - - - 包含有关客户端显示屏幕的信息
  • location - - - 包含有关当前 URL 的信息
  • history - - - 包含用户(在浏览器窗口中)访问过的 URL

这些对象就是一堆非常简单粗暴的 API,可以去 MDN 或者 w3school 这种网站一查就都明白了,下面列举一下常用功能的代码示例

获取浏览器特性(即俗称的UA)然后识别客户端,例如判断是不是 Chrome 浏览器

var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)

获取屏幕的宽度和高度

console.log(screen.width)
console.log(screen.height)

获取网址、协议、path、参数、hash 等

// 例如当前网址是 https://juejin.im/timeline/frontend?a=10&b=10#some
console.log(location.href)  // https://juejin.im/timeline/frontend?a=10&b=10#some
console.log(location.protocol) // https:
console.log(location.pathname) // /timeline/frontend
console.log(location.search) // ?a=10&b=10
console.log(location.hash) // #some

另外,还有调用浏览器的前进、后退功能等

history.back()
history.forward()

DOM

DOM 先从 HTML 讲起,讲 HTML 先从 XML 讲起。XML 是一种可扩展的标记语言,所谓可扩展就是它可以描述任何结构化的数据,它是一棵树!

HTML 是一个有既定标签标准XML 格式,标签的名字、层级关系和属性,都被标准化(否则浏览器无法解析)。同样,它也是一棵树。

我们开发完的 HTML 代码会保存到一个文档中(一般以.html或者.htm结尾),文档放在服务器上,浏览器请求服务器,这个文档被返回。因此,最终浏览器拿到的是一个文档而已,文档的内容就是 HTML 格式的代码。

但是浏览器要把这个文档中的 HTML 按照标准渲染成一个页面,此时浏览器就需要将这堆代码处理成自己能理解的东西,也得处理成 JS 能理解的东西,因为还得允许 JS 修改页面内容呢。

基于以上需求,浏览器就需要把 HTML 转变成 DOMHTML 是一棵树,DOM 也是一棵树。对 DOM 的理解,可以暂时先抛开浏览器的内部因素,先从 JS 着手,即可以认为 DOM 就是 JS 能识别的 HTML 结构,一个普通的 JS 对象或者数组。

DOM 树操作

新增节点

var div1 = document.getElementById('div1')

// 添加新节点
var p1 = document.createElement('p')
p1.innerHTML = 'this is p1'
div1.appendChild(p1) // 添加新创建的元素

// 移动已有节点。注意,这里是“移动”,并不是拷贝
var p2 = document.getElementById('p2')
div1.appendChild(p2)

获取父元素

var div1 = document.getElementById('div1')
var parent = div1.parentElement

获取子元素

var div1 = document.getElementById('div1')
var child = div1.childNodes

删除节点

var div1 = document.getElementById('div1')
var child = div1.childNodes
div1.removeChild(child[0])

事件

注册事件

通常我们使用 addEventListener 注册事件,该函数的第三个参数可以是布尔值,也可以是对象。对于布尔值 useCapture 参数来说,该参数默认值为 falseuseCapture 决定了注册的事件是捕获事件(true)还是冒泡事件(false)

普通的事件绑定写法如下:

var btn = document.getElementById('btn1')
btn.addEventListener('click', function (event) {
    // event.preventDefault() // 阻止默认行为
    // event.stopPropagation() // 阻止冒泡
    console.log('clicked')
})

对应的解绑事件:

btn.removeEventListener("click", myFunction);

兼容ie低版本的可以用:

  x.attachEvent("onclick", myFunction); //绑定事件
  x.detachEvent("onclick", myFunction); //移除事件

事件冒泡

首先我们要先了解事件触发三阶段:

  • 捕获 - - - window 往事件触发处传播,遇到注册的捕获事件会触发
  • 目标 - - - 传播到事件触发处时触发注册的事件
  • 冒泡 - - - 从事件触发处往 window 传播,遇到注册的冒泡事件会触发

当一个HTML中存在多层级的元素嵌套,并且每级都存在事件的绑定,事件的触发会跟我们想的有所不同:

<div id="div1" onclick="console.log(1)">
  外层
  <div id="div2" onclick="console.log(2)">
    中间层
    <p id="div3" onclick="console.log(3)">内层</p>
  </div>
</div>

如果对上面的每个id元素都加上绑定事件,但我只想单独触发p标签的事件,然而结果是:3,2,1,我们可以使用e.stopPropagation() 来阻止事件向上冒泡。

<div id="div1" onclick="console.log(1)">
 外层
 <div id="div2" onclick="console.log(2)">
   中间层
   <p id="div3" onclick="console.log(3)">内层</p>
 </div>
</div>
<script>
 var _p = document.getElementById('div3');
 _p.addEventListener('click',function(e){
   e.stopPropagation();  //最终打印的结果只有3
 })
</script>

事件代理

如果一个节点中的子节点是动态生成的,那么子节点需要注册事件的话应该注册在父节点上

<ul id="ul">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
</ul>
<script>
  let ul = document.querySelector('#ul')
  ul.addEventListener('click', (event) => {
    if(event.target.nodeName == 'LI'){
      console.log(event.target.innerHTML); // 1,2,3,4,5
    }
  })
</script>

事件代理的方式相较于直接给目标注册事件来说,有以下优点:

  • 节省内存
  • 不需要给子节点注销事件

Ajax

XMLHttpRequest

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
    // 这里的函数异步执行,可参考之前 JS 基础中的异步模块
    if (xhr.readyState == 4) {
        if (xhr.status == 200) {
            alert(xhr.responseText)
        }
    }
}
xhr.open("GET", "/api", false)
xhr.send(null)

状态码说明

上述代码中,有两处状态码需要说明。xhr.readyState是浏览器判断请求过程中各个阶段的,xhr.status是 HTTP 协议中规定的不同结果的返回状态说明。
xhr.readyState的状态码说明:

  • 0 -代理被创建,但尚未调用 open() 方法。
  • 1 -open() 方法已经被调用。
  • 2 -send() 方法已经被调用,并且头部和状态已经可获得。
  • 3 -下载中, responseText 属性已经包含部分数据。
  • 4 -下载操作已完成

HTTP 协议中,response 的状态码,常见的有哪些?

xhr.status即 HTTP 状态码,有 2xx 3xx 4xx 5xx 这几种,比较常用的有以下几种:

  • 200 正常
  • 3xx
    • 301 永久重定向。如http://xxx.com这个 GET 请求(最后没有/),就会被301到http://xxx.com/(最后是/)
    • 302 临时重定向。临时的,不是永久的
    • 304 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
  • 404 找不到资源
  • 400 客户端请求的语法错误,服务器无法理解
  • 5xx 服务器端出错了

跨域

浏览器中有 同源策略 ,即一个域下的页面中,无法通过 Ajax 获取到其他域的接口。例如有一个接口http://m.juejin.com/course/ajaxcourserecom?cid=459,你自己的一个页面http://www.yourname.com/page1.html中的 Ajax 无法获取这个接口。这正是命中了“同源策略”。如果浏览器哪些地方忽略了同源策略,那就是浏览器的安全漏洞,需要紧急修复。

url 哪些地方不同算作跨域?

  • 协议
  • 域名
  • 端口

但是 HTML 中几个标签能逃避过同源策略—— \<script src="xxx"\>\<img src="xxxx"/\>\<link href="xxxx"\>,这三个标签的src/href可以加载其他域的资源,不受同源策略限制。

  • \<img\>可以做打点统计,因为统计方并不一定是同域的,在讲解 JS 基础知识异步的时候有过代码示例。除了能跨域之外,\<img\>几乎没有浏览器兼容问题,它是一个非常古老的标签。
  • \<script\>\<link\>可以使用 CDNCDN 基本都是其他域的链接。
  • 另外\<script\>还可以实现 JSONP,能获取其他域接口的信息。

解决跨域 - JSONP

JSONP 的原理很简单,就是利用 <script>标签没有跨域限制的漏洞。通过 <script> 标签指向一个需要访问的地址并提供一个回调函数来接收数据当需要通讯时。

首先,有一个概念你要明白,例如访问http://coding.m.juejin.com/classindex.html的时候,服务器端就一定有一个classindex.html文件吗?—— 不一定,服务器可以拿到这个请求,动态生成一个文件,然后返回。 同理,<script src="http://coding.m.juejin.com/api.js">也不一定加载一个服务器端的静态文件,服务器也可以动态生成文件并返回。OK,接下来正式开始。

<script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
<script>
    function jsonp(data) {
    	console.log(data)
	}
</script>    

JSONP 使用简单且兼容性不错,但是 == 只限于 get 请求 ==。

在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP,以下是简单实现

function jsonp(url, jsonpCallback, success) {
  let script = document.createElement('script')
  script.src = url
  script.async = true
  script.type = 'text/javascript'
  window[jsonpCallback] = function(data) {
    success && success(data)
  }
  document.body.appendChild(script)
}
jsonp('http://xxx', 'callback', function(value) {
  console.log(value)
})

服务器端设置 http header

这是需要在服务器端设置的,作为前端工程师我们不用详细掌握,但是要知道有这么个解决方案。而且,现在推崇的跨域解决方案是这一种,比 JSONP 简单许多。

response.setHeader("Access-Control-Allow-Origin", "http://m.juejin.com/");  // 第二个参数填写允许跨域的域名称,不建议直接写 "*"
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
response.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");

// 接收跨域的cookie
response.setHeader("Access-Control-Allow-Credentials", "true");

存储

涉及面试题:有几种方式可以实现存储功能,分别有什么优缺点?什么是 Service Worker?

cookie

cookie 本身不是用来做服务器端存储的,它是设计用来在服务器和客户端进行信息传递的,因此我们的每个 HTTP 请求都带着 cookie。但是 cookie 也具备浏览器端存储的能力(例如记住用户名和密码),因此就被开发者用上了

使用起来也非常简单,document.cookie = ....即可。

特点:

  • 大小- - - 只有 4KB
  • 生命周期- - - 一般由服务器生成,可以设置过期时间
  • 所有 HTTP 请求都带着,会影响获取资源的效率

localStorage 和 sessionStorage

localStoragesessionStorage,它们是专门为了浏览器端缓存而设计的

localStorage

特点:

  • 大小- - - 5M
  • 生命周期- - - 本地存储,除非被清理,否则一直存在
  • 不会带到 HTTP 请求中

sessionStorage

特点:

  • 大小- - - 5M
  • 生命周期- - - 页面关闭就清理
  • 不会带到 HTTP 请求中

localStorage适用于不怎么改变的数据存储,sessionStorage用于临时的存储,关闭浏览器后下次还是需要重新存储的。

Service Worker

Service Worker 是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 Service Worker的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。

Service Worker 实现缓存功能一般分为三个步骤:首先需要先注册 Service Worker,然后监听到 install 事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。以下是这个步骤的实现:

if (navigator.serviceWorker) {
  navigator.serviceWorker
    .register('sw.js')
    .then(function(registration) {
      console.log('service worker 注册成功')
    })
    .catch(function(err) {
      console.log('servcie worker 注册失败')
    })
}
// sw.js
// 监听 `install` 事件,回调中缓存所需文件
self.addEventListener('install', e => {
  e.waitUntil(
    caches.open('my-cache').then(function(cache) {
      return cache.addAll(['./index.html', './index.js'])
    })
  )
})

// 拦截所有请求事件
// 如果缓存中已经有请求的数据就直接用缓存,否则去请求数据
self.addEventListener('fetch', e => {
  e.respondWith(
    caches.match(e.request).then(function(response) {
      if (response) {
        return response
      }
      console.log('fetch source')
    })
  )
})

打开页面,可以在开发者工具中的 Application 看到 Service Worker 已经启动了
在这里插入图片描述

在 Cache 中也可以发现我们所需的文件已被缓存
在这里插入图片描述

当我们重新刷新页面可以发现我们缓存的数据是从 Service Worker 中读取的


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值