14:00开始面试,14:05就出来了,问题变态?

1. 如何操作让一个盒子右移50px,开销最小?

// 这种方式不会触发文档重排和重绘,性能开销很小。
.box {
    transform: translateX(50px);
}

2. 前端怎么判断一个密钥有没有被修改过?

在前端中,可以使用数字签名来验证密钥是否被修改过。数字签名是一种用于验证数据完整性和认证来源的技术,常用于验证数据是否经过篡改。

具体步骤如下:

使用私钥对数据进行签名,生成数字签名。 将数据和数字签名传输到前端。 前端使用公钥对接收到的数据和数字签名进行验证,来确定数据是否被篡改过。
这样,如果密钥或数据在传输过程中被修改,数字签名验证将失败,从而可以判断密钥是否被修改过。

在实际应用中,可以使用类似 JSON Web Token(JWT)等方式进行数字签名和验证,以确保数据完整性和安全性。

3. transform2d和3d谁性能好?

在大多数情况下,transform 属性的 2D 变换(transform: translateX(50px);)通常比 3D
变换(例如 transform: translate3d(50px, 0, 0);)性能更好。这是因为 2D
变换只涉及在二维平面上的操作,而 3D 变换涉及在三维空间内的操作,需要更复杂的计算。

另外,在移动设备上,使用硬件加速的 2D 变换通常比 3D 变换表现更好,因为硬件加速对于处理简单的 2D 变换更有效率。

总的来说,如果只需要在二维平面上进行简单的变换,推荐使用 2D 变换以获得更好的性能。只有在需要复杂的三维变换时才使用 3D 变换。

4. 怎么区分内外网?

在计算机网络中,内网和外网是相对的概念,通常用来描述一个网络环境与互联网之间的关系。

  1. 内网(Intranet)指的是一个私有网络,通常是指在一个组织或者公司内部建立的局域网。这个网络通常是受到严格管控的,只有组织内部的设备可以连接到这个网络上。

  2. 外网(Internet)指的是互联网,也就是全球范围内的公共网络,任何连接到互联网的设备都可以在全球范围内进行通信和数据交换。

要区分内网和外网,通常可以通过 IP 地址或者网络配置来判断:

  • 内网通常使用私有 IP 地址范围,比如 192.168.0.0/16、172.16.0.0/12 或 10.0.0.0/8 等。当设备的 IP 地址属于这些范围时,通常可以认为它在内网中。
  • 外网则是指除了这些私有 IP 地址之外的公共 IP 地址范围,这些 IP 地址可以在全球范围内被路由器和互联网使用。

另外,也可以通过网络配置和防火墙规则来限制内网和外网的访问权限,从而实现对内外网的区分和管理。

5. 如何写一个加密,需要可逆?

当涉及简单的可逆加密方法时,可以考虑以下几种方式:

  1. 异或运算(XOR):通过将文本与固定密钥进行异或运算来实现加密和解密。
  2. Base64编码:将二进制数据转换为文本字符串,可轻松进行编码和解码操作。
  3. 简单替换加密(Caesar Cipher):通过对字符进行偏移或替换来实现加密和解密。

这些方法都是简单易行的可逆加密方式,但安全性较低,不适合用于保护敏感数据。在实际应用中,建议选择更安全的加密算法,如CryptoJS库(对称加密算法AES)等,以确保数据的安全性。

6. 不用vuex怎么实现全局状态管理?

在Vue应用中,如果不使用Vuex来实现全局状态管理,你仍然可以采用其他方式来管理全局状态。以下是几种替代方案:

  1. 使用Vue的事件总线:你可以通过创建一个空的Vue实例作为事件总线,利用该实例来触发和监听事件,从而实现组件之间的通信和全局状态管理。
// main.js
Vue.prototype.$eventBus = new Vue();

// 在组件中触发事件
this.$eventBus.$emit('eventName', data);

