webpack配置

webpack基础配置教程

1、初识Webpack

  • 什么是webpack
    • Webpack是一个模块打包器(bundler)。
    • 在Webpack看来, 前端的所有资源文件(js/json/css/img/less/…)都会作为模块处理
    • 它将根据模块的依赖关系进行静态分析,生成对应的静态资源
  • 五个核心概念
    • Entry:入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。
      entry: ['./src/js/app.js','./src/app.html'],
    • Output:output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./disoutput: {
      //文件夹路径
        path: resolve(__dirname, 'build'),
        //文件名称
        filename: 'js/app.js',
        //设置资源的路径,代码部署在 CDN 上的地址
        publicPath: '/'
      },t。
      
    * Loader:loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只能解析 JavaScript)。
    * Plugins:插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
    * Mode:模式,有生产模式production和开发模式development
    
  • 理解Loader
    • Webpack 本身只能加载JS/JSON模块,如果要加载其他类型的文件(模块),就需要使用对应的loader 进行转换/加载
    • Loader 本身也是运行在 node.js 环境中的 JavaScript 模块
    • 它本身是一个函数,接受源文件作为参数,返回转换的结果
    • loader 一般以 xxx-loader 的方式命名,xxx 代表了这个 loader 要做的转换功能,比如 json-loader。
  • 理解Plugins
    • 插件可以完成一些loader不能完成的功能。
    • 插件的使用一般是在 webpack 的配置信息 plugins 选项中指定。
  • 配置文件(默认)
    • webpack.config.js : 是一个node模块,返回一个 json 格式的配置信息对象

2、开启项目

  • 初始化项目:
    • 使用npm inityarn init生成一个package.json文件
      {
        "name": "webpack_test",
        "version": "1.0.0"
      } 
      
  • 安装webpack
    • npm install webpack webpack-cli -g //全局安装,作为指令使用
    • npm install webpack webpack-cli -D //本地安装,作为本地依赖使用

3、处理js和json文件

  • 创建js文件
    • src/js/app.js
    • src/js/module1.js
    • src/js/module2.js
  • 创建json文件
    • src/json/data.json
  • 创建主页面:
    • src/index.html
  • 运行指令
    • 开发配置指令:webpack src/js/app.js -o build/js/app.js --mode=development
      • 功能: webpack能够打包js和json文件,并且能将es6的模块化语法转换成浏览器能识别的语法
    • 生产配置指令:webpack src/js/app.js -o build/js/app.js --mode=production
      • 功能: 在开发配置功能上加上一个压缩代码的功能
    • index.html页面中引入:build/js/app.js
  • 结论:
    • webpack能够编译打包js和json文件
    • 能将es6的模块化语法转换成浏览器能识别的语法
    • 能压缩代码
  • 缺点:
    • 不能编译打包css、img等文件
    • 不能将js的es6基本语法转化为es5以下语法
  • 改善:使用webpack配置文件解决,自定义功能

4、使用webpack配置文件

  • 目的:在项目根目录定义配置文件,通过自定义配置文件,还原以上功能
  • 文件名称:webpack.config.js
  • 文件内容:
	const { resolve } = require('path'); //node内置核心模块,用来设置路径。
	
	module.exports = {
		entry: './src/js/app.js', // 入口文件配置(精简写法)
		/*完整写法:
		entry:{
			main:'./src/js/app.js'
		}
		*/
		output: { //输出配置
			filename: './js/app.js',//输出文件名
			path: resolve(__dirname, 'build')//输出文件路径(绝对路径)
		},
		mode: 'development'   //开发环境(二选一)
		//mode: 'production'   //生产环境(二选一)
	};
  • 运行指令: webpack

