精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

使用Service Worker做一個PWA離線網頁應用

開發 開發工具
本文介紹了怎么用Service Worker結合Manifest做一個PWA離線Web APP,主要是用Service Worker控制緩存,由于是寫JS,比較靈活,還可以與頁面進行通信,另外通過請求頁面的更新時間來判斷是否需要更新html緩存。Service Worker的兼容性不是特別好,但是前景比較光明,瀏覽器都在準備支持?,F階段可以結合offline cache的Manifest做離線應用。

[[206816]]

在上一篇《我是怎樣讓網站用上HTML5 Manifest》介紹了怎么用Manifest做一個離線網頁應用,結果被廣大網友吐槽說這個東西已經被deprecated,移出web標準了,現在被Service Worker替代了,不管怎么樣,Manifest的一些思想還是可以借用的。筆者又將網站升級到了Service Worker,如果是用Chrome等瀏覽器就用Service Worker做離線緩存,如果是Safari瀏覽器就還是用Manifest,讀者可以打開這個網站https://fed.renren.com感受一下,斷網也是能正常打開。

1. 什么是Service Worker

Service Worker是谷歌發起的實現PWA(Progressive Web App)的一個關鍵角色,PWA是為了解決傳統Web APP的缺點:

(1)沒有桌面入口

(2)無法離線使用

(3)沒有Push推送

那Service Worker的具體表現是怎么樣的呢?如下圖所示:

Service Worker是在后臺啟動的一條服務Worker線程,上圖我開了兩個標簽頁,所以顯示了兩個Client,但是不管開多少個頁面都只有一個Worker在負責管理。這個Worker的工作是把一些資源緩存起來,然后攔截頁面的請求,先看下緩存庫里有沒有,如果有的話就從緩存里取,響應200,反之沒有的話就走正常的請求。具體來說,Service Worker結合Web App Manifest能完成以下工作(這也是PWA的檢測標準):

包括能夠離線使用、斷網時返回200、能提示用戶把網站添加一個圖標到桌面上等。

2. Service Worker的支持情況

Service Worker目前只有Chrome/Firfox/Opera支持:

Safari和Edge也在準備支持Service Worker,由于Service Worker是谷歌主導的一項標準,對于生態比較封閉的Safari來說也是迫于形勢開始準備支持了,在Safari TP版本,可以看到:

在實驗功能(Experimental Features)里已經有Service Worker的菜單項了,只是即使打開也是不能用,會提示你還沒有實現:

但不管如何,至少說明Safari已經準備支持Service Worker了。另外還可以看到在今年2017年9月發布的Safari 11.0.1版本已經支持WebRTC了,所以Safari還是一個上進的孩子。

Edge也準備支持,所以Service Worker的前景十分光明。

3. 使用Service Worker

Service Worker的使用套路是先注冊一個Worker,然后后臺就會啟動一條線程,可以在這條線程啟動的時候去加載一些資源緩存起來,然后監聽fetch事件,在這個事件里攔截頁面的請求,先看下緩存里有沒有,如果有直接返回,否則正常加載?;蛘呤且婚_始不緩存,每個資源請求后再拷貝一份緩存起來,然后下一次請求的時候緩存里就有了。

(1)注冊一個Service Worker

