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

基于微前端qiankun的多頁簽緩存方案實(shí)踐

開發(fā)
本文梳理了基于阿里開源微前端框架qiankun,實(shí)現(xiàn)多頁簽及子應(yīng)用緩存的方案,同時(shí)還類比了多個(gè)不同方案之間的區(qū)別及優(yōu)劣勢,為使用微前端進(jìn)行多頁簽開發(fā)的同學(xué),提供一些參考。

作者|vivo 互聯(lián)網(wǎng)前端團(tuán)隊(duì)- Tang Xiao

本文梳理了基于阿里開源微前端框架qiankun,實(shí)現(xiàn)多頁簽及子應(yīng)用緩存的方案,同時(shí)還類比了多個(gè)不同方案之間的區(qū)別及優(yōu)劣勢,為使用微前端進(jìn)行多頁簽開發(fā)的同學(xué),提供一些參考。

一、多頁簽是什么?

我們常見的瀏覽器多頁簽、編輯器多頁簽,從產(chǎn)品角度來說,就是為了能夠?qū)崿F(xiàn)用戶訪問可記錄,快速定位工作區(qū)等作用;那對于單頁應(yīng)用,可以通過實(shí)現(xiàn)多頁簽,對用戶的訪問記錄進(jìn)行緩存,從而提供更好的用戶體驗(yàn)。

圖片

前端可以通過多種方式實(shí)現(xiàn)多頁簽,常見的方案有兩種:

  • 通過CSS樣式display:none來控制頁面的顯示隱藏模塊的內(nèi)容;
  • 將模塊序列化緩存,通過緩存的內(nèi)容進(jìn)行渲染(與vue的keep-alive原理類似,在單頁面應(yīng)用中應(yīng)用廣泛)。

相對于第一種方式,第二種方式將DOM格式存儲(chǔ)在序列化的JS對象當(dāng)中,只渲染需要展示的DOM元素,減少了DOM節(jié)點(diǎn)數(shù),提升了渲染的性能,是當(dāng)前主流的實(shí)現(xiàn)多頁簽的方式。

那么相對于傳統(tǒng)的單頁面應(yīng)用,通過微前端qiankun進(jìn)行改造后的前端應(yīng)用,在多頁簽上實(shí)現(xiàn)會(huì)有什么不同呢?

1.1 單頁面應(yīng)用實(shí)現(xiàn)多頁簽

改造前的單頁面應(yīng)用技術(shù)棧是Vue全家桶(vue2.6.10 + element2.15.1 + webpack4.0.0+vue-cli4.2.0)。

vue框架提供了keep-alive來支持緩存相關(guān)的需求,使用keep-alive即可實(shí)現(xiàn)多頁簽的基本功能,但是為了支持更多的功能,我們在其基礎(chǔ)上重新封裝了vue-keep-alive組件。

相對較于keep-alive通過include、exclude對緩存進(jìn)行控制,vue-keep-alive使用更原生的發(fā)布訂閱方式來刪除緩存,可以實(shí)現(xiàn)更完整的多頁簽功能,例如同個(gè)路由可以根據(jù)參數(shù)的不同派生出多個(gè)路由實(shí)例(如打開多個(gè)詳情頁頁簽)以及動(dòng)態(tài)刪除緩存實(shí)例等功能。

下面是vue-keep-alive自定義的拓展實(shí)現(xiàn):

created() {
// 動(dòng)態(tài)刪除緩存實(shí)例監(jiān)聽
this.cache = Object.create(null);
breadCompBus.$on('removeTabByKey', this.removeCacheByKey);
breadCompBus.$on('removeTabByKeys', (data) => {
data.forEach((item) => {
this.removeCacheByKey(item);
});
});
}

vue-keep-alive組件即可傳入自定義方法,用于自定義vnode.key,支持同一匹配路由中派生多個(gè)實(shí)例。

// 傳入`vue-keep-alive`的自定義方法
function updateComponentsKey(key, name, vnode) {
const match = this.$route.matched[1];


if (match && match.meta.multiNodeKey) {
vnode.key = match.meta.multiNodeKey(key, this.$route);
return vnode.key;
}


return key;
}

1.2 使用qiankun進(jìn)行微前端改造后,多頁簽緩存有什么不同

qiankun是由螞蟻金服推出的基于Single-Spa實(shí)現(xiàn)的前端微服務(wù)框架,本質(zhì)上還是路由分發(fā)式的服務(wù)框架,不同于原本 Single-Spa采用JS Entry用的方案,qiankun采用HTML Entry 方式進(jìn)行了替代優(yōu)化。

使用qiankun進(jìn)行微前端改造后,頁面被拆分為一個(gè)基座應(yīng)用和多個(gè)子應(yīng)用,每個(gè)子應(yīng)用都運(yùn)行在獨(dú)立的沙箱環(huán)境中。

