記錄--“非主流” 的純前端性能優化

来源:https://www.cnblogs.com/smileZAZ/archive/2023/01/13/17050395.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 性能優化一直是前端研究的主要課題之一,因為不僅直接影響用戶體驗,對於商業性公司,網頁性能的優劣更關乎流量變現效率的高低。例如 DoubleClick by Google 發現: 如果頁面載入時間超過 3 秒,53% 的用戶會選擇終止當前操 ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

性能優化一直是前端研究的主要課題之一,因為不僅直接影響用戶體驗,對於商業性公司,網頁性能的優劣更關乎流量變現效率的高低。例如 DoubleClick by Google 發現:

  • 如果頁面載入時間超過 3 秒,53% 的用戶會選擇終止當前操作並離開
  • 網站載入時間在 5 秒內的發佈商比 19 秒內的廣告收入至少多出一倍

同時,性能優化學習的不斷深入,也同樣是一個專業前端工程師的進階之路。不過,隨著 HTTP/2 和 SSR(服務端渲染)的不斷普及,早期雅虎 35 條中的很多內容似乎已經顯得有些過時,不少純前端的細節優化方案也逐漸被認為微不足道。

但是,今天,我們依然想談幾個容易被很多前端工程師忽視,但卻卓有成效的純前端優化細節(技術框架以 Vue 為主)。

一、self

這裡想說的 self 並不是  WindowOrWorkerGlobalScope 下的 self,或者說 window 的替身,而是 const self = this  中的 self,或者說對象緩存。

在幾乎所有數據類型皆對象的 JavaScript 中,能有效降低屬性訪問深度的對象緩存是前端優化最基礎的課程,即使在瀏覽器已經進化到即使沒有明確地聲明緩存對象,內核解析時也會自動緩存以增加解析效率的今天。

良好的對象緩存不僅僅只是為了避免寫出下麵的代碼:

const obj = {
        human: {
                man: {}
        }
}
 
obj.human.man.age = 18
obj.human.man.name = 'Chen'
obj.human.man.career = 'programmer'

還有一個更加重要的原因:有效減少工程上線時壓縮後的代碼量!

首先,看一下上面代碼壓縮後的結果:

var ho={human:{man:{}}};ho.human.man.age=18,ho.human.man.name="Chen",ho.human.man.career="programmer";

然後,對屬性對象 man 做一次變數緩存:

const obj = {
        human: {
                man: {}
        }
}
const man = obj.human.man
 
man.age = 18
man.name = 'Chen'
man.career = 'programmer'

再次壓縮代碼後的結果:

var ho={human:{man:{}}},yo=ho.human.man;yo.age=18,yo.name="Chen",yo.career="programmer";

可以看到,對象緩存使得代碼容量有了明顯的減少。

那麼,對於實際的項目,變數緩存對總體代碼又會帶來多大容量的縮減呢?回到小節討論的開始,我們一起感受一下不緩存的 this 對象帶來的直觀震撼吧。

vivo 某個項目的一個 js 文件:

整個文件存在 3836 個 this,保存到本地大概 375 KB。如果緩存 this,代碼壓縮時 4 個字元的 this 會被壓縮成單字元變數。

整個文件的存儲大小降低到 364 KB,一個 this 對象緩存即可讓壓縮後的代碼容量下降超過 10 KB,註意,僅僅只是一個 this 對象!

二、Object.freeze ()

我們知道,在 Vue 組件或者 Vuex 的 state 中定義的數據是響應式的,當這些數據發生改變時,會通知 View 層更新界面。

首先,簡單回憶一下 Vue 響應式數據的原理,如下圖。

其中:

每一個組件 component 都擁有一個自己的觀察者 watcher,內部封裝了 Vue.prototype._render () 函數

每一個響應式數據屬性都擁有一個自己的依賴 dep 收集器,用以收集依賴該數據的組件的 watcher

響應式數據的三個基本步驟:

(1)組件數據的響應化流程:component (options) -> observe (data) -> Reactive Data

  • component 的數據部分,所有的 options.data 屬性通過 observe () 中的 Object.defineProperty () 函數轉換成訪問器屬性
  • 在每一個數據屬性被 Object.defineProperty () 轉換時的函數閉包空間中,存在一個自己的 dep 收集器