Service Worker對象是在window.navigator里面,如下代碼:

  1. window.addEventListener("load"function() { 
  2.     console.log("Will the service worker register?"); 
  3.     navigator.serviceWorker.register('/sw-3.js'
  4.     .then(function(reg){ 
  5.         console.log("Yes, it did."); 
  6.     }).catch(function(err) { 
  7.         console.log("No it didn't. This happened: ", err) 
  8.     });  
  9. }); 

在頁面load完之后注冊,注冊的時候傳一個js文件給它,這個js文件就是Service Worker的運行環境,如果不能成功注冊的話就會拋異常,如Safari TP雖然有這個對象,但是會拋異常無法使用,就可以在catch里面處理。這里有個問題是為什么需要在load事件啟動呢?因為你要額外啟動一個線程,啟動之后你可能還會讓它去加載資源,這些都是需要占用CPU和帶寬的,我們應該保證頁面能正常加載完,然后再啟動我們的后臺線程,不能與正常的頁面加載產生競爭,這個在低端移動設備意義比較大。

還有一點需要注意的是Service Worker和Cookie一樣是有Path路徑的概念的,如果你設定一個cookie假設叫time的path=/page/A,在/page/B這個頁面是不能夠獲取到這個cookie的,如果設置cookie的path為根目錄/,則所有頁面都能獲取到。類似地,如果注冊的時候使用的js路徑為/page/sw.js,那么這個Service Worker只能管理/page路徑下的頁面和資源,而不能夠處理/api路徑下的,所以一般把Service Worker注冊到***目錄,如上面代碼的”/sw-3.js”,這樣這個Service Worker就能接管頁面的所有資源了。

(2)Service Worker安裝和激活

注冊完之后,Service Worker就會進行安裝,這個時候會觸發install事件,在install事件里面可以緩存一些資源,如下sw-3.js:

  1. const CACHE_NAME = "fed-cache"
  2. this.addEventListener("install"function(event) { 
  3.     this.skipWaiting(); 
  4.     console.log("install service worker"); 
  5.     // 創建和打開一個緩存庫 
  6.     caches.open(CACHE_NAME); 
  7.     // 首頁 
  8.     let cacheResources = ["https://fed.renren.com/?launcher=true"]; 
  9.     event.waitUntil( 
  10.         // 請求資源并添加到緩存里面去 
  11.         caches.open(CACHE_NAME).then(cache => { 
  12.             cache.addAll(cacheResources); 
  13.         }) 
  14.     ); 
  15. }); 

通過上面的操作,創建和添加了一個緩存庫叫fed-cache,如下Chrome控制臺所示:

Service Worker的API基本上都是返回Promise對象避免堵塞,所以要用Promise的寫法。上面在安裝Service Worker的時候就把首頁的請求給緩存起來了。在Service Worker的運行環境里面它有一個caches的全局對象,這個是緩存的入口,還有一個常用的clients的全局對象,一個client對應一個標簽頁。

在Service Worker里面可以使用fetch等API,它和DOM是隔離的,沒有windows/document對象,無法直接操作DOM,無法直接和頁面交互,在Service Worker里面無法得知當前頁面打開了、當前頁面的url是什么,因為一個Service Worker管理當前打開的幾個標簽頁,可以通過clients知道所有頁面的url。還有可以通過postMessage的方式和主頁面互相傳遞消息和數據,進而做些控制。

install完之后,就會觸發Service Worker的active事件:

  1. this.addEventListener("active"function(event) { 
  2.     console.log("service worker is active"); 
  3. }); 

Service Worker激活之后就能夠監聽fetch事件了,我們希望每獲取一個資源就把它緩存起來,就不用像上一篇提到的Manifest需要先生成一個列表。

你可能會問,當我刷新頁面的時候不是又重新注冊安裝和激活了一個Service Worker?雖然又調了一次注冊,但并不會重新注冊,它發現”sw-3.js”這個已經注冊了,就不會再注冊了,進而不會觸發install和active事件,因為當前Service Worker已經是active狀態了。當需要更新Service Worker時,如變成”sw-4.js”,或者改變sw-3.js的文本內容,就會重新注冊,新的Service Worker會先install然后進入waiting狀態,等到重啟瀏覽器時,老的Service Worker就會被替換掉,新的Service Worker進入active狀態,如果不想等到重新啟動瀏覽器可以像上面一樣在install里面調skipWaiting:

  1. this.skipWaiting(); 

(3)fetch資源后cache起來

如下代碼,監聽fetch事件做些處理:

  1. this.addEventListener("fetch"function(event) { 
  2.     event.respondWith( 
  3.         caches.match(event.request).then(response => { 
  4.             // cache hit 
  5.             if (response) { 
  6.                 return response; 
  7.             } 
  8.   
  9.             return util.fetchPut(event.request.clone()); 
  10.         }) 
  11.     ); 
  12. }); 

先調caches.match看一下緩存里面是否有了,如果有直接返回緩存里的response,否則的話正常請求資源并把它放到cache里面。放在緩存里資源的key值是Request對象,在match的時候,需要請求的url和header都一致才是相同的資源,可以設定第二個參數ignoreVary:

  1. caches.match(event.request, {ignoreVary: true}) 

表示只要請求url相同就認為是同一個資源。

上面代碼的util.fetchPut是這樣實現的:

  1. let util = { 
  2.     fetchPut: function (request, callback) { 
  3.         return fetch(request).then(response => { 
  4.             // 跨域的資源直接return 
  5.             if (!response || response.status !== 200 || response.type !== "basic") { 
  6.                 return response; 
  7.             } 
  8.             util.putCache(request, response.clone()); 
  9.             typeof callback === "function" && callback(); 
  10.             return response; 
  11.         }); 
  12.     }, 
  13.     putCache: function (request, resource) { 
  14.         // 后臺不要緩存,preview鏈接也不要緩存 
  15.         if (request.method === "GET" && request.url.indexOf("wp-admin") < 0  
  16.               && request.url.indexOf("preview_id") < 0) { 
  17.             caches.open(CACHE_NAME).then(cache => { 
  18.                 cache.put(request, resource); 
  19.             }); 
  20.         } 
  21.     } 
  22. }; 

需要注意的是跨域的資源不能緩存,response.status會返回0,如果跨域的資源支持CORS,那么可以把request的mod改成cors。如果請求失敗了,如404或者是超時之類的,那么也直接返回response讓主頁面處理,否則的話說明加載成功,把這個response克隆一個放到cache里面,然后再返回response給主頁面線程。注意能放緩存里的資源一般只能是GET,通過POST獲取的是不能緩存的,所以要做個判斷(當然你也可以手動把request對象的method改成get),還有把一些個人不希望緩存的資源也做個判斷。

這樣一旦用戶打開過一次頁面,Service Worker就安裝好了,他刷新頁面或者打開第二個頁面的時候就能夠把請求的資源一一做緩存,包括圖片、CSS、JS等,只要緩存里有了不管用戶在線或者離線都能夠正常訪問。這樣我們自然會有一個問題,這個緩存空間到底有多大?上一篇我們提到Manifest也算是本地存儲,PC端的Chrome是5Mb,其實這個說法在新版本的Chrome已經不準確了,在Chrome 61版本可以看到本地存儲的空間和使用情況:

其中Cache Storage是指Service Worker和Manifest占用的空間大小和,上圖可以看到總的空間大小是20GB,幾乎是unlimited,所以基本上不用擔心緩存會不夠用。

(4)cache html

上面第(3)步把圖片、js、css緩存起來了,但是如果把頁面html也緩存了,例如把首頁緩存了,就會有一個尷尬的問題——Service Worker是在頁面注冊的,但是現在獲取頁面的時候是從緩存取的,每次都是一樣的,所以就導致無法更新Service Worker,如變成sw-5.js,但是PWA又要求我們能緩存頁面html。那怎么辦呢?谷歌的開發者文檔它只是提到會存在這個問題,但并沒有說明怎么解決這個問題。這個的問題的解決就要求我們要有一個機制能知道html更新了,從而把緩存里的html給替換掉。

Manifest更新緩存的機制是去看Manifest的文本內容有沒有發生變化,如果發生變化了,則會去更新緩存,Service Worker也是根據sw.js的文本內容有沒有發生變化,我們可以借鑒這個思想,如果請求的是html并從緩存里取出來后,再發個請求獲取一個文件看html更新時間是否發生變化,如果發生變化了則說明發生更改了,進而把緩存給刪了。所以可以在服務端通過控制這個文件從而去更新客戶端的緩存。如下代碼:

  1. this.addEventListener("fetch"function(event) { 
  2.   
  3.     event.respondWith( 
  4.         caches.match(event.request).then(response => { 
  5.             // cache hit 
  6.             if (response) { 
  7.                 //如果取的是html,則看發個請求看html是否更新了 
  8.                 if (response.headers.get("Content-Type").indexOf("text/html") >= 0) { 
  9.                     console.log("update html"); 
  10.                     let url = new URL(event.request.url); 
  11.                     util.updateHtmlPage(url, event.request.clone(), event.clientId); 
  12.                 } 
  13.                 return response; 
  14.             } 
  15.   
  16.             return util.fetchPut(event.request.clone()); 
  17.         }) 
  18.     ); 
  19. }); 

通過響應頭header的content-type是否為text/html,如果是的話就去發個請求獲取一個文件,根據這個文件的內容決定是否需要刪除緩存,這個更新的函數util.updateHtmlPage是這么實現的:

  1. let pageUpdateTime = { 
  2.   
  3. }; 
  4. let util = { 
  5.     updateHtmlPage: function (url, htmlRequest) { 
  6.         let pageName = util.getPageName(url); 
  7.         let jsonRequest = new Request("/html/service-worker/cache-json/" + pageName + ".sw.json"); 
  8.         fetch(jsonRequest).then(response => { 
  9.             response.json().then(content => { 
  10.                 if (pageUpdateTime[pageName] !== content.updateTime) { 
  11.                     console.log("update page html"); 
  12.                     // 如果有更新則重新獲取html 
  13.                     util.fetchPut(htmlRequest); 
  14.                     pageUpdateTime[pageName] = content.updateTime; 
  15.                 } 
  16.             }); 
  17.         }); 
  18.     }, 
  19.     delCache: function (url) { 
  20.         caches.open(CACHE_NAME).then(cache => { 
  21.             console.log("delete cache " + url); 
  22.             cache.delete(url, {ignoreVary: true}); 
  23.         }); 
  24.     } 
  25. }; 

代碼先去獲取一個json文件,一個頁面會對應一個json文件,這個json的內容是這樣的:

  1. {"updateTime":"10/2/2017, 3:23:57 PM","resources": {img: [], css: []}} 

里面主要有一個updateTime的字段,如果本地內存沒有這個頁面的updateTime的數據或者是和***updateTime不一樣,則重新去獲取 html,然后放到緩存里。接著需要通知頁面線程數據發生變化了,你刷新下頁面吧。這樣就不用等用戶刷新頁面才能生效了。所以當刷新完頁面后用postMessage通知頁面:

  1. let util = { 
  2.     postMessage: async function (msg) { 
  3.         const allClients = await clients.matchAll(); 
  4.         allClients.forEach(client => client.postMessage(msg)); 
  5.     } 
  6. }; 
  7. util.fetchPut(htmlRequest, falsefunction() { 
  8.     util.postMessage({type: 1, desc"html found updated", url: url.href}); 
  9. }); 

并規定type: 1就表示這是一個更新html的消息,然后在頁面監聽message事件:

  1. if("serviceWorker" in navigator) { 
  2.     navigator.serviceWorker.addEventListener("message"function(event) { 
  3.         let msg = event.data; 
  4.         if (msg.type === 1 && window.location.href === msg.url) { 
  5.             console.log("recv from service worker", event.data); 
  6.             window.location.reload(); 
  7.         }    
  8.     });  

然后當我們需要更新html的時候就更新json文件,這樣用戶就能看到***的頁面了。或者是當用戶重新啟動瀏覽器的時候會導致Service Worker的運行內存都被清空了,即存儲頁面更新時間的變量被清空了,這個時候也會重新請求頁面。

需要注意的是,要把這個json文件的http cache時間設置成0,這樣瀏覽器就不會緩存了,如下nginx的配置:

  1. location ~* .sw.json$ { 
  2.     expires 0; 

因為這個文件是需要實時獲取的,不能被緩存,firefox默認會緩存,Chrome不會,加上http緩存時間為0,firefox也不會緩存了。

還有一種更新是用戶更新的,例如用戶發表了評論,需要在頁面通知service worker把html緩存刪了重新獲取,這是一個反過來的消息通知:

  1. if ("serviceWorker" in navigator) { 
  2.     document.querySelector(".comment-form").addEventListener("submit"function() { 
  3.             navigator.serviceWorker.controller.postMessage({ 
  4.                 type: 1,  
  5.                 desc"remove html cache",  
  6.                 url: window.location.href} 
  7.             ); 
  8.         } 
  9.     }); 

Service Worker也監聽message事件:

  1. const messageProcess = { 
  2.     // 刪除html index 
  3.     1: function (url) { 
  4.         util.delCache(url); 
  5.     } 
  6. }; 
  7.   
  8. let util = { 
  9.     delCache: function (url) { 
  10.         caches.open(CACHE_NAME).then(cache => { 
  11.             console.log("delete cache " + url); 
  12.             cache.delete(url, {ignoreVary: true}); 
  13.         }); 
  14.     } 
  15. }; 
  16.   
  17. this.addEventListener("message"function(event) { 
  18.     let msg = event.data; 
  19.     console.log(msg); 
  20.     if (typeof messageProcess[msg.type] === "function") { 
  21.         messageProcess[msg.type](msg.url); 
  22.     } 
  23. }); 

根據不同的消息類型調不同的回調函數,如果是1的話就是刪除cache。用戶發表完評論后會觸發刷新頁面,刷新的時候緩存已經被刪了就會重新去請求了。

這樣就解決了實時更新的問題。

4. Http/Manifest/Service Worker三種cache的關系

要緩存可以使用三種手段,使用Http Cache設置緩存時間,也可以用Manifest的Application Cache,還可以用Service Worker緩存,如果三者都用上了會怎么樣呢?

會以Service Worker為優先,因為Service Worker把請求攔截了,它***做處理,如果它緩存庫里有的話直接返回,沒有的話正常請求,就相當于沒有Service Worker了,這個時候就到了Manifest層,Manifest緩存里如果有的話就取這個緩存,如果沒有的話就相當于沒有Manifest了,于是就會從Http緩存里取了,如果Http緩存里也沒有就會發請求去獲取,服務端根據Http的etag或者Modified Time可能會返回304 Not Modified,否則正常返回200和數據內容。這就是整一個獲取的過程。

所以如果既用了Manifest又用Service Worker的話應該會導致同一個資源存了兩次。但是可以讓支持Service Worker的瀏覽器使用Service Worker,而不支持的使用Manifest.

5. 使用Web App Manifest添加桌面入口

注意這里說的是另外一個Manifest,這個Manifest是一個json文件,用來放網站icon名稱等信息以便在桌面添加一個圖標,以及制造一種打開這個網頁就像打開App一樣的效果。上面一直說的Manifest是被廢除的Application Cache的Manifest。

這個Maifest.json文件可以這么寫:

  1.   "short_name""人人FED"
  2.   "name""人人網FED,專注于前端技術"
  3.   "icons": [ 
  4.     { 
  5.       "src""/html/app-manifest/logo_48.png"
  6.       "type""image/png"
  7.       "sizes""48x48" 
  8.     }, 
  9.     { 
  10.       "src""/html/app-manifest/logo_96.png"
  11.       "type""image/png"
  12.       "sizes""96x96" 
  13.     }, 
  14.     { 
  15.       "src""/html/app-manifest/logo_192.png"
  16.       "type""image/png"
  17.       "sizes""192x192" 
  18.     }, 
  19.     { 
  20.       "src""/html/app-manifest/logo_512.png"
  21.       "type""image/png"
  22.       "sizes""512x512" 
  23.     } 
  24.   ], 
  25.   "start_url""/?launcher=true"
  26.   "display""standalone"
  27.   "background_color""#287fc5"
  28.   "theme_color""#fff" 

icon需要準備多種規格,***需要512px * 512px的,這樣Chrome會自動去選取合適的圖片。如果把display改成standalone,從生成的圖標打開就會像打開一個App一樣,沒有瀏覽器地址欄那些東西了。start_url指定打開之后的入口鏈接。

然后添加一個link標簽指向這個manifest文件:

  1. <link rel="manifest" href="/html/app-manifest/manifest.json"

這樣結合Service Worker緩存:

把start_url指向的頁面用Service Worker緩存起來,這樣當用戶用Chrome瀏覽器打開這個網頁的時候,Chrome就會在底部彈一個提示,詢問用戶是否把這個網頁添加到桌面,如果點“添加”就會生成一個桌面圖標,從這個圖標點進去就像打開一個App一樣。感受如下:

 

比較尷尬的是Manifest目前只有Chrome支持,并且只能在安卓系統上使用,IOS的瀏覽器無法添加一個桌面圖標,因為IOS沒有開放這種API,但是自家的Safari卻又是可以的。

綜上,本文介紹了怎么用Service Worker結合Manifest做一個PWA離線Web APP,主要是用Service Worker控制緩存,由于是寫JS,比較靈活,還可以與頁面進行通信,另外通過請求頁面的更新時間來判斷是否需要更新html緩存。Service Worker的兼容性不是特別好,但是前景比較光明,瀏覽器都在準備支持?,F階段可以結合offline cache的Manifest做離線應用。

原文鏈接:https://fed.renren.com/2017/10/04/service-worker/

【本文是51CTO專欄作者“人人網FED”的原創稿件,轉載請通過51CTO聯系原作者獲取授權】

戳這里,看該作者更多好文

責任編輯:武曉燕 來源: 51CTO專欄
相關推薦

2016-11-23 18:13:44

javascriptrxjsreactivex

2021-12-17 10:06:42

鴻蒙HarmonyOS應用

2012-04-10 16:26:46

2015-07-03 11:27:30

程序員自己神器

2023-04-11 09:12:31

北向應用開發鴻蒙

2012-12-17 12:58:18

WebjQuery重構

2017-06-30 15:18:24

對賬系統互聯網

2022-06-19 20:48:06

樹莓派Linux

2022-12-22 19:22:55

應用開發鴻蒙

2011-02-28 09:22:47

SQLite記賬簿

2018-01-04 16:04:35

圓環放大動畫

2022-06-01 09:50:21

Skopeo搬運工鏡像

2022-04-26 09:16:07

PWA線程生命周期

2021-04-19 10:14:37

GoogleChrome瀏覽器

2022-05-31 07:29:42

Windows標簽Web 應用程序

2020-07-20 10:00:52

Python翻譯工具命令行

2021-12-01 07:02:55

Python 記錄器按鍵

2019-04-22 10:25:52

程序員技術職場

2014-04-29 10:50:16

池建強

2023-10-31 15:08:56

WorkBoxServiceWorker
點贊
收藏

51CTO技術棧公眾號

国产精品88a∨| 欧美在线免费观看亚洲| 国产丝袜不卡| 尤物视频免费观看| 91久久电影| 日韩h在线观看| 色一情一区二区三区| heyzo在线欧美播放| 国产亚洲精品bt天堂精选| 亚洲自拍欧美另类| 男操女视频网站| 亚洲人人精品| 久久亚洲春色中文字幕| asian性开放少妇pics| 亚洲色图图片| 日本福利一区二区| 无码人妻少妇伦在线电影| 色网站免费在线观看| 26uuu欧美日本| 国产精品乱码| www.日韩高清| 久久er精品视频| 国产精品18久久久久久首页狼| 欧美国产精品一二三| 日韩精品午夜| 亚洲图片在区色| www.88av| 57pao国产一区二区| 欧美日韩国产综合一区二区三区| 欧美变态另类刺激| free性护士videos欧美| 亚洲精品成人精品456| 伊人av成人| 国产区av在线| 国产午夜亚洲精品羞羞网站| 久久精品人人做人人爽电影| 午夜久久久久久噜噜噜噜| 寂寞少妇一区二区三区| 国产福利视频一区二区| 久久国产视频精品| 亚洲自啪免费| 欧美在线一级视频| 亚洲熟女综合色一区二区三区| 黄色工厂这里只有精品| 欧美国产视频一区二区| 青青操视频在线播放| 中文字幕乱码亚洲无线精品一区| 色av中文字幕一区| 欧美88888| 日韩久久综合| 久久久国产一区二区| 日本中文在线视频| 亚洲色图网站| 欧美激情影音先锋| 国产在线综合网| 亚洲黄色av| 欧美在线观看网址综合| 国产成人一区二区三区影院在线| 亚洲深夜av| 青草青草久热精品视频在线网站| 免费黄色网址在线| 久久精品系列| 国产女精品视频网站免费| 91 中文字幕| 国产一区二区导航在线播放| 国产成人精品日本亚洲11| 无码国产色欲xxxx视频| 久久你懂得1024| 亚洲欧洲精品一区二区| 快射av在线播放一区| 一区av在线播放| 无码中文字幕色专区| 国偷自产一区二区免费视频| 欧美性猛交一区二区三区精品| 亚洲另类第一页| 亚洲日本一区二区三区在线| 精品丝袜一区二区三区| 你懂得视频在线观看| 天天综合一区| 久久久久在线观看| 波多野结衣网站| 激情av综合网| 精品无人乱码一区二区三区的优势| 亚洲色图21p| 国产精品每日更新| aa视频在线播放| 蜜桃精品在线| 欧美成人一区二区三区在线观看| 亚洲成人av免费在线观看| 成人中文在线| 久久免费视频在线观看| 在线观看你懂的网站| 国产成人精品www牛牛影视| 噜噜噜噜噜久久久久久91| 免费看a在线观看| 午夜精品久久久久久| 国产原创精品在线| 欧美日韩破处| 久久国产精品久久久久久| 亚洲综合久久网| 国产成人精品亚洲午夜麻豆| 欧美精品与人动性物交免费看| 国产在线更新| 在线视频国内一区二区| 中文字幕人妻一区| 99视频精品全部免费在线视频| 91国产精品91| 国产成人精品毛片| 欧美高清在线视频| 欧美性大战久久久久xxx | 亚洲午夜激情av| www.涩涩涩| 夜夜春成人影院| 久久久久久午夜| 97成人免费视频| 中文字幕高清不卡| 国产美女无遮挡网站| 日韩精品一区国产| 久久久精品美女| 国内av在线播放| 久久嫩草精品久久久精品| 国产va亚洲va在线va| 人人爱人人干婷婷丁香亚洲| 国产亚洲视频在线观看| 麻豆精品久久久久久久99蜜桃| 国产a久久麻豆| 免费观看中文字幕| 青草综合视频| 日韩一级黄色av| 中文在线免费看视频| 久久久久久久久岛国免费| 国产无限制自拍| 中文字幕日韩高清在线| 久久成人精品电影| 国产一区二区麻豆| 中文字幕一区不卡| 色播五月综合网| 久久精品国产www456c0m| 国产精品91久久久久久| 免费看男男www网站入口在线| 天天综合色天天综合色h| 看全色黄大色黄女片18| 亚洲无毛电影| 国产精品一区二区av| 俄罗斯一级**毛片在线播放| 日韩你懂的在线播放| 青青草手机视频在线观看| 国产麻豆91精品| av一区二区三区免费观看| 免费精品一区| 久久99久国产精品黄毛片入口| 精品国产999久久久免费| 亚洲综合免费观看高清完整版在线 | 在线观看中文字幕视频| 久久日一线二线三线suv| 日本wwww视频| 日韩精品影视| 亚洲精品欧美日韩专区| 精品一性一色一乱农村| 精品国产成人系列| 国产精品suv一区二区三区| 久久嫩草精品久久久久| 色播五月激情五月| 综合天堂av久久久久久久| 成人av蜜桃| 日韩激情电影免费看| 国产一区二区黑人欧美xxxx| 91麻豆成人精品国产| 一区二区三区在线观看动漫| 免费日本黄色网址| 老**午夜毛片一区二区三区| 一级二级三级欧美| 成人另类视频| 国产99久久精品一区二区永久免费 | 久久免费看少妇高潮| 孩娇小videos精品| 欧美99久久| 久久久久久久久久久久久9999| 日韩高清中文字幕一区二区| 色偷偷av亚洲男人的天堂| 国产 欧美 自拍| 日本韩国一区二区三区| 欧美成人一区二区三区高清| 久久久久久影视| 国内精品国产三级国产aⅴ久| 亚洲国产免费| 在线成人性视频| 久久男人av| 国产一区二区视频在线观看| av蜜臀在线| www欧美日韩| 每日更新在线观看av| 在线综合视频播放| 91午夜精品亚洲一区二区三区| 亚洲人妖av一区二区| 日韩人妻无码一区二区三区| 韩国一区二区在线观看| 91黄色小网站| 狠狠入ady亚洲精品经典电影| 欧美中文娱乐网| 9l视频自拍九色9l视频成人| 国产精品免费电影| 免费高潮视频95在线观看网站| 久久精品国产久精国产思思| 日本大臀精品| 欧美精品一区二区三区高清aⅴ| 国产精品露脸视频| 狠狠躁夜夜躁人人爽超碰91| 免费一级黄色大片| 1024精品合集| 18啪啪污污免费网站| 91在线国产观看| 又色又爽又黄18网站| 久久99蜜桃精品| 欧美日韩在线成人| 欧美亚洲一级| 妞干网在线视频观看| 欧美成人有码| 中文字幕精品—区二区日日骚| 一区二区三区韩国免费中文网站| 成人欧美一区二区三区黑人免费| 亚洲最大的免费视频网站| 国产成人啪精品视频免费网| 麻豆理论在线观看| 国内精品久久久久久影视8| 伊人222成人综合网| 日韩有码在线播放| 欧美jizzhd欧美| 一区二区欧美激情| 北岛玲一区二区三区| 亚洲欧洲在线看| 久久视频www| 亚洲欧美日韩第一区| 你懂得在线网址| 国产视频久久久久久久| 四虎成人免费在线| 亚洲男人第一网站| 九九九伊在人线综合| 亚洲欧美日韩精品久久亚洲区 | 日韩av三级在线| 亚洲深夜av| av片中文字幕| 日韩二区三区四区| 欧美日韩中文不卡| 精品亚洲国产成人av制服丝袜 | www.久久精品视频| 色先锋aa成人| 特级西西444www高清大视频| 欧美日韩亚洲综合在线 欧美亚洲特黄一级| 波多野结衣视频观看| 欧美日韩国产精品自在自线| 亚洲一区二区色| 91精品国产综合久久久久久久久久| 一本色道久久综合亚洲| 7777精品久久久大香线蕉| 91麻豆国产视频| 日韩欧美激情四射| 五月色婷婷综合| 国产一区二区黑人欧美xxxx| 天堂地址在线www| 欧美猛交免费看| 高清在线视频不卡| 国产91色在线免费| 亚洲国产91视频| 成人自拍爱视频| 精品在线播放| 正在播放一区| 亚洲精品少妇| 成人免费在线观看视频网站| 国产一区二区视频在线播放| 中国极品少妇xxxx| 国产欧美一区二区三区鸳鸯浴| 天天操天天摸天天舔| 亚洲一区二区三区在线播放| 中文字幕激情小说| 欧美精品日日鲁夜夜添| 国产综合在线播放| 一本色道久久88亚洲综合88| 91国内在线| 国产999精品久久久| 精品国产鲁一鲁****| 精品国产一区二区三区免费| 日韩精品欧美激情一区二区| 97视频在线免费| 美女视频网站久久| 亚洲黄色免费在线观看| 国产精品久久久久久久久快鸭| 精品无码一区二区三区电影桃花 | 天天综合网天天综合色| 中文无码精品一区二区三区| 精品福利视频一区二区三区| 国产二区视频在线观看| 欧美激情综合色综合啪啪五月| 成人啊v在线| 黄色国产精品一区二区三区| 日韩在线看片| 日韩免费一级视频| 国产精品综合网| 精品一区二区三区蜜桃在线| 亚洲va国产天堂va久久en| 中文字幕第31页| 日韩成人中文字幕| 污影院在线观看| 国产免费一区视频观看免费| 香蕉久久夜色精品国产使用方法| 992tv成人免费观看| 日韩电影在线看| 国产麻豆xxxvideo实拍| 亚洲人成网站在线| 中文字幕视频一区二区| 日韩精品免费一线在线观看| 伊人影院在线视频| 国产一区视频在线| 精品久久不卡| 欧美视频第三页| 91蜜桃视频在线| 久久高清免费视频| 日韩欧美卡一卡二| 黄色小网站在线观看| 国产精品视频永久免费播放| 四虎884aa成人精品最新| 日韩成人手机在线| 国产成人在线免费观看| 加勒比婷婷色综合久久| 日本道色综合久久| 青青草超碰在线| 欧美一区二区色| 欧美激情网址| 欧美,日韩,国产在线| 成人av动漫在线| 国产无套在线观看| 亚洲成人久久电影| 国产盗摄精品一区二区酒店| 成人av蜜桃| 国语自产精品视频在线看8查询8| 国产又粗又猛大又黄又爽| 亚洲美女在线一区| 成 人 黄 色 片 在线播放| 久久精品视频va| 国产精品一区三区在线观看| 9999在线观看| 国产精一区二区三区| 唐朝av高清盛宴| 欧美精品一区二| 老牛影视精品| 日本不卡久久| 青青草国产成人99久久| 男人的午夜天堂| 日韩欧美色综合网站| 国产探花视频在线观看| 国外成人免费视频| 男人的天堂亚洲在线| 欧美 日韩 成人| 777xxx欧美| 男女羞羞视频在线观看| 国产一区免费在线| 老牛影视一区二区三区| 国产不卡在线观看视频| 欧美一级日韩一级| 1234区中文字幕在线观看| 欧美日本国产精品| 免费在线观看成人| 少妇久久久久久被弄高潮| 精品久久国产97色综合| 国产精品一区二区av影院萌芽| 亚洲国产精品视频一区| 国产伦精品一区二区三区免费| 青青草手机在线观看| 亚洲精品一区二区久| 欧美亚洲二区| 免费看毛片的网址| 日本一区二区三区dvd视频在线| 91亚洲国产成人精品一区| 欧美激情综合色| 全球成人免费直播| 午夜影院福利社| 在线观看不卡视频| 婷婷丁香在线| 欧美人xxxxx| 国产成人午夜视频| www.欧美色| 欧美大片在线看免费观看| 伊人久久大香线蕉综合网站| 中文字幕久久av| 欧美日韩国产一区二区三区| 在线a人片免费观看视频| 国产精品区一区二区三含羞草| 日韩精品欧美成人高清一区二区| 尤物在线免费视频| 亚洲精品美女久久| 成人精品视频在线观看| 伊人成色综合网| 亚洲精品福利视频网站| 国产大片在线免费观看| 91免费版黄色| 日韩精品一级二级| 日本三级视频在线| 久久精品视频亚洲| 精品视频亚洲| 亚洲麻豆一区二区三区|