前端Webpack面试题

这篇具有很好参考价值的文章主要介绍了前端Webpack面试题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

备注:本文是总结他人资料,供面试背诵使用

1.说说你对webpack的理解

​ 开发时,我们会使用框架 (React、Vue) ,ES6 模块化语法,Less/Sass 等 CSS 预处理器等语法进行开发,这样的代码要想在浏览器运行必须经过编译成浏览器能识别的 JS、CSS语法才能运行。所以我们需要打包工具帮我们做完这些事。除此之外,打包还能压缩代码做兼容性处理提升代码性能等。
​ webpack 是一个静态模块的打包工具。它会在内部从一个或多个入口点构建一个依赖图,然后将项目中所需的每一个模块组合成一个或多个 bundles 进行输出,它们均为静态资源。输出的文件已经编译好了,可以在浏览器运行。 webpack 具有打包压缩、编译兼容、能力扩展等功能。其最初的目标是实现前端项目的模块化,也就是如何更高效地管理和维护项目中的每一个资源。

2.说说webpack的构建流程

初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数

开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译

确定入口:根据配置中的 entry 找出所有的入口文件

编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理

完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系

输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表

输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

在以上过程中,webpack会在特定的时间点广播出特定的事件,插件在监听到事件后会执行特定的逻辑,并且插件可以调用webpack提供的API改变webpack的运行结果

3.Loader是什么?

​ 其作用是让 Webpack 能够去处理那些非 JavaScript 文件。由于 Webpack 自身只理解 JavaScript、JSON ,其他类型/后缀的文件都需要经过 loader 处理,并将它们转换为有效模块。loader 可以是同步的,也可以是异步的;而且支持链式调用,链中的每个 loader 会处理之前已处理过的资源。
loader的两个属性:
1.test,识别出哪些文件会被转换
2.use,在进行转换时,使用的loader
多loader的执行顺序,从右到左(从下到上)

4.常用的Loader

babel-loader 将ES6代码转换成ES5版本

ts-loader ts转js 如果没有babel,使用ts-loader,有babel用@babel/preset-typescript

sass-loader sass转换css

style-loader 创建

css-loader 将css转为js模块导出

url-loader url-loader可以在图片大小小于设定的limit的时候返回的是base编码的图片,大于limit时会调用file-loader对图片进行处理。

file-loader 其实就是将文件拷贝到输出目录下,使用file-loader中配置的新名字

postcss-loader (1)将css解析成js可以操作的AST,(2)调用插件来处理AST并得到结果。

经常配合autoprefixer自动为CSS添加浏览器前缀

html-loader 将html代码转换为js模块导出

eslint-loader 检查代码是否符合eslint规范,只检查错误,不修改代码

5.手写Loader

const loaderUtils = require('loader-utils');

module.exports = function(source){

  // 知识点一  如果为每个构建执行重复的转换操作,这样webpack的构建可能会变得非常慢
  // 开启缓存
  this.cacheable && this.cacheable();

  // 知识点二  使用loaderUtils 获取loader的参数
  const options = loaderUtils.getOptions(this);
  console.log('options ->', options);

  // 知识点三  告诉 Webpack 本次转换是异步的,Loader 会在 callback 中回调结果 
  // 异步用this.axync() 同步用this.callback()
  var callback = this.async()    
  // someAsyncOperation 代表一些异步的方法
  someAsyncOperation(source).then(res => {
      // 通过 callback 返回异步执行后的结果
      callback(err, result, sourceMaps, ast)
  })



  // 真正的手写代码
  const reg = /(console.log((.*)))/g;
  source = source.replace(reg, '');
  this.callback(null, source);
  // return undefined的作用是让webpack知道loader返回的结果应该在this.callback当中,而不是return中
  return;
}

