常用浏览器API整理

1,250 阅读12分钟

面向对象编程,目前还是主流,个人也比较喜欢细化组件、切割对象.这样看起来比较清晰、每个对象职责单一,不会混淆造成混乱.

前端经常会和浏览器打交道,在处理一些与浏览器相关的逻辑时,就会调用浏览器API,整理日常会用到的API对象.

URL

构造、解析、规范化和编码URL.

创建URL对象

构造参数说明

  • url 如果是绝对URL地址,则忽略第二个参数;如果是相对路径,则以base作为基准URL.
  • base 基准URL,如果第一个参数url是绝对URL时,则不生效.
// 指定绝对ULR地址
const url = new URL('http://www.baidu.com')
// 或者
const url = new URL('','http://www.baidu.com')

// 存在路径时
const url = new URL('http://www.baidu.com/hello')
// 或
const url = new URL('/hello','http://www.baidu.com')

构造完的url实例对象有哪些属性呢 , 可以从图中获取该url地址中所有的数据信息:

origin\searchParams 为只读属性 , 其他的属性则可以通过变量赋值进行设置.

image-20210821141202812.png

修改相关属性:

// 修改协议
url.protocol = "ftp:"         // href: 'ftp://www.baidu.com/hello',
// 修改路径
url.pathname = '/admin'       // href: 'ftp://www.baidu.com/admin',
// 追加hash
url.hash = 'app'            	// href: 'ftp://www.baidu.com/admin#app',

实例方法

  • toString() 返回整个URL地址
  • toJSON() , 返回整个URL地址,同url.href

以为有啥不一样的地方,发现都是返现url地址.

还有静态方法,创建一个唯一的资源地址链接:

  • createObjectURL() File、Blog或MediaSource 对象,返回唯一的blog链接.
  • revokeObjectURL() 销毁之前createObjectURL的URL实例对象.
// File 对象,或者blob数据
const dataUrl = URL.createObjectURL('blob:**')

// 销毁创建的实例 , 访问失效
URL.revokeObjectURL(dataUrl)

URLSearchParams 对象

这个应该是我们经常会用到的,用于处理url的查询字符串.

在实例url中,存在searchsearchParams(只读)

// url实例追加一个查询条件
url.search = 'id=45'
// 重新读取时会包含 ? 注意
console.log(url.search)       // ?id=45

// 获取实例URL的查询参数对象
const searchParams = url.searchParams
创建实例
  • 可选的参数 传入会被解析,比如?id=45 \ id=45 会忽略开头的 ?
const searchParams = new URLSearchParams()
// ?id=45
const searchParams = new URLSearchParams("?id=45")    // { 'id' => '45' }
// id=45&name=admin
const searchParams = new URLSearchParams("id=45&name=321")     // { 'id' => '45', 'name' => '321' }
// [['id',45],['name','admin']]
const searchParams = new URLSearchParams([['id',45],['name',"admin"]])
// {'id':45,'name':'admin'}
const searchParams = new URLSearchParams({'id':45,'name':'admin'})

实例化的好处在于可以方便管理,比如添加、删除、查找等,通过实例方法很方便的管理数据.

实例方法
  • append(name,value) - 添加一个数据
  • delete(name) - 删除指定参数名的数据
  • entries() - 迭代遍历键/值对的对象.
  • get(name) - 获取到指定参数名的第一个值.
  • getAll(name) - 返回指定参数名的所有值,数组
  • has(name) - 判断是否存在某个参数.
  • keys() - 所有参数的键的迭代对象.
  • set(name,value) - 设置某个参数的值.
  • sort() - 按键名排序.
  • toString() - 返回查询字符串.
  • values() - 包含所有值的迭代对象.
const searchParams = new URLSearchParams()

