webpack—基础配置

1.webpack 命令

官方解释:webpack是一个现代的JavaScript应用的静态模块打包工具。

(1)使用全局webpack

webpack4以上版本 webpack-cli --entry ./app.js --output build.js 
 
webpack4以下版本 webpack --entry ./app.js --output build.js 

(2)使用本地webpack

  • package.json文件中 增加下面命令,打包会优先使用项目中的webpack打包。使用默认配置文件 webpack.config.js文件进行打包。
"build": "webpack"  //npm run build
  • 指定配置文件,环境等等。
"build": "webpack webpack --config --webpack.common.js    --env development" 
  • output配置

output.chunkFilename 配置无入口的 Chunk 在输出时的文件名称。 chunkFilename和上面的filename 非常类似,但chunkFilename只用于指定 在运行过程中生成的Chunk在输出时的文件名称。会在运行时生成 Chunk的常见场景包括:使用CommonChunkPlugin、使用 import(‘path/to/module’)动态加载等。chunkFilename支持和filename一 致的内置变量。

2. 编译js

ECMAScript 6.0(简称 ES6)是 JavaScript 语言的下一代标准。它在语言层面为 JavaScript 引入了很多新语法和 API ,使得 JavaScript 语言可以用来编写复杂的大型应用程序。例如:

  • 规范模块化;
  • Class 语法;
  • 用 let 声明代码块内有效的变量 ,用 const 声明常量;
  • 箭头函数;
  • async 函数;
  • Set 和 Map 数据结构。
    -通过这些新特性,可以更加高效地编写代码,专注于解决问题本身。但遗憾的是不同浏览器对这些特性的支持不一致,使用了这些特性的代码可能会在部分浏览器下无法运行。为了解决兼容性问题,需要把 ES6 代码转换成 ES5 代码,Babel 是目前解决这个问题最好的工具。 Babel 的插件机制让它可灵活配置,支持把任何新语法转换成 ES5 的写法。

(1)编译器 babel-loader @babel/core

  • 把es6编译成es5语法可以使用 babel-loader , 而 babel-loader是利用 @babel/core来进行编译的。所以需要安装两个:
npm install babel-loader @babel/core --save-dev

webpack.config.js文件简单配置

const path = require('path');
 
 
module.exports = {
    entry: {
      app: './index.js',
    },
    output: {
        path: path.resolve(__dirname,'dist'), // 也可以这么写path: __dirname + 'dist') 
        filename: '[name].[hash:6].js' // 截取hash的前几位 './js/[name].[hash: 6].js' 在dist下面生产js文件夹
      },
    module: {
      rules: [
        {
          test: /\.js$/,
          loader: 'babel-loader'
        }
      ]
  }
}
  • 一个简单的文件结构目录
    在这里插入图片描述

(2)编译规范 @babel/preset-env

  • 指定编译规范
    在这里插入图片描述
 
const path = require('path');
 
module.exports = {
    entry: {
      app: './index.js',
    },
    output: {
        path: path.resolve(__dirname,'dist'), // 也可以这么写path: __dirname + 'dist') 
        filename: '[name].[hash:6].js' // 截取hash的前几位 './js/[name].[hash: 6].js' 在dist下面生产js文件夹
      },
    module: {
      rules: [
        {
          test: /\.js$/,
          use: {
            loader: 'babel-loader',
            options: {
              presets:[
                ['@babel/preset-env', {
                  targets: {
                    browsers: ['>1%'] // 编译成全球占有率大于1%的浏览器都可以识别
                  }
                }]
              ]
            }
          }
        }
      ]
  }
}

在这里插入图片描述

(3)编译es6方法

  • babel-loader只能编译语法,对于一些es6的方法是无能为力的。这就需要借助于一些插件。

npmi babel-polyfill babel-plugin-transform-runtime babel-runtime --save-dev
babel-polyfill

  • babel-polyfill 生成一个全局对象,原理是把所有的es6的方法用es5的方法实现一遍,。 这样的话就会增大文件的体积。
  • 入口文件直接引入
import 'babel-polyfill'
(function() {
    new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(1)
        })
    })
  
})()
  • 也可以通过这样的方式引入 webpack的入口选项配置里使用
 entry: ['babel-polyfill', './index.js'],