(2)響應式數據的依賴收集流程:component (template) -> watcher (vm._render ())(get) -> Reactive Data

  • component 的模板字元串,通過 Vue compiler 後生成渲染函數 vm._render ()

  • 每一個 component 擁有一個自己的觀察者 watcher,watcher 中封裝了 vm._render (),組件初次渲染時:

    (a)watcher 實例暫存在 Dep.target 屬性上

    (b)watcher 執行 vm._render () 函數,併進一步觸發 vm._render () 所依賴數據屬性的 getter

    (c)watcher 實例被收集到其所有依賴數據屬性的 dep 收集器中

(3)響應式數據改變時的重新渲染流程:Reactive Data (set) -> dep 收集器 -> watcher (vm._render ()) -> 非同步隊列

  • 當響應式數據被修改時,觸發數據屬性的 setter 函數
  • 數據屬性的 setter 函數會促使 dep 收集器將其收集的所有 watcher 實例推入非同步隊列 queueWatcher
  • 非同步隊列會被整體放入 nextTick () 中,即在下一個 tick 時被一次性全部執行;其實在 watcher () 中,渲染函數 vm._render () 是被封裝到 vm._update () 中的,它在執行時,會首先通過 vnode 的 diff 演算法比對找到修改的最少步驟,然後將最小的差異化渲染到頁面
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
...
    // 如果沒有舊的虛擬節點 prevVnode,表示是初次渲染,直接渲染到頁面
    if (!prevVnode) {
        // initial render
        vm.$el = vm.__patch__(
            vm.$el, vnode, hydrating, false /* removeOnly */,
            vm.$options._parentElm,
            vm.$options._refElm
        )
         
    // 非初次渲染,數據修改導致需要更新頁面時,進行 vnode diff 後將最小的差異化渲染到頁面
    } else {
        // updates
        vm.$el = vm.__patch__(prevVnode, vnode)
    }
...
}

每一個響應式數據對象屬性都一定會經歷三個基本步驟中的 1 和 2,不過,很多屬性在應用的整個生命周期中可能都不會經歷步驟 3,因為它們始終沒有改變。

但是,需要註意的是:之所以 Vue 會進行步驟 1 和 2 的操作,其實主要就是為了步驟 3 做準備,如果步驟 3 得不到執行,那麼前兩步的操作就是無意義的,或者說浪費。是否有方式避免這種浪費呢?有,就是 Object.freeze ()。

在將普通數據轉變成響應式數據的核心函數 defineReactive(Vue 2.6.x src/core/observer

/index.js) 中,有一個判斷,如果屬性本身不是 configurable 的,則不會被轉化成響應式數據,即不會執行上面的流程 1,與此同時,非響應式的數據也自然不會執行流程 2。

/**
 * Define a reactive property on an Object.
 */
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()
 
  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }
   
  ...
}

對於整個應用生命周期中,不會改變的數據,可以使用 Object.freeze () 將其 configurable 屬性置為 false;或者,將整個數據對象都 freeze 掉:

/**
 * 深度凍結對象
 */
function deepFreeze(obj) {
  Object.keys(obj).forEach(key => {
    const prop = obj[key]
 
    typeof prop === 'object' && prop !== null && deepFreeze(prop)
  })
 
  return Object.freeze(obj)
}

然後,“解凍” 部分需要改變的數據,並將其轉換成響應式數據。

註意,如果解凍的屬性值是對象,不能通過簡單地賦值 “解凍” 該對象,因為對象的引用傳遞特性導致其 configurable 依然是 false。可以使用下麵的簡單深複製方法,讓源對象丟失 configurable 屬性:

/**
 * 簡單對象深複製
 * -- 子對象引用關係丟失
 * -- 不適合迴圈引用數據
 */
function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj))
}

對於 Object.freeze () 帶來的性能提升,Vue 官方的一個 big table benchmark 里,做了一個 1000 x 10 的表格渲染對照實驗,使用 Object.freeze () 的渲染速度比不使用時快了 4 倍。

三、Pre 機制

瀏覽器的 pre(預)機制。

由於可動態修改 DOM 的天然屬性,JavaScript 不僅本身的執行是單線程的,而且其載入 / 解析執行時 HTML 的解析也是停止的,甚至在早期的瀏覽器中,其它資源的載入線程也會被同時阻止。

例如,在 IE7 中,頁面的瀑布流:

其他資源的載入、解析、執行不能和 JavaScript 的載入執行並行,這導致了頁面的載入時間很長。為了提高網路利用率,後來的主流瀏覽器都實現了預載入機制,即解析 HTML 頁面的同時,啟動一個輕量級解析器優先掃描 HTML 中的所有標記,尋找樣式表、腳本、圖像等靜態資源,儘可能地並行載入它們。

