整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          博客網(wǎng)站添加復(fù)制轉(zhuǎn)載提醒彈窗Html代碼

          站如果是完全禁止右鍵(復(fù)制、另存為等)操作,對(duì)用戶來說體驗(yàn)感會(huì)降低,但是又不希望自己的原創(chuàng)內(nèi)容直接被copy,今天飛飛和你們分享幾行復(fù)制轉(zhuǎn)載提醒彈窗Html代碼。


          效果展示:

          復(fù)制以下代碼,將其放在網(wǎng)站footer.php或者h(yuǎn)eader.php任意底部位置即可。


          <!-- 復(fù)制提醒開始 -->

          <link rel="stylesheet" href="https://cdn.bootcss.com/sweetalert/1.1.3/sweetalert.min.css" />

          <script type="text/javascript" src="https://cdn.bootcss.com/sweetalert/1.1.3/sweetalert.min.js"></script>


          <script>

          document.body.oncopy = function() {

          swal("復(fù)制成功!", "若要轉(zhuǎn)載請(qǐng)保留原文鏈接,感謝支持!", "success");

          };

          </script>

          <!-- 復(fù)制提醒結(jié)束 -->


          通過這幾行代碼,我們可以在保護(hù)原創(chuàng)內(nèi)容的同時(shí),不影響用戶體驗(yàn)。當(dāng)然,除了添加復(fù)制轉(zhuǎn)載提醒彈窗之外,我們還可以通過其他方式來保護(hù)原創(chuàng)內(nèi)容,例如添加水印、限制轉(zhuǎn)載等等。總之,在權(quán)衡用戶體驗(yàn)和版權(quán)保護(hù)時(shí),我們需要尋找一個(gè)平衡點(diǎn),既能夠保護(hù)自己的權(quán)益,又不會(huì)影響用戶的使用體驗(yàn)。


          感謝您的閱讀,服務(wù)器大本營助您成為更專業(yè)的服務(wù)器管理員!



          在前面

          今年國慶假期終于可以憋在家里了不用出門了,不用出去看后腦了,真的是一種享受。這么好的光陰怎么浪費(fèi),睡覺、吃飯、打豆豆這怎么可能(耍多了也煩),完全不符合我們程序員的作風(fēng),趕緊起來把文章寫完。

          這篇文章比較基礎(chǔ),在國慶期間的業(yè)余時(shí)間寫的,這幾天又完善了下,力求把更多的前端所涉及到的關(guān)于文件上傳的各種場(chǎng)景和應(yīng)用都涵蓋了,若有疏漏和問題還請(qǐng)留言斧正和補(bǔ)充。

          自測(cè)讀不讀

          以下是本文所涉及到的知識(shí)點(diǎn),break or continue ?

          • 文件上傳原理
          • 最原始的文件上傳
          • 使用 koa2 作為服務(wù)端寫一個(gè)文件上傳接口
          • 單文件上傳和上傳進(jìn)度
          • 多文件上傳和上傳進(jìn)度
          • 拖拽上傳
          • 剪貼板上傳
          • 大文件上傳之分片上傳
          • 大文件上傳之?dāng)帱c(diǎn)續(xù)傳
          • node 端文件上傳

          原理概述

          原理很簡(jiǎn)單,就是根據(jù) http 協(xié)議的規(guī)范和定義,完成請(qǐng)求消息體的封裝和消息體的解析,然后將二進(jìn)制內(nèi)容保存到文件。

          我們都知道如果要上傳一個(gè)文件,需要把 form 標(biāo)簽的enctype設(shè)置為multipart/form-data,同時(shí)method必須為post方法。

          那么multipart/form-data表示什么呢?

          multipart互聯(lián)網(wǎng)上的混合資源,就是資源由多種元素組成,form-data表示可以使用HTML Forms 和 POST 方法上傳文件,具體的定義可以參考RFC 7578。

          multipart/form-data 結(jié)構(gòu)

          看下 http 請(qǐng)求的消息體



          • 請(qǐng)求頭:

          Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDCntfiXcSkPhS4PN 表示本次請(qǐng)求要上傳文件,其中boundary表示分隔符,如果要上傳多個(gè)表單項(xiàng),就要使用boundary分割,每個(gè)表單項(xiàng)由———XXX開始,以———XXX結(jié)尾。

          • 消息體- Form Data 部分

          每一個(gè)表單項(xiàng)又由Content-Type和Content-Disposition組成。

          Content-Disposition: form-data 為固定值,表示一個(gè)表單元素,name 表示表單元素的 名稱,回車換行后面就是name的值,如果是上傳文件就是文件的二進(jìn)制內(nèi)容。

          Content-Type:表示當(dāng)前的內(nèi)容的 MIME 類型,是圖片還是文本還是二進(jìn)制數(shù)據(jù)。

          解析

          客戶端發(fā)送請(qǐng)求到服務(wù)器后,服務(wù)器會(huì)收到請(qǐng)求的消息體,然后對(duì)消息體進(jìn)行解析,解析出哪是普通表單哪些是附件。

          可能大家馬上能想到通過正則或者字符串處理分割出內(nèi)容,不過這樣是行不通的,二進(jìn)制buffer轉(zhuǎn)化為string,對(duì)字符串進(jìn)行截取后,其索引和字符串是不一致的,所以結(jié)果就不會(huì)正確,除非上傳的就是字符串。

          不過一般情況下不需要自行解析,目前已經(jīng)有很成熟的三方庫可以使用。

          至于如何解析,這個(gè)也會(huì)占用很大篇幅,后面的文章在詳細(xì)說。

          最原始的文件上傳

          使用 form 表單上傳文件

          在 ie時(shí)代,如果實(shí)現(xiàn)一個(gè)無刷新的文件上傳那可是費(fèi)老勁了,大部分都是用 iframe 來實(shí)現(xiàn)局部刷新或者使用 flash 插件來搞定,在那個(gè)時(shí)代 ie 就是最好用的瀏覽器(別無選擇)。

          DEMO



          這種方式上傳文件,不需要 js ,而且沒有兼容問題,所有瀏覽器都支持,就是體驗(yàn)很差,導(dǎo)致頁面刷新,頁面其他數(shù)據(jù)丟失。

          HTML

           <form method="post" action="http://localhost:8100" enctype="multipart/form-data">
          
                  選擇文件:
                      <input type="file" name="f1"/> input 必須設(shè)置 name 屬性,否則數(shù)據(jù)無法發(fā)送<br/>
          <br/>
                      標(biāo)題:<input type="text" name="title"/><br/><br/><br/>
          
                  <button type="submit" id="btn-0">上 傳</button>
          
          </form>
          
          復(fù)制代碼

          文件上傳接口

          服務(wù)端文件的保存基于現(xiàn)有的庫koa-body結(jié)合 koa2實(shí)現(xiàn)服務(wù)端文件的保存和數(shù)據(jù)的返回。

          在項(xiàng)目開發(fā)中,文件上傳本身和業(yè)務(wù)無關(guān),代碼基本上都可通用。

          在這里我們使用koa-body庫來實(shí)現(xiàn)解析和文件的保存。

          koa-body 會(huì)自動(dòng)保存文件到系統(tǒng)臨時(shí)目錄下,也可以指定保存的文件路徑。



          然后在后續(xù)中間件內(nèi)得到已保存的文件的信息,再做二次處理。

          • ctx.request.files.f1 得到文件信息,f1為input file 標(biāo)簽的 name
          • 獲得文件的擴(kuò)展名,重命名文件

          NODE

          /**
           * 服務(wù)入口
           */
          var http = require('http');
          var koaStatic = require('koa-static');
          var path = require('path');
          var koaBody = require('koa-body');//文件保存庫
          var fs = require('fs');
          var Koa = require('koa2');
          
          var app = new Koa();
          var port = process.env.PORT || '8100';
          
          var uploadHost= `http://localhost:${port}/uploads/`;
          
          app.use(koaBody({
              formidable: {
                  //設(shè)置文件的默認(rèn)保存目錄,不設(shè)置則保存在系統(tǒng)臨時(shí)目錄下  os
                  uploadDir: path.resolve(__dirname, '../static/uploads')
              },
              multipart: true // 開啟文件上傳,默認(rèn)是關(guān)閉
          }));
          
          //開啟靜態(tài)文件訪問
          app.use(koaStatic(
              path.resolve(__dirname, '../static') 
          ));
          
          //文件二次處理,修改名稱
          app.use((ctx) => {
              var file = ctx.request.files.f1;//得道文件對(duì)象
              var path = file.path;
              var fname = file.name;//原文件名稱
              var nextPath = path+fname;
              if(file.size>0 && path){
                  //得到擴(kuò)展名
                  var extArr = fname.split('.');
                  var ext = extArr[extArr.length-1];
                  var nextPath = path+'.'+ext;
                  //重命名文件
                  fs.renameSync(path, nextPath);
              }
              //以 json 形式輸出上傳文件地址
              ctx.body = `{
                  "fileUrl":"${uploadHost}${nextPath.slice(nextPath.lastIndexOf('/')+1)}"
              }`;
          });
          
          /**
           * http server
           */
          var server = http.createServer(app.callback());
          server.listen(port);
          console.log('demo1 server start ......   ');
          復(fù)制代碼

          CODE

          https://github.com/Bigerfe/fe-learn-code/

          、前言

          本文將敘述web端消息推送是什么,它的原理、項(xiàng)目實(shí)戰(zhàn)、遇到的問題以及解決方法。

          二、消息推送原理

          1、什么是消息推送

          web端消息推送用一句話解釋就是:服務(wù)端向?yàn)g覽器客戶端發(fā)送了一條消息,客戶端收到推送消息后以通知的形式展示出來,用戶點(diǎn)擊消息之后能進(jìn)行一系列后續(xù)的操作。這個(gè)能力讓我們可以從服務(wù)端向用戶推送各類消息并引導(dǎo)用戶觸發(fā)相應(yīng)交互,例如:

          • 游戲新活動(dòng)推廣,推送消息給玩家,點(diǎn)擊即進(jìn)入活動(dòng)頁面。
          • 玩家很久沒有進(jìn)入游戲了,推送消息告知玩家這段時(shí)間游戲的更新,召回玩家

          2、消息推送與通知提醒流程

          從1中可知,消息推送包括兩部分的功能:消息推送通知提醒,整個(gè)流程有些復(fù)雜,因此,在進(jìn)入具體技術(shù)細(xì)節(jié)之前,我們先了解一下整個(gè)流程與相關(guān)概念

          client:就是我們的瀏覽器客戶端
          Push Service:專門的Push服務(wù),可以認(rèn)為是一個(gè)第三方服務(wù),目前chrome與firefox都有自己的Push Service Service。理論上只要瀏覽器支持,可以使用任意的Push Service
          application server:指我們自己的后端服務(wù)


          subscribe階段
          在訂閱階段,首先瀏覽器會(huì)詢問用戶是否允許通知,只有在用戶允許后,才能進(jìn)行后面的操作。這一步不在上圖的流程中,這其實(shí)是瀏覽器中的策略。客戶端如果愿意接收服務(wù)端的推送,會(huì)利用service worker的的接口向 push service 發(fā)起一個(gè)訂閱。push service是提供中間人服務(wù)的服務(wù)器,由瀏覽器負(fù)責(zé),在使用時(shí)對(duì)普通開發(fā)者基本是透明的。

          push service 返回 subscription

          客戶端發(fā)起subscribe后,要收到返回的subscription對(duì)象才算成功。得到的subscription會(huì)被接著傳給應(yīng)用服務(wù)器

          向服務(wù)器發(fā)送 subscription
          客戶端得到的 subscription 要發(fā)給服務(wù)器。這樣服務(wù)器在推送消息時(shí),才能向 push service 證明自己和客戶端是有建立合法推送約定的

          服務(wù)器向客戶端推送消息
          服務(wù)器是不能直接向客戶端發(fā)送消息的,它只是將客戶端給的subscription對(duì)象和要推送的消息一起發(fā)給 push service,然后由push service 確認(rèn)了 它 與客戶端的有效訂閱關(guān)系后,由 push service 代為推送

          push serivce 推送
          push serivce 再驗(yàn)證過服務(wù)器和客戶端的訂閱關(guān)系后,會(huì)將服務(wù)器的消息推送給客戶端。客戶端接收到消息后,以通知的形式展示出來。

          3、具體實(shí)現(xiàn)

          以下分別具體介紹獲取通知授權(quán)、訂閱消息、推送消息、接受消息并展示通知的實(shí)現(xiàn)

          獲取授權(quán)

          在訂閱消息之前,瀏覽器需要得到用戶授權(quán),同意后才能使用消息推送服務(wù)。通知授權(quán)類似如下圖:

          顯示以上對(duì)話框有兩種方式:

          (1)在訂閱之前先獲取用戶授權(quán),使用Notification.requestPermission方法

          Notification.requestPermission().then((permission) => {
           console.log("permission=", permission);
           if (permission === "granted") {
              //do something
           } else if (permission === "denied") {
              //do something
           } else {
              //do something
           }
          });

          Notification.requestPermission()方法執(zhí)行會(huì)返回授權(quán)結(jié)果,主要有g(shù)ranted(已授權(quán))、denied(被拒絕)、default(被關(guān)閉)這3中狀態(tài)。只有當(dāng)授權(quán)結(jié)果為granted時(shí),當(dāng)前網(wǎng)頁才能進(jìn)行后面訂閱推送服務(wù)和通知消息這兩個(gè)步驟。

          (2)如果不選擇使用方法(1),在正式訂閱時(shí)瀏覽器也會(huì)自動(dòng)彈出,對(duì)于開發(fā)者而言不需要顯式調(diào)用

          值得注意的是,當(dāng)用戶允許或者拒絕授權(quán)后,后續(xù)都不會(huì)重復(fù)詢問。想要更改這個(gè)設(shè)置,在 Chrome 地址欄左側(cè)網(wǎng)站信息中如下手動(dòng)修改:

          訂閱消息

          訂閱消息的具體實(shí)現(xiàn)步驟如下:

          (1)注冊(cè) Service Worker

          (2)使用 pushManager 添加訂閱,瀏覽器向推送服務(wù)發(fā)送請(qǐng)求,其中傳遞參數(shù)對(duì)象包含兩個(gè)屬性:

          • userVisibleOnly,不允許靜默的推送,所有推送都對(duì)用戶可見,所以值為true
          • applicationServerKey,服務(wù)器生成的公鑰

          (3)得到推送服務(wù)成功響應(yīng)后,瀏覽器將推送服務(wù)返回的 subscribe,向后端服務(wù)器發(fā)送這個(gè)subscribe并存儲(chǔ)

          代碼參考如下:

          // 注冊(cè)Service Worker
          if ("serviceWorker" in navigator && "PushManager" in window) {
            navigator.serviceWorker
              .register("./service-worker.js")
              .then(function (reg) {});
            navigator.serviceWorker.ready.then(function (reg) {
              subscribe(reg);
            });
          }
          
          
          
          
          // 發(fā)起訂閱
          function subscribe(serviceWorkerReg) {
            serviceWorkerReg.pushManager
              .subscribe({ userVisibleOnly: true, applicationServerKey: "xxxx" })
              .then(function (subscribe) {
                //獲取到的subscribe發(fā)送推送給后端存儲(chǔ)起來
                sendToServer(subscribe);
              })
              .catch(function () {
                // 用戶拒絕了訂閱請(qǐng)求
                if (Notification.permission === "denied") {
                  //do something
                }
              });
          }

          插播一個(gè)生成公鑰私鑰的方法:可以用web push的Node包生成:

          (1)npm install web-push --save

          (2)每運(yùn)行一次就會(huì)生成一對(duì)新的密鑰對(duì),公鑰私鑰只要能配套就好,公鑰在瀏覽器端使用,用來生成subscribe,私鑰在服務(wù)端使用,用來發(fā)Push,代碼如下:

          const webpush = require('web-push');
          //VAPID keys should only be generated only once.
          const vapidKeys = webpush.generateVAPIDKeys();
          console.log(vapidKeys.publicKey, vapidKeys.privateKey);

          推送消息

          當(dāng)服務(wù)器想推送消息給用戶時(shí),可以用FCM提供的web push的庫發(fā)送推送,它支持多種語言,包括Node.js/PHP等版本。用Node.js可以這樣發(fā)Push:

          const webpush = require("web-push");
          // 從數(shù)據(jù)庫取出用戶的subsciption,例如取出的subsciption如下
          const pushSubscription = {
            endpoint: "xxx",
            expirationTime: null,
            keys: {
              p256dh: "xxxx",
              auth: "xxxx",
            },
          };
          // push的數(shù)據(jù)
          const payload = {
            title: "消息標(biāo)題",
            body: "點(diǎn)開看看吧",
            icon: "xxx.png",
            data: { url: "https://www.xxx.com" },
          };
          webpush.sendNotification(pushSubscription, JSON.stringify(payload));

          推送服務(wù)接收到了服務(wù)器的調(diào)用請(qǐng)求,向設(shè)備推送消息。

          接收消息并顯示通知

          要想在瀏覽器中接收推送信息,只需在Service Worker中監(jiān)聽push事件即可,接收到消息之后調(diào)用通知api展示通知:

          this.addEventListener("push", function (event) {
            console.log("[Service Worker] Push Received.");
            console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);
            let notificationData = event.data.json();
            const title = notificationData.title;
            // 彈通知消息框
            event.waitUntil(self.registration.showNotification(title, notificationData));
          });

          三、項(xiàng)目實(shí)戰(zhàn)

          本次項(xiàng)目使用的是firebase云消息服務(wù),firebase對(duì)一系列api進(jìn)行了封裝,在我們項(xiàng)目中的流程為:

          原理大家都懂了,下面關(guān)結(jié)合代碼介紹關(guān)鍵步驟:

          1、firebase配置項(xiàng)目

          在最開始,需要在firebase上進(jìn)行云消息項(xiàng)目的配置,配置完成后,就可以拿到公鑰和私鑰、項(xiàng)目id等信息,用于初始化firebase以及后端發(fā)送推送時(shí)需要的一些私鑰等。

          2、消息推送相關(guān)api瀏覽器支持情況檢查

          來到步驟2,進(jìn)行消息推送相關(guān)api瀏覽器支持情況檢查,若檢查到瀏覽器不支持,則進(jìn)行不支持回調(diào),該瀏覽器無法使用消息推送功能:

          //檢查是否支持service worker、notification、pushManager
          function checkFirebaseSupport() {
            return (
              checkNotificationSupport() &&
              checkServiceworkerSupport() &&
              checkPushMessageSupport()
            );
          }
          
          
          //Notification支持判斷
          function checkNotificationSupport() {
            return "Notification" in window;
          }
          
          
          //service worker支持判斷
          function checkServiceworkerSupport() {
            return "serviceWorker" in window.navigator;
          }
          
          
          //PushManager支持判斷
          function checkPushMessageSupport() {
            return "PushManager" in window;
          }
          
          

          3、初始化firebase

          確認(rèn)支持之后初始化firebase,帶上創(chuàng)建項(xiàng)目時(shí)的參數(shù),具體看如下代碼注釋解析:

          import * as firebase from "firebase";
          //firebase項(xiàng)目初始化
          function firebaseInit() {
            firebase.initializeApp({
              messagingSenderId: FCM_APP_ID,
              projectId: PROJECT_ID,
              apiKey: API_KEY,
              appId: APP_ID,
            });
            this.messaging = firebase.messaging();
            window.messaging = this.messaging;
            htmlOnMessageInit();
          }
          //當(dāng)頁面聚焦時(shí),監(jiān)聽推送事件,必須初始化,否則收不到推送
          function htmlOnMessageInit() {
            window.addEventListener("load", function () {
              //手動(dòng)更新service worker
              navigator.serviceWorker
                .getRegistration("/firebase-cloud-messaging-push-scope")
                .then((registration) => {
                  registration && registration.update();
                });
              if (window.messaging) {
                //firebase封裝的方法,監(jiān)聽service worker的postMessage事件
                //當(dāng)收到推送且用戶當(dāng)前瀏覽器頁面處于該service worker的作用域范圍時(shí),能拿到推送的數(shù)據(jù)payload
                //考慮到用戶已經(jīng)處于我們平臺(tái)的頁面了,此時(shí)不顯示通知,實(shí)際上是可以調(diào)用notification的api去顯示通知的
                window.messaging.onMessage(function (payload) {
                  console.log("Message received:", payload);
                });
              }
            });
          }

          4、通知授權(quán)

          當(dāng)用戶觸發(fā)頁面授權(quán)的操作時(shí),會(huì)彈出詢問是否允許授權(quán)接收通知彈窗,通知有3種狀態(tài),能監(jiān)聽到狀態(tài)并進(jìn)行行為上報(bào)

          //調(diào)起授權(quán)
          function getRequestPermission(grantedCallback, deniedCallback, closeCallback) {
            //調(diào)起授權(quán)詢問
            Notification.requestPermission().then(async (permission) => {
              switch (permission) {
                case "granted": {
                  //此處可加上允許授權(quán)事件上報(bào)
                  //調(diào)用獲取subscribe方法
                  let token = await _this.getToken();
                  if (token) {
                    grantedCallback(token);
                    sendTokenToServer(token);
                  }
                  break;
                }
                case "denied": {
                  //此處可加上拒絕授權(quán)事件上報(bào)
                  deniedCallback();
                  break;
                }
                default: {
                  //此處可加上默認(rèn)處理、關(guān)閉通知授權(quán)事件上報(bào)
                  closeCallback();
                  break;
                }
              }
            });
          }


          5、獲取訂閱

          若授權(quán)了,則能夠調(diào)用firebase封裝好的getToken()方法獲取到subscribe,唯一標(biāo)識(shí)此瀏覽器,并將此標(biāo)識(shí)發(fā)送給后端存儲(chǔ)起來

          //獲取firebase token
          async function getToken() {
            try {
              //firebase封裝好的getToken()方法獲取到subscribe
              let token = await this.messaging.getToken();
              return token;
            } catch (e) {
              console.log("get token error", e);
              return false;
            }
          }

          6、推送消息

          后端使用FCM提供的web push的庫發(fā)送推送,帶上私鑰和subscribe

          from firebase_admin import messaging
          import sys
          import json
          cred = credentials.Certificate('xxxx.json')
          default_app = firebase_admin.initialize_app(cred)
          message = messaging.Message(
              data={
                  'data_title': 'test title',
                  'data_body': 'hahahaha',
                  'data_icon': 'xxx',
                  'jump_url': 'xxx',
                  'send_id' : '123123',
                  'isShow': 'true',
              },
              token='xxx'
          )
          
          
          response = messaging.send(message)

          7、接收推送并展示通知

          FCM驗(yàn)證過后,發(fā)送push給瀏覽器,service worker監(jiān)聽push事件,根據(jù)所在頁面不同狀態(tài)而調(diào)用不同的處理方法并進(jìn)行行為統(tǒng)計(jì)上報(bào)

          a.當(dāng)瀏覽器位于前臺(tái)時(shí),考慮到用戶已經(jīng)在當(dāng)前活動(dòng)頁面了就不顯示通知了(實(shí)際上也是可以調(diào)用notification的方法顯示通知的),但是能監(jiān)聽到消息

          if (window.messaging) {
            //此處是與service worker進(jìn)行通信
            window.messaging.onMessage(function (payload) {
              console.log("Message received:", payload);
            });
          }

          b.當(dāng)瀏覽器位于后臺(tái)時(shí),收到并顯示通知,在service worker文件處理:

          //當(dāng)頁面位于后臺(tái)時(shí),調(diào)用的是此方法
          messaging.setBackgroundMessageHandler(function (payload) {
            console.log(
              "[firebase-messaging-sw.js] Received background message ",
              payload
            );
            if (payload.data.isShow === "true") {
              //payload.data可獲取到消息的內(nèi)容
              const notification = payload.data;
              //設(shè)置消息的標(biāo)題、內(nèi)容、圖標(biāo)、點(diǎn)擊需要跳轉(zhuǎn)的地址、該消息的標(biāo)識(shí)
              const notificationTitle = notification.data_title;
              const notificationOptions = {
                body: notification.data_body,
                icon: notification.data_icon,
                data: {
                  linkUrl: notification.jump_url,
                  sendId: notification.send_id,
                },
              };
              //向后端發(fā)送收到消息統(tǒng)計(jì)
              //service worker限制無法使用XMLHttpRequest,可使用fetch api發(fā)起請(qǐng)求
              var request = new Request(
                `https://xxx.xxx.com/game_message/show_times?SEND_ID=${notification.send_id}`,
                {
                  method: "GET",
                  mode: "no-cors",
                  redirect: "follow",
                  headers: new Headers({
                    "Content-Type": "text/plain",
                  }),
                }
              );
              fetch(request)
                .then(function () {
                  console.log("send show notification statistics succ");
                })
                .catch(function (err) {
                  console.log("send show notification statistics fail");
                });
              //設(shè)置顯示通知
              return self.registration.showNotification(
                notificationTitle,
                notificationOptions
              );
            }
          });

          四、問題&解決

          1、如何監(jiān)控用戶收到消息后的行為?

          firebase消息分2種類型,分別為通知消息和數(shù)據(jù)消息

          • 后端在推送消息時(shí),若使用的是通知字段推送,則由后端控制通知顯示行為,不會(huì)去調(diào)前端的控制方法;
          • 后端在推送消息時(shí),若使用的是數(shù)據(jù)字段推送,則由前端端控制通知顯示行為,這時(shí)就能進(jìn)行一些行為監(jiān)控和數(shù)據(jù)上報(bào)了,比如收到、點(diǎn)擊、關(guān)閉推送通知上報(bào)

          但是不管哪一種方式,頁面都必須要監(jiān)聽messaging.onMessage()方法,否則無法收到推送(踩過的坑)

          2、如何更新service worker文件?

          (1)每次進(jìn)入頁面,代碼手動(dòng)更新servie worker文件,當(dāng)service worker文件有更新的時(shí)候,會(huì)再次進(jìn)行安裝,但是安裝完成之后處于waiting狀態(tài),還是舊的service worker控制頁面,需要關(guān)閉頁面之后再打開頁面,新的service worker才能控制頁面,于是需要配合(2)的使用,安裝完成之后新service worker就能控制頁面了(網(wǎng)上說service worker每24小時(shí)會(huì)自動(dòng)更新一次,測(cè)試結(jié)果來看是沒有的)

          navigator.serviceWorker
            .getRegistration("/firebase-cloud-messaging-push-scope")
            .then((registration) => {
              registration && registration.update();
            });

          2)在service worker的install事件中,調(diào)用self.skipWaiting()跳過waiting狀態(tài)直接進(jìn)入activate激活狀態(tài),新service worker控制頁面

          self.addEventListener("install", function (event) {
            self.skipWaiting();
          })

          3、是否需要頻繁授權(quán)通知,影響用戶體驗(yàn)?(通知授權(quán)邏輯是如何的呢?)

          文檔沒說明,那就手動(dòng)測(cè)試一下吧:

          4、如何清除無效token?

          背景:目前發(fā)送失敗一次我們才知道這條token是無效的,浪費(fèi)了發(fā)送時(shí)間,且當(dāng)后續(xù)收集到很多token時(shí),查詢和發(fā)送效率會(huì)有一定影響。

          解決:與后端協(xié)商推送消息時(shí)帶上isShow參數(shù),isShow為true時(shí),才顯示消息,isShow為false則用來檢查token是否清除,不顯示消息,不會(huì)對(duì)用戶造成影響,這個(gè)可以在服務(wù)端空閑的時(shí)候進(jìn)行定期的清除工作,不影響正常推送過程。

          收益:

          (1)提高查詢token的效率

          (2)提高發(fā)送消息,減少無效發(fā)送

          messaging.setBackgroundMessageHandler(function (payload) {
            //payload.data.isShow === 'true'才顯示消息,否則是token有效性檢查,不顯示通知
            if (payload.data.isShow === "true") {
              const notification = payload.data;
              const notificationTitle = notification.data_title;
              const notificationOptions = {
                body: notification.data_body,
                icon: notification.data_icon,
                data: {
                  linkUrl: notification.jump_url,
                  sendId: notification.send_id,
                },
              };
              return self.registration.showNotification(
                notificationTitle,
                notificationOptions
              );
            }
          });

          五、通知其他玩法

          1、通知多選形式:https://www.jianshu.com/p/f9480c35e32d

          2、更多的通知屬性配置參考

          https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification


          來源-微信公眾號(hào):三七互娛技術(shù)團(tuán)隊(duì)

          出處:https://mp.weixin.qq.com/s/bqfPma5E0PfZ0c3XgpkSCQ


          主站蜘蛛池模板: 亚州国产AV一区二区三区伊在| 精品视频一区二区三三区四区| 亚洲AV无码一区二区三区电影| 人妻无码一区二区视频| 国产精品一区二区av| 成人无号精品一区二区三区| 熟女性饥渴一区二区三区| 国产av一区最新精品| 亚洲V无码一区二区三区四区观看 亚洲爆乳精品无码一区二区三区 亚洲爆乳无码一区二区三区 | 日本中文一区二区三区亚洲| 亚洲一区二区影视| 夜夜添无码一区二区三区| 精品一区二区AV天堂| 中文字幕AV一区二区三区| 久久久久久综合一区中文字幕| 精品乱人伦一区二区| 蜜桃AV抽搐高潮一区二区| 人妻无码第一区二区三区| 国产一区二区三区夜色| 国产av一区二区精品久久凹凸| 一区二区无码免费视频网站| 无码精品人妻一区二区三区中| 精品人体无码一区二区三区| 日本一区二区三区久久| 亚洲av无码一区二区三区在线播放| 亚洲av无码不卡一区二区三区| 丰满人妻一区二区三区免费视频| 波霸影院一区二区| 天天躁日日躁狠狠躁一区| 国产微拍精品一区二区| 中文字幕乱码人妻一区二区三区 | 日韩在线不卡免费视频一区| 中文字幕VA一区二区三区| 一区二区三区国模大胆| 精品3d动漫视频一区在线观看| 久久精品免费一区二区喷潮| 亚洲av综合av一区二区三区| 麻豆果冻传媒2021精品传媒一区下载 | 国偷自产一区二区免费视频| 中日韩一区二区三区| 国产婷婷一区二区三区|