讀Zepto源碼之屬性操作

来源:http://www.cnblogs.com/hefty/archive/2017/06/25/7076114.html
-Advertisement-
Play Games

這篇依然是跟 相關的方法,側重點是操作屬性的方法。 讀Zepto源碼系列文章已經放到了github上,歡迎star: "reading zepto" 源碼版本 本文閱讀的源碼為 "zepto1.2.0" 內部方法 setAttribute 如果屬性值 存在,則調用元素的原生方法 設置對應元素的指定屬 ...


這篇依然是跟 dom 相關的方法,側重點是操作屬性的方法。

讀Zepto源碼系列文章已經放到了github上,歡迎star: reading-zepto

源碼版本

本文閱讀的源碼為 zepto1.2.0

內部方法

setAttribute

function setAttribute(node, name, value) {
  value == null ? node.removeAttribute(name) : node.setAttribute(name, value)
}

如果屬性值 value 存在,則調用元素的原生方法 setAttribute 設置對應元素的指定屬性值,否則調用 removeAttribute 刪除指定的屬性。

deserializeValue

// "true"  => true
// "false" => false
// "null"  => null
// "42"    => 42
// "42.5"  => 42.5
// "08"    => "08"
// JSON    => parse if valid
// String  => self
function deserializeValue(value) {
  try {
    return value ?
      value == "true" ||
      (value == "false" ? false :
       value == "null" ? null :
       +value + "" == value ? +value :
       /^[\[\{]/.test(value) ? $.parseJSON(value) :
       value) :
    value
  } catch (e) {
    return value
  }
}

函數的主體又是個很複雜的三元表達式,但是函數要做什麼事情,註釋已經寫得很明白了。

try catch 保證出錯的情況下依然可以將原值返回。

先將這個複雜的三元表達式拆解下:

value ? 相當複雜的表達式返回的值 : value

值存在時,就進行相當複雜的三元表達式運算,否則返回原值。

再來看看 value === "true" 時的運算

value == "true" || (複雜表達式求出的值) 

這其實是一個或操作,當 value === "true" 時就不執行後面的表達式,直接將 value === "true" 的值返回,也就是返回 true

再來看 value === false 時的求值

value == "false" ? false : (其他表達式求出來的值)

很明顯,value === "false" 時,返回的值為 false

value == "null" ? null : (其他表達式求出來的值)

value == "null" 時, 返回值為 null

再來看看數字字元串的判斷:

 +value + "" == value ? +value : (其他表達式求出來的值)

這個判斷相當有意思。

+valuevalue 隱式轉換成數字類型,"42" 轉換成 42"08" 轉換成 8abc 會轉換成 NaN+ "" 是將轉換成數字後的值再轉換成字元串。然後再用 == 和原值比較。這裡要註意,用的是 == ,不是 === 。左邊表達式不用說,肯定是字元串類型,右邊的如果為字元串類型,並且和左邊的值相等,那表示 value 為數字字元串,可以用 +value 直接轉換成數字。 但是以 0 開頭的數字字元串如 "08" ,經過左邊的轉換後變成 "8",兩個字元串不相等,繼續執行後面的邏輯。

如果 value 為數字,則左邊的字元串會再次轉換成數字後再和 value 進行比較,左邊轉換成數字後肯定為 value 本身,因此表達式成立,返回一樣的數字。

/^[\[\{]/.test(value) ? $.parseJSON(value) : value

這長長的三元表達式終於被剝得只剩下內衣了。

/^[\[\{]/ 這個正則是檢測 value 是否以 [ 或者 { 開頭,如果是,則將其作為對象或者數組,執行 $.parseJSON 方法反序列化,否則按原值返回。

其實,這個正則不太嚴謹的,以這兩個符號開頭的字元串,可能根本不是對象或者數組格式的,序列化可能會出錯,這就是一開始提到的 try catch 所負責的事了。

.html()

html: function(html) {
  return 0 in arguments ?
    this.each(function(idx) {
    var originHtml = this.innerHTML
    $(this).empty().append(funcArg(this, html, idx, originHtml))
  }) :
  (0 in this ? this[0].innerHTML : null)
},

html 方法既可以設置值,也可以獲取值,參數 html 既可以是固定值,也可以是函數。

html 方法的主體是一個三元表達式, 0 in arguments 用來判斷方法是否帶參數,如果不帶參數,則獲取值,否則,設置值。

(0 in this ? this[0].innerHTML : null)

先來看看獲取值,0 in this 是判斷集合是否為空,如果為空,則返回 null ,否則,返回的是集合第一個元素的 innerHTML 屬性值。

this.each(function(idx) {
  var originHtml = this.innerHTML
  $(this).empty().append(funcArg(this, html, idx, originHtml))
})

知道值怎樣獲取後,設置也就簡單了,要註意一點的是,設置值的時候,集合中每個元素的 innerHTML 值都被設置為給定的值。

由於參數 html 可以是固定值或者函數,所以先調用內部函數 funcArg 來對參數進行處理,funcArg 的分析請看 《讀Zepto源碼之樣式操作》 。

設置的邏輯也很簡單,先將當前元素的內容清空,調用的是 empty 方法,然後再調用 append 方法,插入給定的值到當前元素中。append 方法的分析請看《讀Zepto源碼之操作DOM

.text()

text: function(text) {
  return 0 in arguments ?
    this.each(function(idx) {
    var newText = funcArg(this, text, idx, this.textContent)
    this.textContent = newText == null ? '' : '' + newText
  }) :
  (0 in this ? this.pluck('textContent').join("") : null)
},

text 方法用於獲取或設置元素的 textContent 屬性。

先看不傳參的情況:

(0 in this ? this.pluck('textContent').join("") : null)

調用 pluck 方法獲取每個元素的 textContent 屬性,並且將結果集合併成字元串。關於 textContentinnerText 的區別,MDN上說得很清楚:

  • textContent 會獲取所有元素的文本,包括 scriptstyle 的元素
  • innerText 不會將隱藏元素的文本返回
  • innerText 元素遇到 style 時,會重繪

具體參考 MDN:Node.textContent

設置值的邏輯中 html 方法差不多,但是在 newText == null 時,賦值為 '' ,否則,轉換成字元串。這個轉換我有點不太明白, 賦值給 textContent 時,會自動轉換成字元串,為什麼要自己轉換一次呢?還有,textContent 直接賦值為 null 或者 undefined ,也會自動轉換為 '' ,為什麼還要自己轉換一次呢?

.attr()

attr: function(name, value) {
  var result
  return (typeof name == 'string' && !(1 in arguments)) ?
    (0 in this && this[0].nodeType == 1 && (result = this[0].getAttribute(name)) != null ? result : undefined) :
  this.each(function(idx) {
    if (this.nodeType !== 1) return
    if (isObject(name))
    for (key in name) setAttribute(this, key, name[key])
    else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
      })
},

attr 用於獲取或設置元素的屬性值。name 參數可以為 object ,用於設置多組屬性值。

判斷條件:

typeof name == 'string' && !(1 in arguments)

參數 name 為字元串,排除掉 nameobject 的情況,並且第二個參數不存在,在這種情況下,為獲取值。

(0 in this && this[0].nodeType == 1 && (result = this[0].getAttribute(name)) != null ? result : undefined)

獲取屬性時,要滿足幾個條件:

  1. 集合不為空
  2. 集合的第一個元素的 nodeTypeELEMENT_NODE

然後調用元素的原生方法 getAttribute 方法來獲取第一個元素對應的屬性值,如果屬性值 !=null ,則返回獲取到的屬性值,否則返回 undefined

再來看設置值的情況:

this.each(function(idx) {
  if (this.nodeType !== 1) return
  if (isObject(name))
    for (key in name) setAttribute(this, key, name[key])
  else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
    })

如果元素的 nodeType 不為 ELEMENT_NODE 時,直接 return

nameobject 時,遍歷對象,設置對應的屬性

否則,設置給定屬性的值。

.removeAttr()

removeAttr: function(name) {
  return this.each(function() {
    this.nodeType === 1 && name.split(' ').forEach(function(attribute) {
      setAttribute(this, attribute)
    }, this)
  })
},

刪除給定的屬性。可以用空格分隔多個屬性。

調用的其實是 setAttribute 方法,只將元素和需要刪除的屬性傳遞進去, setAttribute 就會將對應的元素屬性刪除。

.prop()

propMap = {
  'tabindex': 'tabIndex',
  'readonly': 'readOnly',
  'for': 'htmlFor',
  'class': 'className',
  'maxlength': 'maxLength',
  'cellspacing': 'cellSpacing',
  'cellpadding': 'cellPadding',
  'rowspan': 'rowSpan',
  'colspan': 'colSpan',
  'usemap': 'useMap',
  'frameborder': 'frameBorder',
  'contenteditable': 'contentEditable'
}
prop: function(name, value) {
  name = propMap[name] || name
  return (1 in arguments) ?
    this.each(function(idx) {
    this[name] = funcArg(this, value, idx, this[name])
  }) :
  (this[0] && this[0][name])
},

prop 也是給元素設置或獲取屬性,但是跟 attr 不同的是, prop 設置的是元素本身固有的屬性,attr 用來設置自定義的屬性(也可以設置固有的屬性)。

propMap 是將一些特殊的屬性做一次映射。

prop 取值和設置值的時候,都是直接操作元素對象上的屬性,不需要調用如 setAttribute 的方法。

.removeProp()

removeProp: function(name) {
  name = propMap[name] || name
  return this.each(function() { delete this[name] })
},

刪除元素固定屬性,調用對象的 delete 方法就可以了。

.data()

capitalRE = /([A-Z])/g
data: function(name, value) {
  var attrName = 'data-' + name.replace(capitalRE, '-$1').toLowerCase()

  var data = (1 in arguments) ?
      this.attr(attrName, value) :
  this.attr(attrName)

  return data !== null ? deserializeValue(data) : undefined
},

data 內部調用的是 attr 方法,但是給屬性名加上了 data- 首碼,這也是向規範靠攏。

name.replace(capitalRE, '-$1').toLowerCase()

稍微解釋下這個正則,capitalRE 匹配的是大寫字母,replace(capitalRE, '-$1') 是在大寫字母前面加上 - 連字元。這整個表達式其實就是將 name 轉換成 data-camel-case 的形式。

return data !== null ? deserializeValue(data) : undefined

如果 data 不嚴格為 null 時,調用 deserializeValue 序列化後返回,否則返回 undefined 。為什麼要用嚴格等 null 來作為判斷呢?這個我也不太明白,因為在獲取值時,attr 方法對不存在的屬性返回值為 undefined ,用 !== undefined 判斷會不會更好點呢?這樣 undefined 根本不需要再走 deserializeValue 方法。

.val()

val: function(value) {
  if (0 in arguments) {
    if (value == null) value = ""
    return this.each(function(idx) {
      this.value = funcArg(this, value, idx, this.value)
    })
  } else {
    return this[0] && (this[0].multiple ?
                       $(this[0]).find('option').filter(function() { return this.selected }).pluck('value') :
                       this[0].value)
  }
},

獲取或設置表單元素的 value 值。

如果傳參,還是慣常的套路,設置的是元素的 value 屬性。

否則,獲取值,看看獲取值的邏輯:

return this[0] && (this[0].multiple ? 
                   $(this[0]).find('option').filter(function() { return this.selected }).pluck('value') : 
                   this[0].value)

this[0].multiple 判斷是否為下拉列表多選,如果是,則找出所有選中的 option ,獲取選中的 optionvalue 值返回。這裡用到 pluck 方法來獲取屬性,具體的分析見:《讀Zepto源碼之集合元素查找

否則,直接返回第一個元素的 value 值。

.offsetParent()

ootNodeRE = /^(?:body|html)$/i
offsetParent: function() {
  return this.map(function() {
    var parent = this.offsetParent || document.body
    while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static")
      parent = parent.offsetParent
    return parent
  })
}

查找最近的祖先定位元素,即最近的屬性 position 被設置為 relativeabsolutefixed 的祖先元素。

var parent = this.offsetParent || document.body

獲取元素的 offsetParent 屬性,如果不存在,則預設賦值為 body 元素。

parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static"

判斷父級定位元素是否存在,並且不為根元素(即 body 元素或 html 元素),並且為相對定位元素,才進入迴圈,迴圈內是獲取下一個 offsetParent 元素。

這個應該做瀏覽器相容的吧,因為 offsetParent 本來返回的就是最近的定位元素。

.offset()

offset: function(coordinates) {
  if (coordinates) return this.each(function(index) {
    var $this = $(this),
        coords = funcArg(this, coordinates, index, $this.offset()),
        parentOffset = $this.offsetParent().offset(),
        props = {
          top: coords.top - parentOffset.top,
          left: coords.left - parentOffset.left
        }

    if ($this.css('position') == 'static') props['position'] = 'relative'
    $this.css(props)
  })
  if (!this.length) return null
  if (document.documentElement !== this[0] && !$.contains(document.documentElement, this[0]))
    return { top: 0, left: 0 }
    var obj = this[0].getBoundingClientRect()
    return {
      left: obj.left + window.pageXOffset,
      top: obj.top + window.pageYOffset,
      width: Math.round(obj.width),
      height: Math.round(obj.height)
    }
},

獲取或設置元素相對 document 的偏移量。

先來看獲取值:

if (!this.length) return null
if (document.documentElement !== this[0] && !$.contains(document.documentElement, this[0]))
  return { top: 0, left: 0 }
var obj = this[0].getBoundingClientRect()
return {
  left: obj.left + window.pageXOffset,
  top: obj.top + window.pageYOffset,
  width: Math.round(obj.width),
  height: Math.round(obj.height)
}

如果集合不存在,則返回 null

if (document.documentElement !== this[0] && !$.contains(document.documentElement, this[0]))
  return { top: 0, left: 0 }

如果集合中第一個元素不為 html 元素對象(document.documentElement !== this[0]) ,並且不為 html 元素的子元素,則返回 { top: 0, left: 0 }

接下來,調用 getBoundingClientRect ,獲取元素的 widthheight 值,以及相對視窗左上角的 lefttop 值。具體參見文檔: Element.getBoundingClientRect()

因為 getBoundingClientRect 獲取到的位置是相對視窗的,因此需要將視窗外偏移量加上,即加上 window.pageXOffsetwindow.pageYOffset

再來看設置值:

if (coordinates) return this.each(function(index) {
  var $this = $(this),
      coords = funcArg(this, coordinates, index, $this.offset()),
      parentOffset = $this.offsetParent().offset(),
      props = {
        top: coords.top - parentOffset.top,
        left: coords.left - parentOffset.left
      }

  if ($this.css('position') == 'static') props['position'] = 'relative'
  $this.css(props)
})

前面幾行都是固有的模式,不再展開,看看這段:

parentOffset = $this.offsetParent().offset()

獲取最近定位元素的 offset 值,這個值有什麼用呢?

props = {
  top: coords.top - parentOffset.top,
  left: coords.left - parentOffset.left
}
if ($this.css('position') == 'static') props['position'] = 'relative'
  $this.css(props)

我們可以看到,設置偏移的時候,其實是設置元素的 lefttop 值。如果父級元素有定位元素,那這個 lefttop 值是相對於第一個父級定位元素的。

因此需要將傳入的 coords.topcoords.left 對應減掉第一個父級定位元素的 offsettopleft 值。

如果當前元素的 position 值為 static ,則將值設置為 relative ,相對自身偏移計算出來相差的 lefttop 值。

.position()

position: function() {
  if (!this.length) return

  var elem = this[0],
    offsetParent = this.offsetParent(),
    offset = this.offset(),
    parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()
  offset.top -= parseFloat($(elem).css('margin-top')) || 0
  offset.left -= parseFloat($(elem).css('margin-left')) || 0
  parentOffset.top += parseFloat($(offsetParent[0]).css('border-top-width')) || 0
  parentOffset.left += parseFloat($(offsetParent[0]).css('border-left-width')) || 0
  return {
    top: offset.top - parentOffset.top,
    left: offset.left - parentOffset.left
  }
},

返回相對父元素的偏移量。

offsetParent = this.offsetParent(),
offset = this.offset(),
parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()

分別獲取到第一個定位父元素 offsetParent 及相對文檔偏移量 parentOffset ,和自身的相對文檔偏移量 offset 。在獲取每一個定位父元素偏移量時,先判斷父元素是否為根元素,如果是,則 lefttop 都返回 0

offset.top -= parseFloat($(elem).css('margin-top')) || 0
offset.left -= parseFloat($(elem).css('margin-left')) || 0

兩個元素之間的距離應該不包含元素的外邊距,因此將外邊距減去。

parentOffset.top += parseFloat($(offsetParent[0]).css('border-top-width')) || 0
parentOffset.left += parseFloat($(offsetParent[0]).css('border-left-width')) || 0

因為 position 返回的是距離第一個定位元素的 context box 的距離,因此父元素的 offsetlefttop 值需要將 border 值加上(offset 算是的外邊距距離文檔的距離)。

return {
  top: offset.top - parentOffset.top,
  left: offset.left - parentOffset.left
}

最後,將他們距離文檔的偏移量相減就得到兩者間的偏移量了。

.scrollTop()

scrollTop: function(value) {
  if (!this.length) return
  var hasScrollTop = 'scrollTop' in this[0]
  if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset
  return this.each(hasScrollTop ?
                   function() { this.scrollTop = value } :
                   function() { this.scrollTo(this.scrollX, value) })
},

獲取或設置元素在縱軸上的滾動距離。

先看獲取值:

var hasScrollTop = 'scrollTop' in this[0]
if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset

如果存在 scrollTop 屬性,則直接用 scrollTop 獲取屬性,否則用 pageYOffset 獲取元素Y軸在屏幕外的距離,也即滾動高度了。

return this.each(hasScrollTop ?
                   function() { this.scrollTop = value } :
                   function() { this.scrollTo(this.scrollX, value) })

知道了獲取值後,設置值也簡單了,如果有 scrollTop 屬性,則直接設置這個屬性的值,否則調用 scrollTo 方法,用 scrollX 獲取到 x 軸的滾動距離,將 y 軸滾動到指定的距離 value

.scrollLeft()

scrollLeft: function(value) {
  if (!this.length) return
  var hasScrollLeft = 'scrollLeft' in this[0]
  if (value === undefined) return hasScrollLeft ? this[0].scrollLeft : this[0].pageXOffset
  return this.each(hasScrollLeft ?
                   function() { this.scrollLeft = value } :
                   function() { this.scrollTo(value, this.scrollY) })
},

scrollLeft 原理同 scrollTop ,不再展開敘述。

系列文章

  1. 讀Zepto源碼之代碼結構
  2. 讀 Zepto 源碼之內部方法
  3. 讀Zepto源碼之工具函數
  4. 讀Zepto源碼之神奇的$
  5. 讀Zepto源碼之集合操作
  6. 讀Zepto源碼之集合元素查找
  7. 讀Zepto源碼之操作DOM
  8. 讀Zepto源碼之樣式操作

參考

License

License: CC BY-NC-ND 4.0

作者:對角另一面


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

-Advertisement-
Play Games
更多相關文章
  • 簡介 基於 webpack2 實現的多入口項目腳手架,主要使用 extract text webpack plugin 實現 js 、css 公共代碼提取,html webpack plugin 實現 html 多入口,less loader 實現 less 編譯,postcss loader 配置 ...
  • Web 前端代碼規範 最後更新時間:2017-06-25 原始文章鏈接:https://github.com/bxm0927/web-code-standards 此項目用於記錄規範的、高可維護性的前端代碼,這是通過分析 Github 眾多前端代碼庫,總結出來的前端代碼書寫規範。 目錄 前端普適性規 ...
  • 介紹: markdown是一種可以使用普通文本編譯器編寫的標記語言,通過簡單的標記語法,它可以使普通文本具有一定的格式。說的簡單一點,markdown其實就是一種簡單的文本,與普通的文本文件(txt文件)不同的是,支持一些特殊的符號,通過特殊的符號來表示不同的語義,並且給予不同的樣式。普通的文本文件 ...
  • javascript的數據類型 (symbol)一、原始數據類型 或 基本數據類型 6種 1,undefined (1,申明未賦值,2,函數沒有返回值)2,null (空,不存在)3,number (數字,小數和整數)4,boolean (布爾值,true,false) 5,string (字元串, ...
  • 一、position語法與結構 position語法: position : static absolute relative position參數:static : 無特殊定位,對象遵循HTML定位規則absolute : 將對象從文檔流中拖出,使用left,right,top,bottom等屬性 ...
  • 前言 眾所周知ES6新增的const關鍵字可以用來聲明常量,但是它只對基本數據類型生效(Number、String、Boolean等),那如果我們想聲明一個常量對象呢?該如何實現,Object內置對象早就替我們想到了,下麵來具體看一下 正題 一、先來看一下const方式來聲明基本類型常量 代碼: 運 ...
  • 今天的知識點主要是數組的迭代方法中的一種filter()方法、數組操作方法中的concat()方法以及字元串的replace()方法。filter()方法可用於使用函數判斷數組中各項的值中返回true值的結果所組成的數組。concat()可以複製和創建新數組。而replace()方法可以替換字元串中... ...
  • 聲明:該書的筆者為徐嬴老師,一名具有5年IOS開發經驗,和兩年RN開發經驗的老司機。 原文可以在gitbook上找到 筆者只是為他的書中提的的一些列問題,進行有償答疑。 有償答疑。本書將持續保持更新,有關問題可以加群討論。 正在上傳...取消 正在上傳...取消 正在上傳...取消 正在上傳...取 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...