圖片

相對于單頁面應(yīng)用中通過keep-alive管控組件實(shí)例的方式,拆分后的各個(gè)子應(yīng)用的keep-alive并不能管控到其他子應(yīng)用的實(shí)例,我們需要緩存對所有的應(yīng)用生效,那么只能將緩存放到基座應(yīng)用中。

這個(gè)就存在幾個(gè)問題:

  1. 加載:主應(yīng)用需要在什么時(shí)候,用什么方式來加載子應(yīng)用實(shí)例?
  2. 渲染:通過緩存實(shí)例來渲染子應(yīng)用時(shí),是通過DOM顯隱方式渲染子應(yīng)用還是有其他方式?
  3. 通信:關(guān)閉頁簽時(shí),如何判斷是否完全卸載子應(yīng)用,主應(yīng)用應(yīng)該使用什么通信方式告訴子應(yīng)用?

二、方案選擇

通過在Github issues及掘金等平臺(tái)的一系列資料查找和對比后,關(guān)于如何在qiankun框架下實(shí)現(xiàn)多頁簽,在不修改qiankun源碼的前提下,主要有兩種實(shí)現(xiàn)的思路。

2.1 方案一:多個(gè)子應(yīng)用同時(shí)存在

實(shí)現(xiàn)思路:

  • 在dom上通過v-show控制顯示哪一個(gè)子應(yīng)用,及display:none;控制不同子應(yīng)用dom的顯示隱藏。
  • url變化時(shí),通過loadMicroApp手動(dòng)控制加載哪個(gè)子應(yīng)用,在頁簽關(guān)閉時(shí),手動(dòng)調(diào)用unmount方法卸載子應(yīng)用。

示例:

<template>
<div id="app">
<header>
<router-link to="/app-vue-hash/">app-vue-hash</router-link>
<router-link to="/app-vue-history/">app-vue-history</router-link>
<router-link to="/about">about</router-link>
</header>
<div id="appContainer1" v-show="$route.path.startsWith('/app-vue-hash/')"></div>
<div id="appContainer2" v-show="$route.path.startsWith('/app-vue-history/')"></div>
<router-view></router-view>
</div>
</template>


<script>
import { loadMicroApp } from 'qiankun';


const apps = [
{
name: 'app-vue-hash',
entry: 'http://localhost:1111',
container: '#appContainer1',
props: { data : { store, router } }
},
{
name: 'app-vue-history',
entry: 'http://localhost:2222',
container: '#appContainer2',
props: { data : store }
}
]


export default {
mounted() {
// 優(yōu)先加載當(dāng)前的子項(xiàng)目
const path = this.$route.path;
const currentAppIndex = apps.findIndex(item => path.includes(item.name));
if(currentAppIndex !== -1){
const currApp = apps.splice(currentAppIndex, 1)[0];
apps.unshift(currApp);
}
// loadMicroApp 返回值是 app 的生命周期函數(shù)數(shù)組
const loadApps = apps.map(item => loadMicroApp(item))
// 當(dāng) tab 頁關(guān)閉時(shí),調(diào)用 loadApps 中 app 的 unmount 函數(shù)即可
},
}
</script>

具體的DOM展示(通過display:none;控制不同子應(yīng)用DOM的顯隱):

圖片

方案優(yōu)勢:

  1. loadMicroApp是qiankun提供的API,可以方便快速接入;
  2. 該方式不卸載子應(yīng)用,頁簽切換速度比較快。

方案不足:

  1. 子應(yīng)用切換時(shí)不銷毀DOM,會(huì)導(dǎo)致DOM節(jié)點(diǎn)和事件監(jiān)聽過多,嚴(yán)重時(shí)會(huì)造成頁面卡頓;
  2. 子應(yīng)用切換時(shí)未卸載,路由事件監(jiān)聽也未卸載,需要對路由變化的監(jiān)聽做特殊的處理。

2.2  方案二:同一時(shí)間僅加載一個(gè)子應(yīng)用,同時(shí)保存其他應(yīng)用的狀態(tài)

實(shí)現(xiàn)思路:

  • 通過registerMicroApps注冊子應(yīng)用,qiankun會(huì)通過自動(dòng)加載匹配的子應(yīng)用;
  • 參考keep-alive實(shí)現(xiàn)方式,每個(gè)子應(yīng)用都緩存自己實(shí)例的vnode,下次進(jìn)入子應(yīng)用時(shí)可以直接使用緩存的vnode直接渲染為真實(shí)DOM。