/** 
this.callback(    
  // 当无法转换原内容时,给 Webpack 返回一个 Error   
  err: Error | null,    
  // 原内容转换后的内容    
  content: string | Buffer,    
  // 用于把转换后的内容得出原内容的 Source Map,方便调试
  sourceMap?: SourceMap,    
  // 如果本次转换为原内容生成了 AST 语法树,可以把这个 AST 返回,以方便之后需要 AST 的 Loader 复用该 AST,以避免重复生成 AST,提升性能 
  abstractSyntaxTree?: AST
); 
*/

6.Plugins是什么?

​ 由于webpack基于发布订阅模式,在运行的生命周期中会广播出许多事件,插件通过监听这些事件,就可以在特定的时机执行自己的插件任务,webpack有两个核心对象:

​ compiler:包含了 webpack 环境的所有的配置信息,包括 options,loader 和 plugin,和 webpack 整个生命周期相关的钩子

​ compilation:作为 plugin 内置事件回调函数的参数,包含了当前的模块资源、编译生成资源、变化的文件以及被跟踪依赖的状态信息。当检测到一个文件变化,一次新的 Compilation 将被创建

1.插件必须是一个带有apply方法的对象

2.不建议修改compiler和compilation对象,因为传入的同一个对象的引用

class CopyrightWebpackPlugin {
  // 编写一个构造器
  // 参数初始化
  constructor(options) {
       console.log(options)
   }

  apply(compiler) {
      //遇到同步时刻   同步用tap
      compiler.hooks.compile.tap('CopyrightWebpackPlugin',() => {
          console.log('compiler');
      });

      //遇到异步时刻  异步用tapAsync
      // emit 输出asset到output目录之前执行
      //Compilation存放打包的所有内容,Compilation.assets放置生成的内容
      compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin', (Compilation, callback) => {
          debugger;
          // 往代码中增加一个文件,copyright.txt
          Compilation.assets['copyright.txt'] = {
              source: function() {
                  return 'copyright by monday';
              },
              size: function() {
                  return 19;
              }
          };
          callback();
      })
  }
}

module.exports = CopyrightWebpackPlugin;

7.常用的Plugin

1.HtmlWebpackPlugin

2.CleanWebpackPlugin

3.CopyWebpackPlugin

4.Webpack.DefinePlugin 定义全局变量

5.MiniCssExtractPlugin 将css抽取成单独的文件

6.PurgeCSSPlugin 删除没用到的CSS样式,需要判断引用文件,起到css tree-shaking的效果

7.webpack.ProvidePlugin 配置全局模块,避免多次引入的麻烦

8.BundleAnalyzerPlugin 分析打包后资源的依赖以及大小

9.ImageminPlugin 压缩图片

10.CompressionPlugin 启用传输压缩,gzip压缩,需要服务端配合

11.webpack.NoEmitOnErrorsPlugin 遇到编译报错不输出

12.OptimizeCssAssetsPlugin 压缩css,去除重复的类名样式,去除无用的空格

13.UglifyJSPlugin 压缩JS

14.webpack.HotModuleReplacementPlugin 热更新插件

15.TerserPlugin 压缩es6,相比UglifyJsPlugin插件,能更好的处理ES6以上的语法

8.实现一个Plugin

​ 由于webpack基于发布订阅模式,在运行的生命周期中会广播出许多事件,插件通过监听这些事件,就可以在特定的时机执行自己的插件任务,webpack有两个核心对象:

​ compiler:包含了 webpack 环境的所有的配置信息,包括 options,loader 和 plugin,和 webpack 整个生命周期相关的钩子

​ compilation:作为 plugin 内置事件回调函数的参数,包含了当前的模块资源、编译生成资源、变化的文件以及被跟踪依赖的状态信息。当检测到一个文件变化,一次新的 Compilation 将被创建

