Webpack 4.0 入门实战:从配置到优化的完整指南

作者:知秋

时间:2026-10-22

浏览:312

本文详细讲解 Webpack 4.0 的核心概念与实战配置。从入口出口、Loader 到 Plugin 的基础用法,深入解析开发环境与生产环境的差异化配置。涵盖代码分割、图片处理、Babel 转码、CSS 预处理器集成及模块热替换(HMR)等关键技术,帮助开发者搭建高效的前端构建流程。

文章目录

Webpack 核心概念与构建流程

Webpack 可以被视为模块打包机:它的主要职责是分析项目结构,识别 JavaScript 模块以及其他浏览器无法直接运行的扩展语言(如 Scss、TypeScript 等),并将它们打包成浏览器可执行的格式。

构建过程即将源代码转换为线上可执行的 JavaScript、CSS 和 HTML 代码,具体包含以下内容:

  • 代码转换:将 TypeScript 编译为 JavaScript,将 SCSS 编译为 CSS 等文件;文件优化:压缩 JavaScript、CSS 和 HTML 代码,合并及压缩图片等。
  • 代码分割:提取多个页面的公共代码,并将首屏无需立即执行的代码异步加载。
  • 模块合并:在采用模块化架构的项目中,存在大量模块和文件,构建功能需将分类后的模块合并为单一文件。
  • 自动刷新:监听本地源代码的变化,自动重新构建并刷新浏览器。
  • 代码校验:在代码提交至仓库前,检测代码是否符合规范,并验证单元测试是否通过。
  • 自动发布:代码更新后,自动构建线上发布代码并传输至发布系统。

构建本质上是工程化与自动化思想在前端开发中的体现。通过代码实现一系列流程,使复杂的操作自动化执行。

Webpack 的基本概念

入口(entry point):指示 Webpack 应使用哪个模块作为构建内部依赖图的起点,Webpack 会找出入口起点(直接和间接)依赖的所有模块和库。

默认值为 ./src/index.js,但可以通过在 Webpack 配置中设置 entry 属性来指定不同的入口起点(或指定多个入口)。

出口(output):属性告知 Webpack 在哪里输出创建的 bundles,以及如何命名这些文件。主输出文件默认为 ./dist/main.js,其他生成文件的默认输出目录为 ./dist。

Loader:使 Webpack 能够处理非 JavaScript 文件(Webpack 自身仅理解 JavaScript)。Loader 可将所有类型的文件转换为 Webpack 能处理的有效模块,从而利用其打包能力进行处理。

注意,Loader 能够导入任何类型的模块(例如 .css 文件),这是 Webpack 特有的功能,其他打包程序或任务执行器可能不支持。我们认为这种语言扩展非常必要,因为它能让开发人员创建更准确的依赖关系图。

插件(plugins):Loader 用于转换特定类型的模块,而插件则用于执行更广泛的任务。插件范围包括打包优化、压缩,乃至重新定义环境变量。插件接口功能强大,可处理各种任务。

模式(mode):通过选择 development 或 production 之一设置 mode 参数,可启用相应模式下 Webpack 内置的优化功能。

Webpack 构建过程

  • 从 Entry 配置的 Module 开始,递归解析 Entry 依赖的所有 Module。
  • 每找到一个 Module,便根据配置的 Loader 找出对应的转换规则。
  • 对 Module 进行转换后,再解析出当前 Module 依赖的其他 Module。
  • 这些模块以 Entry 为单位进行分组,一个 Entry 及其所有依赖的 Module 被分到一个组,即一个 Chunk。
  • 最后,Webpack 将所有 Chunk 转换为文件输出。
  • 在整个流程中,Webpack 会在恰当的时机执行 Plugin 中定义的逻辑。

开发环境与生产环境配置

开发环境和生产环境

在日常前端开发中,通常有两套构建环境:一套用于开发,另一套供线上使用。

  • development:用于开发的配置文件,用于定义 webpack dev server 及其他配置。
  • production:用于生产的配置文件,用于定义 UglifyJSPlugin、sourcemaps 等。

