<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當(dāng)前位置: 首頁 - 科技 - 知識百科 - 正文

        對類Vue的MVVM前端庫的實(shí)現(xiàn)代碼

        來源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 22:08:06
        文檔

        對類Vue的MVVM前端庫的實(shí)現(xiàn)代碼

        對類Vue的MVVM前端庫的實(shí)現(xiàn)代碼:MVVM (ModelView ViewModel)是一種基于MVC的設(shè)計(jì),開發(fā)人員在HTML上寫一些Bindings,利用一些指令綁定,就能在Model和ViewModel保持不變的情況下,很方便的將UI設(shè)計(jì)與業(yè)務(wù)邏輯分離,從而大大的減少繁瑣的DOM操作。 關(guān)于實(shí)現(xiàn)MVVM,網(wǎng)上實(shí)在是太多了,本
        推薦度:
        導(dǎo)讀對類Vue的MVVM前端庫的實(shí)現(xiàn)代碼:MVVM (ModelView ViewModel)是一種基于MVC的設(shè)計(jì),開發(fā)人員在HTML上寫一些Bindings,利用一些指令綁定,就能在Model和ViewModel保持不變的情況下,很方便的將UI設(shè)計(jì)與業(yè)務(wù)邏輯分離,從而大大的減少繁瑣的DOM操作。 關(guān)于實(shí)現(xiàn)MVVM,網(wǎng)上實(shí)在是太多了,本

        MVVM

        (ModelView ViewModel)是一種基于MVC的設(shè)計(jì),開發(fā)人員在HTML上寫一些Bindings,利用一些指令綁定,就能在Model和ViewModel保持不變的情況下,很方便的將UI設(shè)計(jì)與業(yè)務(wù)邏輯分離,從而大大的減少繁瑣的DOM操作。

        關(guān)于實(shí)現(xiàn)MVVM,網(wǎng)上實(shí)在是太多了,本文為個(gè)人總結(jié),結(jié)合源碼以及一些別人的實(shí)現(xiàn)

        關(guān)于雙向綁定

        •vue 數(shù)據(jù)劫持 + 訂閱 - 發(fā)布
        •ng 臟值檢查
        •backbone.js 訂閱-發(fā)布(這個(gè)沒有使用過,并不是主流的用法)

        雙向綁定,從最基本的實(shí)現(xiàn)來說,就是在defineProperty綁定的基礎(chǔ)上在綁定input事件,達(dá)到v-model的功能

        代碼思路圖

        兩個(gè)版本:

        •簡單版本: 非常簡單,但是因?yàn)槭莈s6,并且代碼極度簡化,所以不談功能,思路還是很清晰的
        •標(biāo)準(zhǔn)版本: 參照了Vue的部分源碼,代碼的功能高度向上抽取,閱讀稍微有點(diǎn)困難,實(shí)現(xiàn)了基本的功能,包括計(jì)算屬性,watch,核心功能都實(shí)現(xiàn)沒問題,但是不支持?jǐn)?shù)組

        簡單版本

        簡單版本的地址: 簡單版本

        ​ 這個(gè)MVVM也許代碼邏輯上面實(shí)現(xiàn)的并不完美,并不是正統(tǒng)的MVVM, 但是代碼很精簡,相對于源碼,要好理解很多,并且實(shí)現(xiàn)了v-model以及v-on methods的功能,代碼非常少,就100多行

        class MVVM {
         constructor(options) {
         const {
         el,
         data,
         methods
         } = options
         this.methods = methods
         this.target = null
         this.observer(this, data)
         this.instruction(document.getElementById(el)) // 獲取掛載點(diǎn)
         }
         // 數(shù)據(jù)監(jiān)聽器 攔截所有data數(shù)據(jù) 傳給defineProperty用于數(shù)據(jù)劫持
         observer(root, data) {
         for (const key in data) {
         this.definition(root, key, data[key])
         }
         }
         // 將攔截的數(shù)據(jù)綁定到this上面
         definition(root, key, value) {
         // if (typeof value === 'object') { // 假如value是對象則接著遞歸
         // return this.observer(value, value)
         // }
         let dispatcher = new Dispatcher() // 調(diào)度員
         Object.defineProperty(root, key, {
         set(newValue) {
         value = newValue
         dispatcher.notify(newValue)
         },
         get() {
         dispatcher.add(this.target)
         return value
         }
         })
         }
         //指令解析器
         instruction(dom) {
         const nodes = dom.childNodes; // 返回節(jié)點(diǎn)的子節(jié)點(diǎn)集合
         // console.log(nodes); //查看節(jié)點(diǎn)屬性
         for (const node of nodes) { // 與for in相反 for of 獲取迭代的value值
         if (node.nodeType === 1) { // 元素節(jié)點(diǎn)返回1
         const attrs = node.attributes //獲取屬性
         for (const attr of attrs) {
         if (attr.name === 'v-model') {
         let value = attr.value //獲取v-model的值
         node.addEventListener('input', e => { // 鍵盤事件觸發(fā)
         this[value] = e.target.value
         })
         this.target = new Watcher(node, 'input') // 儲存到訂閱者
         this[value] // get一下,將 this.target 給調(diào)度員
         }
         if (attr.name == "@click") {
         let value = attr.value // 獲取點(diǎn)擊事件名
         node.addEventListener('click',
         this.methods[value].bind(this)
         )
         }
         }
         }
         if (node.nodeType === 3) { // 文本節(jié)點(diǎn)返回3
         let reg = /\{\{(.*)\}\}/; //匹配 {{ }}
         let match = node.nodeValue.match(reg)
         if (match) { // 匹配都就獲取{{}}里面的變量
         const value = match[1].trim()
         this.target = new Watcher(node, 'text')
         this[value] = this[value] // get set更新一下數(shù)據(jù)
         }
         }
         }
         }
        }
        //調(diào)度員 > 調(diào)度訂閱發(fā)布
        class Dispatcher {
         constructor() {
         this.watchers = []
         }
         add(watcher) {
         this.watchers.push(watcher) // 將指令解析器解析的數(shù)據(jù)節(jié)點(diǎn)的訂閱者存儲進(jìn)來,便于訂閱
         }
         notify(newValue) {
         this.watchers.map(watcher => watcher.update(newValue))
         // 有數(shù)據(jù)發(fā)生,也就是觸發(fā)set事件,notify事件就會將新的data交給訂閱者,訂閱者負(fù)責(zé)更新
         }
        }
        //訂閱發(fā)布者 MVVM核心
        class Watcher {
         constructor(node, type) {
         this.node = node
         this.type = type
         }
         update(value) {
         if (this.type === 'input') {
         this.node.value = value // 更新的數(shù)據(jù)通過訂閱者發(fā)布到dom
         }
         if (this.type === 'text') {
         this.node.nodeValue = value
         }
         }
        }
        <!DOCTYPE html>
        <html lang="en">
        <head>
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <meta http-equiv="X-UA-Compatible" content="ie=edge">
         <title>MVVM</title>
        </head>
        <body>
         <div id="app">
         <input type="text" v-model="text">{{ text }}
         <br>
         <button @click="update">重置</button>
         </div>
         <script src="./index.js"></script>
         <script>
         let mvvm = new MVVM({
         el: 'app',
         data: {
         text: 'hello MVVM'
         },
         methods: {
         update() {
         this.text = ''
         }
         }
         })
         </script>
        </body>
        </html>

        這個(gè)版本的MVVM因?yàn)榇a比較少,并且是ES6的原因,思路非常清晰

        我們來看看從new MVVM開始,他都做了什么

        解讀簡單版本

        new MVVM

        首先,通過解構(gòu)獲取所有的new MVVM傳進(jìn)來的對象

        class MVVM {
         constructor(options) {
         const {
         el,
         data,
         methods
         } = options
         this.methods = methods // 提取methods,便于后面將this給methods
         this.target = null // 后面有用
         this.observer(this, data)
         this.instruction(document.getElementById(el)) // 獲取掛載點(diǎn)
         }

        屬性劫持

        開始執(zhí)行this.observer observer是一個(gè)數(shù)據(jù)監(jiān)聽器,將data的數(shù)據(jù)全部攔截下來

        observer(root, data) {
         for (const key in data) {
         this.definition(root, key, data[key])
         }
         }

        在this.definition里面把data數(shù)據(jù)都劫持到this上面

         definition(root, key, value) {
         if (typeof value === 'object') { // 假如value是對象則接著遞歸
         return this.observer(value, value)
         }
         let dispatcher = new Dispatcher() // 調(diào)度員
        
         Object.defineProperty(root, key, {
         set(newValue) {
         value = newValue
         dispatcher.notify(newValue)
         },
         get() {
         dispatcher.add(this.target)
         return value
         }
         })
         }

        此時(shí)data的數(shù)據(jù)變化我們已經(jīng)可以監(jiān)聽到了,但是我們監(jiān)聽到后還要與頁面進(jìn)行實(shí)時(shí)相應(yīng),所以這里我們使用調(diào)度員,在頁面初始化的時(shí)候get(),這樣this.target,也就是后面的指令解析器解析出來的v-model這樣的指令儲存到調(diào)度員里面,主要請看后面的解析器的代碼

        指令解析器

        指令解析器通過執(zhí)行 this.instruction(document.getElementById(el)) 獲取掛載點(diǎn)

        instruction(dom) {
         const nodes = dom.childNodes; // 返回節(jié)點(diǎn)的子節(jié)點(diǎn)集合
         // console.log(nodes); //查看節(jié)點(diǎn)屬性
         for (const node of nodes) { // 與for in相反 for of 獲取迭代的value值
         if (node.nodeType === 1) { // 元素節(jié)點(diǎn)返回1
         const attrs = node.attributes //獲取屬性
         for (const attr of attrs) {
         if (attr.name === 'v-model') {
         let value = attr.value //獲取v-model的值
         node.addEventListener('input', e => { // 鍵盤事件觸發(fā)
         this[value] = e.target.value
         })
         this.target = new Watcher(node, 'input') // 儲存到訂閱者
         this[value] // get一下,將 this.target 給調(diào)度員
         }
         if (attr.name == "@click") {
         let value = attr.value // 獲取點(diǎn)擊事件名
         node.addEventListener('click',
         this.methods[value].bind(this)
         )
         }
         }
         }
         if (node.nodeType === 3) { // 文本節(jié)點(diǎn)返回3
         let reg = /\{\{(.*)\}\}/; //匹配 {{ }}
         let match = node.nodeValue.match(reg)
         if (match) { // 匹配都就獲取{{}}里面的變量
         const value = match[1].trim()
         this.target = new Watcher(node, 'text')
         this[value] = this[value] // get set更新一下數(shù)據(jù)
         }
         }
         }
         }

        這里代碼首先解析出來我們自定義的屬性然后,我們將@click的事件直接指向methods,methds就已經(jīng)實(shí)現(xiàn)了

        現(xiàn)在代碼模型是這樣

        調(diào)度員Dispatcher與訂閱者Watcher

        我們需要將Dispatcher和Watcher聯(lián)系起來

        于是我們之前創(chuàng)建的變量this.target開始發(fā)揮他的作用了

        正執(zhí)行解析器里面使用this.target將node節(jié)點(diǎn),以及觸發(fā)關(guān)鍵詞存儲到當(dāng)前的watcher 訂閱,然后我們獲取一下數(shù)據(jù)

        this.target = new Watcher(node, 'input') // 儲存到訂閱者
        this[value] // get一下,將 this.target 給調(diào)度員

        在執(zhí)行this[value]的時(shí)候,觸發(fā)了get事件

        get() {
         dispatcher.add(this.target)
         return value
        }

        這get事件里面,我們將watcher訂閱者告知到調(diào)度員,調(diào)度員將訂閱事件存儲起來

        //調(diào)度員 > 調(diào)度訂閱發(fā)布
        class Dispatcher {
         constructor() {
         this.watchers = []
         }
         add(watcher) {
         this.watchers.push(watcher) // 將指令解析器解析的數(shù)據(jù)節(jié)點(diǎn)的訂閱者存儲進(jìn)來,便于訂閱
         }
         notify(newValue) {
         this.watchers.map(watcher => watcher.update(newValue))
         // 有數(shù)據(jù)發(fā)生,也就是觸發(fā)set事件,notify事件就會將新的data交給訂閱者,訂閱者負(fù)責(zé)更新
         }
        }

        與input不太一樣的是文本節(jié)點(diǎn)不僅需要獲取,還需要set一下,因?yàn)橐層嗛喺吒耼ode節(jié)點(diǎn)

        this.target = new Watcher(node, 'text')
        this[value] = this[value] // get set更新一下數(shù)據(jù)

        所以在訂閱者就添加了該事件,然后執(zhí)行set

        set(newValue) {
         value = newValue
         dispatcher.notify(newValue)
         },

        notfiy執(zhí)行,訂閱發(fā)布者執(zhí)行update更新node節(jié)點(diǎn)信息

        class Watcher {
         constructor(node, type) {
         this.node = node
         this.type = type
         }
         update(value) {
         if (this.type === 'input') {
         this.node.value = value // 更新的數(shù)據(jù)通過訂閱者發(fā)布到dom
         }
         if (this.type === 'text') {
         this.node.nodeValue = value
         }
         }
        }

        頁面初始化完畢

        更新數(shù)據(jù)

        node.addEventListener('input', e => { // 鍵盤事件觸發(fā)
         this[value] = e.target.value
        })

        this[value]也就是data數(shù)據(jù)發(fā)生變化,觸發(fā)set事件,既然觸發(fā)notfiy事件,notfiy遍歷所有節(jié)點(diǎn),在遍歷的節(jié)點(diǎn)里面根據(jù)頁面初始化的時(shí)候訂閱的觸發(fā)類型.進(jìn)行頁面的刷新

        現(xiàn)在可以完成的看看new MVVM的實(shí)現(xiàn)過程了

        最簡單版本的MVVM完成

        標(biāo)準(zhǔn)版本

        標(biāo)準(zhǔn)版本額外實(shí)現(xiàn)了component,watch,因?yàn)槟K化代碼很碎的關(guān)系,看起來還是有難度的

        從理念上來說,實(shí)現(xiàn)的思想基本是一樣的,可以參照上面的圖示,都是開始的時(shí)候都是攔截屬性,解析指令

        代碼有將近300行,所以就貼一個(gè)地址標(biāo)準(zhǔn)版本MVVM

        執(zhí)行順序

        1.new MVVM
        2.獲取$options = 所以參數(shù)
        3.獲取data,便于后面劫持
        4.因?yàn)槭莈s5,后面forEach內(nèi)部指向window,這不是我們想要的,所以存儲當(dāng)前this 為me
        5._proxyData劫持所有data數(shù)據(jù)
        6.初始化計(jì)算屬性
        7.通過Object.key()獲取計(jì)算屬性的屬性名
        8.初始化計(jì)算屬性將計(jì)算屬性掛載到vm上
        9.開始o(jì)bserver監(jiān)聽數(shù)據(jù)
        10.判斷data是否存在
        11.存在就new Observer(創(chuàng)建監(jiān)聽器)
        12.數(shù)據(jù)全部進(jìn)行進(jìn)行defineProperty存取監(jiān)聽處理,讓后面的數(shù)據(jù)變動都觸發(fā)這個(gè)的get/set
        13.開始獲取掛載點(diǎn)
        14.使用querySelector對象解析el
        15.創(chuàng)建一個(gè)虛擬節(jié)點(diǎn),并存儲當(dāng)前的dom
        16.解析虛擬dom
        17.使用childNodes解析對象
        18.因?yàn)槭莈s5,所以使用[].slice.call將對象轉(zhuǎn)數(shù)組
        19.獲取到后進(jìn)行 {{ }}匹配 指令的匹配 以及遞歸子節(jié)點(diǎn)
        20.指令的匹配: 匹配到指令因?yàn)椴恢蓝嗌賯€(gè)指令名稱,所以這里還是使用[].slice.call循環(huán)遍歷
        21.解析到有 v-的指令使用substring(2)截取后面的屬性名稱
        22.再判斷是不是指令v-on 這里就是匹配on關(guān)鍵字,匹配到了就是事件指令,匹配不到就是普通指令
        23.普通指令解析{{ data }} _getVMValget會觸發(fā)MVVM的_proxyData事件 在_proxyData事件里面觸發(fā)data的get事件
        24.這時(shí)候到了observer的defineReactive的get里面獲取到了數(shù)據(jù),因?yàn)闆]有Dispatcher.target,所以不進(jìn)行會觸發(fā)調(diào)度員
        25.至此_getVMVal獲取到了數(shù)據(jù)
        26.modelUpdater進(jìn)行Dom上面的數(shù)據(jù)更新
        27.數(shù)據(jù)開始進(jìn)行訂閱,在訂閱里面留一個(gè)回調(diào)函數(shù)用于更新dom
        28.在watcher(訂閱者)獲取this,訂閱的屬性,回調(diào)
        29.在this.getter這個(gè)屬性上面返回一個(gè)匿名函數(shù),用于獲取data的值
        30.觸發(fā)get事件,將當(dāng)前watcher的this存儲到Dispatcher.garget上面
        31.給this.getters,callvm的的this,執(zhí)行匿名函數(shù),獲取劫持下來的data,又觸發(fā)了MVVM的_proxyData的get事件,繼而有觸發(fā)了observer的defineReactive的get事件,不過這一次Dispatcher.target有值,執(zhí)行了depend事件
        32.在depend里面執(zhí)行了自己的addDep事件,并且將Observer自己的this傳進(jìn)去
        33.addDep里面執(zhí)行了Dispatcher的addSub事件,
        34.在addUsb事件里面將訂閱存儲到Dispatcher里面的this.watchers里面的
        35.訂閱完成,后面將這些自定義的指令進(jìn)行移除
        36.重復(fù)操作,解析所有指令,v-on:click = "data"直接執(zhí)行methods[data].bind(vm)

        更新數(shù)據(jù):

        1.觸發(fā)input事件
        2.觸發(fā)_setVMVal事件
        3.觸發(fā)MVVM的set事件
        4.觸發(fā)observer的set事件
        5.觸發(fā)dep.notify()
        6.觸發(fā)watcher的run方法
        7.觸發(fā)new Watcher的回調(diào) this.cb
        8.觸發(fā)compile里面的updaterFn 事件
        9.更新視圖

        component的實(shí)現(xiàn)

        計(jì)算屬性的觸發(fā) 查看這個(gè)例子

        computed: {
         getHelloWord: function () {
         return this.someStr + this.child.someStr;
         }
         },

        其實(shí)計(jì)算屬性就是defineproperty的一個(gè)延伸

        1.首先compile里面解析獲取到{{ getHelloword }}'
        2.執(zhí)行updater[textUpdater]
        3.執(zhí)行_getVMVal獲取計(jì)算屬性的返回值
        4.獲取vm[component]就會執(zhí)行下面的get事件

        Object.defineProperty(me, key, {
         get: typeof computed[key] === 'function' ? computed[key] : computed[key].get,
         set: function () {}
         })

        是function執(zhí)行computed[getHelloword],也就是return 的 函數(shù)

        this.someStr + this.child.someStr;

        1.依次獲取data,觸發(fā)mvvm的get 以及observer的get,

        初始化完成,到這里還沒有綁定數(shù)據(jù),僅僅是初始化完成了

        1.開始訂閱該事件 new Watcher()
        2.component不是函數(shù)所以不是function 執(zhí)行this.parseGetter(expOrFn);
        3.返回一個(gè)覆蓋expOrrn的匿名函數(shù)
        4.開始初始化 執(zhí)行g(shù)et()
        5.存儲當(dāng)前this,開始獲取vm[getHelloword]
        6.觸發(fā)component[getHelloword]
        7.開始執(zhí)行MVVM的get this.someStr
        8.到MVVM的get 到 observer的get 因?yàn)?Dispatcher.target存著 getHelloWord 的 this.depend ()所以執(zhí)行
        9.Dispatcher的depend(),執(zhí)行watcher的addDep(),執(zhí)行 Dispatcher的addSub() 將當(dāng)前的watcher存儲到監(jiān)聽器
        10.開始get第二個(gè)數(shù)據(jù) this.child.someStr,同理也將getHelloWord的this存入了當(dāng)前的Dispatcher
        11.開始get第三個(gè)數(shù)據(jù) this.child,同理也將getHelloWord的this存入了當(dāng)前的Dispatcher

        這個(gè)執(zhí)行順序有點(diǎn)迷,第二第三方反來了

        this.parseGetter(expOrFn);就執(zhí)行完畢了

        目前來看為什么component會實(shí)時(shí)屬性數(shù)據(jù)?

        因?yàn)閏omponent的依賴屬性一旦發(fā)生變化都會更新 getHelloword 的 watcher ,隨之執(zhí)行回調(diào)更新dom

        watch的實(shí)現(xiàn)

        watch的實(shí)現(xiàn)相對來說要簡單很多

        1.我們只要將watch監(jiān)聽的數(shù)據(jù)告訴訂閱者就可以了
        2.這樣,wacth更新了
        3.觸發(fā)set,set觸發(fā)notify
        4.notify更新watcher
        5.watcher執(zhí)行run
        6.run方法去執(zhí)行watch的回調(diào)
        7.即完成了watch的監(jiān)聽

        watch: function (key, cb) {
         new Watcher(this, key, cb)
        },


        聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        對類Vue的MVVM前端庫的實(shí)現(xiàn)代碼

        對類Vue的MVVM前端庫的實(shí)現(xiàn)代碼:MVVM (ModelView ViewModel)是一種基于MVC的設(shè)計(jì),開發(fā)人員在HTML上寫一些Bindings,利用一些指令綁定,就能在Model和ViewModel保持不變的情況下,很方便的將UI設(shè)計(jì)與業(yè)務(wù)邏輯分離,從而大大的減少繁瑣的DOM操作。 關(guān)于實(shí)現(xiàn)MVVM,網(wǎng)上實(shí)在是太多了,本
        推薦度:
        標(biāo)簽: VUE 實(shí)現(xiàn) 代碼
        • 熱門焦點(diǎn)

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 亚洲免费一区二区| 暖暖日本免费在线视频| 亚洲AV无码国产精品色| 久久午夜夜伦鲁鲁片无码免费| 亚洲精品午夜国产VA久久成人| v片免费在线观看| 亚洲欧洲自拍拍偷午夜色无码| 在线观看免费视频网站色| 黑人精品videos亚洲人| 久久aⅴ免费观看| 亚洲色欲www综合网| 久久久久久久91精品免费观看| 亚洲一区无码中文字幕乱码| 成人五级毛片免费播放| 亚洲精品色播一区二区| 国产精品亚洲精品日韩已方| 福利免费在线观看| 亚洲综合网美国十次| 无码一区二区三区免费视频| 国产精品亚洲AV三区| 亚洲情综合五月天| 无码国产精品一区二区免费式直播 | 13小箩利洗澡无码视频网站免费| 丁香五月亚洲综合深深爱| 中文字幕视频免费| 亚洲av日韩专区在线观看| 亚洲无线观看国产精品| 日本亚洲免费无线码| 免费人成再在线观看网站| 亚洲成av人影院| 在线免费观看毛片网站| 中国一级全黄的免费观看| 精品久久亚洲中文无码| 亚洲AV无码之日韩精品| 777成影片免费观看| 看免费毛片天天看| 麻豆亚洲av熟女国产一区二| 成人亚洲综合天堂| 国产精品免费精品自在线观看| 深夜福利在线免费观看| 亚洲资源在线观看|