webpack 5(初级实战篇)

一、概念

在讲webpack之前,先了解下构建工具的概念

在正常开发中,我们可能会用到css预处理器来处理页面样式,也可能在js文件中使用最新的一些语法(ES6+),这些语法都是浏览器不能直接识别的,所以都需要借助小工具来进行编译和转换,但是这个多工具管理起来就会相当的麻烦,所以后来就有了构建工具这么一说,所谓的构建工具就可以看成是以上这些小工具的汇总,他包含了这些小工具的所有功能集于一身,后续我们只需要管理好一个工具就可以了,这样对于开发者来说就会简单方便很多;

webpack 就是一个前端资源构建工具,是现代 JavaScript 应用程序的静态模块打包器(module bundler),当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle(捆)。

webpack本身是基于node开发的;像我们打包时要进行各种文件依赖关系的处理,要找到各种文件,这就是通过node中的fs模块来操作的了;

从 webpack v4.0.0 开始,可以不用引入一个配置文件。然而,webpack 仍然还是高度可配置的。

二、安装

为了防止全局安装出现的版本冲突,我们一般都把webpack安装到本地项目中;

cnpm i webpack webpack-cli --save-dev  或者  yard add webpack webpack-cli -D

三、五个核心概念

  1. Entry
    入口指示webpack以哪个文件为入口起点,然后从他开始分析内部的依赖关系
  2. Output
    定义打包输出后bundles放到哪里?如何命名?
  3. Loader
    webpack只能识别js代码,对于那些css、img等都识别不了,所以只能借助loader翻译成webpack能看懂的
  4. Plugins
    插件可以用于执行那些范围更广,功能更强大的任务,插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量
  5. Mode
    在这里插入图片描述

四、webpack初体验

4.1 运行指令

第一步:新建一个index.js和data.json文件

------------------以下为index.js---------------------------
import data from './data.json'  //引入data.json文件
console.log(data)

function add (a,b){
    return a+b
}

console.log(add(2,3));

------------------以下为data.json---------------------------
{
	name:"haha",
	age:12,
}

第二步:执行运行指令:

 - 在开发环境下执行命令:
webpack ./index.js -o ./build/built.js --mode=development
上述代码意思就是webpack会把当前路径下的index.js打包到当前路径build文件夹下,生产的新打包后的文件名叫built.js ,整体打包模式是开发模式

 - 生产环境下执行命令

webpack ./index.js -o ./build/built.js --mode=production
上述代码意思就是webpack会把当前路径下的index.js打包到当前路径build文件夹下,生产的新打包后的文件名叫built.js ,整体打包模式是生产模式


第三步 index.html 文件 中引入了打包后的built.js文件,浏览器打开页面可以看到输出5和{name:“haha”,age:12}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webpack初体验</title>
</head>
<body>
<script src="./build/built.js"></script>

</body>
</html>

