摘要:如何在新的技術(shù)背景下讓前端數(shù)據(jù)采集工作更加完善高效,是本文討論的重點(diǎn)。具體來(lái)說(shuō),我們對(duì)前端的數(shù)據(jù)采集具體主要分為路由切換性能資源錯(cuò)誤日志上報(bào)路由切換等前端技術(shù)的快速發(fā)展使單頁(yè)面應(yīng)用盛行。
隨著業(yè)務(wù)的快速發(fā)展,我們對(duì)生產(chǎn)環(huán)境下的問(wèn)題感知能力越來(lái)越關(guān)注。作為距離用戶最近的一層,前端的表現(xiàn)是否可靠、穩(wěn)定、好用,很大程度上決定著用戶對(duì)整個(gè)產(chǎn)品的體驗(yàn)和感受。因此,對(duì)于前端的監(jiān)控不容忽視。
搭建一套前端監(jiān)控平臺(tái)需要考慮的方面很多,比如數(shù)據(jù)采集、埋點(diǎn)模式、數(shù)據(jù)處理和分析、報(bào)警以及監(jiān)控平臺(tái)在具體業(yè)務(wù)中的應(yīng)用等等。在這所有環(huán)節(jié)中,準(zhǔn)確、完整、全面的數(shù)據(jù)采集是一切的前提,也為后續(xù)的用戶精細(xì)化運(yùn)營(yíng)提供基礎(chǔ)。
前端技術(shù)的日新月異給數(shù)據(jù)采集也帶來(lái)了變化和挑戰(zhàn),傳統(tǒng)的手工打點(diǎn)模式已經(jīng)不能滿足需求。如何在新的技術(shù)背景下讓前端數(shù)據(jù)采集工作更加完善、高效,是本文討論的重點(diǎn)。
前端監(jiān)控?cái)?shù)據(jù)采集在采集數(shù)據(jù)之前,首先要考慮采集什么樣的數(shù)據(jù)。我們重點(diǎn)關(guān)注兩類數(shù)據(jù),一類是與用戶體驗(yàn)相關(guān)的,如首屏?xí)r間、文件加載時(shí)間、頁(yè)面性能等;另外是幫助我們及時(shí)感知產(chǎn)品上線后是否出現(xiàn)異常的,比如資源錯(cuò)誤、API 響應(yīng)時(shí)間等。具體來(lái)說(shuō),我們對(duì)前端的數(shù)據(jù)采集具體主要分為:
路由切換 (href、hashchange、pushState)
JsError
性能 (performance)
資源錯(cuò)誤
API
日志上報(bào)
路由切換Vue、React、Angular 等前端技術(shù)的快速發(fā)展使單頁(yè)面應(yīng)用盛行。我們都知道,傳統(tǒng)的頁(yè)面應(yīng)用是用一些超鏈接來(lái)實(shí)現(xiàn)頁(yè)面切換和跳轉(zhuǎn)的,而單頁(yè)面應(yīng)用是使用各自的路由系統(tǒng)來(lái)管理前端的每一個(gè)頁(yè)面切換,例如 vue-router、react-router 等,跳轉(zhuǎn)時(shí)僅刷新局部資源 ,js、css 等公共資源只需要加載一次,這就使傳統(tǒng)網(wǎng)頁(yè)進(jìn)入離開(kāi)的方式只有第一次打開(kāi)能被記錄。單頁(yè)應(yīng)用后續(xù)所有路由切換的方式有兩種,一種是 Hash,一種是 HTML5 推出的 History API。
1. href
href 為頁(yè)面初始化的第一次進(jìn)入,這里只需要單純上報(bào)「進(jìn)入頁(yè)面」事件即可。
2. hashchange
Hash 路由一個(gè)明顯的標(biāo)志是帶有「 # 」。Hash 的優(yōu)勢(shì)是兼容性更好,但問(wèn)題在于 URL 中一直存在「 # 」并不美觀。我們主要通過(guò)監(jiān)聽(tīng) URL 中的 hashchange 來(lái)捕獲具體的 hash 值進(jìn)行檢測(cè)。
window.addEventListener("hashchange",?function()?{ ????//?上報(bào)【進(jìn)入頁(yè)面】事件 },?true)
需要注意的是,在新版 vue-router 中如果瀏覽器支持 history,即使 mode 選擇 hash 也會(huì)優(yōu)先選擇 history 模式,雖然表現(xiàn)形式暫時(shí)還是 # 號(hào),但實(shí)際上是模擬的,所以千萬(wàn)不要認(rèn)為自己在 mode 選擇了hash 就一定會(huì)是 hash。
3. History API
History 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法進(jìn)行路由切換,是目前主流的無(wú)刷新切換路由方式。與 hashchange 只能改變 # 后面的代碼片段相比,History API (pushState、replaceState) 給了前端完全的自由。
PopState 是瀏覽器返回事件的回調(diào),但是更新路由的 pushState、replaceState 并沒(méi)有回調(diào)事件,因此,還需要分別在 history.pushState() 和 history.replaceState() 方法里處理 URL 的變化。在這里,我們運(yùn)用到了一種類似 Java 的 AOP 編程思想,對(duì) pushState 和 replaceState 進(jìn)行改造。
AOP (Aspect-oriented programming)即面向切面編程,提倡針對(duì)同一類問(wèn)題進(jìn)行統(tǒng)一處理。AOP 的核心思想是讓某個(gè)模塊能夠重用,它采用橫向抽取機(jī)制,將功能代碼從業(yè)務(wù)邏輯代碼中分離出來(lái),擴(kuò)展功能而不修改源代碼,相比封裝來(lái)說(shuō)隔離得更加徹底。
下面介紹我們的具體改造方式:
//?第一階段:我們對(duì)原生方法進(jìn)行包裝,調(diào)用前執(zhí)行?dispatchEvent?了一個(gè)同樣的事件 function?aop?(type)?{ ????var?source?=?window.history[type]; ????return?function?()?{ ????????var?event?=?new?Event(type); ????????event.arguments?=?arguments; ????????window.dispatchEvent(event); ????????var?rewrite?=?source.apply(this,?arguments); ????????return?rewrite; ????}; } //?第二階段:將?pushState?和?replaceState?進(jìn)行基于?AOP?思想的代碼注入 window.history.pushState?=?aop("pushState"); window.history.replaceState?=?aop("replaceState");?//?更改路由,不會(huì)留下歷史記錄 //?第三階段:捕獲pushState?和?replaceState window.addEventListener("pushState",?function()?{ ????//?上報(bào)【進(jìn)入頁(yè)面】事件 },?true) window.addEventListener("replaceState",?function()?{ ????//?上報(bào)【進(jìn)入頁(yè)面】事件 },?true)
window.history.pushState 實(shí)際調(diào)用關(guān)系如圖:
至此,我們對(duì) pushState、replaceState 改造完畢,實(shí)現(xiàn)了有效地捕獲路由切換??梢钥吹剑覀?cè)诓磺秩霕I(yè)務(wù)代碼的情況下,對(duì) window.history.pushState 進(jìn)行了擴(kuò)展,在調(diào)用的同時(shí)會(huì)主動(dòng) dispatchEvent 一個(gè) pushState。
但在這里我們也能看到一個(gè)弊端,就是如果 AOP 代理函數(shù)發(fā)生 JS 錯(cuò)誤,將會(huì)阻斷后續(xù)的調(diào)用關(guān)系,使實(shí)際的 window.history.pushState 無(wú)法被調(diào)用。所以在使用此方式的時(shí)候,要對(duì) AOP 代理函數(shù)的內(nèi)容做好完善的 try catch,來(lái)防止業(yè)務(wù)上出現(xiàn)異常。
*__Tips:想自動(dòng)捕獲頁(yè)面停留時(shí)間只需要在下一個(gè)進(jìn)入頁(yè)面事件觸發(fā)時(shí),通過(guò)上一個(gè)頁(yè)面的打點(diǎn)時(shí)間和當(dāng)前時(shí)間做差值即可,這時(shí)候可以上報(bào)一個(gè)【離開(kāi)頁(yè)面】事件。
JsError前端項(xiàng)目中,由于 JavaScript 本身是一個(gè)弱類型語(yǔ)言,加上瀏覽器環(huán)境的復(fù)雜性、網(wǎng)絡(luò)問(wèn)題等,很容易發(fā)生錯(cuò)誤。因此做好網(wǎng)頁(yè)錯(cuò)誤監(jiān)控,不斷優(yōu)化代碼,提高代碼健壯性是一項(xiàng)很重要的工作。
JsError 的捕獲可以幫助我們分析和監(jiān)控線上問(wèn)題,它與我們?cè)?Chrome 瀏覽器的調(diào)試工具 Console 中看到的內(nèi)容一致。
1. window.onerror
我們使用 window.onerror 捕獲一般情況下 JS 錯(cuò)誤的異常信息。捕獲 JS 錯(cuò)誤的方式有兩種,window.onerror 和 window.addEventListener(‘error’)。一般情況下,捕獲 JS 異常不推薦使用 addEventListener(‘error’),主要是因?yàn)樗鼪](méi)有堆棧信息,而且還需要對(duì)捕獲到的信息做區(qū)分,因?yàn)樗鼤?huì)將所有異常信息捕獲到,包括資源加載錯(cuò)誤等。
window.onerror?=?function?(msg,?url,?lineno,?colno,?stack)?{ ????//?上報(bào)?【js錯(cuò)誤】事件 }
2. Uncaught (in promise)
當(dāng) Promise 內(nèi)發(fā)生 JS 錯(cuò)誤或者 reject 信息未被業(yè)務(wù)處理的情況時(shí),會(huì)拋出一個(gè) unhandledrejection,并且這個(gè)錯(cuò)誤不會(huì)被 window.onerror 以及 window.addEventListener("error") ?捕獲,這里需要用專門的 window.addEventListener("unhandledrejection") ?進(jìn)行捕獲處理:
window.addEventListener("unhandledrejection",?function?(e)?{ ????var?reg_url?=?/(([^)]*))/; ????var?fileMsg?=?e.reason.stack.split(" ")[1].match(reg_url)[1]; ????var?fileArr?=?fileMsg.split(":"); ????var?lineno?=?fileArr[fileArr.length?-?2]; ????var?colno?=?fileArr[fileArr.length?-?1]; ????var?url?=?fileMsg.slice(0,?-lno.length?-?cno.length?-?2);},?true); ????var?msg?=?e.reason.message; ????//?上報(bào)?【js錯(cuò)誤】事件 }
我們注意到 unhandledrejection 因?yàn)槔^承自 PromiseRejectionEvent,PromiseRejectionEvent 又繼承自 Event,所以 msg、url、lineno、colno、stack 以字符串形式放到了 e.reason.stack 中,我們需要解析出來(lái)上述參數(shù)來(lái)和 onerror 參數(shù)對(duì)齊,為后續(xù)監(jiān)控平臺(tái)的指標(biāo)統(tǒng)一化打下基礎(chǔ)。
3.?常見(jiàn)問(wèn)題
"Script error."
如果出現(xiàn)捕獲的 msg 全部為 "Script error." ,問(wèn)題在于你的 JS 地址和當(dāng)前網(wǎng)頁(yè)不在同一個(gè)域下。因?yàn)槲覀円?jīng)常在線上的版本做靜態(tài)資源 CDN 化,會(huì)導(dǎo)致常訪問(wèn)的頁(yè)面跟腳本文件來(lái)自不同的域名。這時(shí)如果沒(méi)有進(jìn)行額外的配置,瀏覽器出于安全方面的設(shè)計(jì)就容易出現(xiàn) "Script error."。我們可以利用目前流行的 Webpack 打包工具來(lái)處理此類問(wèn)題。
//?webpack?config?配置 //?處理?html?注入?js?添加跨域標(biāo)識(shí) plugins:?[ ????new?HtmlWebpackPlugin({ ??????filename:?"html/index.html", ??????template:?HTML_PATH, ??????attributes:?{ ????????crossorigin:?"anonymous" ??????} ????}), ????new?HtmlWebpackPluginCrossorigin({ ??????inject:?true ????}) ] //?處理按需加載的?js?添加跨域標(biāo)識(shí) output:?{ ????crossOriginLoading:?true }
SourceMap
大部分場(chǎng)景下,生產(chǎn)環(huán)境中的代碼都是經(jīng)過(guò)壓縮合并的,這使得我們捕獲到的錯(cuò)誤很難映射到具體的源碼,為我們解決問(wèn)題帶來(lái)很大困擾,這里簡(jiǎn)要提出 2 個(gè)解決方案的思路。
生產(chǎn)環(huán)境我們需要添加 sourceMap 配置,這會(huì)導(dǎo)致安全隱患,因?yàn)檫@樣外網(wǎng)就可以通過(guò) sourceMap 進(jìn)行源碼映射。為了降低風(fēng)險(xiǎn),我們可以通過(guò)如下方式:
將 sourceMap 生成的 .map 文件設(shè)置公司內(nèi)網(wǎng)訪問(wèn),降低源碼安全風(fēng)險(xiǎn)
在發(fā)布代碼到 CDN 的時(shí)候,將 .map 文件存儲(chǔ)到公司內(nèi)網(wǎng)下
這時(shí)我們已經(jīng)擁有了 .map 文件,后續(xù)要做的就是通過(guò)捕獲到的 lineno、colno、url 調(diào)用 mozilla/source-map 庫(kù)進(jìn)行源碼映射,即可拿到真實(shí)的源碼錯(cuò)誤信息。
性能性能指標(biāo)的獲取相對(duì)比較簡(jiǎn)單,在 onload 之后讀取 window.performance 即可,里面包含了性能、內(nèi)存等信息。這部分內(nèi)容在很多現(xiàn)有的文章中都有介紹,因篇幅所限不在本文做過(guò)多展開(kāi),之后在相關(guān)主題文章中我們會(huì)有相關(guān)探討,感興趣的朋友可以添加「馬蜂窩技術(shù)」公眾號(hào)持續(xù)關(guān)注。
資源錯(cuò)誤首先我們要明確下資源錯(cuò)誤捕獲的使用場(chǎng)景,更多的是感知 DNS 劫持 及 CDN 節(jié)點(diǎn)異常等,具體方式如下:
window.addEventListener("error",?function?(e)?{ ????var?target?=?e.target?||?e.srcElement; ????if?(target?instanceof?HTMLScriptElement)?{ ????????//?上報(bào)?【資源錯(cuò)誤】事件 ????} },?true)
這里只做基本演示,實(shí)際環(huán)境中我們會(huì)關(guān)心更多的 Element 錯(cuò)誤,如 css、img、woff 等,大家可以根據(jù)不同的場(chǎng)景自行添加。
*資源錯(cuò)誤的使用場(chǎng)景更多依賴其他幾個(gè)維度,如:__地域、運(yùn)營(yíng)商等,后續(xù)的篇幅中我們會(huì)具體講解。
API市面上主流的框架(如 Axios、jQuery.ajax 等)中,基本上所有的 API 請(qǐng)求都是基于xmlHttpRequest 或者 fetch,所以捕獲全局接口錯(cuò)誤的方式就是封裝 xmlHttpRequest 或者 fetch。這里,我們的 SDK 仍然使用到上文提及的 AOP 思想,對(duì) API 進(jìn)行攔截。
1. XmlHttpRequest
var?xhr?=?window.XMLHttpRequest; var?_open?=?xhr.prototype.open; var?_send?=?xhr.prototype.send; var?attr?=?{}; var?openReplacement?=?function?(method,?url)?{ ????//?可以存儲(chǔ)method、url、時(shí)間打點(diǎn)等信息 ????attr.duration?=?new?Date().getTime(); ????_open.apply(this,?arguments); } var?sendReplacement?=?function?()?{ ????methods.addEvent(this,?"readystatechange",?function?(attr)?{ ????????//?可以存儲(chǔ)response的status、計(jì)算客戶端實(shí)際響應(yīng)時(shí)間 ????????attr.status?=?this.status; ????????attr.duration?=?new?Date().getTime()?-?attr.duration; ????????//?上報(bào)【API】事件 ????}.bind(this,?,?JSON.parse(JSON.stringify(attr)))); ????_send.apply(this,?arguments); } xmlhttp.prototype.open?=?openReplacement; xmlhttp.prototype.send?=?sendReplacement;
2. Fetch
需要注意的是,API 攔截一定要對(duì) SDK 自己上報(bào)的 API 設(shè)置好忽略,否則將會(huì)導(dǎo)致循環(huán)上報(bào)問(wèn)題。
var?_fetch?=?window.fetch; window.fetch?=?function?()?{ ????var?attr?=?{ ????????method:?arguments[1].method, ????????url:?arguments[0], ????????duration:?new?Date().getTime() ????}; ????return?_fetch.apply(this,?arguments).then(res?=>?{ ????????attr.status?=?res.status; ????????attr.duration?=?new?Date().getTime()?-?attr.duration; ????????//?上報(bào)【API】事件 ????????return?res; ????}); }日志上報(bào)
為了監(jiān)控前端應(yīng)用是否正常運(yùn)行,通常會(huì)在前端收集錯(cuò)誤與性能等數(shù)據(jù),最終將這些數(shù)據(jù)上報(bào)到服務(wù)端。因?yàn)槿罩旧蠄?bào)并不是應(yīng)用的主要功能邏輯,優(yōu)先級(jí)比較低,所以我們?cè)诖_保日志數(shù)據(jù)上報(bào)更高效的同時(shí),還應(yīng)該考慮如何盡可能地減少與其他關(guān)鍵操作的資源爭(zhēng)搶。
1. sendBeacon
navigator.sendBeacon() 方法主要用于滿足統(tǒng)計(jì)和診斷代碼的需要。這些代碼通常會(huì)在卸載文檔之前,嘗試通過(guò) HTTP 將少量數(shù)據(jù)異步傳輸?shù)?Web 服務(wù)器。它解決了日志上報(bào)在 unload 時(shí)成功率很低的問(wèn)題。我們?cè)诼顸c(diǎn)時(shí)有很多對(duì)離開(kāi)頁(yè)面時(shí)上報(bào)的需求,因?yàn)?SendBeacon 是異步的,不會(huì)影響當(dāng)前頁(yè)到下一個(gè)頁(yè)面的跳轉(zhuǎn)速度,可以更可靠地保障事件上報(bào)成功率,并且不影響路由切換。
window.navigator.sendBeacon("上報(bào)事件的api",?"數(shù)據(jù)參數(shù)")
2. img.src
當(dāng)瀏覽器不支持 navigator.sendBeacon 時(shí),我們可以采用模擬圖片加載的方式發(fā)送日志上報(bào)事件,且不會(huì)存在跨域問(wèn)題。
var?img?=?new?Image(); img.src?=?API?+?"?"?+?"數(shù)據(jù)參數(shù)"
3. 關(guān)于 XmlHttpRequest
這里不推薦用 XmlHttpRequest。XHR 雖然支持異步請(qǐng)求,直接發(fā)送數(shù)據(jù)到后端,但是會(huì)受到跨域和同源的限制。而通過(guò)日志上報(bào) API 跟業(yè)務(wù)是不在一個(gè)域下的,如果采用這種模式需要設(shè)置 Access-Control-Allow-Origin:* 跨域,非常不方便,并且在 unload 情況下上報(bào)發(fā)生的丟包率最高。
總結(jié)來(lái)看,日志上報(bào)推薦采用 sendBeacon -> img.src。在不影響用戶路由切換和阻塞用戶的情況下丟包率可以控制在 10%-30%,具體要看用戶群體對(duì)應(yīng)的環(huán)境。
小結(jié)高效的前端數(shù)據(jù)采集對(duì)于搭建前端監(jiān)控平臺(tái)來(lái)說(shuō)非常關(guān)鍵。本文我們分享了馬蜂窩在保證數(shù)據(jù)采集及時(shí)、準(zhǔn)確、全面等方面的一些思路和實(shí)踐。需要提示大家注意的是,文中涉及到的演示只做了核心代碼的關(guān)鍵描述,不具備生產(chǎn)使用,我們?cè)趯?shí)際使用中需要做好兼容及容錯(cuò)。
本文也將作為馬蜂窩前端監(jiān)控平臺(tái)系列文章的開(kāi)篇,今后還將陸續(xù)推出埋點(diǎn)模式、數(shù)據(jù)處理和分析、報(bào)警以及監(jiān)控平臺(tái)在具體業(yè)務(wù)中的應(yīng)用等內(nèi)容,歡迎大家持續(xù)關(guān)注。
本文作者:王崢,馬蜂窩大數(shù)據(jù)平臺(tái)前端技術(shù)專家。
(馬蜂窩技術(shù)原創(chuàng)內(nèi)容,轉(zhuǎn)載務(wù)必注明出處保存文末二維碼圖片,謝謝配合。)
關(guān)注馬蜂窩技術(shù),找到更多你想要的內(nèi)容
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/103660.html
摘要:當(dāng)其他人在浪費(fèi)時(shí)間的時(shí)候,因?yàn)橛辛怂?,你可以與時(shí)間賽跑,在有限的生命中取得更好的價(jià)值當(dāng)其他人在刷著抖音和快手時(shí),因?yàn)橛辛怂?,你可以?jiǎn)單工作,開(kāi)心工作,享受工作當(dāng)其他人在羨慕你取得成功時(shí),你可以驕傲的說(shuō),它是我最得意的伴侶,一款讓你可以讓放心 當(dāng)其他人在浪費(fèi)時(shí)間的時(shí)候,因?yàn)橛辛怂憧梢耘c時(shí)間賽跑,在有限的生命中取得更好的價(jià)值 當(dāng)其他人在刷著抖音和快手時(shí),因?yàn)橛辛怂?,你可以?jiǎn)單工作,開(kāi)...
摘要:我所在的美團(tuán)酒店事業(yè)部去年月份成立,新的業(yè)務(wù)新的開(kāi)發(fā)團(tuán)隊(duì),這一切使得我們的前后端分離推進(jìn)的很徹底。日志監(jiān)控平臺(tái)日志監(jiān)控平臺(tái)是美團(tuán)內(nèi)部的一個(gè)日志收集系統(tǒng),目前美團(tuán)統(tǒng)一使用收集日志,具有接收格式日志的能力,而日志監(jiān)控平臺(tái)也是以格式日志來(lái)收集。 轉(zhuǎn)自:美團(tuán)技術(shù)團(tuán)隊(duì) 作者:美團(tuán)技術(shù)團(tuán)隊(duì) 分享理由:很好的分享,可見(jiàn),基于Node的前后端分離的架構(gòu)是越顯流行和重要,前端攻城獅們,No...
摘要:本文將結(jié)合馬蜂窩容器化平臺(tái)賦能前端應(yīng)用構(gòu)建的實(shí)踐經(jīng)驗(yàn),介紹整個(gè)平臺(tái)背后的設(shè)計(jì)和實(shí)現(xiàn)原理,取得的一些效果及問(wèn)題的優(yōu)化方案。如果使用容器化平臺(tái)就不會(huì)出現(xiàn)這方面的擔(dān)憂。 容器對(duì)前端開(kāi)發(fā)真的有用嗎?答案是肯定的。 最初當(dāng)我向公司的前端同學(xué)「安利」容器技術(shù)的時(shí)候,很多人都會(huì)說(shuō):「容器?這不是用在后端的技術(shù)嗎?我不懂啊,而且前端開(kāi)發(fā)用不上吧?!?showImg(https://segmentfau...
摘要:本文將結(jié)合馬蜂窩容器化平臺(tái)賦能前端應(yīng)用構(gòu)建的實(shí)踐經(jīng)驗(yàn),介紹整個(gè)平臺(tái)背后的設(shè)計(jì)和實(shí)現(xiàn)原理,取得的一些效果及問(wèn)題的優(yōu)化方案。如果使用容器化平臺(tái)就不會(huì)出現(xiàn)這方面的擔(dān)憂。 容器對(duì)前端開(kāi)發(fā)真的有用嗎?答案是肯定的。 最初當(dāng)我向公司的前端同學(xué)「安利」容器技術(shù)的時(shí)候,很多人都會(huì)說(shuō):「容器?這不是用在后端的技術(shù)嗎?我不懂啊,而且前端開(kāi)發(fā)用不上吧?!?showImg(https://segmentfau...
摘要:本文將結(jié)合馬蜂窩容器化平臺(tái)賦能前端應(yīng)用構(gòu)建的實(shí)踐經(jīng)驗(yàn),介紹整個(gè)平臺(tái)背后的設(shè)計(jì)和實(shí)現(xiàn)原理,取得的一些效果及問(wèn)題的優(yōu)化方案。如果使用容器化平臺(tái)就不會(huì)出現(xiàn)這方面的擔(dān)憂。 容器對(duì)前端開(kāi)發(fā)真的有用嗎?答案是肯定的。 最初當(dāng)我向公司的前端同學(xué)「安利」容器技術(shù)的時(shí)候,很多人都會(huì)說(shuō):「容器?這不是用在后端的技術(shù)嗎?我不懂啊,而且前端開(kāi)發(fā)用不上吧?!?showImg(https://segmentfau...
閱讀 1609·2021-11-04 16:10
閱讀 3006·2021-09-30 09:48
閱讀 2895·2019-08-29 11:31
閱讀 1636·2019-08-28 18:22
閱讀 3287·2019-08-26 13:44
閱讀 1377·2019-08-26 13:42
閱讀 2907·2019-08-26 10:20
閱讀 818·2019-08-23 17:00