摘要:在項(xiàng)目架構(gòu)中這兩個(gè)東西基本成為了標(biāo)配,但的模塊必須在使用前經(jīng)過的構(gòu)建后文稱為才能在瀏覽器端使用,而每次修改也都需要重新構(gòu)建后文稱為才能生效,如何提高的構(gòu)建效率成為了提高開發(fā)效率的關(guān)鍵之一。
0. 前言
圖1:ES6 + Webpack + React + Babel
webpack 是個(gè)好東西,和 NPM 搭配起來(lái)使用管理模塊實(shí)在非常方便。而 Babel 更是神一般的存在,讓我們?cè)谶@個(gè)瀏覽器尚未全面普及 ES6 語(yǔ)法的時(shí)代可以先一步體驗(yàn)到新的語(yǔ)法帶來(lái)的便利和效率上的提升。在 React 項(xiàng)目架構(gòu)中這兩個(gè)東西基本成為了標(biāo)配,但 commonjs 的模塊必須在使用前經(jīng)過 webpack 的構(gòu)建(后文稱為 build)才能在瀏覽器端使用,而每次修改也都需要重新構(gòu)建(后文稱為 rebuild)才能生效,如何提高 webpack 的構(gòu)建效率成為了提高開發(fā)效率的關(guān)鍵之一。
1. Webpack 的構(gòu)建流程在開始正式的優(yōu)化之前,讓我們先回顧一下 Webpack 的構(gòu)建流程,有哪些關(guān)鍵步驟,只有了解了這些,我們才能分析出哪些地方有優(yōu)化的可能性。
圖2:webpack is a module bundler.
首先,我們來(lái)看看官方對(duì)于 Webpack 的理念闡釋,webapck 把所有的靜態(tài)資源都看做是一個(gè) module,通過 webpack,將這些 module 組成到一個(gè) bundle 中去,從而實(shí)現(xiàn)在頁(yè)面上引入一個(gè) bundle.js,來(lái)實(shí)現(xiàn)所有靜態(tài)資源的加載。所以詳細(xì)一點(diǎn)看,webpack 應(yīng)該是這樣的:
圖3:Every static asset should be able to be a module --webpack
通過 loader,webpack 可以把各種非原生 js 的靜態(tài)資源轉(zhuǎn)換成 JavaScript,所以理論上任何一種靜態(tài)資源都可以成為一個(gè) module。
當(dāng)然 webpack 還有很多其他好玩的特性,但不是本文的重點(diǎn)因此不鋪開進(jìn)行說(shuō)明了。了解了上述的過程,我們就可以根據(jù)這些過程的前后處理進(jìn)行對(duì)應(yīng)的優(yōu)化,接下來(lái)我們會(huì)針對(duì) build 和 rebuild 的過程給與相應(yīng)的意見。
我們先從解析模塊路徑和分析依賴講起,有人可能覺得這無(wú)所謂,但當(dāng)項(xiàng)目應(yīng)用依賴的模塊越來(lái)越多,越來(lái)越重時(shí),項(xiàng)目越來(lái)越大,文件和文件夾越來(lái)越多時(shí),這個(gè)過程就變得越來(lái)越關(guān)乎性能。
2.1 減小 Webpack 覆蓋的范圍build +, rebuild +
webpack 默認(rèn)會(huì)去尋找所有 resolve.root 下的模塊,但是有些目錄我們是可以明確告知 webpack 不要管這里,從而減輕 webpack 的工作量。這時(shí)會(huì)用到 module.noParse 參數(shù)。
2.2 Resolove.root VS Resolove.moduledirectoriesbuild +, rebuild +
root 和 moduledirectories 如果只從用法上來(lái)看,似乎是可以互相替代的。但因?yàn)?moduledirectories 從設(shè)計(jì)上是取相對(duì)路徑,所以比起 root ,所以會(huì)多 parse 很多路徑。
resolve: { root: path.resolve("src/node_modules"), extensions: ["", ".js", ".jsx"] }, resolve: { modulesDirectories: ["node_modules", "./src"], extensions: ["", ".js", ".jsx"] },
上面的配置,只會(huì)解析
./src/node_modules/a
==== 此處有修改 2016/09/10 感謝 @lili_21 ====
而下面的配置會(huì)解析
/some/folder/structure/node_modules/a /some/folder/structure/src/a /some/folder/node_modules/a /some/folder/src/a /some/node_modules/a /some/src/a /node_modules/a /src/a
大部分的情況下使用 root 即可,只有在有很復(fù)雜的路徑下,才考慮使用 moduledirectories,這可以明顯提高 webpack 的構(gòu)建性能。這個(gè) issue 也很詳細(xì)地討論了這個(gè)問題。
3. LOADERSwebpack 官方和社區(qū)為我們提供了各種各樣 loader 來(lái)處理各種類型的文件,這些 loader 的配置也直接影響了構(gòu)建的性能。
3.1 Babel-loader: 能者少勞build ++, rebuild ++
以 babel-loader 為例,我們?cè)陂_發(fā) React 項(xiàng)目時(shí)很可能會(huì)使用到了 ES6 或者 jsx 的語(yǔ)法,因此使用到 babel-loader 的情況很多,最簡(jiǎn)單的情況下我們可以這樣配置,讓所有的 js/jsx 通過 babel-loader:
module: { loaders: [ { test: /.js(x)*$/, loader: "babel-loader", query: { presets: ["react", "es2015-ie", "stage-1"] } } ] }
上面這樣的做法當(dāng)然是 ok 的,但是對(duì)于很多的 npm 包來(lái)說(shuō),他們完全沒有經(jīng)過 babel 的必要(成熟的 npm 包會(huì)在發(fā)布前將自己 es5,甚至 es3 化),讓這些包通過 babel 會(huì)帶來(lái)巨大的性能負(fù)擔(dān),畢竟 babel6 要經(jīng)過幾十個(gè)插件的處理,雖然 babel-loader 強(qiáng)大,但能者多勞的這種保守的想法卻使得 babel-loader 成為了整個(gè)構(gòu)建的性能瓶頸。所以我們可以使用 exclude,大膽地屏蔽掉 npm 里的包,從而使整包的構(gòu)建效率飛速提高。
module: { loaders: [ { test: /.js(x)*$/, loader: "babel-loader", exclude: function(path) { // 路徑中含有 node_modules 的就不去解析。 var isNpmModule = !!path.match(/node_modules/); return isNpmModule; }, query: { presets: ["react", "es2015-ie", "stage-1"] } } ] }
甚至,在我們十分確信的情況下,使用 include 來(lái)限定 babel 的使用范圍,進(jìn)一步提高效率。
var path = require("path"); module.exports = { module: { loaders: [ { test: /.js(x)*$/, loader: "babel-loader", include: [ // 只去解析運(yùn)行目錄下的 src 和 demo 文件夾 path.join(process.cwd(), "./src"), path.join(process.cwd(), "./demo") ], query: { presets: ["react", "es2015-ie", "stage-1"] } } ] } }4. PLUGINS
webpack 官方和社區(qū)為我們提供了很多方便的插件,有些插件為我們開發(fā)和生產(chǎn)帶來(lái)了很多的便利,但是不合適地使用插件也會(huì)拖慢 webpack 的構(gòu)建效率,而有些插件雖然不會(huì)為我們的開發(fā)上直接提供便利,但使用他們卻可以幫助我們提高 webpack 的構(gòu)建效率,這也是本文會(huì)提到的。
4.1 SourceMapsbuild +
SourceMaps 是一個(gè)非常實(shí)用的功能,可以讓我們?cè)?chrome debug 時(shí)可以不用直接看已經(jīng) bundle 過的 js,而是直接在源代碼上進(jìn)行查看和調(diào)試,但完美的 SourceMaps 是很慢的,webpack 官方提供了七種 sourceMap 模式共大家選擇,性能對(duì)比如下:
devtool | build speed | rebuild speed | production supported | quality |
---|---|---|---|---|
eval | +++ | +++ | no | generated code |
cheap-eval-source-map | + | ++ | no | transformed code (lines only) |
cheap-source-map | + | o | yes | transformed code (lines only) |
cheap-module-eval-source-map | o | ++ | no | original source (lines only) |
cheap-module-source-map | o | - | yes | original source (lines only) |
eval-source-map | -- | + | no | original source |
source-map | -- | -- | yes | original source |
具體各自的區(qū)別請(qǐng)參考 https://github.com/webpack/do... ,我們這里推薦使用 cheap-source-map,也就是去掉了column mapping 和 loader-sourceMap(例如 jsx to js) 的 sourceMap,雖然帶上 eval 參數(shù)的可以快更多,但是這種 sourceMap 只能看,不能調(diào)試,得不償失。
4.2 OPTIMIZATIONbuild ++,rebuild ++
webpack 提供了一些可以優(yōu)化瀏覽器端性能的優(yōu)化插件,如UglifyJsPlugin,OccurrenceOrderPlugin 和 DedupePlugin,都很實(shí)用,也都在消耗構(gòu)建性能(UglifyJsPlugin 非常耗性能),如果你是在開發(fā)環(huán)境下,這些插件最好都不要使用,畢竟腳本大一些,跑的慢一些這些比起每次構(gòu)建要耗費(fèi)更多時(shí)間來(lái)說(shuō),顯然還是后者更會(huì)消磨開發(fā)者的耐心,因此,只在正產(chǎn)環(huán)境中使用 OPTIMIZATION。
4.3 CommonsChunkrebuild +
當(dāng)你的 webpack 構(gòu)建任務(wù)中有多個(gè)入口文件,而這些文件都 require 了相同的模塊,如果你不做任何事情,webpack 會(huì)為每個(gè)入口文件引入一份相同的模塊,顯然這樣做,會(huì)使得相同模塊變化時(shí),所有引入的 entry 都需要一次 rebuild,造成了性能的浪費(fèi),CommonsChunkPlugin 可以將相同的模塊提取出來(lái)多帶帶打包,進(jìn)而減小 rebuild 時(shí)的性能消耗。這里有一篇很通俗易懂的使用方法:http://webpack.toobug.net/zh-... ,感興趣的朋友不妨一試。
4.4 DLL & DllReferencebuild +++, rebuild +++
除了正在開發(fā)的源代碼之外,通常還會(huì)引入很多第三方 NPM 包,這些包我們不會(huì)進(jìn)行修改,但是仍然需要在每次 build 的過程中消耗構(gòu)建性能,那有沒有什么辦法可以減少這些消耗呢?DLLPlugin 就是一個(gè)解決方案,他通過前置這些依賴包的構(gòu)建,來(lái)提高真正的 build 和 rebuild 的構(gòu)建效率。
鑒于現(xiàn)有的資料對(duì)于這兩個(gè)插件的解釋都不是很清楚,筆者這里翻譯了一篇日本同學(xué)的文章,通過一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明一下這兩個(gè)插件的用法。我們舉例,把 react 和 react-dom 打包成為 dll bundle。
首先,我們來(lái)寫一個(gè) DLLPlugin 的 config 文件。
webpack.dll.config.js
const path = require("path"); const webpack = require("webpack"); module.exports = { entry: { vendor: ["react", "react-dom"] }, output: { path: path.join(__dirname, "dist"), filename: "[name].dll.js", /** * output.library * 將會(huì)定義為 window.${output.library} * 在這次的例子中,將會(huì)定義為`window.vendor_library` */ library: "[name]_library" }, plugins: [ new webpack.DllPlugin({ /** * path * 定義 manifest 文件生成的位置 * [name]的部分由entry的名字替換 */ path: path.join(__dirname, "dist", "[name]-manifest.json"), /** * name * dll bundle 輸出到那個(gè)全局變量上 * 和 output.library 一樣即可。 */ name: "[name]_library" }) ] };
執(zhí)行 webpack 后,就會(huì)在 dist 目錄下生成 dll bundle 和對(duì)應(yīng)的 manifest 文件
$ ./node_modules/.bin/webpack --config webpack.dll.config.js Hash: 36187493b1d9a06b228d Version: webpack 1.13.1 Time: 860ms Asset Size Chunks Chunk Names vendor.dll.js 699 kB 0 [emitted] vendor [0] dll vendor 12 bytes {0} [built] + 167 hidden modules $ ls dist ./ vendor-manifest.json ../ vendor.dll.js
manifest 文件的格式大致如下,由包含的 module 和對(duì)應(yīng)的 id 的鍵值對(duì)構(gòu)成。
cat dist/vendor-manifest.json { "name": "vendor_library", "content": { "./node_modules/react/react.js": 1, "./node_modules/react/lib/React.js": 2, "./node_modules/process/browser.js": 3, "./node_modules/object-assign/index.js": 4, "./node_modules/react/lib/ReactChildren.js": 5, "./node_modules/react/lib/PooledClass.js": 6, "./node_modules/fbjs/lib/invariant.js": 7, ...
好,接下來(lái)我們通過 DLLReferencePlugin 來(lái)使用剛才生成的 DLL Bundle。
首先我們寫一個(gè)只去 require react,并通過 console.log 吐出的 index.js。
var React = require("react"); var ReactDOM = require("react-dom"); console.log("dll"s React:", React); console.log("dll"s ReactDOM:", ReactDOM);
再寫一個(gè)不參考 Dll Bundle 的普通 webpack config 文件。
webpack.conf.js
const path = require("path"); const webpack = require("webpack"); module.exports = { entry: { "dll-user": ["./index.js"] }, output: { path: path.join(__dirname, "dist"), filename: "[name].bundle.js" } };
執(zhí)行 webpack,會(huì)在 dist 下生成 dll-user.bundle.js,約 700K,耗時(shí) 801ms。
$ ./node_modules/.bin/webpack Hash: d8cab39e58c13b9713a6 Version: webpack 1.13.1 Time: 801ms Asset Size Chunks Chunk Names dll-user.bundle.js 700 kB 0 [emitted] dll-user [0] multi dll-user 28 bytes {0} [built] [1] ./index.js 145 bytes {0} [built] + 167 hidden modules
接下來(lái),我們加入 DLLReferencePlugin
webpack.conf.js
const path = require("path"); const webpack = require("webpack"); module.exports = { entry: { "dll-user": ["./index.js"] }, output: { path: path.join(__dirname, "dist"), filename: "[name].bundle.js" }, // ----在這里追加---- plugins: [ new webpack.DllReferencePlugin({ context: __dirname, /** * 在這里引入 manifest 文件 */ manifest: require("./dist/vendor-manifest.json") }) ] // ----在這里追加---- };
./node_modules/.bin/webpack Hash: 3bc7bf760779b4ca8523 Version: webpack 1.13.1 Time: 70ms Asset Size Chunks Chunk Names dll-user.bundle.js 2.01 kB 0 [emitted] dll-user [0] multi dll-user 28 bytes {0} [built] [1] ./index.js 145 bytes {0} [built] + 3 hidden modules
結(jié)果是非常驚人的,只有2.01K,耗時(shí) 70 ms,無(wú)疑大大提高了 build 和 rebuild 的效率。實(shí)際放到頁(yè)面上看下是否可行。
因?yàn)?Dll bundle 在依賴安裝完畢后就可以進(jìn)行了,我們可以在第一次執(zhí)行 dev server 前執(zhí)行一次 dll bundle 的 webapck 任務(wù)。
4.4.1 和 external 的比較有人會(huì)說(shuō),這個(gè)和 用 webpack 的 externals 配置把 require 的 module 指向全局變量有點(diǎn)像啊。
const path = require("path"); const webpack = require("webpack"); module.exports = { entry: { "ex": ["./index.js"] }, output: { path: path.join(__dirname, "dist"), filename: "[name].bundle.js" }, externals: { // require("react")はwindow.Reactを使う "react": "React", // require("react-dom")はwindow.ReactDOMを使う "react-dom": "ReactDOM" } };
這里有兩個(gè)主要的區(qū)別:
像是 react 這種已經(jīng)打好了生產(chǎn)包的使用 externals 很方便,但是也有很多 npm 包是沒有提供的,這種情況下 DLLBundle 仍可以使用。
如果只是引入 npm 包一部分的功能,比如 require("react/lib/React") 或者 require("lodash/fp/extend") ,這種情況下 DLLBundle 仍可以使用。
當(dāng)然如果只是引用了 react 這類的話,externals 因?yàn)榕渲煤?jiǎn)單所以也推薦使用。
4.5 HappyPackbuild +, rebuild +
webpack 的長(zhǎng)時(shí)間構(gòu)建搞的大家都很 unhappy。于是 @amireh 想到了一個(gè)點(diǎn)子,既然 loader 默認(rèn)都是一個(gè)進(jìn)程在跑,那是否可以讓 loader 多進(jìn)程去處理文件呢?
happyPack 的文檔寫的很易懂,這里就不再贅述,happyPack 不僅利用了多進(jìn)程,同時(shí)還利用緩存來(lái)使得 rebuild 更快。下面是插件作者給出的性能數(shù)據(jù):
For the main repository I tested on, which had around 3067 modules, the build time went down from 39 seconds to a whopping ~10 seconds when there was yet no
Successive builds now take between 6 and 7 seconds.
Here"s a rundown of the various states the build was performed in:
Elapsed (ms) | Happy? | Cache enabled? | Cache present? | Using DLLs? | |
---|---|---|---|---|---|
39851 | NO | N/A | N/A | NO | |
37393 | NO | N/A | N/A | YES | |
14605 | YES | NO | N/A | NO | |
13925 | YES | YES | NO | NO | |
11877 | YES | YES | YES | NO | |
9228 | YES | NO | N/A | YES | |
9597 | YES | YES | NO | YES | |
6975 | YES | YES | YES | YES |
5. 其他The builds above were run on Linux over a machine with 12 cores.
上面我們針對(duì) webpack 的 resolve、loader 和 plugin 的過程給出了相應(yīng)的優(yōu)化意見,除了這些哪些優(yōu)化點(diǎn)呢?其實(shí)有些優(yōu)化貫穿在這個(gè)流程中,比如緩存和文件 IO。
5.1 Cache無(wú)論在何種性能優(yōu)化中,緩存總是必不可少的一部分,畢竟每次變動(dòng)都只影響很小的一部分,如果能夠緩存住那些沒有變動(dòng)的部分,直接拿來(lái)使用,自然會(huì)事半功倍,在 webpack 的整個(gè)構(gòu)建過程中,有多個(gè)地方提供了緩存的機(jī)會(huì),如果我們打開了這些緩存,會(huì)大大加速我們的構(gòu)建,尤其是 rebuild 的效率。
5.1.1 webpack.cacherebuild +
webpack 自身就有 cache 的配置,并且在 watch 模式下自動(dòng)開啟,雖然效果不是最明顯的,但卻對(duì)所有的 module 都有效。
5.1.2 babel-loader.cacheDirectoryrebuild ++
babel-loader 可以利用系統(tǒng)的臨時(shí)文件夾緩存經(jīng)過 babel 處理好的模塊,對(duì)于 rebuild js 有著非常大的性能提升。
5.1.3 HappyPack.cachebuild +, rebuild +
上面提到的 happyPack 插件也同樣提供了 cache 功能,默認(rèn)是以 .happypack/cache--[id].json 的路徑進(jìn)行緩存。因?yàn)槭蔷彺嬖诋?dāng)前目錄下,所以他也可以輔助下次 build 時(shí)的效率。
5.2 FileSystem默認(rèn)的情況下,構(gòu)建好的目錄一定要輸出到某個(gè)目錄下面才能使用,但 webpack 提供了一種很棒的讀寫機(jī)制,使得我們可以直接在內(nèi)存中進(jìn)行讀寫,從而極大地提高 IO 的效率,開啟的方法也很簡(jiǎn)單。
var MemoryFS = require("memory-fs"); var webpack = require("webpack"); var fs = new MemoryFS(); var compiler = webpack({ ... }); compiler.outputFileSystem = fs; compiler.run(function(err, stats) { // ... var fileContent = fs.readFileSync("..."); });
當(dāng)然,我們還可以通過 webpackDevMiddleware 更加無(wú)縫地就接入到 dev server 中,例如我們以 express 作為靜態(tài) server 的例子。
var compiler = webpack(webpackCfg); var webpackDevMiddlewareInstance = webpackDevMiddleware(compiler, { // webpackDevMiddleware 默認(rèn)使用了 memory-fs publicPath: "/dist", aggregateTimeout: 300, // wait so long for more changes poll: true, // use polling instead of native watchers stats: { chunks: false } }); var app = express(); app.use(webpackDevMiddlewareInstance); app.listen(xxxx, function(err) { console.log(colors.info("dev server start: listening at " + xxxx)); if (err) { console.error(err); } }6. 總結(jié)
上面我們從 webpack 構(gòu)建的各個(gè)部分,給出了相應(yīng)的優(yōu)化策略,如果你的項(xiàng)目中能夠?qū)⑵渫耆瀼仄饋?lái),10 倍提速不是夢(mèng)想。這些優(yōu)化也同樣應(yīng)用到了我們團(tuán)隊(duì)的 react 項(xiàng)目中,https://github.com/uxcore/uxcore ,歡迎一起來(lái)討論 webpack 的效率優(yōu)化方案。
7. 參考文章webpack build performance:http://webpack.github.io/docs...
webpackのDLLバンドルを使ってビルドを速くする:http://qiita.com/pirosikick/i...
How to make your Webpack builds 10x faster:http://www.slideshare.net/tru...
本文作者 eternalsky,始發(fā)于團(tuán)隊(duì)微信公眾號(hào) 猿猿相抱 和個(gè)人博客 空の屋敷,轉(zhuǎn)載請(qǐng)保留作者信息。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/79738.html
學(xué)習(xí)的過程中收藏了這些優(yōu)秀教程和的項(xiàng)目,希望對(duì)你有幫助。 github地址, 有不錯(cuò)的就更新 官方文檔 中文指南 初級(jí)教程 webpack-howto 作者:Pete Hunt Webpack 入門指迷 作者:題葉 webpack-demos 作者:ruanyf 一小時(shí)包教會(huì) —— webpack 入門指南 作者:VaJoy Larn webpack 入門及實(shí)踐 作者:...
摘要:發(fā)布一周以來(lái),獲得了,登上了,在上進(jìn)入了前三。為什么要?jiǎng)?chuàng)造當(dāng)我在設(shè)計(jì)一個(gè)新的移動(dòng)端用戶的網(wǎng)站比如面向微信用戶的網(wǎng)站的時(shí)候,調(diào)研了一些現(xiàn)有的框架,應(yīng)用比較廣泛的有等。否則只能有中國(guó)人來(lái)關(guān)注你的項(xiàng)目了。要知道,外國(guó)開發(fā)者比中國(guó)開發(fā)者多很多倍的。 受邀寫一篇 Mobi.css 的誕生歷程,請(qǐng)?jiān)徫椅矍虻臉?biāo)題,我會(huì)努力把這篇文章寫得有一些干貨的。 GitHub Repo | Homepag...
摘要:打包分析與性能優(yōu)化背景在去年年末參與的一個(gè)項(xiàng)目中,項(xiàng)目技術(shù)棧使用,生產(chǎn)環(huán)境全量構(gòu)建將近三分鐘,項(xiàng)目業(yè)務(wù)模塊多達(dá)數(shù)百個(gè),項(xiàng)目依賴數(shù)千個(gè),并且該項(xiàng)目協(xié)同前后端開發(fā)人員較多,提高構(gòu)建效率,成為了改善團(tuán)隊(duì)開發(fā)效率的關(guān)鍵之一。 webpack打包分析與性能優(yōu)化 背景 在去年年末參與的一個(gè)項(xiàng)目中,項(xiàng)目技術(shù)棧使用react+es6+ant-design+webpack+babel,生產(chǎn)環(huán)境全量構(gòu)建將...
摘要:前言月份開始出沒社區(qū),現(xiàn)在差不多月了,按照工作的說(shuō)法,就是差不多過了三個(gè)月的試用期,準(zhǔn)備轉(zhuǎn)正了一般來(lái)說(shuō),差不多到了轉(zhuǎn)正的時(shí)候,會(huì)進(jìn)行總結(jié)或者分享會(huì)議那么今天我就把看過的一些學(xué)習(xí)資源主要是博客,博文推薦分享給大家。 1.前言 6月份開始出沒社區(qū),現(xiàn)在差不多9月了,按照工作的說(shuō)法,就是差不多過了三個(gè)月的試用期,準(zhǔn)備轉(zhuǎn)正了!一般來(lái)說(shuō),差不多到了轉(zhuǎn)正的時(shí)候,會(huì)進(jìn)行總結(jié)或者分享會(huì)議!那么今天我就...
閱讀 828·2021-07-25 21:37
閱讀 3713·2019-08-30 15:55
閱讀 2639·2019-08-30 15:54
閱讀 1802·2019-08-30 15:44
閱讀 3175·2019-08-30 15:44
閱讀 920·2019-08-30 15:43
閱讀 1127·2019-08-29 15:36
閱讀 3127·2019-08-29 10:58