chainWebpack长用配置方式

chainWebpack长用配置集合

webpack 配置很多,这里我们探讨比较经常需要修改的:

  • 不复杂,可以在 configWebpack 中操作:
    • mode
    • devtool
  • 配置复杂,可以在 chainWebpack 中操作:
    • module.rules
    • plugins
    • optimization

2、使用vue inspect 可以查看当前 webpack 配置

vue inspect > output.js 

chainWebpack长用配置方式

//vue.config.js
module.exports={
  chainWebpack:config=>{
  }
}
}

1 输入输出配置

module.exports = {
  chainWebpack: config => {
    // 清理所有默认入口配置
    config.entryPoints.clear();
    // 增加一个入口main
    config.entry("main").add("./src/main.js");
    // 增加一个入口about
    config.entry("about").add("./src/about.js");
    config.output
      .path("dist")//输出目录
      .filename("[name].[chunkhash].js")//输出文件名
      .chunkFilename("chunks/[name].[chunkhash].js")//输出chunk名
      .libraryTarget("umd")//输出格式
      .library();//输出库
  }
};
// 其他的output配置
config.output
  .auxiliaryComment(auxiliaryComment)
  .chunkFilename(chunkFilename)
  .chunkLoadTimeout(chunkLoadTimeout)
  .crossOriginLoading(crossOriginLoading)
  .devtoolFallbackModuleFilenameTemplate(devtoolFallbackModuleFilenameTemplate)
  .devtoolLineToLine(devtoolLineToLine)
  .devtoolModuleFilenameTemplate(devtoolModuleFilenameTemplate)
  .filename(filename)
  .hashFunction(hashFunction)
  .hashDigest(hashDigest)
  .hashDigestLength(hashDigestLength)
  .hashSalt(hashSalt)
  .hotUpdateChunkFilename(hotUpdateChunkFilename)
  .hotUpdateFunction(hotUpdateFunction)
  .hotUpdateMainFilename(hotUpdateMainFilename)
  .jsonpFunction(jsonpFunction)
  .library(library)
  .libraryExport(libraryExport)
  .libraryTarget(libraryTarget)
  .path(path)
  .pathinfo(pathinfo)
  .publicPath(publicPath)
  .sourceMapFilename(sourceMapFilename)
  .sourcePrefix(sourcePrefix)
  .strictModuleExceptionHandling(strictModuleExceptionHandling)
  .umdNamedDefine(umdNamedDefine)

2.设置别名

const path = require("path");
function resolve(dir) {
  return path.join(__dirname, dir);
}
module.exports = {
    chainWebpack: config => {
      config.resolve.alias
        .set("@$", resolve("src"))
        .set("assets", resolve("src/assets"))
        .set("components", resolve("src/components"))
        .set("layout", resolve("src/layout"))
        .set("base", resolve("src/base"))
        .set("static", resolve("src/static"))
        .delete("base"); // 删掉指定的别名
      // .clear()  会把全部别名都删掉
    }
};

3. 配置代理

module.exports = {
  chainWebpack: config => {
    config.devServer
      .port("8080")
      .open(true)
      .proxy({
        "/api": {
          target: "http://www.baidu.com",
          changeOrigin: true,
          pathRewrite: {
            "^/api": ""
          }
        }
      });
  }
};

// 注意:changeOrigin 属性名字具有误导性,它并不是表示修改 Origin 头部,而是表示修改 Host 头部的 Origin(原始值)。从上面官网的那句话中 the origin of the host 也可以理解出来。
//f12中查看未生效是正常的,因为vite node服务中代理了一次浏览器的请求,所以冲浏览器的角度来看host还是它发出请求的host,vite node代理服务器看host才是改的值
// chain其余proxy的配置
config.devServer
  .bonjour(bonjour)
  .clientLogLevel(clientLogLevel)
  .color(color)
  .compress(compress)
  .contentBase(contentBase)
  .disableHostCheck(disableHostCheck)
  .filename(filename)
  .headers(headers)
  .historyApiFallback(historyApiFallback)
  .host(host)
  .hot(hot)
  .hotOnly(hotOnly)
  .https(https)
  .inline(inline)
  .info(info)
  .lazy(lazy)
  .noInfo(noInfo)
  .open(open)
  .openPage(openPage)
  .overlay(overlay)
  .pfx(pfx)
  .pfxPassphrase(pfxPassphrase)
  .port(port)
  .progress(progress)
  .proxy(proxy)
  .public(public)
  .publicPath(publicPath)
  .quiet(quiet)
  .setup(setup)
  .socket(socket)
  .staticOptions(staticOptions)
  .stats(stats)
  .stdin(stdin)
  .useLocalIp(useLocalIp)
  .watchContentBase(watchContentBase)
  .watchOptions(watchOptions)


