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

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

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問(wèn)答1問(wèn)答10問(wèn)答100問(wèn)答1000問(wèn)答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
        問(wèn)答文章1 問(wèn)答文章501 問(wèn)答文章1001 問(wèn)答文章1501 問(wèn)答文章2001 問(wèn)答文章2501 問(wèn)答文章3001 問(wèn)答文章3501 問(wèn)答文章4001 問(wèn)答文章4501 問(wèn)答文章5001 問(wèn)答文章5501 問(wèn)答文章6001 問(wèn)答文章6501 問(wèn)答文章7001 問(wèn)答文章7501 問(wèn)答文章8001 問(wèn)答文章8501 問(wèn)答文章9001 問(wèn)答文章9501
        當(dāng)前位置: 首頁(yè) - 科技 - 知識(shí)百科 - 正文

        vue實(shí)現(xiàn)簡(jiǎn)單的MVVM框架

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

        vue實(shí)現(xiàn)簡(jiǎn)單的MVVM框架

        vue實(shí)現(xiàn)簡(jiǎn)單的MVVM框架:不知不覺(jué)接觸前端的時(shí)間已經(jīng)過(guò)去半年了,越來(lái)越發(fā)覺(jué)對(duì)知識(shí)的學(xué)習(xí)不應(yīng)該只停留在會(huì)用的層面,這在我學(xué)jQuery的一段時(shí)間后便有這樣的體會(huì)。 雖然jQuery只是一個(gè)JS的代碼庫(kù),只要會(huì)一些JS的基本操作學(xué)習(xí)一兩天就能很快掌握jQuery的基本語(yǔ)法并熟練使用,但是如果
        推薦度:
        導(dǎo)讀vue實(shí)現(xiàn)簡(jiǎn)單的MVVM框架:不知不覺(jué)接觸前端的時(shí)間已經(jīng)過(guò)去半年了,越來(lái)越發(fā)覺(jué)對(duì)知識(shí)的學(xué)習(xí)不應(yīng)該只停留在會(huì)用的層面,這在我學(xué)jQuery的一段時(shí)間后便有這樣的體會(huì)。 雖然jQuery只是一個(gè)JS的代碼庫(kù),只要會(huì)一些JS的基本操作學(xué)習(xí)一兩天就能很快掌握jQuery的基本語(yǔ)法并熟練使用,但是如果

        不知不覺(jué)接觸前端的時(shí)間已經(jīng)過(guò)去半年了,越來(lái)越發(fā)覺(jué)對(duì)知識(shí)的學(xué)習(xí)不應(yīng)該只停留在會(huì)用的層面,這在我學(xué)jQuery的一段時(shí)間后便有這樣的體會(huì)。

        雖然jQuery只是一個(gè)JS的代碼庫(kù),只要會(huì)一些JS的基本操作學(xué)習(xí)一兩天就能很快掌握jQuery的基本語(yǔ)法并熟練使用,但是如果不了解jQUery庫(kù)背后的實(shí)現(xiàn)原理,相信只要你一段時(shí)間不再使用jQuery的話就會(huì)把jQuery忘得一干二凈,這也許就是知其然不知其所以然的后果。

        最近在學(xué)vue的時(shí)候又再一次經(jīng)歷了這樣的困惑,雖然能夠比較熟練的掌握vue的基本使用,也能夠?qū)V*模式、數(shù)據(jù)劫持、雙向數(shù)據(jù)綁定、數(shù)據(jù)代理侃上兩句。但是要是稍微深入一點(diǎn)就有點(diǎn)吃力了。所以這幾天痛下決心研究大量技術(shù)文章(起初嘗試看早期源碼,無(wú)奈vue與jQuery不是一個(gè)層級(jí)的,相比于jQuery,vue是真正意義上的前端框架。只能無(wú)奈棄坑轉(zhuǎn)而看技術(shù)博客),對(duì)vue也算有了一個(gè)管中窺豹的認(rèn)識(shí)。最后嘗試實(shí)踐一下自己學(xué)到的知識(shí),基于數(shù)據(jù)代理、數(shù)據(jù)劫持、模板解析、雙向綁定實(shí)現(xiàn)了一個(gè)小型的vue框架。

        溫馨提示:文章是按照每個(gè)模塊的實(shí)現(xiàn)依賴關(guān)系來(lái)進(jìn)行分析的,但是在閱讀的時(shí)候可以按照vue的執(zhí)行順序來(lái)分析,這樣對(duì)初學(xué)者更加的友好。推薦的閱讀順序?yàn)椋簩?shí)現(xiàn)VMVM、數(shù)據(jù)代理、實(shí)現(xiàn)Observe、實(shí)現(xiàn)Complie、實(shí)現(xiàn)Watcher。

        源碼:https://github.com/yuliangbin/MVVM

        功能演示如下所示:

        數(shù)據(jù)代理

        以下面這個(gè)模板為例,要替換的根元素“#mvvm-app”內(nèi)只有一個(gè)文本節(jié)點(diǎn)#text,#text的內(nèi)容為{{name}}。我們就以下面這個(gè)模板詳細(xì)了解一下VUE框架的大體實(shí)現(xiàn)流程。

        <body>
         <div id="mvvm-app">
         {{name}}
         </div>
         <script src="./js/observer.js"></script>
         <script src="./js/watcher.js"></script>
         <script src="./js/compile.js"></script>
         <script src="./js/mvvm.js"></script>
         <script>
         let vm = new MVVM({
         el: "#mvvm-app",
         data: {
         name: "hello world"
         }, 
         })
        
         </script>
        </body>

        數(shù)據(jù)代理

        1、什么是數(shù)據(jù)代理

        在vue里面,我們將數(shù)據(jù)寫在data對(duì)象中。但是我們?cè)谠L問(wèn)data里的數(shù)據(jù)時(shí),既可以通過(guò)vm.data.name訪問(wèn),也可以通過(guò)vm.name訪問(wèn)。這就是數(shù)據(jù)代理:在一個(gè)對(duì)象中,可以動(dòng)態(tài)的訪問(wèn)和設(shè)置另一個(gè)對(duì)象的屬性。

        2、實(shí)現(xiàn)原理

        我們知道靜態(tài)綁定(如vm.name = vm.data.name)可以一次性的將結(jié)果賦給變量,而使用Object.defineProperty()方法來(lái)綁定則可以通過(guò)set和get函數(shù)實(shí)現(xiàn)賦值的中間過(guò)程,從而實(shí)現(xiàn)數(shù)據(jù)的動(dòng)態(tài)綁定。具體實(shí)現(xiàn)如下:

        let obj = {};
        let obj1 = {
         name: 'xiaoyu',
         age: 18,
        }
        //實(shí)現(xiàn)origin對(duì)象代理target對(duì)象
        function proxyData(origin,target){
         Object.keys(target).forEach(function(key){
         Object.defineProperty(origin,key,{//定義origin對(duì)象的key屬性
         enumerable: false,
         configurable: true,
         get: function getter(){
         return target[key];//origin[key] = target[key];
         },
         set: function setter(newValue){
         target[key] = newValue;
         }
         })
         })
        }

        vue中的數(shù)據(jù)代理也是通過(guò)這種方式來(lái)實(shí)現(xiàn)的。

        function MVVM(options) {
         this.$options = options || {};
         var data = this._data = this.$options.data;
         var _this = this;//當(dāng)前實(shí)例vm
        
         // 數(shù)據(jù)代理
         // 實(shí)現(xiàn) vm._data.xxx -> vm.xxx 
         Object.keys(data).forEach(function(key) {
         _this._proxyData(key);
         });
         observe(data, this);
         this.$compile = new Compile(options.el || document.body, this);
        
        }
        
        MVVM.prototype = {
        _proxyData: function(key) {
         var _this = this;
         if (typeof key == 'object' && !(key instanceof Array)){//這里只實(shí)現(xiàn)了對(duì)對(duì)象的監(jiān)聽(tīng),沒(méi)有實(shí)現(xiàn)數(shù)組的
         this._proxyData(key);
         }
         Object.defineProperty(_this, key, {
         configurable: false,
         enumerable: true,
         get: function proxyGetter() {
         return _this._data[key];
         },
         set: function proxySetter(newVal) {
         _this._data[key] = newVal;
         }
         });
        },
        };

        實(shí)現(xiàn)Observe

        1、雙向數(shù)據(jù)綁定

        數(shù)據(jù)變動(dòng)  --->  視圖更新

        視圖更新  --->  數(shù)據(jù)變動(dòng)

        要想實(shí)現(xiàn)當(dāng)數(shù)據(jù)變動(dòng)時(shí)視圖更新,首先要做的就是如何知道數(shù)據(jù)變動(dòng)了,可以通過(guò)Object.defineProperty()函數(shù)監(jiān)聽(tīng)data對(duì)象里的數(shù)據(jù),當(dāng)數(shù)據(jù)變動(dòng)了就會(huì)觸發(fā)set()方法。所以我們需要實(shí)現(xiàn)一個(gè)數(shù)據(jù)監(jiān)聽(tīng)器Observe,來(lái)對(duì)數(shù)據(jù)對(duì)象中的所有屬性進(jìn)行監(jiān)聽(tīng),當(dāng)某一屬性數(shù)據(jù)發(fā)生變化時(shí),拿到最新的數(shù)據(jù)通知綁定了該屬性的訂閱器,訂閱器再執(zhí)行相應(yīng)的數(shù)據(jù)更新回調(diào)函數(shù),從而實(shí)現(xiàn)視圖的刷新。

        當(dāng)設(shè)置this.name = 'hello vue'時(shí),就會(huì)執(zhí)行set函數(shù),通知訂閱器里的訂閱者執(zhí)行相應(yīng)的回調(diào)函數(shù),實(shí)現(xiàn)數(shù)據(jù)變動(dòng),對(duì)應(yīng)視圖更新。

        function observe(data){
         if (typeof data != 'object') {
         return ;
         }
         return new Observe(data);
        }
        
        function Observe(data){
         this.data = data;
         this.walk(data);
        }
        
        Observe.prototype = {
         walk: function(data){
         let _this = this;
         for (key in data) {
         if (data.hasOwnProperty(key)){
         let value = data[key];
         if (typeof value == 'object'){
         observe(value);
         }
         _this.defineReactive(data,key,data[key]);
         }
         }
         },
         defineReactive: function(data,key,value){
         Object.defineProperty(data,key,{
         enumerable: true,//可枚舉
         configurable: false,//不能再define
         get: function(){
         console.log('你訪問(wèn)了' + key);return value;
         },
         set: function(newValue){
         console.log('你設(shè)置了' + key);
         if (newValue == value) return;
         value = newValue;
         observe(newValue);//監(jiān)聽(tīng)新設(shè)置的值
         }
         })
         }
        }

        2、實(shí)現(xiàn)一個(gè)訂閱器

        要想通知訂閱者,首先得要有一個(gè)訂閱器(統(tǒng)一管理所有的訂閱者)。為了方便管理,我們會(huì)為每一個(gè)data對(duì)象的屬性都添加一個(gè)訂閱器(new Dep)。

        訂閱器里存著的是訂閱者Watcher(后面會(huì)講到),由于訂閱者可能會(huì)有多個(gè),我們需要建立一個(gè)數(shù)組來(lái)維護(hù)。一旦數(shù)據(jù)變化,就會(huì)觸發(fā)訂閱器的notify()方法,訂閱者就會(huì)調(diào)用自身的update方法實(shí)現(xiàn)視圖更新。

        function Dep(){
         this.subs = [];
        }
        Dep.prototype = {
         addSub: function(sub){this.subs.push(sub);
         },
         notify: function(){
         this.subs.forEach(function(sub) {
         sub.update();
         })
         }
        }

        每次響應(yīng)屬性的set()函數(shù)調(diào)用的時(shí)候,都會(huì)觸發(fā)訂閱器,所以代碼補(bǔ)充完整。

        Observe.prototype = {
         //省略的代碼未作更改
         defineReactive: function(data,key,value){
         let dep = new Dep();//創(chuàng)建一個(gè)訂閱器,會(huì)被閉包在key屬性的get/set函數(shù)內(nèi),因此每個(gè)屬性對(duì)應(yīng)唯一一個(gè)訂閱器dep實(shí)例
         Object.defineProperty(data,key,{
         enumerable: true,//可枚舉
         configurable: false,//不能再define
         get: function(){
         console.log('你訪問(wèn)了' + key);
         return value;
         },
         set: function(newValue){
         console.log('你設(shè)置了' + key);
         if (newValue == value) return;
         value = newValue;
         observe(newValue);//監(jiān)聽(tīng)新設(shè)置的值
         dep.notify();//通知所有的訂閱者
         }
         })
         }
        }

        實(shí)現(xiàn)Complie

        compile主要做的事情是解析模板指令,將模板中的data屬性替換成data屬性對(duì)應(yīng)的值(比如將{{name}}替換成data.name值),然后初始化渲染頁(yè)面視圖,并且為每個(gè)data屬性添加一個(gè)監(jiān)聽(tīng)數(shù)據(jù)的訂閱者(new Watcher),一旦數(shù)據(jù)有變動(dòng),收到通知,更新視圖。

        遍歷解析需要替換的根元素el下的HTML標(biāo)簽必然會(huì)涉及到多次的DOM節(jié)點(diǎn)操作,因此不可避免的會(huì)引發(fā)頁(yè)面的重排或重繪,為了提高性能和效率,我們把根元素el下的所有節(jié)點(diǎn)轉(zhuǎn)換為文檔碎片fragment進(jìn)行解析編譯操作,解析完成,再將fragment添加回原來(lái)的真實(shí)dom節(jié)點(diǎn)中。

        注:文檔碎片本身也是一個(gè)節(jié)點(diǎn),但是當(dāng)將該節(jié)點(diǎn)append進(jìn)頁(yè)面時(shí),該節(jié)點(diǎn)標(biāo)簽作為根節(jié)點(diǎn)不會(huì)顯示html文檔中,其里面的子節(jié)點(diǎn)則可以完全顯示。

        Compile解析模板,將模板內(nèi)的子元素#text添加進(jìn)文檔碎片節(jié)點(diǎn)fragment。

        function Compile(el,vm){
         this.$vm = vm;//vm為當(dāng)前實(shí)例
         this.$el = document.querySelector(el);//獲得要解析的根元素 
         if (this.$el){
         this.$fragment = this.nodeToFragment(this.$el);
         this.init();
         this.$el.appendChild(this.$fragment);
         } 
        }
        Compile.prototype = {
         nodeToFragment: function(el){
         let fragment = document.createDocumentFragment();
         let child;
         while (child = el.firstChild){
         fragment.appendChild(child);//append相當(dāng)于剪切的功能
         }
         return fragment;
         
         },
        };

        compileElement方法將遍歷所有節(jié)點(diǎn)及其子節(jié)點(diǎn),進(jìn)行掃描解析編譯,調(diào)用對(duì)應(yīng)的指令渲染函數(shù)進(jìn)行數(shù)據(jù)渲染,并調(diào)用對(duì)應(yīng)的指令更新函數(shù)進(jìn)行綁定,詳看代碼及注釋說(shuō)明:

        因?yàn)槲覀兊哪0逯缓幸粋€(gè)文本節(jié)點(diǎn)#text,因此compileElement方法執(zhí)行后會(huì)進(jìn)入_this.compileText(node,reg.exec(node.textContent)[1]);//#text,'name'

        Compile.prototype = {
         nodeToFragment: function(el){
         let fragment = document.createDocumentFragment();
         let child;
         while (child = el.firstChild){
         fragment.appendChild(child);//append相當(dāng)于剪切的功能
         }
         return fragment;
         
         },
         
         init: function(){
         this.compileElement(this.$fragment);
         },
         
         compileElement: function(node){
         let childNodes = node.childNodes;
         const _this = this;
         let reg = /\{\{(.*)\}\}/g;
         [].slice.call(childNodes).forEach(function(node){
         
         if (_this.isElementNode(node)){//如果為元素節(jié)點(diǎn),則進(jìn)行相應(yīng)操作
         _this.compile(node);
         } else if (_this.isTextNode(node) && reg.test(node.textContent)){
         //如果為文本節(jié)點(diǎn),并且包含data屬性(如{{name}}),則進(jìn)行相應(yīng)操作
         _this.compileText(node,reg.exec(node.textContent)[1]);//#text,'name'
         }
         
         if (node.childNodes && node.childNodes.length){
         //如果節(jié)點(diǎn)內(nèi)還有子節(jié)點(diǎn),則遞歸繼續(xù)解析節(jié)點(diǎn)
         _this.compileElement(node);
         
         }
         })
         },
         compileText: function(node,exp){//#text,'name'
         compileUtil.text(node,this.$vm,exp);//#text,vm,'name'
         },};

        CompileText()函數(shù)實(shí)現(xiàn)初始化渲染頁(yè)面視圖(將data.name的值通過(guò)#text.textContent = data.name顯示在頁(yè)面上),并且為每個(gè)DOM節(jié)點(diǎn)添加一個(gè)監(jiān)聽(tīng)數(shù)據(jù)的訂閱者(這里是為#text節(jié)點(diǎn)新增一個(gè)Wather)。

        let updater = {
         textUpdater: function(node,value){ 
         node.textContent = typeof value == 'undefined' ? '' : value;
         },
        }
         
        let compileUtil = {
         text: function(node,vm,exp){//#text,vm,'name'
         this.bind(node,vm,exp,'text');
         },
         
         bind: function(node,vm,exp,dir){//#text,vm,'name','text'
         let updaterFn = updater[dir + 'Updater'];
         updaterFn && updaterFn(node,this._getVMVal(vm,exp));
         new Watcher(vm,exp,function(value){
         updaterFn && updaterFn(node,value)
         });
         console.log('加進(jìn)去了');
         }
        };

        現(xiàn)在我們完成了一個(gè)能實(shí)現(xiàn)文本節(jié)點(diǎn)解析的Compile()函數(shù),接下來(lái)我們實(shí)現(xiàn)一個(gè)Watcher()函數(shù)。

        實(shí)現(xiàn)Watcher

        我們前面講過(guò),Observe()函數(shù)實(shí)現(xiàn)data對(duì)象的屬性劫持,并在屬性值改變時(shí)觸發(fā)訂閱器的notify()通知訂閱者Watcher,訂閱者就會(huì)調(diào)用自身的update方法實(shí)現(xiàn)視圖更新。

        Compile()函數(shù)負(fù)責(zé)解析模板,初始化頁(yè)面,并且為每個(gè)data屬性新增一個(gè)監(jiān)聽(tīng)數(shù)據(jù)的訂閱者(new Watcher)。

        Watcher訂閱者作為Observer和Compile之間通信的橋梁,所以我們可以大致知道Watcher的作用是什么。

        主要做的事情是:

        在自身實(shí)例化時(shí)往訂閱器(dep)里面添加自己。

        自身必須有一個(gè)update()方法 。

        待屬性變動(dòng)dep.notice()通知時(shí),能調(diào)用自身的update()方法,并觸發(fā)Compile中綁定的回調(diào)。

        先給出全部代碼,再分析具體的功能。

        //Watcher
        function Watcher(vm, exp, cb) {
         this.vm = vm;
         this.cb = cb;
         this.exp = exp;
         this.value = this.get();//初始化時(shí)將自己添加進(jìn)訂閱器
        };
        
        Watcher.prototype = {
         update: function(){
         this.run();
         },
         run: function(){
         const value = this.vm[this.exp];
         //console.log('me:'+value);
         if (value != this.value){
         this.value = value;
         this.cb.call(this.vm,value);
         }
         },
         get: function() { 
         Dep.target = this; // 緩存自己
         var value = this.vm[this.exp] // 訪問(wèn)自己,執(zhí)行defineProperty里的get函數(shù) 
         Dep.target = null; // 釋放自己
         return value;
         }
        }
        
        //這里列出Observe和Dep,方便理解
        Observe.prototype = {
         defineReactive: function(data,key,value){
         let dep = new Dep();
         Object.defineProperty(data,key,{
         enumerable: true,//可枚舉
         configurable: false,//不能再define
         get: function(){
         console.log('你訪問(wèn)了' + key);
         //說(shuō)明這是實(shí)例化Watcher時(shí)引起的,則添加進(jìn)訂閱器
         if (Dep.target){
         //console.log('訪問(wèn)了Dep.target');
         dep.addSub(Dep.target);
         }
         return value;
         },
         })
         }
        }
        
        Dep.prototype = {
         addSub: function(sub){this.subs.push(sub);
         },
        }

        我們知道在Observe()函數(shù)執(zhí)行時(shí),我們?yōu)槊總€(gè)屬性都添加了一個(gè)訂閱器dep,而這個(gè)dep被閉包在屬性的get/set函數(shù)內(nèi)。所以,我們可以在實(shí)例化Watcher時(shí)調(diào)用this.get()函數(shù)訪問(wèn)data.name屬性,這會(huì)觸發(fā)defineProperty()函數(shù)內(nèi)的get函數(shù),get方法執(zhí)行的時(shí)候,就會(huì)在屬性的訂閱器dep添加當(dāng)前watcher實(shí)例,從而在屬性值有變化的時(shí)候,watcher實(shí)例就能收到更新通知。

        那么Watcher()函數(shù)中的get()函數(shù)內(nèi)Dep.taeger = this又有什么特殊的含義呢?我們希望的是在實(shí)例化Watcher時(shí)將相應(yīng)的Watcher實(shí)例添加一次進(jìn)dep訂閱器即可,而不希望在以后每次訪問(wèn)data.name屬性時(shí)都加入一次dep訂閱器。所以我們?cè)趯?shí)例化執(zhí)行this.get()函數(shù)時(shí)用Dep.target = this來(lái)標(biāo)識(shí)當(dāng)前Watcher實(shí)例,當(dāng)添加進(jìn)dep訂閱器后設(shè)置Dep.target=null。

        實(shí)現(xiàn)VMVM

        MVVM作為數(shù)據(jù)綁定的入口,整合Observer、Compile和Watcher三者,通過(guò)Observer來(lái)監(jiān)聽(tīng)自己的model數(shù)據(jù)變化,通過(guò)Compile來(lái)解析編譯模板指令,最終利用Watcher搭起Observer和Compile之間的通信橋梁,達(dá)到數(shù)據(jù)變化 -> 視圖更新;視圖交互變化(input) -> 數(shù)據(jù)model變更的雙向綁定效果。

        function MVVM(options) {
         this.$options = options || {};
         var data = this._data = this.$options.data;
         var _this = this;
         // 數(shù)據(jù)代理
         // 實(shí)現(xiàn) vm._data.xxx -> vm.xxx 
         Object.keys(data).forEach(function(key) {
         _this._proxyData(key);
         });
         observe(data, this);
         this.$compile = new Compile(options.el || document.body, this);
        }

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

        文檔

        vue實(shí)現(xiàn)簡(jiǎn)單的MVVM框架

        vue實(shí)現(xiàn)簡(jiǎn)單的MVVM框架:不知不覺(jué)接觸前端的時(shí)間已經(jīng)過(guò)去半年了,越來(lái)越發(fā)覺(jué)對(duì)知識(shí)的學(xué)習(xí)不應(yīng)該只停留在會(huì)用的層面,這在我學(xué)jQuery的一段時(shí)間后便有這樣的體會(huì)。 雖然jQuery只是一個(gè)JS的代碼庫(kù),只要會(huì)一些JS的基本操作學(xué)習(xí)一兩天就能很快掌握jQuery的基本語(yǔ)法并熟練使用,但是如果
        推薦度:
        標(biāo)簽: 簡(jiǎn)易 VUE 簡(jiǎn)單
        • 熱門焦點(diǎn)

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 亚洲国产精品久久久久| 99精品视频在线观看免费播放| 亚洲美女色在线欧洲美女| 日韩精品一区二区亚洲AV观看| 99热这里有免费国产精品| 一级做a爰片久久毛片免费陪| 亚洲fuli在线观看| 亚洲沟沟美女亚洲沟沟| 亚洲精品乱码久久久久久中文字幕| 性无码免费一区二区三区在线| 成年免费大片黄在线观看com| 亚洲乱码中文字幕在线| 亚洲w码欧洲s码免费| 亚洲免费在线视频| 亚洲国产成人一区二区三区| 精品国产日韩亚洲一区| 免费不卡中文字幕在线| 99精品国产成人a∨免费看| 久久国产一片免费观看| 亚美影视免费在线观看| 亚洲一级片在线播放| 亚洲精品资源在线| 久久亚洲精精品中文字幕| 亚洲av日韩av无码| 日韩精品亚洲专区在线观看| 免费理论片51人人看电影| 最近中文字幕mv免费高清视频7| 国产成人yy免费视频| 皇色在线视频免费网站| 久久久久久国产a免费观看黄色大片| 99ee6热久久免费精品6| 18禁男女爽爽爽午夜网站免费| 久久精品无码专区免费东京热| 99热这里只有精品6免费| 99久久免费中文字幕精品| 18观看免费永久视频| 亚洲国产精品免费观看| 成年18网站免费视频网站| 日韩电影免费在线观看视频 | 亚洲一级特黄无码片| 国色精品卡一卡2卡3卡4卡免费|