// 在其他组件中监听事件
this.$eventBus.$on('eventName', (data) => {
  // 处理数据
});
  1. 通过属性传递:你可以通过props属性将状态从父组件传递给子组件,或者通过自定义事件将子组件的状态传递给父组件,从而实现状态管理。

  2. 使用混入(Mixins):通过混入可以在多个组件之间共享同样的逻辑和状态。你可以创建一个混入对象,然后在需要的组件中引入和使用。

  3. 通过localStorage或sessionStorage:将状态存储在localStorage或sessionStorage中,以实现全局状态管理。这种方式适合简单的应用程序,但不适合处理大量数据或敏感信息。

虽然这些方法可以帮助你实现全局状态管理,但随着应用规模的增长,可能会出现管理和维护上的困难。因此,对于大型应用程序,推荐使用Vuex或其他专门的状态管理工具来更好地管理全局状态。

7. 页面混动,怎么防止他的父级元素跟着滚动?

要防止页面滚动时其父级元素也跟着滚动,可以通过以下几种方法来实现:

  1. 使用CSS属性 overflow: hidden:将父级元素的overflow属性设置为hidden,可以阻止其内容滚动。这样,即使页面内容滚动,父级元素也不会跟着滚动。
.parent-element {
  overflow: hidden;
}
  1. 固定父级元素位置:通过CSS的position属性将父级元素固定在页面上的某个位置,可以使其保持固定而不随页面滚动而滚动。
.parent-element {
  position: fixed;
  top: 0;
  left: 0;
}
  1. 监听页面滚动事件并取消事件冒泡:在父级元素上监听页面滚动事件,并通过阻止事件冒泡来阻止父级元素跟着滚动。
document.addEventListener('scroll', function(event) {
  event.stopPropagation();
}, true); // 使用捕获模式捕获事件

以上方法可以根据具体情况选择适合的方式来防止父级元素跟随页面滚动。

8. webpack做了哪些优化?有哪些常见的loader和Plugin?

Webpack 是一个现代 JavaScript 应用程序的静态模块打包工具,可以将各种资源,如
JavaScript、样式表、图片等,打包成静态资源文件。在实际项目中,为了提高性能和效率,Webpack 可以进行许多优化操作。以下是
Webpack 常见的优化方式:

  1. 代码分割 (Code Splitting)

    • 通过代码分割可以将代码拆分成多个小块,实现按需加载,减少初始加载时间。
    • 使用动态 import、webpack 的 splitChunks 配置等方式实现代码分割。
  2. 懒加载 (Lazy Loading)

    • 将不同路由或组件单独打包,实现页面级懒加载,减少首屏加载时间。
    • 可以结合路由懒加载或动态 import 实现懒加载。
  3. Tree Shaking

    • 通过 Tree Shaking 只打包项目中使用的代码,去除未引用的代码,减小打包体积。
    • 主要针对 ES6 模块静态引入的情况,需要在 webpack 配置中启用 optimization.providedExports: trueoptimization.sideEffects: true
  4. 代码压缩

    • 使用 UglifyJSPlugin、TerserPlugin 等插件对代码进行压缩,减小文件体积。
    • 在生产环境下一般会自动开启代码压缩。
  5. 缓存优化

    • 使用 hash、chunkhash、contenthash 等方式生成文件名,实现文件内容改变时对应文件名也改变,防止浏览器缓存问题。
    • 利用缓存可以减少文件请求,提高加载速度。
  6. 多进程打包

    • 使用 webpack-parallel-uglify-plugin、thread-loader 等工具实现多进程打包,加快打包速度。
    • 多进程打包可以利用多核 CPU 资源,提高打包效率。
  7. 模块热替换 (Hot Module Replacement)

    • 在开发环境下使用模块热替换可以实现局部刷新,提高开发效率。
    • HMR 可以保持应用的状态,使开发过程更加流畅。
  8. 分离第三方库

    • 将第三方库单独打包,避免频繁变动的业务代码影响第三方库的缓存。
    • 可以使用 webpack 的 splitChunks 配置来实现第三方库的分离。

以上是 Webpack
中常见的优化方式,通过这些优化可以提高项目的性能、减小打包体积、加快打包速度等。根据具体项目需求和情况,可以选择适合的优化策略来优化
Webpack 打包过程。

一、Loader:

babel-loader:将ES6+的代码转换成ES5的代码。

css-loader:解析CSS文件,并处理CSS中的依赖关系。