5、打包less、css资源

  • 概述:less、css文件webpack不能解析,需要借助loader编译解析

  • 创建less文件

    • src/css/demo1.less
    • src/css/demo2.css
  • 入口app.js文件

    • 引入less、css资源
  • 安装loader

    • npm install css-loader style-loader less-loader less -D
  • 配置loader

    {
    	// 处理less资源
    	test: /\.less$/,
    	use: [
    		'style-loader', //创建style标签,将js中的样式资源插入进行,添加到head中生效
    		'css-loader', //将css文件变成commonjs模块加载js中,里面内容是样式字符串
    		'less-loader' //将less文件编译成css文件
    	]
    },
    {
    	// 处理css资源
    	test: /\.css$/,
    	use: [ // use数组中loader执行顺序:从右到左,从下到上 依次执行
    		'style-loader',// 创建style标签,将js中的样式资源插入进行,添加到head中生效
    		'css-loader'// 将css文件变成commonjs模块加载js中,里面内容是样式字符串
    	]
    },
    
  • 运行指令:webpack

6、打包html文件

  • 概述:借助html-webpack-plugin插件打包html资源

  • 创建html文件

    • src/index.html
    • 注意:不要在该html中引入任何css和js文件
  • 安装插件:html-webpack-plugin

    • npm install html-webpack-plugin -D
  • 在webpack.config.js中引入插件(插件都需要手动引入,而loader会自动加载)

    • const HtmlWebpackPlugin = require(‘html-webpack-plugin’)
  • 配置插件Plugins

    plugins: [
      new HtmlWebpackPlugin({
    	// 以当前文件为模板创建新的HtML(1. 结构和原来一样 2. 会自动引入打包的资源)
        template: './src/index.html', 
      }),
    ]
    
  • 运行指令:webpack

7、打包样式中的图片

  • 概述:图片文件webpack不能解析,需要借助loader编译解析
  • 添加2张图片:
    • 小图, 小于8kb: src/images/vue.png
    • 大图, 大于8kb: src/images/react.jpg
  • 在less文件中通过背景图的方式引入两个图片
  • 安装loader
    • npm install file-loader url-loader file-loader -D
    • 补充:url-loader是对象file-loader的上层封装,使用时需配合file-loader使用。
  • 配置loader
    {
    	// 处理图片资源
    	test: /\.(jpg|png|gif)$/,
    	loader: 'url-loader', //url-loader是对file-loader的上层封装
    	options: {
    		limit: 8 * 1024, //临界值为8KB,小于8KB的图片会被转为base64编码
    		name: '[hash:10].[ext]', //加工后图片的名字
    		// 关闭es6模块化
    		esModule: false, //防止html中<img>变为[object,Object]的问题
    		outputPath: 'imgs' //输出路径
    	}
    },
    

8、打包html中的图片

  • 概述:html中的<img>标签url-loader没法处理,需要引入其他loader处理。

  • 添加图片

    • 在src/index.html添加一个img标签,src/images/angular.png
  • 安装loader

    • npm install html-loader --save-dev
  • 配置loader

    {
    	// 处理html中<img>资源
    	test: /\.html$/,
    	loader: 'html-loader'
    	options: {
    		esModule: false, //防止html中<img>变为[object,Object]的问题
    	}
    },
    
  • 坑:打包后html文件中的图片的src变成了:[object Module],

  • 解决办法:url-loader中加入一个配置:esModule:false

  • 运行指令:webpack

9、打包其他资源

  • 概述:其他资源(字体、音视频等)webpack不能解析,需要借助loader编译解析
  • 以处理几个字体图标的字体为例,media下添加几个下载好的字体文件:
    • src/media/iconfont.eot
    • src/media/iconfont.svg
    • src/media/iconfont.ttf
    • src/media/iconfont.woff
    • src/media/iconfont.woff2
  • 修改incofont.css中字体的url
    @font-face {
      font-family: 'iconfont';
      src: url('../media/iconfont.eot');
      src: url('../media/iconfont.eot?#iefix') format('embedded-opentype'),
      url('../media/iconfont.woff2') format('woff2'),
      url('../media/iconfont.woff') format('woff'),
      url('../media/iconfont.ttf') format('truetype'),
      url('../media/iconfont.svg#iconfont') format('svg');
    }
    
    .iconfont {
      font-family: "iconfont" !important;
      font-size: 16px;
      font-style: normal;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }
    
  • 修改html,添加字体图标,例如:<span class="iconfont icon-icon-test"></span>
  • 配置loader
    {
    	// 处理其他资源(字体、音视频等等)
    	exclude: /\.(html|js|css|less|jpg|png|gif)/, //排除的文件
    	loader: 'file-loader',
    	options: {
    		name: '[hash:10].[ext]', //命名
    		outputPath: 'media' //输出路径
    	}
    }
    
  • 运行指令:webpack