结论:
1、webpack能处理js/json文件,不能处理css/img等其他资源
2、生产环境比开发环境多一个压缩代码
3、生产环境和开发环境将ES6模块编译化成浏览器能识别的模块化(import data from './data.json"这句是es6的语法)

4.2、打包样式资源

1、新建一个less格式的样式文件以及一个引入该样式文件的一个js文件

// index.less
html,body{
	margin: 0;
	padding: 0;
	background-color: pink;
}
----------------------------------------
// index.js
import './index.less'

console.log(12313)

2、然后新建一个webpack.config.js配置文件(因为loader要配合配置文件才能正常使用)

注意:这个配置文件就是用来指示webpack要怎么干活,当你运行webpack指令时,会自动加载里面的配置;
所有构建工具都是基于nodejs平台运行的,所以遵循的都是Commonjs模块规范

const {resolve} =require("path")
module.exports={
	/*入口文件*/
	entry:'./src/index.js',
	
	/*输出文件*/
	output:{
		filename:"built.js",
		path:resolve(__dirname,"build")
	},
	
	/*loader配置*/
	module:{  
		rules:[
			{
				test:/\.css$/,   //匹配css文件
				use:[     //use数组中执行顺序是从下到上
					/*创建style标签,将index.js中的样式资源插入到style,然后将整个style标签添加到head中生效*/
					"style-loader",
					/* 将css文件变成commonjs模块,加载到index.js中,里面内容是样式字符串*/
					"css-loader" 
				]
			},
			{
				test:/\.less$/,   //匹配less文件
				use:[  
					"style-loader",
					"css-loader",
					"less-loader",   //这里需要注意下要下载less-loader和less两个文件
				]
			}
		]
	},
	
	/*插件配置*/
	plugins:[],
	
	/*模式配置*/
	mode:“development”  

}

2、下载上述用到的3个loader和一个less文件(style-loader、css-loader、less-loader、less)

npm i style-loader css-loader less-loader less -D

3、找到index.js的文件路径,执行webpack即可(它会根据配置文件中的入口文件去打包)

4、最终执行webpack打包完,会在build文件下生成一个built.js文件,其中原先的css文件变成commonjs模块,加载到index.js中,在buildt.js文件中能找到这个样式文件模块的字符内容

5、为了看样式效果是否生效,我们再新建一个html文件,将打包完的js文件引入看下效果
在这里插入图片描述

4.3、打包html资源

1、创建一个html文件和index.js

// index.html文件

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<h1>hello webpack</h1>
	</body>
</html>
----------------------------------------
// index.js

function add(a,b){
	return a+b;
}

console.log(add(2,3));

2、然后写webpack配置文件

const {resolve} =require("path") 
const HtmlWebpackPlugin = require('html-webpack-plugin') //插件使用前先引入

module.exports={
	entry:"./index.js",
	output:{
		filename:"built.js",
		path:resolve(__dirname,"build")
	},
	module:{},
	plugins:[
		new HtmlWebpackPlugin({
			template:'./index.html'  //复制已有的一个自建html文件格式
		})
	],
	mode:"development"  //开发模式
}

注意: loader使用方式是下载、使用,但plugin不同的一点就是使用前还要再加一步引入

3、最终打包完的结果就是再build文件夹下生成一个built.js和一个index.html文件,这个html文件中的结构和src下自建的那个html一样,只不过就多了一个script标签,这个script标签自动引入了打包生成后的built.js文件
在这里插入图片描述

4.4、打包图片资源

4.4.1、打包样式文件中引用的图片资源
1、要想打包样式文件中引用的图片资源,则需要用到两个loader(url-loader、file-loader),那么首先就是下载这两个loader文件

npm i url-loader file-loader -D

2、新建一个项目,项目中新建一个index.html、index.js、index.less、以及imgs文件夹(用于放图片资源)以及一个webpack.config.js

//index.html
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>样式图片资源</title>
	</head>
	<body>
		<div id="box"></div>
	</body>
</html>
------------------------------------
//index.js
import './index.less'
-------------------------------------
//index.less
#div{
	height: 400px;
	width: 400px;
	background-image: url('./img/2.jpg');
	background-repeat: no-repeat;
	background-size: 100% 100%;
}
-------------------------------------
//webpack.config.js
const {resolve} =require('path')
const HtmlWebpackPlugin =require('html-webpack-plugin')

module.exports ={
	entry:"./index.js",
	output:{
		filename:'built.js',
		path:resolve(__dirname,"build")
	},
	module:{
		rules:[
			{
				test:/\.css$/,
				use:['style-loader','css-loader']
			},
			{
				test:/\.less$/,
				use:['style-loader','css-loader','less-loader']
			},
			{   
				test:/\.jpg$/,
				loader:'url-loader',     //这里只需要写url-loader即可,但还需要下载一个file-loader,因为url-loader依赖于file-loader
				options:{
					limit:10*1024  //当 图片大小小于10MB时就会被base64处理,减少请求次数,但增大的文件大小,有利有弊
				}
			}
		],
	},
	plugins:[
		new HtmlWebpackPlugin({
			template:'./index.html'
		})
	],
	mode:"development",
}

注意:再module中如果只需要一个loader的话,则test下面就写loader:,如果有多个loader作用,则需要写成use:

