欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue3自定義打印實現(xiàn)原理詳解

 更新時間:2024年07月02日 08:30:09   作者:子洋  
近接觸到了一個 Vue3 的打印需求,我發(fā)現(xiàn)自己雖然從事前端開發(fā)已有多年,但對如何實現(xiàn)自定義打印還沒有深入研究,一般都是找現(xiàn)成的庫來解決問題,借這次的機會研究了一下如何實現(xiàn)自定義打印,需要的朋友可以參考下

前言

最近接觸到了一個 Vue3 的打印需求,我發(fā)現(xiàn)自己雖然從事前端開發(fā)已有多年,但對如何實現(xiàn)自定義打印還沒有深入研究,一般都是找現(xiàn)成的庫來解決問題,借這次的機會研究了一下如何實現(xiàn)自定義打印。

在現(xiàn)在的前端開發(fā)中,打印是一個比較常見的功能,無論是在生成報表、下載發(fā)票還是其他用途上都會用到。 最基本的打印方式是直接打印整個頁面內(nèi)容,但在實際項目中,我們常常需要更靈活的打印功能,例如選擇性打印某個特定部分、提供打印預(yù)覽功能等。這就產(chǎn)生了許多 JavaScript 打印庫,如 print-jsvue-print-nb 等,這些庫不僅簡化了打印流程,還提供了豐富的自定義打印配置,滿足各種不同的打印需求。

由于當(dāng)前的項目基于 Vue3 進行開發(fā),我簡單調(diào)研后發(fā)現(xiàn) vue-print-nb 是 Vue 中常用的打印庫,同時其提供了適用于 Vue3 的 vue3-print-nb 庫。然而,在實際使用過程中,我發(fā)現(xiàn)該庫由于發(fā)布較早,且后期未繼續(xù)維護,因此在支持 Vue3 的一些特性方面有所欠缺,所以我在此基礎(chǔ)上進行了優(yōu)化和改造,來更好地滿足需求。

本文將詳細探討如何使用原生 JavaScript 實現(xiàn)自定義打印功能,同時講解對 vue3-print-nb 的改造和優(yōu)化。

實現(xiàn)自定義打印

實現(xiàn)自定義打印的核心思想是通過將要打印的內(nèi)容放入一個 iframe 或者新窗口中,然后調(diào)用 window.print() 方法進行打印。然而,需要考慮的問題遠不止這些,例如樣式還原、表單展示、以及canvas 的打印等。

實現(xiàn)思路

1. 確定打印區(qū)域

首先,需要確定用戶希望打印的頁面區(qū)域, 這通常通過用戶傳遞的一個選擇器或直接傳入的 DOM 元素來實現(xiàn):

  • 選擇器:通過傳遞一個 CSS 選擇器來標(biāo)識需要打印的元素。
  • DOM 元素:直接傳遞所需打印的 DOM 對象。

2. 克隆打印內(nèi)容

為了保證打印內(nèi)容與頁面其他部分隔離開來,通常會將需要打印的內(nèi)容深度克隆。這確保了打印時不會受到動態(tài)內(nèi)容變化的干擾,并且可以對其進行獨立處理:

/**
 * 拷貝需要打印的節(jié)點
 */
function cloneContent(selector) {
  const originalContent = document
	.querySelector(selector)
	.cloneNode(true);

  // 在克隆內(nèi)容中處理樣式和事件,比如刪除不需要的節(jié)點
  originalContent
	.querySelectorAll('.no-print')
	.forEach((el) => el.remove());

  return originalContent.outerHTML;
}

3. 創(chuàng)建新的上下文

為了確保打印內(nèi)容不受當(dāng)前頁面的影響,通常會創(chuàng)建一個新的窗口或 iframe 并復(fù)制打印內(nèi)容到新的上下文中。兩種常見的實現(xiàn)方式是:

  • 新窗口:打開一個新的瀏覽器窗口或標(biāo)簽頁,將克隆內(nèi)容放置其內(nèi),然后觸發(fā)打印。
  • Iframe:在當(dāng)前頁面中創(chuàng)建一個隱藏的 iframe,將打印內(nèi)容放入 iframe 后,再從 iframe 中觸發(fā)打印。
/**
 * 創(chuàng)建一個新的上下文(打印窗口)
 */