方案優(yōu)勢:

  • 同一時(shí)間,只是展示一個(gè)子應(yīng)用的active頁面,可減少DOM節(jié)點(diǎn)數(shù);
  • 非active子應(yīng)用卸載時(shí)同時(shí)會(huì)卸載DOM及不需要的事件監(jiān)聽,可釋放一定內(nèi)存。

方案不足:

  • 沒有現(xiàn)有的API可以快速實(shí)現(xiàn),需要自己管理子應(yīng)用緩存,實(shí)現(xiàn)較為復(fù)雜;
  • DOM渲染多了一個(gè)從虛擬DOM轉(zhuǎn)化為真實(shí)DOM的一個(gè)過程,渲染時(shí)間會(huì)比第一種方案稍多。

vue組件實(shí)例化過程簡介

這里簡單的回顧下vue的幾個(gè)關(guān)鍵的渲染節(jié)點(diǎn):

圖片

vue關(guān)鍵渲染節(jié)點(diǎn)(來源:掘金社區(qū))

  • compile:對template進(jìn)行編譯,將AST轉(zhuǎn)化后生成render function;
  • render:生成VNODE虛擬DOM;
  • patch :將虛擬DOM轉(zhuǎn)換為真實(shí)DOM;

因此,方案二相對于方案一,就是多了最后patch的過程。

2.3 最終選擇

根據(jù)兩種方案優(yōu)勢與不足的評估,同時(shí)根據(jù)我們項(xiàng)目的具體情況,最終選擇了方案二進(jìn)行實(shí)現(xiàn),具體原因如下:

  • 過多的DOM及事件監(jiān)聽,會(huì)造成不必要的內(nèi)存浪費(fèi),同時(shí)我們的項(xiàng)目主要以編輯器展示和數(shù)據(jù)展示為主,單個(gè)頁簽內(nèi)內(nèi)容較多,會(huì)更傾向于關(guān)注內(nèi)存使用情況;
  • 方案二在子應(yīng)用二次渲染時(shí)多了一個(gè)patch過程,渲染速度不會(huì)慢多少,在可接受范圍內(nèi)。

三、具體實(shí)現(xiàn)

在上面一部分我們簡單的描述了方案二的一個(gè)實(shí)現(xiàn)思路,其核心思想就是是通過緩存子應(yīng)用實(shí)例的vnode,那么這一部分,就來看下它的一個(gè)具體的實(shí)現(xiàn)的過程。

3.1 從組件級別的緩存到應(yīng)用級別的緩存

在vue中,keep-alive組件通過緩存vnode的方式,實(shí)現(xiàn)了組件級別的緩存,對于通過vue框架實(shí)現(xiàn)的子應(yīng)用來說,它其實(shí)也是一個(gè)vue實(shí)例,那么我們同樣也可以做到通過緩存vnode的方式,實(shí)現(xiàn)應(yīng)用級別的緩存。

通過分析keep-alive源碼,我們了解到keep-alive是通過在render中進(jìn)行緩存命中,返回對應(yīng)組件的vnode,并在mounted和upda

// keep-alive核心代碼
render () {
const slot = this.$slots.default
const vnode: VNode = getFirstComponentChild(slot)
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// 更多代碼...
// 緩存命中
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance
// make current key freshest
remove(keys, key)
keys.push(key)
} else {
// delay setting the cache until update
this.vnodeToCache = vnode
this.keyToCache = key
}
// 設(shè)置keep-alive,防止再次觸發(fā)created等生命周期
vnode.data.keepAlive = true
}
return vnode || (slot && slot[0])
}
// mounted和updated時(shí)緩存當(dāng)前組件的vnode
mounted() {
this.cacheVNode()
}
updated() {
this.cacheVNode()
}

相對于keep-alive需要在mounted和updated兩個(gè)生命周期中對vnode緩存進(jìn)行更新,在應(yīng)用級的緩存中,我們只需要在子應(yīng)用卸載時(shí),主動(dòng)對整個(gè)實(shí)例的vnode進(jìn)行緩存即可。

// 父應(yīng)用提供unmountCache方法
function unmountCache() {
// 此處永遠(yuǎn)只會(huì)保存首次加載生成的實(shí)例
const needCached = this.instance?.cachedInstance || this.instance;
const cachedInstance = {};
cachedInstance._vnode = needCached._vnode;
// keepalive設(shè)置為必須 防止進(jìn)入時(shí)再次created,同keep-alive實(shí)現(xiàn)
if (!cachedInstance._vnode.data.keepAlive) cachedInstance._vnode.data.keepAlive = true;
// 省略其他代碼...


// loadedApplicationMap用于是key-value形式,用于保存當(dāng)前應(yīng)用的實(shí)例
loadedApplicationMap[this.cacheKey] = cachedInstance;
// 省略其他代碼...


// 卸載實(shí)例
this.instance.$destroy();
// 設(shè)置為null后可進(jìn)行垃圾回收
this.instance = null;
}