在这里插入代码片class CopyrightWebpackPlugin {
  // 编写一个构造器
  // 参数初始化
  constructor(options) {
       console.log(options)
   }

  apply(compiler) {
      //遇到同步时刻   同步用tap
      compiler.hooks.compile.tap('CopyrightWebpackPlugin',() => {
          console.log('compiler');
      });

      //遇到异步时刻  异步用tapAsync
      // emit 输出asset到output目录之前执行
      //Compilation存放打包的所有内容,Compilation.assets放置生成的内容
      compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin', (Compilation, callback) => {
          debugger;
          // 往代码中增加一个文件,copyright.txt
          Compilation.assets['copyright.txt'] = {
              source: function() {
                  return 'copyright by monday';
              },
              size: function() {
                  return 19;
              }
          };
          callback();
      })
  }
}

module.exports = CopyrightWebpackPlugin;

9.Source map是什么?

source map是将编译、打包、压缩后的代码映射和回源代码的过程。打包压缩后的代码不具备良好的可读性,想要调试代码就需要source-map,提高开发效率

10.文件指纹是什么?

文件指纹是打包文件的唯一标识,文件指纹通常有两个用途:

  • 版本管理: 在发布版本时,通过文件指纹来区分 修改的文件 和 未修改的文件。

  • 使用缓存: 未修改的文件,文件指纹保持不变,浏览器继续使用缓存访问。

    Hash 是和整个项目的构建相关,compilation 实例的变化就会触发 Hash 的变化。

    Chunkhash 是和 webpack 打包的模块相关,每一个 entry 作为一个模块,会产生不同的 Chunkhash 值,所以他们之间的变化是互不影响的。

    Contenthash 是和根据文件内容相关,比如一个页面内的 JS 内容、CSS 内容都会拥有自己的 Contenthash,可以保持各自的独立更新。

11.说说webpack热更新的原理

​ HMR全称Hot Module Replacement,可以理解为模块热替换,指在应用程序运行过程中,替换,添加,删除模块,而无需重新刷新整个应用。
​ 例如,我们在应用运行过程中修改了某个模块,通过自动刷新会导致整个应用的整体刷新,那页面中的状态信息都会丢失如果使用的是 HMR,就可以实现只将修改的模块实时替换至应用中,不必完全刷新整个应用

前端Webpack面试题,前端,webpack,node.js

webpack的原理:

  • 启动阶段
    1-2-A-B 在编写为经webpack打包的源代码后,webpacl compiler将源代码和HMR Runtime一起编译成bundle文件,放在webpack Dev Server,当浏览器请求bundle.js文件时,bundle.js就返回给浏览器,运行在浏览器上
    (socket.js在服务器和浏览器建立了一个websocket长链接)

  • 更新阶段
    1-2-3-4 当某一个文件或者模块发生变化时,webpack监听到文件变化对文件重新编译打包,编译生成唯一的hash值,本次生成的hash值会作为下一次热更新的标识,也就是本次热更新使用的hash值,是上一次热更新生成文件的hash(此次的hash值是上次热更新中manifest中h的值)

    由于socket服务器在HMR RuntimeHMR Server之间建立 websocket链接,当文件发生改动的时候,服务端会向浏览器推送一条消息,消息包含文件改动后生成的hash值,如下图的h属性,作为下一次热更细的标识

前端Webpack面试题,前端,webpack,node.js

浏览器根据hash标识,去创建ajax去获取此次的mainfest(h:build生成的hash值,c:变化的模块 )文件,使用jsonp获取此次的修改内容,浏览器拿到这两个文件后,通过HMR runtime机制,加载这两个文件,触发render流程,实现局部刷新

12.说说webpack proxy工作原理

​ 在开发阶段, webpack-dev-server 会启动一个本地开发服务器,所以我们的应用在开发阶段是独立运行在 localhost的一个端口上,而后端服务又是运行在另外一个地址上

所以在开发阶段中,由于浏览器同源策略的原因,当本地访问后端就会出现跨域请求的问题

通过设置webpack proxy实现代理请求后,相当于浏览器与服务端中添加一个代理者

当本地发送请求的时候,代理服务器响应该请求,并将请求转发到目标服务器,目标服务器响应数据后再将数据返回给代理服务器,最终再由代理服务器将数据响应给本地

