面向?qū)ο缶幊?目前還是主流,個(gè)人也比較喜歡細(xì)化組件、切割對(duì)象.這樣看起來比較清晰、每個(gè)對(duì)象職責(zé)單一,不會(huì)混淆造成混亂.
前端經(jīng)常會(huì)和瀏覽器打交道,在處理一些與瀏覽器相關(guān)的邏輯時(shí),就會(huì)調(diào)用瀏覽器API,整理日常會(huì)用到的API對(duì)象.
URL
構(gòu)造、解析、規(guī)范化和編碼URL.
創(chuàng)建URL對(duì)象
構(gòu)造參數(shù)說明
url如果是絕對(duì)URL地址,則忽略第二個(gè)參數(shù);如果是相對(duì)路徑,則以base作為基準(zhǔn)URL.base基準(zhǔn)URL,如果第一個(gè)參數(shù)url是絕對(duì)URL時(shí),則不生效.
// 指定絕對(duì)ULR地址
const url = new URL('http://www.baidu.com')
// 或者
const url = new URL('','http://www.baidu.com')
// 存在路徑時(shí)
const url = new URL('http://www.baidu.com/hello')
// 或
const url = new URL('/hello','http://www.baidu.com')
構(gòu)造完的url實(shí)例對(duì)象有哪些屬性呢 , 可以從圖中獲取該url地址中所有的數(shù)據(jù)信息:
origin\searchParams 為只讀屬性 , 其他的屬性則可以通過變量賦值進(jìn)行設(shè)置.

