面向对象编程,目前还是主流,个人也比较喜欢细化组件、切割对象.这样看起来比较清晰、每个对象职责单一,不会混淆造成混乱.
前端经常会和浏览器打交道,在处理一些与浏览器相关的逻辑时,就会调用浏览器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
为只读属性 , 其他的属性则可以通过变量赋值进行设置.
修改相关属性:
// 修改协议
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中,存在search
和searchParams
(只读)
// 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的位置顺序
让人头疼的是hash
与pathname
的位置,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页时,会导航到初始主路由 😢
所以只能去重新拼接一下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吧,不知道啥时候写完. 🐶
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
图像完整的URLuseMap
定义引用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 对比是否出现滚动条,进而追加数据)
但自动触发处理逻辑岂不更好 😄
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; 子节点变化 - childListtarget
- 变更的目标节点addedNodes
- 被添加的节点NodeListremovedNodes
- 被移除的节点NodeListoladValue
- 变更前的旧值记录,需要配置属性才会有;- …
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