摘要:視覺(jué)感知測(cè)試視覺(jué)回歸測(cè)試為了解決上面提到的各種問(wèn)題,視覺(jué)感知測(cè)試孕育而生。第三種方式,無(wú)法進(jìn)行視覺(jué)感知測(cè)試結(jié)果只能進(jìn)行視覺(jué)回歸測(cè)試和上一版的繼續(xù)比較差異。
前端自動(dòng)化測(cè)試 之 視覺(jué)測(cè)試 前端測(cè)試分類(lèi)
前端測(cè)試主要分五大方向測(cè)試,而這五大方向也分很多小方向測(cè)試,首先簡(jiǎn)單的介紹每個(gè)方向的概念
界面樣式測(cè)試
固定界面樣式測(cè)試:主要針對(duì)文字內(nèi)容不變的區(qū)域,例如頁(yè)面的頁(yè)頭,頁(yè)腳這類(lèi)結(jié)構(gòu)、內(nèi)容不變的區(qū)域,而測(cè)試一般通過(guò)截圖對(duì)比解決。
結(jié)構(gòu)不變界面樣式測(cè)試:主要針對(duì)結(jié)構(gòu)不變的區(qū)域,例如新聞區(qū)域這類(lèi)結(jié)構(gòu)不變,內(nèi)容變化的區(qū)域,這類(lèi)測(cè)試一般通過(guò)DOM元素對(duì)比解決。
計(jì)算樣式測(cè)試:主要針對(duì)計(jì)算樣式不變的區(qū)域,這類(lèi)測(cè)試一般通過(guò)比較計(jì)算樣式解決,但是這種測(cè)試不推薦,因?yàn)闇y(cè)試成本比較大。
功能測(cè)試
服務(wù)器數(shù)據(jù)預(yù)期測(cè)試:主要針對(duì)用戶在前端界面進(jìn)行某種操作后,提交數(shù)據(jù)給后臺(tái)后,測(cè)試后臺(tái)能否返回預(yù)期的數(shù)據(jù)
界面功能測(cè)試:主要針對(duì)用戶在前端界面進(jìn)行某種交互性操作后,測(cè)試能否獲取預(yù)期的功能、界面交互
多瀏覽器測(cè)試
多瀏覽器測(cè)試:基于界面樣式測(cè)試、功能測(cè)試的基礎(chǔ)上來(lái)進(jìn)行不同瀏覽器的的測(cè)試,俗稱(chēng)兼容性測(cè)試。
性能測(cè)試
白屏?xí)r間:用戶瀏覽器輸入網(wǎng)址后至瀏覽器出現(xiàn)至少1px畫(huà)面為止。
首屏?xí)r間:用戶瀏覽器首屏內(nèi)所有的元素呈現(xiàn)所花費(fèi)時(shí)間。
頁(yè)面回歸時(shí)間:用戶瀏覽器非第一次加載所有的元素呈現(xiàn)所花費(fèi)時(shí)間。
用戶可操作時(shí)間(dom ready) :網(wǎng)站某些功能可以使用的時(shí)間。
頁(yè)面總下載時(shí)間(onload):網(wǎng)站中所有資源加載完成并且可用時(shí)間。
質(zhì)量測(cè)試
雅虎網(wǎng)站優(yōu)化規(guī)則
什么樣的項(xiàng)目適合自動(dòng)化測(cè)試呢?如上圖所示,真正工作中無(wú)法全部滿足以上條件,所以需要作出權(quán)衡,一般來(lái)說(shuō),只需要滿足以下幾點(diǎn),就可以對(duì)項(xiàng)目開(kāi)展自動(dòng)化測(cè)試(1.需求穩(wěn)定
不會(huì)頻繁變更。2.多平臺(tái)運(yùn)行,組合遍歷型,大量的重復(fù)任務(wù)。3.軟件維護(hù)周期長(zhǎng),有生命力。4.被測(cè)系統(tǒng)開(kāi)發(fā)較為規(guī)范,可測(cè)試性強(qiáng)。):
需求穩(wěn)定,不會(huì)頻繁變更
自動(dòng)化測(cè)試最大的挑戰(zhàn)就是需求的變化,而自動(dòng)化腳本本身就需要修改、擴(kuò)展、debug,去適應(yīng)新的功能,如果投入產(chǎn)出比太低,那么自動(dòng)化測(cè)試也失去了其價(jià)值和意義;
折中的做法是選擇相對(duì)穩(wěn)定的模塊和功能進(jìn)行自動(dòng)化測(cè)試,變動(dòng)較大、需求變更較頻繁的部分用手工測(cè)試;
多平臺(tái)運(yùn)行,組合遍歷型、大量的重復(fù)任務(wù)
測(cè)試數(shù)據(jù)、測(cè)試用例、自動(dòng)化腳本的重用性和移植性較強(qiáng),降低成本,提高效率和價(jià)值;
軟件維護(hù)周期長(zhǎng),有生命力
自動(dòng)化測(cè)試的需求穩(wěn)定性要求、自動(dòng)化框架的設(shè)計(jì)、腳本開(kāi)發(fā)與調(diào)試均需要時(shí)間,這其實(shí)也是一個(gè)軟件開(kāi)發(fā)過(guò)程,如果項(xiàng)目周期較短,沒(méi)有足夠的時(shí)間去支持這一過(guò)程,那自動(dòng)化測(cè)試也就不需要了;
被測(cè)系統(tǒng)開(kāi)發(fā)較為規(guī)范,可測(cè)試性強(qiáng)
主要出于這幾點(diǎn)考慮:被測(cè)試系統(tǒng)的架構(gòu)差異、測(cè)試技術(shù)和工具的適應(yīng)性、測(cè)試人員的能力能否設(shè)計(jì)開(kāi)發(fā)出適應(yīng)差異的自動(dòng)化測(cè)試框架;
什么是UI測(cè)試對(duì)于界面布局,傳統(tǒng)的測(cè)試都是由人工對(duì)比設(shè)計(jì)圖和產(chǎn)品界面。當(dāng)界面有修改之后,再由人通過(guò)肉眼去檢查修改(包括正確的和錯(cuò)誤的修改),這樣即費(fèi)時(shí)而且測(cè)試結(jié)果又不穩(wěn)定,因?yàn)槿斯?duì)比測(cè)試存在兩個(gè)巨坑:1.效率低;2.人的不確定性。對(duì)于擁有大量復(fù)雜界面的Web應(yīng)用,界面布局的測(cè)試的數(shù)量巨大,再加上這兩個(gè)問(wèn)題,導(dǎo)致這類(lèi)應(yīng)用的界面布局測(cè)試/回歸測(cè)試時(shí)間很長(zhǎng),成本很高,所以很多基于Agile(敏捷開(kāi)發(fā))項(xiàng)目基本不可能在迭代周期內(nèi)高質(zhì)量的完成其視覺(jué)測(cè)試。對(duì)于每天做一次,那更是不可能完成的任務(wù)。
視覺(jué)感知測(cè)試/視覺(jué)回歸測(cè)試為了解決上面提到的各種問(wèn)題,視覺(jué)感知測(cè)試孕育而生。它使用傳統(tǒng)的對(duì)圖片進(jìn)行二進(jìn)制比較的辦法,結(jié)合敏捷迭代開(kāi)發(fā)的理念,產(chǎn)生的一種針對(duì)界面布局的自動(dòng)化測(cè)試方法。
視覺(jué)感知測(cè)試視覺(jué)感知測(cè)試就是對(duì)第一個(gè)版本的所有界面進(jìn)行第一次測(cè)試。
視覺(jué)感知測(cè)試包含以下幾個(gè)主要的測(cè)試步驟:
需要注意的是!
配對(duì)URL(忽略hostname)
通過(guò)配對(duì)URL,對(duì)所有的截圖按照相同的URL進(jìn)行分組。當(dāng)然有時(shí)候會(huì)出現(xiàn)新的界面,有時(shí)候老的界面會(huì)被刪除。對(duì)于新的界面就需要人工進(jìn)行首次驗(yàn)證測(cè)試 。
像素級(jí)別的圖形比較
對(duì)于分組之后的截圖進(jìn)行像素級(jí)別的比較并生產(chǎn)差別圖。有時(shí)候?yàn)榱私翟?,可以只?duì)局部關(guān)心的組件進(jìn)行比較。
人工查看所有不同
最后通過(guò)人工審查差別圖報(bào)告完成測(cè)試。
視覺(jué)感知測(cè)試結(jié)果:
預(yù)期(expected) | 實(shí)際(actual) | 比較結(jié)果(diff) |
---|---|---|
我們認(rèn)為如果一個(gè)界面通過(guò)第一次的人工驗(yàn)證并發(fā)布之后,它就是一個(gè)正確的標(biāo)準(zhǔn)界面,并且是包含了人工測(cè)試價(jià)值的資產(chǎn)。當(dāng)下一次測(cè)試的時(shí)候,這部分價(jià)值就應(yīng)該被保留并重用起來(lái),用于減少新的一次測(cè)試的時(shí)間,從而實(shí)現(xiàn)界面的快速回歸測(cè)試。
視覺(jué)回歸測(cè)試包含以下幾個(gè)主要的測(cè)試步驟:
回歸和感知測(cè)試流程差不多只是差異值要更小一點(diǎn),并且只有效果圖需要替換內(nèi)容。
視覺(jué)自動(dòng)測(cè)試怎么做?要進(jìn)行視覺(jué)自動(dòng)測(cè)試,有三種方式。
第一種是截屏比對(duì)(局部、整頁(yè))。
第二種通過(guò)JavaScript調(diào)用window.getComputedStyle()獲取計(jì)算后的樣式并進(jìn)行斷言。
第三種dom結(jié)構(gòu)對(duì)比加css樣式對(duì)比。
這三種各有明顯的優(yōu)勢(shì)和不足。 第二種方式強(qiáng)綁定了實(shí)現(xiàn),從而變得可能比較脆弱。 第一種方式離設(shè)計(jì)太近了,當(dāng)頁(yè)面中有可變內(nèi)容時(shí)就會(huì)有問(wèn)題。
第三種方式,無(wú)法進(jìn)行視覺(jué)感知測(cè)試結(jié)果只能進(jìn)行視覺(jué)回歸測(cè)試和上一版的dom繼續(xù)比較差異。
我更傾向與第一種截圖對(duì)比;它的測(cè)試基于用戶所見(jiàn)而不是用戶所見(jiàn)的抽象。當(dāng)然第三種也是非常好的 page-monitor 有興趣的朋友可以自行了解。為什么第三種那么好為什么不使用呢?因?yàn)樯厦孢@個(gè)庫(kù)是基于phantomjs并且它的實(shí)現(xiàn)方式過(guò)于復(fù)雜不適合新手玩玩。
像素對(duì)比工具,有哪些?名稱(chēng) | 地址 |
---|---|
PhantomCSS | https://github.com/HuddleEng/... |
GhostStory | https://github.com/thingsinja... |
Cactus | https://github.com/winston/ca... |
Needle | https://github.com/python-nee... |
CSSCritic | https://github.com/cburgmer/c... |
sikuli | http://www.sikuli.org/ |
Mogo | http://mogotest.com/ |
pixelmatch | https://github.com/mapbox/pix... |
pixel-diff | https://github.com/koola/pixe... |
好了介紹了那么多,怎么選一個(gè)合適的Headless Browser呢?
Headless Browser???我是視覺(jué)測(cè)試要無(wú)界面瀏覽器干嘛?
因?yàn)橛辛讼袼貙?duì)比工具我們還需要一個(gè)瀏覽器進(jìn)行截圖和設(shè)計(jì)圖進(jìn)行像素比較。
比較常見(jiàn)出名的幾個(gè)Headless Browser,有哪些?名稱(chēng) | 內(nèi)核 | 地址 |
---|---|---|
Puppeteer | Webkit | https://github.com/GoogleChro... |
PhantomJS | Webkit | http://phantomjs.org/ |
SlimerJS | Gecko | https://github.com/laurentj/s... |
TrifleJS | IE | https://github.com/sdesalas/t... |
PhantomJS 基于 Webkit 內(nèi)核,不支持 Flash 的播放;SlimerJS 基于火狐的 Gecko 內(nèi)核,支持 Flash播放,并且執(zhí)行過(guò)程會(huì)有頁(yè)面展示。
我們這里呢就只講Webkit內(nèi)核的,其他的我就不講了。
PhantomJS簡(jiǎn)介:PhantomJS 是一個(gè)基于webkit的JavaScript API。它使用QtWebKit作為它核心瀏覽器的功能,使用webkit來(lái)編譯解釋執(zhí)行JavaScript代碼。任何你可以在基于webkit瀏覽器做的事情,它都能做到。它不僅是個(gè)隱形的瀏覽器,提供了諸如CSS選擇器、支持Web標(biāo)準(zhǔn)、DOM操作、JSON、HTML5、Canvas、SVG等,同時(shí)也提供了處理文件I/O的操作,從而使你可以向操作系統(tǒng)讀寫(xiě)文件等。PhantomJS的用處可謂非常廣泛,諸如網(wǎng)絡(luò)監(jiān)測(cè)、網(wǎng)頁(yè)截屏、無(wú)需瀏覽器的 Web 測(cè)試、頁(yè)面訪問(wèn)自動(dòng)化等。
但是 PhantomJS 因?yàn)楫吘共皇钦鎸?shí)的用戶瀏覽器環(huán)境,使用起來(lái)還是有不少的詬病。之前一直在使用 PhantomJS ,功能雖然夠用,不過(guò)和在真實(shí)的瀏覽器里面訪問(wèn)的界面來(lái)對(duì)比差別還是比較大的。
Puppeteer簡(jiǎn)介:Puppeteer是Chrome團(tuán)隊(duì)開(kāi)發(fā)的一個(gè)Node庫(kù)。它提供了一個(gè)高級(jí)API來(lái)控制無(wú)頭或完整的Chrome。它通過(guò)使用Chrome無(wú)界面模式 (Headless Chrome )和DevTools協(xié)議的組合來(lái)實(shí)現(xiàn)這一點(diǎn)。它使用一個(gè)更上層的API來(lái)封裝其功能,讓用戶界面測(cè)試自動(dòng)化變得輕而易舉。
人們基于Chrome DevTools協(xié)議開(kāi)發(fā)了一系列Google Chrome工具。你在瀏覽器中點(diǎn)擊更多工具 ->開(kāi)發(fā)工具,打開(kāi)的就是DevTools。DevTools協(xié)議是DevTools的動(dòng)力基礎(chǔ),我們現(xiàn)在可以使用Chrome中的DevTools來(lái)做更多的事情。
好了簡(jiǎn)介講完了,我們來(lái)對(duì)比一下這兩個(gè)Headless Browser的區(qū)別。
PhantomJS:
var page = require("webpage").create(); page.viewportSize = { width: 400, height: 400 }; page.open("http://localhost:8899/VS", function(status) { if (status === "success") { page.render("a.jpg"); } else { console.log("Page failed to load."); } phantom.exit(0); });
Puppeteer:
const puppeteer = require("puppeteer"); (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto("http://localhost:8899/VS"); await page.setViewport({ width: 400, height: 400 }) //保存圖片 const images = await page.screenshot({ path: "VS.jpg", fullPage: true, omitBackground: true }); //關(guān)閉瀏覽器 await browser.close(); })();
Puppeteer | PhantomJS | Chrome瀏覽器 |
---|---|---|
瀏覽器效果(大圖):
好了看到這里已經(jīng)可以分別出來(lái)勝負(fù)了。
使用puppeteer進(jìn)行視覺(jué)感知測(cè)試我們來(lái)做一個(gè)簡(jiǎn)單的dome
我們這里拿掘金來(lái)做一個(gè)視覺(jué)感知測(cè)試的例子。
1.首先創(chuàng)建項(xiàng)目名字就叫“PerceptionTest”把。
2.安裝依賴(lài)
安裝Puppeteer
npm install puppeteer --save
像素對(duì)比工具我就選我最常用的blink-dif了
npm install blink-diff --save
3.依賴(lài)安完了我們來(lái)創(chuàng)建一個(gè)js文件“app.js”
4.加載依賴(lài)
const puppeteer = require("puppeteer"),//無(wú)頭瀏覽器 BlinkDiff = require("blink-diff"),//像素對(duì)比 imgUrl = __dirname + "/blink-diff_img/";//圖片目錄
5.使用puppeteer進(jìn)行截圖
(async () => { //創(chuàng)建puppeteer const browser = await puppeteer.launch({ headless: true }); //new 一個(gè)新的tab頁(yè)面 const page = await browser.newPage(); //設(shè)置瀏覽器的尺寸 await page.setViewport({ width: 1920, height: 945 }); //打開(kāi)url await page.goto("https://juejin.im/"); //保存截圖 await page.screenshot({ path: imgUrl + "Screenshots.png", fullPage: true }); //關(guān)閉瀏覽器 await browser.close(); })();
6.分析頁(yè)面找到要替換的內(nèi)容
為什么要替換內(nèi)容呢,因?yàn)槲覀僓I測(cè)試指的是測(cè)試界面樣式而不是去匹配里面的內(nèi)容,如果不替換里面的內(nèi)容那像素對(duì)比工具比較出來(lái)的相似度肯定很低。所以我們要替換掉內(nèi)容,要讓內(nèi)容完整統(tǒng)一,我們才好更加精確的去比較差異。
好了我們來(lái)分析一下要替換的內(nèi)容。
要替換的內(nèi)容如下:
標(biāo)題
標(biāo)簽
作者
發(fā)布時(shí)間
閱讀數(shù)
列表圖片
7.替換內(nèi)容
puppeteer提供了非常豐富的api,其中有個(gè)api叫page.evaluate可以向頁(yè)面插入一段js。
await page.evaluate(async () => { //列表 var Lists = document.querySelectorAll("div.feed.welcome__feed > ul > li > div > a > div"); Lists.forEach(function (element, index, array) { element.querySelector("a.title").innerHTML = "測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試"; //替換標(biāo)簽 element.querySelector("ul > li.item.category > span").innerHTML = "測(cè)試"; //替換作者 element.querySelector("ul > li.item.username.clickable > div > a").innerHTML = "測(cè)試"; //替換發(fā)布時(shí)間 element.querySelector("div.info-row.meta-row > ul > li:nth-child(3)").innerHTML = "9999天前"; //替換發(fā)布時(shí)間 element.querySelector("div.info-row.meta-row > ul > li:nth-child(4)").innerHTML = "99999999999 次閱讀"; //列表圖片 if (element.querySelectorAll("div.lazy.thumb.thumb.loaded").length==1) { element.querySelector("div.lazy.thumb.thumb.loaded").style.background = "#fdedc9"; } else { var loaded=document.createElement("div"); loaded.className=" lazy thumb thumb loaded"; loaded.style.background = "#fdedc9"; loaded.setAttribute("data-v-b2db8566",""); loaded.setAttribute("data-v-009ea7bb",""); loaded.setAttribute("data-v-f2ca14b0",""); element.appendChild(loaded); } }); });
8.使用Blink-Diff進(jìn)行像素對(duì)比較
const diff = new BlinkDiff({ imageAPath: imgUrl + "example.png", // 設(shè)計(jì)圖 imageBPath: imgUrl + "Screenshots.png",//頁(yè)面截圖 //低于其中差異的像素?cái)?shù)/ p(默認(rèn)值:500) - 百分比閾值:1 = 100%,0.2 = 20% threshold: 0.02, // 1% threshold imageOutputPath: imgUrl + "Diff.png"http://Diff路徑 }); diff.run(function (error, result) { if (error) { throw error; } else { console.log(diff.hasPassed(result.code) ? "通過(guò)" : "失敗"); console.log("總像素:" + result.dimension); console.log("發(fā)現(xiàn):" + result.differences + " 差異."); } });
完整代碼:
const puppeteer = require("puppeteer"), BlinkDiff = require("blink-diff"), imgUrl = __dirname + "/blink-diff_img/"; (async () => { const browser = await puppeteer.launch({ headless: true }); const page = await browser.newPage(); await page.setViewport({ width: 1920, height: 945 }); await page.goto("https://juejin.im/"); await page.evaluate(async () => { //列表 var Lists = document.querySelectorAll("div.feed.welcome__feed > ul > li > div > a > div"); Lists.forEach(function (element, index, array) { element.querySelector("a.title").innerHTML = "測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試"; //替換標(biāo)簽 element.querySelector("ul > li.item.category > span").innerHTML = "測(cè)試"; //替換作者 element.querySelector("ul > li.item.username.clickable > div > a").innerHTML = "測(cè)試"; //替換發(fā)布時(shí)間 element.querySelector("div.info-row.meta-row > ul > li:nth-child(3)").innerHTML = "9999天前"; //替換發(fā)布時(shí)間 element.querySelector("div.info-row.meta-row > ul > li:nth-child(4)").innerHTML = "99999999999 次閱讀"; //列表圖片 if (element.querySelectorAll("div.lazy.thumb.thumb.loaded").length==1) { element.querySelector("div.lazy.thumb.thumb.loaded").style.background = "#fdedc9"; } else { var loaded=document.createElement("div"); loaded.className=" lazy thumb thumb loaded"; loaded.style.background = "#fdedc9"; loaded.setAttribute("data-v-b2db8566",""); loaded.setAttribute("data-v-009ea7bb",""); loaded.setAttribute("data-v-f2ca14b0",""); element.appendChild(loaded); } }); }); await page.screenshot({ path: imgUrl + "Screenshots.png", fullPage: true }); const diff = new BlinkDiff({ imageAPath: imgUrl + "example.png", // 設(shè)計(jì)圖 imageBPath: imgUrl + "Screenshots.png",//頁(yè)面截圖 threshold: 0.02, // 1% threshold imageOutputPath: imgUrl + "Diff.png"http://Diff路徑 }); diff.run(function (error, result) { if (error) { throw error; } else { console.log(diff.hasPassed(result.code) ? "通過(guò)" : "失敗"); console.log("總像素:" + result.dimension); console.log("發(fā)現(xiàn):" + result.differences + " 差異."); } }); //關(guān)閉puppeteer await browser.close(); })();
差異圖
有差異 | 無(wú)差異 |
---|---|
git完整代碼:https://github.com/my07ke/Per...
好了,欲知后事如何,請(qǐng)聽(tīng)下回分解。
點(diǎn)擊鏈接加入群聊【前端|WEB|CSS|Javascript|HTML】:https://jq.qq.com/?_wv=1027&k...
參考:淺談UI自動(dòng)化測(cè)試
視覺(jué)感知測(cè)試
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/52288.html
摘要:視覺(jué)感知測(cè)試視覺(jué)回歸測(cè)試為了解決上面提到的各種問(wèn)題,視覺(jué)感知測(cè)試孕育而生。第三種方式,無(wú)法進(jìn)行視覺(jué)感知測(cè)試結(jié)果只能進(jìn)行視覺(jué)回歸測(cè)試和上一版的繼續(xù)比較差異。 前端自動(dòng)化測(cè)試 之 視覺(jué)測(cè)試 showImg(https://segmentfault.com/img/remote/1460000014720180); 前端測(cè)試分類(lèi) showImg(https://segmentfault.co...
摘要:年,和前端開(kāi)發(fā)者與應(yīng)用程序前端開(kāi)發(fā)者之間產(chǎn)生了巨大的分歧。開(kāi)發(fā)最常見(jiàn)的解決方案有手機(jī)和平板的原生應(yīng)用程序桌面應(yīng)用程序桌面應(yīng)用程序原生技術(shù)最后,前端開(kāi)發(fā)者可以從瀏覽器開(kāi)發(fā)中學(xué)習(xí)到,編寫(xiě)代碼不需要考慮瀏覽器引擎的限制。 前端開(kāi)發(fā)者手冊(cè)2019 Cody Lindley 編著 原文地址 本手冊(cè)由Frontend Masters贊助,通過(guò)深入現(xiàn)代化的前端工程課程來(lái)提高你的技能。 下載:PDF ...
摘要:年,和前端開(kāi)發(fā)者與應(yīng)用程序前端開(kāi)發(fā)者之間產(chǎn)生了巨大的分歧。開(kāi)發(fā)最常見(jiàn)的解決方案有手機(jī)和平板的原生應(yīng)用程序桌面應(yīng)用程序桌面應(yīng)用程序原生技術(shù)最后,前端開(kāi)發(fā)者可以從瀏覽器開(kāi)發(fā)中學(xué)習(xí)到,編寫(xiě)代碼不需要考慮瀏覽器引擎的限制。 前端開(kāi)發(fā)者手冊(cè)2019 Cody Lindley 編著 原文地址 本手冊(cè)由Frontend Masters贊助,通過(guò)深入現(xiàn)代化的前端工程課程來(lái)提高你的技能。 下載:PDF ...
摘要:年,和前端開(kāi)發(fā)者與應(yīng)用程序前端開(kāi)發(fā)者之間產(chǎn)生了巨大的分歧。開(kāi)發(fā)最常見(jiàn)的解決方案有手機(jī)和平板的原生應(yīng)用程序桌面應(yīng)用程序桌面應(yīng)用程序原生技術(shù)最后,前端開(kāi)發(fā)者可以從瀏覽器開(kāi)發(fā)中學(xué)習(xí)到,編寫(xiě)代碼不需要考慮瀏覽器引擎的限制。 前端開(kāi)發(fā)者手冊(cè)2019 Cody Lindley 編著 原文地址 本手冊(cè)由Frontend Masters贊助,通過(guò)深入現(xiàn)代化的前端工程課程來(lái)提高你的技能。 下載:PDF ...
閱讀 1605·2021-09-23 11:31
閱讀 920·2021-09-23 11:22
閱讀 1337·2021-09-22 15:41
閱讀 4062·2021-09-03 10:28
閱讀 2907·2019-08-30 15:55
閱讀 3536·2019-08-30 15:55
閱讀 1942·2019-08-30 15:44
閱讀 2712·2019-08-30 13:50