style-loader:将CSS代码注入到HTML文档中。

file-loader:解析文件路径,将文件赋值到输出目录,并返回文件路径。

url-loader:类似于file-loader,但是可以将小于指定大小的文件转成base64编码的Data URL格式

sass-loader:将Sass文件编译成CSS文件。

less-loader:将Less文件编译成CSS文件。

postcss-loader:自动添加CSS前缀,优化CSS代码等。

vue-loader:将Vue单文件组件编译成JavaScript代码。

二、Plugin:

HtmlWebpackPlugin:生成HTML文件,并自动将打包后的javaScript和CSS文件引入到HTML文件中。

CleanWebpackPlugin:清除输出目录。

ExtractTextWebpackPlugin:将CSS代码提取到单独的CSS文件中。

DefinePlugin:定义全局变量。

UglifyJsWebpackPlugin:压缩JavaScript代码。

HotModuleReplacementPlugin:热模块替换,用于在开发环境下实现热更新。

MiniCssExtractPlugin:与ExtractTextWebpackPlugin类似,将CSS代码提取到单独的CSS文件中。

BundleAnalyzerPlugin:分析打包后的文件大小和依赖关系。

9. 强缓存和协商缓存?

强缓存和协商缓存是浏览器缓存的两种主要方式,用于优化网络请求,减少加载时间。下面我将简要介绍强缓存和协商缓存的概念和区别:

强缓存 (HTTP Cache-Control: max-age 和 Expires):

  • 概念:强缓存是利用 HTTP 头中的一些字段来告诉浏览器,在缓存未过期之前可以直接使用本地缓存副本,不需要再发起请求到服务器。
  • 实现方式
    • 使用 Cache-Control: max-age=xxxExpires 头来指定资源缓存的有效期,单位为秒。
    • 如果客户端的本地缓存未过期,浏览器会直接使用本地缓存,不会向服务器发送请求。

协商缓存 (HTTP ETag 和 Last-Modified):

  • 概念:协商缓存是在强缓存失效时,浏览器通过与服务器协商确定是否可以使用缓存的一种机制。
  • 实现方式
    • 服务器端会在响应头中提供 ETagLast-Modified 字段,分别表示资源的标识和最后修改时间。
    • 浏览器在发起请求时,会在请求头中携带 If-None-MatchIf-Modified-Since 字段,用于与服务器进行比较判断资源是否有更新。
    • 如果资源未发生变化,服务器返回 304 Not Modified 状态码,浏览器从本地缓存获取资源,否则服务器返回最新资源。

区别:

  • 强缓存:浏览器在本地判断是否使用缓存,不会发起请求到服务器。
  • 协商缓存:浏览器会发起请求到服务器,由服务器判断是否可以使用缓存。

如何同时使用强缓存和协商缓存:

  • 可以在响应头中同时设置 Cache-ControlExpiresETagLast-Modified,让浏览器既可以根据缓存的有效期判断是否使用本地缓存,又能够通过协商缓存与服务器确认是否需要重新获取资源。

综上所述,强缓存和协商缓存是 Web
开发中常用的缓存策略,可以有效减少网络请求,提升网站性能。合理配置缓存策略可以减少服务器压力、提高用户体验。

10. 深拷贝和浅拷贝

浅拷贝是指将一个对象或数组复制到一个新的对象或数组中,但只复制了对象或数组的引用,而不是实际的内容。
当对原始对象或数组进行修改时,复制后的对象或数组也会受到影响,因为它们共享相同的引用。 常见的浅拷贝方法包括
Object.assign(), Array.prototype.slice(), 扩展运算符 … 等。

深拷贝是指将一个对象或数组以及其所有嵌套对象或数组都完全复制到一个新的对象或数组中,而不共享任何引用。
深拷贝会递归地复制所有的子对象或数组,确保复制后的对象与原始对象完全独立,互不影响。 实现深拷贝,可以使用第三方库(如 Lodash 的_.cloneDeep() 方法)或自行编写递归函数来实现。

11. 移动端开发中常见的兼容性的问题?

  1. 音视频问题-------自动播放

