笔记

代码重构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XO56tAfI-1589971833570)(C:\Users\wanglei\AppData\Roaming\Typora\typora-user-images\1589010837908.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-65kEqSAI-1589971833643)(C:\Users\wanglei\AppData\Roaming\Typora\typora-user-images\1589022076208.png)]

项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1x5vBuLB-1589971833647)(C:\Users\wanglei\AppData\Roaming\Typora\typora-user-images\1588955408055.png)]

*单页面应用首屏加载慢

  • Vue-router懒加载

Vue-router懒加载就是按需加载组件,只有当路由被访问时才会加载对应的组件,而不是在加载首页的时候就加载,项目越大,对首屏加载的速度提升得越明显。

  • 使用CDN加速

在做项目时,我们会用到很多库,采用cdn加载可以加快加载速度。

  • 异步加载组件
  • 服务端渲染

服务端渲染还能对seo优化起到作用,有利于搜索引擎抓取更多有用的信息(如果页面纯前端渲染,搜索引擎抓取到的就只是空页面)

*单页面多页面区别

单页面的优点:

1,用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小

2,前后端分离

3,页面效果会比较炫酷(比如切换页面内容时的专场动画)

单页面缺点:

1,不利于seo

2,导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理)

3,初次加载时耗时多

4,页面复杂度提高很多

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fayFxEn5-1589971833651)(C:\Users\wanglei\AppData\Roaming\Typora\typora-user-images\1588213585241.png)]

*单页面实现前进后退

1.前进:
history.forward();
history.go(1);
2.后退:
history.back();
history.go(-1);

3.获取记录个数:
history.length:

webpack

打包流程及原理

treeshaking

多页打包

优化

跨域

*为什么AJAX不能跨域访问?

在网页中写了一段js代码,使用ajax向淘宝发起登陆请求,因为很多数人都访问过淘宝,所以电脑中存有淘宝的cookie,不需要输入账号密码直接就自动登录了,然后小黑在ajax回调函数中解析了淘宝返回的数据,得到了很多人的隐私信息。

*同源策略

同源政策:不是同协议 同域名 同端口 的网页无法相互访问。

用form表单提交到不同源的网页是被允许的,因为 form 提交到另一个域名之后,原页面的脚本无法获取新页面中的内容,所以浏览器认为这是安全的。

而 AJAX 是可以读取响应内容的,因此浏览器不能允许你这样做。如果你细心的话你会发现,其实请求已经发送出去了,你只是拿不到响应而已。

所以浏览器这个策略的本质是,一个域名的 JS ,在未经允许的情况下,不得读取另一个域名的内容。但浏览器并不阻止你向另一个域名发送请求。

同源策略限制内容有:

  • Cookie、LocalStorage、IndexedDB 等存储性内容
  • DOM 节点
  • AJAX 请求发送后,结果被浏览器拦截了

但是有三个标签是允许跨域加载资源:

  • <img src=XXX>
  • <link href=XXX>
  • <script src=XXX>

JSONP

对script的资源引用没有同源限制,通过动态插入一个script标签,当资源加载到页面后会立即执行的原理实现跨域

允许用户传递一个callback或者开始就定义一个回调方法,参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

只支持GET请求而不支持POST等其它类型的HTTP请求,它只支持跨域HTTP请求

缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I1uK21IR-1589971833653)(C:\Users\wanglei\AppData\Roaming\Typora\typora-user-images\1588932515140.png)]

nginx反向代理

搭建一个中转nginx服务器,用于转发请求。

使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。

实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DLmqxnWv-1589971833654)(C:\Users\wanglei\AppData\Roaming\Typora\typora-user-images\1588953094846.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1WwsgukF-1589971833656)(C:\Users\wanglei\AppData\Roaming\Typora\typora-user-images\1588953112719.png)]

CORS

该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin;浏览器判断该相应头中是否包含Origin的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。

CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,需要两方配合

跨域请求不仅解决了ajax也解决了cookie,要在ajax请求里加上

jquery:xhrFields: {withCredentials: true}, crossDomain: true

js: xhr.withCredentials = true

xhr.withCredentials 有什么用?

