摘要:我們先假設設置為從安裝然后就可以執行的命令了上面的命令會自動打開你的瀏覽器并指定到修改一下你的文件并保存。比如,類似與解析等都是共通的功能,需要放在基礎配置文件里面然后使用來合并特定環境變量下指定的配置文件。
動態模塊替換(Hot Module Repalcement -React)原文發布與抹橋的博客-【翻譯向】webpack2 指南(中)
就像之前 理念頁面 中解析的細節那樣,動態模塊替換(HMR)會在應用運行時動態的替換、添加或者刪除模塊而不用重新刷新頁面。 HMR 非常有用,當應用只有一個狀態樹(single state tree)時。
下面介紹的方法描述中使用了 Babel 和 React ,但這并不是使用 HRM 所必須的工具。
項目配置這里會指導你如何用 Babel, React 和 PostCss 一起使用 HMR 去演示一個項目。為了能夠跟著下面走下去,需要把這些依賴添加到 package.json 中去。
為了使用 HMR,你需要如下這些依賴:
npm install --save-dev babel@6.5.2 babel-core@6.13.2 babel-loader@6.2.4 babel-preset-es2015@6.13.2 babel-preset-react@6.11.1 babel-preset-stage-2@6.13.0 css-loader@0.23.1 postcss-loader@0.9.1 react-hot-loader@3.0.0-beta.6 style-loader@0.13.1 webpack@2.1.0-beta.25 webpack-dev-server@2.1.0-beta.0
同時,為了達到我們演示的目的,還需要:
npm install --save react@15.3.0 react-dom@15.3.0Babel Config
.babelrc 文件應該如下:
{ "presets": [ ["es2015", {"modules": false}], // webpack understands the native import syntax, and uses it for tree shaking "stage-2", // Specifies what level of language features to activate. // Stage 2 is "draft", 4 is finished, 0 is strawman. // See https://tc39.github.io/process-document/ "react" // Transpile React components to JavaScript ], "plugins": [ "react-hot-loader/babel" // Enables React code to work with HMR. ] }Webpack Config
const { resolve } = require("path"); const webpack = require("webpack"); module.exports = { entry: [ "react-hot-loader/patch", // activate HMR for React "webpack-dev-server/client?http://localhost:8080", // bundle the client for webpack-dev-server // and connect to the provided endpoint "webpack/hot/only-dev-server", // bundle the client for hot reloading // only- means to only hot reload for successful updates "./index.js" // the entry point of our app ], output: { filename: "bundle.js", // the output bundle path: resolve(__dirname, "dist"), publicPath: "/" // necessary for HMR to know where to load the hot update chunks }, context: resolve(__dirname, "src"), devtool: "inline-source-map", devServer: { hot: true, // enable HMR on the server contentBase: resolve(__dirname, "dist"), // match the output path publicPath: "/" // match the output `publicPath` }, module: { rules: [ { test: /.js$/, use: [ "babel-loader", ], exclude: /node_modules/ }, { test: /.css$/, use: [ "style-loader", "css-loader?modules", "postcss-loader", ], }, ], }, plugins: [ new webpack.HotModuleReplacementPlugin(), // enable HMR globally new webpack.NamedModulesPlugin(), // prints more readable module names in the browser console on HMR updates ], };
上面有很多配置,但不是所有都和 HMR 有關。可以通過查閱 webpack-dev-server options 和concept pages 來加深理解。
我們基礎設想是這樣的,你的 JavaScript 入口文件在 ./src/index.js 且你使用 CSS Module 來編寫樣式文件。
配置文件中需要重點關注的是 devServer 和 entry key. HotModueReplacementPlugin 同樣需要被包含在 plugins key 中。
為了達到目的,我們引入了兩個模塊:
react-hot-loader 添加到了入口中, 是為了能夠使 React 支持 HMR
為了更好的理解 HMR 每次更新的時候做了哪些事情,我們添加了 NamedModulePlugin
Code// ./src/index.js import React from "react"; import ReactDOM from "react-dom"; import { AppContainer } from "react-hot-loader"; // AppContainer is a necessary wrapper component for HMR import App from "./components/App"; const render = (Component) => { ReactDOM.render(, document.getElementById("root") ); }; render(App); // Hot Module Replacement API if (module.hot) { module.hot.accept("./components/App", () => { const NewApp = require("./components/App").default render(NewApp) }); }
// ./src/components/App.js import React from "react"; import styles from "./App.css"; const App = () => (); export default App;Hello,
.app { text-size-adjust: none; font-family: helvetica, arial, sans-serif; line-height: 200%; padding: 6px 20px 30px; }
一個需要特別注意的是 module 的引用:
Webpack 會暴露出 module.hot 給我們的代碼,當我們設置 devServer: { hot: true } 時;
這樣我們可以使用 module.hot 來給特定的資源棄用 HMR (這里是 App.js). 這里有一個非常重要的 API module.hot.accept ,用來決定如何處理這些特定的依賴。
需要注意的是,webpack2 內建支持 ES2015 模塊語法,你不需要在 module.hot.accept 中重新引用跟組件。為了達到這個目的,需要在 .babelrc 配置 Babel ES2015 的預先設置:
["es2015", {"modules": false}]
就像我們在之前 Babel Config 中配置的那樣。需要注意,禁用 Babel 的模塊功能 不僅僅是為了啟用 HMR。如果你不關掉這個配置,那么你會碰到需要問題。
如果你在 webpack2 的配置文件中使用 ES6 模塊,并且你按照 #3 修改了 .babelrc,那么你需要使用 require 語法來創建兩個 .babelrc 文件:
一個放在根目錄下面并配置為 "presets: ["es2015"]"
一個放在 webpack 要編譯的文件夾下,比如在這個例子中,就是 src/
所以在這個案例中,module.hot.accept 會執行 render 方法無論 src/compoents/App.js 或者其它的依賴文件變化的時候 ——這意味著當 App.css 被引入到 App.js 中以后,即使是 App.css 被修改,
render 方法同樣會被執行。
Index.html入口頁面需要被放在頁面 dist 下面,webpack-dev-server 的運行需要這個文件。
Package.jsonExample Index
最后,我們需要啟動 webpack-dev-server 來打包我們的代碼并且看看 HMR 是如何工作的。我們可以使用如下的 package.json 入口:
{ "scripts" : { "start" : "webpack-dev-server" } }
執行 npm start, 打開瀏覽器輸入 http://localhost:8080, 應該可以看到下面這些項展示在 console.log中:
dev-server.js:49[HMR] Waiting for update signal from WDS… only-dev-server.js:74[HMR] Waiting for update signal from WDS… client?c7c8:24 [WDS] Hot Module Replacement enabled.
然后編輯并且修改 App.js 文件,你會在 console.log 中看到類似如下的日志:
[WDS] App updated. Recompiling… client?c7c8:91 [WDS] App hot update… dev-server.js:45 [HMR] Checking for updates on the server… log-apply-result.js:20 [HMR] Updated modules: log-apply-result.js:22 [HMR] - ./components/App.js dev-server.js:27 [HMR] App is up to date.
注意 HMR 指出了更新模塊的路徑。這是因為我們使用了 NamedModulesPlugin.
開發環境(Development)這個章節介紹在開發過程中可以使用的一些工具。
需要注意,不能在生產環境使用
Source Map當 JS 發生異常的時候,我們需要指導是哪一個文件的哪一行出錯了。但是當文件都被 webpack 打包以后,找問題會變得很麻煩。
Source Map 就是為了解決這個問題的。它有很多不同的選項,每一種都有的好處和不足。在一開始,我們使用:
devtool: "cheap-eval-source-map"選擇一個工具(Choosing a Tool)
Webpack 可被用于監視模式(watch mode)。這種模式下, webpack 會監視你的文件,當它們有變動的時候就會重編譯。Webpack-dev-server 提供了一個很方便使用的開發環境的服務,并且支持自動刷新功能。如果你已經有了一個開發環境的服務,并且希望能夠擁有更好的適應性,那么 webpack-dev-middleware 可以被用作一個中間件來達到這個目的。
Webpack-dev-server 和 webpack-dev-middleware 實在內存中進行編譯的,這意味著打包后的代碼包并不會保存到本地磁盤中。這回使打包變得很快,同時不會產生很多臨時文件來污染你的本地文件系統。
大多數情況下,你都會想要去使用 webpack-dev-server, 因為它使用起來很方便,而且提供了許多開箱即用的功能。
Webpack 監視模式(wtach mode)Webpack 的監視模式會檢測文件的變動。只要變動被檢測到,它就會重新進行一次編譯。我們希望它的編譯過程能有一個很好的進度展示。那么就執行以下命令:
webpack --progress --watch
隨便修改一個文件然后保存,你就會看到重新編譯的過程。
監視模式沒有考慮任何和服務有關的問題,所以你需要自己提供一個服務。一個簡單的服務就是 [server](https://github.com/tj/serve). 當安裝好后(npm i server -g),在你打包后的文件目錄下運行:
server
當每次重新編譯后,你都需要手動的去刷新瀏覽器。
webpack-dev-serverwebpack-dev-server 提供一個支持自動刷新的服務。
首先,確認你 index.html 頁面里面已經引用了你的代碼包。我們先假設 output.filename 設置為 bundle.js:
從 npm 安裝 webpack-dev-server:
npm install webpack-dev-server --save-dev
然后就可以執行 webpack-dev-server 的命令了:
webpack-dev-server --open
上面的命令會自動打開你的瀏覽器并指定到 http://localhost:8080.
修改一下你的文件并保存。你會發現代碼被重新打包了,當打包完成的時候,頁面會自動刷新。如果沒有如愿達到效果,那么你需要調整 watchOptions(https://webpack.js.org/configuration/dev-server#devserver-watchoptions-).
現在你有了一個可以自動刷新的服務,接下來我們看如何啟用動態模塊替換(Hot Module Replacement)。這是一個可以提供不刷新頁面替換模塊的接口,查看這里了解更多 。
webpack-dev-server 可以做很多的事情,比如代理請求到你的后端服務。想了解更多的配置項,那就查看 devServer 的文檔吧。
webpack-dev-middlewarewebpack-dev-middleware 適用于基于中間件的鏈接堆棧(好難翻譯)。當你已經有一個 Node.js 服務或者你想要完全的控制服務的時候會很有用。
這個中間件會讓文件編譯在內存中進行。當一個編譯在進行過程中,它會延遲一個文件請求,直到它編譯完成。
首先從 npm 上安裝:
npm install express webpack-dev-server --save-dev
作為一個例子,我們可以這樣使用中間件:
var express = require("express"); var webpackDevMiddleware = require("webpack-dev-middleware"); var webpack = require("webpack"); var webpackConfig = require("./webpack.config"); var app = express(); var compiler = webpack(webpackConfig); app.use(webpackDevMiddleware(compiler, { publicPath: "/" // Same as `output.publicPath` in most cases. })); app.listen(3000, function () { console.log("Listening on port 3000!"); });
根據你在 output.publicPath 和 output.filename 中的配置,你打包的代碼應該可以通過 http://localhost:3000/bundle.js 訪問。
默認情況下使用的是監視模式。它還支持懶模式(lazy mode),只有在有請求進來的時候才會重新編譯。
配置如下:
app.use(webpackDevMiddleware(compiler, { lazy: true, filename: "bundle.js" // Same as `output.filename` in most cases. }));
還有許多其它有用的選項,詳細內容可以查看 文檔.
為生產環境構建(Building for Production)本章介紹如何用 webpack 來做生產環境的構建。
一條自動化的方式執行 webpack -p(等同于 webpack --optimize--minimize --define process.env.NODE_ENV="production").
這會執行以下幾個步驟:
使用 UglifyJsPlugin 壓縮文件
執行了 LoaderOptionsPlugin, 查看文檔
設置 Node 的環境變量
源碼壓縮webpack 使用 UglifyJsPlugin 來壓縮源碼,通過執行 UglifyJs 來達到壓縮輸出代碼的目的。這個插件支持所有 UgilfyJs 的功能。在命令行里輸入 --optimize-minimize ,那么相當與在配置文件中添加了以下配置:
// webpack.config.js const webpack = require("webpack"); module.exports = { /*...*/ plugins:[ new webpack.optimize.UglifyJsPlugin({ sourceMap: options.devtool && (options.devtool.indexOf("sourcemap") >= 0 || options.devtool.indexOf("source-map") >= 0) }) ] };
這樣,基于 devtools option ,在打包的時候會生成 Source Map.
資源映射(Source Map)我們推薦在開發環境啟用 Source Map. 因為在 debug 或者測試的時候很有用。Webpack 可以生成包含在代碼包或者分離文件中的 inline Source Map.
在配置文件中,通過修改 devtools 配置來設置 Source Map 類型。目前我們支持七種不同類型的 Source Map. 可以在具體文檔中找到更加詳細的介紹。
一個比較好好的選擇是使用 cheap-module-source-map,可以將源映射簡化為每行映射(simplifies the Source Maps to a single mapping per line)。
Node 環境變量執行 webpack -p( --define process.env.NODE_EMV="production") 會通過如下的配置調用 DefinePlugin:
// webpack.config.js const webpack = require("webpack"); module.exports = { /*...*/ plugins:[ new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }) ] };
DefindPlugin 會在源碼中進行查找和替換的工作。所有找到的 process.env.NODE_ENV 都會被替換為 production. 這樣,類似與 if(process.env.NODE_ENV !=="procution") console.log(…) 這樣的代碼就會被 UnglifyJs 認為等同于 if(false) console.log(…) .
一個手動的方式:為 webpack 配置不同環境變量下的配置文件一個最簡單的方式來為 webpack 配置不同環境變量下的配置文件的方法就是建立多個配置文件。比如:
dev.js
// 此處官網文檔有語法錯誤,我改了一下 module.exports = function (env) { return { devtool: "cheap-module-source-map", output: { path: path.join(__dirname, "/../dist/assets"), filename: "[name].bundle.js", publicPath: publicPath, sourceMapFilename: "[name].map" }, devServer: { port: 7777, host: "localhost", historyApiFallback: true, noInfo: false, stats: "minimal", publicPath: publicPath } } }
prod.js
module.exports = function (env) { return { output: { path: path.join(__dirname, "/../dist/assets"), filename: "[name].bundle.js", publicPath: publicPath, sourceMapFilename: "[name].map" }, plugins: [ new webpack.LoaderOptionsPlugin({ minimize: true, debug: false }), new UglifyJsPlugin({ beautify: false, mangle: { screw_ie8: true, keep_fnames: true }, compress: { screw_ie8: true }, comments: false }) ] } }
然后把我們的 webpack.config.js 的內容改成下面這樣:
function buildConfig(env) { return require("./config/" + env + ".js")({ env: env }) } module.exports = buildConfig(env);
最后,在 package.json 中添加如下命令:
"build:dev": "webpack --env=dev --progress --profile --colors", "build:dist": "webpack --env=prod --progress --profile --colors",
可以看到,我們把環境變量傳給了 webpack.config.js 文件。從這里我們使用一個簡單的方式通過傳遞環境變量來決定使用正確的配置文件。
一個更加高級的途徑是我們有一個基礎配置文件,里面有所有共通的功能,然后在不同環境變量下的不同功能通過指定特定的文件,然后使用 webpack-merge 來合并成一個完整的配置。這樣可以避免寫很多
重復的代碼。比如,類似與解析 js,ts,png,jpeg 等都是共通的功能,需要放在基礎配置文件里面:
base.js
module.exports = function() { return { entry: { "polyfills": "./src/polyfills.ts", "vendor": "./src/vendor.ts", "main": "./src/main.ts" }, output: { path: path.join(__dirname, "/../dist/assets"), filename: "[name].bundle.js", publicPath: publicPath, sourceMapFilename: "[name].map" }, resolve: { extensions: ["", ".ts", ".js", ".json"], modules: [path.join(__dirname, "src"), "node_modules"] }, module: { loaders: [{ test: /.ts$/, loaders: [ "awesome-typescript-loader", "angular2-template-loader" ], exclude: [/.(spec|e2e).ts$/] }, { test: /.css$/, loaders: ["to-string-loader", "css-loader"] }, { test: /.(jpg|png|gif)$/, loader: "file" }, { test: /.(woff|woff2|eot|ttf|svg)$/, loader: "url-loader?limit=100000" }], }, plugins: [ new ForkCheckerPlugin(), new webpack.optimize.CommonsChunkPlugin({ name: ["polyfills", "vendor"].reverse() }), new HtmlWebpackPlugin({ template: "src/index.html", chunksSortMode: "dependency" }) ], }; }
然后使用 webpack-merge 來合并特定環境變量下指定的配置文件。來看一個合并生產環境下特定配置的例子(和上面 prod.js 對比以下):
prod.js(updated)
const webpackMerge = require("webpack-merge"); const commonConfig = require("./base.js"); module.exports = function(env) { return webpackMerge(commonConfig(), { plugins: [ new webpack.LoaderOptionsPlugin({ minimize: true, debug: false }), new webpack.DefinePlugin({ "process.env": { "NODE_ENV": JSON.stringify("prod") } }), new webpack.optimize.UglifyJsPlugin({ beautify: false, mangle: { screw_ie8: true, keep_fnames: true }, compress: { screw_ie8: true }, comments: false }) ] }) }
可以注意到,在 "prod.js" 中主要有三處更新,分別是:
? 通過 "webpack-meger" 合并了 `base.js` ? 把 `output` 屬性移到了 `base.js` 中。我們只要關心在 `base.js` 中以外的不同的配置就可以了 ? 通過 `DefinePlugin` 把 `process.env.NODE_ENV` 設置為 `prod`. 這樣,整個應用代碼中的 `process.env.NODE_ENV` 都有一個為 `prod` 的值了。
哪些需要在不同的環境變量下保持一致都由你來決定。這里只是通過一個 DEMO 來典型的說明一下如何在不同的環境變量下保持部分配置的統一。
可以看到,webpack-merge 是多么強大,可以讓我們避免寫很多重復的代碼(外國人話真多)。
React 懶加載(Lazy Loading - React)通過使用高階函數可以使一個組件懶加載它的依賴而不需要它的消費者知道,或者使用一個接收函數或者模塊的組件,可以使一個消費者可以懶加載它的子組件而不需要它的子組件知道。
組件懶加載先看一個消費者選擇去懶加載一些組件。importLazy 是一個返回 defualt 屬性的函數,這是為了能和 Babel/ES2015 互通。如果你不需要,可以忽略掉 importLazy 方法。importLazy 只是簡單的返回了通過 export default 暴露出的模塊。
高階組件(Higher Order Component)importLazy(import("./components/TodoHandler")), TodoMenuHandler: () => importLazy(import("./components/TodoMenuHandler")), TodoMenu: () => importLazy(import("./components/TodoMenu")), }}> {({TodoHandler, TodoMenuHandler, TodoMenu}) => ( )}
作為一個組件,你可以確保整個組件本身的依賴是懶加載的。當一個組件依賴一個非常大的庫文件的時候會很有用。假設我們要寫一個支持代碼高亮的 Todo 組件:
class Todo extends React.Component { render() { return ({this.props.isCode ?); } }{content} : content}
我們可以確保只有當我們需要代碼高亮功能的時候才去加載這個代價高昂的庫文件:
// Highlight.js class Highlight extends React.Component { render() { const {Highlight} = this.props.highlight; // highlight js is now on our props for use } } export LazilyLoadFactory(Highlight, { highlight: () => import("highlight"), });
注意這個 Highlight 組件的消費者是如何在不知情的情況下被懶加載的。
完整的代碼LazilyLoad 組件的源碼,暴露了組件接口和高階組件接口。
import React from "react"; class LazilyLoad extends React.Component { constructor() { super(...arguments); this.state = { isLoaded: false, }; } componentWillMount() { this.load(this.props); } componentDidMount() { this._isMounted = true; } componentWillReceiveProps(next) { if (next.modules === this.props.modules) return null; this.load(next); } componentWillUnmount() { this._isMounted = false; } load(props) { this.setState({ isLoaded: false, }); const { modules } = props; const keys = Object.keys(modules); Promise.all(keys.map((key) => modules[key]())) .then((values) => (keys.reduce((agg, key, index) => { agg[key] = values[index]; return agg; }, {}))) .then((result) => { if (!this._isMounted) return null; this.setState({ modules: result, isLoaded: true }); }); } render() { if (!this.state.isLoaded) return null; return React.Children.only(this.props.children(this.state.modules)); } } LazilyLoad.propTypes = { children: React.PropTypes.func.isRequired, }; export const LazilyLoadFactory = (Component, modules) => { return (props) => ({(mods) => ); }; export const importLazy = (promise) => ( promise.then((result) => result.default) ); export default LazilyLoad;}
提示
通過使用 bundle loader 可以語義化命名代碼塊,一次來智能的加載一組代碼
確保你使用了 babel-preset-2015, 并且設置 modules 為 false,這允許 webpack 去處理 modules
公開路徑?(Public Path)Webpack 提供了一個很長有用的功能,可以設置你應用中所有資源引用的基礎路徑。它被稱之為 publicPath.
使用場景(Use case)這里有一些真實應用中的場景,通過這個功能來達到目的。
在構建的時候設置值在開發模式下,我們通常會把 assets/ 目錄放在和入口頁同級的目錄下面。這樣沒有問題,但是假如在生產環境下你的靜態資源是存放在 CDN 上那又該怎么辦呢?
可以很方便的通過環境變量來解決這個問題。假設我們有一個變量 ASSET_PATH:
// 這里看起來好像有問題 import webpack from "webpack"; // Whatever comes as an environment variable, otherwise use root const ASSET_PATH = process.env.ASSET_PATH || "/"; export default { output: { publicPath: ASSET_PATH }, plugins: [ // This makes it possible for us to safely use env vars on our code new webpack.DefinePlugin({ "process.env.ASSET_PATH": JSON.stringify(ASSET_PATH) }) ] };在開發中設置值(Set Value on the fly)
另一種方式是在開發過程成設置 public 路徑。Webpack 暴露了一個全局變量 __webpack_public_path__ 來讓我們達到這個目的。所以在你的入口文件中,你可以這樣做:
__webpack_publick_path__ = process.en.ASSET_PATH;
如何來做都取決于你。當我們通過 DefinePlugin 進行了配置以后, process.env.ASSET_PATH 在任何地方都可以直接拿來使用。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81428.html
摘要:一些有用的一些有用的,包括轉換小箭頭三角形媒體查詢等中文指南是當下最熱門的前端資源模塊化管理和打包工具。 nodejs 入門 nodejs 入門教程,大家可以在 github 上提交錯誤 2016 年最好用的表單驗證庫 SMValidator.js 前端表單驗證工具分享 淺談前端線上部署與運維 說到前端部署,可能大多數前端工程師在工作中都是使用的公司現成的部署系統,與SRE對接、一起完...
摘要:一些有用的一些有用的,包括轉換小箭頭三角形媒體查詢等中文指南是當下最熱門的前端資源模塊化管理和打包工具。 nodejs 入門 nodejs 入門教程,大家可以在 github 上提交錯誤 2016 年最好用的表單驗證庫 SMValidator.js 前端表單驗證工具分享 淺談前端線上部署與運維 說到前端部署,可能大多數前端工程師在工作中都是使用的公司現成的部署系統,與SRE對接、一起完...
摘要:原文發布與抹橋的博客翻譯向指南上前置定義代碼包代碼塊安裝代碼分割代碼分割是中最引人注目的功能之一。回調函數一個回調函數會被執行一次當所有依賴都被加載以后。對象的實現作為一個參數傳遞給這個回調函數。 原文發布與 抹橋的博客 -【翻譯向】webpack2 指南(上) 前置定義 Bundle 代碼包Chunk 代碼塊 安裝 npm install webpack --save-dev 代碼分...
摘要:名稱后自動自動補全的功能將被移除在配置時,官方不再允許省略擴展名,的配置寫法上將逐步趨于嚴謹。使用自定義參數作為配置項傳入方式將做調整如果你隨意將自定義參數通過傳入到配置項中,如你會發現這將不會被允許,的執行將會遵循更為嚴格的標準。 歷時多日,webpack2.2正式版終于趕在年前發布了,此次更新相對于1.X版本有了諸多的升級優化改進,筆者也在第一時間查閱了官方的文檔,整理和翻譯了由w...
摘要:從再到目前當紅明星,前端模塊打包技術日新月異,在今年月份和月份左右接連更新了和版本為了減少冗余模塊,縮減文件大小,中也加入了關于的特征,可以查看知乎如何評價新引入的代碼優化技術的討論。 從Grunt->gulp->webpack,再到目前當紅明星rollup,前端模塊打包技術日新月異,webpack在今年1月份和6月份左右接連更新了v2和v3版本,為了減少冗余模塊,縮減bundle文件...
閱讀 3167·2021-11-22 15:25
閱讀 3838·2021-11-17 09:33
閱讀 3362·2021-11-08 13:15
閱讀 3044·2021-09-22 10:56
閱讀 535·2021-08-31 09:45
閱讀 2745·2019-08-30 13:49
閱讀 3072·2019-08-30 12:52
閱讀 1136·2019-08-29 17:05