简单来说,开发时可能需要打印 debug 信息,包含 sourcemap 文件;而生产环境用于线上,代码经过压缩,运行时不打印 debug 信息等。例如 axios、antd 等在生产环境中使用的依赖应安装到生产环境,而 webpack-dev-server 则需安装在开发环境中。

平时我们在 npm 中安装的文件带有 -S 和 -D 参数,-D 表示依赖安装在开发环境,而 -S 表示依赖安装在生产环境。

本文将带你搭建基本的前端开发环境,前端开发环境需要哪些配置呢?

  • 构建发布所需的 HTML、CSS、JS、图片等资源。
  • 使用 CSS 预处理器,这里选用 less。
  • 配置 babel 转码器,使用 es6+。
  • 处理和压缩图片。
  • 配置热加载,即 HMR。

以上配置即可满足前端开发的基本需求。以下是本文打包后的效果图:

Webpack 核心概念与构建流程 

搭建基本开发环境

搭建基本的开发环境

安装


mkdir webpack-dev && cd webpack-dev
npm init -y
npm i webpack webpack-cli -D

添加 scripts

生成了 package.json 文件,在文件中添加


 "scripts": {
 "build": "webpack --mode production"
 }

-- mode 模式(必选,否则会出现 WARNING),这是 webpack4 新增的参数选项,默认值为 production。

--mode production 生产环境

  • 提供 uglifyjs-webpack-plugin 代码压缩。
  • 无需定义 new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") })。
  • 默认 production 开启 NoEmitOnErrorsPlugin -> optimization.noEmitOnErrors,编译出错时跳过输出,以确保输出资源不包含错误。
  • 默认开启 ModuleConcatenationPlugin -> optimization.concatenateModules,webpack3 添加的作用域提升(Scope Hoisting)。

--mode development 开发环境

  • 使用 eval 构建 module,提升增量构建速度。
  • 无需定义 new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),默认 development。
  • 默认开启 NamedModulesPlugin -> optimization.namedModules,使用模块热替换(HMR)时会显示模块的相对路径。

添加 scripts 后,新建 src/index.js,然后执行 npm run build,你会发现新增了一个 dist 目录,其中存放的是 Webpack 构建好的 main.js 文件。

ps npm scripts 使用指南

Webpack 基础配置详解

新建 webpack.config.js 文件

若要在 Webpack 中增加更多配置信息,需建立 Webpack 配置文件。在根目录下创建 webpack.config.js 后执行 webpack 命令,Webpack 便会使用该配置文件的配置。

配置中包含以下基本信息:


module.exports = {
 entry: '', // 打包入口:指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始
 output: '', // 出口
 resolve: {}, // 配置解析:配置别名、extensions 自动解析确定的扩展等等
 devServer: {}, // 开发服务器:run dev/start 的配置,如端口、proxy等
 module: {}, // 模块配置:配置loader(处理非 JavaScript 文件,比如 less、sass、jsx、图片等等)等
 plugins: [] // 插件的配置:打包优化、资源管理和注入环境变量
}

配置打包入口和出口

首先向 webpack.config.js 添加配置信息。


const path = require('path')

module.exports = {
 // 指定打包入口
 entry: './src/index.js',

 // 打包出口
 output: {
 path: path.resolve(__dirname, 'dist'), // 解析路径为 ./dist
 filename: 'bundle.js'
 }
}

上面我们定义了打包入口 ./src/index.js,打包出口为 ./dist,打包文件夹名为 bundle.js。执行 npm run build 命令后,index.js 文件会被打包为 bundle.js 文件。此时随便建立一个 html 文件引用这个 bundle.js,即可看到你在 index.js 中编写的代码。

path.resolve([...paths]) 方法会将一个路径或路径片段的序列解析为绝对路径。

HTML 与 CSS 处理

使用 html-webpack-plugin 创建 html 文件

更多情况下我们不希望每次打包都新建 html 文件来引用打包后的文件,这样显得不够智能,或者说当打包文件名修改后,引用路径就会出错。

此时我们可以使用 html-webpack-plugin 插件,将 HTML 引用路径与构建结果关联起来。


npm install html-webpack-plugin -D