function createPrintIframe() {
    const iframe = document.createElement('iframe');  
	iframe.id = this.iframeId;  
	iframe.src = new Date().getTime().toString();  
	iframe.style.display = 'none';
	document.body.appendChild(iframe);
    return iframe;
}

/**
 * 將需要打印的內(nèi)容寫入新的上下文
 */
function write(printWindow, writeContent) {
  const iframeDocument = printWindow.contentDocument;
  iframeDocument.open();
  iframeDocument.write(`<!DOCTYPE html><html lang='zh'><head>${getHead()}</head><body>${writeContent}</body></html>`);
  iframeDocument.close();
}

4. 處理打印樣式

設(shè)計打印庫時,首先需要解決的問題就是如何在新的上下文中還原樣式,包括頁面原有樣式和用戶自定義樣式,以滿足復(fù)雜業(yè)務(wù)需求。

  • 獨立樣式:插入到新的打印文檔或上下文中,可以是內(nèi)聯(lián)樣式或者外部樣式表。
  • 打印媒體查詢:使用 @media print 媒體查詢,以定義僅在打印時生效的特殊樣式,使得打印和屏幕顯示效果獨立。
/**
 * 獲取 head
 */
function getHead() {
  // 獲取網(wǎng)頁原有的 css 鏈接
  const links = Array.from(document.querySelectorAll('link'))
	.filter((item) => item.href.includes('.css'))
	.map(
	  (item) =>
		`<link type="text/css" rel="stylesheet" href='${item.href}'>`
	)
	.join('');

  // 獲取頁面中的 style 標(biāo)簽
  const style = Array.from(document.styleSheets).reduce(
	(acc, styleSheet) => {
	  const rules = styleSheet.cssRules || styleSheet.rules;
	  if (!rules) return acc;
	  acc += Array.from(rules).reduce(
		(innerAcc, rule) => innerAcc + rule.cssText,
		''
	  );
	  return acc;
	},
	''
  );
  // ...省略處理用戶自定義引入的 css 樣式
  
  // 返回 head
  return `<title>自定義打印示例</title>${links}<style type="text/css">${style}</style>`;
}

5. 觸發(fā)打印

準備好打印內(nèi)容和樣式后,打印庫通過調(diào)用 window.print 方法來觸發(fā)打印。打印操作通常包含以下步驟:

  • 打印對話框:調(diào)用 window.print 來觸發(fā)系統(tǒng)打印對話框。
  • 清除上下文:打印完成后,關(guān)閉或銷毀用于打印的臨時窗口或 iframe,以避免內(nèi)存泄漏和資源占用。
/**
 * 觸發(fā)打印操作
 */
function triggerPrint(printWindow) {
  const iframeWin = printWindow?.contentWindow;
  iframeWin.addEventListener('load', () => {
	iframeWin.focus();
	iframeWin.print();
	iframe.remove();
  })
}

通過上述步驟,我們就可以靈活地控制打印內(nèi)容和樣式,并能提供良好的打印體驗和可定制化的功能。

綜合示例

通過以上步驟,我們可以實現(xiàn)一個簡易的自定義打印功能。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>自定義打印示例</title>
  <style>
    html,
    body {
      margin: 0;
    }

    body {
      padding: 20px
    }

    .printable {
      border: 1px solid #000;
      padding: 20px;
    }

    .gc {
      color: #0aff;
      font-size: 14px;
    }
  </style>
</head>