ios safari中不支持,但在webview中可能被开启;iOS开发文档明确说明蜂窝网络下不允许autoplay;
chrome中,设置mouted后可以自动播放
微信中不允许自动播放。但是可以借助WeixinJSBridge实现

  1. 经典的1px边框
    一般是采用伪元素模拟的方式,原理:把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位
  .scale{
    position: relative;
    border:none;
  }
  .scale:after{
    content: '';
    position: absolute;
    bottom: 0;
    background: #000;
    width: 100%;
    height: 1px;
    transform: scaleY(0.5);
    transform-origin: 0 0;
  }
  1. android,border-radius:50%不圆,设置具体的px数值,不用50%即可
  2. 安卓部分版本input的placeholder偏上,解决:input的line-height设为normal
  3. ios 滚动不流畅

使用了absolute布局之后,ios会发现元素内的滚动非常不流畅,滑动的手指松开后,滚动立刻停止,失去了原本的流畅滚动特性。百度一下弹性滚动的问题,发现在
webkit 中,下面的属性可以恢复弹性滚动。
-webkit-overflow-scrolling: touch;

  1. ios日期格式问题
    具体就是,new Date(‘2020-11-12 00:00:00’)在ios中会为NAN
    解决方案:用new Date(‘2020/11/12 00:00:00’)的日期格式,或者写个正则把 - 转换 /

12. redux的原理以及工作流程?

Redux是什么?
Redux是一个独立于React的、可预测的状态管理库。它最初是为JavaScript应用设计的,但通常与React框架一起使用。Redux可以帮助你在应用中维护一个统一的、可预测的状态,这对于大型应用或复杂的状态管理场景特别有用。

Redux 的核心原理:
1 单一数据源(Single Source of Truth): Redux 应用中所有的状态都保存在一个单一的
store 中。这个 store 是一个对象树,包含了整个应用的状态。

2 状态是不可变的(State is Immutable):
意味着不能直接修改状态,而是通过返回一个新的状态来更新状态。这是为了保证状态的不可预测性和可追踪性。

3 使用 Reducer 来执行状态更新: reducer 是一个纯函数,它接收当前的应用状态和一个 action
对象,然后返回一个新的状态。reducer 用来描述如何通过 action 来改变状态。

4 纯函数(Pure Functions): Redux 中的 reducer
必须是纯函数,这意味着它们不会产生副作用,给定相同的输入总是返回相同的输出。

5 Action 来描述变化: action 是一个包含 type 属性的普通 JavaScript
对象,它描述发生了什么(通常还有其他属性携带数据)。action 被发送给 store,store再将 action 传递给
reducer。

6 Middleware 来处理副作用: Redux 允许使用中间件来处理异步逻辑、日志记录、异常监控等。中间件可以让你在 action
被发送到 reducer 之前或者之后插入逻辑。

Redux的工作流程:

  1. 初始化:

    • 你首先定义应用的初始状态(initial state)。
    • 创建一个reducer函数,它根据接收到的action类型来处理状态变化。
  2. 创建Store:

    • 使用Redux的createStore方法,将你的reducer传递给它,以创建一个全局的store实例。
  3. 派发Action:

    • 当应用中的某个部分(比如UI组件)需要改变状态时,它会创建并发送一个action对象到store。
    • Action通过store.dispatch()方法发送。
  4. 执行Reducer:

    • Store接收到action后,会调用reducer函数,并将当前的state和action作为参数传递给reducer。
    • Reducer根据action类型处理这个动作,并返回一个新的state。
  5. 更新State:

    • Store接收来自reducer的新state,并更新自己的内部状态。
  6. 订阅State变化:

    • 组件可以通过store.subscribe()方法来注册监听器,当store中的状态发生变化时,监听器会被调用。
    • 这时,组件可以获取更新后的state(通过store.getState())并重新渲染。
  7. UI响应:

    • 组件在监听到状态变化后,会根据新的state更新其UI。

代码示例:

// 1. 初始状态
const initialState = {
  count: 0
};

// 2. Reducer
const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

// 3. 创建Store
const store = Redux.createStore(reducer);

// 4. 订阅State变化
store.subscribe(() => {
  console.log(store.getState().count);
});