// 子應(yīng)用在qiankun框架提供的卸載方法中,調(diào)用unmountCache
export async function unmount() {
console.log('[vue] system app unmount');
mainService.unmountCache();
}

3.2 移花接木——將vnode重新掛載到一個(gè)新實(shí)例上

將vnode緩存到內(nèi)存中后,再將原有的instance卸載,重新進(jìn)入子應(yīng)用時(shí),就可以使用緩存的vnode進(jìn)行render渲染。

// 創(chuàng)建子應(yīng)用實(shí)例,有緩存的vnode則使用緩存的vnode
function newVueInstance(cachedNode) {
const config = {
router: this.router,
store: this.store,
render: cachedNode ? () => cachedNode : instance.render, // 優(yōu)先使用緩存vnode
});
return new Vue(config);
}


// 實(shí)例化子應(yīng)用實(shí)例,根據(jù)是否有緩存vnode確定是否傳入cachedNode
this.instance = newVueInstance(cachedNode);
this.instance.$mount('#app');

那么,這里不禁就會(huì)有些疑問:

  1. 如果我們每次進(jìn)入子應(yīng)用時(shí),都重新創(chuàng)建一個(gè)實(shí)例,那么為什么還要卸載,直接不卸載就可以了嗎?
  2. 將緩存vnode使用到一個(gè)新的實(shí)例上,不會(huì)有什么問題嗎?

首先我們回答一下第一個(gè)問題,為什么在切換子應(yīng)用時(shí),要卸載掉原來的子應(yīng)用實(shí)例,有兩個(gè)考慮方面:

  • 其一,是對內(nèi)存的考量,我們需要的其實(shí)僅僅是vnode,而不是整個(gè)實(shí)例,緩存整個(gè)實(shí)例是方案一的實(shí)現(xiàn)方案,所以,我們僅需要緩存我們需要的對象即可;
  • 其二,卸載子應(yīng)用實(shí)例可以移除不必要的事件監(jiān)聽,比如vue-router對popstate事件就進(jìn)行了監(jiān)聽,我們在其他子應(yīng)用操作時(shí),并不希望原來的子應(yīng)用也對這些事件進(jìn)行響應(yīng),那么在子應(yīng)用卸載時(shí),就可以移除掉這些監(jiān)聽。

對于第二個(gè)問題,情況會(huì)更加復(fù)雜一點(diǎn),下面一個(gè)部分,就主要來看下主要遇到了哪些問題,又該如何去解決。

3.3 解決應(yīng)用級緩存方案的問題

3.3.1 vue-router相關(guān)問題
  • 在實(shí)例卸載后對路由變化監(jiān)聽失效;
  • 新的vue-router對原有的router params等參數(shù)記錄失效。

首先我們需要明確這兩個(gè)問題的原因:

  • 第一個(gè)是因?yàn)樵谧討?yīng)用卸載時(shí)移除了對popstate事件的監(jiān)聽,那么我們需要做的就是重新注冊對popstate事件的監(jiān)聽,這里可以通過重新實(shí)例化一個(gè)vue-router解決;
  • 第二問題是因?yàn)橥ㄟ^重新實(shí)例化vue-router解決第一個(gè)問題之后,實(shí)際上是一個(gè)新的vue-router,我們需要做的就是不僅要緩存vnode,還需要緩存router相關(guān)的信息。

大致的解決實(shí)現(xiàn)如下:

// 實(shí)例化子應(yīng)用vue-router
function initRouter() {
const { router: originRouter } = this.baseConfig;
const config = Object.assign(originRouter, {
base: `app-kafka/`,
});
Vue.use(VueRouter);
this.router = new VueRouter(config);
}


// 創(chuàng)建子應(yīng)用實(shí)例,有緩存的vnode則使用緩存的vnode
function newVueInstance(cachedNode) {
const config = {
router: this.router, // 在vue init過程中,會(huì)重新調(diào)用vue-router的init方法,重新啟動(dòng)對popstate事件監(jiān)聽
store: this.store,
render: cachedNode ? () => cachedNode : instance.render, // 優(yōu)先使用緩存vnode
});
return new Vue(config);
}


function render() {
if(isCache) {
// 場景一、重新進(jìn)入應(yīng)用(有緩存)
const cachedInstance = loadedApplicationMap[this.cacheKey];


// router使用緩存命中
this.router = cachedInstance.$router;
// 讓當(dāng)前路由在最初的Vue實(shí)例上可用
this.router.apps = cachedInstance.catchRoute.apps;
// 使用緩存vnode重新實(shí)例化子應(yīng)用
const cachedNode = cachedInstance._vnode;
this.instance = this.newVueInstance(cachedNode);
} else {
// 場景二、首次加載子應(yīng)用/重新進(jìn)入應(yīng)用(無緩存)
this.initRouter();
// 正常實(shí)例化
this.instance = this.newVueInstance();
}
}