10、自动打包运行(decServer)

  • 安装webpack-dev-server

    • npm install webpack-dev-server --save-dev
  • 详细配置见官网:指南 -> 开发环境 -> 使用webpack-dev-server

  • 修改webpack配置对象,追加devServer配置(注意不是loader中追加)

    //devServer配置(开发模式所特有的配置)
    devServer: {
    	contentBase: resolve(__dirname, 'build'),//本地打包文件的位置
    	compress: true, //启用gzip压缩
    	port: 3000, //端口号
    	open: true //自动打开浏览器
    }
    
  • 修改package.json中scripts指令

    • "dev": "webpack-dev-server",
  • 运行指令:npm run dev 或者 yarn dev


至此,你已经完成了用webpack搭建一个简单的开发环境,但这套配置只适用于开发过程中调试代码,项目上线并不能运用这套配置,因为你还有很多的问题没有处理,比如:css还不是单独的文件,css、js还有很多兼容性问题等等,接下来我们开始去搭建生产环境。

文件准备:

1.新建config文件夹,重命名webpack.config.js为webpack.dev.js,放入config文件夹
2.复制webpack.dev.js,重命名为webpack.prod.js,删除其中的devServer配置,因为这是开发环境特有的,生产环境是不需要的
3.修改package.json中scripts指令:

	"dev": "webpack-dev-server --config ./config/webpack.dev.js",
	"build": "webpack --config ./config/webpack.prod.js"

4.修改output中path为:path: resolve(__dirname, '../build')

11、提取css为单独文件

  • 安装插件

    • npm install mini-css-extract-plugin -D
  • 引入插件

    • const MiniCssExtractPlugin = require(“mini-css-extract-plugin”);
  • 配置loader

    //引入mini-css-extract-plugin,用于提取css为单独文件
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    {
       // 处理less资源
       test: /\.less$/,
       use: [
         MiniCssExtractPlugin.loader,
         'css-loader',
         'less-loader',
       ]
     },
    {
       // 处理css资源
       test: /\.css$/,
       use: [
         MiniCssExtractPlugin.loader,
         'css-loader',
       ]
    }
    
  • 配置插件

    //提取css为单独文件
      new MiniCssExtractPlugin({
      	// 对输出的css文件进行重命名
      	filename: 'css/built.css',
      })
    
  • 运行指令

    • npm run build
  • 备注:由于在提取了独立的文件,要从外部引入,所以可能会有路径问题,解决方案是在output配置中,添加:publicPath:'/'publicPath根据实际情况自行调整,若上线运行值为/,若本地右键运行值为/build/

12、css兼容性处理

  • 安装loader
    • npm install postcss postcss-loader postcss-preset-env -D
  • 因为css和less样式文件都要进行兼容性处理,所以我们定义好一个通用的配置:
// 配置一个commonCssLoader,处理less和css时都会使用
const commonCssLoader = [
	MiniCssExtractPlugin.loader, //提取css为单独的文件
		'css-loader', //将css文件变成commonjs模块加载js中,里面内容是样式字符串
		{
		//注意:想让postcss-loader工作,还需在package.json中定义browserslist配置兼容程度
		loader: 'postcss-loader',
		options: {
		  ident: 'postcss',
		  plugins: () => [require('postcss-preset-env')()]
		}
	}
];
  • 修改css-loader和less-loader配置
	{
		// 处理css资源
		test: /\.css$/,
		use: [...commonCssLoader]
	},
	{
		// 处理less资源
		test: /\.less$/,
		use: [...commonCssLoader, 'less-loader']
	},
  • 配置package.json,在其中追加browserslist配置,通过配置加载指定的css兼容性样式

    "browserslist": {
    	// 开发环境
    	"development": [
    		"last 1 chrome version",
    		"last 1 firefox version",
    		"last 1 safari version"
    	],
    	// 生产环境:默认是生产环境
    	"production": [
    		">0.2%", //兼容市面上99.8%的浏览器
    		"not dead", //"死去"的浏览器不做兼容,例如IE8
    		"not op_mini all",//不做欧朋浏览器mini版的兼容
    		"ie 10" //兼容IE10
    	]
    }
    
  • 备注1: 注意上方browserslist中的development和production,并不是看webpack配置文件中mode的取值,而是看node中的一个环境变量:process.env.NODE_ENV,这个值默认是production,若想修改则使用:process.env.NODE_ENV = development

  • 备注2: browserslist 是一套描述产品目标运行环境的工具,它被广泛用在各种涉及浏览器/移动端的兼容性支持工具中,详细配置规则参考:https://github.com/browserslist/browserslist

  • 运行指令:

    • npm run build

13、js语法检查

  • 概述:对js基本语法错误/隐患,进行提前检查

  • 安装loader

    • npm install eslint-loader eslint
  • 安装检查规则库:

    • npm install eslint-config-airbnb-base eslint-plugin-import
  • 备注:eslint-config-airbnb-base定制了一套标准的、常用的js语法检查规则,推荐使用

  • 配置loader

    module: {
      rules: [
        {
            // 对js进行语法检查
            test: /\.js$/,
            exclude: /node_modules/,
            // 优先执行
            enforce: 'pre',
            loader: 'eslint-loader',
            options: {
              fix: true //若有问题自动修复,重要!!!!
        	}
    	}      
      ]
    }
    
  • 修改package.json

    "eslintConfig": {
      "extends": "airbnb-base", //直接使用airbnb-base提供的规则
      "env": {
       "browser": true
      }
     }
    
  • 运行指令:webpack

  • 备注:若出现:warning Unexpected console statement no-console警告,意思是不应该在项目中写console.log(),若想忽略,就在要忽略检查代码的上方输入一行注释:// eslint-disable-next-line即可。

14、js语法转换

  • 概述:将浏览器不能识别的新语法转换成原来识别的旧语法,做浏览器兼容性处理

  • 安装loader

    • npm install babel-loader @babel/core @babel/preset-env --save-dev
  • 配置loader

    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
            loader: "babel-loader",
            options: {
              presets: ['@babel/preset-env']
            }
          }
        }
      ]
    }
    
  • 运行指令:webpack

15、js兼容性处理

第一种方法:使用经典的polyfill
  • 安装包

    • npm install @babel/polyfill
  • 使用

    - app.js
    
    	import '@babel/polyfill'; // 包含ES6的高级语法的转换
    
  • 优点:解决babel只能转换部分低级语法的问题(如:let/const/解构赋值…),引入polyfill可以转换高级语法(如:Promise…)

  • 缺点:将所有高级语法都进行了转换,但实际上可能只使用一部分

  • 解决:需要按需加载(使用了什么高级语法,就转换什么,而其他的不转换)

第二种方法:借助按需引入core-js按需引入
  • 安装包

    • npm install core-js
  • 配置loader

    {
    	test: /\.js$/,
    	exclude: /(node_modules)/,
    	use: {
    	  loader: 'babel-loader',
    	  options: {
    	    presets: [
    	      [
    	        '@babel/preset-env',
    	        {
    	          useBuiltIns: 'usage',  // 按需引入(需要使用polyfill)
    	          corejs: { version: 3 }, // 解决warning警告  必须写版本号
    	          targets: { // 指定兼容性处理哪些浏览器
    	            "chrome": "58",
    	            "ie": "9",
    	          }
    	        }
    	      ]
    	    ],
    	    cacheDirectory: true, // 开启babel缓存
    	  }
    	}
    },
    

