Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 日本高清不卡在线观看,成人免费视频一区二区,在线精品一区二区三区电影

          整合營銷服務商

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

          免費咨詢熱線:

          如何在Vue.js中創建模態彈窗

          載說明:原創不易,未經授權,謝絕任何形式的轉載

          開篇

          模態框(彈出層對話框)在大多數現代應用程序中非常常見。它們主要用于呈現簡潔的信息,非常適合顯示廣告和促銷內容。模態框提供了一種快速傳達信息的方式,并提供了用戶友好的關閉選項。

          在本文中,我們將使用Vuejs構建一個彈出模態框。該模態框將包括一個取消或關閉按鈕,以方便用戶在完成任務后關閉它。此外,我們還將實現一個功能,允許用戶在模態框區域外點擊以關閉它。

          模態彈出組件

          <script setup lang="ts">
          import { ref } from 'vue';
          
          const message = ref('This is a modal popup');
          const emit = defineEmits(['close']);
          
          const closeModal = () => {
            emit('close');
          };
          </script>
          <template>
            <div class="popup" @click.self="closeModal">
              <div class="popup-content">
                <div class="popup-header">
                  <h2 class="popup-title">{{ message }}</h2>
                  <button class="popup-close-button" @click.prevent="closeModal">X</button>
                </div>
                <article>
                  <div class="popup-content-text">
                    This is a simple modal popup in Vue.js
                  </div>
                </article>
              </div>
            </div>
          </template>

          Script Section

          <script setup lang="ts">
          import { ref } from 'vue';
          const message = ref('This is a modal popup');
          const emit = defineEmits(['close']);
          const closeModal = () => {
           emit('close');
          };
          </script>

          在這個部分,我們從Vue中導入所需的功能。

          • ref 用于創建一個包含在模態框中顯示的響應式變量消息。
          • emit用于定義一個名為“close”的事件,該事件可被觸發以關閉模態框。
          • closeModal是一個函數,當調用時會觸發“close”事件,從而有效地關閉模態框。

          Template Section

          <template>
             <div class="popup" @click.self="closeModal">
             <div class="popup-content">
             <div class="popup-header">
             <h2 class="popup-title">{{ message }}</h2>
             <button class="popup-close-button" @click.prevent="closeModal">X</button>
           </div>
           <article>
             <div class="popup-content-text">
             This is a simple modal popup in Vue.js
             </div>
           </article>
           </div>
           </div>
          </template>

          本段代碼義了模板中模態框的結構。

          • 具有“popup”類的最外層div用作模態框的背景。
          • @click.self="closeModal"事件監聽器附加到背景上,允許在其內容之外點擊時關閉模態框。
          • 內容包括一個標題(popup-title)和一個關閉按鈕(popup-close-button)。
          • 在標題下方,有一個文章部分,其中包含了模態框的主要內容。

          渲染模態框組件

          <script setup lang="ts">
          import { ref } from 'vue'
          import Popup from "@/components/Popup.vue"; // @ is an alias to /src
          
          const msg = ref('Hello World!')
          const isOpened = ref(false)
          
          </script>
          <template>
            <div>
              <h1>{{ msg }}</h1>
              <button @click="isOpened = !isOpened">Open Popup</button>
              <Teleport to="body">
                <Popup v-if="isOpened" @close="isOpened = !isOpened" />
              </Teleport>
            </div>
          </template>

          數據和狀態管理:

          代碼使用Vue的ref函數創建了兩個響應式變量:

          • - msg: 初始設置為“Hello World!”的文本消息被存儲。
          • - isOpened: 這是一個布爾變量,初始值為false,表示彈出窗口是否打開或關閉。

          按鈕點擊事件

          模板中有一個帶有點擊事件監聽器(@click)的<button>元素。當按鈕被點擊時,它會切換isOpened變量的值,從而有效地打開或關閉彈出窗口。

          導入彈出框組件

          • 代碼導入了一個彈出組件(Popup.vue)。
          • 在模板中,使用v-if條件渲染彈出窗口組件。只有當isOpened變量為true時(v-if="isOpened"),彈出窗口才會顯示,表示彈出窗口應該是打開的。
          • <Teleport>用于將彈出窗口組件移動到HTML文檔的元素中。這樣可以確保彈出窗口在當前組件的DOM層次結構之外渲染,并且可以顯示在頁面上的其他內容之上。

          組件之間的通信:

          • 當需要關閉彈出組件時,Popup組件會觸發一個關閉事件@close。父組件使用@close事件監聽器來監聽此關閉事件。
          • 當Popup組件發出事件時,它切換isOpened變量,從而關閉彈出窗口。

          您可以在CodeSandbox上使用本文中設計的代碼進行操作。

          https://codesandbox.io/s/suspicious-kepler-993dmh?file=%2Fsrc%2Fviews%2FHome.vue%3A0-420

          結束

          由于文章內容篇幅有限,今天的內容就分享到這里,文章結尾,我想提醒您,文章的創作不易,如果您喜歡我的分享,請別忘了點贊和轉發,讓更多有需要的人看到。同時,如果您想獲取更多前端技術的知識,歡迎關注我,您的支持將是我分享最大的動力。我會持續輸出更多內容,敬請期待。

          作為 TOB 的業務方,我們偶爾會收到一些如下圖所示的反饋。

          作為 PC 頁面為主的業務方,大多數用戶在一天的工作中,可能都不太會刷新或者重新打開我們的頁面,導致我們在下午或者白天發布的前端版本,往往需要到幾個小時甚至第二天,才能覆蓋到 98% 以上的用戶。

          我們統計了 bscm 平臺 5 次下午 2-3 點左右發布的版本,在發布后每個時間段內老版本用戶的占比情況。選擇這個時間點發布的原因是這個時間點基本是平臺用戶的上班時間,是最有可能出現用戶已經打開了頁面同時我們在發布新代碼的場景的,比較具有代表性。按平臺用戶六七點下班來看,我們可以看到還有將近 6% 的用戶在當天是會一直訪問老版本的前端代碼的,按照 bscm 平臺 1w+的 uv 來看,約有 600 多人會可能遇到前端版本過低導致的使用問題。

          方案

          彈窗內容

          彈窗的觸發條件

          首先介紹兩個概念,本地版本號和云端版本號。本地版本號是用戶請求到的前端頁面的代碼版本號,是用戶訪問頁面時決定;云端版本號可以理解為最新前端版本號,它是每次開發者發布前端代碼時決定的。

          判斷觸發條件的時機

          有了彈窗的觸發條件,我們還需要去決定什么時候判斷彈窗是否滿足觸發的條件,上面也提到了,出現這類問題的場景多見于用戶在使用過程中,開發者進行了前端代碼發布,那我們主要可以有兩個類型的時機去進行觸發條件的判斷。

          1. 前端代碼去感知什么時候有新版本的代碼發布了,去進行條件判斷(消息推送)
          2. 前端在一定的條件下主動去判斷觸發條件(輪詢,請求后端接口時,一些中頻前端事件的監聽)

          我們對這些時機在更新是否及時,判斷次數多少、實現成本高低等維度進行一個對比。

          ?? 越多表示這個維度得分越高

          根據表格可以看到 websocket 消息推送和前端事件監聽這兩種方案綜合來看是更合適一些的,但是前端事件監聽其實它的劣勢在實際運用場景中會被弱化(一天的上線數量有限,請求次數一天不會多太多次),但是實現成本遠低于 websocket,所以無疑是實際落地場景中比較理想的選擇。

          根據 can i use 的結果我們也可以發現 visibilitychange 事件也基本符合我們目前 B 端頁面對于 PC 瀏覽器的要求。

          版本號的生成

          本地版本號

          本地版本號是用戶訪問時決定的,那無疑頁面的 html 文件就是這個版本號存在的最佳載體,我們可以在打包時通過 plugin 給 html 文件注入一個版本號。

          云端版本號

          云端版本號的選擇則有很多方式了,數據庫、cdn 等等都可以滿足需求。不過考慮到實現成本和泳道的情況,想了一下兩個思路一個是打包的同時生成一個 version.json 文件,配一個路由去訪問;另一個是直接訪問對應的 html 代碼,解析出注入的版本號,二者各自有適合的場景。

          微前端的適配

          我們現在的大多數項目都包含了主應用和子應用,那其實不管是子應用的更新還是主應用的更新都應該有相關的提示,而且相互獨立,但同時又需要保證彈窗不同時出現。

          想要沿用之前的方案其實只需要解決三個問題。

          1. 主子應用的本地版本號標識需要有區分,因為 html 文件只有一個,需要能在 html 文件中區分出哪個應用的版本是什么,這個我們只需在 plugin 中注入標識即可解決。
          2. 云端版本號請求時也要請求對應的云端版本號,這個目前采用的方案是主應用去請求唯一的 version.json 文件,因為主應用路由是唯一的,子應用則去請求最新的 html 資源文件,解析出云端版本號。
          3. 不重復彈窗我們只需要在展示彈窗前,多加一個是否已經有彈窗展示的判斷即可了。

          具體實現

          版本號的寫入和讀取

          監聽時機和頻控邏輯

          正如前文提到的,本身版本發布不是一個高頻事件,但是監聽事件的頻次有時候可能過高了,不希望頻繁的去進行觸發條件判斷。同時如果出現一天內多次發布的場景,也不希望這個彈窗對于用戶有過多的打擾,所以需要去添加一個頻控邏輯。

          具體代碼

          plugin

          /* eslint-disable */
          import { CoraWebpackPlugin, WebpackCompiler } from '@ies/eden-web-build';
          const fs = require('fs');
          const path = require('path');
          const cheerio = require('cheerio');
          
          interface IVersion {
            name?: string; // 編譯完的文件夾名稱
            subName?: string; // 子應用的名稱,主應用可以不傳
          }
          
          export class VersionPlugin implements CoraWebpackPlugin {
            readonly name = 'versionPlugin'; // 插件必須要有一個名字,這個名字不能和已有插件沖突
            private _version: number;
            private _name: string;
            private _subName: string;
            constructor(params: IVersion) {
              this._version = new Date().getTime();
              this._name = params?.name || 'build';
              this._subName = params?.subName || ''
            }
            apply(compiler: WebpackCompiler): void {
              compiler.hooks.afterCompile.tap('versionPlugin', () => {
                try {
                  const filePath = path.resolve(`./${this._name}/template/version.json`);
                  fs.writeFile(filePath, JSON.stringify({ version: this._version }), (err: any) => {
                    if (err) {
                      console.log('@@@err', err);
                    }
                  });
                  const htmlPath = path.resolve(`./${this._name}/template/index.html`);
                  const data = fs.readFileSync(htmlPath);
                  const $ = cheerio.load(data);
                  $('body').append(`<div id="${this._subName}versionTag" style="display: none">${this._version}</div>`);
                  fs.writeFile(htmlPath, $.html(), (err: any) => {
                    if (err) {
                      console.log('@@@htmlerr', err);
                    }
                  });
                } catch (err) {
                  console.log(err);
                }
              });
            }
          }
          

          彈窗組件

          import React, { useEffect } from 'react';
          
          import { Modal } from '@ecom/auxo';
          import axios from 'axios';
          import moment from 'moment';
          
          export interface IProps {
            isSub?: boolean; // 是否為子應用
            subName?: string; // 子應用名稱
            resourceUrl?: string; // 子應用的資源url
          }
          
          export type IType = 'visibilitychange' | 'popstate' | 'init';
          
          export default React.memo<IProps>(props => {
            const { isSub = false, subName = '', resourceUrl = '' } = props || {};
          
            const cb = (latestVersion: number | undefined, currentVersion: number | undefined, type: IType) => {
              try {
                // 版本落后,提示可以刷新頁面
                if (latestVersion && currentVersion && latestVersion > currentVersion) {
                  // 提醒過了就設置一個更新提示過期時間,一天內不需要再提示了,彈窗過期時間暫時全局只需要一個??!
                  localStorage.setItem(`versionUpdateExpireTime`, moment().endOf('day').format('x'));
                  if (!document.getElementById('versionModalTitle')) {
                    Modal.confirm({
                      title: <div id="versionModalTitle">版本更新提示</div>,
                      content:
                        '您已經長時間未使用此頁面,在此期間平臺有過更新,如您此時在頁面中沒有填寫相關信息等操作,請點擊刷新頁面使用最新版本!',
                      okText: <div data-text={`前端版本升級引導-立即更新 ${type}`}>刷新頁面</div>,
                      cancelText: <div data-text={`前端版本升級引導-我知道了 ${type}`}>我知道了</div>,
                      onCancel: () => {
                        console.log('fe-version-watcher INFO: 未更新~');
                      },
                      onOk: () => {
                        location.reload();
                      },
                    });
                  }
                }
                // 不管版本是否落后,半小時內都不需要去重新請求判斷
                localStorage.setItem(`versionInfoExpireTime`, String(new Date().getTime() + 1000 * 60 * 30));
              } catch {}
            };
          
            const formatVersion = (text?: string) => (text ? Number(text) : undefined);
          
            useEffect(() => {
              try {
                const fn = function (type: IType) {
                  if (document.visibilityState === 'visible') {
                    /**
                     * @desc 為了防止打擾,版本更新每個應用一天只提示一次 所以過期時間設為當天23:59:59,沒過期則直接return
                     */
                    if (Number(localStorage.getItem(`versionUpdateExpireTime`) || 0) >= new Date().getTime()) {
                      return;
                    }
                    /**
                     * @desc 不需要每次切換頁面都去判斷資源,每次從服務器獲取到的版本信息,給半個小時的緩存時間,需要區分子應用
                     */
                    if (Number(localStorage.getItem(`versionInfoExpireTime`) || 0) > new Date().getTime()) {
                      return;
                    }
          
                    if (!isSub) {
                      /**
                       * @desc 主應用使用version.json文件來獲取最新的版本號
                       */
                      const dom = document.getElementById('versionTag');
                      const currentVersion = formatVersion(dom?.innerText);
                      axios.get(`/version?timestamp=${new Date().getTime()}`).then(res => {
                        const latestVersion = res?.data?.version;
                        cb(latestVersion, currentVersion, type);
                      });
                    } else {
                      /**
                       * @desc 子應用使用最新html中的innerText來獲取最新版本號
                       */
                      if (resourceUrl) {
                        const dom = document.getElementById(`${subName}versionTag`);
                        const currentVersion = dom?.innerText ? Number(dom?.innerText) : undefined;
                        axios.get(resourceUrl).then(res => {
                          /** ignore_security_alert */
                          try {
                            const html = res.data;
                            const doc = new DOMParser().parseFromString(html, 'text/html');
                            const latestVersion = formatVersion(doc.getElementById(`${subName}versionTag`)?.innerText);
                            cb(latestVersion, currentVersion, type);
                          } catch {}
                        });
                      }
                    }
                  }
                };
                const visibleFn = () => {
                  fn('visibilitychange');
                };
                const routerFn = () => {
                  fn('popstate');
                };
                if (isSub) {
                  // 子應用可能會有緩存,初始化的時候先判斷一次
                  fn('init');
                }
                document.addEventListener('visibilitychange', visibleFn);
                window.addEventListener('popstate', routerFn);
                return () => {
                  document.removeEventListener('visibilitychange', visibleFn);
                  window.removeEventListener('popstate', routerFn);
                };
              } catch {}
            }, []);
          
            return <div />;
          });
          

          如何接入

          主應用版本

          1. 安裝依賴
          npm i @ecom/fe-version-watcher-plugin # 安裝plugin 
          npm i @ecom/logistics-supply-chain-fe-version-watcher # 安裝引導彈窗
          
          1. 引入 versionPlugin,自動生成 version.json + html 文件中自動注入
          import { VersionPlugin } from '@ecom/fe-version-watcher-plugin';
          
          // 有些項目打包后template文件夾下的名字不是build而是build_cn
          // 可以根據自己項目的實際情況傳入{name: build_cn}
          
          {
              ...,
              plugins: [
                  ...,
                  [VersionPlugin, {}],
              ]
           }
          
          1. 引入版本引導彈窗
          import { FeVersionWatcher } from '@ecom/logistics-supply-chain-fe-version-watcher';
          
          <FeVersionWatcher />
          
          1. goofy 新增路由配置,/version 指向 version.json 文件 (或者其它方式可以使得/version 的路由指向該 version.json 文件)

          預告

          采用 version.json 的方案,引入 FersionWatcher 組件就不再需要任何參數,目前主應用只支持這種模式。未來也將參考子應用,主應用支持讀取 html 中版本標識的能力,將配置路由的工作改成組件 props 傳入資源 url,開發者可以根據實際情況自行選擇。

          子應用版本

          1. 安裝依賴
          npm i @ecom/fe-version-watcher-plugin # 安裝plugin
          npm i @ecom/logistics-supply-chain-fe-version-watcher # 安裝引導彈窗
          
          1. 引入 versionPlugin, html 文件中自動注入版本號,需要子應用標識參數(必填)
          import { VersionPlugin } from '@ecom/fe-version-watcher-plugin';
          
          // 有些項目打包后template文件夾下的名字不是build而是build_cn
          // 可以根據自己項目的實際情況傳入{name: build_cn}
          
          {
              ...,
              plugins: [
                  ...,
                  [VersionPlugin, {subName: 'general-supplier', name: 'build_cn'}],
              ]
           }
          
          1. 引入版本引導彈窗(subName 和 plugin 中保持一致,resourceUrl 為配置的子應用路由)
          import { FeVersionWatcher } from '@ecom/logistics-supply-chain-fe-version-watcher';
          
          // subName需要和plugin的參數保持一致,resourceUrl為子應用資源的路徑(子引用goofy上配置的路由)
          <FeVersionWatcher isSub subName="general-supplier" resourceUrl="/webApp/general-supplier" />
          

          resourceUrl一般就是goofy上配置的路由設置,,如果不同平臺有區分,可以動態傳入。

          如何調試/效果展示

          發布成功后,可以根據如下步驟測試:

          1. 刪除 localstorage 中相關的 value
          2. 修改 html 中的 version,改成一個比較小的數值即可
          3. 切換路由,或者隱藏/打開頁面,出現彈窗

          收益統計

          同樣我們截取了 4 次該平臺 2-3 點發布的版本情況,可以看到老版本用戶的 uv 占比有著明顯的下降。

          上線至今共計提示 10 萬+用戶,幫助約 5 萬人次及時更新了前端代碼。


          作者:費昀鋒

          來源:微信公眾號:字節前端 ByteFE

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

          生javascript實現帶動畫的提示型彈窗,常用于網站彈層的彈窗也有很多,一般用插件比較多,所以今天就來寫一寫該功能,如有錯誤之處請指出!

          彈出跟消失都有放大縮小動畫在里面!

          實現方法:

          html:

          可以自己輸入內容,再點擊彈出即可看到彈窗效果

          css:

          javascript:


          主站蜘蛛池模板: 亚洲码一区二区三区| 久久久久国产一区二区| 亚洲天堂一区二区三区| 亚洲国产精品无码久久一区二区| 免费一区二区无码视频在线播放| 无码人妻一区二区三区精品视频| 亚洲性日韩精品一区二区三区 | 亚洲一区二区三区在线视频| 中文字幕乱码一区二区免费| 国精产品一区一区三区有限公司| 国产丝袜一区二区三区在线观看| 精品视频一区二区三区| 波多野结衣一区二区三区| 视频一区精品自拍| 日本在线观看一区二区三区| 亚洲av无码一区二区三区天堂古代| 国内精品一区二区三区最新| 国产无线乱码一区二三区| 亚洲一区二区精品视频| 国产精品一区12p| 美女AV一区二区三区| 国产一区二区草草影院| 亚洲av无码片vr一区二区三区| 国产一区二区三区久久精品| 成人午夜视频精品一区| 视频在线观看一区| 本免费AV无码专区一区| 三上悠亚日韩精品一区在线| 国产一区三区三区| 国产精品视频一区二区噜噜| 国模吧一区二区三区| 日韩人妻无码一区二区三区久久 | 国产一区二区三区不卡在线观看| 亚洲国产欧美国产综合一区| 国产主播福利精品一区二区| 成人国产一区二区三区| 精品国产一区二区三区久久久狼| 91视频一区二区| 乱人伦一区二区三区| 国产亚洲无线码一区二区| 亚洲AV无码国产精品永久一区|