摘要:圖離線情況下發(fā)送微信消息,等網(wǎng)絡正常后微信會繼續(xù)處理我們的消息。無論是在微信中還是手機短信,在沒有信號時都不影響我們編輯發(fā)送短信,等網(wǎng)絡恢復時會自動幫我們把之前編輯好的信息順利遞送出去。
(刪掉了第一小段,因為和內(nèi)容關(guān)系不大。。)
本來這該是個技術(shù)分享會的內(nèi)容,參加完 Google Developer Day(GDD) 后想做個 AI Demo 來分享,奈何技術(shù)實力不夠,害怕把大家?guī)нM溝里,想想還是講比較擅長的前端領(lǐng)域的比較靠譜。等哪天經(jīng)歷過人工智能相關(guān)項目后再分享也不遲。(分享會已經(jīng)開過了,這是PPT)
圖:在 GDD 上海站圍觀 TensorFlow 與藝術(shù)分享會
Progressive Web AppsProgressive Web Apps(PWA)指代那些使用最新的 Web 技術(shù)構(gòu)建的類似原生 App 體驗的 webapp,這也是今年前端最熱詞之一,雖然感覺說來說去就是為了一堆新技術(shù)取個名字,就像之前的 HTML5 一樣。不過,這些技術(shù)確確實實提高 Web 的用戶體驗。
為此各大瀏覽器廠商都卯足了勁(我是說除了Safari,連 IE 都在努力了啊喂?。?,這篇文章將從三個方面來講解這些技術(shù):
可靠,即使網(wǎng)絡不穩(wěn)定也需要提供服務;
更快;
功能更豐富!
(恰好 PWA 也這么認為)
這些都是為了讓 WebApp 能像 App 一樣提供沉浸式的用戶體驗。
技術(shù)是為內(nèi)容服務的,單純的技術(shù)沒法讓我們爬到馬斯洛需求理論的更高層,但是技術(shù)一直在為實現(xiàn)它而努力。接下來將要介紹的就是2016年各大瀏覽器廠商為前端技術(shù)做出的努力。(本文不包括類似 async 函數(shù)、Promise、以及 Websocket 之類的一些“老技術(shù)”)
圖:只有Safari還在考慮是否實現(xiàn) Service worker,連 IE 都在努力你 Safari 憑啥不努力。。。
可靠技術(shù)關(guān)鍵詞:Service Worker,Background Sync
相對于 App , Web 最痛的點在于如果網(wǎng)絡不靠譜,一切無從談起。打開任何一個頁面,WebApp 都需要經(jīng)過下載、解析并初始化代碼、執(zhí)行路由邏輯跳轉(zhuǎn)到相應頁面、請求網(wǎng)絡數(shù)據(jù)、渲染頁面...而性能瓶頸最容易出現(xiàn)在網(wǎng)絡部分。設想如果可以預先將網(wǎng)頁的外殼安裝到設備上,啟動時從本地加載這層殼,并請求網(wǎng)絡獲取渲染需要的數(shù)據(jù),甚至更極端一點,數(shù)據(jù)全存在本地磁盤,啟動時優(yōu)先使用的本地已經(jīng)保存同步好的數(shù)據(jù),保證即使在 Lie-Fi* 環(huán)境下也能提供正常服務。
注:Lie-Fi是比斷網(wǎng)更要命的移動網(wǎng)絡。癥狀是手機告訴你你已經(jīng)聯(lián)網(wǎng),但是網(wǎng)頁卻打不開,甚至連“Chrome小恐龍”都不讓玩。
圖: 這就是那只“Chrome小恐龍”
在講 Service worker 之前,瀏覽器中已經(jīng)實現(xiàn)了一個類似的功能,叫 AppCache ,AppCache 的運行原理不復雜,就是在網(wǎng)頁加載完成后,瀏覽器閑下來的時下載一個清單文件,這個文件列明了哪些資源可以離線使用,哪些資源需要請求網(wǎng)絡,如果請求不到網(wǎng)絡該怎么辦等等。類似一個配置清單。
圖:AppCache在首次啟動頁面時緩存數(shù)據(jù)到磁盤,此后打開的頁面如果有用到已緩存的資源則直接由緩存提供。
不過,HTTP 緩存在上古時期就實現(xiàn)了這個功能,看起來只是換了個馬甲。
的確,理論上 HTTP 緩存完全能實現(xiàn)這個功能,但實際卻不是這樣,由于瀏覽器廠商很多、版本也多,相互之間兼容性或多或少有些差異,如果某個瀏覽器對程序員的失誤容忍程度過低,很容易導致渲染出現(xiàn)問題。類似問題也出現(xiàn)在 HTTP 請求上,如果瀏覽器嚴格按照請求頭的緩存控制規(guī)則來控制網(wǎng)絡請求,很容易導致頁面無法更新或者部分更新、新舊代碼混合的問題。所以大部分瀏覽器傾向不完全相信 HTTP 請求頭。這也從另一個方面印證了“單一職責原則“的重要性(傳輸數(shù)據(jù)的就好好傳輸數(shù)據(jù),別搶人家緩存的飯碗,你也搶不來。。)。
嗯,看起來 AppCache 比 HTTP 緩存棒多了,功能強大而且單一職責,只要一份清單文件就能把資源都緩存起來離線訪問。
但AppCache犯了比HTTP嚴重的多的錯誤!
我們知道,AppCache的確是單一職責,意味著瀏覽器必須信任他,如果不相信那它存在的意義是什么?問題在于,假設程序員犯了一個小錯,一個匹配規(guī)則不小心匹配到了這個清單文件,你的代碼將永遠都更新不了!!
代碼不小心犯個錯簡直太正常不過了,就好像程序員需要吃飯睡覺上廁所一樣,程序員也需要會寫bug。。
延伸閱讀:AppCache Issue is a Douchebag
好的,冷靜一下。。其實有個不完美的解決方案,通過腳本自動生成緩存規(guī)則,具體可以參考 offline-plugin AppCache 部分的做法,但這樣 AppCache 的其他“好處”就完全體現(xiàn)無法體現(xiàn)出來!
這時候 Service worker 該登場了!簡單來說,Service worker 是一段運行在頁面之外的一段腳本,這段腳本在啟動時會注冊一些事件到瀏覽器中,當事件觸發(fā)時可以通過預先定義好的 Javascript handler 控制如何響應這個事件。
但為什么說 Service worker 比 AppCache 好呢?
首先 Service worker 應該說徹底解決了 AppCache 無法更新的問題,在使用 Service worker 時,只要網(wǎng)絡暢通,瀏覽器都會嘗試在啟動后檢查 Service worker 是否有更新,而且 Service worker 并不能截獲請求更新 Service worker 的事件,換句話說就是無法緩存自身,也就是永遠都有機會更新自己。
其次,Service Worker 通過事件機制配合 js 腳本來控制瀏覽器,并且脫離于頁面生存,這讓web終于有了“常駐”系統(tǒng)的機會!
這是技術(shù)上的一小步,但是卻給 web 的能力提供了無限的想象空間!
為了更好的理解什么是 Service Worker,我們可以把它想象成是生存于瀏覽器頁面中、頁面之外的動態(tài)代理,當類似網(wǎng)絡請求之類的事件觸發(fā)時,由這個代理決定如何處置這個事件。下面這個例子就是演示如何截獲并處理網(wǎng)絡請求的。
圖:瀏覽器向服務器發(fā)起請求前先被 Service worker 截獲,處理完成后再向遠端服務器發(fā)起請求
這里有個小視頻,演示了如何通過 Service worker “憑空捏造” 離線數(shù)據(jù)?。代碼在https://github.com/MofeLee/sw...
既然服務器沒有的數(shù)據(jù)都能造出來,離線訪問數(shù)據(jù)豈不是輕而易舉?并且這個例子實際上只有幾行關(guān)鍵代碼????
一、 注冊 service worker
navigator.serviceWorker.register("/sw.js")
二、 注冊響應規(guī)則
self.addEventListener("fetch", function(event) { if (/offline-data$/.test(event.request.url)) { event.respondWith( new Response("這是一條來自service worker的響應信息") ); } });
三、沒有第三步。。。
實際上現(xiàn)在已經(jīng)有很多網(wǎng)頁可以離線訪問了,比如說大名鼎鼎的lodash,如果想知道你平常訪問的哪些網(wǎng)站是離線可訪問的,可以在瀏覽器地址欄輸入 chrome://serviceworker-internals/ 查看~
圖:可以看到 https://lodash.com/ 是注冊了service worker 到chrome中的,試著打開網(wǎng)址,稍微等下再斷網(wǎng)打開 https://lodash.com/ ~ Boom!
到目前為止,我們知道使用 Service Worker 可以解決在沒有網(wǎng)絡的情況加載應用的場景,現(xiàn)在,我們設想另一種場景:在線聊天。
圖:離線情況下發(fā)送微信消息,等網(wǎng)絡正常后微信會繼續(xù)處理我們的消息。
無論是在微信中還是手機短信,在沒有信號時都不影響我們編輯發(fā)送短信,等網(wǎng)絡恢復時 App 會自動幫我們把之前編輯好的信息順利遞送出去。而移動設備中的 webapp 體驗卻糟糕透了,我們必須開著網(wǎng)頁直到網(wǎng)絡恢復,以便發(fā)出我們的消息,假設這時候我們切換到其他App中,不但消息發(fā)不出去,很有可能連瀏覽器占用的資源都會被操作系統(tǒng)回收?。?/p>
問題究竟出在哪?
仔細思考后會發(fā)現(xiàn)問題出在網(wǎng)絡請求生命周期和頁面的生命周期耦合了,我們必須要找到一種方法能讓瀏覽器在關(guān)掉頁面后也能繼續(xù)為我們繼續(xù)工作!
但是,Webapp 不像普通的 App, 有權(quán)限在退出應用之后繼續(xù)在后臺運行,如何才能既安全又高效的在后臺運行程序成了一個比較關(guān)鍵的問題,如果放出權(quán)限讓 js 持續(xù)在后臺運行勢必會影響整個操作系統(tǒng)的效率(沒有人能寫出沒有bug的程序,只是訪問了一個網(wǎng)頁,卻強奸了我的系統(tǒng)。。當然不能忍。。)。所以 Service worker 把后臺應用的權(quán)限局限在"事件"級別,這里的事件就是前文提到的 Service worker 在瀏覽器啟動時注冊的事件。后臺腳本是否執(zhí)行完全取決于瀏覽器是否喚醒它,這也意味著如果這段腳本很煩人,我們可以通過設置關(guān)掉這段腳本執(zhí)行的入口,并且,統(tǒng)一管理所有事件的觸發(fā)也有利于提高執(zhí)行效率,畢竟讓專業(yè)的“人”做專業(yè)的事更靠譜~
同樣的,Background Sync 也是Service worker 事件之一,它讓我們有機會在退出瀏覽器后也能繼續(xù)執(zhí)行一些功能。
是不是急不可耐的想看一段演示了呢?
帶梯子可以訪問這個鏈接 圖片打不開點這
圖:斷網(wǎng)后發(fā)送的消息,聯(lián)網(wǎng)后自動發(fā)送到服務器
圖片打不開點這里
圖:演示流程
斷網(wǎng)
注冊Background sync
聯(lián)網(wǎng)觸發(fā)sync事件
截獲sync事件,彈出彈窗
實際上,除了這些,Service worker 還可以完成很多有趣的東西,比如制作一個網(wǎng)頁,網(wǎng)頁的header和footer由緩存提供,中間的content從網(wǎng)絡上獲取。如果對Service worker的其他功能感興趣,可以看看這個視頻 Instant-loading Offline-first (Progressive Web App Summit 2016)(需自帶梯子)
更快技術(shù)關(guān)鍵詞:WebAssembly,passive event listeners,requestIdleCallback
實際上,web的痛點不止在網(wǎng)絡層面上,相對于 native,js的執(zhí)行效率也是硬傷之一,雖然js在操作 DOM 上得心應手,但在處理大量數(shù)據(jù)或者圖形計算上,依舊比較吃力,以至于web平臺上很難有比較華麗動作游戲。直到WebAssembly的出現(xiàn),讓 web 平臺實現(xiàn)炫酷的動畫效果成為可能。WebAssembly可以讓我們在瀏覽器中執(zhí)行C或者C++已經(jīng)編譯出來的字節(jié)碼。但有一點需要注意的是,WebAssembly的出現(xiàn)并不是為了替代 js,更多的是用來編寫高性能的模塊,替換js中容易出現(xiàn)性能瓶頸的地方。
圖: 試玩WebAssembly 試玩鏈接
鑒于 WebAssembly 目前還處于試驗階段,這里不細講相關(guān)的代碼如何實現(xiàn)了。有興趣可以點擊后面的鏈接了解更多內(nèi)容 How Webassembly Will Change the Way You Write Javascript | Seth Samuel(需自帶梯子) demo源代碼
除了 js 的執(zhí)行效率外,流暢的屏幕滾動體驗對web來說非常重要,這點在基于觸摸的移動設備體現(xiàn)的尤為明顯,但事實上,web瀏覽器中很容易在屏幕滾動上出現(xiàn)問題,如果細心的話,會發(fā)現(xiàn)即使在設置一個空函數(shù)為event handler也可能會造成滾動不流暢。
問題出現(xiàn)在滾動時我們可以調(diào)用preventDefault來取消滾動,即使完全沒有調(diào)用preventDefault,瀏覽器頁需要在每次滾動前先等待handler調(diào)用結(jié)束,以確定我們的確沒有調(diào)用preventDefault來阻止?jié)L動。
但實際情況卻是大多數(shù)handler都只是需要獲取滾動的位置信息,監(jiān)聽與否并不會影響滾動這個動作的發(fā)生,我們完全讓屏幕一邊滾著一邊調(diào)用回調(diào)函數(shù)。所以,只要明確告訴瀏覽器這個監(jiān)聽事件是不會影響瀏覽器滾動的,對滾動事件的執(zhí)行效率有非常大的提升。
為了解釋清楚這其中的區(qū)別,我再舉個例子,游戲中的高樓,即使再高你也會毫不猶豫的往下跳,現(xiàn)實中的蹦極,雖然前人無數(shù)次的驗證過安全也會猶豫擔心。
明確知道安全:無所畏懼
潛在可能危險:畏首畏尾
這就是“明確知道”和“潛在可能”的差別,也是passive event能提升瀏覽器效率的原因。
當然,Passive Event Listeners的接口非常簡單,只需要將第三個參數(shù)設置為{passive: true}即可。
var elem = document.getElementById("elem"); elem.addEventListener("touchmove", function listener() { /* do something */ }, { passive: true });
圖:對比執(zhí)行效率的差別,右邊的listener設置了passive屬性
了解更多相關(guān)passive event listeners的信息
上面都是一些主動提高web執(zhí)行效率的方法,下面要講的是利用好瀏覽器的空閑時間。
在web開發(fā)中,更快的顯示出有內(nèi)容的首屏非常重要,尤其對于webapp來說。相對于傳統(tǒng)web頁面,webapp在初始化時需要占用大量的時間運行腳本,并且大多數(shù) webapp 在第一次打開時執(zhí)行的腳本遠遠多于后續(xù)的頁面,如何分配好腳本的執(zhí)行時間也是非常重要的課題。
圖:react webapp 在啟動前500ms需要進行非常密集的計算。
setTimeout(()=>{ const head = document.getElementsByTagName("head")[0]; const gajs = document.createElement("script"); gajs.async = 1; gajs.src = "https://www.google-analytics.com/analytics.js"; head.insertBefore(gajs, head.lastChild); }, 2000);
這是一段等瀏覽器空閑下來再去加載google analytics的腳本。
相對于動畫以及加載首屏頁面,加載第三方統(tǒng)計代碼的優(yōu)先級低得多,所以設置2000ms對于大多數(shù)場景夠用了,但事實上我們完全無法保證timer觸發(fā)時瀏覽器處于空閑狀態(tài)。
如果對于 JavaScript 足夠了解的話,應該知道瀏覽器中的 Timer 是在瀏覽器的調(diào)用棧清空時才會從回調(diào)隊列中取setTimeout推入的回調(diào)函數(shù)。但調(diào)用棧清空是瀏覽器空閑的假象,很有可能下一刻就要執(zhí)行類似requestAnimationFrame注冊的回調(diào)函數(shù),但是我們卻無法查詢到已經(jīng)注冊的回調(diào)函數(shù)會在何時觸發(fā)。
圖:年初做的視頻教程截圖,下方為回調(diào)隊列,事件輪詢在調(diào)用棧清空時會從回調(diào)隊列取函數(shù)出來運行
實際上,真正空閑的時候是在調(diào)用棧清空并且Web Api中注冊的沒有需要立刻推入回調(diào)隊列的回調(diào)函數(shù)時。結(jié)合下面的圖片可能更容易理解上面這句話
圖:圖中的idel period才是瀏覽器真正空閑的時候。
requestIdleCallback 提供的接口也非常簡單
var handle = window.requestIdleCallback(callback[, options])
如果想了解更多關(guān)于requestIdleCallback的內(nèi)容,可以查看Using requestIdleCallback和Cooperative Scheduling of Background Tasks
功能更豐富講完了前面相對來說比較復雜的api,終于可以講些輕松的內(nèi)容了,如果是比較簡單的內(nèi)容我就一筆帶過啦~(點擊右側(cè)導航欄快速跳轉(zhuǎn)到相應內(nèi)容
Fullscreen Api全屏Api,可以將類似視頻圖片或者Canvas之類的元素占滿全屏demo
code
var i = document.getElementById("myimage"); // click event handler i.onclick = function() { // in full-screen? if ( document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement ) { // exit full-screen if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); } } else { // go full-screen if (i.requestFullscreen) { this.requestFullscreen(); } else if (i.webkitRequestFullscreen) { i.webkitRequestFullscreen(); } else if (i.mozRequestFullScreen) { i.mozRequestFullScreen(); } else if (i.msRequestFullscreen) { i.msRequestFullscreen(); } } }Media Recorder
Media Recorder API 讓我們的網(wǎng)頁實現(xiàn)錄制流媒體的能力。
給個相對簡單的demo以及源代碼地址:source code demo
延伸閱讀:Record Audio and Video with MediaRecorder
(寫到這發(fā)現(xiàn)可以直接嵌入視頻。吐血中。讓我靜靜。。。。???)
Media Source Extensions另一個比較有用的接口就是 Media source extensions,它讓我們可以在 web 上有機會播放任意格式的視頻格式,著名的 flv.js 就是靠這個接口實現(xiàn)的。另外,由于視頻的內(nèi)容可以完全用腳本控制,我們可以使用這個接口完成類似播放加密的視頻流、限制視頻流的加載速度、根據(jù)緩沖的快慢自動切換視頻流的源、web頁面視頻聊天等等等等~
說點題外話,直播現(xiàn)在在國內(nèi)這么火爆,但是直播流的格式兼容性卻還有些小問題,要想一個格式支持全平臺就會涉及到相關(guān) media source extensions 的接口。flv支持桌面平臺,移動設備支持m3u8,一個方案是桌面用flv.js直播,但在實際使用中發(fā)現(xiàn)iOS不支持media source extensions,無法使用flv.js直播;另一個方案是在桌面環(huán)境使用 media source extensions 將 m3u8 轉(zhuǎn)換成瀏覽器支持的直播流格式,實際使用中發(fā)現(xiàn)后面這個方案還是更靠譜,而且輪子都已經(jīng)造好了,有興趣可以看看 videojs-contrib-hls
圖:使用 media source extensions 分片加載音頻內(nèi)容。source code demo
Offscreen Canvas可以在 web worker 上渲染canvas的接口,沒有 demo 因為還沒有支持的瀏覽器。。(就算有那應該怎么演示??)
example:
var one = document.getElementById("one").getContext("bitmaprenderer"); var two = document.getElementById("two").getContext("bitmaprenderer"); var offscreen = new OffscreenCanvas(256, 256); var gl = offscreen.getContext("webgl"); // ... some drawing for the first canvas using the gl context ... // Commit rendering to the first canvas var bitmapOne = offscreen.transferToImageBitmap(); one.transferImageBitmap(bitmapOne); // ... some more drawing for the second canvas using the gl context ... // Commit rendering to the second canvas var bitmapTwo = offscreen.transferToImageBitmap(); two.transferImageBitmap(bitmapTwo);
延伸閱讀:OffscreenCanvas
Pointer Events pointer lockdemo
web bluetooth api..
webrtc..
demo
demo
延伸閱讀https://developer.mozilla.org...
https://platform-status.mozil...
https://developers.google.com...
原創(chuàng)博文,轉(zhuǎn)載寫明出處就好(是的,出處就是這個頁面)。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/86620.html
摘要:輸出在中,值表示一個空對象指針,而這正是使用操作符檢測值時會返回的原因。屬性規(guī)定必需在提交之前填寫輸入字段。通過字面量方式創(chuàng)建的數(shù)組對象是屬于類的一個實例,所以返回,故彈出。第期年月日代碼運行的結(jié)果輸出前端教程。 第1期(2016年4月6日): (1)js中關(guān)閉當前窗口的方法是:window.close(); 第2期(2016年4月7日): (1)js中使字符串中的字符變?yōu)樾懙姆椒ㄊ?..
摘要:輸出在中,值表示一個空對象指針,而這正是使用操作符檢測值時會返回的原因。屬性規(guī)定必需在提交之前填寫輸入字段。通過字面量方式創(chuàng)建的數(shù)組對象是屬于類的一個實例,所以返回,故彈出。第期年月日代碼運行的結(jié)果輸出前端教程。 第1期(2016年4月6日): (1)js中關(guān)閉當前窗口的方法是:window.close(); 第2期(2016年4月7日): (1)js中使字符串中的字符變?yōu)樾懙姆椒ㄊ?..
摘要:輸出在中,值表示一個空對象指針,而這正是使用操作符檢測值時會返回的原因。屬性規(guī)定必需在提交之前填寫輸入字段。通過字面量方式創(chuàng)建的數(shù)組對象是屬于類的一個實例,所以返回,故彈出。第期年月日代碼運行的結(jié)果輸出前端教程。 第1期(2016年4月6日): (1)js中關(guān)閉當前窗口的方法是:window.close(); 第2期(2016年4月7日): (1)js中使字符串中的字符變?yōu)樾懙姆椒ㄊ?..
閱讀 1882·2021-11-11 16:55
閱讀 2064·2021-10-08 10:13
閱讀 739·2019-08-30 11:01
閱讀 2155·2019-08-29 13:19
閱讀 3277·2019-08-28 18:18
閱讀 2620·2019-08-26 13:26
閱讀 579·2019-08-26 11:40
閱讀 1864·2019-08-23 17:17