4.4.2、打包html文件中的图片资源

1、这里需要用到另外一个loader(html-loader),他专门是负责引入html中的图片资源的,便于能被url-loader进行处理;

npm i html-loader -D

2、这里只写相关代码了:(增加一个loader处理对象)

....
{
	test:/\.html$/,
	loader:'html-loader'
}
....

3、如果此时执行webpack还显示不了,观察下打包后的html文件中img src的地址是什么?如果是[ object module ],说明还是有问题,这是为什么呢?

注意:因为,url-loader默认使用es6 模块化解析,而html-loader引入图片是CommonJs,所以要想解决这个问题,可以把url-loader中的esModule关闭,直接在loader:'url-loader’下面加上esModule:false即可

补充:如果此时觉得打包后的图片文件名太长,不美观,想要重新定义文件名长度,则可以在url-loader的options中添加name:’[hash:10.[ext]]’,这表示取hash值的前10位作为图片文件名,图片的后缀格式还是保持图片原先的

4.5、打包其他资源

在这里插入图片描述

注意:打包其他资源时这里使用排除的方式

4.6、devServer

正常情况下,文件内容发生改变后,需要重新打包生成新的打包后的文件,才能看到最新的效果,这样效率有点低,不便于开发,所以出现了devServer(开发服务器)帮助我们做一些自动编译、自动打开浏览器并自动刷新页面的工作

下载:npm i webpack-dev-server -D
启动devServer:npx webpack-dev-server

在这里插入图片描述

五、开发环境的基本配置

const {resolve} = require('path')
const HtmlWebpackPlugin =require('html-webpack-plugin')

module.exports={
    entry:"./src/index.js",
    output: {
        path: resolve(__dirname,"build"),
        filename: "built.js"
    },
    module: {
        rules:[
            {
                test:/\.css$/,
                use:[
                    "style-loader",
                    "css-loader"
                ]
            },
            {
                test:/\.jpg$/,
                use:[
                    "url-loader"
                ]
            },
            {
                exclude: /\.(html|js|css|less|jpg|png|gif)/,
                loader: "file-loader",
                options:{
                    name:'[hash:10].[ext]'
                }
            }
        ]
    },
    plugins:[
        new HtmlWebpackPlugin(
            {
                template:"./src/index.html"
            }
        )
    ],
    mode:'development',

    devServer: {
        contentBase:resolve(__dirname,"build"),
        compress:true,
        port:9000,
        open:true,
    }
}

六、提取css文件成单独文件

这里要用到一个插件:mini-css-extract-plugin

  • 下载:npm i mini-css-extract-plugin -D
  • 配置:
const {resolve} = require('path')
const HtmlWebpackPlugin =require('html-webpack-plugin')
const MiniCssExtractPlugin =require('mini-css-extract-plugin');

module.exports={
    entry:"./src/index.js",
    output: {
        path: resolve(__dirname,"build"),
        filename: "built.js"
    },
    module: {
        rules:[
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,  //这个对象自带loader取代原先的style-loader,作用就是从js中提取css文件
                    "css-loader"
                ]
            }
        ]
    },
    plugins:[
        new HtmlWebpackPlugin(
            {
                template:"./src/index.html"
            }
        ),
        new MiniCssExtractPlugin(
            filename:'css/built.css'  //对输出文件进行路径和名称自定义
        )
    ],
    mode:'development',

    devServer: {
        contentBase:resolve(__dirname,"build"),
        compress:true,
        port:9000,
        open:true,
    }
}

注意:这个插件使用上有所不同:1、插件初始化配置;2、需要用自带loader取代srtle-loader,作用就是从js文件中提取css文件

七、css兼容性处理

需要用到postcss-loader、postcss-preset-env
下载:npm i postcss-loader postcss-preset-env -D
在这里插入图片描述
除此之外还需要哎package.json中配置browserslist属性
在这里插入图片描述

注意: 这里要注意默认是走生产环境的兼容配置,如果你要走开发环境的兼容配置的话,则还需要在配置文件中设置node的环境变量,即:
process.env.NODE_ENV=“development”