前端Webpack面试题,前端,webpack,node.js

在代理服务器传递数据给本地浏览器的过程中,两者同源,并不存在跨域行为,这时候浏览器就能正常接收数据

注意:服务器与服务器之间请求数据并不会存在跨域行为,跨域行为是浏览器安全策略限制

13.webpack打包体积优化

一、提取公共模块

假如现在有一个MPA(多页面应用)的react项目,每个页面的入口文件及其依赖的组件中都会引入一份react和react-dom等,那最终打包后的每个页面中同样也会有一份以上的公共包的代码,可以将这个包单独抽离出来,最终在每个打包后的页面入口文件引入,从而减少打包后的总体积

module.exports = {
  optimization: {
    splitChunks: {
      minSize: 20000,
      cacheGroups: {
        react: {
          test: /(react|react-dom)/,
          name: 'vendors',
          chunks: 'all',
        },
      },
    },
  }
};

二、配置CDN服务器

1.可以修改output:{publicPath: ‘’} 的值,打包时添加上自己的CDN地址

2.在CDN上部署自己依赖的第三方资源,

3.在externals中加入不进行打包的资源,在html模版中加入CDN服务器地址

前端Webpack面试题,前端,webpack,node.js

4.如果没有自己配置CDN服务器,可以使用其他人放置在cdn服务器上的资源

三、代码压缩(此处的代码压缩并非真正的压缩,而是删掉代码中无意义的部分,例如空格)

1.压缩js,使用插件TerserPlugin

前端Webpack面试题,前端,webpack,node.js

2.压缩css,使用CSSMinimizerPlugin

css压缩通常是去除无用的空格等,因为很难去修改选择器,属性和名称、值等

前端Webpack面试题,前端,webpack,node.js

3.压缩html,使用HtmlWebpackPlugin的minify配置

new HtmlWebpackPlugin({
    template: path.join(__dirname, './public/index.html'),
    filename: 'index.html',
    minify: {            // minify配置可以压缩html文件,设置了minify,实际上会使用另一个插件html-minifier-terser
      minifyCSS: false,                    //是否压缩css
      collapseWhitespace: true,            // 是否折叠空格
      removeComments: true,                // 是否移除注释
    }
}),

前端Webpack面试题,前端,webpack,node.js

4.压缩图片,使用image-webpack-loader

这个不太理解,压缩后图片会不会失真或者像素变低?打包的时候压缩,使用的时候进行解压缩?

四、使用TreeShaking

tree shaking是一个术语,在计算机中标识消除死代码,尽量使用纯函数编程,用于消除未调用的代码

1.JS实现Tree Shaking

(1)usedExports,配置方法很简单,只需将usedExports设置为true

module.exports = {
    ...
    optimization:{
        usedExports
    }
}

使用后,没被用上的代码在webpack打包中会添加 unused harmony export … 注释,用来告知Terser在优化时,可以删掉这段代码

前端Webpack面试题,前端,webpack,node.js

(2)sideEffects实现

sideEffects用于指定哪些模块是有副作用的,副作用指的是这里面的代码有执行一些特殊的任务,不能仅仅依靠export来判断这段代码的意义

sideEffect设置为false,就是告知webpack可以安全的删除未用到的exports

sideEffect设置为数组,则将代码中的数据进行保留

前端Webpack面试题,前端,webpack,node.js

总结:

  • 在optimization中配置usedExports为true,来帮助Terser进行优化;
  • 在package.json中配置sideEffects,直接对模块进行优化;

2.CSS实现Tree Shaking

css主要使用的purgecss-webpack-plugin

前端Webpack面试题,前端,webpack,node.js

五、文件大小压缩(真正意义的压缩,gzip算法)

compression-webpack-plugin 插件

对文件的大小进行压缩,将前端打包好的资源文件进一步压缩,生成指定的、体积更小的压缩文件,减小http传输过程中带宽的损耗