babel-plugin-transform-runtime

  • 这是 Babel 官方提供的一个插件,作用是减少冗余的代码。Babel 在将 ES6 代码转换成 ES5 代码时,通常需要 一些由 ES5 编写的辅助函数来完成新语法的实现,例如在转换class extent语法时会在转换后的ES5代码里注入_extent辅助函数用于实现继 承:
function _extent(target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i];
    for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key];
      }
    }
  }
  return target;
}
  • 这会导致每个使用 class extent 语法的文件都被注入重复的_extent 辅助函数代码,babel-plugin-transform-runtime的作用在于将原本注入 JavaScript文件里的辅助函数替换成一条导入语句:
var _extent = require('babel-runtime/helpers/_extent');
  • 这样能减小Babel编译出来的代码的文件大小。 需要个@babel/runtime 配置使用:
npm i @babel/plugin-transform-runtime @babel/runtime --save

修改options的配置

        "options": {
              "presets":[
                ["@babel/preset-env", {
                  "targets": {
                    "browsers": [">1%"] // 编译成全球占有率大于1%的浏览器都可以识别 node 
                  }
                }]
              ],
              "plugins": [
                  ["@babel/plugin-transform-runtime"]
              ]
            }

(4)TypeScript

  • 安装TypeScript和ts-loader
  • 在配置文件中写入ts-loader
  • tsconfig.json中配置

3 css编译

webpack是以js文件为入口文件打包的,那么项目的css怎么办?如何引入css?webpack会把所有的文件当作模块,包裹css文件,所以css文件需要引入到入口文件中。

css可以通过js引入但是必须使用loader

  1. css-loader,读取css文件
  2. style-loader,把css文件注入到js文件中,让css被引入后可以被正确的以一个style标签插入页面
  3. 两个的循序很重要,现过css-loader,交给style-loader
    npm install --save css-loader style-loader

(1) style-loader

属性:

  • insertAt: style标签插入在哪一块区域
  • insertInfo: 插入指定的dom
  • singleton: 是否合并为一个style标签
  • transform: 在浏览器环境下,插入style到页面前,使用js对css进行操作
{
          test: /\.css$/,
          use: [
            {
              loader: 'style-loader',
              options: {
                insertAt: 'top', // 'bottom' 相对于头部(只在头部区域)的top和bottom,
                before: '#mydiv' // style标签插入的位置
                insertInfo: '#mydiv', // style标签插入到哪个区域
                singleton: true, // 是否合并为一个style标签
                transform: './transform.js' // 用js对css修改
              }
            },
            {
              loader: 'css-loader',
              
            }
          ]
        }

transform.js

module.exports = function(css) {
    console.log(window)
    if(window.screen.width < 500) {
        css = css.replace('red', 'yellow')
    }
    return css
}

(2) css-loader

     {
      	loader: 'css-loader',
         options: {
           // modules: true, // css模块化
           // webpack的写法
           modules: {
             localIdentName: '[path][name]_[local]_[hash:4]'
           },
           // webpack3的写法
           // modules: true,
           // localIdentName: '[path][name]_[local]_[hash:4]'
         }
       }

(3) less和sass css的预处理语言

module.exports = {
  module: {
    rules: [
      {
        // 增加对 SCSS 文件的支持
        test: /\.scss$/,
        // SCSS 文件的处理顺序为先 sass-loader 再 css-loader 再 style-loader
        use: ['style-loader', 'css-loader', 'sass-loader'],
      },
    ]
  },
};

以上配置通过正则/.scss/匹配所有以.scss 为后缀的 SCSS 文件,再 分别使用 3个Loader去处理。具体处理流程如下。

  • 通过sass-loader将SCSS源码转换为CSS代码,再将CSS代码交给 css-loader处理。
  • ·css-loader 会找出 CSS 代码中@import 和 url()这样的导入语 句,告诉 Webpack依赖这些资源。同时支持CSS Modules、压缩CSS等 功能。处理完后再将结果交给style-loader处理。
  • style-loader会将CSS代码转换成字符串后,注入JavaScript代码中,通过JavaScript向 DOM 增加样式。如果我们想将 CSS 代码提取到 一个单独的文件中,而不是和JavaScript混在一起,则可以使用在1.5节 中介绍过的ExtractTextPlugin