八、压缩css

需要用到optimize-css-assets-webpack-plugin插件

下载:npm i optimize-css-assets-webpack-plugin -D
配置如下:

const optimizeCssAssetsWebpackPlugin =require("optimize-css-assets-webpack-plugin")

 plugins:[
        new optimizeCssAssetsWebpackPlugin()
    ],

九、js语法检查

这个需要用到eslint-loader和eslint

  • 下载:npm i eslint-loader eslint -D
  • 配置:
rules:[
            {
                test: /\.js$/,
                exclude: /node_modules,
                loader:"eslint-loader",
                options:{}
            }
        ]

但是这样配置了以后,它还不知道要以什么样的规则去检查代码,所以还需要设置检查规则,这里推荐使用airbnb规则,这个规则需要用到3个库
eslint-config-airbnb-base、eslint-plugin-import 、eslint
所以此时还需要下载2个库(eslint前面已经下过了)

  • 下载airbnb相关的库文件:npm i eslint-config-airbnb-base eslint-plugin-import -D
  • 在package.json中配置
//增加一个配置对象"eslintConfig"
"eslintConfig":{
	"extends":"ai'rbnb-base"
}

十、js兼容性处理

要用到babel-loader、@babel/core(都需先下载)

方案一:通过@babel/preset-env 实现

//webpack.config.js文件中的loader配置

rules:[
    {
        test:/\.js$/,
        exclude:/node_modules/,
        loader:"babel-loader",
        options:{
            presets:["@babel/preset-env"]
        }
    }
]

**问题:**它只能转换兼容一些基本的语法,比如箭头函数转成function形式等,不能处理兼容想promise等高级语法

方案二:全部js兼容处理(@babel/polyfill)

首先还是要下载该库npm i @babel/polyfill -D
使用方式:直接挂在js文件中引入即可import "@babel/polyfill"

**问题:**将所有的兼容代码都引入了,代码体积很大

方案三:按需加载(兼容):core-js

下载core-js: npm i core-js -D

然后改造下配置文件,具体如下:
在这里插入图片描述

十一、js压缩

这个很简单,只需将开发环境改为生产环境即可:mode:”production“

十二、html压缩

在原先的html插件配置中进行修改配置,增加一个minify属性,具体见下:
在这里插入图片描述

十三、性能优化配置

  • 开发环境性能优化:①主要是优化构建速度;②优化代码调试
  • 生产环境性能优化:①主要是优化构建速度;②优化代码运行性能

13.1、开发环境优化-HMR

概念:hot module replacement 热模块替换/模块热替换
作用:一个模块发生变化,只会重新打包这个模块,极大提高构建速度
如何使用:在devServer配置中添加一个hot:true,加了这句代码意思就是开启了HMR功能,但是不同文件对改功能实现不同,我们分别来看。。

注意:修改了配置文件,一定要重启,不然不生效

  • 样式文件: 此时修改样式文件时它能做到样式模块的局部更新,能实现HMR功能,但一定要使用style-loader,这个loader中已经内部实现了
  • js文件:默认不能使用HMR功能,需要增加代码来支持
    在这里插入图片描述

注意:HMR功能对js文件的处理时,只能处理非入口文件,因为入口文件引入了所有资源,它一变,所有文件都会重新加载,这是没办法避免的

  • html文件:默认不能使用HMR功能,同时会导致一个问题,html文件不能更新了,此时需要修改入口文件,将html文件引入entry:[ './src/index' ,'./src/index.html'],html正常不用做HMR功能,因为它只有一个文件,没有其他文件

13.2、开发环境优化-source-map

概念:一种提供源代码到构建后代码的映射 技术(如果构建后代码出错了,通过映射可以追踪到源代码错误)
在这里插入图片描述
在这里插入图片描述

总结:根据不同的环境,选择不同的类型,开发环境可以使用eval-source-map,生产环境用source-map

13.3、生产环境优化-oneOf

产生背景:配置文件中的所有loader在匹配的过程中都会从上往下匹配执行一遍,为了提高效率,将所有loader可以用oneOf包裹,这样,这里面的loader对于同一种文件类型只会匹配一个,所以这也是它使用过程中的注意点,不能有两个loader处理同一种类型文件