<body>
  <div class="printable" id="printableArea">
    <h1>葫蘆娃</h1>
    <p class="gc">葫蘆娃 葫蘆娃</p>
    <p class="gc">一根藤上七朵花</p>
    <p class="gc">風(fēng)吹雨打都不怕 </p>
    <p class="gc">啦啦啦啦</p>
    <p class="gc">叮當(dāng)當(dāng)咚咚當(dāng)當(dāng) 葫蘆娃</p>
    <p class="gc">叮當(dāng)當(dāng)咚咚當(dāng)當(dāng) 本領(lǐng)大</p>
    <p class="gc">啦啦啦啦</p>
    <p class="no-print">我是不需要打印的內(nèi)容,啦啦啦啦啦</p>
    <p class="no-print">我是不需要打印的內(nèi)容,啦啦啦啦啦</p>
    <p class="no-print">我是不需要打印的內(nèi)容,啦啦啦啦啦</p>
    <p class="no-print">我是不需要打印的內(nèi)容,啦啦啦啦啦</p>
  </div>
  <div>
    <button id="printBtn" style="margin-top: 10px">打印內(nèi)容</button>
  </div>

  <script>
    /**
     * 獲取 head
     */
    function getHead() {
      // 獲取網(wǎng)頁原有的 css 鏈接
      const links = Array.from(document.querySelectorAll('link'))
        .filter((item) => item.href.includes('.css'))
        .map(
          (item) =>
            `<link type="text/css" rel="stylesheet" href='${item.href}'>`
        )
        .join('');

      // 獲取頁面中的 style 標(biāo)簽
      const style = Array.from(document.styleSheets).reduce(
        (acc, styleSheet) => {
          const rules = styleSheet.cssRules || styleSheet.rules;
          if (!rules) return acc;
          acc += Array.from(rules).reduce(
            (innerAcc, rule) => innerAcc + rule.cssText,
            ''
          );
          return acc;
        },
        ''
      );

      // 返回 head
      return `<title>自定義打印示例</title>${links}<style type="text/css">${style}</style>`;
    }

    /**
     * 創(chuàng)建一個新的上下文(打印窗口)
     */
    function createPrintIframe() {
      const iframe = document.createElement('iframe');
      iframe.src = new Date().getTime().toString();
      iframe.style.display = 'none';
      document.body.appendChild(iframe);
      return iframe;
    }

    /**
     * 拷貝需要打印的節(jié)點
     */
    function cloneContent(selector) {
      const originalContent = document
        .querySelector(selector)
        .cloneNode(true);

      // 在克隆內(nèi)容中處理樣式和事件,比如刪除不需要的節(jié)點
      originalContent
        .querySelectorAll('.no-print')
        .forEach((el) => el.remove());

      return originalContent.outerHTML;
    }

    /**
     * 將需要打印的內(nèi)容寫入新的上下文
     */
    function write(printWindow, writeContent) {
      const iframeDocument = printWindow.contentDocument;
      iframeDocument.open();
      iframeDocument.write(`<!DOCTYPE html><html lang='zh'><head>${getHead()}</head><body>${writeContent}</body></html>`);
      iframeDocument.close();
    }

    /**
     * 觸發(fā)打印操作
     */
    function triggerPrint(printWindow) {
      const iframeWin = printWindow?.contentWindow;
      iframeWin.addEventListener('load', () => {
        iframeWin.focus();
        iframeWin.print();
        iframe.remove();
      })
    }

    /**
     * 打印元素
     */
    function printElement(selector) {
      const clonedContent = cloneContent(selector);
      const printWindow = createPrintIframe();
      write(printWindow, clonedContent);
      triggerPrint(printWindow);
    }

    document.getElementById('printBtn').addEventListener('click', () => {
      printElement('#printableArea');
    });
  </script>
</body>

</html>

實現(xiàn)效果如下:

通過上述示例,可以實現(xiàn)一個簡易的自定義打印功能。在實際業(yè)務(wù)中,我們可能需要支持更多復(fù)雜的需求,如外部 CSS 的引入,打印 canvas、表單等。

vue3-print-nb

vue3-print-nb 是一個用于 Vue3 的打印插件,提供了相對豐富的打印功能。但在實際使用過程中,我發(fā)現(xiàn)了一些不足之處,例如對 Vue3 setup 函數(shù)的支持不夠友好,以及不支持手動調(diào)用等。

優(yōu)化和改造

為了更好地支持 Vue3 setup 函數(shù),我對 vue3-print-nb 進行了以下改造:

  • 使用 TypeScript 重寫了整個庫,使得調(diào)用傳參時體驗更佳。
  • 增加了對 VuePrintNext 類的導(dǎo)出,允許手動調(diào)用打印方法,不再局限于指令調(diào)用。
  • 增強 Vue3 setup 支持,允許通過直接導(dǎo)入 vPrint 指令進行局部導(dǎo)入使用。
  • 支持設(shè)置指定的 CSS 選擇器來忽略打印部分內(nèi)容。
  • 支持通過 CSS 選擇器或手動傳入 DOM 節(jié)點進行局部打印。

此外,還修復(fù)了一些已知的 bug:

  • 背景色打印失效問題。
  • 通過 URL 打印時,有時無法觸發(fā)打印行為的問題。
  • 其他一些小問題。

新打印插件

我已將上述優(yōu)化和改造封裝成一個新的插件:vue-print-next,并已上傳至 GitHub:https://github.com/Alessandro-Pang/vue-print-next。你可以通過 npm 進行安裝和使用。