(4) 把css文件提取为单独的文件

webpack3  npm i extract-text-webpack-plugin --save 
 
webpack4 npm i extract-text-webpack-plugin@next --save 
 
const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
 
module.exports = {
    entry: './index.js',
    output: {
        path: path.resolve(__dirname,'dist'), // 也可以这么写path: __dirname + 'dist') 
        filename: '[name].js' // 截取hash的前几位 './js/[name].[hash: 6].js' 在dist下面生产js文件夹
      },
    module: {
      rules: [
        {
          test: /\.js$/,
          use: {
            loader: 'babel-loader',
            "options": {
              "presets":[
                ["@babel/preset-env", {
                  "targets": {
                    "browsers": [">1%"] // 编译成全球占有率大于1%的浏览器都可以识别 node 
                  }
                }]
              ],
              "plugins": [
                  ["@babel/transform-runtime"]
              ]
            }
          }
        }, 
        {
          test: /\.css$/,
          use: ExtractTextPlugin.extract({
            // fallback: "style-loader", 
            fallback: {
              loader: 'style-loader',
              options: {
                insertAt: 'top', // 'bottom' 相对于头部(只在头部区域)的top和bottom, style标签插入的位置 {before: '#mydiv'} 
                // insertInfo: '#mydiv', // style标签插入到哪个区域
                singleton: true, // 是否合并为一个style标签
                transform: './transform.js' // 用js对css修改
              }
            },
            // use: "css-loader",
            use: {
              loader: 'css-loader',
              options: {
                // modules: true, // css模块化
                // webpack的写法
                modules: {
                  localIdentName: '[path][name]_[local]_[hash:4]'
                },
                // webpack3的写法
                // modules: true,
                // localIdentName: '[path][name]_[local]_[hash:4]'
              }
            }
          }),
        }
      ]
  },
  plugins: [
    // new ExtractTextPlugin("styles.css"),
    new ExtractTextPlugin({
      filename: '[name].min.css'
    }),
 
  ]
    // devServer: {
    //   contentBase: "/dist",
    // //   hot:true,
    //   host: "0.0.0.0",
    //   compress: true,
    //   port: 9000,
    //   overlay: true,
    //   hotOnly: true // 只用热更新 不用reload
    // },
    // plugins: [ 
    //   new HtmlWebpackPlugin({
    //     filename:'index.html',
    //     template : './index.html',
    //     minify:{
    //       removeComments:true,   //删除注释
    //       collapseWhitespace: true      //删除空格,压缩
    //     }
    //   })
    // ]
};

在这里插入图片描述

(5)postcss-loader

因为要兼容不同的浏览器,需要对css文件加前缀做一些兼容性处理,手机端的还需要把px转换成rem自适应。这里需要用到一个工具postcss-loader。

postcss 一种对css编译的工具,类似babel对js的处理,常见的功能如:

  • 使用下一代css语法
  • 自动补全浏览器前缀
  • 自动把px代为转换成rem(rem不熟悉的,点这)
  • css 代码压缩等等
    postcss 只是一个工具,本身不会对css操作,它通过插件实现功能,autoprefixer 就是其一。
npm i postcss postcss-loader autoprefixer postcss-cssnext postcss-pxtorem --save 
 
const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
 
