成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

service worker在移動(dòng)端H5項(xiàng)目的應(yīng)用

cucumber / 2240人閱讀

摘要:和的關(guān)系不是一項(xiàng)技術(shù),也不是一個(gè)框架,我們可以把她理解為一種模式,一種通過應(yīng)用一些技術(shù)將在安全性能和體驗(yàn)等方面帶來漸進(jìn)式的提升的一種的模式。這里需要注意的是,首次注冊(cè)線程的頁面需要再次加載才會(huì)受其控制。

1. PWA和Service Worker的關(guān)系

PWA (Progressive Web Apps) 不是一項(xiàng)技術(shù),也不是一個(gè)框架,我們可以把她理解為一種模式,一種通過應(yīng)用一些技術(shù)將 Web App 在安全、性能和體驗(yàn)等方面帶來漸進(jìn)式的提升的一種 Web App的模式。對(duì)于 webview 來說,Service Worker 是一個(gè)獨(dú)立于js主線程的一種 Web Worker 線程, 一個(gè)獨(dú)立于主線程的 Context,但是面向開發(fā)者來說 Service Worker 的形態(tài)其實(shí)就是一個(gè)需要開發(fā)者自己維護(hù)的文件,我們假設(shè)這個(gè)文件叫做 sw.js。通過 service worker 我們可以代理 webview 的請(qǐng)求相當(dāng)于是一個(gè)正向代理的線程,fiddler也是干這些事情),在特定路徑注冊(cè) service worker 后,可以攔截并處理該路徑下所有的網(wǎng)絡(luò)請(qǐng)求,進(jìn)而實(shí)現(xiàn)頁面資源的可編程式緩存,在弱網(wǎng)和無網(wǎng)情況下帶來流暢的產(chǎn)品體驗(yàn),所以 service worker 可以看做是實(shí)現(xiàn)pwa模式的一項(xiàng)技術(shù)實(shí)現(xiàn)。

2. service worker簡介

注意事項(xiàng)

service worker 是一種JS工作線程,無法直接訪問DOM, 該線程通過postMessage接口消息形式來與其控制的頁面進(jìn)行通信;

service worker 廣泛使用了Promise,這些在接下來代碼示例中將會(huì)看到;

目前并不是所有主流瀏覽器支持 service worker, 可以通過 navigator && navigator.serviceWorker 來進(jìn)行特性探測(cè);

在開發(fā)過程中,可以通過 localhost 使用服務(wù)工作線程,如若上線部署,必須要通過https來訪問注冊(cè)服務(wù)工作線程的頁面,但有種場(chǎng)景是我們的測(cè)試環(huán)境可能并不支持https,這時(shí)就要通過更改host文件將localhost指向測(cè)試環(huán)境ip來巧妙繞過該問題(例如:192.168.22.144 localhost);

生命周期

service worker的生命周期完全獨(dú)立于網(wǎng)頁,要為網(wǎng)站安裝服務(wù)工作線程,我們需要在頁面業(yè)務(wù)js代碼中注冊(cè),瀏覽器從指定路徑下載并解析服務(wù)工作線程腳本進(jìn)而瀏覽器將會(huì)在后臺(tái)啟動(dòng)安裝步驟,在安裝過程中,我們通常會(huì)緩存靜態(tài)資源,如果所有文件都成功緩存,那么服務(wù)工程線程就安裝完畢,如果任何文件下載失敗或緩存失敗,那么安裝步驟將會(huì)失敗,當(dāng)然也不會(huì)被激活。安裝后就進(jìn)入激活步驟,這里是管理舊緩存的絕佳機(jī)會(huì)(后面代碼示例中將會(huì)介紹原因),激活后service worker將開始對(duì)其作用域內(nèi)的所有頁面實(shí)施控制。這里需要注意的是,首次注冊(cè) service worker 線程的頁面需要再次加載才會(huì)受其控制。在成功安裝完成并處于激活狀態(tài)之前,服務(wù)工程線程不會(huì)收到fetch和push事件;

工作流程

注冊(cè)

