日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

詳解vue 組件的實(shí)現(xiàn)原理

瀏覽:20日期:2022-10-31 16:26:30

組件機(jī)制的設(shè)計(jì),可以讓開發(fā)者把一個(gè)復(fù)雜的應(yīng)用分割成一個(gè)個(gè)功能獨(dú)立組件,降低開發(fā)的難度的同時(shí),也提供了極好的復(fù)用性和可維護(hù)性。本文我們一起從源碼的角度,了解一下組件的底層實(shí)現(xiàn)原理。

組件注冊時(shí)做了什么?

在Vue中使用組件,要做的第一步就是注冊。Vue提供了全局注冊和局部注冊兩種方式。

全局注冊方式如下:

Vue.component(’my-component-name’, { /* ... */ })

局部注冊方式如下:

var ComponentA = { /* ... */ }new Vue({ el: ’#app’, components: { ’component-a’: ComponentA }})

全局注冊的組件,會(huì)在任何Vue實(shí)例中使用。局部注冊的組件,只能在該組件的注冊地,也就是注冊該組件的Vue實(shí)例中使用,甚至Vue實(shí)例的子組件中也不能使用。

有一定Vue使用經(jīng)驗(yàn)的小伙伴都了解上面的差異,但是為啥會(huì)有這樣的差異呢?我們從組件注冊的代碼實(shí)現(xiàn)上進(jìn)行解釋。

// Vue.component的核心代碼// ASSET_TYPES = [’component’, ’directive’, ’filter’]ASSET_TYPES.forEach(type => { Vue[type] = function (id, definition ){ if (!definition) { return this.options[type + ’s’][id] } else { // 組件注冊 if (type === ’component’ && isPlainObject(definition)) { definition.name = definition.name || id // 如果definition是一個(gè)對象,需要調(diào)用Vue.extend()轉(zhuǎn)換成函數(shù)。Vue.extend會(huì)創(chuàng)建一個(gè)Vue的子類(組件類),并返回子類的構(gòu)造函數(shù)。 definition = this.options._base.extend(definition) }// ...省略其他代碼 // 這里很關(guān)鍵,將組件添加到構(gòu)造函數(shù)的選項(xiàng)對象中Vue.options上。 this.options[type + ’s’][id] = definition return definition } } })

// Vue的構(gòu)造函數(shù)function Vue(options){ if (process.env.NODE_ENV !== ’production’ && !(this instanceof Vue) ) { warn(’Vue is a constructor and should be called with the `new` keyword’) } this._init(options) }// Vue的初始化中進(jìn)行選項(xiàng)對象的合并Vue.prototype._init = function (options) { const vm = this vm._uid = uid++ vm._isVue = true // ...省略其他代碼 if (options && options._isComponent) { initInternalComponent(vm, options) } else { // 合并vue選項(xiàng)對象,合并構(gòu)造函數(shù)的選項(xiàng)對象和實(shí)例中的選項(xiàng)對象 vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } // ...省略其他代碼 }

以上摘取了組件注冊的主要代碼。可以看到Vue實(shí)例的選項(xiàng)對象由Vue的構(gòu)造函數(shù)選項(xiàng)對象和Vue實(shí)例的選項(xiàng)對象兩部分組成。

全局注冊的組件,實(shí)際上通過Vue.component添加到了Vue構(gòu)造函數(shù)的選項(xiàng)對象 Vue.options.components 上了。

Vue 在實(shí)例化時(shí)(new Vue(options))所指定的選項(xiàng)對象會(huì)與構(gòu)造函數(shù)的選項(xiàng)對象合并作為Vue實(shí)例最終的選項(xiàng)對象。因此,全局注冊的組件在所有的Vue實(shí)例中都可以使用,而在Vue實(shí)例中局部注冊的組件只會(huì)影響Vue實(shí)例本身。

為啥在HTML模板中可以正常使用組件標(biāo)簽?

我們知道組件可以跟普通的HTML一樣在模板中直接使用。例如:

<div id='app'> <!--使用組件button-counter--> <button-counter></button-counter></div>

// 全局注冊一個(gè)名為 button-counter 的組件Vue.component(’button-counter’, { data: function () { return { count: 0 } }, template: ’<button v-on:click='count++'>You clicked me {{ count }} times.</button>’})// 創(chuàng)建Vue實(shí)例new Vue({ el: ’#app’})

那么,當(dāng)Vue解析到自定義的組件標(biāo)簽時(shí)是如何處理的呢?

Vue 對組件標(biāo)簽的解析與普通HTML標(biāo)簽的解析一樣,不會(huì)因?yàn)槭欠?HTML標(biāo)準(zhǔn)的標(biāo)簽而特殊處理。處理過程中第一個(gè)不同的地方出現(xiàn)在vnode節(jié)點(diǎn)創(chuàng)建時(shí)。vue 內(nèi)部通過_createElement函數(shù)實(shí)現(xiàn)vnode的創(chuàng)建。

export function _createElement ( context: Component, tag?: string | Class<Component> | Function | Object, data?: VNodeData, children?: any, normalizationType?: number): VNode | Array<VNode> { //...省略其他代碼 let vnode, ns if (typeof tag === ’string’) { let Ctor ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag) // 如果是普通的HTML標(biāo)簽 if (config.isReservedTag(tag)) { vnode = new VNode( config.parsePlatformTagName(tag), data, children, undefined, undefined, context ) } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, ’components’, tag))) { // 如果是組件標(biāo)簽,e.g. my-custom-tag vnode = createComponent(Ctor, data, context, children, tag) } else { vnode = new VNode( tag, data, children, undefined, undefined, context ) } } else { // direct component options / constructor vnode = createComponent(tag, data, context, children) } if (Array.isArray(vnode)) { return vnode } else if (isDef(vnode)) { if (isDef(ns)) applyNS(vnode, ns) if (isDef(data)) registerDeepBindings(data) return vnode } else { return createEmptyVNode() }}

以文中的button-counter組件為例,由于button-counter標(biāo)簽不是合法的HTML標(biāo)簽,不能直接new VNode()創(chuàng)建vnode。Vue 會(huì)通過resolveAsset函數(shù)檢查該標(biāo)簽是否為自定義組件的標(biāo)簽。

export function resolveAsset ( options: Object, type: string, id: string, warnMissing?: boolean): any { /* istanbul ignore if */ if (typeof id !== ’string’) { return } const assets = options[type] // 首先檢查vue實(shí)例本身有無該組件 if (hasOwn(assets, id)) return assets[id] const camelizedId = camelize(id) if (hasOwn(assets, camelizedId)) return assets[camelizedId] const PascalCaseId = capitalize(camelizedId) if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId] // 如果實(shí)例上沒有找到,去查找原型鏈 const res = assets[id] || assets[camelizedId] || assets[PascalCaseId] if (process.env.NODE_ENV !== ’production’ && warnMissing && !res) { warn( ’Failed to resolve ’ + type.slice(0, -1) + ’: ’ + id, options ) } return res}

button-counter是我們?nèi)肿缘慕M件,顯然可以在this.$options.components找到其定義。因此,Vue會(huì)執(zhí)行createComponent函數(shù)來生成組件的vnode。

// createComponentexport function createComponent ( Ctor: Class<Component> | Function | Object | void, data: ?VNodeData, context: Component, children: ?Array<VNode>, tag?: string): VNode | Array<VNode> | void { if (isUndef(Ctor)) { return } // 獲取Vue的構(gòu)造函數(shù) const baseCtor = context.$options._base // 如果Ctor是一個(gè)選項(xiàng)對象,需要使用Vue.extend使用選項(xiàng)對象,創(chuàng)建將組件選項(xiàng)對象轉(zhuǎn)換成一個(gè)Vue的子類 if (isObject(Ctor)) { Ctor = baseCtor.extend(Ctor) } // 如果Ctor還不是一個(gè)構(gòu)造函數(shù)或者異步組件工廠函數(shù),不再往下執(zhí)行。 if (typeof Ctor !== ’function’) { if (process.env.NODE_ENV !== ’production’) { warn(`Invalid Component definition: ${String(Ctor)}`, context) } return } // 異步組件 let asyncFactory if (isUndef(Ctor.cid)) { asyncFactory = Ctor Ctor = resolveAsyncComponent(asyncFactory, baseCtor) if (Ctor === undefined) { // return a placeholder node for async component, which is rendered // as a comment node but preserves all the raw information for the node. // the information will be used for async server-rendering and hydration. return createAsyncPlaceholder( asyncFactory, data, context, children, tag ) } } data = data || {} // 重新解析構(gòu)造函數(shù)的選項(xiàng)對象,在組件構(gòu)造函數(shù)創(chuàng)建后,Vue可能會(huì)使用全局混入造成構(gòu)造函數(shù)選項(xiàng)對象改變。 resolveConstructorOptions(Ctor) // 處理組件的v-model if (isDef(data.model)) { transformModel(Ctor.options, data) } // 提取props const propsData = extractPropsFromVNodeData(data, Ctor, tag) // 函數(shù)式組件 if (isTrue(Ctor.options.functional)) { return createFunctionalComponent(Ctor, propsData, data, context, children) } const listeners = data.on data.on = data.nativeOn if (isTrue(Ctor.options.abstract)) { const slot = data.slot data = {} if (slot) { data.slot = slot } } // 安裝組件hooks installComponentHooks(data) // 創(chuàng)建 vnode const name = Ctor.options.name || tag const vnode = new VNode( `vue-component-${Ctor.cid}${name ? `-${name}` : ’’}`, data, undefined, undefined, undefined, context, { Ctor, propsData, listeners, tag, children }, asyncFactory ) return vnode}

由于Vue允許通過一個(gè)選項(xiàng)對象定義組件,Vue需要使用Vue.extend將組件的選項(xiàng)對象轉(zhuǎn)換成一個(gè)構(gòu)造函數(shù)。

/** * Vue類繼承,以Vue的原型為原型創(chuàng)建Vue組件子類。繼承實(shí)現(xiàn)方式是采用Object.create(),在內(nèi)部實(shí)現(xiàn)中,加入了緩存的機(jī)制,避免重復(fù)創(chuàng)建子類。 */ Vue.extend = function (extendOptions: Object): Function { // extendOptions 是組件的選項(xiàng)對象,與vue所接收的一樣 extendOptions = extendOptions || {} // Super變量保存對父類Vue的引用 const Super = this // SuperId 保存父類的cid const SuperId = Super.cid // 緩存構(gòu)造函數(shù) const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}) if (cachedCtors[SuperId]) { return cachedCtors[SuperId] } // 獲取組件的名字 const name = extendOptions.name || Super.options.name if (process.env.NODE_ENV !== ’production’ && name) { validateComponentName(name) } // 定義組件的構(gòu)造函數(shù) const Sub = function VueComponent (options) { this._init(options) } // 組件的原型對象指向Vue的選項(xiàng)對象 Sub.prototype = Object.create(Super.prototype) Sub.prototype.constructor = Sub // 為組件分配一個(gè)cid Sub.cid = cid++ // 將組件的選項(xiàng)對象與Vue的選項(xiàng)合并 Sub.options = mergeOptions( Super.options, extendOptions ) // 通過super屬性指向父類 Sub[’super’] = Super // 將組件實(shí)例的props和computed屬代理到組件原型對象上,避免每個(gè)實(shí)例創(chuàng)建的時(shí)候重復(fù)調(diào)用Object.defineProperty。 if (Sub.options.props) { initProps(Sub) } if (Sub.options.computed) { initComputed(Sub) } // 復(fù)制父類Vue上的extend/mixin/use等全局方法 Sub.extend = Super.extend Sub.mixin = Super.mixin Sub.use = Super.use // 復(fù)制父類Vue上的component、directive、filter等資源注冊方法 ASSET_TYPES.forEach(function (type) { Sub[type] = Super[type] }) // enable recursive self-lookup if (name) { Sub.options.components[name] = Sub } // 保存父類Vue的選項(xiàng)對象 Sub.superOptions = Super.options // 保存組件的選項(xiàng)對象 Sub.extendOptions = extendOptions // 保存最終的選項(xiàng)對象 Sub.sealedOptions = extend({}, Sub.options) // 緩存組件的構(gòu)造函數(shù) cachedCtors[SuperId] = Sub return Sub }}

還有一處重要的代碼是installComponentHooks(data)。該方法會(huì)給組件vnode的data添加組件鉤子,這些鉤子在組件的不同階段被調(diào)用,例如init鉤子在組件patch時(shí)會(huì)調(diào)用。

function installComponentHooks (data: VNodeData) { const hooks = data.hook || (data.hook = {}) for (let i = 0; i < hooksToMerge.length; i++) { const key = hooksToMerge[i] // 外部定義的鉤子 const existing = hooks[key] // 內(nèi)置的組件vnode鉤子 const toMerge = componentVNodeHooks[key] // 合并鉤子 if (existing !== toMerge && !(existing && existing._merged)) { hooks[key] = existing ? mergeHook(toMerge, existing) : toMerge } }}// 組件vnode的鉤子。const componentVNodeHooks = { // 實(shí)例化組件 init (vnode: VNodeWithData, hydrating: boolean): ?boolean { if ( vnode.componentInstance && !vnode.componentInstance._isDestroyed && vnode.data.keepAlive ) { // kept-alive components, treat as a patch const mountedNode: any = vnode // work around flow componentVNodeHooks.prepatch(mountedNode, mountedNode) } else { // 生成組件實(shí)例 const child = vnode.componentInstance = createComponentInstanceForVnode( vnode, activeInstance ) // 掛載組件,與vue的$mount一樣 child.$mount(hydrating ? vnode.elm : undefined, hydrating) } }, prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) { const options = vnode.componentOptions const child = vnode.componentInstance = oldVnode.componentInstance updateChildComponent( child, options.propsData, // updated props options.listeners, // updated listeners vnode, // new parent vnode options.children // new children ) }, insert (vnode: MountedComponentVNode) { const { context, componentInstance } = vnode if (!componentInstance._isMounted) { componentInstance._isMounted = true // 觸發(fā)組件的mounted鉤子 callHook(componentInstance, ’mounted’) } if (vnode.data.keepAlive) { if (context._isMounted) { queueActivatedComponent(componentInstance) } else { activateChildComponent(componentInstance, true /* direct */) } } }, destroy (vnode: MountedComponentVNode) { const { componentInstance } = vnode if (!componentInstance._isDestroyed) { if (!vnode.data.keepAlive) { componentInstance.$destroy() } else { deactivateChildComponent(componentInstance, true /* direct */) } } }}const hooksToMerge = Object.keys(componentVNodeHooks)

最后,與普通HTML標(biāo)簽一樣,為組件生成vnode節(jié)點(diǎn):

// 創(chuàng)建 vnode const vnode = new VNode( `vue-component-${Ctor.cid}${name ? `-${name}` : ’’}`, data, undefined, undefined, undefined, context, { Ctor, propsData, listeners, tag, children }, asyncFactory )

組件在patch時(shí)對vnode的處理與普通標(biāo)簽有所不同。

Vue 如果發(fā)現(xiàn)正在patch的vnode是組件,那么調(diào)用createComponent方法。

function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) { let i = vnode.data if (isDef(i)) { const isReactivated = isDef(vnode.componentInstance) && i.keepAlive // 執(zhí)行組件鉤子中的init鉤子,創(chuàng)建組件實(shí)例 if (isDef(i = i.hook) && isDef(i = i.init)) { i(vnode, false /* hydrating */) } // init鉤子執(zhí)行后,如果vnode是個(gè)子組件,該組件應(yīng)該創(chuàng)建一個(gè)vue子實(shí)例,并掛載到DOM元素上。子組件的vnode.elm也設(shè)置完成。然后我們只需要返回該DOM元素。 if (isDef(vnode.componentInstance)) { // 設(shè)置vnode.elm initComponent(vnode, insertedVnodeQueue) // 將組件的elm插入到父組件的dom節(jié)點(diǎn)上 insert(parentElm, vnode.elm, refElm) if (isTrue(isReactivated)) { reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm) } return true } } }

createComponent會(huì)調(diào)用組件vnode的data對象上定義的init鉤子方法,創(chuàng)建組件實(shí)例。現(xiàn)在我們回過頭來看下init鉤子的代碼:

// ... 省略其他代碼 init (vnode: VNodeWithData, hydrating: boolean): ?boolean { if ( vnode.componentInstance && !vnode.componentInstance._isDestroyed && vnode.data.keepAlive ) { // kept-alive components, treat as a patch const mountedNode: any = vnode // work around flow componentVNodeHooks.prepatch(mountedNode, mountedNode) } else { // 生成組件實(shí)例 const child = vnode.componentInstance = createComponentInstanceForVnode( vnode, activeInstance ) // 掛載組件,與vue的$mount一樣 child.$mount(hydrating ? vnode.elm : undefined, hydrating) } } // ...省略其他代碼

由于組件是初次創(chuàng)建,因此init鉤子會(huì)調(diào)用createComponentInstanceForVnode創(chuàng)建一個(gè)組件實(shí)例,并賦值給vnode.componentInstance。

export function createComponentInstanceForVnode ( vnode: any, parent: any,): Component { // 內(nèi)部組件選項(xiàng) const options: InternalComponentOptions = { // 標(biāo)記是否是組件 _isComponent: true, // 父Vnode _parentVnode: vnode, // 父Vue實(shí)例 parent } // check inline-template render functions const inlineTemplate = vnode.data.inlineTemplate if (isDef(inlineTemplate)) { options.render = inlineTemplate.render options.staticRenderFns = inlineTemplate.staticRenderFns } // new 一個(gè)組件實(shí)例。組件實(shí)例化 與 new Vue() 執(zhí)行的過程相同。 return new vnode.componentOptions.Ctor(options)}

createComponentInstanceForVnode 中會(huì)執(zhí)行 new vnode.componentOptions.Ctor(options)。由前面我們在創(chuàng)建組件vnode時(shí)可知,vnode.componentOptions的值是一個(gè)對象:{ Ctor, propsData, listeners, tag, children },其中包含了組件的構(gòu)造函數(shù)Ctor。因此 new vnode.componentOptions.Ctor(options)等價(jià)于new VueComponent(options)。

// 生成組件實(shí)例const child = vnode.componentInstance = createComponentInstanceForVnode(vnode, activeInstance)// 掛載組件,與vue的$mount一樣child.$mount(hydrating ? vnode.elm : undefined, hydrating)

等價(jià)于:

new VueComponent(options).$mount(hydrating ? vnode.elm : undefined, hydrating)

這段代碼想必大家都很熟悉了,是組件初始化和掛載的過程。組件的初始化和掛載與在前文中所介紹Vue初始化和掛載過程相同,因此不再展開說明。大致的過程就是創(chuàng)建了一個(gè)組件實(shí)例并掛載后。使用initComponent將組件實(shí)例的$el設(shè)置為vnode.elm的值。最后,調(diào)用insert將組件實(shí)例的DOM根節(jié)點(diǎn)插入其父節(jié)點(diǎn)。然后就完成了組件的處理。

總結(jié)

通過對組件底層實(shí)現(xiàn)的分析,我們可以知道,每個(gè)組件都是一個(gè)VueComponent實(shí)例,而VueComponent又是繼承自Vue。每個(gè)組件實(shí)例獨(dú)立維護(hù)自己的狀態(tài)、模板的解析、DOM的創(chuàng)建和更新。篇幅有限,文中只分析了基本的組件的注冊解析過程,未對異步組件、keep-alive等做分析。等后面再慢慢補(bǔ)上。

以上就是詳解vue 組件的實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于vue組件的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Vue
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
精品成人免费一区二区在线播放| 99久久九九| 水蜜桃久久夜色精品一区的特点| 石原莉奈一区二区三区在线观看| 日本午夜精品一区二区三区电影| 日本成人中文字幕| 日韩精品不卡一区二区| 亚洲精品免费观看| 免费在线小视频| 日韩精品一区二区三区中文 | 欧产日产国产精品视频| 亚洲一级网站| 日韩二区在线观看| 尹人成人综合网| 国产精品午夜av| 国产一区清纯| 精品免费在线| 欧美日韩精品一区二区三区视频 | 精品一区二区三区在线观看视频| 国产日韩一区二区三区在线| 国产欧美日韩在线一区二区| 欧美激情91| 91综合网人人| 欧美一级精品| 丝袜脚交一区二区| 国产日产精品一区二区三区四区的观看方式| 麻豆国产精品| 亚洲精品中文字幕99999| 欧美国产专区| 午夜欧美精品| 成人日韩av| 综合精品一区| 欧美亚洲国产精品久久| 国产日产一区| 亚洲综合欧美| 欧美成人基地| 91欧美日韩在线| 136国产福利精品导航网址| 麻豆久久一区| 奇米777国产一区国产二区| 亚洲二区三区不卡| 92国产精品| 免费在线亚洲欧美| 青青国产91久久久久久| 久久人人88| 91精品xxx在线观看| 日韩国产一区二区三区| 久久天堂影院| 精品视频一区二区三区四区五区| 综合国产精品| 日韩精品一区第一页| 国产精品久久久久av蜜臀| 欧美天堂一区| 青草av.久久免费一区| 在线亚洲观看| 亚洲激情中文| 日韩午夜av| 欧美日韩国产亚洲一区| 久久久久伊人| 日韩极品在线观看| 99综合视频| 欧美二区视频| 久久亚洲图片| 国产精品毛片| 水野朝阳av一区二区三区| 蜜臀精品一区二区三区在线观看| 亚洲免费中文| 国产麻豆一区二区三区| 久久男人av| 九一国产精品| 日韩国产成人精品| 美女高潮久久久| 国产亚洲一区二区手机在线观看| 精精国产xxxx视频在线野外 | 国产精品jk白丝蜜臀av小说| 国产精品密蕾丝视频下载| 正在播放日韩精品| 91九色精品| 日本不卡一区二区| 国产精品xx| 日本一区福利在线| 狠狠久久伊人| 亚洲神马久久| 久久免费视频66| 久久亚洲影院| 日韩电影免费在线观看| 亚洲综合不卡| 精品一区91| 亚洲午夜免费| 99久久久久| 日韩视频一区| 黄色亚洲精品| 国产精品18| 国产精品毛片在线| 国产九一精品| av资源亚洲| 国产精品1区| 久久精品xxxxx| 麻豆亚洲精品| 午夜一级久久| 深夜福利亚洲| 国产精品久久久久久久久久白浆| 日韩三级视频| 国产精品视频一区二区三区| 国产精品一区亚洲| 国产日韩欧美一区在线| 欧美日韩18| 精品九九久久| 国产不卡人人| 亚洲一区二区三区四区五区午夜| 三级欧美韩日大片在线看| 日韩手机在线| 91日韩欧美| 九一国产精品| 日韩国产成人精品| 久久久久久夜| 国产精品成人**免费视频| 国产亚洲电影| 蜜臀精品久久久久久蜜臀| 久久亚洲图片| 蜜桃视频一区二区| 麻豆9191精品国产| 视频一区视频二区中文字幕| 在线综合欧美| 亚洲激情不卡| 综合色一区二区| 亚洲免费一区三区| 亚洲精品乱码日韩| 欧美亚洲一区二区三区| 国产精品嫩模av在线| 久久精品国产999大香线蕉| 狠狠久久伊人| 日韩免费看片| 视频福利一区| 亚洲男人在线| 久久超碰99| 欧美性感美女一区二区| 老司机久久99久久精品播放免费| 日韩精品一级中文字幕精品视频免费观看 | 国产一区成人| 日本午夜精品一区二区三区电影| 亚洲久草在线| 免费一级欧美片在线观看网站| 欧美91在线|欧美| 日韩欧美精品| 午夜久久av | 亚洲影视一区| 91亚洲无吗| 日韩不卡免费高清视频| 玖玖精品视频| 成人亚洲精品| 亚洲一区二区三区高清| 国产麻豆一区| 在线亚洲欧美| 久久精品免费看| 视频精品一区二区| 黄色在线网站噜噜噜| 亚洲精品乱码久久久久久蜜桃麻豆 | 免费成人av在线播放| 精品视频免费| 在线看片日韩| 亚洲91精品| 欧美精品91| 免费观看在线综合| 久久一级电影| 中文字幕人成乱码在线观看 | 欧美男人天堂| 国产精品嫩模av在线| 日韩精品1区2区3区| 99pao成人国产永久免费视频| 天堂网av成人| 精品美女在线视频| 国产精品久久亚洲不卡| 日韩亚洲精品在线| 国产经典一区| 91亚洲精品视频在线观看| 一区二区三区国产盗摄| 欧美日韩在线二区| 日韩三区在线| 国产超碰精品| 国产亚洲一区二区手机在线观看| 精品国产精品国产偷麻豆| 日韩欧美中文在线观看| 蜜臀久久99精品久久久久宅男 | 国产亚洲一区二区手机在线观看| 久久精品国产999大香线蕉| 麻豆高清免费国产一区| 国产成人a视频高清在线观看| 麻豆国产精品| 亚洲我射av| 另类专区亚洲| 视频一区二区中文字幕| 欧美日韩视频| 久久视频精品| 香蕉国产精品| 日本中文字幕不卡| 国产午夜精品一区在线观看| 日韩1区2区3区| 黄色成人精品网站| 日本а中文在线天堂|