rules:[
	{
		oneOf:[   
			{
				loader1....
			},
			{
				loader2....
			}
		]
	}
]

那么有一个问题,如果我确实有两个loader要处理同一种类型文件怎么办?

rules:[
	{
				loader3....
	},
	----------------------可以将loader单独提取到oneOf外面---------------------------------
	{
		oneOf:[   
			{
				loader1....
			},
			{
				loader2....
			}
		]
	}
]

13.4、生产环境优化-缓存

背景:像开发环境基于dev-server,有HMR可以实现单一文件修改后局部更新,但是生产环境不用dev-server,所以遇到这种情况下为了更快更高效的构建,所以需要用到缓存

这里提到的缓存主要是两种:babel缓存、文件资源缓存

  1. babel缓存
{	
	test: /\.js$/,
	exclude:/.node_modules/,
	loader:"babel-loader",
	options:{
		cacheDirectory:true    //开启babel缓存,第二次构建的时候会读取缓存
	}
}
  1. 文件资源缓存

这个文件资源缓存通常是服务器后端设置了缓存的时间,前端第一次请求时会缓存起来,第二次请求页面时如果在强缓存期间就不会发请求,而是直接读取缓存了,但是这有个问题就是如果我此时修改了某个文件,刷新不会生效。。。因为走了缓存,那怎么办?

方案一:可以在配置文件中给打包后文件名添加构建时生产的唯一hash值,这样每次构建时hash值都会变化,文件名也就变了,也就不会走缓存了

output:{
	filename:'js/built.[hash:10].js',  //去hash值得前10位
}

问题:这样一来,如果某些文件没改动,但也加了这个hash值,就也会跟着重新请求,其他没变的文件缓存也失效了,那要怎么办呢?

方案二:不用hash值,用chunkhash,这个chunkhash会根据chunk生产的hash,如果打包来源于同一个chunk,那么hash值一样,这个方案貌似还是不行,因为有些像css文件,构建时会被引入到js文件中,他们属于同一个chunk,所以还是会同时变化。。。

方案三:contenthash:根据文件内容生产的hash值,不同文件,hash值一定不一样,这个就可以了

13.5、生产环境优化-tree shaking(树摇)

作用:为了去除无用代码,减少代码体积
前提:1、必须使用es6模块化;2、必须开启production生产模式,只要满足上述两个条件就会自动帮你树摇了
问题:但有些不同的版本中,可能会把你引入的css文件当成无用文件删除了,所以最好你在package.json中配置下'sideEffects':["*.css","*.less"],写了这个,这个数组中的文件就不会被tree shaking

13.6、生产环境优化-code-split(代码分割)

背景:像之前一个入口js引入了另外的js,那么构建后会自动耦合成一个js文件,但如果我想要让其单独打包,便于我后续按需加载或者其他操作,那要如何操作呢?

  • 方式一:将单一入口文件改为多入口文件方式
entry:{     //这样就会输出两个bundle文件
	main:'./src/js/index.js',
	test:'./src/js/test.js'
}
  • 方式二:在配置文件中增加一个optimization属性,具体如下:
plugins:[....],

// 和plugins同级的属性配置
optimization:{
	splitChunks:{
		chunks:"all"
	}
}

这个配置的作用:
1、它会把node_modules中的第三方包文件单独打包成一个chunk输出 ;
2、遇到多入口时,它会自动分析多入口chunk中有没有公共的文件,如果有会打包成单一chunk,比如上面的多入口例子main.js引入了jquery,test.js中也引入了jquery,那么最终只会输出3个文件(main.js 、test.js、jquery.js),而不是4个

13.7、生产环境优化-js文件的懒加载和预加载

在这里插入图片描述

13.8、生产环境优化-PWA

概念:渐进式网络开发应用程序 ,可以让你在离线状态下访问页面

如何使用:

  1. 先在webpack.config.js文件中下载一个插件(workbox-webpack-plugin),然后配置