這里需要注意的是register方法注冊(cè)服務(wù)工作線程文件的位置,該path就是默認(rèn)的 serviceworker 的作用域,例如注冊(cè)path為/a/b/service-worker.js,則默認(rèn)scope為/a/b/,當(dāng)然也可以通過傳入{scope: "/a/b/c/"}來指定自己的scope,但這里要特別注意的是,傳入的scope參數(shù)一定是在默認(rèn)作用域范圍內(nèi)再自定義(例如/a/b/c/),反之自定義為/d/e/就不行;

通俗來講,上面提到的scope就是 service worker 能夠控制和發(fā)揮作用的范圍;

注意注冊(cè)是在自己的業(yè)務(wù)代碼中進(jìn)行,后面會(huì)有具體通過插件來實(shí)現(xiàn)注冊(cè)的代碼示例;

if(navigator && navigator.serviceWorker) {
    navigator.serviceWorker.register("/service-worker.js").then(function (registration) {
        console.log(registration)
    }).catch(function (err) {
        console.log(err)
    })
}

安裝

下面代碼就是前面注冊(cè)的service-worker.js文件內(nèi)容;

我們通過install事件來定義安裝步驟,通過緩存名稱調(diào)用caches.open(), 之后再調(diào)用cache.addAll()并傳入具體緩存文件清單數(shù)組,這是一個(gè)Promise鏈?zhǔn)絜vent.waitUntil()方法帶有Promise參數(shù)并使用它來判斷花費(fèi)耗時(shí)以及安裝是否成功;

正如前面提到,安裝過程中如果所有清單中文件成功緩存,則安裝結(jié)束,否則安裝過程視為失敗,所以在實(shí)踐中我們盡可能緩存核心資源以避免服務(wù)工作線程未能安裝;

var cacheVersion = "test_2017122608";
// 安裝服務(wù)工作線程
self.addEventListener("install", function(event){
    // 需要緩存的資源
    var cacheFiles = [
        "/dist/index.html",
        "/dist/js/index_async_bundle.js"
    ];
    console.log("service worker: run into install");
    event.waitUntil(caches.open(cacheVersion).then(function(cache)
    {
        return cache.addAll(cacheFiles);
    }));
});

?激活

在某個(gè)時(shí)間點(diǎn)服務(wù)工程線程需要更新(例如:service-worker.js文件發(fā)生更改并上線),用戶訪問頁面時(shí)瀏覽器會(huì)嘗試在后臺(tái)重新下載service-worker.js,如果服務(wù)工程線程文件與當(dāng)前所用文件存在字節(jié)差異,則將其視為“新服務(wù)工作線程”;

新服務(wù)工作線程將會(huì)啟動(dòng),且將會(huì)觸發(fā) install 事件;

此時(shí)舊的服務(wù)工作線程仍將控制著當(dāng)前頁面,因此新服務(wù)工作線程將會(huì)進(jìn)入waiting狀態(tài);

當(dāng)網(wǎng)站當(dāng)前頁面關(guān)閉時(shí),舊服務(wù)工作線程將會(huì)終止,新服務(wù)工作線程將會(huì)取得控權(quán);

新服務(wù)工作線程取得控制權(quán)后,將會(huì)觸發(fā) activate 事件;

監(jiān)聽 activate 事件的回調(diào)函數(shù)中常見的任務(wù)是管理緩存,前面我也提到過這是管理舊緩存的絕佳時(shí)機(jī),因?yàn)槿绻诎惭b步驟中清理了舊緩存,由于舊的服務(wù)工作線程仍舊控制著頁面,將無法從緩存中提取文件,但是在 activate 時(shí)舊服務(wù)工作線程已經(jīng)終止了頁面控制權(quán),所在在這里清理舊緩存再合適不過;

// 新的service worker線程被激活(其實(shí)和離線包一樣存在"二次生效"的機(jī)理)
self.addEventListener("activate", function (event) {
    console.log("service worker: run into activate");
    event.waitUntil(caches.keys().then(function (cacheNames) {
        return Promise.all(cacheNames.map(function (cacheName) {
            // 注意這里cacheVersion也可以是一個(gè)數(shù)組
            if(cacheName !== cacheVersion){
                console.log("service worker: clear cache" + cacheName);
                return caches.delete(cacheName);
            }
        }));
    }));
});