module.exports = {
    entry: './index.js',
    output: {
        path: path.resolve(__dirname,'dist'), // 也可以这么写path: __dirname + 'dist') 
        filename: '[name].js' // 截取hash的前几位 './js/[name].[hash: 6].js' 在dist下面生产js文件夹
      },
    module: {
      rules: [
        {
          test: /\.js$/,
          use: {
            loader: 'babel-loader',
            "options": {
              "presets":[
                ["@babel/preset-env", {
                  "targets": {
                    "browsers": [">1%"] // 编译成全球占有率大于1%的浏览器都可以识别 node 
                  }
                }]
              ],
              "plugins": [
                  ["@babel/transform-runtime"]
              ]
            }
          }
        }, 
        {
          test: /\.css$/,
          use: ExtractTextPlugin.extract({
            // fallback: "style-loader", 
            fallback: {
              loader: 'style-loader',
              options: {
                insertAt: 'top', // 'bottom' 相对于头部(只在头部区域)的top和bottom, style标签插入的位置 {before: '#mydiv'} 
                // insertInfo: '#mydiv', // style标签插入到哪个区域
                singleton: true, // 是否合并为一个style标签
                transform: './transform.js' // 用js对css修改
              }
            },
            // use: "css-loader",
            use: [
              {
                loader: 'css-loader', // 让js文件识别css文件
                options: {
                // modules: true, // css模块化
                // webpack的写法
                modules: {
                  localIdentName: '[path][name]_[local]_[hash:4]' // 模块化指定class名称
                },
                // webpack3的写法
                // modules: true,
                // localIdentName: '[path][name]_[local]_[hash:4]'
              }
            },
            {
              loader: 'postcss-loader',
              options: {
                ident: 'postcss',
                plugins: [
                  require('autoprefixer')({
                    "overrideBrowserslist": [
                      '>1%', 'last 2 versions' // 指定兼容的浏览器目标,市场占有率大于1%
                    ]
                  })
                ]
              }
            }
          ]
          }),
        }
      ]
  },
  plugins: [
    // new ExtractTextPlugin("styles.css"),
    new ExtractTextPlugin({
      filename: '[name].min.css'
    }),
 
  ]
    // devServer: {
    //   contentBase: "/dist",
    // //   hot:true,
    //   host: "0.0.0.0",
    //   compress: true,
    //   port: 9000,
    //   overlay: true,
    //   hotOnly: true // 只用热更新 不用reload
    // },
    // plugins: [ 
    //   new HtmlWebpackPlugin({
    //     filename:'index.html',
    //     template : './index.html',
    //     minify:{
    //       removeComments:true,   //删除注释
    //       collapseWhitespace: true      //删除空格,压缩
    //     }
    //   })
    // ]
};

index.css文件中增加 display: flex, 打包后,自动增加了前缀
在这里插入图片描述
也可以在package.json文件中加入"browserslist": [ “>1%”, “last 2 versions” ]
在 PostCSS 启动时,会从目录下的 postcss.config.js 文件中读取所需配置,如果没有,再去webpack中找,所以也可以新建该文件,文件内容大致如下:

module.exports = {
  plugins: [
    // 需要使用的插件列表
    require('postcss-cssnext')
  ]
}

4 打包html文件

npm i html-webpack-plugin --save  

(1)options

  • filename: 打包生成后的html文件的名称
  • template: 指定一个html为模版
  • minify: 压缩html
  • inject: 是否把js,css文件插入到html
  • chunks: 多入口时,指定需要引入的chunks
    该插件将为你生成一个 HTML5 文件, 其中包括使用 script 标签的 body 中的所有 webpack 包。 只需添加插件到你的 webpack 配置如下:
  plugins: [
    // new ExtractTextPlugin("styles.css"),
    new ExtractTextPlugin({
      filename: '[name].min.css'
    }),
    // 处理html文件 打包压缩
    new HtmlWebpackPlugin({
          filename:'index.html',
          template : './index.html',
          minify:{ // 压缩
            removeComments:true,   //删除注释
            collapseWhitespace: true      //删除空格,压缩
          }
        })
 
  ]

打包发现会报错,webpack4需要这样安装插件 npm i html-webpack-plugin@next --save 这样打包就没有问题了

(2)chunks

chunks主要用于多入口文件,当你有多个入口文件,那就会编译后生成多个打包后的文件,那么chunks 就能选择你要使用那些js文件

entry: {
    index: path.resolve(__dirname, './src/index.js'),
    devor: path.resolve(__dirname, './src/devor.js'),
    main: path.resolve(__dirname, './src/main.js')
}
 
plugins: [
    new HtmlWebpackPlugin({
        chunks: ['index','main']
    })
]

5 webpack的环境