const WorkWebpackPlugin =require('workbox-webpack-plugin')

new WorkWebpackPlugin (
	clientsClaim:true,
	skipWaiting:true
)
//这两个配置有什么用呢?
1、帮助serviceworker快速启动
2、删除旧的serviceworker,生成一个serviceworker的配置文件
  1. 在入口js文件中注册serviceworker
    在这里插入图片描述

3、 这样写后会有问题,如果你在配置文件中配了eslint的话,此时eslint会报错,因为eslint不认识window、navigator等全局变量,所以还需要在packsge.json中修改eslintConfig配置

"env":{
	"browser":true    //支持浏览器端全局变量
}

4、由于serviceworker代码必须运行在服务器端,所以必须新建一个服务器,这里我们用serve库来创建服务器,先下载npm i serve -g ,然后执行serve -s build 启动服务器,这样它就会把build文件下的所有资源作为静态资源暴露出去,然后打开服务启动后的地址就可以看到效果了

5、如果成功了,可以在页面的Application中看到注册的service-worker资源,以及Cache中缓存的东西
在这里插入图片描述
在这里插入图片描述

13.9、生产环境优化-多进程打包

下载一个包:npm i thread-loader -D

一般和babel-loader一同使用,因为项目中一般是js代码最多,所以在处理js代码时会比较慢

使用方式:

{
	test: /\.js$/,
	exclude:/node_modules/,     
	use:[
		"thread-loader",    //就是和babel-loader放一起,这里就要改为[]
		{
			loader:'babel-loader',
			options{
				.....
			}
		}
	]
}

//注意:这个开启多线程打包也是需要消耗时间的,只有真的打包时间很久时采用多线程打包,不然比以前没用时还慢下·

13.10、生产环境优化-externals

作用:是防止一些包被打包输出,也就是你可以自定义哪些包你不要打包的,但是要注意,这些包因为没被打包,所以需要你在html中手动引入cdn的一些链接
在这里插入图片描述

13.11、生产环境优化-dll

  1. 新建一个webpack.dll.js文件,下面是文件内容,就是把需要单独打包的文件在这里声明输出,下面的例子就是单独打包jquery
    在这里插入图片描述

2、新建完这个文件后,执行webpack --config webpack.dll.js 这句代码的意思就是执行.dll这个配置文件(默认是webpack.config.js)执行后输出两个文件,一个jquery.js,还有一个manifest.json,这是单独打包的文件的一种映射关系

3、然后再webpack.package.js配置文件中告诉webpack哪些包不用打包了,通过manifest告诉webpack,因为它存有映射关系
在这里插入图片描述

注意:dll与externals的区别:dll是需要打包的,只不过打包一次,后续就不需要打包了,但externals是不打包的,通过cdn链接引入的

十四、webpack详细配置

14.1、entry

在这里插入图片描述

14.2、output

常用的配置如下:
在这里插入图片描述

14.3、module

在这里插入图片描述

14.4、resolve

在这里插入图片描述

14.5、devServer

devServer:{
	//运行代码的目录
	contentBase:resolve(__dirname,"build"),
	//监视contentbase目录下的所有文件,一旦改变,就会reload
	watchContentBase:resolve(__dirname,"build"),
	//忽略文件
	watchOptions:{
		ignored:/node_modules/
	}
	//启动压缩
	compress:true,
	//端口号
	port:5000,
	//域名
	host: "localhost",
	//自动打开浏览器
	open:true,
	//开启HMR功能
	hot:true
	//不要显示启动服务器日志信息
	clientLogLevel:"none"
	//除了一些基本信息外,其他都不要显示
	quiet:true,
	//如果出错了,不要全屏提示
	overlay:false,
	//代理
	proxy:{
		"/api":{
			"target":"http://localhost:3000",  //一旦devServer(5000)服务器接收到/api请求时就会把请求转发给另外一个服务器(3000)
			"pathRewrite":{    //发送请求时,请求路径重写,将/api/xxx=>/xxx,去掉前面的前缀
				'^/api':''
			}
		}
	}
}

十五、webpack5

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ronychen’s blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值