new ComepressionPlugin({
    test:/\.(css|js)$/,  // 哪些文件需要压缩
    threshold:500, // 设置文件多大开始压缩
    minRatio:0.7, // 至少压缩的比例
    algorithm:"gzip", // 采用的压缩算法
})

​ 使用npm run build,会生成许多以.gz格式的文件
​ 生成压缩后的文件,不能直接使用,需要服务端配置才可以使用,而且发现打包生成的“dist/index.html”首页内,也没有直接引用这些“.gz”格式的文件。
​ 而实现的关键,其实就是让服务端向浏览器发送“Content-Encoding=gzip”这个响应头,并把对应的“.gz”格式文件发送给浏览器,让浏览器通过“gzip”编码格式来解析资源。

const path = require('path');
const fs = require('fs');
const express = require('express');
const app = express();

app.use((request, response, next) => {
 const fullPath = path.join(__dirname, `${request.originalUrl}.gz`);     
 
 // 检测是否存在同名.gz压缩文件
 if (fs.existsSync(fullPath)) {
     // 存在就告诉浏览器用gzip编码格式来解析,并把对应的“.gz”格式文件发送给浏览器。
     response.setHeader('Content-Encoding', 'gzip')  
     response.sendFile(fullPath);
 } else {
     next()
 }
})
app.use(express.static('./'));

app.listen(1055, _ => {
 console.log('1055服务器已经启动');
});

六、antd按需导入

import {Col,Row} from 'antd'

14.优化构建速度

这个问题个人认为比较宽泛,顺着聊几点就好了,答不上来也没关系,重点是上面的压缩打包后的体积

可以使用speed-measure-webpack-plugin统计总打包耗时以及每个plugin和loader的打包耗时

一、缩小构建范围、优化Loader配置

在使用loader时,可通过配置include、exclude、test属性来匹配文件

module.exports = {
  module: {
    rules: [
      {
        // 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/,提升正则表达式性能
        test: /\.js$/,
        // babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启
        use: ['babel-loader?cacheDirectory'],
        // 只对项目根目录下的 src 目录中的文件采用 babel-loader
        include: path.resolve(__dirname, 'src'),
      },
    ]
  },
};

二、优化resolve.alias

alias给一些常用的路径起一个别名,特别当我们的项目目录结构比较深的时候,一个文件的路径可能是./../../的形式

通过配置alias以减少查找过程

module.exports = {
    ...
    resolve:{
        alias:{
            "@":path.resolve(__dirname,'./src')
        }
    }
}

三、多进程构建

对于耗时较长的模块,同时开启多个 nodejs 进程进行构建,可以有效地提升打包的速度。terser启动多线程

module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: true,
      }),
    ],
  },
};

四、合理使用source-map

打包生成sourceMap的时候,信息越详细,打包速度就越慢

五、cache-loader

在一些性能开销较大的 loader之前添加 cache-loader,以将结果缓存到磁盘里,显著提升二次构建速度

保存和读取这些缓存文件会有一些时间开销,所以请只对性能开销较大的 loader 使用此loader文章来源地址https://www.toymoban.com/news/detail-648729.html

module.exports = {
    module: {
        rules: [
            {
                test: /\.ext$/,
                use: ['cache-loader', ...loaders],
                include: path.resolve('src'),
            },
        ],
    },
};