创建文件 public/index.html,修改 webpack.config.js 文件。


const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
 //...
 plugins: [
 new HtmlWebpackPlugin({
 filename: 'index.html', // 配置输出文件名和路径
 template: './public/index.html' // 配置要被编译的html文件
 })
 ]
}

重新执行 npm run build,dist 目录便会多出 index.html 并引入了 bundle.js。

压缩 html 文件

修改 webpack.config.js。


const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
 //...
 plugins: [
 new HtmlWebpackPlugin({
 filename: 'index.html', // 配置输出文件名和路径
 template: './public/index.html', // 配置要被编译的html文件
 hash: true,
 // 压缩 => production 模式使用
 minify: {
 removeAttributeQuotes: true, //删除双引号
 collapseWhitespace: true //折叠 html 为一行
 }
 })
 ]
}

打包 css 文件

我们希望使用 Webpack 来构建 CSS 文件,为此需在配置中引入 loader 来解析和处理 CSS 文件:


npm install style-loader css-loader -D

新建 src/assets/style/color.css,修改 webpack.config.js 文件:


module.exports = {
 //...
 module: {
 /**
 * test: 匹配特定条件。一般是提供一个正则表达式或正则表达式的数组
 * include: 匹配特定条件。一般是提供一个字符串或者字符串数组
 * exclude: 排除特定条件
 * and: 必须匹配数组中的所有条件
 * or: 匹配数组中任何一个条件,
 * nor: 必须排除这个条件
 */
 rules: [
 {
 test: /\.css$/,
 include: [path.resolve(__dirname, 'src')],
 use: ['style-loader', 'css-loader']
 }
 ]
 }
 //...
}

经过上述两个 loader 处理后,CSS 代码会转变为 JS。若需将 CSS 文件单独分离出来,需使用 mini-css-extract-plugin 插件。

抽取 css 到独立文件,自动添加前缀


npm i mini-css-extract-plugin postcss-loader autoprefixer -D

编写 CSS 时需考虑浏览器兼容性问题,如 transform 属性,需添加浏览器前缀以适配其他浏览器。因此使用 postcss-loader 这个 loader,以下是相关配置。

webpack.config.js


const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
 // ...
 module: {
 rules: [
 {
 test: /\.css$/,
 include: [path.resolve(__dirname, 'src')],
 use: [
 MiniCssExtractPlugin.loader,
 'css-loader',
 {
 loader: 'postcss-loader',
 options: {
 plugins: [require('autoprefixer')]
 }
 }
 ]
 }
 ]
 },
 plugins: [
 //...
 new MiniCssExtractPlugin({
 filename: '[name].css',
 chunkFilename: '[id].css'
 })
 ]
}

Less 与图片打包

打包 less 文件

开发中通常会用到一门预处理语言,这里以 less 为例,通过 less-loader 可将 less 打包为 css 文件。


npm install less less-loader -D

新建 src/assets/style/index.less,并在 src/index.js 中引入 import './assets/style/index.less'。

配置 webpack.config.js。


const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
 module: {
 rules: [
 // ...
 {
 test: /\.less$/,
 use: [
 MiniCssExtractPlugin.loader,
 'css-loader',
 {
 loader: 'postcss-loader',
 options: {
 plugins: [require('autoprefixer')] // 添加css中的浏览器前缀
 }
 },
 'less-loader'
 ]
 }
 ]
 }
 //...
}

执行打包命令后,便可发现 index.less 中编写的样式会与 color.css 一样被打包进 main.css 中。

webpack@v4 升级踩坑:关于使用 mini-css-extract-plugin 的注意点。

打包图片


npm install file-loader url-loader -D

file-loader:可用于处理多种类型的文件,其主要作用是直接输出文件,返回构建后的文件路径。

url-loader:若图片较多,会发出大量 HTTP 请求,降低页面性能。url-loader 会将引入的图片编码,生成 dataURL。相当于把图片数据翻译成一串字符,再将该字符串打包到文件中,最终只需引入该文件即可访问图片。当然,若图片较大,编码会消耗性能。因此 url-loader 提供了一个 limit 参数,小于 limit 字节的文件会被转为 DataURL,大于 limit 的仍会使用 file-loader 进行 copy。

  • url-loader 可视为增强版的 file-loader。
  • url-loader 将图片编码为 base64 格式写入页面,从而减少服务器请求。