IE8 中的頁面瀑布流:

可以很明顯地看到,靜態資源被儘可能的並行載入了,即使在腳本載入解析的時候。

不過,隨著 Web 應用的越加複雜化,CSS 和 JavaScript 資源容量也越來越大,很多資源並不是一開始就出現在 HTML 中,而是後期被 CSS 和 JavaScript 動態引入的。為了儘可能提前解析 / 載入這些資源,瀏覽器開始提供豐富的 pre 機制。

1、Preload

瀏覽器內核的預載入機制只適用於在 HTML 中顯式聲明的資源,對於 CSS 和 JavaScript 中定義的資源可能並不起作用。preload 很好地剋服了這個問題,可以通過 preload 標識需要瀏覽器提前載入的重要資源,例如樣式表、腳本、圖片、字體甚至文檔。

# 預載入 css
<link rel="preload" as="style" href="/assets/css/app.css">
 
# 預載入 js
<link rel="preload" as="script" href="/assets/js/app.js">
 
# 預載入圖片
<link rel="preload" as="image" href="/assets/images/man.png">
 
# 預載入字體
<link rel="preload" as="font" href="/assets/font/rom9.ttf">

2、Prefetch

Prefetch 是一個低優先順序的資源提示,允許瀏覽器在後臺(空閑時)獲取將來可能用得到的資源,並且將他們存儲在瀏覽器的緩存中。有三種不同的 prefetch 類型:

(1)Link Prefetching:允許瀏覽器獲取資源並將他們存儲在緩存中。

  • HTML
<link rel="prefetch" href="/uploads/images/pic.png">
  • HTTP Header
Link: </uploads/images/pic.png>; rel=prefetch

(2)DNS Prefetching:允許瀏覽器在用戶瀏覽頁面時在後臺運行 DNS 解析。

可以在一個 link 標簽的屬性中添加 rel="dns-prefetch' 來對指定進行 DNS prefetching 的 URL:

<!-- 功能變數名稱 dns-prefetch -->
<link rel="dns-prefetch" href="//sthf.vivo.com.cn">
<link rel="dns-prefetch" href="//apph5wsdl.vivo.com.cn">
<link rel="dns-prefetch" href="//cfg-stsdk.vivo.com.cn">
<link rel="dns-prefetch" href="//trace-h5sdk.vivo.com.cn">
<link rel="dns-prefetch" href="//topicstatic.vivo.com.cn">

DNS 請求在帶寬方面流量非常小,可是延遲會很高,尤其是在移動設備上。

(3)Prerendering:和 prefetching 非常相似,優化可能資源的載入,區別是 prerendering 在後臺渲染整個未來可能載入的頁面。

<link rel="prerender" href="https://www.vivo.com.cn">

這三種類型中,Link Prefetching 和前文的 preload 比較相似,但是優先順序較低,而且更加專註於下一個頁面;Prerendering 會預渲染一個用戶不一定訪問的完整頁面,這會導致較高的帶寬浪費和資源占用,應用的機會可能並不多;而 DNS Prefetching 是當前我們應用最多的。

在瀏覽一個網頁時,DNS 解析總是發生在一個新功能變數名稱初次被解析的時候,如果功能變數名稱解析是獨立串列的(如頁面主域的解析),解析時間的長短(如下圖中的 vivo 游戲大會員 supermember.vivo.com.cn)將直接影響頁面的打開速度。得益於現代瀏覽器的預載入機制,除頁面主域以外的其他資源功能變數名稱的解析時間,一定程度上很好地掩蔽在了資源的並行載入過程中。

但是,dns 的解析並不一定是穩定可靠的,時間跨度從幾十 ms 至過千 ms 都有可能,如果頁面主要資源的 dns 解析時間過長,就會直接影響用戶的使用體驗,所以,恰當的 DNS Prefetching 依然很有必要。

3、Preconnect

相比於 DNS Prefetching,Preconnect 除了提前完成功能變數名稱的 DNS 解析,還更近一步地完成 http 連接通道的建立,這包括 TCP 握手,TLS 協商等。

使用方法:

<!-- 功能變數名稱 preconnect -->
<link rel="preconnect" href="//sthf.vivo.com.cn">
<link rel="preconnect" href="//apph5wsdl.vivo.com.cn">
<link rel="preconnect" href="//cfg-stsdk.vivo.com.cn">
<link rel="preconnect" href="//trace-h5sdk.vivo.com.cn">
<link rel="preconnect" href="//topicstatic.vivo.com.cn">