監(jiān)聽

這里通過監(jiān)聽fetch事件來代理響應(yīng),進(jìn)而實(shí)現(xiàn)自定義前端資源緩存;

在event.respondWith()中我們傳入來自caches.match()的一個(gè)promise,此方法攔截請(qǐng)求并從服務(wù)工作線程所創(chuàng)建的任何緩存中查找緩存結(jié)果,如若發(fā)現(xiàn)匹配的響應(yīng)則返回緩存的值,否則,將會(huì)調(diào)用fetch以代理發(fā)出網(wǎng)絡(luò)請(qǐng)求,并將從網(wǎng)絡(luò)中檢索的數(shù)據(jù)作為結(jié)果返回;

如果希望連續(xù)性緩存新的請(qǐng)求,則注意注釋的代碼部分,其通過cache.put來將請(qǐng)求的響應(yīng)添加到緩存來實(shí)現(xiàn);

在fetch請(qǐng)求中添加對(duì)then()的回調(diào),獲得響應(yīng)后執(zhí)行檢查,并clone響應(yīng),注意這樣處理的原因是該響應(yīng)是stream,主體只能使用一次,我們需要返回能被瀏覽器使用的響應(yīng),還要傳遞到緩存以供使用,因此需要克隆一份副本;

// 攔截請(qǐng)求并響應(yīng)
self.addEventListener("fetch", function (event) {
    console.log("service worker: run into fetch");
    event.respondWith(caches.match(event.request).then(function (response) {
        // 發(fā)現(xiàn)匹配的響應(yīng)緩存
        if(response){
            console.log("service worker 匹配并讀取緩存:" + event.request.url);
            return response;
        }
        console.log("沒有匹配上:" + event.request.url);
        return fetch(event.request);
        /*var fetchRequest = event.request.clone();
        return fetch(fetchRequest).then(function(response){
            if(!response || response.status !== 200 || response.type !== "basic"){
                return response;
            }
            var responseToCache = response.clone();
            caches.open(cacheVersion).then(function (cache) {
                console.log(cache);
                cache.put(fetchRequest, responseToCache);
            });
            return response;
        });*/
    }));
});

3. 前端資源緩存演進(jìn)

利用webview自身的http緩存機(jī)制。這里往往需要服務(wù)器運(yùn)維同事配合,對(duì)于前端來講不夠靈活且緩存粒度太粗,而且在http協(xié)議在不同版本下緩存機(jī)制有一定的差異(例如1.0版本中If-Modified-Since、Last-Modified、expires, 1.1版本中對(duì)緩存進(jìn)行了優(yōu)化,添加If-None-Match、Etag、cache-control等;

離線包策略,其大致原理是通過將靜態(tài)資源打包至離線管理平臺(tái)(自行開發(fā)),在app啟動(dòng)時(shí)從離線管理平臺(tái)拉取資源包并存放于本地,后續(xù)終端將會(huì)攔截url請(qǐng)求并基于約定規(guī)則將請(qǐng)求代理到本地文件系統(tǒng),進(jìn)而加快靜態(tài)資源的訪問以及為cdn減壓,該方案的缺陷在于需要離線資源管理平臺(tái)和終端的配合,牽扯資源過多,但其優(yōu)點(diǎn)是不存在兼容性問題;

h5離線緩存manifest,其實(shí)質(zhì)就是一個(gè)緩存清單文件(xx.manifest),然后在html標(biāo)簽設(shè)置manifest屬性為xx.manifest,該緩存方案也存在“二次更新”的問題,該方案需要注意的問題是xx.manifest文件自身不要被webview緩存,且manifest文件cache部分不能使用通配符,必須手動(dòng)指定,不過好在可以通過構(gòu)建工具來解決,主流瀏覽器對(duì)該方案支持度也不錯(cuò)。與service worker相對(duì),其業(yè)務(wù)JS代碼無法感知緩存更新的時(shí)機(jī),所以service worker方案更具有想象空間;

service worker 通過一個(gè)獨(dú)立JS線程來實(shí)現(xiàn)資源的可編程式緩存;

4. 項(xiàng)目如何快速接入service worker

在接入前有兩個(gè)問題擺在我們面前,service worker可以幫助我們解決資源緩存問題,有緩存就必須要有更新的機(jī)制,service-worker.js本身也會(huì)被瀏覽器緩存,后續(xù)產(chǎn)品迭代過程中如何解決該文件自身的更新問題,否則其他資源的緩存更新也就無從談起(舊的服務(wù)工作線程將一直控制頁面),無可厚非每次構(gòu)建部署時(shí)service-worker.js需要攜帶版本號(hào)(例如?v=201801021721),當(dāng)然也可以在服務(wù)器運(yùn)維層控制該文件的cache-control: no-cache從而規(guī)避瀏覽器緩存問題,但這樣太麻煩;

我們是在業(yè)務(wù)代碼中通過register的方式引入service-worker.js, 那問題就變?yōu)槿绾卧谧?cè)服務(wù)工作線程的位置引入版本號(hào)呢,我們可以通過sw-register-webpack-plugin來解決該問題,其思路是將服務(wù)工作線程的注冊(cè)放在一個(gè)多帶帶的文件中(sw-register.js),然后自動(dòng)在頁面入口(例如index.html)寫入一段JS腳本來動(dòng)態(tài)加載sw-register.js文件,這里sw-register.js的加載路徑是帶有實(shí)時(shí)時(shí)間戳的,而生成的sw-register.js文件內(nèi)容中注冊(cè)service-worker.js的位置自動(dòng)攜帶構(gòu)建版本號(hào)參數(shù)(默認(rèn)是當(dāng)前構(gòu)建時(shí)間),該插件配置如下(基于webpack構(gòu)建的項(xiàng)目):