16、压缩html、压缩js

  • 直接修改webpack.prod.js中的model为production即可。
  • 坑:若设置了模式为production,必须在new HtmlWebpackPlugin时添加配置minify: false:
	new HtmlWebpackPlugin({
  	// 以当前文件为模板创建新的HtML(1. 结构和原来一样 2. 会自动引入打包的资源)
		template: './src/index.html', 
		minify: false
	}),

以上就是webpack生产环境的配置,可以生成打包后的文件。

webpack高级配置

1、清空打包目录

  • 概述:每次打包前为了避免其他无关文件的干扰,我们最好每次都清空build文件夹

  • 所需插件:clean-webpack-plugin,命令:npm i clean-webpack-plugin -D

  • 配置:
    1.引入clean-webpack-plugin:

    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    

    2.实例化插件

    new CleanWebpackPlugin()
    

2、HMR(热模替换)

  • 概述:在开发环境中我们每次修改文件时,都会造成:整个页面的刷新,全部模块都重新打包、加载,这样效率是很低的,为了确保只是我们修改的模块重新加载,我们要用到HMR技术
  • 配置:
    1.devServer配置中追加:hot:true //开启热模替换
  • 问题:按照上述配置之后,只可以完成样式模块的HMR,且会造成:
    1. 更改html后,页面也不自动刷新了
    2. 不能实现js的HMR,
  • 解决:
    1. 修改入口文件配置为:entry: ['./src/js/index.js','./src/index.html']
    2. 编写监听完成js的HMR:
    if (module.hot) {
    	  // 若module.hot 为true,说明开启了HMR功能
    	  module.hot.accept('./module1.js', () => {
    	    //accept方法会监听 module1.js 文件的变化,一旦发生变化,会执行后面的回调函数,其他模块不会重新打包构建。
    	   
    	   //此处写需要热模替换的js代码
    	   
    	  });
    }
    

3、source-map

  • 概述:当我们的模块经过了压缩以后,若源代码出了问题,提示很不友好,例如:报错永远是js的第一行有问题,我们需要将压缩过后代码的报错,映射会源文件的具体位置,就要使用source-map

  • 配置:

    1. 追加配置:devtool: 'xxx'

    2. 上述的xxx是映射模式,可选值可参考:https://www.webpackjs.com/configuration/devtool/

    3. 开发模式推荐值:cheap-module-eval-source-map

    4. 生产模式推荐值:source-map

      module.exports = {
          .
          .
          .
          devtool:  'cheap-module-eval-source-map', //设置 devtool 策略
          mode: 'development'
      }
      
  • 备注:devtool的取值可以有很多种组合,可以参考官网的配置

4、oneOf

  • 概述:正常来讲,一个文件只能被一个loader处理。 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序,例如我们的loader中对js的处理:要先执行eslint 在执行babel
  • 配置:
		module:{
			rules:[
				{},//若有些文件不只是被一个loader处理,要把其他loader放在oneOf外侧
				{
					oneOf:[
						{},//loader1
						{},//loader2
						{},//loader3
					]
				}
			]
		}

5、开启babel缓存

  • 概述:在生产环境中我们是没有devServer的,所以我们有些时候只是改变了某几个文件,或一个文件后,重新build时,webpack也是会将所有的模块全都重新构建一遍,很浪费时间。
  • 配置:在babel-loader配置中追加配置项:cacheDirectory: true

6、强缓存下的修复

  • 概述:我们都知道,代码经过最终的打包之后是要部署在服务器上的,若服务器开启了,强缓存,就意味着:在强缓存期间,若某个文件要临时更改我们是办不到的,这时我们就要通过文件名字去解决。
  • 配置方案一:
    • 文件名都混入hash值,每次wepack构建时会生成一个唯一的hash值,我们将这个值混入文件名。
    output: {
        filename: 'js/built.[hash:10].js',
        path: resolve(__dirname, 'build')
      },
    
    new MiniCssExtractPlugin({
      filename: 'css/built.[hash:10].css'
    })
    
    • 弊端:因为每次打包都有一个唯一的hash值,所以每次打包之后没有修改的文件名字也发生了改变,就导致没有改变的文件也重新请求了服务器,效率不高。
  • 配置方案二:
    • 使用contenthash
    output: {
        filename: 'js/built.[contenthash:10].js',
        path: resolve(__dirname, 'build')
      },
    
    new MiniCssExtractPlugin({
      filename: 'css/built.[contenthash:10].css'
    })
    
    • 优势:只有当文件的内容改变时,contenthash的值才会改变,内容不变就算多次打包,contenthash值也不变。