// 5. 派发Action
store.dispatch({ type: 'INCREMENT' }); // state.count 现在为 1
store.dispatch({ type: 'DECREMENT' }); // state.count 现在为 0

13. react组件之间的通信方式有哪些?

在 React 应用中,组件之间通信是非常常见的需求。下面我将介绍几种 React 组件之间通信的方式:

1. Props(属性)传递:

Props 是 React 中组件之间通信最常用的方式之一。通过在父组件中将数据传递给子组件的 Props,可以实现组件之间的数据传递和通信。

// ParentComponent.js
import React from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  const data = 'Hello from Parent';

  return <ChildComponent data={data} />;
};

export default ParentComponent;

// ChildComponent.js
import React from 'react';

const ChildComponent = (props) => {
  return <div>{props.data}</div>;
};

export default ChildComponent;

2. Callback 函数传递:

父组件可以将函数作为 Props 传递给子组件,子组件可以调用该函数来通知父组件发生了某些事件或状态变化。

// ParentComponent.js
import React from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  const handleCallback = () => {
    console.log('Callback from Child');
  };

  return <ChildComponent callback={handleCallback} />;
};

export default ParentComponent;

// ChildComponent.js
import React from 'react';

const ChildComponent = (props) => {
  const handleClick = () => {
    props.callback();
  };

  return <button onClick={handleClick}>Click me</button>;
};

export default ChildComponent;

3. Context API:

Context API 可以用来在 React 组件树中共享数据,从而避免 Props 层层传递的繁琐。父组件通过 Context 提供数据,子组件通过 useContext 钩子或 Consumer 来访问这些数据。

// App.js
import React, { createContext, useContext } from 'react';

const DataContext = createContext();

const App = () => {
  const data = 'Hello from Context';

  return (
    <DataContext.Provider value={data}>
      <ChildComponent />
    </DataContext.Provider>
  );
};

export default App;

// ChildComponent.js
import React, { useContext } from 'react';
import { DataContext } from './App';

const ChildComponent = () => {
  const data = useContext(DataContext);

  return <div>{data}</div>;
};

export default ChildComponent;

4. redux 等等

以上是几种 React 组件之间通信的方式,根据具体场景和需求选择合适的方式来实现组件之间的通信。

14. promise、setTimeOut以及async/await的执行顺序

在JavaScript中,PromisesetTimeoutasync/await 是常见的异步处理模式。它们的执行顺序取决于它们是如何被编写和调用的。下面我将解释它们的执行顺序,并提供一个代码示例。

执行顺序规则:

  1. 同步代码首先执行。
  2. setTimeout 回调将在指定的延迟后添加到事件队列中。
  3. Promise 的处理(包括resolvereject)会在当前执行栈清空后立即执行。
  4. 使用 async/await 时,await 会暂停当前 async 函数的执行,直到 Promise 完成(解决或拒绝)。
  5. 事件循环会在执行栈清空后检查事件队列,并按顺序处理其中的事件。

代码示例:

console.log('1. 同步代码开始执行');

// 使用 setTimeout 设置一个异步任务
setTimeout(() => {
  console.log('4. setTimeout 的回调函数执行');
}, 0);

// 创建一个立即解决的 Promise
Promise.resolve().then(() => {
  console.log('3. Promise 的 then 方法执行');
});

// async 函数
async function asyncFunction() {
  console.log('2. async 函数开始执行');
  await Promise.resolve(); // 等待 Promise 解决
  console.log('5. await 后的代码执行');
}

// 调用 async 函数
asyncFunction();

console.log('6. 同步代码继续执行');

// 最终输出顺序可能是:
// 1. 同步代码开始执行
// 2. async 函数开始执行
// 6. 同步代码继续执行
// 3. Promise 的 then 方法执行
// 5. await 后的代码执行
// 4. setTimeout 的回调函数执行

请注意,虽然 setTimeout 设置的延迟是0毫秒,但它的回调并不会立即执行。这是因为JavaScript引擎的最小时间切片(通常为4ms)和其他任务可能优先执行。

此外,由于 awaitasync 函数中,await Promise.resolve() 实际上会等待当前执行栈清空,然后才会继续执行之后的代码,所以它会在 Promisethen 方法之后执行。