可以同時設置 Preconnect 和 DNS Prefetching,讓瀏覽器優先進行 Preconnect,在不支持的前提下,優雅回退至 DNS Prefetching。

四、並行載入

隨著 Web 應用的複雜化大型化,使用 MV* 類框架( Vue、React、Angular 等)進行快捷開發已經成為前端開發的主流模式。但是,這些框架都存在基礎框架包較大,解析時間較長的問題。

首先,我們看一個標準的 Vue 項目 - vivo 游戲大會員 Chrome 開發者工具中的瀑布流:

可以看出資源的載入存在明顯的層級結構:

  • 第 1 級:獲取頁面 HTML 文檔並解析
  • 第 2 級:獲取頁面 CSS 和 JavaScript 文件並解析
  • 第 3 級:請求介面獲取服務端數據
  • 第 4 級:頁面渲染載入主頁圖片等資源

同時,可以發現由於 JavaScript 文件較大,解析時間較長,第 2 級與第 3 級,以及第 3 級和第 4 級之間的時間間隔較大。如果這種串列的逐級解析載入模式能夠改變為並行的載入模式,勢必將顯著降低頁面的載入時長。

註意,如果項目未開啟 HTTP/2,可能需要增加資源功能變數名稱以突破瀏覽器對單個功能變數名稱並行下載數量的限制。當然,在下麵實現並行載入的過程中,我們也使用了很明顯的反模式 - 通過 window 全局變數傳遞數據。不過,在沒有更好的實現方案前,通過有限可控的反模式實現更好的頁面體驗還是值得的。

下麵,我們討論如何將串列載入的資源變成並行載入。

1、介面

大多數時候,介面的請求並不需要等待 Vue.js 載入解析完成,完全可以在 HTML 中通過幾行簡單的 JavaScript 代碼提前進行 Ajax 請求。

/**
 * 主介面請求前置
 */
var win = window
var xhr = new XMLHttpRequest()
 
xhr.open('get', '/api/member/masterpage?t=' + Date.now(), true)
xhr.onerror = function () { win._mainPageData = { msg: '請求出錯', code: 10000 } }
xhr.timeout = 10000
xhr.ontimeout = function () { win._mainPageData = { msg: '請求超時', code: 10001 } }
xhr.onreadystatechange = function () {
  try {
    var status = xhr.status
 
    if (xhr.readyState == 4) {
      win._mainPageData = (status >= 200 && status < 300) || status == 304
      ? JSON.parse(xhr.responseText)
      : {
          msg: '',
          code: 10002
        }
    }
  } catch (e) { /* 請求超時時readyState可能也是4,但是訪問status可能出錯 */ }
}
xhr.send(null)

需要註意的是,直接插入到 HTML 中的 JavaScript 可能不會通過 babel 的編譯,所以不要使用 ES6 語法,因為很可能一個簡單的 const 就會讓 Android 5/4.4.4 直接白屏。

2、圖片

通常,Web 應用主頁首屏會有幾張裝飾性且容量較大的圖片,將圖片寫在 Vue 組件中,圖片的載入會推遲到組件解析完成,我們同樣可以在 HTML 中提前載入這些圖片。

一種方式是使用前文 Pre 機制中提到的 Preload:

<link rel="preload" as="image" href="/assets/images/00.png">
<link rel="preload" as="image" href="/assets/images/01.png">
<link rel="preload" as="image" href="/assets/images/02.png">

儘管 Preload 擁有更簡潔且不阻塞頁面渲染的優點,但是這種方式當前依然存在兩個明顯的問題:

(1)低版本 Android 不支持 Preload

(2)如果項目需要判斷環境是否支持 webp 格式,以便有區分地載入圖片的 webp 格式和普通格式,Preload 就不好辦了,除非你兩種格式都載入,但很明顯這樣會造成嚴重的流量浪費。

所以,我們可以使用 JavaScript 代碼在判斷環境是否支持 webp 格式後,載入需要格式的圖片:

/**
 * webp 探測
 */
var win = window
var doc = document
 
win._supportsWebP = (function () {
    var mime = 'image/webp'
  var canvas = typeof doc === 'object' ? doc.createElement('canvas') : {}
  canvas.width = canvas.height = 1
  return canvas.toDataURL ? canvas.toDataURL(mime).indexOf(mime) === 5 : false
}())
 
/**
 * 圖片預載入
 */