跨域请求是否提供凭据信息(cookie、HTTP认证及客户端SSL证明等)
也可以简单的理解为,当前请求为跨域类型时是否在请求中协带cookie

当配置了xhr.withCredentials = true时,必须在后端增加 response 头信息Access-Control-Allow-Origin,且必须指定域名,而不能指定为*。

res.setHeader(“Access-Control-Allow-withCredentials”,“true”)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YRk9dWgW-1589971833657)(C:\Users\wanglei\AppData\Roaming\Typora\typora-user-images\1588932661968.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HptXS5Uj-1589971833658)(C:\Users\wanglei\AppData\Roaming\Typora\typora-user-images\1588932688537.png)]

1) 简单请求

只要同时满足以下两大条件,就属于简单请求

条件1:使用下列方法之一:

  • GET
  • HEAD
  • POST

条件2:Content-Type 的值仅限于下列三者之一:

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded
2) 复杂请求

不符合以上条件的请求就肯定是复杂请求了。 复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。

比如请求方法是PUTDELETE,或者Content-Type字段的类型是application/json

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP方法和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

"预检"请求的HTTP头信息:

"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
除了Origin字段,"预检"请求的头信息包括两个特殊字段。
(1)Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT

(2)Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header

上面的HTTP回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为星号,表示同意任意跨源求。

Access-Control-Allow-Origin: *

如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。
控制台会打印出如下的报错信息。

XMLHttpRequest cannot load http://api.alice.com.Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

服务器回应的其他CORS相关字段如下。

Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000

一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
下面是"预检"请求之后,浏览器的正常CORS请求。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HbL66aCv-1589971833660)(C:\Users\wanglei\AppData\Roaming\Typora\typora-user-images\1588951781081.png)]

图片懒加载

1、offsetLeft

假设 obj 为某个 HTML 控件。

obj.offsetTop 指 obj 距离上方或上层控件的位置,整型,单位像素。

obj.offsetLeft 指 obj 距离左方或上层控件的位置,整型,单位像素。

obj.offsetWidth 指 obj 控件自身的宽度,整型,单位像素。

obj.offsetHeight 指 obj 控件自身的高度,整型,单位像素。

一、offsetTop 返回的是数字,而 style.top 返回的是字符串,除了数字外还带有单位:px。

二、offsetTop 只读,而 style.top 可读写

三、如果没有给 HTML 元素指定过 top 样式,则 style.top 返回的是空字符串。

offsetLeft 与 style.left、offsetWidth 与 style.width、offsetHeight 与 style.height 也是同样道理。

2、clientHeight 就是透过浏览器看内容的这个区域高度。

scrollHeight 则是网页内容实际高度。数字

clientWidth、offsetWidth 和 scrollWidth 的解释与上面相同,只是把高度换成宽度即可。

clientWidth = width + padding

clientHeight = height + padding

offsetWidth = width + padding + border

offsetHeight = width + padding + border

3、scrollTop:已滚动过去的高度 “卷起高度” onscoll函数

4、clientTop:clientTop可以返回div的上边框的大小,其值为一个整数,没有单位。

移动端适配

在不同尺寸的手机设备上,页面“相对性的达到合理的展示(自适应)”或者“保持统一效果的等比缩放(看起来差不多)”;

1)viewport(scale=1/dpr)
2)rem
3)flex
4)vm/vh

<meta name="viewport" content="width=device-width, initial-scale=1">

img

Vue

*Vue实例里面的data属性为什么用函数返回?

当一个组件被定义, data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象


*jQuery与Vue的区别是什么?

JQuery和Vue的主要区别是JQuery主要是通过选择器来选取DOM,对其进行赋值,取值,事件绑定等操作,数据和页面是混合在一起的;Vue则是通过Vue对象将数据和视图完全分割开来,对数据进行操作,不再需要引用相应的DOM对象,实现了MVVM。


*生命周期

创建前/后: 在beforeCreate阶段,vue实例的挂载元素el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,el还没有。

载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。

更新前/后:当data变化时,会触发beforeUpdate和updated方法。

销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在

(1)、什么是vue生命周期

答: Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。

(2)、vue生命周期的作用是什么

答:它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。

(3)、vue生命周期总共有几个阶段

答:可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后