实际的执行顺序可能会因JavaScript引擎的调度策略和运行环境的不同而有所差异,但上述代码提供了一个大致的执行顺序参考。
在这里插入图片描述
在这里插入图片描述

15 为 Vue3 Vite 项目设置不同的运行环境和打包环境

在 Vue3 的 Vite 项目中,区分运行环境和分环境打包的配置与传统的 Vue CLI 项目有所不同。以下是使用 Vite 的配置步骤:

1. 创建环境变量文件

在项目根目录下创建以下环境变量文件:

  • .env.development:开发环境变量
  • .env.test:测试环境变量
  • .env.production:生产环境变量

内容如下:

.env.development:

VITE_API_URL=http://localhost:3000

.env.test:

VITE_API_URL=http://test.example.com

.env.production:

VITE_API_URL=https://api.example.com

Vite 会自动加载这些环境变量文件,并根据 VITE_ 前缀识别它们。

2. 配置 vite.config.js

在项目根目录下创建或修改 vite.config.js 文件,以配置不同环境的构建:

import { defineConfig } from 'vite'

export default defineConfig({
  // 默认情况下,Vite 会假设你的应用部署在根目录下。
  // 如果应用被部署在一个子路径下,你需要在 base 配置中设置这个子路径。
  // 例如:https://www.example.com/my-app/
  base: process.env.NODE_ENV === 'production' ? '/my-app/' : '/',
  // 其他配置...
})

3. 配置 package.json

package.json 中的 scripts 部分定义不同的运行和打包命令:

{
  "scripts": {
    "dev": "vite --mode development",
    "test": "vite --mode test",
    "build": "vite build --mode production",
    "preview": "vite preview"
  }
}

4. 在代码中使用环境变量

在应用中,你可以通过 import.meta.env 来访问这些环境变量:

// 假设这是一个 API 请求的基础 URL
const apiBaseUrl = import.meta.env.VITE_API_URL;

// 可以在 Vue 组件中使用
export default {
  data() {
    return {
      apiUrl: apiBaseUrl
    };
  }
};

5. 打包和运行命令

  • 开发环境:npm run dev
  • 测试环境:npm run test
  • 生产环境打包:npm run build
  • 预览打包结果:npm run preview

完整示例

假设我们想要根据不同环境加载不同的环境变量,以下是完整的步骤和代码示例:

环境变量文件

.env.development:

VITE_API_URL=http://localhost:3000

.env.test:

VITE_API_URL=http://test.example.com

.env.production:

VITE_API_URL=https://api.example.com

vite.config.js

import { defineConfig } from 'vite'

export default defineConfig({
  // base 配置可以根据环境变量来设置
  base: process.env.VITE_BASE_URL || '/',
  // 其他配置...
})

package.json

{
  "scripts": {
    "dev": "vite --mode development",
    "test": "vite --mode test",
    "build": "vite build --mode production",
    "preview": "vite preview"
  }
}

使用环境变量

// 在组件或脚本中
const apiBaseUrl = import.meta.env.VITE_API_URL;
console.log('API Base URL:', apiBaseUrl);

通过上述配置,你可以为 Vue3 Vite 项目设置不同的运行环境和打包环境,并在代码中根据不同的环境变量进行相应的逻辑处理。

16 webpack的构建流程?

1、初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler
2、编译:从 Entry 出发,针对每个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理
3、输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中

17 什么是Webpack的热更新(Hot Module Replacement)?原理是什么?

Webpack的热更新(Hot Module Replacement,简称HMR),在不刷新页面的前提下,将新代码替换掉旧代码。
HRM的原理实际上是 webpack-dev-server(WDS)和浏览器之间维护了一个websocket服务。当本地资源发生变化后,webpack会先将打包生成新的模块代码放入内存中,然后WDS向浏览器推送更新,并附带上构建时的hash,让客户端和上一次资源进行对比。客户端对比出差异后会向WDS发起Ajax请求获取到更改后的内容(文件列表、hash),通过这些信息再向WDS发起jsonp请求获取到最新的模块代码。

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值