let SwRegisterWebpackPlugin = require("sw-register-webpack-plugin")
...
plugins: [
    new SwRegisterWebpackPlugin({
        filePath: path.resolve(__dirname, "../src/sw-register.js")
    })
]

構(gòu)建后html新增部分如圖:

構(gòu)建后生成的sw-register.js文件變化如圖:

這樣處理后,sw-register.js文件就不會(huì)被瀏覽器緩存,也即每次刷新會(huì)多一次sw-register.js的文件請(qǐng)求,由于它只是用來做注冊(cè)的工作,體量不會(huì)太大,可以接受,關(guān)鍵是前端可以自行控制

已緩存資源文件如何更新呢?上述插件只是解決了service-worker.js文件本身的更新的問題(保證每次構(gòu)建部署后會(huì)新啟一個(gè)服務(wù)工作線程),但對(duì)于service-worker.js文件中定義的cacheFiles而言,當(dāng)我們修改了已緩存文件后如何來更新緩存呢,我的項(xiàng)目是基于vue.js + webpack,打包后的JS文件是[name].[hash].[ext]格式,從前面的介紹可知資源的緩存也是基于url(作為key)來的,不可能每次構(gòu)建后都手動(dòng)去調(diào)整service-worker.js文件內(nèi)容中cacheFiles的路徑值吧,應(yīng)該是將構(gòu)建后的文件名(包括路徑)直接放到service-worker.js內(nèi)容中,看到這里你應(yīng)該想到了有webpack插件已經(jīng)幫我們做好了,那就是sw-precache-webpack-plugin,該插件會(huì)自動(dòng)在dist目錄下生成service-worker.js文件,供給service worker運(yùn)行,也就是說service-worker.js文件本身不需要我們手動(dòng)添加了,但問題是我們?nèi)绾巫远x需要緩存的文件呢,該插件的配置參數(shù)會(huì)告訴你,我的項(xiàng)目該插件配置如下:

// 生成service-worker.js和配置緩存清單
new SwPrecacheWebpackPlugin({
    cacheId: "attendance-mobile-cache",
    filename: "service-worker.js",
    minify: true,
    dontCacheBustUrlsMatching: false,
    staticFileGlobs: [
        "dist/static/js/manifest.**.*",
        "dist/static/js/vendor.**.*",
        "dist/static/js/app.**.*"
    ],
    stripPrefix: "dist/"
})