(4)、第一次页面加载会触发哪几个钩子

答:第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子

(5)、DOM 渲染在 哪个周期中就已经完成

答:DOM 渲染在 mounted 中就已经完成了。

(6)、简单描述每个周期具体适合哪些场景

答:生命周期钩子的一些使用方法:

beforecreate : 可以在这加个loading事件,在加载实例时触发

created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用

mounted : 挂载元素,获取到DOM节点

updated : 如果对数据统一处理,在这里写上相应函数

beforeDestroy : 可以做一个确认停止事件的确认框

nextTick : 更新数据后立即操作dom

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YmRucxtD-1589971833663)(G:\html实例\笔记\lifecycle.png)]

在 MVC 模式中我们使用观察者模式,来实现当 Model 层数据发生变化的时候,通知 View 层的更新。

*MVVM

Model 代表数据模型,用纯JavaScript对象表示,数据和业务逻辑都是在Model层中定义。

View 代表UI 视图,负责对数据的展示,将数据模型转化成UI 展现出来。

ViewModel 负责监听Model中数据的改变并控制视图的更新,处理用户交互操作,是一个同步View 和 Model的对象。ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。

Model和View并无直接关联,而是通过ViewModel来进行联系的,Model和ViewModel之间有着双向数据绑定的联系。因此当Model中的数据改变时会触发View层的刷新,View中由于用户交互操作而改变的数据也会在Model中同步。

ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

*双向绑定Object.defineProperty()

采用数据劫持结合发布者-订阅者模式,通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调。

在这里插å¥å›¾ç‰‡æè¿°

将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者。

1.监听器Observer,用来劫持并监听model所有属性,如果有变动就通知订阅者。

2.订阅者Watcher,可以收到属性的变化通知 并执行相应的回调函数。

3.解析器Compile,可以扫描和解析每个节点的相关指令,并替换模板数据,初始化视图 以及 将模板指令对应的节点绑定对应的更新函数,初始化相应的订阅器。

事件监听

computed其实只是纯数据操作,需要返回数据结果。但是watch就可以监测某个数据发生了变更进行一系列的回调操作,不仅仅局限于返回数据,你也可以不返回。

计算属性具有缓存。计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 lastName和firstName都没有发生改变,多次访问 fullName计算属性会立即返回之前的计算结果,而不必再次执行函数。

而侦听器watch是侦听一个特定的值,当该值变化时执行特定的函数。例如分页组件中,我们可以监听当前页码,当页码变化时执行对应的获取数据的函数。

过滤

*自定义指令

vue.directive(‘xxx’,{bind:function(el){…}},{update:function(){…}})

*key

用 v-for 正在更新已渲染过的元素列表时,它默认用 “就地复用” 策略。如果数据项的顺序被改变,Vue将不是移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。

key的作用主要是为了高效的更新虚拟DOM

Vue的路由实现:hash模式 和 history模式

**hash模式:**在浏览器中符号“#”,#/hello称之为hash,用window.location.hash读取;
hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。

**history模式:**history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。

mode: 'history',

history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”

二、404 错误

​ 1、hash模式下,仅hash符号之前的内容会被包含在请求中,如 http://www.abc.com, 因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回404错误

​ 2、history模式下,前端的url必须和实际向后端发起请求的url 一致,如http://www.abc.com/book/id 。如果后端缺少对/book/id 的路由处理,将返回404错误