// 追加一个参数
searchParams.append("id","sv2341")      // { 'id' => 'sv2341' }
// 判断是否包含某个参数
searchParams.has("name")           // false
// 获取指定参数的值
searchParams.get("id")              // sv2341
// 获取所有参数的值
[...searchParams.values()]         // ['sv2341']
// 
searchParams.toString();        //. 

处理URL地址参数

不需要自己再去分隔字符串处理查询参数了,通过searchParams对象优雅处理查询参数,方便多了.

// 获取实例URL的查询字符串对象
const searchParams = url.searchParams

// 追加一个name查询参数
searchParams.append('name','admin')        // href: 'ftp://www.baidu.com/admin?id=45&name=admin#app',

如果是实例化的新的URLSearchParams , 则复制给url对象属性search

const url = new URL('http://www.baidu.com/hello')
// 新实例化的对象
const searchParams = new URLSearchParams()
searchParams.append("id","sv2341")
searchParams.append("name","test")

// 设置查询参数
url.search = searchParams             // href: 'http://www.baidu.com/hello?id=sv2341&name=test',

无法处理hash与pathname的位置顺序

让人头疼的是hashpathname 的位置,hash设置总是在pathname \ search 后面

const url = new URL('http://www.baidu.com')

// 赋值hash
url.hash = '/'
// 赋值路径
url.pathname = '/app'
// 赋值查询条件
url.search = 'id=45'

结果展示为http://www.baidu.com/app?id=45#/ 怎么调试也没能改变, 我想要的结果是http://www.baidu.com/#/app?id=45

不然重新打开一个新的tab页时,会导航到初始主路由 :cry:

所以只能去重新拼接一下URL地址:

const url = new URL('http://www.baidu.com/#/app')
const searchParams = new URLSearchParams()

searchParams.append("id","sv2341")
searchParams.append("name","test")
// 打开新的tab页面
window.open(url.toString()+"?"+searchParams.toString()) // http://www.baidu.com/#/app?id=sv2341&name=test

File

处理文件相关的信息.访问文件中的数据. UTF8编码

构造实例

参数定义:

  • bits 定义的文件内容,包含ArrayBuffer\ArrayBufferView\Blob\DOMString[]等类型
  • name 文件名称,可以追加路径.
  • options 可选的配置项
    • type - 文件MIME类型 ,默认‘’
    • lastModified 文件修改的时间
const file = new File(bits,name,options)

可访问的属性,都为只读属性:

  • lastModified - 文件最后的修改时间
  • name - 文件名称
  • size - 文件大小
  • type - 文件类型

实例方法

自身没有定义方法,继承自Blob接口,可选的参数定义:

  • slice(start,end,contentType) - 返回一个新的Blob对象,原始的一段数据.
// 自定义文本内容
const file = new File(['hello world','luck for you'],'test.txt')

FileReader

上述只是定义个一个文件对象,我们要读取到文件的内容,则需要FileReader 对象读取数据.

构造实例
const fileReader = new FileReader();

可访问的属性:

  • error - 读取文件时发生的错误
  • readyState - 取值状态, 0-未加载任何数据; 1-数据正在加载; 2-已读取完成.
  • result - 读取到的文件内容.
实例方法

定义实例,则可以用来读取文件,文件的读取是一个异步过程. 那么异步就会存在异步流程,对应着不同的事件.

先看一下实例定义的方法:

  • abort() 中止读取操作
  • readAsArrayBuffer() 读取内容以ArrayBuffer格式保存数据.
  • readAsDataURL() 读取内容,返回格式为data:base64数据
  • readAsText() 读取内容,返回的内容为字符串.

可以继续之前的文件数据读取;

// 定义文件对象
const file = new File(['hello world','luck for you'],'test.txt')
// 定义文件读取的实例对象
const fileReader = new FileReader()

// 通过onload事件回调获取到文件内容
fileReader.onload = function(event){
    console.log(event.target.result)        // hello worldluck for you
}
// 读取文件为字符串
fileReader.readAsText(file)

