整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          一個vue頁面復用方案

          問大家一個問題,曾經的你是否也遇到過,一個項目中有好幾個頁面長得基本相同,但又差那么一點,想用 vue extends 繼承它又不能按需繼承html模板部分,恰好 B 頁面需要用的 A 頁面 80% 的模板,剩下的 20% 由 B 頁面自定義,舉個栗子:

          我們假設這是兩個頁面,B頁面比A頁面多了個p標簽,剩余的東西都一樣,難道僅僅是因為這一個 p標簽就要重新寫一份模板嗎?相信大部分伙伴解決方式是把公共部分抽成一個組件來用,這是一個好的做法。沒錯,但是來了,老板讓你在 標題1、標題2下面分別插入一段內容,這會兒你是不是頭大了?難道只能重寫一份了嗎?當然不是,來開始我們的填坑之路~(當你的業務能用插槽或者組件抽離的方式固然更好,以下內容僅針對當你項目達到一定體量,vue老三套難以處理的情況下采用)

          準備工作

          準備以下工具包:

          • node-html-parser: 將html生成dom樹 官網
          npm install --save node-html-parser
          


          思路

          1. 子頁面提供繼承的父頁面的路徑,如下:
          <template extend="./xxx.vue">
          </template>
          


          1. 子頁面需要通過一個自定義標簽(假設是 extend)的方式,來決定如何拓展父頁面,如下就應該是一個替換的操作,它最少應該具備拓展類型 type 與目標節點 target 屬性。
          <template extend="./xxx.vue">
            <div>
              <extend type="replace" target="#div_1">
                <a>通過replace替換掉父頁面下id為div_1的元素 </a>
              </extend>
            </div>
          </template>
          


          最終它生成的應該是除了 id 為 div_1元素被<a>通過replace替換掉父頁面下id為div_1的元素 </a>替換掉之外,剩下的全部和xxx.vue一樣的頁面。

          梳理需求點

          子頁面繼承父頁面既可以完全繼承,也可以通過某種方式以父頁面為基板,對其進行增、刪、改。方便理解,我們先定義一個自定義標簽 extend,子頁面通過該標簽對其繼承的頁面操刀動手術,為了實現一個比較完善的繼承拓展,extend 標簽需要具備以下屬性:

          Extend Attributes

          參數

          說明

          類型

          可選值

          type

          指定擴展類型

          string

          insert(插入)、replace(替換)、remove(移除)、append(向子集追加)

          position

          指定插入的位置(僅在 type 取值 insert 時生效)

          string

          before(目標前)、after(目標后)

          指定插入的位置(僅在 type 取值 append 時生效,用于指定插入成為第幾個子節點)

          number

          -

          target

          指定擴展的目標

          string


          實現需求

          新建一個vue2的項目,項目結構如下:

          我們的繼承拓展通過自定義loader在編譯的時候實現,進入到src/loader/index.js

          const extend = require('./extend');
          module.exports = function (source) {
               // 當前模塊目錄
               const resourcePath = this.resourcePath;
               // 合并
               const result = new extend(source, resourcePath).mergePage();
               // console.log('result :>> ', result);
               // 返回合并后的內容
               this.callback(null, result);
          };
          


          實現繼承拓展主要邏輯代碼:src/loader/extend.js

          const parser = require('node-html-parser');
          const fs = require('fs');
          const pathFile = require('path');
          /**
           * 通過node-html-parser解析頁面文件重組模板
           * @param {String} source 頁面內容
           * @param {String} resourcePath 頁面目錄
           * @returns {String} 重組后的文件內容
           */
          class Extend {
              constructor(source, resourcePath) {
                  this.source = source;
                  this.resourcePath = resourcePath;
              }
              // 合并頁面
              mergePage() {
                  // 通過node-html-parser解析模板文件
                  const pageAst = parser.parse(this.source).removeWhitespace();
                  // 獲取template標簽extend屬性值
                  const extendPath = pageAst.querySelector('template').getAttribute('extend');
                  if (!extendPath) {
                      return pageAst.toString();
                  }
                  // extendPath文件內容
                  const extendContent = fs.readFileSync(pathFile.resolve(pathFile.dirname(this.resourcePath), extendPath), 'utf-8');
                  // extendContent文件解析
                  const extendAst = parser.parse(extendContent).removeWhitespace();
                  // 獲取頁面文件標簽為extend的元素
                  const extendElements = pageAst.querySelectorAll('extend');
          
                  extendElements.forEach((el) => {
                      // 獲取對應屬性值
                      const type = el.getAttribute('type');
                      const target = el.getAttribute('target');
                      const position = parseInt(el.getAttribute('position'));
          
                      // 匹配模板符合target的元素
                      let templateElements = extendAst.querySelectorAll(target);
          
                      // type屬性為insert
                      if (type === 'insert') {
                          templateElements.forEach((tel) => {
                              // 通過position屬性判斷插入位置 默認為after
                              if (position === 'before') {
                                  el.childNodes.forEach((child) => {
                                      tel.insertAdjacentHTML('beforebegin', child.toString());
                                  });
                              } else {
                                  el.childNodes.forEach((child) => {
                                      tel.insertAdjacentHTML('afterend', child.toString());
                                  });
                              }
                          });
                      }
                      // type屬性為append
                      if (type === 'append') {
                          templateElements.forEach((tel) => {
                             const elNodes = el.childNodes;
                             let tlNodes = tel.childNodes;
                             const len = tlNodes.filter((node) => node.nodeType === 1 || node.nodeType === 3).length;
                              // 未傳position屬性或不為數字、大于len、小于0時默認插入到最后
                              if(isNaN(position) || position > len || position <= 0){
                                  elNodes.forEach((child) => {
                                      tel.insertAdjacentHTML('beforeend', child.toString());
                                  });
                              }else {
                                  tlNodes =  [...tlNodes.slice(0, position-1), ...elNodes, ...tlNodes.slice(position-1)]
                                  tel.set_content(tlNodes);
                              }
                          });
                      }
                      // type屬性為replace
                      if (type === 'replace') {
                          templateElements.forEach((tel) => {
                              tel.replaceWith(...el.childNodes);
                          });
                      }
                      // type屬性為remove
                      if (type === 'remove') {
                          templateElements.forEach((tel) => {
                              tel.remove();
                          });
                      }
                  });
                  // 重組文件內容
                  const template = extendAst.querySelector('template').toString();
                  const script = pageAst.querySelector('script').toString();
                  const style = extendAst.querySelector('style').toString() + pageAst.querySelector('style').toString() 
                  return`${template}${script}${style}`
              }
          
          }
          module.exports = Extend;
          


          好的,自定義loader已經編寫完成,在vue.config.js里面配置好我們的loader

          const { defineConfig } = require('@vue/cli-service')
          module.exports = defineConfig({
            configureWebpack: {
              module: {
                rules: [
                  {
                    test: /\.vue$/,
                    use: [
                      {
                        loader: require.resolve('./src/loader'),
                      },
                    ],
                  },
                ],
              },
            },
          })
          
          


          接下來我們嘗試編寫A頁面和B頁面:

          A.vue:

          <template>
            <div class="template">
                <div id="div_1" class="div">父頁面的div_1</div>
                <div id="div_2" class="div">父頁面的div_2</div>
                <div id="div_3" class="div">父頁面的div_3</div>
                <div id="div_4" class="div">父頁面的div_4</div>
                <div id="div_5" class="div">父頁面的div_5</div>
                <div id="div_6" class="div">父頁面的div_6</div>
                <div id="div_7" class="div">父頁面的div_7</div>
                <div id="div_8" class="div">父頁面的div_8</div>
            </div>
          </template>
          <script>
          export default {
            name: 'COM_A',
            props: {
              msg: String
            }
          }
          </script>
          <style scoped>
          .div {
            color: #42b983;
            font-size: 1.5em;
            margin: 0.5em;
            padding: 0.5em;
            border: 2px solid #42b983; 
            border-radius:  0.2em;
          }
          </style>
          


          B.vue:

          <template extend="./A.vue">
            <div>
              <extend type="insert" target="#div_1" position="after">
                <div id="div_child" class="div">子頁面的div_5</div>
              </extend>
              <extend type="append" target="#div_3" position="2">
                <a> 子頁面通過append插入的超鏈接 </a>
              </extend>
            </div>
          </template>
          <script>
          import A from './A.vue'
          export default {
            name: 'COM_B',
            extends: A,//繼承業務邏輯代碼
            props: {
              msg: String
            }
          }
          </script>
          <style scoped>
          #div_child {
            color: #d68924;
            font-size: 1.5em;
            margin: 0.5em;
            padding: 0.5em;
            border: 2px solid #d68924;
          }
          a {
            color: blue;
            font-size: 0.7em;
          }
          </style>
          


          我們在App.vue下引入B.vue

          <template>
            <div id="app">
              <B/>
            </div>
          </template>
          <script>
          import B from './components/B.vue'
          export default {
            name: 'App',
            components: {
              B
            }
          }
          </script>
          <style>
          #app {
            font-family: Avenir, Helvetica, Arial, sans-serif;
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
            text-align: center;
            color: #2c3e50;
            margin-top: 60px;
          }
          </style>
          


          當我們執行編譯的時候,實際上B.vue的編譯結果如下:

          <template>
            <div class="template">
              <div id="div_1" class="div">父頁面的div_1</div>
              <div id="div_child" class="div">子頁面的div_5</div>
              <div id="div_2" class="div">父頁面的div_2</div>
              <div id="div_3" class="div">
                父頁面的div_3
                <a> 子頁面通過append插入的超鏈接 </a>
              </div>
              <div id="div_4" class="div">父頁面的div_4</div>
              <div id="div_5" class="div">父頁面的div_5</div>
              <div id="div_6" class="div">父頁面的div_6</div>
              <div id="div_7" class="div">父頁面的div_7</div>
              <div id="div_8" class="div">父頁面的div_8</div>
            </div>
          </template>
          <script>
          import A from './A.vue'
          export default {
            name: 'COM_B',
            extends: A,//繼承業務邏輯代碼
            props: {
              msg: String
            }
          }
          </script>
          <style scoped>
          .div {
            color: #42b983;
            font-size: 1.5em;
            margin: 0.5em;
            padding: 0.5em;
            border: 2px solid #42b983;
            border-radius: 0.2em;
          }
          </style>
          <style scoped>
          #div_child {
            color: #d68924;
            font-size: 1.5em;
            margin: 0.5em;
            padding: 0.5em;
            border: 2px solid #d68924;
          }
          
          a {
            color: blue;
            font-size: 0.7em;
          }
          </style>
          


          注意我們在B.vue使用了extends繼承了組件A,這里是為了能復用業務邏輯代碼,最后我們運行代碼,頁面輸出為:

          結語

          在真實的項目當中,我們遇到大量重復的頁面但是又有小區別的頁面,是可以通過這種方式減少我們的代碼量,當然也許有更好的辦法,也希望大伙能提出寶貴的建議。

          最后引用一下 @XivLaw 老哥的評論:有很多人說通過cv就能解決,但是當你的業務有成千上萬個頁面是趨同,并且具有相同的基本功能,當界面需要統一調整或者需要進行ui統一管控的時候,cv就成了你的累贅了。 也有朋友說通過組件化和插槽解決,組件化是一個不錯的方案,但是當成千上萬個趨同的界面存在時,插槽并一定能覆蓋所有的業務定制化。 使不使用這種方式,主要看你的業務。

          直白一點說就是:我現在有一千個頁面幾乎一樣,有的頁面是頭部多一點東西,有的是底部,有的是某個按鈕旁邊多一個按鈕,有的是輸入框之間多個輸入框,ui或者界面或者同時需要添加固定功能,需要調整的時候,這一千個頁面要怎么調?


          作者:小小小小_柏
          鏈接:https://juejin.cn/post/7347973138787467274

          于經常需要處理PDF文件的用戶來說,合并多個PDF文件是一項重要且經常使用的操作技能。為了更好地保存、管理和使用這些文件,他們希望能夠很容易地將多個PDF文件合并成一個。同時,他們也希望合并后的PDF文件能夠方便地發送給他人,從而提高工作效率。本期我們將分享幾種合并PDF的方法。讓我們來看看。

          方法1、另存功能

          PDF文件是我們工作和生活中常見的文件格式。有時,為了便于管理和共享,我們需要將多個PDF文件合并成一個文件。如果目前需要合并的PDF文件數量很大,比如只有5個以內,那么此時可以使用辦公內置的另存功能來完成。也就是說,使用Word文檔依次打開需要合并的PDF文件,在頂部的“文件”菜單中找到“另存為”的選項,另存為“.pdf“格式就可以了。

          方法2、手工編輯

          如果需要同時引用或處理多個PDF文件,可以節省重復操作步驟。無需多次打開、關閉或復制粘貼文件,可以提高工作效率,降低操作失誤的可能性。通過將多個PDF文件合并成一個文件,可以減少文件的數量,從而減少存儲空間的占用。特別是對于大量的小文件,合并后可以節省寶貴的磁盤空間。同樣,如果需要合并的PDF文件數量較少,可以選擇專門的編輯器,將所需內容復制到一個文件中,以滿足合并要求。

          方法3、高效率軟件合并文件

          假如目前你需要合并大量的PDF文件,并且對合并文件有很高的要求,那么在這個時候選擇一個高效率的軟件進行操作,這是目前最好的方法。舉例來說,啟源PDF轉換器軟件,它是一個專業的PDF處理軟件,提供了一個簡單快捷的PDF合并功能。在合并PDF文件時,您可以很容易地將多個PDF文件合并為一個整體,從而解決您的煩惱。

          軟件的操作方法比較簡單,只需直接在計算機上啟動“啟源PDF轉換”軟件,在界面上選擇“PDF合并”功能,然后添加需要合并的PDF文件。您可以通過拖動或點擊按鈕上傳文件,并調整文件的順序和頁面設置。所有設置完成后,只需點擊“開始合并”按鈕,這些PDF文件就會很快合并成一個整體。

          方法4、在線平臺合并文件

          除了上面分享的三種方法外,如果您想在計算機上合并PDF文件,還可以選擇一些在線平臺進行操作,例如iLovePDF工具。這是一個國外的PDF在線解決方案,其合并文件的效果也挺不錯。您可以在計算機上打開平臺網頁,選擇對應的功能,并批量添加需要合并的PDF文件。點擊“開始合并”解決問題。

          關于如何將PDF文件合并的方法,小編就與大家分享到這里了,希望對大家有所幫助。

          備好獎狀的獲獎名作為:Word獎狀模板的數據源(含表頭-表格模板對應的信息)哦!如:

          打開word制作好需要制作的獎狀模板-郵件選項卡-選擇收件人-選擇使用現有列表-插入合并域(表頭中對應的內容)-預覽結果-完全并合并-保存-打印就可以批量制作啦 !!!

          該功能還可以和郵箱聯合使用用于批量相同格式不同填充內容的信息發放哦——比如offer發放等:大致步驟如下(暫時沒有示例。下次有了更新上)

          還是先將word中的offer格式調整好(注意offer發放中不同人的姓名,薪資待遇等方面要做到帶表頭的數據表格中)-郵件-選擇收件人使用現有列表-編輯域-預覽-完成并合并中郵件-郵件(郵箱配合outlook使用,html格式就是word中看見的,附件是作為郵件附件發送)-需要在outlook郵箱顯示-outlook設為默認的郵箱發送


          主站蜘蛛池模板: 国产一区二区三区电影| 精品一区精品二区| 国产精品va无码一区二区| 久久精品无码一区二区WWW| 日本无码一区二区三区白峰美| 精品日本一区二区三区在线观看| 一区国产传媒国产精品| 久久亚洲中文字幕精品一区四| 亚洲日本久久一区二区va| 日韩精品无码一区二区三区AV| 在线观看精品视频一区二区三区| 亚洲一区AV无码少妇电影☆| 日韩国产一区二区| 波多野结衣在线观看一区二区三区| 亚洲欧洲精品一区二区三区| 国产成人一区二区三区视频免费 | 欧洲精品一区二区三区在线观看| 一区二区三区影院| 精品中文字幕一区在线| 精品天海翼一区二区| 在线视频一区二区日韩国产| 无码中文字幕乱码一区| 国模无码一区二区三区| 波多野结衣AV一区二区三区中文| 亚洲av乱码中文一区二区三区| 夜夜高潮夜夜爽夜夜爱爱一区| 51视频国产精品一区二区| 无码人妻一区二区三区一| 99偷拍视频精品一区二区| 精品一区二区三区无码免费直播| 无码人妻一区二区三区免费视频| 美女视频免费看一区二区| 人妻少妇AV无码一区二区| 免费精品一区二区三区第35| 精品福利一区二区三区免费视频| 无码人妻精品一区二区蜜桃网站 | 无码人妻精品一区二区蜜桃百度 | 国产一区二区精品久久岳| 亚洲欧洲一区二区| 国产一区二区精品久久凹凸| 无码精品久久一区二区三区 |