因为在不同的场景下可能需要不同的配置,使用不同的功能,所以需要区分环境:

  • 开发模式:会额外用到一些调试功能,比如webpack-dev-server, 但是为了加快调试速度,可能不会压缩、three-shaking之类的功能
  • 生成模式:为了减少文件体积,会去压缩,three-shaking等功能,但不需要webpack-dev-server或者eslint这样的调试工具。
productiondevelopment
去除无用代码webpack-dev-server
图片压缩,转base64source-map
提取公用代码代码风格检查
等等等等

"build": "webpack --env development" // 打包的时候告诉webpack是什么环境
把webpack.config.js调整为暴露一个函数, 主要内容不变

const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
 
module.exports = env => {
  console.log(env)
 const common = {
    mode: 'development',
    entry: './index.js',
    output: {
        path: path.resolve(__dirname,'dist'), // 也可以这么写path: __dirname + 'dist') 
        filename: '[name].js' // 截取hash的前几位 './js/[name].[hash: 6].js' 在dist下面生产js文件夹
      },
    module: {
      rules: [
        {
          test: /\.js$/,
          use: {
            loader: 'babel-loader',
            "options": {
              "presets":[
                ["@babel/preset-env", {
                  "targets": {
                    "browsers": [">1%"] // 编译成全球占有率大于1%的浏览器都可以识别 node 
                  }
                }]
              ],
              "plugins": [
                  ["@babel/transform-runtime"]
              ]
            }
          }
        }, 
        {
          test: /\.css$/,
          use: ExtractTextPlugin.extract({
            // fallback: "style-loader", 
            fallback: {
              loader: 'style-loader',
              options: {
                insertAt: 'top', // 'bottom' 相对于头部(只在头部区域)的top和bottom, style标签插入的位置 {before: '#mydiv'} 
                // insertInfo: '#mydiv', // style标签插入到哪个区域
                singleton: true, // 是否合并为一个style标签
                transform: './transform.js' // 用js对css修改
              }
            },
            // use: "css-loader",
            use: [
              {
                loader: 'css-loader', // 让js文件识别css文件
                options: {
                // modules: true, // css模块化
                // webpack的写法
                modules: {
                  localIdentName: '[path][name]_[local]_[hash:4]' // 模块化指定class名称
                },
                // webpack3的写法
                // modules: true,
                // localIdentName: '[path][name]_[local]_[hash:4]'
              }
            },
            {
              loader: 'postcss-loader',
              options: {
                ident: 'postcss',
                plugins: [
                  require('autoprefixer')()
                ]
              }
            }
          ]
          }),
        }
      ]
  },
  plugins: [
    // new ExtractTextPlugin("styles.css"),
    new ExtractTextPlugin({
      filename: '[name].min.css'
    }),
    // 处理html文件 打包压缩
    new HtmlWebpackPlugin({
        filename:'index.html',
        template : 'index.html',
        minify:{ // 压缩
          removeComments:true,   //删除注释
          collapseWhitespace: true      //删除空格,压缩
        },
        inject: true, // 是否自动引入 默认true true/false
    })
 
  ]
    // devServer: {
    //   contentBase: "/dist",
    // //   hot:true,
    //   host: "0.0.0.0",
    //   compress: true,
    //   port: 9000,
    //   overlay: true,
    //   hotOnly: true // 只用热更新 不用reload
    // }
  }
    return common;
};

tnpm run build 打包后看到控制台可以打印 出development

因为生产环境和开发环境需要做的不一,所以我们就需要编写两套webpack配置。

创建webpack.dev.js文件‘

 
const webpack = require('webpack')
 
module.exports = {
  devServer: {
      // contentBase: "/dist",
      hot:true, 
      host: "0.0.0.0",
      compress: true,
      port: 9000,
      overlay: true,
      // hotOnly: true // 只用热更新 不用reload
    },
    // plugins: [
    //   new webpack.HotModuleReplacementPlugin(),
    //   new webpack.NamedModulesPlugin()
    // ]
};

创建webpack.pro.js文件

 
var HtmlWebpackPlugin = require('html-webpack-plugin');
 
 
module.exports =  {
  optimization :{
    minimize: false
  },
  plugins: [
    // 处理html文件 打包压缩
    new HtmlWebpackPlugin({
        filename:'index.html',
        template : './index.html',
        minify:{ // 压缩
          removeComments:true,   //删除注释
          collapseWhitespace: true      //删除空格,压缩
        },
        inject: true, // 是否自动引入 默认true true/false
    })
 
  ]
  }