var body = doc.body
var parentNode = document.createDocumentFragment()
var imgPostfix = '.png' + (win._supportsWebP ? '.webp' : '')
var linkPrefix = '//topicstatic.vivo.com.cn/f5ZUD0HxhQMn3J32/wukong/img/'
var imgPreLoad = win._imgPreLoad = [
  linkPrefix + '5f88483c-4d76-42d4-912d-35c8c92be8e6' + imgPostfix,
  linkPrefix + '5ee4c220-cd98-4d8c-9cdc-5fca3e103227' + imgPostfix,
  linkPrefix + '131008e1-9230-480c-934a-30f9f83e17ae' + imgPostfix,
  linkPrefix + 'cee41d4d-853d-4677-9a20-b9b5e1c4ffbenwebp' + imgPostfix,
  linkPrefix + 'ddf2cad0-d334-437a-8923-7b36a65544d1nwebp' + imgPostfix
]
 
imgPreLoad.forEach(function (link) {
  var img = doc.createElement('img')
 
  img.src = link
  img.style.left = '-9999px'
  img.style.position = 'absolute'
 
  parentNode.appendChild(img)
})
 
body.insertBefore(parentNode, body.firstChild)

此外,在合適的時候,可以嘗試使用 svg 圖片,除了永不失真的圖片質量,更重要的是,svg 可以很好地打包到代碼中,並始終保持比 base64 更好的可讀性。

3、字體

有的時候,為了實現更好的視覺效果,並能應對動態變化的介面數據,我們會引入一些系統不支持的字體,比如數字字體 Rom9。

不過,我們可能只是用到字體中的某一部分,比如數字,此時除了使用字體編輯軟體刪除不需要的字元外,我們還可以將字體 base64 化後整合到 CSS 中以便更好地並行載入:

@font-face{
    src: url(data:font/truetype;charset=utf-8;base64,AA...省略...AK) format("truetype");
    font-style: normal;
    font-weight: normal;
    font-family: "Rom9";
    font-display: swap;
}

本文轉載於:

https://my.oschina.net/vivotech/blog/4633382

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、合作背景 萬里開源軟體有限公司 ​ 北京萬里開源軟體有限公司,是專註於國產自主可控資料庫產品研發超 20年的國家高新技術企業,參與多個國家級的資料庫行業標準制定工作。本次用於測試的 GreatSQL 開源資料庫是適用於金融級應用的國內自主 MySQL 版本,專註於提升 MGR 可靠性及性能,支持 ...
  • Calcite在大數據系統中有著廣泛的運用, 比如Apache Flink, Apache Drill等都大量使用了Calcite,理解Calcite的原理可以說已經成為理解大數據系統中SQL訪問層實現原理的必備條件之一。 但是不少人在學習Calcite的過程中都發現關於Calcite的實踐案例其實 ...
  • 摘要:通過雲服務形式提供資料庫功能的雲資料庫應運而生,但這還僅僅是資料庫變革的開端。 本文分享自華為雲社區《透視華為云云原生資料庫的前世今生及未來演進,能給行業帶來哪些啟發?》,作者:萬佳。 自雲計算出現後,風雲變幻十餘載,硬體、軟體行業都經歷了重構變革所帶來的機遇與激蕩。企業 IT 基礎設施逐漸雲 ...
  • Day1 選擇 595. 大的國家 World表: + + + | Column Name | Type | + + + | name | varchar | | continent | varchar | | area | int | | population | int | | gdp | in ...
  • 如果入職一些中小型公司,往往需要接手一些很“坑”的項目,到底多坑就不牢騷了,只講一下,如果破解這些歷史遺留的項目問題。項目代碼可能短時間無法進行通讀研究,我們就需要從底層資料庫進行挖掘問題,有經驗的老開發工程師,他會開啟Sql Server Profiler 這個功能,進行語句的跟蹤。這個是一個很好 ...
  • 對於一個集中式緩存的分散式能力構建,必須要額外提供一些機制,來保障數據在各個節點上的安全與一致性。本文以Redis為代表,看下集Redis面對上述問題交出的是怎樣一份答卷。 ...
  • 一、系統性能問題五大特性 二、系統性能排查方略 三、MySQL開發規範和常見調優策略 四、MySQL性能管控體系 五、未來展望 ...
  • 安裝 wavesurfer.js 在項目中安裝 wavesurfer.js npm install --save wavesurfer.js 常規方式引入 如果你的根目錄中沒有 components 目錄則需要創建該目錄,併在此目錄中創建 WaveSurfer.vue 內容如下: <template ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...