function unmountCache() {
// 省略其他代碼...
cachedInstance.$router = this.instance.$router;
cachedInstance.$router.app = null;
// 省略其他代碼...
}

3.3.2父子組件通信

多頁簽的方式增加了父子組件通信的頻率,qiankun有提供setGlobalState通信方式,但是在單應(yīng)用模式下,同一時(shí)間僅支持和一個(gè)子應(yīng)用進(jìn)行通行,對于unmount 的子應(yīng)用來說,無法接收到父應(yīng)用的通信,因此,對于不同的場景,我們需要更加靈活的通信方式。

子應(yīng)用——父應(yīng)用:使用qiankun自帶通信方式;

從子到父的通信場景較為簡單,一般只有路由變化時(shí)進(jìn)行上報(bào),并且僅為激活狀態(tài)的子應(yīng)用才會(huì)上報(bào),可直接使用qiankun自帶通信方式;

父應(yīng)用——子應(yīng)用:使用自定義事件通信;

父應(yīng)用到子應(yīng)用,不僅需要和active狀態(tài)的子應(yīng)用通信,還需要和當(dāng)前處于緩存中子應(yīng)用通信;

因此,父應(yīng)用到子應(yīng)用,通過自定義事件的方式,能夠?qū)崿F(xiàn)父應(yīng)用和多個(gè)子應(yīng)用的通信。

// 自定義事件發(fā)布
const evt = new CustomEvent('microServiceEvent', {
detail: {
action: { name: action, data },
basePath, // 用于子應(yīng)用唯一標(biāo)識
},
});
document.dispatchEvent(evt);


// 自定義事件監(jiān)聽
document.addEventListener('microServiceEvent', this.listener);

3.3.3 緩存管理,防止內(nèi)存泄露

使用緩存最重要的事項(xiàng)就是對緩存的管理,在不需要的時(shí)候及時(shí)清理,這在JS中是非常重要但很容易被忽略的事項(xiàng)。

應(yīng)用級緩存

  • 子應(yīng)用vnode、router等屬性,子應(yīng)用切換時(shí)緩存;

頁面級緩存

  • 通過vue-keep-alive緩存組件的vnode;
  • 刪除頁簽時(shí),監(jiān)聽remove事件,刪除頁面對應(yīng)的vnode;
  • vue-keep-alive組件中所有緩存均被刪除時(shí),通知?jiǎng)h除整個(gè)子應(yīng)用緩存;

3.4 整體框架

最后,我們從整體的視角來了解下多頁簽緩存的實(shí)現(xiàn)方案。

因?yàn)椴粌H僅需要對子應(yīng)用的緩存進(jìn)行管理,還需要將vue-keep-alive組件注冊到各個(gè)子應(yīng)用中等事項(xiàng),我們將這些服務(wù)統(tǒng)一在主應(yīng)用的mainService中進(jìn)行管理,在registerMicroApps注冊子應(yīng)用時(shí)通過props傳入子應(yīng)用,這樣就能夠?qū)崿F(xiàn)同一套代碼,多處復(fù)用。

圖片

// 子應(yīng)用main.js
let mainService = null;


export async function mount(props) {
mainService = null;
const { MainService } = props;
// 注冊主應(yīng)用服務(wù)
mainService = new MainService({
// 傳入對應(yīng)參數(shù)
});
// 實(shí)例化vue并渲染
mainService.render(props);
}
export async function unmount() {
mainService.unmountCache();
}

最后對關(guān)鍵流程進(jìn)行梳理:

圖片

四、現(xiàn)有問題

4.1 暫時(shí)只支持vue框架的實(shí)例緩存

該方案也是基于vue現(xiàn)有特性支持實(shí)現(xiàn)的,在react社區(qū)中對于多頁簽實(shí)現(xiàn)并沒有統(tǒng)一的實(shí)現(xiàn)方案,筆者也沒有過多的探索,考慮到現(xiàn)有項(xiàng)目是以vue技術(shù)棧為主,后期升級也會(huì)只升級到vue3.0,在一段時(shí)間內(nèi)是可以完全支持的。

五、總結(jié)

相較于社區(qū)上大部分通過方案一進(jìn)行實(shí)現(xiàn),本文提供了另一種實(shí)現(xiàn)多頁簽緩存的一種思路,主要是對子應(yīng)用緩存處理上有些許的不同,大致的思路及通信的方式都是互通的。

另外本文對qiankun框架的使用沒有做太多的發(fā)散總結(jié),官網(wǎng)和Github上已經(jīng)有很多相關(guān)問題的總結(jié)和踩坑經(jīng)驗(yàn)可供參考。