5. 使用插件和添加、删除插件参数

使用插件

// 添加API
config
  .plugin(name)
  .use(WebpackPlugin, args)

// 一个例子
const fileManager = require("filemanager-webpack-plugin");
...
//注意:use部分,不能使用new的方式建立插件实例
webpackConfig.plugin("zip").use(fileManager, [
    {
      onEnd: {
        archive: [
          {
            source: "dist",
            destination: zipName
          }
        ]
      }
    }
  ]);

修改参数、添加参数

module.exports = {
  chainWebpack: config => {
    // 可使用tap方式,修改插件参数
    config.plugin(name).tap(args => newArgs);

    // 一个例子
    config
      .plugin("env")
      //使用tag修改参数
      .tap(args => [...args, "SECRET_KEY"]);
    //更改html插件的title
    config.plugin("html").tap(args => {
      args[0] = "documnet的title";
      return args[0];
    });
  }
};

6.修改插件初始化和删除插件

修改插件

module.exports = {  chainWebpack: config => {    config.plugin(name).init((Plugin, args) => new Plugin(...args));  }};

删除插件

module.exports = {  chainWebpack: config => {    config.plugins.delete("prefetch");    // 移除 preload 插件    config.plugins.delete("preload");  }};

7.有前后顺序的插件

// A插件之前要调用B插件;config  .plugin(name)    .before(otherName)module.exports = {  chainWebpack: config => {    config      .plugin("b")      .use(B)      .end()      .plugin("a")      .use(A)      .before(B);  }};
// A插件之后要调用B插件;config.plugin(name).after(otherName);module.exports = {  chainWebpack: config => {    config      .plugin("a")      .after("b")      .use(A)      .end()      .plugin("b")      .use(B);  }

9. performace性能警告阀配置

config.performance  .hints(hints)//false | "error" | "warning"。打开/关闭提示  .maxEntrypointSize(maxEntrypointSize)//入口起点表示针对指定的入口,对于全部资源,要充分利用初始加载时(initial load time)期间。此选项根据入口起点的最大致积,控制 webpack 什么时候生成性能提示。默认值是:250000  .maxAssetSize(maxAssetSize)//资源(asset)是从 webpack 生成的任何文件。此选项根据单个资源体积,控制 webpack 什么时候生成性能提示。默认值是:250000  .assetFilter(assetFilter)//此属性容许 webpack 控制用于计算性能提示的文件

使用、修改、删除loader

module.exports = {  chainWebpack: config => {    config.module      .rule(name)      .use(name)      .loader(loader)      .options();  }};//修改参数module.exports = {  chainWebpack: config => {    config.module.rule(name);    use(name)      .loader(loader)      .tap(options => {        let newOptions = { ...options, xx: "黑黑" };        return newOptions;      });  }};// 覆盖原来的loadermodule.exports = {  chainWebpack: config => {    const svgRule = config.module.rule("svg");    // 清除已有的全部 loader。    // 若是你不这样作,接下来的 loader 会附加在该规则现有的 loader 以后。    svgRule.uses.clear();    svgRule.use("vue-svg-loader").loader("vue-svg-loader");  }};//使原来loader忽略某个目录,使用新的loadermodule.exports = {  chainWebpack: config => {    config.module      .rule("svg")      .exclude.add(resolve("src/icons"))      .end();    config.module      .rule("icons")      .test("/.svg$/")      .include.add(resolve("src/icons"))      .end()      .use("svg-sprite-loader")      .options({        symbolId: "icon-[name]"      })      .end();  }};

条件

config.when(process.env.NODE_ENV !== "development", config => {      config        .plugin("ScriptExtHtmlWebpackPlugin")        .after("html")        .use("script-ext-html-webpack-plugin", [          {            // `runtime` must same as runtimeChunk name. default is `runtime`            inline: /runtime\..*\.js$/          }        ])        .end();      config.optimization.splitChunks({        chunks: "all",        cacheGroups: {          libs: {            name: "chunk-libs",            test: /[\\/]node_modules[\\/]/,            priority: 10,            chunks: "initial" // only package third parties that are initially dependent          },          elementUI: {            name: "chunk-elementUI", // split elementUI into a single package            priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app            test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm          },          commons: {            name: "chunk-commons",            test: resolve("src/components"), // can customize your rules            minChunks: 3, //  minimum common number            priority: 5,            reuseExistingChunk: true          }        }      });      // https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk      config.optimization.runtimeChunk("single");    });

删除单个规则中的全部 loader

// vue.config.jsmodule.exports = {  chainWebpack: config => {    const svgRule = config.module.rule('svg')    // 清除已有规则 svg 的所有 loader。    // 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。    svgRule.uses.clear()    // 添加要替换的 loader    svgRule      .use('vue-svg-loader')        .loader('vue-svg-loader')  }}

删除单个规则中的一个loader

// 删除前:{    test: /\.m?jsx?$/,    exclude: [        function () { /* omitted long function */ }    ],    use: [        /* config.module.rule('js').use('cache-loader') */        {            loader: 'cache-loader',            options: {                cacheDirectory: 'D:\\webproject\\webapp-jy\\node_modules\\.cache\\babel-loader',                cacheIdentifier: '519fc596'            }        },        /* config.module.rule('js').use('babel-loader') */        {            loader: 'babel-loader'        }    ]}// 删除后:const jsRule = config.module.rule('js')// 删除 cache-loaderjsRule.uses.delete('cache-loader'){    test: /\.m?jsx?$/,    exclude: [        function () { /* omitted long function */ }    ],    use: [        /* config.module.rule('js').use('babel-loader') */        {            loader: 'babel-loader'        }    ]}

删除单个规则

const moduleRule = config.module// 删除命名为  js 的规则moduleRule.rules.delete('js')

删除插件

config.plugins.delete(name)

删除 optimization.minimizers

config.optimization.minimizers.delete(name)

增加规则

// loader 默认是从下往上处理// enforce: 决定现有规则调用顺序// - pre 优先处理// - normal 正常处理(默认)// - inline 其次处理// - post 最后处理module.exports = {    chainWebpack: config => {        config.module            .rule('lint') // 定义一个名叫 lint 的规则                .test(/\.js$/) // 设置 lint 的匹配正则                .pre()  // 指定当前规则的调用优先级                .include // 设置当前规则的作用目录,只在当前目录下才执行当前规则                    .add('src')                    .end()                .use('eslint') // 指定一个名叫 eslint 的 loader 配置                    .loader('eslint-loader') // 该配置使用 eslint-loader 作为处理 loader                    .options({ // 该 eslint-loader 的配置                        rules: {                            semi: 'off'                        }                    })                    .end()                .use('zidingyi') // 指定一个名叫 zidingyi 的 loader 配置                    .loader('zidingyi-loader') // 该配置使用 zidingyi-loader 作为处理 loader                    .options({ // 该 zidingyi-loader 的配置                        rules: {                            semi: 'off'                        }                    })        config.module            .rule('compile')                .test(/\.js$/)                .include                    .add('src')                    .add('test')                    .end()                .use('babel')                    .loader('babel-loader')                    .options({                        presets: [                            ['@babel/preset-env', { modules: false }]                        ]                    })    }}

最后将解析为如下配置:

{
    module: {
        rules: [
            /* config.module.rule('lint') */
            {
                test: /\.js$/,
                enforce: 'pre',
                include: ['src'],
                use: [
                    /* config.module.rule('lint').use('eslint') */
                    {
                        loader: 'eslint-loader',
                        options: {
                            rules: {
                                semi: 'off'
                            }
                        }
                    },
                    /* config.module.rule('lint').use('zidingyi') */
                    {
                        loader: 'zidingyi-loader',
                        options: {
                            rules: {
                                semi: 'off'
                            }
                        }
                    }
                ]
            },
            /* config.module.rule('compile') */
            {
                test: /\.js$/,
                include: ['src', 'test'],
                use: [
                    /* config.module.rule('compile').use('babel') */
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                ['@babel/preset-env', { modules: false }]
                            ]
                        }
                    }
                ]
            }
        ]
    }
}



config.optimization.splitChunks

Webpack 4给我们带来了一些改变。包括更快的打包速度,引入了SplitChunksPlugin插件来取代(之前版本里的)CommonsChunksPlugin插件。在这篇文章中,你将学习如何分割你的输出代码,从而提升我们应用的性能。

SplitChunks插件(webpack 4.x以前使用CommonsChunkPlugin)允许我们将公共依赖项提取到现有的entry chunk或全新的代码块中。

代码分割的理念

首先搞明白: webpack里的代码分割是个什么鬼? 它允许你将一个文件分割成多个文件。如果使用的好,它能大幅提升你的应用的性能。其原因是基于浏览器会缓存你的代码这一事实。每当你对某一文件做点改变,访问你站点的人们就要重新下载它。然而依赖却很少变动。如果你将(这些依赖)分离成单独的文件,访问者就无需多次重复下载它们了。

使用webpack生成一个或多个包含你源代码最终版本的“打包好的文件”(bundles),(概念上我们当作)它们由(一个一个的)chunks组成。

首先 webpack 总共提供了三种办法来实现 Code Splitting,如下:

  • 入口配置:entry 入口使用多个入口文件;
  • 抽取公有代码:使用 SplitChunks 抽取公有代码;
  • 动态加载 :动态加载一些代码。

这里我们姑且只讨论使用 SplitChunks

splitChunks配置

在src目录下创建三个文件pageA.js、pageB.js和pageC.js。代码详情见文章开头git仓库。

// src/pageA.js
var react = require('react');
var reactDom = require('react-dom');
var utility1 = require('../utils/utility1');
var utility2 = require('../utils/utility2');
new Vue();

module.exports = "pageA";
// src/pageB.js
var react = require('react');
var reactDom = require('react-dom');
var utility2 = require('../utils/utility2');
var utility3 = require('../utils/utility3');

module.exports = "pageB";
// src/pageC.js
var react = require('react');
var reactDom = require('react-dom');
var utility2 = require('../utils/utility2');
var utility3 = require('../utils/utility3');

module.exports = "pageC";

入口文件 && 出口文件
entry: {
    pageA: "./src/pageA",    // 引用utility1.js  utility2.js
    pageB: "./src/pageB",    // 引用utility2.js  utility3.js
    pageC: "./src/pageC",   // 引用utility2.js  utility3.js
},
output: {
    path: path.join(__dirname, "dist"),
    filename: "[name].[hash:8].bundle.js"
},

配置optimization

首先我们配置optimization如下:

optimization: {
    splitChunks: {
      chunks: "all",
  },

执行npm run build打包命令之后,查看dist目录
在这里插入图片描述

可以发现,打包出来的除了三个page文件,还存在一个vendorspageApageB~pageC.[hash].bundle.js文件(此文件中保存了pageA、pageB、pageC和node_modules中共有的size大于30KB的文件)。事实上这全靠了配置中本身默认固有一个cacheGroups的配置项:

splitChunks: {
    chunks: "all",
    cacheGroups: {
      vendors: {
        test: /[\\/]node_modules[\\/]/,  // 匹配node_modules目录下的文件
        priority: -10   // 优先级配置项
      },
      default: {
        minChunks: 2,
        priority: -20,   // 优先级配置项
        reuseExistingChunk: true
      }
    }
  }

在默认设置中,会将 node_mudules 文件夹中的模块打包进一个叫 vendors的bundle中,所有引用超过两次的模块分配到 default bundle 中。更可以通过 priority 来设置优先级。

参数说明如下:

  • chunks:表示从哪些chunks里面抽取代码,除了三个可选字符串值 initial、async、all 之外,还可以通过函数来过滤所需的 chunks;
  • minSize:表示抽取出来的文件在压缩前的最小大小,默认为 30000
  • maxSize:表示抽取出来的文件在压缩前的最大大小,默认为 0,表示不限制最大大小;
  • minChunks:表示被引用次数,默认为1;上述配置commons中minChunks为2,表示将被多次引用的代码抽离成commons。

值得注意的是,如果没有修改minSize属性的话,而且被公用的代码(假设是utilities.js)size小于30KB的话,它就不会分割成一个单独的文件。在真实情形下,这是合理的,因为(如分割)并不能带来性能确实的提升,反而使得浏览器多了一次对utilities.js的请求,而这个utilities.js又是如此之小(不划算)。

  • maxAsyncRequests:最大的按需(异步)加载次数,默认为 5;
  • maxInitialRequests:最大的初始化加载次数,默认为 3;
  • automaticNameDelimiter:抽取出来的文件的自动生成名字的分割符,默认为 ~;
  • name:抽取出来文件的名字,默认为 true,表示自动生成文件名;
  • cacheGroups: 缓存组。(这才是配置的关键)

缓存组会继承splitChunks的配置,但是test、priorty和reuseExistingChunk只能用于配置缓存组。cacheGroups是一个对象,按上述介绍的键值对方式来配置即可,值代表对应的选项。除此之外,所有上面列出的选择都是可以用在缓存组里的:chunks, minSize, minChunks, maxAsyncRequests, maxInitialRequests, name。可以通过optimization.splitChunks.cacheGroups.default: false禁用default缓存组。默认缓存组的优先级(priotity)是负数,因此所有自定义缓存组都可以有比它更高优先级(译注:更高优先级的缓存组可以优先打包所选择的模块)(默认自定义缓存组优先级为0)

现在我们再重新来看一下pageA、pageB、pageC三个js文件,这三个文件中都引入了utility2.js文件,但是此文件size很明显小于30KB,所以这部分公用代码并没有分割出来。如果想要分割出来很简单,只需要:

optimization: {
    splitChunks: {
      chunks: "all",
      minSize: 0
  },

执行npm run build打包命令之后,查看dist目录
在这里插入图片描述

显然多了一个pageApageBpageC.[hash].bundle.js文件。查看文件可得知此文件中存储了utility2.js中的代码。如下图所示(借助于webpack-bundle-analyzer插件,详情文章末尾附录)。
在这里插入图片描述

上图可以看出,React相关代码均放在了vendorspageApageB~pageC.[hash].bundle.js文件中,如果我们想要抽离出React代码,应该怎么做呐?

splitChunks: {
      chunks: "all",
      cacheGroups: {
        commons: {
          chunks: "initial",
          minChunks: 2,
          name: "commons",
          maxInitialRequests: 5,
          minSize: 0, // 默认是30kb,minSize设置为0之后
                            // 多次引用的utility1.js和utility2.js会被压缩到commons中
        },
        reactBase: {
          test: (module) => {
            return /react|redux|prop-types/.test(module.context);
          }, // 直接使用 test 来做路径匹配,抽离react相关代码
          chunks: "initial",
          name: "reactBase",
          priority: 10,
        }
      }
    },

run build之后如下图所示。

在这里插入图片描述

看似非常完美,但是reactBase文件中竟然包含了node_modules,神奇的问题?室友都睡觉了,这键盘声影响不好,明天接着看。

附录

我们再安装一个 webpack-bundle-analyzer,这个插件会清晰的展示出打包后的各个bundle所依赖的模块:

npm i webpack-bundle-analyzer -D

引入:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

使用,在plugins数组中添加即可:

new BundleAnalyzerPlugin()
config.when(process.env.NODE_ENV!== 'development',config=>{
          config.optimization.splitChunks({
                chunks: "all",   // 共有3个值"initial","async"和"all"。配置后,代码分割优化仅选择初始块,按需块或所有块
                minSize: 30000,   // (默认值:30000)块的最小大小
                minChunks: 1,    // (默认值:1)在拆分之前共享模块的最小块数
                maxAsyncRequests: 5,   //(默认为5)按需加载时并行请求的最大数量
                maxInitialRequests: 3,  //(默认值为3)入口点的最大并行请求数
                automaticNameDelimiter: '~',  // 默认情况下,webpack将使用块的来源和名称生成名称,例如vendors~main.js
                name: true,
                cacheGroups: {  // 以上条件都满足后会走入cacheGroups进一步进行优化的判断
                  vendors: {
                    test: /[\\/]node_modules[\\/]/,  // 判断引入库是否是node_modules里的
                    priority: -10,   // 数字越大优先级越高 (-10大于-20)
                    filename: 'vendors.js'  // 设置代码分割后的文件名
                      },
                  default: {   //所有代码分割快都符合默认值,此时判断priority优先级
                    minChunks: 2,  
                    priority: -20,
                    reuseExistingChunk: true   // 允许在模块完全匹配时重用现有的块,而不是创建新的块。
                  }
                }
              })
        })

在这里插入图片描述

mixin 全局注入

    css: {
        //   varibles mixin 全局注入
        loaderOptions: {
            sass: {
                data: `@import "~@/styles/variables.scss";@import "~@/styles/mixin.scss";`
            }
        }
    },
  • 11
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 如果您想删除Vue项目的node_modules文件夹,可以按照以下步骤操作: 1. 打开终端或命令行工具,进入Vue项目的根目录。 2. 输入以下命令,删除node_modules文件夹: ``` rm -rf node_modules ``` 3. 如果您使用的是Windows系统,可以使用以下命令: ``` rmdir /s node_modules ``` 请注意,删除node_modules文件夹可能会导致您的项目无法正常运行,因此在删除之前请确保备份了重要文件,并在必要时重新安装依赖项。 ### 回答2: Vue项目的node_modules是项目所依赖的所有第三方库和插件的集合,它的存在对项目的开发很重要。但是,在某些情况下,可能需要删除node_modules文件夹,比如: 1. 减轻项目占用空间 node_modules文件夹内可能包含大量的代码和资源文件,如果项目已经部署完成或已经开发结束,删除node_modules可以显著减轻项目所占用的磁盘空间。 2. 解决项目安装问题 有时候,在使用npm安装包时可能会出现奇怪的错误,比如404错误或找不到包等。此时,删除node_modules文件夹再重新安装所有依赖可以解决这些问题。 3. 优化项目性能 即使项目的依赖已经安装完成,node_modules依然可能会影响项目的性能。这是因为node_modules可能包含大量的代码和资源文件,而这些文件会占用大量的内存,影响项目运行速度。如果我们删除node_modules文件夹,可以优化项目的性能。 当然,删除node_modules文件夹也有一些风险。首先,我们必须备份项目的源代码,以免造成意外损失。其次,如果我们删除node_modules文件夹,项目要重新安装所有依赖,这可能需要一些时间。 总之,删除Vue项目的node_modules是一个需要权衡利弊的过程。如果您决定删除node_modules,请务必备份项目代码,并了解重新安装依赖所需的步骤和时间。这样可以帮助您更好地管理项目,减轻项目所占用的磁盘空间,并优化项目的性能。 ### 回答3: Vue项目的node_modules文件夹是安装在项目中的所有依赖项的集合,包括Vue本身。删除node_modules文件夹可以释放项目所占用的磁盘空间,但是在删除之前,需要考虑以下几个方面。 首先,需要备份整个项目或者至少备份node_modules文件夹。因为node_modules包含所有依赖项和配置文件,如果删除后需要重新安装依赖项,需要花费较长时间,此时备份将会节省大量时间。 其次,需要在删除node_modules文件夹之前,确认项目中是否有未提交的更改。如果有未提交的更改,需要先将它们提交并推送到代码库中,否则这些更改将会丢失。 最后,在删除node_modules文件夹之前,需要确认项目是否处于安全状态。如果在项目运行期间删除node_modules文件夹,可能会导致应用程序崩溃或者出现其他错误。建议在项目停止运行后再进行删除操作。 总之,在删除Vue项目的node_modules文件夹之前,需要考虑所有因素,并确保备份项目以避免数据丢失。同时,建议谨慎处理这个文件夹,以免造成不必要的麻烦。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值