这里把webpack.config.js文件改为了webpack.common.js文件 你也可以不用改这个名字。使用默认的就行

增加引入内容

const dev = require('./webpack.dev.js');
const pro = require('./webpack.pro.js');
const merge = require('webpack-merge')

return的不再是common而是合并后的

return merge(common, env == 'production' ? pro : dev)

修改package.json文件

 "dev": "webpack-dev-server --env development --config webpack.common.js",
 "build": "webpack --env production --config webpack.common.js",

webpack --env name 指定环境 --config 指定webpack配置文件

这样就可以执行不同的命令,不同的环境下完成不同的任务。

6 devtool

在这里插入图片描述

7 图片资源的加载

(1)file-loader

    {
        test:/\.(png|jpg|jgeg|gif)$/,
        use:[
          {
            loader:'file-loader',
            options: {
              name: "[name].[hash:4].[ext]",
              outputPath: "assets/img", // outputPath: "assets/img",可以让我们指定文件夹。 
              publicPath: 'aaa'
            }
          },
        ],
      }

publicPath
在这里插入图片描述

(2)url-loader

url-loader是file-loader的二次封装,我们也可以把file-loader直接换成url-loader。一般我们都会用url-loader。增加了一些新的功能,最核心的就是base64功能。把一些资源转化成base64,好处就是减少一些http请求。

简单说下工作中遇到的问题吧,我们做的一个项目中首页用了十多张图片,每张图片都是一个静态资源,所以都会有http请求,为了减少请求,我们可以通过base64编码的方法来展示图片。webpack中有一个包叫做url-loader,他可以将html以及css中的图片打包成base64,但是js中如果有图片url并不能编译成功

{
        test:/\.(png|jpg|jgeg|gif)$/,
        use:[
          {
            loader:'url-loader',
            options: {
              name: "[name].[hash:4].[ext]",
              outputPath: "assets/img",
              publicPath: 'aaa',
              limit: 30 * 1024
            }
          },
        ],
      }

limit: 3000,只对30kb以下的图片转码成base64。 因为转码会增大文件体积,所以不会对所有图片进行转码,会影响加载速度

可以使用如下配置做适配:

module.exports = {
  module: {
    rules: [
      {
        test: /\.png$/,
        use: [{
          loader: 'url-loader',
          options: {
            // 30KB 以下的文件采用 url-loader
            limit: 1024 * 30,
            // 否则采用 file-loader,默认值就是 file-loader 
            fallback: 'file-loader',
          }
        }]
      }
    ]
  },
};

还可以使用图片压缩来优化,img-loader图片压缩 本身没有压缩功能,是由一系列的插件来做的

    {
            loader:'img-loader',
            options: {
              plugins: [
                require('imagemin-gifsicle')({  // 压缩gif
                  interlaced: false,
                  optimizationLevel: 3, // 1, 2, 3  3是极限压缩,体积很小 质量很好
                }),
                require('imagemin-mozjpeg')({ // 压缩jpeg图片
                  quality: 80, // 1-100 图片质量 越大质量越好
                  progressive: true,
                  arithmetic: false
                }),
                require('imagemin-pngquant')({ // png图片的压缩
                  floyd: 0.5,
                  speed: 2 // 1-11 越大压缩的的体积越大,跟质量成正比
                }),
                require('imagemin-svgo')({
                  plugins: [
                    { removeTitle: true },
                    { convertPathData: false }
                  ]
                })
              ]
            }
          },

如果要在html中引入图片,需要以下面的方式引入,webpack才会识别处理

 <img src="${require('./assets/img/img4.jpg')}"/>

(3)html-loader

使得html中引入的文件也可以被html识别

    {
            test: /\.html$/,
            use: {
              loader: 'html-loader',
              options: {
                attrs: ["img:data-src"]  // 或者["img":src] 就会对src属性处理
              }
            }
          },

然后这样引入图片

<img data-src="./assets/img/img4.jpg"/>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值