利用Pjax下載動(dòng)態(tài)加載插件方案分享
在純靜態(tài)網(wǎng)站里,有時(shí)候會(huì)動(dòng)態(tài)更新某個(gè)區(qū)域往會(huì)選擇 Pjax(swup、barba.js)去處理,他們都是使用 ajax 和 pushState 通過真正的永久鏈接,頁面標(biāo)題和后退按鈕提供快速瀏覽體驗(yàn)。
但是實(shí)際使用中可能會(huì)遇到不同頁面可能會(huì)需要加載不同插件處理,有些人可能會(huì)全量選擇加載,這樣會(huì)導(dǎo)致加載很多無用的腳本,有可能在用戶關(guān)閉頁面時(shí)都不一定會(huì)訪問到,會(huì)很浪費(fèi)資源。
解決思路
首先想到的肯定是在請(qǐng)求到新的頁面后,我們手動(dòng)去比較當(dāng)前 DOM 和 新 DOM 之間 script
標(biāo)簽的差異,手動(dòng)給他插入到 body 里。
處理 Script
一般來說 JavaScript 腳本都是放在 body
后,避免阻塞頁面渲染,假設(shè)我們頁面腳本也都是在 body
后,并在 script 添加 [data-reload-script]
表明哪些是需要?jiǎng)討B(tài)加載的。
首先我們直接獲取到帶有 [data-reload-script]
屬性的 script 標(biāo)簽:
// NewHTML 為 新頁面 HTML const pageContent = NewHTML.replace('<body', '<div id="DynamicPluginBody"').replace('</body>', '</div>'); let element = document.createElement('div'); element.innerHTML = pageContent; const children = element.querySelector('#DynamicPluginBody').querySelectorAll('script[data-reload-script]');
然后通過創(chuàng)建 script 標(biāo)簽插入到 body
:
children.forEach(item => { const element = document.createElement('script'); for (const { name, value } of arrayify(item.attributes)) { element.setAttribute(name, value); } element.textContent = item.textContent; element.setAttribute('async', 'false'); document.body.insertBefore(element) })
如果你的插件都是通過 script 引入,且不需要執(zhí)行額外的 JavaScript 代碼,只需要在 Pjax 鉤子函數(shù)這樣處理就可以了。
執(zhí)行代碼塊
實(shí)際很多插件不僅僅需要你引入,還需要你手動(dòng)去初始化做一些操作的。我們可以通過 src
去判斷是引入的腳本,還是代碼塊。
let scripts = Array.from(document.scripts) let scriptCDN = [] let scriptBlock = [] children.forEach(item => { if (item.src) scripts.findIndex(s => s.src === item.src) < 0 && scriptCDN.push(item); else scriptBlock.push(item.innerText) })
scriptCDN 繼續(xù)通過上面方式插入到 body 里,然后通過 eval 或者 new Function 去執(zhí)行 scriptBlock 。因?yàn)?scriptBlock 里的代碼可能是會(huì)依賴 scriptCDN 里的插件的,所以需要在 scriptCDN 加載完成后在執(zhí)行 scriptBlock 。
const loadScript = (item) => { return new Promise((resolve, reject) => { const element = document.createElement('script'); for (const { name, value } of arrayify(item.attributes)) { element.setAttribute(name, value); } element.textContent = item.textContent; element.setAttribute('async', 'false'); element.onload = resolve element.onerror = reject document.body.insertBefore(element) }) } const runScriptBlock = (code) => { try { const func = new Function(code); func() } catch (error) { try { window.eval(code) } catch (error) { } } } Promise.all(scriptCDN.map(item => loadScript(item))).then(_ => { scriptBlock.forEach(code => { runScriptBlock(code) }) })
卸載插件
按照上面思去處理之后,會(huì)存在一個(gè)問題。 比如:我們添加了一個(gè) 全局的 'resize' 事件的監(jiān)聽,在跳轉(zhuǎn)其他頁面時(shí)候我們需要移除這個(gè)監(jiān)聽事件。
這個(gè)時(shí)候我們需要對(duì)代碼塊的格式進(jìn)行一個(gè)約束,比如像下面這樣,在初次加載時(shí)執(zhí)行 mount 里代碼,頁面卸載時(shí)執(zhí)行 unmount 里代碼。
<script data-reload-script> DynamicPlugin.add({ // 頁面加載時(shí)執(zhí)行 mount() { this.timer = setInterval(() => { document.getElementById('time').innerText = new Date().toString() }, 1000) }, // 頁面卸載時(shí)執(zhí)行 unmount() { window.clearInterval(this.timer) this.timer = null } }) </script>
DynamicPlugin 大致結(jié)構(gòu):
let cacheMount = [] let cacheUnMount = [] let context = {} class DynamicPlugin { add(options) { if (isFunction(options)) cacheMount.push(options) if (isPlainObject(options)) { let { mount, unmount } = options if (isFunction(mount)) cacheMount.push(mount) if (isFunction(unmount)) cacheUnMount.push(unmount) } // 執(zhí)行當(dāng)前頁面加載鉤子 this.runMount() } runMount() { while (cacheMount.length) { let item = cacheMount.shift(); item.call(context); } } runUnMount() { while (cacheUnMount.length) { let item = cacheUnMount.shift(); item.call(context); } } }
頁面卸載時(shí)調(diào)用 DynamicPlugin.runUnMount()。
處理 Head
Head 部分處理來說相對(duì)比較簡單,可以通過拿到新舊兩個(gè) Head,然后循環(huán)對(duì)比每個(gè)標(biāo)簽的 outerHTML
,用來判斷哪些比是需要新增的哪些是需要?jiǎng)h除的。
結(jié)尾
本文示例代碼完整版本可以 參考這里
到此這篇關(guān)于利用Pjax下載動(dòng)態(tài)加載插件方案分享的文章就介紹到這了,更多相關(guān)Pjax下載動(dòng)態(tài)加載插件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序通過點(diǎn)擊事件傳參(data-)的操作示例
微信小程序可以通過直接寫 data-index="1" 進(jìn)行數(shù)據(jù)的綁定 ,利用 bindtap 點(diǎn)擊事件執(zhí)行函數(shù)從而獲取到參數(shù)信息,本文給大家介紹微信小程序通過點(diǎn)擊事件傳參(data-)的操作,感興趣的朋友一起看看吧2023-12-12javascript實(shí)現(xiàn)網(wǎng)頁端解壓并查看zip文件
昨天給大家分享了在網(wǎng)頁端使用zip.js插件實(shí)現(xiàn)在線壓縮文件的代碼,今天給大家分享一下javascript實(shí)現(xiàn)網(wǎng)頁端解壓并查看zip文件的方法,非常的實(shí)用,有需要的小伙伴可以參考下2015-12-12基于JavaScript實(shí)現(xiàn)簡單抽獎(jiǎng)功能代碼實(shí)例
這篇文章主要介紹了基于JavaScript實(shí)現(xiàn)簡單抽獎(jiǎng)功能代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10layui框架table 數(shù)據(jù)表格的方法級(jí)渲染詳解
今天小編就為大家分享一篇layui框架table 數(shù)據(jù)表格的方法級(jí)渲染詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08微信小程序?qū)崿F(xiàn)答題倒計(jì)時(shí)
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)答題倒計(jì)時(shí),自定義計(jì)時(shí)器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09