修改相關(guān)屬性:
// 修改協(xié)議
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',
實(shí)例方法
toString()返回整個(gè)URL地址toJSON(), 返回整個(gè)URL地址,同url.href
以為有啥不一樣的地方,發(fā)現(xiàn)都是返現(xiàn)url地址.
還有靜態(tài)方法,創(chuàng)建一個(gè)唯一的資源地址鏈接:
createObjectURL()File、Blog或MediaSource對(duì)象,返回唯一的blog鏈接.revokeObjectURL()銷毀之前createObjectURL的URL實(shí)例對(duì)象.
// File 對(duì)象,或者blob數(shù)據(jù)
const dataUrl = URL.createObjectURL('blob:**')
// 銷毀創(chuàng)建的實(shí)例 , 訪問失效
URL.revokeObjectURL(dataUrl)
URLSearchParams 對(duì)象
這個(gè)應(yīng)該是我們經(jīng)常會(huì)用到的,用于處理url的查詢字符串.
在實(shí)例url中,存在search 和searchParams(只讀)
// url實(shí)例追加一個(gè)查詢條件
url.search = 'id=45'
// 重新讀取時(shí)會(huì)包含 ? 注意
console.log(url.search) // ?id=45
// 獲取實(shí)例URL的查詢參數(shù)對(duì)象
const searchParams = url.searchParams
創(chuàng)建實(shí)例
- 可選的參數(shù) 傳入會(huì)被解析,比如
?id=45\id=45會(huì)忽略開頭的?
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'})
實(shí)例化的好處在于可以方便管理,比如添加、刪除、查找等,通過實(shí)例方法很方便的管理數(shù)據(jù).
實(shí)例方法
append(name,value)- 添加一個(gè)數(shù)據(jù)delete(name)- 刪除指定參數(shù)名的數(shù)據(jù)entries()- 迭代遍歷鍵/值對(duì)的對(duì)象.get(name)- 獲取到指定參數(shù)名的第一個(gè)值.getAll(name)- 返回指定參數(shù)名的所有值,數(shù)組has(name)- 判斷是否存在某個(gè)參數(shù).keys()- 所有參數(shù)的鍵的迭代對(duì)象.set(name,value)- 設(shè)置某個(gè)參數(shù)的值.sort()- 按鍵名排序.toString()- 返回查詢字符串.values()- 包含所有值的迭代對(duì)象.
const searchParams = new URLSearchParams()
// 追加一個(gè)參數(shù)
searchParams.append("id","sv2341") // { 'id' => 'sv2341' }
// 判斷是否包含某個(gè)參數(shù)
searchParams.has("name") // false
// 獲取指定參數(shù)的值
searchParams.get("id") // sv2341
// 獲取所有參數(shù)的值
[...searchParams.values()] // ['sv2341']
//
searchParams.toString(); //.
處理URL地址參數(shù)
不需要自己再去分隔字符串處理查詢參數(shù)了,通過searchParams對(duì)象優(yōu)雅處理查詢參數(shù),方便多了.
// 獲取實(shí)例URL的查詢字符串對(duì)象
const searchParams = url.searchParams
// 追加一個(gè)name查詢參數(shù)
searchParams.append('name','admin') // href: 'ftp://www.baidu.com/admin?id=45&name=admin#app',
如果是實(shí)例化的新的URLSearchParams , 則復(fù)制給url對(duì)象屬性search
const url = new URL('http://www.baidu.com/hello')
// 新實(shí)例化的對(duì)象
const searchParams = new URLSearchParams()
searchParams.append("id","sv2341")
searchParams.append("name","test")
// 設(shè)置查詢參數(shù)
url.search = searchParams // href: 'http://www.baidu.com/hello?id=sv2341&name=test',
無法處理hash與pathname的位置順序
讓人頭疼的是hash與pathname 的位置,hash設(shè)置總是在pathname \ search 后面
const url = new URL('http://www.baidu.com')
// 賦值hash
url.hash = '/'
// 賦值路徑
url.pathname = '/app'
// 賦值查詢條件
url.search = 'id=45'
結(jié)果展示為http://www.baidu.com/app?id=45#/ 怎么調(diào)試也沒能改變, 我想要的結(jié)果是http://www.baidu.com/#/app?id=45
不然重新打開一個(gè)新的tab頁時(shí),會(huì)導(dǎo)航到初始主路由 :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
處理文件相關(guān)的信息.訪問文件中的數(shù)據(jù). UTF8編碼
構(gòu)造實(shí)例
參數(shù)定義:
bits定義的文件內(nèi)容,包含ArrayBuffer\ArrayBufferView\Blob\DOMString[]等類型name文件名稱,可以追加路徑.-
options可選的配置項(xiàng)type- 文件MIME類型 ,默認(rèn)‘’lastModified文件修改的時(shí)間
const file = new File(bits,name,options)
可訪問的屬性,都為只讀屬性:
lastModified- 文件最后的修改時(shí)間name- 文件名稱size- 文件大小type- 文件類型
實(shí)例方法
自身沒有定義方法,繼承自Blob接口,可選的參數(shù)定義:
-
slice(start,end,contentType)- 返回一個(gè)新的Blob對(duì)象,原始的一段數(shù)據(jù).
// 自定義文本內(nèi)容
const file = new File(['hello world','luck for you'],'test.txt')
FileReader
上述只是定義個(gè)一個(gè)文件對(duì)象,我們要讀取到文件的內(nèi)容,則需要FileReader 對(duì)象讀取數(shù)據(jù).
構(gòu)造實(shí)例
const fileReader = new FileReader();
可訪問的屬性:
error- 讀取文件時(shí)發(fā)生的錯(cuò)誤readyState- 取值狀態(tài), 0-未加載任何數(shù)據(jù); 1-數(shù)據(jù)正在加載; 2-已讀取完成.result- 讀取到的文件內(nèi)容.
實(shí)例方法
定義實(shí)例,則可以用來讀取文件,文件的讀取是一個(gè)異步過程. 那么異步就會(huì)存在異步流程,對(duì)應(yīng)著不同的事件.
先看一下實(shí)例定義的方法:
abort()中止讀取操作readAsArrayBuffer()讀取內(nèi)容以ArrayBuffer格式保存數(shù)據(jù).readAsDataURL()讀取內(nèi)容,返回格式為data:base64數(shù)據(jù)readAsText()讀取內(nèi)容,返回的內(nèi)容為字符串.
可以繼續(xù)之前的文件數(shù)據(jù)讀取;
// 定義文件對(duì)象
const file = new File(['hello world','luck for you'],'test.txt')
// 定義文件讀取的實(shí)例對(duì)象
const fileReader = new FileReader()
// 通過onload事件回調(diào)獲取到文件內(nèi)容
fileReader.onload = function(event){
console.log(event.target.result) // hello worldluck for you
}
// 讀取文件為字符串
fileReader.readAsText(file)
這里是所有的事件:
onabort中斷讀取時(shí)觸發(fā).onerror讀取操作發(fā)生錯(cuò)誤觸發(fā).onload讀取完成時(shí)觸發(fā).onloadstart讀取開始時(shí)觸發(fā)onloadend讀取結(jié)束觸發(fā),成功或者失敗onprogress讀取Blob對(duì)象時(shí)觸發(fā)
定義文件下載
通常前端會(huì)處理一些簡單的文件下載,比如文本、圖片之類的. 會(huì)使用a標(biāo)簽進(jìn)行下載處理
// 完成簡單的自定義.txt文件下載
const file = new File(['hello world'],'test.txt')
// 定義讀取文件對(duì)象
const fileReader = new FileReader()
// 讀取文件
fileReader.onload = function(event){
// 定義a標(biāo)簽,追加到body中,點(diǎn)擊進(jìn)行下載
let a = document.createElement('a')
a.download = 'hello.txt'
a.href = event.target.result
a.textContent = '下載'
document.body.append(a)
}
// a標(biāo)簽下載接受的格式blob:或者data:
// 定義讀取base64 格式的數(shù)據(jù)
fileReader.readAsDataURL(file)
可以讀取遠(yuǎn)程的URL地址文件信息,在前端實(shí)現(xiàn)下載.
通常自定義實(shí)現(xiàn)的下載,不需要去點(diǎn)擊,啟動(dòng)觸發(fā)下載事件
// 通過click方法模擬點(diǎn)擊事件,觸發(fā)下載
a.click()
// 定義a標(biāo)簽不可見
a.style.display = 'none'
通過URL.createObjectURL定義文件下載
上述可以通過FileReader來讀取文件內(nèi)容,也可以通過URL靜態(tài)方法創(chuàng)建blob:格式的URL對(duì)象,指向源內(nèi)容.
// 創(chuàng)建文件內(nèi)容ULR
const dataUrl = URL.createObjectURL(file)
// 構(gòu)建下載
let a = document.createElement('a')
a.download = 'hello.txt'
a.href = dataUrl
a.textContent = '下載'
a.style.display = 'none'
document.body.append(a)
a.click();
準(zhǔn)備寫一下文件的上傳、下載;主要是斷點(diǎn)續(xù)傳、分片上傳等;先立個(gè)flag吧,不知道啥時(shí)候?qū)懲? :dog:
Image
常用的展示形式-圖片,不可或缺. 通常的網(wǎng)站并不會(huì)處理圖片,拿到ULR地址,直接做展示就好.如果是一個(gè)專門處理圖片的工程的話, 就會(huì)對(duì)圖片各方面處理要求的多. 通常先獲取到圖片的大小,初始畫布等.
構(gòu)造實(shí)例
實(shí)例同等于html元素img, 接受參數(shù)
width寬度height高度
const img = new Image()
// 等同于
img = document.createElement('img')
可訪問屬性:
alt描述內(nèi)容complete表示加載正常,沒有發(fā)生錯(cuò)誤.crossOrigin跨域設(shè)置currentSrc表示正在加載圖像的URL.decoding圖片的加載后的解碼設(shè)置heightcss渲染的高度widthcss渲染的寬度isMap是否是某一圖片映射的一部分naturalHeight圖片固有高度;不同于實(shí)際展示大小可能會(huì)受CSS影響.naturalWidth圖片固有寬度;不同于實(shí)際展示大小可能會(huì)受CSS影響.referrerPolicy定時(shí)告訴用戶如何獲取圖片資源src圖像完整的URLuseMap定義引用map元素#開頭srcset候選圖像列表,逗號(hào)分隔;w表示圖像寬度,x表示圖像密度sizes定義圖像特定現(xiàn)實(shí)的大小;
// 定義實(shí)例
const img = new Image(150,160)
img.src = './test.png'
// 定義了寬度、高度,則可以直接獲取到屬性 img.width img.height
img.onload = function(){
// 要獲取實(shí)際的圖片的大小,則必須等待圖片加載完成
// img.naturalWidth,img.naturalHeight
}
// 可以像普通的img標(biāo)簽添加到頁面中
document.body.appendChild(img)
實(shí)例方法
基本沒什么主要的方法提供調(diào)用,
-
decode()用于解碼加載圖片的幀,安全的加載到DOM中。返回一個(gè)promise
繼承自HTMLELement接口,擁有常規(guī)DOM的事件
onload加載完成onerror加載錯(cuò)誤時(shí),觸發(fā)調(diào)用。
// 主要是onload 加載完成獲取到實(shí)際圖片的大小信息
img.onload = function(){
// 要獲取實(shí)際的圖片的大小,則必須等待圖片加載完成
// img.naturalWidth,img.naturalHeight
}
創(chuàng)建實(shí)例時(shí),沒有指定大小,img.widht/img.hegiht即是圖片的真實(shí)大小。
響應(yīng)式圖片大小
現(xiàn)在的電子設(shè)備越來越多,屏幕大小、分隔各不相同,設(shè)計(jì)網(wǎng)站展示想達(dá)到完美的展示效果,則必須創(chuàng)建適合各個(gè)大小屏幕分隔的圖片進(jìn)行展示.
使用圖片的srcset \ sizes定義圖片資源
<img srcset="1.png 500w,
2.png 800w,
3.png 1200w"
sizes="(max-width:520px) 500px,
(max-width:820px) 800px,
1200px"
alt="圖像"
/>
檢測(cè)設(shè)備寬度, 檢查符合條件的sizes列表媒體查詢;獲取到定義的展示圖片的大小,從srcset加載到最符合size大小的圖像進(jìn)行展示.
可以通過定義srcset x 不同的分辨率;
MutationObserver
提供了監(jiān)視對(duì)DOM樹所做更改的能力 ,
這個(gè)是在vue指令滾動(dòng)加載的時(shí)候看到源碼里寫的,之后就業(yè)務(wù)功能中也會(huì)使用它做一些加載的優(yōu)化處理.
構(gòu)造實(shí)例
-
參數(shù)為回調(diào) , 即指定的節(jié)點(diǎn)或子節(jié)點(diǎn)發(fā)生dom變動(dòng)時(shí)別調(diào)用.
回調(diào)第一個(gè)參數(shù)為變動(dòng)的MutationRecord 對(duì)象數(shù)組.
實(shí)例對(duì)象observer
// 實(shí)例化observer對(duì)象
const observer = new MutationObserver(handlerChange)
// 配置監(jiān)聽的dom,以及監(jiān)聽哪些屬性的變動(dòng)配置
const options = {
childList:true, // 觀察子節(jié)點(diǎn)的變化,添加或刪除
attributes:true, // 觀察屬性變動(dòng)
subtree:true, // 觀察子孫節(jié)點(diǎn)
}
// 監(jiān)聽
observe.observe(document.querySelector("#app"),options)
// 回調(diào)事件
const handlerChange = function(mutationList,observer){
// 觸發(fā)變動(dòng)的節(jié)點(diǎn)、屬性
}
實(shí)例方法
-
observe(dom,options)- 配置監(jiān)聽的DOM給定選項(xiàng)更改時(shí),調(diào)用回調(diào)函數(shù)const options = { attributeFiter:[], // 設(shè)置監(jiān)聽指定屬性,比如width、height等.不設(shè)置則監(jiān)聽所有屬性. attributeOldValue:true, // 觀察節(jié)點(diǎn)屬性變更時(shí),記錄舊值 attributes:true, // 觀察節(jié)點(diǎn)屬性的變更 characterData:true, // 監(jiān)聽文本節(jié)點(diǎn)文本的變化 characterDataOldValue:true, // 監(jiān)聽文本節(jié)點(diǎn),記錄文本節(jié)點(diǎn)舊值 childList:true, // 觀察子孫節(jié)點(diǎn)的添加、刪除更改 subtree:true, // 觀察子孫節(jié)點(diǎn)、屬性等所有的變化 }多次調(diào)用監(jiān)聽方法,會(huì)移除現(xiàn)有的觀察目標(biāo)的監(jiān)聽; 如果沒有指定DOM,則保留現(xiàn)有的觀察目標(biāo),并添加新的觀察者.
disconnect()- 停止監(jiān)聽DOM,回調(diào)不會(huì)在調(diào)用takeRecords()- 刪除所有待處理的變更通知.
滾動(dòng)加載時(shí)加載初始數(shù)據(jù)
之前看到element的滾動(dòng)加載的指令v-infinite-scroll , 在初始加載數(shù)據(jù)時(shí),使用API監(jiān)聽子節(jié)點(diǎn)的變化,從而加載讓數(shù)據(jù)內(nèi)容去出現(xiàn)滾動(dòng)條為止. 我們的滾動(dòng)加載必須要有滾動(dòng)條才能出發(fā)滾動(dòng)事件.
(當(dāng)然我們可以通過遞歸回調(diào)的方式判定數(shù)據(jù)區(qū)的offsetHeight/clientHeight 對(duì)比是否出現(xiàn)滾動(dòng)條,進(jìn)而追加數(shù)據(jù))
但自動(dòng)觸發(fā)處理邏輯豈不更好 :smile:
let id = 0
// 加載數(shù)據(jù)的方法
function loadChild(){
id++;
let p = document.createElement('p')
p.textContent = `數(shù)據(jù)id${id}`
//
dom.appendChild(p)
}
// 實(shí)例化observer對(duì)象
const observer = new MutationObserver(function(){
// 觸發(fā)之后,添加子節(jié)點(diǎn)
loadChild()
// 判斷是否出現(xiàn)了滾動(dòng)條,
// 有滾動(dòng)條了則不需要監(jiān)聽DOM 了
if(dom.scrollHeight > dom.clientHeight){
// 存在滾動(dòng)條
if(observer){
observer.disconnect();
observer = null;
}
}
})
// 配置監(jiān)聽的dom,以及監(jiān)聽哪些屬性的變動(dòng)配置
const options = {
childList: true, // 觀察子節(jié)點(diǎn)的變化,添加或刪除
attributes: false, // 觀察屬性變動(dòng)
subtree: false, // 觀察子孫節(jié)點(diǎn)
}
// 監(jiān)聽
let dom = document.querySelector(".list-box");
observer.observe(dom, options)
// 初始調(diào)用一次
loadChild();
// 然后是正常的scroll 事件監(jiān)聽
dom.addEventListener('scroll',function(){
// 滾動(dòng)距離+可視高度 <= 內(nèi)容區(qū)高度-20 追加數(shù)據(jù)
let scrollBottom = dom.scrollTop + dom.clientHeight
if(dom.scrollHeight - scrollBottom <= 20){
loadChild();
}
})
為了初始觸發(fā)目標(biāo)節(jié)點(diǎn)的變動(dòng),需要手動(dòng)調(diào)用一次數(shù)據(jù)加載函數(shù)loadChild() , 當(dāng)添加第一條數(shù)據(jù)后,DOM監(jiān)聽回調(diào)開始執(zhí)行 ;一直到出現(xiàn)滾動(dòng)條(可視高度不等于內(nèi)容區(qū)域高度時(shí))停止檢測(cè).
正常的scroll事件被監(jiān)聽,滾動(dòng)到底部距離小于20(閥值)時(shí),觸發(fā)數(shù)據(jù)加載函數(shù);
數(shù)據(jù)加載完成時(shí),可以在loadChild函數(shù)中增加取消滾動(dòng)事件,停止?jié)L動(dòng)加載時(shí)間觸發(fā).
MutationRecord
監(jiān)聽DOM 變更后回調(diào)第一個(gè)參數(shù).
type- 變更的類型, 屬性-attributes;節(jié)點(diǎn)文本變化 - characterData; 子節(jié)點(diǎn)變化 - childListtarget- 變更的目標(biāo)節(jié)點(diǎn)addedNodes- 被添加的節(jié)點(diǎn)NodeListremovedNodes- 被移除的節(jié)點(diǎn)NodeListoladValue- 變更前的舊值記錄,需要配置屬性才會(huì)有;...
FormData
提供處理form表單數(shù)據(jù)的功能,form表單處理在日常開發(fā)中處處可見。現(xiàn)在的前端框架幫我很好的處理了這一問題,必要的學(xué)習(xí)了解還是必須的。
構(gòu)造實(shí)例
- 參數(shù)為
form標(biāo)簽dom對(duì)象,非必選。
//
const form = new FormData()
當(dāng)我們傳入form 參數(shù)時(shí),每個(gè)form元素都需要name屬性,這是必須的。會(huì)自動(dòng)將表單值序列化
// 指定form元素,同步獲取到表單里的表單屬性、值。
const form = new FormData(document.querySelector('#form'))
實(shí)例方法
append()key/value 新增鍵值對(duì),如果存在key,則新增一個(gè)值delete()刪除指定鍵entries()所有鍵值對(duì)的迭代對(duì)象get()獲取到指定鍵的第一個(gè)值getAll()返回指定鍵的包含所有值的數(shù)組has()是否包含某個(gè)鍵keys()所有屬性鍵的迭代對(duì)象set()設(shè)置屬性值,覆蓋原有的值valuse()返回包含所有屬性值得迭代對(duì)象
<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表單,創(chuàng)建FormData實(shí)例后,操作表單元素,并可增加新的屬性。
// 所有屬性 ,按照表單元素的name屬性序列化
form.keys() // [...keys] - result:["name", "age", "gender"]
// 查找某個(gè)值
form.has('id') // false
// 添加一個(gè)值,重復(fù)的name屬性
form.append('name','test')
form.get('name') // 返回第一個(gè)符合的值, 為 admin
form.getAll('name') // 返回所有的值得數(shù)組, ['admin','test']
XMLHttpRequest發(fā)送數(shù)據(jù)
創(chuàng)建一個(gè)ajax實(shí)例,發(fā)送請(qǐng)求。
// 直接發(fā)送數(shù)據(jù)
const xhr = new XMLHttpRequest()
xhr.open('post','/addUserInfo')
xhr.send(form)
使用File 創(chuàng)建文件,上傳文件。
// 創(chuàng)建文件對(duì)象
const file = new File(['hello world'],'test.txt')
// 定義文件讀取的實(shí)例對(duì)象
const fileReader = new FileReader()
// 定義請(qǐng)求實(shí)例
const xhr = new XMLHttpRequest()
// 通過onload事件回調(diào)獲取到文件內(nèi)容
fileReader.onload = function(event){
// 讀取到文件blob數(shù)據(jù)
form.append('file',event.target.result)
xhr.open('post','/addUserInfo')
xhr.send(form)
}
// 讀取文件為字符串
fileReader.readAsDataURL(file)
作為URLSearchParams 構(gòu)造參數(shù)解析
可直接傳入URLSearchParams解析,然后追加到URL上。
//
const params = new URLSearchParams(form)
// 可獲取使用實(shí)例的方法
params.toString() // name=admin&age=23&gender=1&name=test