<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于webpack搭建前端工程解決方案探索

          共 3833字,需瀏覽 8分鐘

           ·

          2020-10-05 16:13

          作者:dmyang

          來源:SegmentFault 思否社區(qū)




          本篇主要介紹webpack的基本原理以及基于webpack搭建純靜態(tài)頁面型前端項目工程化解決方案的思路。


          關于前端工程


          下面是百科關于“軟件工程”的名詞解釋:

          軟件工程是一門研究用工程化方法構建和維護有效的、實用的和高質(zhì)量的軟件的學科。


          其中,工程化是方法,是將軟件研發(fā)的各個鏈路串接起來的工具。


          對于軟件“工程化”,個人以為至少應當有如下特點:


          • 有IDE的支持,負責初始化工程、工程結構組織、debug、編譯、打包等工作
          • 有固定或者約定的工程結構,規(guī)定軟件所依賴的不同類別的資源的存放路徑甚至代碼的寫法等
          • 軟件依賴的資源可能來自軟件開發(fā)者,也有可能是第三方,工程化需要集成對資源的獲取、打包、發(fā)布、版本管理等能力
          • 和其他系統(tǒng)的集成,如CI系統(tǒng)、運維系統(tǒng)、監(jiān)控系統(tǒng)等

          廣泛意義上講,前端也屬于軟件工程的范疇。


          但前端沒有Eclipse、Visual Studio等為特定語言量身打造的IDE。因為前端不需要編譯,即改即生效,在開發(fā)和調(diào)試時足夠方便,只需要打開個瀏覽器即可完成,所以前端一般不會扯到“工程”這個概念。


          在很長一段時間里,前端很簡單,比如下面簡單的幾行代碼就能夠成一個可運行前端應用:





          ????webapp
          ????"stylesheet"?href="app.css">


          ????

          app?title


          ????



          但隨著webapp的復雜程度不斷在增加,前端也在變得很龐大和復雜,按照傳統(tǒng)的開發(fā)方式會讓前端失控:代碼龐大難以維護、性能優(yōu)化難做、開發(fā)成本變高。


          感謝Node.js,使得JavaScript這門前端的主力語言突破了瀏覽器環(huán)境的限制可以獨立運行在OS之上,這讓JavaScript擁有了文件IO、網(wǎng)絡IO的能力,前端可以根據(jù)需要任意定制研發(fā)輔助工具。


          一時間出現(xiàn)了以Grunt、Gulp為代表的一批前端構建工具,“前端工程”這個概念逐漸被強調(diào)和重視。但是由于前端的復雜性和特殊性,前端工程化一直很難做,構建工具有太多局限性。


          誠如 張云龍@fouber?所言:

          前端是一種特殊的GUI軟件,它有兩個特殊性:一是前端由三種編程語言組成,二是前端代碼在用戶端運行時增量安裝。


          html、css和js的配合才能保證webapp的運行,增量安裝是按需加載的需要。開發(fā)完成后輸出三種以上不同格式的靜態(tài)資源,靜態(tài)資源之間有可能存在互相依賴關系,最終構成一個復雜的資源依賴樹(甚至網(wǎng))。


          所以,前端工程,最起碼需要解決以下問題:


          • 提供開發(fā)所需的一整套運行環(huán)境,這和IDE作用類似
          • 資源管理,包括資源獲取、依賴處理、實時更新、按需加載、公共模塊管理等
          • 打通研發(fā)鏈路的各個環(huán)節(jié),debug、mock、proxy、test、build、deploy等


          其中,資源管理是前端最需要也是最難做的一個環(huán)節(jié)。


          注:個人以為,與前端工程化對應的另一個重要的領域是前端組件化,前者屬于工具,解決研發(fā)效率問題,后者屬于前端生態(tài),解決代碼復用的問題,本篇對于后者不做深入。


          在此以開發(fā)一個多頁面型webapp為例,給出上面所提出的問題的解決方案。




          前端開發(fā)環(huán)境搭建


          主要目錄結構


          -?webapp/???????????????#?webapp根目錄
          ??-?src/????????????????#?開發(fā)目錄
          ????+?css/??????????????#?css資源目錄
          ????+?img/??????????????#?webapp圖片資源目錄
          ????-?js/???????????????#?webapp?js&jsx資源目錄
          ??????-?components/?????#?標準組件存放目錄
          ??????????-?foo/????????#?組件foo
          ????????????+?css/??????#?組件foo的樣式
          ????????????+?js/???????#?組件foo的邏輯
          ????????????+?tmpl/?????#?組件foo的模板
          ????????????index.js????#?組件foo的入口
          ??????????+?bar/????????#?組件bar
          ??????+?lib/????????????#?第三方純js庫
          ??????...???????????????#?根據(jù)項目需要任意添加的代碼目錄
          ????+?tmpl/?????????????#?webapp前端模板資源目錄
          ????a.html??????????????#?webapp入口文件a
          ????b.html??????????????#?webapp入口文件b
          ??-?assets/?????????????#?編譯輸出目錄,即發(fā)布目錄
          ????+?js/???????????????#?編譯輸出的js目錄
          ????+?img/??????????????#?編譯輸出的圖片目錄
          ????+?css/??????????????#?編譯輸出的css目錄
          ????a.html??????????????#?編譯輸出的入口a
          ????b.html??????????????#?編譯處理后的入口b
          ??+?mock/???????????????#?假數(shù)據(jù)目錄
          ??app.js????????????????#?本地server入口
          ??routes.js?????????????#?本地路由配置
          ??webpack.config.js?????#?webpack配置文件
          ??gulpfile.js???????????#?gulp任務配置
          ??package.json??????????#?項目配置
          ??README.md?????????????#?項目說明


          這是個經(jīng)典的前端項目目錄結構,項目目結構在一定程度上約定了開發(fā)規(guī)范。業(yè)務開發(fā)的同學只需關注src目錄即可,開發(fā)時盡可能最小化模塊粒度,這是異步加載的需要。assets是整個工程的產(chǎn)出,無需關注里邊的內(nèi)容是什么,至于怎么打包和解決資源依賴的,往下看。


          本地開發(fā)環(huán)境


          我們使用開源web框架搭建一個webserver,便于本地開發(fā)和調(diào)試,以及靈活地處理前端路由,以koa為例,主要代碼如下:


          //?app.js
          var?http?=?require('http');
          var?koa?=?require('koa');
          var?serve?=?require('koa-static');

          var?app?=?koa();
          var?debug?=?process.env.NODE_ENV?!==?'production';
          //?開發(fā)環(huán)境和生產(chǎn)環(huán)境對應不同的目錄
          var?viewDir?=?debug???'src'?:?'assets';

          //?處理靜態(tài)資源和入口文件
          app.use(serve(path.resolve(__dirname,?viewDir),?{
          ????maxage:?0
          }));

          app?=?http.createServer(app.callback());

          app.listen(3005,?'0.0.0.0',?function()?{
          ????console.log('app?listen?success.');
          });


          運行node app啟動本地server,瀏覽器輸入http://localhost:3005/a.html即可看到頁面內(nèi)容,最基本的環(huán)境就算搭建完成。


          如果只是處理靜態(tài)資源請求,可以有很多的替代方案,如Fiddler替換文件、本地起Nginx服務器等等。搭建一個Web服務器,個性化地定制開發(fā)環(huán)境用于提升開發(fā)效率,如處理動態(tài)請求、dnsproxy(多用于解決移動端配置host的問題)等,總之local webserver擁有無限的可能。


          定制動態(tài)請求


          我們的local server是localhost域,在ajax請求時為了突破前端同源策略的限制,本地server需支持代理其他域下的api的功能,即proxy。同時還要支持對未完成的api進行mock的功能。


          //?app.js
          var?router?=?require('koa-router')();
          var?routes?=?require('./routes');
          routes(router,?app);
          app.use(router.routes());

          //?routes.js
          var?proxy?=?require('koa-proxy');
          var?list?=?require('./mock/list');
          module.exports?=?function(router,?app)?{
          ????//?mock?api
          ????//?可以根據(jù)需要任意定制接口的返回
          ????router.get('/api/list',?function*()?{
          ????????var?query?=?this.query?||?{};
          ????????var?offset?=?query.offset?||?0;
          ????????var?limit?=?query.limit?||?10;
          ????????var?diff?=?limit?-?list.length;

          ????????if(diff?<=?0)?{
          ????????????this.body?=?{code:?0,?data:?list.slice(0,?limit)};
          ????????}?else?{
          ????????????var?arr?=?list.slice(0,?list.length);
          ????????????var?i?=?0;

          ????????????while(diff--)?arr.push(arr[i++]);

          ????????????this.body?=?{code:?0,?data:?arr};
          ????????}
          ????});

          ????//?proxy?api
          ????router.get('/api/foo/bar',?proxy({url:?'http://foo.bar.com'}));
          }




          webpack資源管理


          資源的獲取


          ECMAScript 6之前,前端的模塊化一直沒有統(tǒng)一的標準,僅前端包管理系統(tǒng)就有好幾個。所以任何一個庫實現(xiàn)的loader都不得不去兼容基于多種模塊化標準開發(fā)的模塊。


          webpack同時提供了對CommonJS、AMD和ES6模塊化標準的支持,對于非前三種標準開發(fā)的模塊,webpack提供了shimming modules的功能。


          受Node.js的影響,越來越多的前端開發(fā)者開始采用CommonJS作為模塊開發(fā)標準,npm已經(jīng)逐漸成為前端模塊的托管平臺,這大大降低了前后端模塊復用的難度。


          在webpack配置項里,可以把node_modules路徑添加到resolve search root列表里邊,這樣就可以直接load npm模塊了:


          //?webpack.config.js
          resolve:?{
          ????root:?[process.cwd()?+?'/src',?process.cwd()?+?'/node_modules'],
          ????alias:?{},
          ????extensions:?['',?'.js',?'.css',?'.scss',?'.ejs',?'.png',?'.jpg']
          },
          $?npm?install?jquery?react?--save
          //?page-x.js
          import?$?from?'jquery';
          import?React?from?'react';


          資源引用


          根據(jù)webpack的設計理念,所有資源都是“模塊”,webpack內(nèi)部實現(xiàn)了一套資源加載機制,這與Requirejs、Sea.js、Browserify等實現(xiàn)有所不同,除了借助插件體系加載不同類型的資源文件之外,webpack還對輸出結果提供了非常精細的控制能力,開發(fā)者只需要根據(jù)需要調(diào)整參數(shù)即可:


          //?webpack.config.js
          //?webpack?loaders的配置示例
          ...
          loaders:?[
          ????{
          ????????test:?/\.(jpe?g|png|gif|svg)$/i,
          ????????loaders:?[
          ????????????'image?{bypassOnDebug:?true,?progressive:true,?\
          ????????????????optimizationLevel:?3,?pngquant:{quality:?"65-80"}}'
          ,
          ????????????'url?limit=10000&name=img/[hash:8].[name].[ext]',
          ????????]
          ????},
          ????{
          ????????test:?/\.(woff|eot|ttf)$/i,
          ????????loader:?'url?limit=10000&name=fonts/[hash:8].[name].[ext]'
          ????},
          ????{test:?/\.(tpl|ejs)$/,?loader:?'ejs'},
          ????{test:?/\.js$/,?loader:?'jsx'},
          ????{test:?/\.css$/,?loader:?'style!css'},
          ????{test:?/\.scss$/,?loader:?'style!css!scss'},
          ]
          ...


          簡單解釋下上面的代碼,test項表示匹配的資源類型,loader或loaders項表示用來加載這種類型的資源的loader,loader的使用可以參考using loaders,更多的loader可以參考list of loaders。


          對于開發(fā)者來說,使用loader很簡單,最好先配置好特定類型的資源對應的loaders,在業(yè)務代碼直接使用webpack提供的require(source path)接口即可:


          //?a.js
          //?加載css資源
          require('../css/a.css');

          //?加載其他js資源
          var?foo?=?require('./widgets/foo');
          var?bar?=?require('./widgets/bar');

          //?加載圖片資源
          var?loadingImg?=?require('../img/loading.png');

          var?img?=?document.createElement('img');

          img.src?=?loadingImg;


          注意,require()還支持在資源path前面指定loader,即require(![loaders list]![source path])形式:


          require("!style!css!less!bootstrap/less/bootstrap.less");
          //?“bootstrap.less”這個資源會先被"less-loader"處理,
          //?其結果又會被"css-loader"處理,接著是"style-loader"
          //?可類比pipe操作


          require()時指定的loader會覆蓋配置文件里對應的loader配置項。


          資源依賴處理


          通過loader機制,可以不需要做額外的轉(zhuǎn)換即可加載瀏覽器不直接支持的資源類型,如.scss、.less、.json、.ejs等。


          但是對于css、js和圖片,采用webpack加載和直接采用標簽引用加載,有何不同呢?


          運行webpack的打包命令,可以得到a.js的輸出的結果:


          webpackJsonp([0],?{
          ????/***/0:
          ????/***/function(module,?exports,?__webpack_require__)?{

          ????????__webpack_require__(6);

          ????????var?foo?=?__webpack_require__(25);
          ????????var?bar?=?__webpack_require__(26);

          ????????var?loadingImg?=?__webpack_require__(24);
          ????????var?img?=?document.createElement('img');

          ????????img.src?=?loadingImg;
          ????},

          ????/***/6:
          ????/***/function(module,?exports,?__webpack_require__)?{
          ????????...
          ????},

          ????/***/7:
          ????/***/function(module,?exports,?__webpack_require__)?{
          ????????...
          ????},

          ????/***/24:
          ????/***/function(module,?exports)?{
          ????????...
          ????},

          ????/***/25:
          ????/***/function(module,?exports)?{
          ????????...
          ????},

          ????/***/26:
          ????/***/function(module,?exports)?{
          ????????...
          ????}
          });


          從輸出結果可以看到,webpack內(nèi)部實現(xiàn)了一個全局的webpackJsonp()用于加載處理后的資源,并且webpack把資源進行重新編號,每一個資源成為一個模塊,對應一個id,后邊是模塊的內(nèi)部實現(xiàn),而這些操作都是webpack內(nèi)部處理的,使用者無需關心內(nèi)部細節(jié)甚至輸出結果。


          上面的輸出代碼,因篇幅限制刪除了其他模塊的內(nèi)部實現(xiàn)細節(jié),完整的輸出請看a.out.js,來看看圖片的輸出:


          /***/24:
          /***/function(module,?exports)?{

          ????module.exports?=?"data:image/png;base64,...";

          ????/***/
          }


          注意到圖片資源的loader配置:


          {
          ????test:?/\.(jpe?g|png|gif|svg)$/i,
          ????loaders:?[
          ????????'image?...',
          ????????'url?limit=10000&name=img/[hash:8].[name].[ext]',
          ????]
          }


          意思是,圖片資源在加載時先壓縮,然后當內(nèi)容size小于~10KB時,會自動轉(zhuǎn)成base64的方式內(nèi)嵌進去,這樣可以減少一個HTTP的請求。當圖片大于10KB時,則會在img/下生成壓縮后的圖片,命名是[hash:8].[name].[ext]的形式。hash:8的意思是取圖片內(nèi)容hashsum值的前8位,這樣做能夠保證引用的是圖片資源的最新修改版本,保證瀏覽器端能夠即時更新。


          對于css文件,默認情況下webpack會把css content內(nèi)嵌到js里邊,運行時會使用style標簽內(nèi)聯(lián)。如果希望將css使用link標簽引入,可以使用ExtractTextPlugin插件進行提取。


          資源的編譯輸出


          webpack的三個概念:模塊(module)、入口文件(entry)、分塊(chunk)。


          其中,module指各種資源文件,如js、css、圖片、svg、scss、less等等,一切資源皆被當做模塊。


          webpack編譯輸出的文件包括以下2種:


          • entry:入口,可以是一個或者多個資源合并而成,由html通過script標簽引入
          • chunk:被entry所依賴的額外的代碼塊,同樣可以包含一個或者多個文件

          下面是一段entry和output項的配置示例:


          entry:?{
          ????a:?'./src/js/a.js'
          },
          output:?{
          ????path:?path.resolve(debug???'__build'?:?'./assets/'),
          ????filename:?debug???'[name].js'?:?'js/[chunkhash:8].[name].min.js',
          ????chunkFilename:?debug???'[chunkhash:8].chunk.js'?:?'js/[chunkhash:8].chunk.min.js',
          ????publicPath:?debug???'/__build/'?:?''
          }


          其中entry項是入口文件路徑映射表,output項是對輸出文件路徑和名稱的配置,占位符如[id]、[chunkhash]、[name]等分別代表編譯后的模塊id、chunk的hashnum值、chunk名等,可以任意組合決定最終輸出的資源格式。hashnum的做法,基本上弱化了版本號的概念,版本迭代的時候chunk是否更新只取決于chnuk的內(nèi)容是否發(fā)生變化。


          細心的同學可能會有疑問,entry表示入口文件,需要手動指定,那么chunk到底是什么,chunk是怎么生成的?


          在開發(fā)webapp時,總會有一些功能是使用過程中才會用到的,出于性能優(yōu)化的需要,對于這部分資源我們希望做成異步加載,所以這部分的代碼一般不用打包到入口文件里邊。


          對于這一點,webpack提供了非常好的支持,即code splitting,即使用require.ensure()作為代碼分割的標識。


          例如某個需求場景,根據(jù)url參數(shù),加載不同的兩個UI組件,示例代碼如下:


          var?component?=?getUrlQuery('component');

          if('dialog'?===?component)?{
          ????require.ensure([],?function(require)?{
          ????????var?dialog?=?require('./components/dialog');
          ????????//?todo?...
          ????});
          }

          if('toast'?===?component)?{
          ????require.ensure([],?function(require)?{
          ????????var?toast?=?require('./components/toast');
          ????????//?todo?...
          ????});
          }


          url分別輸入不同的參數(shù)后得到瀑布圖:



          webpack將require.ensure()包裹的部分單獨打包了,即圖中看到的[hash].chunk.js,既解決了異步加載的問題,又保證了加載到的是最新的chunk的內(nèi)容。


          假設app還有一個入口頁面b.html,那麼就需要相應的再增加一個入口文件b.js,直接在entry項配置即可。多個入口文件之間可能公用一個模塊,可以使用CommonsChunkPlugin插件對指定的chunks進行公共模塊的提取,下面代碼示例演示提取所有入口文件公用的模塊,將其獨立打包:


          var?chunks?=?Object.keys(entries);

          plugins:?[
          ????new?CommonsChunkPlugin({
          ????????name:?'vendors',?//?將公共模塊提取,生成名為`vendors`的chunk
          ????????chunks:?chunks,
          ????????minChunks:?chunks.length?//?提取所有entry共同依賴的模塊
          ????})
          ],


          資源的實時更新


          引用模塊,webpack提供了require()API(也可以通過添加bable插件來支持ES6的import語法)。但是在開發(fā)階段不可能改一次編譯一次,webpack提供了強大的熱更新支持,即HMR(hot module replace)。


          HMR簡單說就是webpack啟動一個本地webserver(webpack-dev-server),負責處理由webpack生成的靜態(tài)資源請求。注意webpack-dev-server是把所有資源存儲在內(nèi)存的,所以你會發(fā)現(xiàn)在本地沒有生成對應的chunk訪問卻正常。


          下面這張來自webpack官網(wǎng)的圖片,可以很清晰地說明module、entry、chunk三者的關系以及webpack如何實現(xiàn)熱更新的:



          enter0表示入口文件,chunk1~4分別是提取公共模塊所生成的資源塊,當模塊4和9發(fā)生改變時,因為模塊4被打包在chunk1中,模塊9打包在chunk3中,所以HMR runtime會將變更部分同步到chunk1和chunk3中對應的模塊,從而達到hot replace。


          webpack-dev-server的啟動很簡單,配置完成之后可以通過cli啟動,然后在頁面引入入口文件時添加webpack-dev-server的host即可將HMR集成到已有服務器:


          ...

          ????...
          ????
          ????

          ...


          因為我們的local server就是基于Node.js的webserver,這里可以更進一步,將webpack開發(fā)服務器以中間件的形式集成到local webserver,不需要cli方式啟動(少開一個cmd tab):


          //?app.js
          var?webpackDevMiddleware?=?require('koa-webpack-dev-middleware');
          var?webpack?=?require('webpack');
          var?webpackConf?=?require('./webpack.config');

          app.use(webpackDevMiddleware(webpack(webpackConf),?{
          ????contentBase:?webpackConf.output.path,
          ????publicPath:?webpackConf.output.publicPath,
          ????hot:?true,
          ????stats:?webpackConf.devServer.stats
          }));


          啟動HMR之后,每次保存都會重新編譯生成新的chnuk,通過控制臺的log,可以很直觀地看到這一過程:



          公用代碼的處理:封裝組件


          webpack解決了資源依賴的問題,這使得封裝組件變得很容易,例如:


          //?js/components/component-x.js
          require('./component-x.css');

          //?@see?https://github.com/okonet/ejs-loader
          var?template?=?require('./component-x.ejs');
          var?str?=?template({foo:?'bar'});

          function?someMethod()?{}

          exports.someMethod?=?someMethod;


          使用:


          //?js/a.js
          import?{someMethod}?from?"./components/component-x";
          someMethod();


          正如開頭所說,將三種語言、多種資源合并成js來管理,大大降低了維護成本。


          對于新開發(fā)的組件或library,建議推送到npm倉庫進行共享。如果需要支持其他加載方式(如RequireJS或標簽直接引入),可以參考webpack提供的externals項。


          資源路徑切換


          由于入口文件是手動使用script引入的,在webpack編譯之后入口文件的名稱和路徑一般會改變,即開發(fā)環(huán)境和生產(chǎn)環(huán)境引用的路徑不同:


          //?開發(fā)環(huán)境
          //?a.html


          //?生產(chǎn)環(huán)境
          //?a.html



          webpack提供了HtmlWebpackPlugin插件來解決這個問題,HtmlWebpackPlugin支持從模板生成html文件,生成的html里邊可以正確解決js打包之后的路徑、文件名問題,配置示例:


          //?webpack.config.js
          plugins:?[
          ????new?HtmlWebpackPlugin({
          ????????template:?'./src/a.html',
          ????????filename:?'a',
          ????????inject:?'body',
          ????????chunks:?['vendors',?'a']
          ????})
          ]


          這里資源根路徑的配置在output項:


          //?webpack.config.js
          output:?{
          ????...
          ????publicPath:?debug???'/__build/'?:?'http://cdn.site.com/'
          }


          其他入口html文件采用類似處理方式。




          輔助工具集成

          local server解決本地開發(fā)環(huán)境的問題,webpack解決開發(fā)和生產(chǎn)環(huán)境資源依賴管理的問題。在項目開發(fā)中,可能會有許多額外的任務需要完成,比如對于使用compass生成sprites的項目,因目前webpack還不直接支持sprites,所以還需要compass watch,再比如工程的遠程部署等,所以需要使用一些構建工具或者腳本的配合,打通研發(fā)的鏈路。


          因為每個團隊在部署代碼、單元測試、自動化測試、發(fā)布等方面做法都不同,前端需要遵循公司的標準進行自動化的整合,這部分不深入了。




          對比&綜述


          前端工程化的建設,早期的做法是使用Grunt、Gulp等構建工具。但本質(zhì)上它們只是一個任務調(diào)度器,將功能獨立的任務拆解出來,按需組合運行任務。如果要完成前端工程化,這兩者配置門檻很高,每一個任務都需要開發(fā)者自行使用插件解決,而且對于資源的依賴管理能力太弱。


          在國內(nèi),百度出品的fis也是一種不錯的工程化工具的選擇,fis內(nèi)部也解決了資源依賴管理的問題。因筆者沒有在項目中實踐過fis,所以不進行更多的評價。

          webpack以一種非常優(yōu)雅的方式解決了前端資源依賴管理的問題,它在內(nèi)部已經(jīng)集成了許多資源依賴處理的細節(jié),但是對于使用者而言只需要做少量的配置,再結合構建工具,很容易搭建一套前端工程解決方案。


          基于webpack的前端自動化工具,可以自由組合各種開源技術棧(Koa/Express/其他web框架、webpack、Sass/Less/Stylus、Gulp/Grunt等),沒有復雜的資源依賴配置,工程結構也相對簡單和靈活。


          附上筆者根據(jù)本篇的理論所完成的一個前端自動化解決方案項目模板:
          webpack-seed :?https://github.com/chemdemo/webpack-seed





          點擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開更多互動和交流。

          -?END -

          瀏覽 50
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲欧美电影 | 在线国色天看一区一 | 天天日夜夜干 | 亚洲国产一级黄片 | 欧美AAAAAA |