module.exports = {
 module: {
 rules: [
 // ...
 {
 test: /\.(png|jpg|gif)$/,
 use: [
 {
 loader: 'url-loader',
 options: {
 outputPath: 'images/', //输出到images文件夹
 limit: 500 //是把小于500B的文件打成Base64的格式,写入JS
 }
 }
 ]
 }
 ]
 }
 //...
}

url-loader 和 file-loader 是什么关系呢?

简单来说,url-loader 封装了 file-loader。url-loader 不依赖于 file-loader,即使用 url-loader 时,只需安装 url-loader 即可,无需安装 file-loader,因为 url-loader 内置了 file-loader。

通过上述介绍,我们可以看到,url-loader 的工作分为两种情况:

  • 文件大小小于 limit 参数,url-loader 会将文件转为 DataURL;
  • 文件大小大于 limit,url-loader 会调用 file-loader 进行处理,参数也会直接传给 file-loader。因此只需安装 url-loader 即可。

有关 url-loader 和 file-loader 的解析:webpack 之图片引入-增强的 file-loader:url-loader

Babel 转码与 Polyfill 配置

配置 babel

babel-loader

Babel 是一个让我们能够使用 ES 新特性的 JS 编译工具,我们可在 Webpack 中配置 Babel,以便使用 ES6、ES7 标准编写 JS 代码。

Babel 7 的相关依赖包需加上 @babel scope。主要变化是 presets 设置由原来的 env 换成了 @babel/preset-env,可配置 targets、useBuiltIns 等选项以编译出兼容目标环境的代码。其中 useBuiltIns 若设为 "usage",Babel 会根据实际代码中使用的 ES6/ES7 代码及指定的 targets,按需引入对应的 polyfill,而无需在代码中直接引入 import '@babel/polyfill',避免输出包过大,同时可放心使用各种新语法特性。


npm i babel-loader @babel/core @babel/preset-env -D

笔者这里配置的版本号如下


{
 "babel-loader": "^8.0.4",
 "@babel/core": "^7.1.2",
 "@babel/preset-env": "^7.1.0"
}

babel-loader:用 babel 转换 ES6 代码需使用 babel-loader。

@babel-preset-env:默认情况下等于 ES2015 + ES2016 + ES2017,即对这三个版本的 ES 语法进行转化。

@babel/core:babel 核心库。

根目录下新建 .babelrc 文件。


{
 "presets": [
 [
 "@babel/preset-env",
 {
 "modules": false,
 "targets": {
 "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
 },
 "useBuiltIns": "usage"
 }
 ]
 ]
}
  • presets 是一堆 plugins 的预设,起到方便作用。
  • plugins 是编码转化工具,Babel 会根据配置的插件对代码进行相应转化。

修改 webpack.config.js。


module.exports = {
 module: {
 rules: [
 //...
 {
 test: /\.m?js$/,
 exclude: /(node_modules|bower_components)/,
 use: {
 loader: 'babel-loader'
 }
 }
 ]
 }
}

babel/polyfill 和 transform-runtime

Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API,如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及定义在全局对象上的方法(如 Object.assign)都不会转码。

babel-polyfill:如上述所述,对于新的 API,可能需要引入 babel-polyfill 进行兼容。

关键点

  • babel-polyfill 旨在模拟完整的 ES2015+环境,适用于应用程序而非库/工具。
  • babel-polyfill 会污染全局作用域。

babel-runtime 的作用:

  • 提取辅助函数。ES6 转码时,Babel 需要一些辅助函数,如 _extend。Babel 默认将这些辅助函数内联到每个 JS 文件中,Babel 提供 transform-runtime 将这些辅助函数“搬”到单独的模块 babel-runtime 中,此举能减小项目文件大小。
  • 提供 polyfill:不会污染全局作用域,但不支持实例方法如 Array.includes。