責(zé)任編輯:未麗燕 來源: vivo互聯(lián)網(wǎng)技術(shù)
相關(guān)推薦

2020-05-06 09:25:10

微前端qiankun架構(gòu)

2022-02-13 23:00:48

前端微前端qiankun

2022-10-09 14:05:24

前端single-spa

2022-01-17 11:41:50

前端Vite組件

2020-11-09 11:10:56

前端api緩存

2017-09-01 18:27:36

前端 RxJs數(shù)據(jù)層

2023-03-10 10:29:19

前端邏輯拆分

2022-09-07 21:31:19

微前端架構(gòu)iframe

2023-11-03 08:04:47

Web微前端框架

2024-04-26 09:33:18

攜程實(shí)踐

2022-01-24 12:38:58

Vite插件開發(fā)

2022-05-26 21:33:09

業(yè)務(wù)前端測試

2022-08-27 21:37:57

PrometheusRedis?監(jiān)控

2024-05-16 08:18:55

TabsArkUI增刪頁簽功能

2022-08-10 10:32:47

編程實(shí)踐

2022-05-26 10:12:21

前端優(yōu)化測試

2023-06-12 21:32:56

卡口服務(wù)系統(tǒng)

2022-06-27 09:36:29

攜程度假GraphQL多端開發(fā)

2020-06-02 09:45:07

微前端組件代碼

2021-09-09 09:43:38

MaxComputePAI 阿里云
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