这里是所有的事件:

  • onabort 中断读取时触发.
  • onerror 读取操作发生错误触发.
  • onload 读取完成时触发.
  • onloadstart 读取开始时触发
  • onloadend 读取结束触发,成功或者失败
  • onprogress 读取Blob对象时触发

定义文件下载

通常前端会处理一些简单的文件下载,比如文本、图片之类的. 会使用a标签进行下载处理

// 完成简单的自定义.txt文件下载
const file = new File(['hello world'],'test.txt')

// 定义读取文件对象
const fileReader = new FileReader()
// 读取文件
fileReader.onload = function(event){
    // 定义a标签,追加到body中,点击进行下载
    let a = document.createElement('a')
    a.download = 'hello.txt'
    a.href = event.target.result
    a.textContent = '下载'
    document.body.append(a)
}
// a标签下载接受的格式blob:或者data:
// 定义读取base64 格式的数据
fileReader.readAsDataURL(file)

可以读取远程的URL地址文件信息,在前端实现下载.

通常自定义实现的下载,不需要去点击,启动触发下载事件

// 通过click方法模拟点击事件,触发下载
a.click()

// 定义a标签不可见
a.style.display = 'none'

通过URL.createObjectURL定义文件下载

上述可以通过FileReader来读取文件内容,也可以通过URL静态方法创建blob:格式的URL对象,指向源内容.

// 创建文件内容ULR 
const dataUrl = URL.createObjectURL(file)
// 构建下载
let a = document.createElement('a')
a.download = 'hello.txt'
a.href = dataUrl
a.textContent = '下载'
a.style.display = 'none'
document.body.append(a)
a.click();

准备写一下文件的上传、下载;主要是断点续传、分片上传等;先立个flag吧,不知道啥时候写完. :dog:

Image

常用的展示形式-图片,不可或缺. 通常的网站并不会处理图片,拿到ULR地址,直接做展示就好.如果是一个专门处理图片的工程的话, 就会对图片各方面处理要求的多. 通常先获取到图片的大小,初始画布等.

构造实例

实例同等于html元素img, 接受参数

  • width 宽度
  • height 高度
const img = new Image()
// 等同于
img = document.createElement('img')

可访问属性:

  • alt 描述内容
  • complete 表示加载正常,没有发生错误.
  • crossOrigin 跨域设置
  • currentSrc 表示正在加载图像的URL.
  • decoding 图片的加载后的解码设置
  • height css渲染的高度
  • width css渲染的宽度
  • isMap 是否是某一图片映射的一部分
  • naturalHeight图片固有高度;不同于实际展示大小可能会受CSS影响.
  • naturalWidth 图片固有宽度;不同于实际展示大小可能会受CSS影响.
  • referrerPolicy 定时告诉用户如何获取图片资源
  • src 图像完整的URL
  • useMap 定义引用map 元素#开头
  • srcset 候选图像列表,逗号分隔; w表示图像宽度,x 表示图像密度
  • sizes 定义图像特定现实的大小;
// 定义实例
const img = new Image(150,160)
img.src = './test.png'
// 定义了宽度、高度,则可以直接获取到属性 img.width img.height
img.onload = function(){
  // 要获取实际的图片的大小,则必须等待图片加载完成
  // img.naturalWidth,img.naturalHeight
}

// 可以像普通的img标签添加到页面中
document.body.appendChild(img)

实例方法

基本没什么主要的方法提供调用,

  • decode() 用于解码加载图片的帧,安全的加载到DOM中。返回一个promise

继承自HTMLELement接口,拥有常规DOM的事件

  • onload 加载完成
  • onerror 加载错误时,触发调用。
// 主要是onload 加载完成获取到实际图片的大小信息
img.onload = function(){
  // 要获取实际的图片的大小,则必须等待图片加载完成
  // img.naturalWidth,img.naturalHeight
}