7、tree-shaking

  • 概述:有些时候,我们一个模块向外暴露了n个函数、对象、或其他一些数据,但是我们只是用到了其中的一个或几个,那在最终打包的时候,我们只希望把我们所用的打包进去,这时候就要tree-shaking,即:去除无用代码。
  • 配置:满足两个条件webpack会自动开启tree-shaking:
  1. 必须使用ES6模块化
  2. 开启production环境
  • 备注:开启了tree-shaking后,有时候webpack会错误的把我们的样式代码全部干掉,解决办法是:在package.json中追加一个配置:"sideEffects":["*.css","*.less"]

8、代码分割(code split)

  1. 方案一:调整入口文件,写成多入口形式
    1. 存在问题:多个文件都用到第三方库会导致重复打包。
  2. 方案二:借助optimization配置将node_modules中的第三方库单独打包
    具体配置:
	 /*
	    1. 可以将node_modules中代码单独打包
	    2. 会自动分析多入口chunk中,有没有公共的文件。
	  */
	  optimization: {
	    splitChunks: {
	      chunks: 'all'
	    }
	  }
  1. 特殊情况(方案三):a.js中要引入b.js暴露出来的内容,就不可避免在a.js中引入b.js,这样就会导致webpack打包时把a.js和b.js打包成一个js文件,但有时我们的需求是b.js要是一个单独的文件。
	import(/* webpackChunkName: 'test' */'./module2').then(
		(result)=>{
			// eslint-disable-next-line
			console.log(result)
		}
	).catch(()=>{
		// eslint-disable-next-line
		console.log('error');
	})
  1. 备注:如果采取方案三可能eslint-loader的语法检查不认识方案三的引入方式,会报错,我们需要安装:babel-eslint,npm i babel-eslint -D随后再package.json中的eslintConfig中追加:"parser": "babel-eslint"

9、js文件的懒加载(lazy loading)

  • 概述:
    1. 懒加载:当文件需要使用时才加载
    2. 正常加载:可以认为是并行加载(同一时间加载多个文件)
  • 懒加载配置:
	btn.onclick = ()=>{
		import(/* webpackChunkName: 'test'*/'./test').then(({ demo }) => {
		    console.log(demo(4, 5));
		  });
	} 

10、PWA

  • 概述,PWA即:渐进式网络开发应用程序(离线可访问),使网页应用在无网络连接状态不至于白屏。
  • 使用到的技术:google推出的workbox,我们在webpack中使用的插件:workbox-webpack-plugin
  • 配置:
    1. npm i workbox-webpack-plugin -D
    2. const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
    3. 实例化插件
    new WorkboxWebpackPlugin.GenerateSW({
          /*
            1. 帮助serviceworker快速启动
            2. 删除旧的 serviceworker
            生成一个 serviceworker 配置文件~
          */
          clientsClaim: true,
          skipWaiting: true
    })
    
    1. 注册serviceWorker
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker
          .register('/service-worker.js')
          .then(() => {
            console.log('sw注册成功了~');
          })
          .catch(() => {
            console.log('sw注册失败了~');
          });
      });
    }
    

11、多进程打包

  • 概述:多进程打包可以让构建速度更快(不是绝对的)
  • 配置:npm i thread-loader -D
  • 修改babel-loader的配置,追加一个loader
{
    loader: 'thread-loader',
    options: {
      workers: 2 // 进程2个
    }
  },

带你深度解锁Webpack系列(基础篇)
带你深度解锁Webpack系列(进阶篇)
带你深度解锁Webpack系列(优化篇)
「吐血整理」再来一打Webpack面试题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值