国产一精品一aⅴ一免费| 国产精品久久成人免费观看| 可以免费在线观看的av| 免费电影一区二区三区| 欧美日韩一本到| 国产香蕉一区二区三区| 色婷婷av一区二区三区之e本道| 久久综合九色| 久久艹在线视频| 黄色a一级视频| 欧洲亚洲精品久久久久| 午夜天堂影视香蕉久久| 亚洲精品一区二区三区蜜桃久| www.桃色av嫩草.com| 久久久久久一区二区| 欧美成人中文字幕| 五月天精品视频| 天堂va在线高清一区| 色婷婷综合激情| www.亚洲视频.com| 日本中文字幕在线视频| 99久久免费精品| 91网在线免费观看| 无码人妻熟妇av又粗又大| 欧美久久综合| 久久精品久久久久电影| 亚洲AV无码国产成人久久| 香港久久久电影| 欧美日韩黄色一区二区| 波多野结衣乳巨码无在线| aaa大片在线观看| 国产网站一区二区三区| 国模一区二区三区私拍视频| 国产免费一区二区三区最新不卡 | av无码久久久久久不卡网站| 秋霞影院午夜丰满少妇在线视频| 91丨九色丨国产丨porny| **亚洲第一综合导航网站| 最近中文字幕在线视频| 免费国产自线拍一欧美视频| 久久久噜噜噜久久| 全网免费在线播放视频入口 | 性欧美video另类hd尤物| 色噜噜狠狠一区二区三区果冻| 国产美女在线一区| 欧美aaa免费| 一区二区成人在线观看| 国产一级大片免费看| av黄在线观看| 一区二区在线免费| 男人天堂成人网| dy888亚洲精品一区二区三区| 中文字幕一区二区三区在线观看| 婷婷久久五月天| 成人免费高清在线播放| 国产喂奶挤奶一区二区三区| 日韩一区二区三区资源| 国产一二三区在线| 国产日韩欧美在线一区| 日韩一本精品| 欧美极品视频| 亚洲免费av高清| 欧美做受777cos| 欧洲中文在线| 午夜精品成人在线| 青青视频在线播放| 韩国精品主播一区二区在线观看| 色呦呦一区二区三区| 欧美 日韩 国产 激情| 日韩在线观看不卡| 欧美精品日韩精品| 日本wwwxx| 国产精品毛片视频| 亚洲精品永久免费精品| 少妇一级黄色片| 亚洲成人国产| 性色av一区二区三区免费| 亚洲不卡视频在线观看| 天堂一区二区在线| 成人黄色免费在线观看| 亚洲av少妇一区二区在线观看| 国产精品一二三在| 狠狠色噜噜狠狠狠狠色吗综合| 天堂av在线资源| 国产精品乱人伦中文| 老汉色影院首页| 国产精品yjizz视频网| 在线日韩av片| 中文字幕在线播放一区二区| 日韩高清成人在线| 中文字幕久热精品视频在线| 日本一级片免费| 夜夜嗨av一区二区三区网站四季av| 日韩av大片在线| jizz中国女人| 97久久超碰国产精品| 亚洲午夜精品久久久中文影院av | 亚洲欧美国产77777| 青青草精品视频在线| 免费成人高清在线视频| 精品国产免费视频| 亚洲国产av一区| 欧美一区二区三区免费看| 欧美一区二区三区免费视| 一区二区久久精品66国产精品| 成人av网址在线| 亚洲图片都市激情| 大胆人体一区| 精品免费视频.| 青青草自拍偷拍| 国产一区91| 91牛牛免费视频| 久草在现在线| 婷婷国产v国产偷v亚洲高清| 欧美激情国内自拍| 欧美色图激情小说| 久久久免费观看视频| 亚洲中文一区二区三区| 91亚洲大成网污www| 91免费国产精品| 日韩深夜福利网站| 亚洲人成伊人成综合网久久久| 免费一级黄色大片| 激情综合亚洲精品| 亚洲成人av动漫| 伊人色综合一区二区三区影院视频 | 日韩av片在线免费观看| 久久久久观看| 欧美日韩国产123| 亚洲综合精品国产一区二区三区| 91免费版在线| 少妇av一区二区三区无码| 国产日韩欧美中文在线| 日日骚久久av| 真实新婚偷拍xxxxx| 91视频免费播放| 97国产精东麻豆人妻电影| aaa国产精品视频| 欧美成人免费在线观看| 亚洲图片中文字幕| 国产精品无人区| 黄色手机在线视频| 欧美三级伦理在线| 国产精品欧美日韩| 成人资源www网在线最新版| 欧美视频在线免费看| av网站免费在线播放| 国产视频一区三区| 欧美成人第一区| 亚洲人成午夜免电影费观看| 亚洲精品第一页| 国产成人精品一区二三区| 95精品视频在线| 久久黄色片视频| 亚洲春色h网| 国产精品第10页| 高清在线观看av| 欧美日韩在线播放一区| 亚洲色图27p| 国产一区二三区好的| 国产小视频免费| 成人午夜三级| 91tv亚洲精品香蕉国产一区7ujn| 亚洲 精品 综合 精品 自拍| 色屁屁一区二区| 91成人精品一区二区| 麻豆国产一区二区| 大地资源网在线观看免费官网| youjizz亚洲| 欧美在线中文字幕| av中文天堂在线| 91精品视频网| 日本熟妇毛茸茸丰满| 久久综合99re88久久爱| 五月婷婷狠狠操| 亚洲一区在线| 久久精品成人一区二区三区蜜臀| 黑人巨大亚洲一区二区久| 中文字幕日韩欧美在线视频| 国产欧美日韩综合精品一区二区三区| 亚洲一区二区三区四区五区黄 | 亚洲日本三级| 国产精品丝袜视频| 大桥未久在线播放| 亚洲小视频在线观看| 国产精品久久久久久久免费看| 一区二区三区四区在线播放| 国产艳俗歌舞表演hd| 麻豆成人91精品二区三区| 欧美一区二区激情| 日本a口亚洲| 国产91精品一区二区绿帽| 电影天堂国产精品| 欧美日韩国产va另类| 欧美在线一卡| 日韩欧美中文字幕精品| 丁香六月婷婷综合| 亚洲欧美乱综合| 中文字幕人妻一区二区| 国产老肥熟一区二区三区| 男人靠女人免费视频网站| 欧美第一精品| 免费在线国产精品| 午夜久久av| 国产一区二区在线免费| 天堂av中文在线观看| 久久中文精品视频| 国产精品一区二区三区四区色| 日韩视频中午一区| 怡春院在线视频| 黑人巨大精品欧美一区二区三区| 99热这里只有精品4| 26uuu精品一区二区在线观看| av在线免费观看不卡| 视频一区视频二区中文| xxxx18hd亚洲hd捆绑| 中国成人一区| 亚洲国产一区二区三区在线 | 国产精品综合网| 日韩福利视频在线| 亚洲毛片网站| www.亚洲成人网| 亚洲一区 二区 三区| 一区二区三视频| 欧美中文字幕一区二区| 欧美人与性禽动交精品| 精品亚洲自拍| 国产免费高清一区| 91欧美极品| 国产高清自拍一区| 日本一区影院| 999国内精品视频在线| 国产一区二区高清在线| 成人国产精品一区二区| 色综合视频一区二区三区日韩| 日本老师69xxx| 涩涩av在线| 2021国产精品视频| 欧美激情网站| 2018中文字幕一区二区三区| gogo高清午夜人体在线| 欧美激情区在线播放| 在线看女人毛片| 欧美激情欧美狂野欧美精品| 日本aa在线| 欧美激情乱人伦一区| 青青草原国产在线| 久久乐国产精品| 超碰高清在线| 欧美亚洲另类制服自拍| 中文一区一区三区高中清不卡免费| 97在线视频国产| xxxxxx欧美| 国产精品av在线| 国产精品黄色片| 91在线色戒在线| 影音先锋欧美激情| 精品国产一区二区三区久久久久久| 久久久伦理片| 日韩欧美一区二区三区四区五区| 欧美亚洲高清| 国产免费一区二区三区四在线播放| 久久久久久久久99精品大| 亚洲小视频在线播放| 国一区二区在线观看| 每日在线更新av| 秋霞午夜鲁丝一区二区老狼| 羞羞的视频在线| 国产伦精品一区二区三区视频青涩| 国产精品偷伦视频免费观看了| www.日韩av| 国产又粗又猛又爽又黄av| 中文字幕在线观看一区| 毛片a片免费观看| 福利视频一区二区| 中文在线字幕av| 日韩免费性生活视频播放| 无码精品人妻一区二区| 国产午夜精品全部视频在线播放| 黄色动漫在线| 26uuu久久噜噜噜噜| 国产精品原创视频| 91情侣偷在线精品国产| 国产精品主播在线观看| 天堂社区 天堂综合网 天堂资源最新版 | 一区二区三区的久久的视频| 亚洲午夜精品久久久久久app| 国内外成人激情视频| 精品在线观看视频| 99re久久精品国产| 中文在线一区二区| 日韩激情一区二区三区| 精品视频在线免费看| 欧美一级淫片免费视频魅影视频| 一区二区在线视频播放| 蜜臀av在线| 国产伦精品一区二区三区精品视频| 成人动态视频| 亚洲精品成人a8198a| 在线观看亚洲| 亚洲理论中文字幕| 久久综合网色—综合色88| tube国产麻豆| 欧美无人高清视频在线观看| 深爱激情五月婷婷| 欧美大尺度激情区在线播放| 日韩在线影院| 精品一区久久久久久| 综合天堂久久久久久久| 丰满少妇在线观看| 99精品视频一区| 国产日韩欧美在线观看视频| 91久久精品日日躁夜夜躁欧美| 欧美熟妇另类久久久久久不卡| 色黄久久久久久| 黄色亚洲网站| 久久久亚洲综合网站| 欧美日韩视频| 日本黄色www| 国产精品久久久久久一区二区三区 | 一级黄色片大全| 亚洲午夜免费电影| www.亚洲天堂.com| 久久精品精品电影网| 久久影视精品| 手机在线观看国产精品| 久久久精品五月天| 在线免费观看污视频| 亚洲午夜在线电影| 成 人片 黄 色 大 片| 另类少妇人与禽zozz0性伦| jizz免费一区二区三区| 日韩欧美第二区在线观看| 午夜在线视频观看日韩17c| 亚洲熟女一区二区| 亚洲国产aⅴ成人精品无吗| 亚洲av无码国产综合专区| 久久亚洲春色中文字幕| 91成人在线网站| 男女爱爱视频网站| 国产尤物一区二区| 亚洲国产精品免费在线观看| 欧美欧美欧美欧美首页| 老司机在线永久免费观看| 成人a在线视频| 自产国语精品视频| 中文字幕一二三| 亚洲一区在线观看网站| 男人天堂综合网| 69久久夜色精品国产69| 亚洲综合小说图片| 色七七在线观看| 国产精品久久久久婷婷| 国产精品一区二区三区在线免费观看 | 91精品视频免费| 欧美在线网址| 日本一区二区免费视频| 舔着乳尖日韩一区| 可以在线观看的黄色| 国产综合色香蕉精品| 欧美久色视频| 国产精品无码一区二区三区免费| 日本韩国一区二区| 麻豆视频免费在线观看| 91在线短视频| 亚洲美洲欧洲综合国产一区| 爱爱免费小视频| 欧美日韩国产在线观看| 国内精品不卡| 国产日本一区二区三区| 日韩精品五月天| 可以免费看av的网址| 日韩亚洲欧美一区二区三区| 蜜桃视频m3u8在线观看| 日韩欧美在线观看强乱免费| 国产在线观看一区二区| 国产在线观看成人| 伊人伊成久久人综合网小说| 精品一区二区三区中文字幕 | 性高爱久久久久久久久| 一区二区三区欧美成人| 成人看片黄a免费看在线| 亚洲成人第一网站| 久久久国产精品亚洲一区| av成人综合| 在线观看日本一区二区| 亚洲综合免费观看高清完整版在线| 欧美日韩视频精品二区| 91亚洲精品久久久| 久久国产高清| 黄色一级片中国| 亚洲欧美日韩精品久久亚洲区| 国产精久久一区二区| 久久久久久久久久久视频| 中文字幕日韩精品一区| 亚州精品国产精品乱码不99按摩| 国产欧美在线看| 麻豆成人在线| 久久亚洲AV无码| 日韩中文字幕免费视频|