記一次老項目中的跨頁面通訊問題和前端實現文件下載功能

因爲筆者以前維護了幾個比較老的項目是用jquery全家桶開發的,其中有些需求是須要跨頁面交互和父子頁面通訊,故藉此總結一下。另外一塊是前端實現文件下載功能,雖然方法不少,爲了避免用重複造輪子,在此仍是總結一波,畢竟多頁面下的應用場景仍是不少的。javascript

文章摘要

  • 實現頁面之間通訊的方法
  • 實現父子頁面和子頁面與子頁面之間通訊的方法
  • 前端實現文件下載功能

因爲本文介紹的主要仍是基於javascript,不涉及任何框架方面的問題(若是想研究vue,react,angular方面的技術問題,能夠移步個人其餘文章),因此讓咱們用原生javascript來解決咱們上面提到的問題吧。html

正文

1. 實現頁面之間通訊的方法

雖然咱們使用postmessage也能夠實現頁面通訊,但這裏咱們主要使用window.opener這個API,MDN對它的解釋以下:前端

The Window interface's opener property returns a reference to the window that opened the window using open().vue

意思就是window提供的opener接口返回一個打開當前頁面的頁面的一個引用,換句話說,若是A頁面打開B,那麼B頁面的opener將返回A。經過這種方式,咱們能夠在A頁面定義全局的方法掛載在window上,那麼B頁面就能夠經過opener拿到A頁面的方法從而控制A頁面的行爲。java

目前主流的瀏覽器對這個API支持的都比較好,因此咱們在大部分場景下能夠考慮使用這個API。node

爲了更方便的理解他的應用場景,咱們這裏實現一個小功能:咱們定義兩個頁面,A,B,當A頁面打開B頁面的時候,用B頁面改變A頁面的背景色。 代碼以下:react

// A頁面
<body>
    <h1>父頁面A</h1>
    <a href="./b.html" target="_blank">打開b頁面</a>
    <script> function changeColor(color) { document.body.style.background = color } </script>
</body>

// B頁面
<body>
    <h1>父頁面B</h1>
    <script> window.opener.changeColor('blue') </script>
</body>
複製代碼

首先咱們在A頁面裏定義一個全局方法,當點擊a標籤跳轉到新開的B頁面時,B頁面就是經過opener,調用A定義的changeColor,並傳入參數給A頁面,從而改變A頁面的背景色。效果以下:jquery

2.實現父子頁面和子頁面與子頁面之間通訊的方法

父子頁面這裏主要針對iframe而言,即iframe和父頁面以及iframe頁面之間的通訊。好比下圖:ios

咱們想實現父頁面A操控子頁面A,B,而且讓子頁面和父頁面交互,這裏咱們主要使用 iframe的

  • contentWindow
  • parent.window 經過contentWindow,咱們能夠拿到iframe內部的方法和dom元素,進而能夠操控iframe頁面

首先咱們來看看父頁面操控子頁面的場景:父頁面A調用子頁面的方法傳遞一條數據,並並顯示在子頁面中:redis

// 父頁面
window.onload = function() {
    let iframe1 = $id('a1').contentWindow;
    // 控制子頁面dom
    iframe1.document.body.style.background = "#000"
    iframe1.loadData({a: '1'})
}

function $id(id) {
    return document.getElementById(id)
}

// 子頁面
function loadData(data) {
    document.body.append(`父頁面的數據數據${data.a}`)
}
複製代碼

由上可知,父頁面經過contentWindow拿到iframe的window對象從而向其傳遞數據並調用其方法。

一樣,子頁面也能夠操控父頁面:

// 父頁面
function $id(id) {
    return document.getElementById(id)
}
// 子頁面
parent.window.$id('bridge').innerHTML = '子頁面操控父頁面dom'
複製代碼

從代碼能夠看到,咱們使用parent.window拿到父頁面的window,而後調用父頁面提供的$id方法來操做父頁面dom。

接下來咱們來解決子頁面和子頁面通訊的問題,其實方法在上面已經提到了,咱們能夠把父頁面做爲一個橋樑,子頁面A經過parent.window拿到父頁面的window,進而能夠獲取另外一個子頁面B的dom,這樣咱們就可讓子頁面A操做子頁面B了,反之也是同樣的。

// 子頁面A
let iframeBWin = parent.window.$id('a2').contentWindow
iframeBWin.onload = function() {
    iframeBWin.document.getElementById('show').innerHTML = "來自子頁面A的問候"
}
複製代碼

由上面代碼咱們能夠知道,咱們經過parent.window來拿到子頁面B進而實現和子頁面B通訊的目的,經過這種方式,咱們能夠實現不少有意思的東西。

注意,咱們所討論的這些方法都是基於同域下的,其實實現跨域的方法也有不少,好比使用中間iframe實現橋接,經過設置window.domain將window提升到頂層等等,不過實現起來仍是有些坑的,不過大部分場景都能知足。

4.前端實現文件下載功能

對於下載文件來講,大部分場景都是後端來實現,點端指需求請求接口就行了,可是有時候這種方式反而會佔用多餘的資源和帶寬,若是須要下載的是用戶本身生成的內容或者內容已經返回到客戶端了,這時候能不通過服務端而直接生成下載任務,能節省很多的資源和時間開銷。

通常來講前端實現的思路就是經過動態建立a標籤,設置其download屬性,最後刪除a就行了,對於不是圖片的文件通常均可如下載,可是若是是圖片,有些瀏覽器會自動打開圖片,因此咱們須要手動把它轉化爲data:URLs或blob:URLs,基於這個原理,咱們能夠用fileReader,也能夠用fetch-URL.createObjectURL,這裏通過大量測試我採用後者:

function download(url, filename) {
    return fetch(url).then(res => res.blob().then(blob => {
        let a = document.createElement('a');
        let url = window.URL.createObjectURL(blob);
        a.href = url;
        a.download = filename;
        a.click();
        window.URL.revokeObjectURL(url);
    }))
}
複製代碼

該方法傳入一個文件的地址和但願使用的文件名,這樣,咱們就能優雅的使用它來實現下載了。

最後

因爲筆者最近在寫開源的CMS系統,技術架構:

  • 後臺Node+Koa+redis+JsonSchema
  • 管理後臺界面 vue-cli3 + vue + ts + vuex + antd-vue + axios
  • 客戶端前臺 react + antd + react-hooks + axios

後面將推出該系統的設計思想,架構和實現過程,歡迎在公衆號《趣談前端》裏查看更詳細的介紹。

歡迎你們相互學習交流,一塊兒探索前端的邊界。

更多推薦

相關文章
相關標籤/搜索