创建实例时,没有指定大小,img.widht/img.hegiht即是图片的真实大小。

响应式图片大小

现在的电子设备越来越多,屏幕大小、分隔各不相同,设计网站展示想达到完美的展示效果,则必须创建适合各个大小屏幕分隔的图片进行展示.

使用图片的srcset \ sizes定义图片资源

<img srcset="1.png 500w,
             2.png 800w,
             3.png 1200w"
     sizes="(max-width:520px) 500px,
            (max-width:820px) 800px,
            1200px" 
    alt="图像"
   />

检测设备宽度, 检查符合条件的sizes列表媒体查询;获取到定义的展示图片的大小,从srcset加载到最符合size大小的图像进行展示.

可以通过定义srcset x 不同的分辨率;

MutationObserver

提供了监视对DOM树所做更改的能力 ,

这个是在vue指令滚动加载的时候看到源码里写的,之后就业务功能中也会使用它做一些加载的优化处理.

构造实例

  • 参数为回调 , 即指定的节点或子节点发生dom变动时别调用.
    • 回调第一个参数为变动的MutationRecord 对象数组.
    • 实例对象observer
// 实例化observer对象
const observer = new MutationObserver(handlerChange)

// 配置监听的dom,以及监听哪些属性的变动配置
const options = {
  childList:true, // 观察子节点的变化,添加或删除
  attributes:true, // 观察属性变动
  subtree:true, // 观察子孙节点
}

// 监听
observe.observe(document.querySelector("#app"),options)

// 回调事件
const handlerChange = function(mutationList,observer){
  // 触发变动的节点、属性
}

实例方法

  • observe(dom,options) - 配置监听的DOM给定选项更改时,调用回调函数

    const options = {
      attributeFiter:[], // 设置监听指定属性,比如width、height等.不设置则监听所有属性.
      attributeOldValue:true, // 观察节点属性变更时,记录旧值
      attributes:true, // 观察节点属性的变更
      characterData:true, // 监听文本节点文本的变化
      characterDataOldValue:true, // 监听文本节点,记录文本节点旧值
      childList:true, // 观察子孙节点的添加、删除更改
      subtree:true, // 观察子孙节点、属性等所有的变化
    }
    

    多次调用监听方法,会移除现有的观察目标的监听; 如果没有指定DOM,则保留现有的观察目标,并添加新的观察者.

  • disconnect() - 停止监听DOM,回调不会在调用

  • takeRecords() - 删除所有待处理的变更通知.

滚动加载时加载初始数据

之前看到element的滚动加载的指令v-infinite-scroll , 在初始加载数据时,使用API监听子节点的变化,从而加载让数据内容去出现滚动条为止. 我们的滚动加载必须要有滚动条才能出发滚动事件.

(当然我们可以通过递归回调的方式判定数据区的offsetHeight/clientHeight 对比是否出现滚动条,进而追加数据)

但自动触发处理逻辑岂不更好 :smile:

let id = 0
// 加载数据的方法
function loadChild(){
    id++;
    let p = document.createElement('p')
    p.textContent = `数据id${id}`
    //
    dom.appendChild(p)
}

// 实例化observer对象
const observer = new MutationObserver(function(){
    // 触发之后,添加子节点
    loadChild()
    // 判断是否出现了滚动条,
    // 有滚动条了则不需要监听DOM 了
    if(dom.scrollHeight > dom.clientHeight){
        // 存在滚动条
        if(observer){
            observer.disconnect();
            observer = null;
        }
    }
})

// 配置监听的dom,以及监听哪些属性的变动配置
const options = {
    childList: true, // 观察子节点的变化,添加或删除
    attributes: false, // 观察属性变动
    subtree: false, // 观察子孙节点
}

// 监听
let dom = document.querySelector(".list-box");
observer.observe(dom, options)

// 初始调用一次
loadChild();