到了这里,关于前端Webpack面试题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第三节:Node.js 之 Webpack 打包

    官网网址:https://webpack.docschina.org/ 减少文件数量,缩减代码体积,提高浏览器打开速度。 用于分析、并打包代码。支持所有类型文件的打包,其本质是一个第三方模块包。 初始化包环境 安装依赖包 配置package.json 的 scripts(自定义命令) 在根目录下新建 src 文件夹, 所有要

    2024年02月15日
    浏览(52)
  • 【前端工程化面试题】使用 webpack 来优化前端性能/ webpack的功能

    这个题目实际上就是来回答 webpack 是干啥的,你对webpack的理解,都是一个问题。 (1)对 webpack 的理解 webpack 为啥提出 + webpack 是啥 + webpack 的主要功能 前端开发通常是基于模块化的,为了提高开发效率,webpack 是一个基于模块的构建工具,是一个用于 js 应用程序的静态模块

    2024年02月20日
    浏览(51)
  • Node.js入门与 Webpack模块打包工具

    目录 Node.js入门 fs模块-读写文件 path模块-路径处理  压缩前端html 认识URL中的端口号 http模块-创建Web服务 体验 Web 服务除了接口数据以外,还能返回网页资源等  Node.js模块化  ECMAScript标准-默认导出和导入 ECMAScript标准-命名导出和导入  包的概念 npm软件包管理器 npm安装所有依

    2024年02月11日
    浏览(44)
  • 前端Webpack面试题

    备注:本文是总结他人资料,供面试背诵使用 ​ 开发时,我们会使用框架 (React、Vue) ,ES6 模块化语法,Less/Sass 等 CSS 预处理器等语法进行开发,这样的代码要想在浏览器运行必须经过编译成浏览器能识别的 JS、CSS语法才能运行。所以我们需要打包工具帮我们做完这些事。除

    2024年02月13日
    浏览(33)
  • 前端面试题-webpack

    1.webpack是什么? 模块打包工具,用于将前端资源,如JavaScript、css、图片等打包成可以在浏览器运行的静态资源。可以将多个模块打包成一个或多个bundle。 主要功能: 模块化:可以将多个模块打包成一个或多个bundle,方便管理维护。 自动化:可以自动处理和编译JavaScript等文

    2024年01月17日
    浏览(35)
  • Node.JS学习 | Babel | webpack | ES6

    💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! Node.JS能够在服务器端运行JavaScript的开放源代码、跨平台运行环境;Node.js采用Google开发的V8运行代码,使用事件驱动、非阻塞IO和异步输入输出模型等技术来提高性能,可优化应用程序的传输量和规模 let有效避免var的变

    2024年02月07日
    浏览(41)
  • 【前端工程化面试题目】webpack 的热更新原理

    可以在顺便学习一下 vite 的热更新原理,请参考这篇文章。 首先有几个知识点需要明确 热更新是针对开发过程中的开发服务器的,也就是 webpack-dev-server webpack 的热更新不需要额外的插件,但是需要在配置文件中 devServer 属性中配置 hot: true,需要安装 webpack-dev-server 这个

    2024年02月19日
    浏览(57)
  • 前端面试:【前端工程化】构建工具Webpack、Parcel和Rollup

    嗨,亲爱的前端开发者!在现代Web开发中,前端工程化变得愈发重要。构建工具如Webpack、Parcel和Rollup帮助我们自动化任务、管理依赖、优化性能等。本文将深入探讨这三个前端构建工具,帮助你了解它们的优点和用途。 1. Webpack: 用途: Webpack是一个强大的模块打包工具。它

    2024年02月11日
    浏览(45)
  • 【前端工程化面试题】说一下 webpack 的构建流程

    类似问题是,说一下 vite 的构建流程,参考这篇文章。 初始化流程 从配置文件和shell 语句中读取合并参数,初始化需要使用的插件和执行环境所需要的参数 配置文件默认是 webpack.config.js 编译构建流程 解析入口模块,从入口模块开始串行调用对应的loader 去翻译文件内容,依

    2024年02月20日
    浏览(54)
  • node.js下载安装和报错大全(express、淘宝镜像、webpack、Vue)

    目录 一、概述 二、操作步骤 (一)下载node.js (二) 安装node.js (三)查看环境变量 (四)查看版本信息 (五)新建全局下载包和缓存包位置 (六)配置环境变量 (七)安装express(内嵌http模块) 安装成功 安装失败 解决办法一(最好的办法)  解决办法二(其次) (八

    2024年02月01日
    浏览(75)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包