babel-runtime 更像是分散的 polyfill 模块,需在各自模块中单独引入,借助 transform-runtime 插件自动化处理这一切,即无需在文件开头 import 相关的 polyfill,只需使用,transform-runtime 会帮你引入。

对于开发应用而言,直接使用上述按需 polyfill 方案较为方便,但若开发工具、库,则此方案未必适合(babel-polyfill 是通过向全局对象和内置对象的 prototype 上添加方法实现,会造成全局变量污染)。Babel 提供另一种方案 transform-runtime,其在编译过程中仅将需要 polyfill 的代码引入指向 core-js 中对应模块的链接(alias)。关于这两种方案的具体差异和选择,可自行搜索相关教程,此处不再展开,下面提供 transform-runtime 的参考配置方案。

首先安装 runtime 相关依赖


npm i @babel/plugin-transform-runtime -D
npm i @babel/runtime -S

修改 .babelrc。


{
 //...
 "plugins": ["@babel/plugin-transform-runtime"]
}

代码优化与清理

打包前清理源目录文件 clean-webpack-plugin

每次打包都会生成项目的静态资源,随着某些文件的增删,dist 目录下可能产生不再使用的静态资源。Webpack 不会自动判断哪些资源是需要的,为避免这些旧文件部署到生产环境占用空间,在 Webpack 打包前最好清理 dist 目录。


npm install clean-webpack-plugin -D

修改 webpack.config.js 文件。


const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
 plugins: [new CleanWebpackPlugin(['dist'])]
}

提取公用代码

假如你 a.js 和 b.js 都 import 了 c.js 文件,这段代码便显得冗杂。提取公共代码的目的在于减少代码冗余,提高加载速度。


module.exports = {
 //...
 optimization: {
 splitChunks: {
 cacheGroups: {
 commons: {
 // 抽离自己写的公共代码
 chunks: 'initial',
 name: 'common', // 打包后的文件名,任意命名
 minChunks: 2, //最小引用2次
 minSize: 0 // 只要超出0字节就生成一个新包
 },
 styles: {
 name: 'styles', // 抽离公用样式
 test: /\.css$/,
 chunks: 'all',
 minChunks: 2,
 enforce: true
 },
 vendor: {
 // 抽离第三方插件
 test: /node_modules/, // 指定是node_modules下的第三方包
 chunks: 'initial',
 name: 'vendor', // 打包后的文件名,任意命名
 // 设置优先级,防止和自定义的公共代码提取时被覆盖,不进行打包
 priority: 10
 }
 }
 }
 }
}

hash

hash 的作用是什么?我们每次打包出的结果可能都是同一个文件,上线时是否需要替换上线的 JS?如何知道哪一个是最新的?我们通常会清理缓存。而 hash 正是为了解决此问题而存在。

此时我们在修改一些 webpack.config.js 的配置。


module.exports = {
 //...
 output: {
 path: path.resolve(__dirname, 'dist'),
 filename: '[name].[hash:8].js'
 },
 //...
 plugins: [
 new MiniCssExtractPlugin({
 filename: '[name].[hash:8].css',
 chunkFilename: '[id].[hash:8].css'
 })
 ]
}

减少 resolve 的解析,配置别名

若能精简 resolve 配置,使 webpack 在查询模块路径时尽可能快速定位到所需模块,不做额外查询工作,则 webpack 的构建速度也会更快。


module.exports = {
 resolve: {
 /**
 * alias: 别名的配置
 *
 * extensions: 自动解析确定的扩展,
 * 比如 import 'xxx/theme.css' 可以在extensions 中添加 '.css', 引入方式则为 import 'xxx/theme'
 * @default ['.wasm', '.mjs', '.js', '.json']
 *
 * modules 告诉 webpack 解析模块时应该搜索的目录
 * 如果你想要添加一个目录到模块搜索目录,此目录优先于 node_modules/ 搜索
 * 这样配置在某种程度上可以简化模块的查找,提升构建速度 @default node_modules 优先
 */
 alias: {
 '@': path.resolve(__dirname, 'src'),
 tool$: path.resolve(__dirname, 'src/utils/tool.js') // 给定对象的键后的末尾添加 $,以表示精准匹配
 },
 extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx'],
 modules: [path.resolve(__dirname, 'src'), 'node_modules']
 }
}

