乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      12.其它細(xì)節(jié)

       怡紅公子0526 2021-09-12

      上面11篇論述了主要的原理,作為最后一篇,我們主要論述單頁(yè)面相比于多頁(yè)面的靈活的部分,如何使用最原始的html,js,css發(fā)揮web的最大魅力。

      動(dòng)畫(huà),過(guò)場(chǎng)動(dòng)畫(huà)

      單頁(yè)面要比多頁(yè)面靈活,擁有過(guò)場(chǎng)動(dòng)畫(huà)是它最直觀的表現(xiàn),并且頁(yè)面切換不會(huì)出現(xiàn)白屏的現(xiàn)象。

      在底層ReplaceProto對(duì)象中,專門(mén)設(shè)置了兩個(gè)dom,一個(gè)dom作為放置當(dāng)前頁(yè)面,另一個(gè)dom放置切換頁(yè)面。在切換過(guò)程中,通過(guò)兩個(gè)dom的過(guò)渡產(chǎn)生過(guò)場(chǎng)動(dòng)畫(huà)。動(dòng)畫(huà)方式在css3中定義,然后根據(jù)情況進(jìn)行不同的動(dòng)畫(huà)切換, 同時(shí)完成后退和前進(jìn)兩個(gè)過(guò)場(chǎng)

      1. 進(jìn)場(chǎng)動(dòng)畫(huà),當(dāng)切換到另一個(gè)頁(yè)面。另一個(gè)頁(yè)面就會(huì)以入場(chǎng)動(dòng)畫(huà)顯示到屏幕上;

      2. 出場(chǎng)動(dòng)畫(huà),當(dāng)切換到另一個(gè)頁(yè)面,當(dāng)前頁(yè)面就會(huì)以出場(chǎng)動(dòng)畫(huà)退出屏幕中。

      它們共同組成了頁(yè)面的動(dòng)畫(huà)效果

      默認(rèn)的動(dòng)畫(huà)由App對(duì)象里面定義,在附帶的app.css中定義行為

      this.options = {
          changeClass: "app-change",
          backClass: "app-back",
          area: "change-state",
          in: { // 進(jìn)場(chǎng)動(dòng)畫(huà)
              back: "page-out",
              change: "page-in"
          },
          out: { // 出場(chǎng)動(dòng)畫(huà)
              back: "page-in-reverse",
              change: "page-out-reverse"
          }
      };

      在切換頁(yè)面的時(shí)候,也可以通過(guò)傳入不同的類名,實(shí)現(xiàn)自定義動(dòng)畫(huà), 詳細(xì)見(jiàn)ReplaceProto的render方法。render: function (pagename, isReplace, option)
      如果使用replaceState方法不會(huì)觸發(fā)動(dòng)畫(huà)。
      其中第三個(gè)參數(shù)不僅可以傳數(shù)據(jù),也可以傳動(dòng)畫(huà)配置,并且在_getReplaceClass方法中進(jìn)行切換類名,達(dá)到動(dòng)畫(huà)的效果。

       _getReplaceClass: function (option) {
          var options = this.options;
          option = option || {};
          return {
              backStaticClass: option.backClass || options.backClass,
              changeStaticClass: option.changeClass || options.changeClass,
              areaClass: option.area || options.area,
              backActiveClass: this.isRenderBack ? 
                  option.backClass || options.out.back : 
                  option.backClass || options.in.back,
              changeActiveClass: this.isRenderBack ? 
                  option.changeClass || options.out.change : 
                  option.changeClass || options.in.change
          }
      },

      類似的在PopUp對(duì)象中同樣有頁(yè)面切換動(dòng)畫(huà),PopUp對(duì)象還要有個(gè)彈出彈窗和關(guān)閉的動(dòng)畫(huà)。請(qǐng)看動(dòng)畫(huà)設(shè)置:

      this.options = {
          className: "popup",
          changeClass: "popup-change",
          backClass: "popup-back",
          area: "popup-state",
          currentIn: { // 顯示頁(yè)面上的入場(chǎng)
              backClass: "popup-active-out",
              changeClass: "popup-active-in"
          },
          currentOut: { // 顯示頁(yè)面上的出場(chǎng)
              backClass: "popup-active-in-reverse",
              changeClass: "popup-active-out-reverse"
          },
          staticIn: "popup-static-out", // 彈窗進(jìn)入頁(yè)面
          staticOut: "popup-static-in" // 彈窗隱藏
      };

      可以在PopUp配置或在show和hidden方法中設(shè)置不同的動(dòng)畫(huà)效果。

      show: function (dom, config, target, isDismisBeforeShow) // config.staticIn
      
      hidden: function (option, bk) // option.staticOut或show方法傳入的config.staticOut

      對(duì)于組件的頁(yè)面切換動(dòng)畫(huà)和App的動(dòng)畫(huà)切換是一致的。

      初始化頁(yè)面歷史緩存

      如果用戶從首頁(yè)進(jìn)入網(wǎng)站,我們不用對(duì)history記錄做任何更改,這是一種常規(guī)情況。然而網(wǎng)站的入口是url,如果url不是進(jìn)入首頁(yè),而是從詳情頁(yè)或是付款頁(yè)進(jìn)入網(wǎng)站,或者通過(guò)其它手段(掃碼等)。

      當(dāng)用戶在該頁(yè)面進(jìn)行了操作(如果不做任何操作,點(diǎn)擊后退應(yīng)該是退出網(wǎng)站),為了讓用戶有一個(gè)瀏覽流程,在詳情頁(yè)點(diǎn)擊后退應(yīng)該是返回到列表頁(yè)。

      原理很簡(jiǎn)單,判斷進(jìn)入初始頁(yè),然后先pushState若干個(gè)頁(yè)面。然后渲染頁(yè)面。從App.initialize的方法來(lái)看,有一個(gè)_prevAttachHistory(prevHistory)操作,這就是為了該目的。這里的prevHistory是由開(kāi)發(fā)初始化定義的App實(shí)例對(duì)象的getInitHistory來(lái)得到,這是一個(gè)url數(shù)組。 例如我們初始化定義

      app.getInitHistory = function (pathname) {
          if (pathname === "/detail") return [ "/home" ]; // 也可以帶參數(shù)的url
      }

      通過(guò)這樣配置后,頁(yè)面直接打開(kāi)詳情頁(yè),在詳情頁(yè)操作后,點(diǎn)擊后退鍵回退到首頁(yè),而不是退出該網(wǎng)站。注意,如果進(jìn)入頁(yè)面后沒(méi)有任何操作,直接點(diǎn)擊退出,是不會(huì)退到首頁(yè)的,這是瀏覽器的一大特性,只有用戶與該頁(yè)面有交互,即使是touchstart,mousedown都能后退到首頁(yè)。后退到前頁(yè)面,因?yàn)闅v史記錄中存的不是頁(yè)面緩存,它是初始化一個(gè)新的Page對(duì)象,會(huì)走完P(guān)age的整個(gè)生命周期流程

      加速加載優(yōu)化,service延遲加載

      從上面篇章提到,整個(gè)資源獲取都是按需加載的,即使組件中的小圖標(biāo)的svg片段也是如此。特別一個(gè)大的首頁(yè),里面包含著很多小圖標(biāo),引入很多組件js,片段html,需要等它們?nèi)考虞d完會(huì)耗費(fèi)很長(zhǎng)時(shí)間。因此我們可以對(duì)常見(jiàn)的資源進(jìn)行統(tǒng)一定義加載。

      1. 可以將多個(gè)組件的js放在一個(gè)js文件中,因?yàn)楂@取組件的時(shí)候,判斷是否存在已經(jīng)加載過(guò)該組件, 需要手動(dòng)關(guān)閉。在App.define, app.defineComponent, app.definePage, app.definePopup的第三個(gè)參數(shù)設(shè)為true,其中app為App的實(shí)例對(duì)象。

      2. 可以將引入的html當(dāng)作字符串, 注入到Http.cache對(duì)象中 App.setHttpCache(url, str); 由于轉(zhuǎn)換為字符串編入js中(以字符串形式編輯html比較困難,不建議全部放入js中)。

      3. 如果某個(gè)service服務(wù)僅僅與該方法有關(guān),把引入代碼從頂部移到方法內(nèi),。比如

         clickHandler: function (ev) {
             App.require(["mapTool"], function (mapTool) {
                 // dosomething
             })
         }

      持續(xù)化數(shù)據(jù)和異步操作

      對(duì)于Component,Page, PopUp,App對(duì)象中,都定義一個(gè)data,這是一個(gè)放置臨時(shí)數(shù)據(jù)的對(duì)象。特別是Page,這個(gè)data格式有嚴(yán)格的要求,必須是可序列化的。

      在Page執(zhí)行restore的時(shí)候,我們是無(wú)法獲取到在其他頁(yè)面改變了的全局?jǐn)?shù)據(jù),我們可以把數(shù)據(jù)存放在App實(shí)例對(duì)象的data里面,然后切換頁(yè)面的時(shí)候獲取App對(duì)象中的data數(shù)據(jù),進(jìn)行有效的局部刷新操作。這要比重新去后端獲取一次更合理。

      如果在PopUp對(duì)象的Page對(duì)象,也可以用相同的方式放在PopUp的對(duì)象的data里面。統(tǒng)一放置方法可以用this.parent.data.key = value;

      一個(gè)網(wǎng)站,很少使用大量的持久化數(shù)據(jù),對(duì)于webapp,使用持久化數(shù)據(jù)卻很常見(jiàn)。我們可以使用同步操作的localStorage或異步操作的IndexedDB數(shù)據(jù)庫(kù)。在使用IndexedDB的時(shí)候要特別的注意,異步操作時(shí)頁(yè)面突然切換導(dǎo)致回調(diào)函數(shù)執(zhí)行錯(cuò)誤,因此盡量在執(zhí)行完后再執(zhí)行跳轉(zhuǎn)。如果不可避免,可以仿照Ajax的封裝方式,跳轉(zhuǎn)頁(yè)面的時(shí)候讓獲取數(shù)據(jù)操作停止,存數(shù)據(jù)的回調(diào)中判斷是否當(dāng)前頁(yè)面是有效顯示頁(yè)。
      對(duì)于不做中斷的異步操作,可以放在staticPage中執(zhí)行,因?yàn)樗且恢贝嬖诘?,等?zhí)行完畢后通過(guò)觸發(fā)app.currentPage的自定義事件,進(jìn)行相關(guān)的更新操作。

      瀏覽器緩存

      瀏覽器緩存可以提高頁(yè)面的加載速度,有時(shí)候卻成了我們更新項(xiàng)目的一大阻礙,特別在測(cè)試公眾號(hào)的時(shí)候。因此我們通過(guò)后綴名版本來(lái)解決問(wèn)題。比如我們?cè)趇ndex.html上header的新建script上加一句App.version = 2.0; 再把str.js和index.js版本號(hào)更改相同的版本號(hào)。接著框架內(nèi)部會(huì)把所有的引入的js以及獲取的靜態(tài)文件都會(huì)加上?v=2.0重置所有的版本號(hào)

      內(nèi)存使用

      單頁(yè)面對(duì)于內(nèi)存的使用非常的苛刻。如果無(wú)限制的使用,會(huì)導(dǎo)致頁(yè)面奔潰或讓手機(jī)設(shè)備快速耗電。因此這里我們對(duì)每個(gè)模塊的引用都做了嚴(yán)格的處理。對(duì)于dom和事件,在頁(yè)面銷毀的時(shí)候都會(huì)自動(dòng)去銷毀。而且引用外庫(kù)的時(shí)候,我們建議在init初始化數(shù)據(jù),在dispose方法中進(jìn)行數(shù)據(jù)清理。對(duì)于引用沒(méi)有數(shù)據(jù)回收操作的外庫(kù)的時(shí)候要特別小心,不能無(wú)限制的新建對(duì)象,這樣會(huì)導(dǎo)致頁(yè)面堆積越多的內(nèi)存而無(wú)法銷毀。我們可以使用創(chuàng)建一個(gè)對(duì)象,然后進(jìn)行無(wú)限制的使用(單例模式)。

      通過(guò)異步按需加載的好處在于,能讓內(nèi)存使用量盡可能的變少。在加載首頁(yè)的時(shí)候,我們的網(wǎng)頁(yè)的內(nèi)存使用量基本和純使用靜態(tài)頁(yè)面的網(wǎng)站持平的。隨著組件量以及頁(yè)面的增加,我們緩存了大量的js,靜態(tài)html,會(huì)讓內(nèi)存使用量增多,而且緩存在history的Page對(duì)象,也會(huì)提高內(nèi)存使用量。盡管如此,我們的內(nèi)存使用量也不會(huì)超過(guò)靜態(tài)頁(yè)面太多,在可以接受的范圍之內(nèi)。

      本地文件打開(kāi)

      有時(shí)候我們需要本地直接打開(kāi),雖然用的很少,但還是會(huì)遇到的。比如原生App嵌入webview,在沒(méi)有網(wǎng)的情況下要打開(kāi)網(wǎng)站,這時(shí)候只能通過(guò)打開(kāi)本地頁(yè)面,雖然功能有點(diǎn)閹割,但是頁(yè)面布局還是可以復(fù)用原來(lái)的,我們需要做一下的調(diào)整:

      1. 把絕對(duì)引用全部改為相對(duì)引用,這一點(diǎn)都是可以支持的,通過(guò)改寫(xiě)App.join方法統(tǒng)一更改js的加入;

      2. 無(wú)法使用按需加載(可以按需加載js),需要對(duì)所有的靜態(tài)資源進(jìn)行統(tǒng)一加入。這一點(diǎn)難度雖然不大,但是操作起來(lái)比較繁瑣;

      3. 如果使用了strui框架,需要針對(duì)使用的組件進(jìn)行資源統(tǒng)一加入。

      雖然付出了一些努力,但是非常值得的,底層是支持本地文件打開(kāi)的,以下功能會(huì)受到限制:

      1. 無(wú)法使用ajax功能,無(wú)法與后端進(jìn)行交互;

      2. 無(wú)法使用history api。在本地打開(kāi),會(huì)將這些方法全部過(guò)濾掉。

      支持SSR

      SSR對(duì)于單頁(yè)面相對(duì)多頁(yè)面是一個(gè)缺陷,盡管努力去彌補(bǔ),但總是無(wú)法盡善盡美。而且單純?cè)谇岸伺κ菬o(wú)法完成的。這里我們通過(guò)以下手段來(lái)實(shí)現(xiàn)SSR:

      1. 如果是純粹單頁(yè)面,index.html的body元素應(yīng)該只有引用script的。我們?cè)赽ody上加入data-preload屬性,代表它使用了SSR, 然后加入{{{body}}}, 代表著服務(wù)生成的html代碼;

      2. 接著在服務(wù)端,復(fù)制前端的renderHTML方法。根據(jù)瀏覽器訪問(wèn)地址,拼裝填充{{{body}}}的html片段(這里后端使用nodejs,可以共享前端的js方法);

      3. index.js中的聲明App對(duì)象的時(shí)候,currentName需要根據(jù)pathname改變。如下代碼

        // location.pathname = "/detail";
        if (location.pathname == "/detail") app = new App("hello str", "static", "detail");
        else app = new App("hello str", "static", "home");

      雖然通過(guò)上面拼接成的html可以在瀏覽器上直接打開(kāi),然而瀏覽器畢竟沒(méi)有直接渲染組件的功能, 因此渲染的結(jié)果不會(huì)太好。只能讓搜索引擎獲取到, 然后通過(guò)下面的方法進(jìn)行分別渲染:

      if (preload === "true") {
          // 通過(guò)更改html,渲染組件
          var activeHtml = pageContainer.innerHTML;
          pageContainer.innerHTML = "";
          var staticHtml = document.body.innerHTML;
          if (staticPage.preload) staticPage.preload();
          staticPage.initialize(body, staticHtml, {}, function () {
              body.removeAttribute("data-preload");
              that._initCurrentPage(staticPage, currentPage, prevHistory);
              if (currentPage.preload) currentPage.preload();
              currentPage.initialize(that.changeDom, activeHtml);
          });
      }
      else {
          // 常規(guī)手段
          staticPage.render(function (html) {
              staticPage.initialize(body, html, {}, function () {
                  that._initCurrentPage(staticPage, currentPage, prevHistory)
                  currentPage.render(function (html) {
                      currentPage.initialize(that.changeDom, html);
                  });
              });
          });
      }

      超越web,支持electron等方式

      現(xiàn)在web在通過(guò)electron打包成桌面App,因?yàn)閑lectron使用了node技術(shù),所以在獲取文件或者資源的時(shí)候就不一樣了。我們可以更改fetch方法:

      if (typeof __dirname === "string") {
          require("fs").readFile(url, "utf-8", function (error, result) {
              if (error) console.log(error);
              else Http.cache.dispatch(url, result);
          })
      }
      else {
          var obj = createRequest(this, url, undefined, function (result) {
              Http.cache.dispatch(url, result);
          }, {
              onabort: function (ev) {
                  Http.cache.remove(url);
              }
          });
          this.http.ajax(obj);
      }

      更改獲取文件路徑方法

      App.join = function (url) {
          if (typeof __dirname === "string") return require("path").join(__dirname, url);
          return url;
      };

      還需要更改Page獲取資源路徑方法

      function getBaseUrl(urlStr) {
          if (typeof __dirname === "string") {
              urlStr = require("path").join(__dirname, urlStr);
              return urlStr.split("\\").slice(0, -1).join("\\") + "\\";
          }
          return urlStr.split("/").slice(0, -1).join("/") + "/";
      }

      這樣子,就可以兼容electron的環(huán)境了。

      使用pwa技術(shù)

      有幸于web的發(fā)展進(jìn)程都是圍繞了漸進(jìn)增強(qiáng)的路線,所以很容易讓webapp支持pwa的各種技術(shù)

      1. 在index.html中加入以下js字段

         if ("serviceWorker" in navigator) {
             navigator.serviceWorker.register("./sw.js")
                 .then(function (registration) {
                     console.log("ServiceWorker registration successful with scope: ", 
                         registration.scope);
                 }).catch(function (err) {
                     console.log("ServiceWorker registration failed: ", err);
                 });
         }
      2. 然后在根目錄中加入sw.js,里面的內(nèi)容自定義,代碼略。

      總結(jié)

      這一篇作為完結(jié)篇,主要對(duì)常見(jiàn)的開(kāi)發(fā)問(wèn)題進(jìn)行了進(jìn)一步的擴(kuò)展。

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多