// 然后是正常的scroll 事件监听
dom.addEventListener('scroll',function(){
    // 滚动距离+可视高度 <= 内容区高度-20 追加数据
    let scrollBottom = dom.scrollTop + dom.clientHeight
    if(dom.scrollHeight - scrollBottom <= 20){
        loadChild();
    }
})

为了初始触发目标节点的变动,需要手动调用一次数据加载函数loadChild() , 当添加第一条数据后,DOM监听回调开始执行 ;一直到出现滚动条(可视高度不等于内容区域高度时)停止检测.

正常的scroll事件被监听,滚动到底部距离小于20(阀值)时,触发数据加载函数;

数据加载完成时,可以在loadChild函数中增加取消滚动事件,停止滚动加载时间触发.

MutationRecord

监听DOM 变更后回调第一个参数.

  • type - 变更的类型, 属性-attributes;节点文本变化 - characterData; 子节点变化 - childList
  • target - 变更的目标节点
  • addedNodes - 被添加的节点NodeList
  • removedNodes - 被移除的节点NodeList
  • oladValue - 变更前的旧值记录,需要配置属性才会有;
  • ...

FormData

提供处理form表单数据的功能,form表单处理在日常开发中处处可见。现在的前端框架帮我很好的处理了这一问题,必要的学习了解还是必须的。

构造实例

  • 参数为 form标签dom对象,非必选。
// 
const form = new FormData()

当我们传入form 参数时,每个form元素都需要name属性,这是必须的。会自动将表单值序列化

// 指定form元素,同步获取到表单里的表单属性、值。
const form = new FormData(document.querySelector('#form'))

实例方法

  • append() key/value 新增键值对,如果存在key,则新增一个值
  • delete() 删除指定键
  • entries() 所有键值对的迭代对象
  • get() 获取到指定键的第一个值
  • getAll() 返回指定键的包含所有值的数组
  • has() 是否包含某个键
  • keys() 所有属性键的迭代对象
  • set() 设置属性值,覆盖原有的值
  • valuse() 返回包含所有属性值得迭代对象
<form id="form">
    <div class="form-item">
        <label>姓名</label>
        <input type="text" name="name" value="admin" placeholder="输入姓名" />
    </div>
    <div class="form-item">
        <label>年龄</label>
        <input type="number" min="0" name="age" value="23" max="200" step="1" />
    </div>
    <div class="form-item">
        <label>性别</label>
        <input type="radio" name="gender" value="1" checked><input type="radio" name="gender" value="2"></div>
</form>

定义的form表单,创建FormData实例后,操作表单元素,并可增加新的属性。

// 所有属性 ,按照表单元素的name属性序列化
form.keys()         // [...keys] - result:["name", "age", "gender"]
// 查找某个值 
form.has('id')          // false
// 添加一个值,重复的name属性
form.append('name','test')         
form.get('name')               // 返回第一个符合的值, 为 admin
form.getAll('name')               // 返回所有的值得数组, ['admin','test']

XMLHttpRequest发送数据

创建一个ajax实例,发送请求。

// 直接发送数据
const xhr = new XMLHttpRequest()

xhr.open('post','/addUserInfo')
xhr.send(form)

使用File 创建文件,上传文件。

// 创建文件对象
const file = new File(['hello world'],'test.txt')
// 定义文件读取的实例对象
const fileReader = new FileReader()
// 定义请求实例
const xhr = new XMLHttpRequest()
// 通过onload事件回调获取到文件内容
fileReader.onload = function(event){
    // 读取到文件blob数据
    form.append('file',event.target.result)
    xhr.open('post','/addUserInfo')
    xhr.send(form)
}
// 读取文件为字符串
fileReader.readAsDataURL(file)

作为URLSearchParams 构造参数解析

可直接传入URLSearchParams解析,然后追加到URL上。

// 
const params = new URLSearchParams(form)

// 可获取使用实例的方法
params.toString()                  // name=admin&age=23&gender=1&name=test