本地服务与热更新(HMR)

webpack-dev-serve

上述内容均涉及文件打包,但开发中我们需要本地服务,此时可使用 webpack-dev-server 在本地开启简单的静态服务进行开发。

webpack-dev-server 是 Webpack 官方提供的工具,可基于当前 Webpack 构建配置快速启动静态服务。当 mode 为 development 时,具备 hot reload 功能,即当源码文件变化时,会即时更新当前页面,以便看到最新效果。...


npm install webpack-dev-server -D

在 package.json 的 scripts 中添加


"start": "webpack-dev-server --mode development"

默认开启一个本地服务窗口 http://localhost:8080/ 便于开发。

配置开发服务器

我们可以对 webpack-dev-server 进行针对性配置。


module.exports = {
 // 配置开发服务器
 devServer: {
 port: 1234,
 open: true, // 自动打开浏览器
 compress: true // 服务器压缩
 //... proxy、hot
 }
}
  • contentBase:服务器访问的根目录(可用于访问静态资源)。
  • port:端口。
  • open:自动打开浏览器。

模块热替换(hot module replacement)

模块热替换(HMR - Hot Module Replacement)功能可在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面。主要通过以下几种方式显著加快开发速度:

  • 保留在完全重新加载页面时丢失的应用程序状态。
  • 仅更新变更内容,以节省宝贵的开发时间。
  • 调整样式更加快速——几乎相当于在浏览器调试器中更改样式。

上面我们 npm start 后修改一次文件,页面便会刷新一次。这就存在很大问题,例如使用 redux、vuex 等插件,页面一刷新,存放在 redux、vuex 中的数据便会丢失,非常不利于开发。

HMR 配合 webpack-dev-server,首先配置 webpack.config.js。


const webpack = require('webpack')

module.exports = {
 devServer: {
 //...
 hot: true
 },
 plugins: [
 new webpack.HotModuleReplacementPlugin()
 //...
 ]
}

配置后仍不行,因为 Webpack 尚不知你要更新何处,需修改 src/index.js 文件,添加


if (module.hot) {
 module.hot.accept()
}

重启服务,npm start 之后,修改引入 index.js 文件后,页面便不会重新刷新,这便实现了 HMR。

但是,有个问题是,你修改 css/less 等样式文件并未发生改变,what?

HMR 修改样式表需借助于 style-loader,而我们之前使用的是 MiniCssExtractPlugin.loader,这也好办,修改其中一个 rules 即可,我们可以试试改


module.exports = {
 module: {
 rules: [
 {
 test: /\.less$/,
 use: [
 // MiniCssExtractPlugin.loader,
 'style-loader',
 'css-loader',
 {
 loader: 'postcss-loader',
 options: {
 plugins: [require('autoprefixer')] // 添加css中的浏览器前缀
 }
 },
 'less-loader'
 ]
 }
 ]
 }
}

这样我们修改 less 文件便会发现 HMR 已实现。

其实,我们可以发现,dev 下配置的 loader 为 style-loader,而生产环境下则需要 MiniCssExtractPlugin.loader。

这就涉及到了不同环境之间的配置。可通过 process.env.NODE_ENV 获取当前是开发环境还是生产环境,然后配置不同的 loader,此处不再展开。下一篇文章打算做一个 react-cli 或 vue-cli 的配置,将开发环境的配置与生产环境的配置分开为不同的文件。

结语

前面讲解的知识均为 Webpack 的基础知识,更多资料可查询 Webpack 中文官网,官网讲解较为详细。本文主要介绍最常用的配置,属于入门系列文章,文中涉及的知识点还有许多地方需要完善,例如优化 Webpack 的构建速度、减小打包体积等。

学习 webpack 4.0 还需多实践、多尝试,笔者也是刚刚学习 Webpack 的配置,如有不对之处请各位指出。

下一篇文章打算从零配置一个脚手架,以加深自己对 Webpack 的理解。

本文产生的代码:webpack-dev

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章