npm install vue-print-next

結(jié)語

通過本文,我們對自定義打印的實現(xiàn)原理有了比較全面的了解。盡管實現(xiàn)打印功能仍然依賴于 window.print API,但通過 window.open 或者 iframe 巧妙的將所需打印內(nèi)容進行隔離處理,可以實現(xiàn)靈活的打印功能。

以上就是Vue3自定義打印實現(xiàn)原理詳解的詳細內(nèi)容,更多關(guān)于Vue3自定義打印的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue3 vite配置跨域及不生效問題解決

    vue3 vite配置跨域及不生效問題解決

    這篇文章主要介紹了vue3 vite配置跨域以及不生效問題,本文給大家分享完美解決方案,需要的朋友可以參考下
    2023-07-07
  • vue-cli + sass 的正確打開方式圖文詳解

    vue-cli + sass 的正確打開方式圖文詳解

    本文通過圖文并茂的形式給大家介紹了vue-cli + sass 的正確打開方式,非常不錯,具有參考借鑒價值,需要的朋友參考下吧
    2017-10-10
  • vue實現(xiàn)的網(wǎng)易云音樂在線播放和下載功能案例

    vue實現(xiàn)的網(wǎng)易云音樂在線播放和下載功能案例

    這篇文章主要介紹了vue實現(xiàn)的網(wǎng)易云音樂在線播放和下載功能,結(jié)合具體實例形式分析了網(wǎng)易云音樂相關(guān)接口調(diào)用與操作技巧,需要的朋友可以參考下
    2019-02-02
  • 詳解Vue中的路由與多種守衛(wèi)

    詳解Vue中的路由與多種守衛(wèi)

    路由守衛(wèi)又稱導(dǎo)航守衛(wèi),指是路由跳轉(zhuǎn)前、中、后過程中的一些鉤子函數(shù),這篇文章主要介紹了Vue中的路由與多種守衛(wèi),需要的朋友可以參考下
    2023-02-02
  • 使用element-ui設(shè)置table組件寬度(width)為百分比

    使用element-ui設(shè)置table組件寬度(width)為百分比

    這篇文章主要介紹了使用element-ui設(shè)置table組件寬度(width)為百分比方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • Vue.js基礎(chǔ)知識匯總

    Vue.js基礎(chǔ)知識匯總

    Vue.js 專注于 MVVM 模型的 ViewModel 層。它通過雙向數(shù)據(jù)綁定把 View 層和 Model 層連接了起來。Vue.js和其他庫相比是一個小而美的庫,作者的主要目的是通過一個盡量簡單的 API 產(chǎn)生可反映的數(shù)據(jù)綁定和可組合的視圖組件,感覺作者的思路非常清晰。
    2016-04-04
  • Vue3通過hooks方式封裝節(jié)流和防抖的代碼詳解

    Vue3通過hooks方式封裝節(jié)流和防抖的代碼詳解

    vue3 中的 hooks 就是函數(shù)的一種寫法,就是將文件的一些單獨功能的js代碼進行抽離出來,放到單獨的js文件中,或者說是一些可以復(fù)用的公共方法/功能,本文給大家介紹了Vue3通過hooks方式封裝節(jié)流和防抖,需要的朋友可以參考下
    2024-10-10
  • Vue3中的unref詳解與常見使用方法

    Vue3中的unref詳解與常見使用方法

    這篇文章主要給大家介紹了關(guān)于Vue3中unref詳解與常見使用的相關(guān)資料,Vue3中的unref是一個實用工具,用于簡化訪問響應(yīng)式引用和普通值的過程,通過自動判斷類型,unref可以處理任何類型的值,從而使代碼更加簡潔和易讀,需要的朋友可以參考下
    2024-11-11
  • vscode 使用Prettier插件格式化配置使用代碼詳解

    vscode 使用Prettier插件格式化配置使用代碼詳解

    這篇文章主要介紹了vscode 使用Prettier插件格式化配置使用,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • Vue開發(fā)配置tsconfig.json文件的實現(xiàn)

    Vue開發(fā)配置tsconfig.json文件的實現(xiàn)

    tsconfig.json文件中指定了用來編譯這個項目的根文件和編譯選項,本文就來介紹一下Vue開發(fā)配置tsconfig.json文件的實現(xiàn),感興趣的可以了解一下
    2023-08-08

最新評論