由上可知,我們能夠通過正則來匹配需要緩存的文件,這里特別要注意的是stripPrefix參數(shù)的使用,我們配置的緩存文件路徑是項(xiàng)目中的路徑,但對(duì)于部署線上而言,我們可能需要過濾前綴的部分路徑(我的項(xiàng)目線上部署文件根目錄下就是static等,所以需要過濾dist路徑),最終該插件生成的service-worker.js文件如圖所示(僅截取緩存文件清單部分代碼)

4. 調(diào)試service worker

通過上述兩個(gè)插件,我們的service-worker接入工作基本完成,那接下來就是驗(yàn)證服務(wù)工作線程運(yùn)行是否ok,通過chrome devTools(Application項(xiàng))我們可以很方面的查看當(dāng)前服務(wù)工作線程的運(yùn)行情況和已緩存了哪些文件,具體如何查看這里不再介紹;

當(dāng)首次運(yùn)行 service worker 時(shí)我們會(huì)發(fā)現(xiàn)要緩存的文件還是走正常的網(wǎng)絡(luò)請(qǐng)求,cache storage 下也看不到我們的緩存項(xiàng),因?yàn)榉?wù)工程線程也存在“二次生效”的機(jī)制(即使需要緩存的資源延遲加載),具體如下圖所示:


通過刷新訪問我們可以看到,service worker 緩存文件已經(jīng)生效,在network面板下自定義的緩存文件size項(xiàng)都顯示為“from ServiceWorker”, 耗時(shí)也明顯很低。在cache storage下面也可以看到已經(jīng)緩存的文件列表,具體如下圖所示:


接下來我們更新service-worker.js文件來看下新服務(wù)工作線程如何工作,正如前面所講新服務(wù)工作線程將會(huì)啟動(dòng)安裝,但由于舊服務(wù)工作線程控制著頁面,所以新服務(wù)工作線程將進(jìn)入waiting狀態(tài),當(dāng)當(dāng)前打開的頁面關(guān)閉時(shí),舊服務(wù)工作線程將會(huì)被終止,新服務(wù)工作線程會(huì)得的控制權(quán)并觸發(fā)activate事件,在開發(fā)過程中我們需要通過Chrome Devtools的skipWaiting或者勾選Updated on reload來強(qiáng)制激活新服務(wù)工作線程,具體如下圖所示:

在開發(fā)過程中我們可以通過上述來了解新服務(wù)工作線程的更新流程,但在實(shí)際項(xiàng)目中我們可以通過self.skipWaiting()跳過等待過程安裝后直接激活,一般我們?cè)趇nstall事件中調(diào)用,具體可參見sw-precache-webpack-plugin生成的service-worker源代碼。這會(huì)導(dǎo)致新服務(wù)工作線程將當(dāng)前活動(dòng)的工作線程逐出,skipWaiting()意味著新服務(wù)工作線程可能會(huì)控制使用較舊工作線程加載的頁面,也就是頁面獲取的部分?jǐn)?shù)據(jù)由舊工作線程處理,而新服務(wù)工作線程處理后來獲取的數(shù)據(jù),如果有問題就不要使用skipWaiting();

手動(dòng)清理service worker緩存后刷新頁面,在 Network 面板中,我們會(huì)看到本應(yīng)緩存文件的一組初始請(qǐng)求。之后是前面帶有齒輪圖標(biāo)的第二輪請(qǐng)求,這些請(qǐng)求似乎要獲取相同的資源,“齒輪”圖標(biāo)代表這些請(qǐng)求來自服務(wù)工作線程,如果不unregsiter該服務(wù)工作線程,我們會(huì)發(fā)現(xiàn)即使多次刷新頁面,Network 面板依然如此,其實(shí)也就是說資源沒有再次緩存(因?yàn)榉?wù)工作線程已經(jīng)安裝且控制當(dāng)前頁面,刷新操作不會(huì)重新觸發(fā)install事件,也就不會(huì)再次添加資源到緩存,除非unregister或者更新service-worker.js文件),具體如下圖所示:


5. 異?;貪L(注銷)

