1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Svelte 原理淺析與評測

        共 4983字,需瀏覽 10分鐘

         ·

        2021-12-29 17:04

        ????

        簡介

        Svelte 是一個構建 web 應用程序的工具,與 React 和 Vue 等 JavaScript 框架類似,都懷揣著一顆讓構建交互式用戶界面變得更容易的心。

        但是有一個關鍵的區(qū)別:Svelte 在 構建/編譯階段 將你的應用程序轉換為理想的 JavaScript 應用,而不是在 運行階段 解釋應用程序的代碼。這意味著你不需要為框架所消耗的性能付出成本,并且在應用程序首次加載時沒有額外損失。

        你可以使用 Svelte 構建整個應用程序,也可以逐步將其融合到現(xiàn)有的代碼中。你還可以將組件作為獨立的包(package)交付到任何地方,并且不會有傳統(tǒng)框架所帶來的額外開銷。

        特點

        代碼簡潔

        我們以一個簡單例子來說明,在輸入框中輸入內(nèi)容,然后在彈窗中顯示相關內(nèi)容。然后將svelte的代碼與react、vue作一下對比,可以很明顯的發(fā)現(xiàn),svelte要寫的代碼量遠少于react和vue。

        • 使用svelte,代碼如下:




        type="text"?bind:value={animal}?/>

        彈出
        • react:
        import?React,?{?useState?}?from?'react';



        export?default?function?App()?{

        ??const?[animal,?setAnimal]?=?useState('dog');

        ??const?showModal?=?()?=>?{

        ????alert(`My?favorite?animal?is?${animal}`);

        ??};

        ??return?(

        ????<>

        ??????
        ????????type="text"

        ????????value={animal}

        ????????onChange={()?=>?{

        ??????????setAnimal(animal);

        ????????}}

        ??????/>

        ??????彈出

        ????

        ??);

        }
        • Vue





        無虛擬dom

        Svelte 能夠?qū)⒋a編譯成體積小不依賴框架的普通js代碼,讓應用程序無論是啟動還是運行都很迅速。

        性能更好

        許多同學在學習 react 或者 vue 時可能聽說過諸如“虛擬dom很快”之類的言論,所以看到這里就會疑惑,svelte 沒有虛擬dom,為什么反而更快呢?

        這其實是一個誤區(qū),react 和 vue 等框架實現(xiàn)虛擬 dom 的最主要的目的不是性能,而是為了掩蓋底層 dom 操作,讓用戶通過聲明式的、基于狀態(tài)驅(qū)動UI的方式去構建我們的應用程序,提高代碼的可維護性。

        另外 react 或者 vue 所說的虛擬 dom 的性能好,是指我們在沒有對頁面做特殊優(yōu)化的情況下,框架依然能夠提供不錯的性能保障。例如以下場景,我們每次從服務端接收數(shù)據(jù)后就重新渲染列表,如果我們通過普通dom操作不做特殊優(yōu)化,每次都重新渲染所有列表項,性能消耗比較高。而像react等框架會通過key對列表項做標記,只對發(fā)生變化的列表項重新渲染,如此一來性能便提高了。

        思考上面這個場景,如果我們操作真實dom時也對列表項做標記,只對發(fā)生變化的列表項重新渲染,省去了虛擬dom diff等環(huán)節(jié),那么性能是比虛擬dom還要高的。

        svelte便實現(xiàn)了這種優(yōu)化,通過將數(shù)據(jù)和真實dom的映射關系,在編譯的時候通過 ast 計算并保存起來,數(shù)據(jù)發(fā)生變動時直接更新dom,由于不依賴虛擬dom,初始化和更新時都都十分迅速。

        體積更小?

        我們都知道 react 和 vue 都是基于運行時的框架,打包后除了用戶自己編寫的代碼之外,還有框架本身的 runtime。而 svelte 是通過靜態(tài)編譯減少框架運行時的代碼量。

        https://www.npmtrends.com/react-vs-react-dom-vs-vue-vs-svelte[1]

        參照 npm trends,react、vue和svelte的 minzipped 體積分別為:42.2kb、22.9kb和1.6kb,足以看出 svelte 的短小精悍。

        但是上面這個單看框架的體積稍微有些片面,svelte 由于在編譯時將組件直接解釋為 js,所以相對來說組件編譯后的代碼量會比 vue 和 react 編譯后要大一些。假如有 n 個組件,svelte 每個組件編譯后個規(guī)模為 a,vue 或者 react 每個組件編譯后的規(guī)模為 b:

        • svelte:體積 = a * n
        • vue:體積 = S + b * n (S為框架運行時的體積)

        在 a > b 的情況下,隨著 n 的數(shù)量的增多,svelte 項目在體積上并不會占據(jù)太大的優(yōu)勢。

        vue 對比

        Vue 方面,尤雨溪曾將 vue3 和 svelte 做了對比:https://github.com/yyx990803/vue-svelte-size-analysis[2]

        基于真實的 todomvc 場景構建組件,編譯以后Svelte 的組件輸出大小是Vue的1.7倍,在 SSR 的情況下,這一比例會上升到2.1倍。在不開啟 SSR 的情況下,大概19個組件后就會抹平運行時體積的大小差異,開啟 SSR 的情況下,大概 13 個組件后就會抹平差異。

        與 react 對比

        Jacek Schae 也曾將 svelte 和 react進行對比,也是在組件數(shù)量達到一定的閾值之后, svelte 的體積優(yōu)勢就不再存在。


        可見,大型項目中使用 svelte 的體積問題還有待考究。

        真正的reactivity

        無需復雜的狀態(tài)管理庫,Svelte 為 JavaScript 自身添加反應能力。后面的源碼解讀部分會講解 svelte 的響應式實現(xiàn)。

        發(fā)展趨勢

        Svelte 是 Rich Harris[3] (rollup 作者),2016 年 svelte 開始開源, 2019 年開始引起較為廣泛的關注。

        Github 上 svelte[4] 現(xiàn)在是 49.9k star:

        Npm 上 svelte[5] 的周下載量大概在 15w 左右:


        雖然從 star 數(shù)和下載量來說離 react、vue 和 angular 還有較大差距,但是鑒于其出道比較晚也是可以理解。而且從框架的調(diào)研[6]來看,近兩年來其用戶滿意度和感興趣度都是高居第一,使用和知名度也是在急速上升的。


        總體來看,未來可期!

        源碼解讀

        svelte 的源碼由兩大部分組成,compiler 和 runtime。compiler 的作用是將 svelte 模版語法編譯為瀏覽器能夠識別的js SvelteComponent,而 runtime 則是在瀏覽器中幫助業(yè)務代碼運作的運行時函數(shù)。

        complier

        Svelte 如其介紹所說,在 complier 階段完成了大部分的工作,而 complier 又分為 parse 和 complie 兩部分:

        parse

        parse 會讀取 .svelte 文件的內(nèi)容進行解析。

        • 對于 html 部分的內(nèi)容,會分為 tag、mustache、text 三種解析類型:

          • tag: tag 解析的內(nèi)容以 < 作為標識,包括 HTMLElement、style標簽、script標簽以及用戶自定義的 svelte 組件以及 svelte 實現(xiàn)的一些特殊標簽如 svelte:head 、svelte:optionssvelte:window 以及svelte:body 等。
          • muscache:mustache 以 { 作為標識,識別的內(nèi)容除了模板語法之外,還包括 svelte 的邏輯渲染(else……if、each)等語法、{``@html``}、{``@debug} 等。
          • text就是無語義的靜態(tài)文本

        最終parse會將.svelte 的內(nèi)容解析成含有 htmlcss 、instance 、module 四部分的ast。

        Instance 是指 script 標簽中響應式的屬性和方法,module 是使用





        ??增加

        ??

        count?is:?{count}




        svelte編譯后的結果為:

        /*?App.svelte?generated?by?Svelte?v3.42.4?*/

        import?{

        ??SvelteComponent,

        ??append,

        ??detach,

        ??element,

        ??init,

        ??insert,

        ??listen,

        ??noop,

        ??safe_not_equal,

        ??set_data,

        ??space,

        ??text,

        }?from?'svelte/internal';



        function?create_fragment(ctx)?{

        ??let?div;

        ??let?button;

        ??let?t1;

        ??let?p;

        ??let?t2;

        ??let?t3;

        ??let?mounted;

        ??let?dispose;



        ??return?{

        ????c()?{

        ??????div?=?element('div');

        ??????button?=?element('button');

        ??????button.textContent?=?'增加';

        ??????t1?=?space();

        ??????p?=?element('p');

        ??????t2?=?text('count?is:?');

        ??????t3?=?text(/*count*/?ctx[0]);

        ????},

        ????m(target,?anchor)?{

        ??????insert(target,?div,?anchor);

        ??????append(div,?button);

        ??????append(div,?t1);

        ??????append(div,?p);

        ??????append(p,?t2);

        ??????append(p,?t3);



        ??????if?(!mounted)?{

        ????????dispose?=?listen(button,?'click',?/*addCount*/?ctx[1]);

        ????????mounted?=?true;

        ??????}

        ????},

        ????p(ctx,?[dirty])?{

        ??????if?(dirty?&?/*count*/?1)?set_data(t3,?/*count*/?ctx[0]);

        ????},

        ????i:?noop,

        ????o:?noop,

        ????d(detaching)?{

        ??????if?(detaching)?detach(div);

        ??????mounted?=?false;

        ??????dispose();

        ????},

        ??};

        }



        function?instance($$self,?$$props,?$$invalidate)?{

        ??let?count?=?0;



        ??const?addCount?=?()?=>?{

        ????$$invalidate(0,?(count?+=?1));

        ??};



        ??return?[count,?addCount];

        }



        class?App?extends?SvelteComponent?{

        ??constructor(options)?{

        ????super();

        ????init(this,?options,?instance,?create_fragment,?safe_not_equal,?{});

        ??}

        }



        export?default?App;

        我們看到編譯后的結果中,有一個 create_fragement 的方法和 instance 方法。

        另外還從 svelte/internal 引入了 appenddetach 、element 、insertlisten 等方法,從源碼[7]中可以知道都是一些很簡單的對原生 dom 操作的封裝。

        create_fragment

        create_fragment 是和每個組件生成 dom 相關的方法,里面定義了 c 、 m 、p 、i 、o 、d 等一系列內(nèi)置方法,從縮寫上不好理解,我們可以看下源碼[8]中其類型定義:

        export?interface?Fragment?{

        ??key:?string|null;

        ??first:?null;

        ??/*?create??*/?c:?()?=>?void;

        ??/*?claim???*/?l:?(nodes:?any)?=>?void;

        ??/*?hydrate?*/?h:?()?=>?void;

        ??/*?mount???*/?m:?(target:?HTMLElement,?anchor:?any)?=>?void;

        ??/*?update??*/?p:?(ctx:?any,?dirty:?any)?=>?void;

        ??/*?measure?*/?r:?()?=>?void;

        ??/*?fix?????*/?f:?()?=>?void;

        ??/*?animate?*/?a:?()?=>?void;

        ??/*?intro???*/?i:?(local:?any)?=>?void;

        ??/*?outro???*/?o:?(local:?any)?=>?void;

        ??/*?destroy?*/?d:?(detaching:?0|1)?=>?void;

        }
        • c(create):create 函數(shù)里會對一系列的 dom 節(jié)點進行創(chuàng)建,并將它們保存在 create_fragment 的閉包中。
        • m(mount):mount 函數(shù)中會對根據(jù) dom 節(jié)點的層級關系,構建 dom 樹,并將 dom 樹插入到頁面 target 節(jié)點下。同時還會將 dom 上相關的事件監(jiān)聽進行綁定,然后將組件的 mounted 狀態(tài)置為 true。
        • p(update):組件的狀態(tài)發(fā)生改變時會觸發(fā) update 函數(shù),對 dom 中相應的數(shù)據(jù)重新進行更新渲染。
        • d(distroy):將 dom 掛載從頁面中移除,將組件的 mounted 狀態(tài)置為 false,同時移除一系列的事件監(jiān)聽。

        instance

        instance 方法中返回了包含組件實例中屬性和方法的數(shù)組,將相應的數(shù)據(jù)綁定在組件實例的 $$.ctx 上,并且根據(jù)用戶定義的觸發(fā)屬性修改的方法去調(diào)用一個 $$invalidate方法,我們來看下$$invalidate這個方法干了什么:

        • $$.instance:
        instance(component,?options.props?||?{},?(i,?ret,?...rest)?=>?{

        ??const?value?=?rest.length???rest[0]?:?ret;

        ??if?($$.ctx?&&?not_equal($$.ctx[i],?($$.ctx[i]?=?value)))?{

        ????if?(!$$.skip_bound?&&?$$.bound[i])?$.bound[i](value?"i]?"i])?$.bound[i")?$.bound[i");

        ????if?(ready)?make_dirty(component,?i);

        ??}

        ??return?ret;

        });

        $$invalidate接收2個或更多參數(shù)。第一個參數(shù)是 i 是 屬性在 $$.ctx,第二個參數(shù) ret 是定義的屬性改變的邏輯函數(shù)。然后判斷屬性重新賦值后與之前的值是否相等,若不相等,則會調(diào)用 make_dirty 更新相關的 ui。

        臟檢測

        • make_dirty:
        function?make_dirty(component,?i)?{

        ??if?(component.$$.dirty[0]?===?-1)?{

        ????dirty_components.push(component);

        ????schedule_update();

        ????component.$$.dirty.fill(0);

        ??}

        ??component.$$.dirty[(i?/?31)?|?0]?|=?(1?<
        }

        每個組件的 $$ 屬性上有一個 dirty 數(shù)組,用于標記 instance 中需要更新的屬性下標,當 dirty 第一項為 -1 時,表示這個組件當前是干凈的,將其 push 到 dirty_components 中,然后執(zhí)行 schedule_update 方法。

        • schedule_update:

        schedule_update 中會異步去執(zhí)行 flush 函數(shù):

        export?function?schedule_update()?{

        ??if?(!update_scheduled)?{

        ????update_scheduled?=?true;

        ????resolved_promise.then(flush);

        ??}

        }
        • flush:

        flush 中對剛剛的 dirty_components 進行遍歷,執(zhí)行 update 函數(shù).

        for?(let?i?=?0;?i?
        ??const?component?=?dirty_components[i];

        ??set_current_component(component);

        ??update(component.$$);

        }
        • Update

        update函數(shù)會調(diào)用組件 update 生命周期鉤子函數(shù),將 dirty 數(shù)組重新置為 -1,然后調(diào)用 fragment 的 p(update) 去更新ui。

        function?update($$)?{

        ??if?($$.fragment?!==?null)?{

        ????$$.update();

        ????run_all($$.before_update);

        ????const?dirty?=?$$.dirty;

        ????$$.dirty?=?[-1];

        ????$$.fragment?&&?$$.fragment.p($$.ctx,?dirty);



        ????$$.after_update.forEach(add_render_callback);

        ??}

        }
        • dirty 標記

        回到上面的 make_dirty 方法,svelte 是通過如下操作對屬性進行臟標記的:

        component.$$.dirty[(i?/?31)?|?0]?|=?(1?<

        了解了位運算,那我們看上面臟標記的代碼,(i / 31) | 0 對每個 instance 返回的數(shù)組下標除以 31 后和 0 做或運算,即除 31 向下取整,(1 << (i % 31)) i 對 31 取余之后向左進行移位操作。通過上述的兩步操作,可以了解到 dirty 數(shù)組存儲了一系列的 32 位整數(shù),通過這一操作,提高了內(nèi)存利用率,每個數(shù)組項可以存儲31個屬性是否需要更新。

        例如如下32位的整數(shù)43,對應的32位二進制為:

        Dirty = [43]

        43 -> 0000 0000 0000 0000 0000 0000 0010 1011

        二進制中為1的位代表需要更新的 instance 中數(shù)組第幾項,即第1、2、4、6項屬性需要更新。

        整體流程

        周邊生態(tài)

        狀態(tài)管理

        Svelte 框架中自己實現(xiàn)了 store[9],無需安裝單獨的狀態(tài)管理庫。

        路由

        Svelte 官方目前沒有自己的路由,社區(qū)實現(xiàn)的路由庫:

        • svelte-routing[10],1.4k star,npm 周下載量 3.5k,和 react-router-dom 的使用方式很像
        • svelte-spa-router[11],886 star,npm 周下載量 3.5k,更類似于 vue-router 的使用方式

        SSR

        • sveltekit[12]

        目前官方主推的 ssr 框架,具備以下的特點:

        • 服務端渲染(SSR)
        • 路由
        • typescript支持
        • less, scss支持
        • serverless
        • vite打包
        • Sapper[13]

        sapper開發(fā)比較早,也是官方的 ssr 框架,但是 Rich Harris 在2020年10月的svelte峰會上表示:sapper永遠不會發(fā)布1.0版本。也就是說 sapper 不會發(fā)布穩(wěn)定版甚至被放棄,而 svelte kit 則是它的繼任者。

        跨平臺

        Svelte 偏向于性能,目前在跨平臺方面還沒有進行探究。

        • native

        svelte-native[14] (社區(qū)庫)

        • 小程序

        暫不支持

        • 桌面應用

        可以 electron[15] 結合開發(fā)桌面應用

        組件庫

        Svelte 現(xiàn)在組件庫數(shù)量尚可,但是都不夠完備,如 table 等復雜組件都沒有實現(xiàn)

        • svelte-material-ui[16] 1.8k star,扁平化設計,偏向 google 的 UI 設計系統(tǒng)。
        • carbon-components-svelte[17] 1k star,偏商務,非主流,偏向 IBM 的開源設計系統(tǒng)。
        • smelte[18] 1k star,material 的另一種方案

        測試工具

        缺少官方的測試工具,社區(qū)單元測試庫:

        svelte-testing-library[19]

        總結來說,svelte 的周邊生態(tài)目前還不夠完備,但由于起步較晚可以理解。

        VSCode插件

        • Svelte for VS Code[20] 識別 svelte 語法,對 svelte 進行語法高亮
        • Svelte 3 Snippets[21] 在 vscode 中通過簡寫快速生成 svelte 相關語法
        • Svelte Intellisense[22] svelte 組件 command + click 快速跳轉及 svelte 引入省略 .svelte 文件后綴

        Typescript

        支持

        CSS 預處理器

        支持 less、 scss 及 postcss

        使用 svelte 構建 web component

        我們平臺組最近正在進行 web component 組件庫開發(fā)的選型調(diào)研,svelte 也作為備選的框架之一。傳統(tǒng)的框架如 vue、react 如果想要開發(fā)web component,需要每個組件都打包一份體積龐大的運行時,而 svelte 的運行時會根據(jù)你的功能按需引入,所以十分適合 web component 的開發(fā)場景。

        配置

        下面是通過 svelte 開發(fā)一個簡單的 web component 的實例:

        1. 通過官方提供的腳手架創(chuàng)建一個組件
        npx?degit?sveltejs/component-template?custom-test-button
        1. 修改相關的文件配置:

        修改 package.json 包名稱

        {

        ??"name":?"CustomTestButton",

        ??"svelte":?"src/index.js",

        ??"module":?"dist/index.mjs",

        ??"main":?"dist/index.js",

        ??"scripts":?{

        ????"build":?"rollup?-c",

        ????"prepublishOnly":?"npm?run?build"

        ??},

        ??"devDependencies":?{

        ????"@rollup/plugin-node-resolve":?"^9.0.0",

        ????"rollup":?"^2.0.0",

        ????"rollup-plugin-svelte":?"^6.0.0",

        ????"svelte":?"^3.0.0"

        ??},

        ??"keywords":?[

        ????"svelte"

        ??],

        ??"files":?[

        ????"src",

        ????"dist"

        ??]

        }

        修改 rollup.config.js 文件的內(nèi)容:

        import?svelte?from?'rollup-plugin-svelte';

        import?resolve?from?'@rollup/plugin-node-resolve';

        import?pkg?from?'./package.json';



        const?name?=?pkg.name

        ??.replace(/^(@\S+/)?(svelte-)?(\S+)/,?'$3')

        ??.replace(/^\w/,?(m)?=>?m.toUpperCase())

        ??.replace(/-\w/g,?(m)?=>?m[1].toUpperCase());



        export?default?{

        ??input:?'src/index.js',

        ??output:?[

        ????{?file:?pkg.module,?format:?'es'?},

        ????{?file:?pkg.main,?format:?'umd',?name?},

        ??],

        ??plugins:?[svelte({?customElement:?true?}),?resolve()],

        };
        1. 增加組件內(nèi)容

        如下定義了一個組件內(nèi)容

        "custom-test-button"?/>







        ${type}`}>{value}




        1. 在項目目錄執(zhí)行 npm run build 將組件打包,假設打包后的文件為 index.js

        使用

        • Html 引入


        "en">

        ??

        ????"UTF-8"?/>

        ????"X-UA-Compatible"?content="IE=edge"?/>

        ????"viewport"?content="width=device-width,?initial-scale=1.0"?/>

        ????svelte?web?component

        ??

        ??

        ??????

        ????"測試按鈕"?type="danger">

        ??


        • vue 引入

        在 vue 的 html 中引入 index.js



        ????

        ????"app">


        ????

        ????

        ??

        然后在 vue 組件中使用


        • React 中引入

        同 vue,就不做過多介紹了

        總結

        整體來說,svelte 繼前端三大框架之后推陳出新,以一種新的思路實現(xiàn)了響應式,由于起步時間不算很長,目前來說其生態(tài)還不夠完備, 在大型項目中的應用目前也還有待考究,但是在一些簡單頁面如活動頁、靜態(tài)頁等場景感覺目前還是十分適合的,個人對其未來發(fā)展表示看好。

        由于其簡潔的語法以及與 vue 語法相似的特點,上手成本十分小,大家感興趣可以稍花一點點時間了解一下,豐富自己的武器庫。

        參考資料

        [1]

        https://www.npmtrends.com/react-vs-react-dom-vs-vue-vs-svelte: https://www.npmtrends.com/react-vs-react-dom-vs-vue-vs-svelte

        [2]

        https://github.com/yyx990803/vue-svelte-size-analysis: https://github.com/yyx990803/vue-svelte-size-analysis

        [3]

        Rich Harris: https://github.com/Rich-Harris

        [4]

        svelte: https://github.com/sveltejs/svelte

        [5]

        svelte: https://www.npmjs.com/package/svelte

        [6]

        調(diào)研: https://2020.stateofjs.com/en-US/technologies/front-end-frameworks/

        [7]

        源碼: https://github.com/sveltejs/svelte/blob/master/src/runtime/internal/dom.ts

        [8]

        源碼: https://github.com/sveltejs/svelte/blob/master/src/runtime/internal/Component.ts

        [9]

        store: https://github.com/sveltejs/svelte/blob/master/src/runtime/store/index.ts

        [10]

        svelte-routing: https://github.com/EmilTholin/svelte-routing

        [11]

        svelte-spa-router: https://github.com/italypaleale/svelte-spa-router

        [12]

        sveltekit: https://github.com/sveltejs/kit

        [13]

        Sapper: https://github.com/sveltejs/sapper

        [14]

        svelte-native: https://github.com/halfnelson/svelte-native

        [15]

        electron: https://github.com/electron/electron

        [16]

        svelte-material-ui: https://github.com/hperrin/svelte-material-ui

        [17]

        carbon-components-svelte: https://github.com/carbon-design-system/carbon-components-svelte

        [18]

        smelte: https://smeltejs.com/

        [19]

        svelte-testing-library: https://github.com/testing-library/svelte-testing-library

        [20]

        Svelte for VS Code: https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode

        [21]

        Svelte 3 Snippets: https://marketplace.visualstudio.com/items?itemName=fivethree.vscode-svelte-snippets

        [22]

        Svelte Intellisense: https://marketplace.visualstudio.com/items?itemName=ardenivanov.svelte-intellisense


        ???

        便內(nèi),^_^

        ?、?~

        ?ELab?~

        節(jié)跳,,節(jié)產(chǎn)發(fā)。

        產(chǎn)質(zhì)、發(fā)創(chuàng)沿業(yè)業(yè)經(jīng)。監(jiān)、、、Serverless、產(chǎn)內(nèi)。

        節(jié)/內(nèi):?CBP6BY9

        :?https://job.toutiao.com/s/8RWRqsn



        瀏覽 56
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产精品日日躁夜夜躁欧美 | 免费干逼| 黄色小说在线免费 | 人人插人人爽 | 久久屄| 最近中文字幕免费mv第一季歌词強上 | 亚洲专区免费 | 91香蕉视频网 | 操逼三级 | 扛起美妇雪白双腿进入在线观看 |