组件通信

  • 父传子用 props传递(异步,有可能数据还没获取到就渲染了,所以监听)

    父亲:
    <son :message="msg"></son>
    data(){
    return{
    msg:'i am father'
    }
    }
    儿子:
    props:['message'],
    <div>{
        {message}}</div>
    
  • 子传父用$emit传递

           <input @click="sendMsg" type="button" value="给父组件传递值">
    <script>
    export default {
        data () {
            return {
                //将msg传递给父组件
                msg: "我是子组件的msg",
            }
        },
         methods:{
             sendMsg(){
                 //func: 是父组件指定的传数据绑定的函数,this.msg:子组件给父组件传递的数据
                 this.$emit('func',this.msg)
             }
         }
    
            <child @func="getMsgFormSon"></child>
    import child from './child.vue'
    export default {
        data () {
            return {
                msgFormSon: "this is msg"
            }
        },
        components:{
            child,
        },
        methods:{
                getMsgFormSon(data){
                    this.msgFormSon = data
                    console.log(this.msgFormSon)
                }
        }
    
  • 路由参数中:params 看不见参数 post

    this.$router.push({name:'componentB',params:{num:123}})
    this.$route.params.num
    
    
    path:'/hi/:num'
    
    
  • 路由参数中:query ?num=123 嫩看见 get

    A中:
    this.$router.push({path:'componentB',query:{num:123}})
    B中:
    this.$route.query.num
    
    
  • localStorage,sessionStorage

    vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。

    window.localStorage.setItem('defaultCity', JSON.stringify(state.city));
    
    if (!defaultCity){
        defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
      }
    
    
  • e m i t / emit/ emit/on这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。

        var Event=new Vue();
        Event.$emit(事件名,数据);
        Event.$on(事件名,data => {});
        
        
        组件ab
        var Event = new Vue();//定义一个空的Vue实例
        methods: {
          send() {
            Event.$emit('data-b', this.age);
          }
        }
        组件c
        mounted() {//在模板编译完成后执行
         Event.$on('data-a',name => {
             this.name = name;//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
         })
         Event.$on('data-b',age => {
             this.age = age;
         })
        }
    
    

*单页应用首屏加载速度慢,白屏时间过长

  • 将公用的JS库通过script标签在index.html进行外部引入,减少我们打包出来的js文件的大小,让浏览器并行下载资源文件,提高下载速度
  • 在配置路由的时候进行路由的懒加载,在调用到该路由时再加载次路由相对应的js文件
  • 加一个首屏loading图或骨架屏,提高用户的体验
  • 尽可能使用CSS Sprites和字体图标库
  • 图片的懒加载

Vuex

Vuex是通过全局注入store对象,来实现组件间的状态共享。

(1)vuex是什么?怎么使用?哪种功能场景使用它?

vue框架中状态管理。在main.js引入store,注入。新建一个目录store,…… export 。场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车

(2)vuex有哪几种属性?

有五种,分别是 State、 Getter、Mutation 、Action、 Module

vuex的State特性

A、Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,不可以直接修改里面的数据

B、state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新

C、它通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中

vuex的Getter特性

A、getters 可以对State进行计算操作,它就是Store的计算属性

B、 虽然在组件内也可以做计算属性,但是getters 可以在多组件之间复用

C、 如果一个状态只在一个组件内使用,是可以不用getters

vuex的Mutation特性(定义的方法动态修改Vuex 的 store 中的状态或数据)

Action (异步)类似于 mutation(同步),不同在于:Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作。

(3)不用Vuex会带来什么问题?

可维护性会下降,想修改数据要维护三个地方;

可读性会下降,因为一个组件里的数据,根本就看不出来是从哪来的;

增加耦合,大量的上传派发,会让耦合性大大增加,本来Vue用Component就是为了减少耦合,现在这么用,和组件化的初衷相背。

小问题

v-show和v-if指令的共同点和不同点

v-show指令是通过修改元素的display的CSS属性让其显示display或者隐藏none;

v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果;

使用v-show会更加节省性能上的开销;当只需要一次显示或隐藏时,使用v-if更加合理。

r o u t e 和 route和 routerouter的区别

答:$route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。只读

而$router是“路由实例”对象包括了路由的跳转方法,钩子函数等。只写 push

vue常用的修饰符?
答:.prevent: 提交事件不再重载页面;.stop: 阻止单击事件冒泡;.self: 当事件发生在该元素本身而不是子元素的时候会触发;.capture: 事件侦听,事件发生的时候会调用

什么是vue的计算属性?
答:在模板中放入太多的逻辑会让模板过重且难以维护,在需要对数据进行复杂处理,且可能多次使用的情况下,尽量采取计算属性的方式。好处:①使得数据处理结构清晰;②依赖于数据,数据更新,处理结果自动更新;③计算属性内部this指向vm实例;④在template调用时,直接写计算属性名即可;⑤常用的是getter方法,获取数据,也可以使用set方法改变数据;⑥相较于methods,不管依赖的数据变不变,methods都会重新计

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值