某些場(chǎng)景下如果service worker使用出現(xiàn)異常,比如不同頁面間 service worker 控制的scope存在“重疊污染”的問題,那么我們就需要緊急回滾(撤銷)當(dāng)前 service worker,在開發(fā)環(huán)境很好解決,我們依然可以通過Chrome Devtools來進(jìn)行unregister, 那么在線上環(huán)境已經(jīng)有服務(wù)工作線程在運(yùn)行的情況下呢,我們需要在新上線版本的service worker注冊(cè)前將被污染或者異常的service worker注銷掉,具體代碼如下:

if (navigator.serviceWorker) {
    navigator.serviceWorker.getRegistrations().then(function (registrations) {
        for (var item of registrations) {
            if (item.scope === "http://localhost/attendance-mobile/dist/") {
                item.unregister();
            }
        }
        // 注銷掉污染 Service Worker 之后再重新注冊(cè)...
    });
}

備注:文中部分內(nèi)容摘選自Google開發(fā)者文檔

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/51674.html

相關(guān)文章

  • service worker移動(dòng)H5項(xiàng)目應(yīng)用

    摘要:和的關(guān)系不是一項(xiàng)技術(shù),也不是一個(gè)框架,我們可以把她理解為一種模式,一種通過應(yīng)用一些技術(shù)將在安全性能和體驗(yàn)等方面帶來漸進(jìn)式的提升的一種的模式。這里需要注意的是,首次注冊(cè)線程的頁面需要再次加載才會(huì)受其控制。 1. PWA和Service Worker的關(guān)系 PWA (Progressive Web Apps) 不是一項(xiàng)技術(shù),也不是一個(gè)框架,我們可以把她理解為一種模式,一種通過應(yīng)用一些技...

    Tonny 評(píng)論0 收藏0
  • 每周清單半年盤點(diǎn)之 PWA 篇

    摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開發(fā)教程、工程實(shí)踐、深度閱讀、開源項(xiàng)目、巔峰人生等欄目。歡迎關(guān)注【前端之巔】微信公眾號(hào)(ID:frontshow),及時(shí)獲取前端每周清單;本文則是對(duì)于...

    崔曉明 評(píng)論0 收藏0
  • 構(gòu)建離線web應(yīng)用(一)

    摘要:我喜歡移動(dòng),而且也是那些堅(jiān)持使用技術(shù)構(gòu)建移動(dòng)應(yīng)用程序的人之一。我們準(zhǔn)備做這樣的一個(gè)漸進(jìn)式應(yīng)用是典型的旨在提高用戶離線體驗(yàn)的應(yīng)用。當(dāng)我們開始構(gòu)建應(yīng)用時(shí),你就能理解上面的場(chǎng)景了。的作用范圍是針對(duì)相對(duì)路徑的。最佳的做法是在應(yīng)用的入口。 我喜歡移動(dòng)app,而且也是那些堅(jiān)持使用Web技術(shù)構(gòu)建移動(dòng)應(yīng)用程序的人之一。 經(jīng)過技術(shù)的不斷迭代(可能還有一些其它的東西),移動(dòng)體驗(yàn)設(shè)計(jì)愈來愈平易近人,給予用戶...

    Sanchi 評(píng)論0 收藏0
  • 用vue從零開發(fā)和部署一款移動(dòng)pwa單頁應(yīng)用

    摘要:另外,單頁應(yīng)用因?yàn)閿?shù)據(jù)前置到了前端,不利于搜索引擎的抓取。所以我們需要對(duì)自己的單頁應(yīng)用進(jìn)行一些優(yōu)化。 前言 最近秋招之余空出時(shí)間來按自己的興趣動(dòng)手做了一個(gè)項(xiàng)目,一個(gè)基于vue-cli3.0, vue,typescript的移動(dòng)端pwa,現(xiàn)在趁熱打鐵,將這個(gè)項(xiàng)目從開發(fā)到部署整個(gè)過程記錄下來,并將從這個(gè)項(xiàng)目中學(xué)習(xí)到的東西分享出來,如果大家有什么意見或補(bǔ)充也可以在評(píng)論區(qū)提出。先介紹一下這個(gè)項(xiàng)...

    Channe 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<