大前端- vue - vuex状态管理

一:组件间通信方式

state:驱动应用的数据源。

view:以声明方式将state映射到视图。

actions:响应在view上的用户输入导致的状态变化。

状态管理:
请添加图片描述

组件间的通信方式:
  1. 父组件给子之间的传值(父子组件)
    1. 子组件中通过props接收数据。2. 父组件中给子组件通过相应属性传值

子组件

<template>
  <div>
    <h1>Props Down Child</h1>
    <h2>{{ title }}</h2>
  </div>
</template>

<script>
export default {
 // props接收父组件传递的参数
  // props: ['title'],
  props: {
    title: String
  }
}
</script>

父组件

<template>
  <div>
    <h1>Props Down Parent</h1>
    // 传递title给子组件
    <child title="My journey with Vue"></child>
  </div>
</template>

<script>
import child from './01-Child'
export default {
  components: {
    child
  }
}
</script>

  1. 子组件给父组件传值
    子组件:
    this.$emit('enlargeText', 0.1)
<template>
  <div>
    <h1 :style="{ fontSize: fontSize + 'em' }">Props Down Child</h1>
    <button @click="handler">文字增大</button>
  </div>
</template>

<script>
export default {
  props: {
    fontSize: Number
  },
  methods: {
    handler () {
     // 在子组件中使用父组件传递过来的事件,并传递值
      this.$emit('enlargeText', 0.1)
    }
  }
}
</script>

父组件:

<template>
  <div>
    <h1 :style="{ fontSize: hFontSize + 'em'}">Event Up Parent</h1>

    这里的文字不需要变化
	// @enlargeText="enlargeText" 传递事件给子组件
    <child :fontSize="hFontSize" @enlargeText="enlargeText"></child>
    <child :fontSize="hFontSize" @enlargeText="enlargeText"></child>
    <child :fontSize="hFontSize" @enlargeText="hFontSize += $event"></child>
  </div>
</template>

<script>
import child from './02-Child'
export default {
  components: {
    child
  },
  data () {
    return {
      hFontSize: 1
    }
  },
  methods: {
    enlargeText (size) {
      this.hFontSize += size
    }
  }
}
</script>

  1. 不相关组件之间的传值
    也是使用事件的方式:bus:事件中心

eventBus.js

import Vue from 'vue'
export default new Vue()

子组件1:

bus.$emit('numchange', this.value) bus.$emit触发事件, numchange:自定义事件名,this.value传递的参数

<template>
  <div>
    <h1>Event Bus Sibling01</h1>
    <div class="number" @click="sub">-</div>
    <input type="text" style="width: 30px; text-align: center" :value="value">
    <div class="number" @click="add">+</div>
  </div>
</template>

<script>
import bus from './eventbus'

export default {
  props: {
    num: Number
  },
  created () {
    this.value = this.num
  },
  data () {
    return {
      value: -1
    }
  },
  methods: {
    sub () {
      if (this.value > 1) {
        this.value--
        //  bus.$emit('numchange', this.value)触发事件
        // numchange:自定义事件名,this.value传递的参数
        bus.$emit('numchange', this.value)
      }
    },
    add () {
      this.value++
      bus.$emit('numchange', this.value)
    }
  }
}
</script>

<style>
.number {
  display: inline-block;
  cursor: pointer;
  width: 20px;
  text-align: center;
}
</style>

子组件2
bus.$on:注册:自定义事件numchange,() => {}:回调函数

<template>
  <div>
    <h1>Event Bus Sibling02</h1>

    <div>{{ msg }}</div>
  </div>
</template>

<script>
import bus from './eventbus'
export default {
  data () {
    return {
      msg: ''
    }
  },
  created () {
  
    bus.$on('numchange', (value) => {
      this.msg = `您选择了${value}件商品`
    })
  }
}
</script>
  1. 组件间通信方式回顾-通过 ref 获取子组件
    ref的2个作用:
    1. 在普通的html标签上使用ref,获取到的是DOM
    2. 在组件标签上使用ref,获取到的是组件实例
    child.vue
<template>
  <div>
    <h1>ref Child</h1>
    <input ref="input" type="text" v-model="value">
  </div>
</template>

<script>
export default {
  data () {
    return {
      value: ''
    }
  },
  methods: {
    focus () {
     // 在普通的html 上使用ref
      this.$refs.input.focus()
    }
  }
}
</script>

parent.vue

<template>
  <div>
    <h1>ref Parent</h1>

    <child ref="c"></child>
  </div>
</template>

<script>
import child from './04-Child'
export default {
  components: {
    child
  },
  mounted () {
  	// 在组件上使用ref,获取到的是组件的实例
    this.$refs.c.focus()
    this.$refs.c.value = 'hello input'
  }
}
</script>
  1. 状态管理方案 - Vuex
    Vuex:集中式的状态管理
    在组件中不能直接更改状态的属性,如果想要更该需要在store中actions中的定义函数修改,这样做的好处是:能记录store中的数据的所有的变更。

解决的问题:多个视图依赖同一状态
来自不同视图的行为需要变更同一状态

store.js

// state: 存储的状态
// setUserNameAction:通过视图和用户交互的时候更改状态用的。
// debug:方便调试,如果为true,在通过action修改数据的时候会打印日志。
export default {
  debug: true,
  state: {
    user: {
      name: 'xiaomao',
      age: 18,
      sex: '男'
    }
  },
  setUserNameAction (name) {
    if (this.debug) {
      console.log('setUserNameAction triggered:', name)
    }
    this.state.user.name = name
  }
}

在组件中使用:

  1. 引入store
  2. 使用store.state.user
<template>
  <div>
    <h1>componentA</h1>
    user name: {{ sharedState.user.name }}
    <button @click="change">Change Info</button>
  </div>
</template>

<script>
// 1. 引入store
import store from './store'
export default {
  methods: {
    change () {
    // 使用store中的action更改数据
      store.setUserNameAction('componentA')
    }
  },
  data () {
    return {
      privateState: {},
      // 使用:store.state
      sharedState: store.state
    }
  }
}
</script>

vuex核心概念和基本使用

什么是vuex?
vuex是专门为vuejs设计的状态管理库。
vuex采用集中式的方式存储需要共享的状态。
vuex的作用是进行状态管理,解决复杂组件通信,数据共享。
vuex集成到了devtools中,提供了time-travel时光旅行历史回滚功能。

什么情况下使用vuex?
非必要的情况不需要使用vuex。
大型的单页应用程序:1.多个视图依赖同一状态。2.来自不同视图的行为需要变更同一状态。

vuex的核心概念:

请添加图片描述
上述图的描述:
state:state中存储数据
vue components:把状态绑定到组件中渲染到用户界面展示给用户。用户可以和视图交互,例如点击按钮(加入购物车):通过dispatch分发actions,此处不直接提交mutations,因为actions中可以做异步操作,(购买的时候需要发送异步请求,异步请求结束之后,再通过提交mutation记录状态的更改,)
mutations: mutations必须是同步的,所有状态的更改都要通过mutations,这样做的目的是:通过mutations可以追踪到所有状态的改变,阅读代码的时候更容易分析应用内部的状态改变,还可以记录每一次状态的改变,实现高级的调试功能。

核心概念:
Store:
state:响应式的状态
Getter: 有点类似于vue中的计算属性,方便从一个属性派生出其他的值。内部可以对计算的结果进行缓存,只有当依赖的状态发生改变的时候才会重新计算。
Mutation:状态的变化必须通过提交mutation来完成。
Actions: action和mutation类似,不同的是action可以进行异步的操作。内部改变状态的时候都需要提交mutation
Module:模块,当数据变得非常大的时候可以采用模块的方式。

如果使用?整体的代码结构
store.js

import Vue from 'vue'
import Vuex from 'vuex'


Vue.use(Vuex)

export default new Vuex.Store({

  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
})

main.js

import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

console.log(store)

使用state:

import Vue from 'vue'
import Vuex from 'vuex'


Vue.use(Vuex)

export default new Vuex.Store({

  state: {
  	count:0,
  	msg: 'hello vuex'
  },
  getters: {
  	// 把msg倒序输出
  	reverseMsg(state) {
		return state.msg.split('').reverse().join()
	}
  },
  mutations: {
  },
  actions: {
  },
})

在组件中使用:
方法1: $store.state.count

<template>
  <div id="app">
     count:{{ $store.state.count }}
    msg: {{ $store.state.msg }}
{{count}}
{{msg}}
   </div>
<template>

方法2:mapState

import { mapState } from 'vuex'
computed: {
	// 方法1:写成数组的方式
	//...mapState(['count', 'msg'])
	// 方法2:函数方式的写法
	count: state => state.count,
	msg: state => state.msg,
	// 方法3:写成对象的形式,可以解决命名冲突的问题
	...mapState({num: 'count', message: 'msg'}),
}

Gutter(在视图中显示数据): 类似于计算属性,想把store中的数据处理一下再显示到页面上。

 getters: {
  	// 把msg倒序输出
  	reverseMsg(state) {
		return state.msg.split('').reverse().join()
	}
  },

在组件中使用:
方式1: 直接在模版中使用:$store.getters.reverseMsg方式2:使用mapGetters`


<div>
	{{$store.getters.reverseMsg}}
	{{reverseMsg}}
</div>
<script>
	import { mapGetters} from 'vuex'
	computed: {
		...mapGetters([reverseMsg])
	}
</script>

Mutation(修改状态):如何修改状态?状态的修改必须经过提交mutation,mutation必须是同步执行,这样可以保证能够在mutation中收集到所有的状态修改。不要在mutation中使用异步方式,如果需要使用异步则使用action
用法:

// 点击【增加】按钮
mutations: {
	increate(state, paylod) {
		state.count + = paylod
	}
}

在组件中使用:
方式1:$store.commit('increate', 2)

<button @click="$store.commit('increate', 2)">增加</button>

方式2: mapMutations

<button @click="increate(3)">增加</button>


import {mapMutations} from 'Vuex'
method:{
	...mapMutations(['increate']),
}

Action: 如果需要使用异步操作,则使用action,当异步结束后如果需要更改状态,需要提mutation来修改state。因为所有的状态更改都是使用mutation。例如:如果需要获取商品数据,则需要在action 中发送请求,异步执行完毕,获取到商品数据之后,需要再提交mutation,把数据记录到state中。

Action使用:

actions: {
	increateAsync(context, payload) {
	// setTimeout是异步任务
      setTimeout(() => {
        context.commit('increate', payload)
      }, 2000)		
	}
}

在组件中使用:
方法1:$store.dispatch('increateAsync', 5)
方法2: mapActions

<button @click="$store.dispatch('increateAsync', 5)">Action</button> 

<button @click="increateAsync(6)">Action</button>

import { mapActions } from 'vuex'
methods: {
    ...mapActions(['increateAsync']),
  }

Module:模块化处理state

目录结构:
请添加图片描述
index.js

import Vue from 'vue'
import Vuex from 'vuex'
// 模块化处理state
import products from './modules/products'
import cart from './modules/cart'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0,
    msg: 'Hello Vuex'
  },
  getters: {
    reverseMsg (state) {
      return state.msg.split('').reverse().join('')
    }
  },
  mutations: {
    increate (state, payload) {
      state.count += payload
    }
  },
  actions: {
    increateAsync (context, payload) {
      setTimeout(() => {
        context.commit('increate', payload)
      }, 2000)
    }
  },
  modules: {
    products,
    cart
  }
})

modules/cart.js

const state = {}
const getters = {}
const mutations = {}
const actions = {}

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}

modules/products.js

const state = {
  products: [
    { id: 1, title: 'iPhone 11', price: 8000 },
    { id: 2, title: 'iPhone 12', price: 10000 }
  ]
}
const getters = {}
const mutations = {
  setProducts (state, payload) {
    state.products = payload
  }
}
const actions = {}

export default {
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}

在组件中使用模块的方法和state属性
方法1:$store.state.products.products. 第一个products指的是模块的名字,第2个products指的是state中的属性
方法2:mapMutations

products: {{ $store.state.products.products }} <br>
<button @click="$store.commit('setProducts', [])">Mutation</button>

方法2:

 products: {{ products }} <br>
<button @click="setProducts([])">Mutation</button>

import {mapMutations} from 'vuex'
computed: {
	// 需要注意:mapState的第一个参数:products指的是模块的名字
	// 第2个参数:['products']指的是state中的属性
    ...mapState('products', ['products'])
  },
methods: {
	// 需要注意:mapMutations的第一个参数:products指的是模块的名字
	// 第2个参数:['setProducts']指的是mutation中的方法名字
    ...mapMutations('products', ['setProducts'])
  }

vuex严格模式:
所有的状态更改必须经过mutation,但是这只是一个口头约定,如果在组件中$store.state.count = 1也是可以修改的的,不会抛出错误,但是这样状态state的修改就变得不可追踪,devtools页没有办法追踪,开启严格模式之后,如果这样修改的话,就会抛出错误。

开启严格模式:strict: process.env.NODE_ENV !== 'production',

import Vue from 'vue'
import Vuex from 'vuex'
import products from './modules/products'
import cart from './modules/cart'

Vue.use(Vuex)

export default new Vuex.Store({
 // 开启严格模式
  strict: process.env.NODE_ENV !== 'production',
  state: {
    count: 0,
    msg: 'Hello Vuex'
  },
  getters: {
    reverseMsg (state) {
      return state.msg.split('').reverse().join('')
    }
  },
  mutations: {
    increate (state, payload) {
      state.count += payload
    }
  },
  actions: {
    increateAsync (context, payload) {
      setTimeout(() => {
        context.commit('increate', payload)
      }, 2000)
    }
  },
  modules: {
    products,
    cart
  }
})

开启之后的报错:
请添加图片描述
不要在生产模式开启,因为会深度监听影响性能。

购物车案例

购物车功能:
1.用到了node写的接口:node server.js
2.
页面:
请添加图片描述

请添加图片描述
请添加图片描述

当刷新购物车页面之后当,前购物车中的数据是存储在本地的。

项目代码地址:
三个组件:商品列表组件(products.vue),
购物车组件(cart.vue),
鼠标悬浮【我的购物车】展示购物车中的商品组件(pop-cart.vue)

商品列表组件:

store
store/modules/products.js

import axios from 'axios'
const state = {
  products: [], // 商品列表
}
const getters = {}
const mutations = {
  setProducts (state, payload) {
    state.products = payload
  }
}
const actions = {
  async getProducts ({ commit }) {
    const { data } = await axios({
      method: 'GET',
      url: 'http://127.0.0.1:3000/products'
    })
    commit('setProducts', data)
  }
}

export default {
  // 开启命名空间
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}

2.6版本以前获取插槽中的数据
<template slot-scope="scope"></template>	
2.6之后
<template v-slot="scope"></template>	

store/modules/cart.js

const state = {
  // 购物车中的数据存储到本地存储中
  cartProducts: JSON.parse(window.localStorage.getItem('cart-products')) || []
}
const getters = {
  // 统计商品数量
  totalCount (state) {
    return state.cartProducts.reduce((sum, prod) => sum + prod.count, 0)
  },
  // 统计商品总价
  totalPrice (state) {
    return state.cartProducts.reduce((sum, prod) => sum + prod.totalPrice, 0)
  },
  // 统计选中商品的数量
  checkedCount (state) {
    return state.cartProducts.reduce((sum, prod) => {
      if (prod.isChecked) {
        sum += prod.count
      }
      return sum
    }, 0)
  },
  // 统计选中商品的价格
  checkedPrice (state) {
    return state.cartProducts.reduce((sum, prod) => {
      if (prod.isChecked) {
        sum += prod.totalPrice
      }
      return sum
    }, 0)
  }
}
const mutations = {
  // 加入购物车
  addToCart (state, product) {
    // 1. cartProducts 中没有该商品,把该商品添加到数组,并增加 count,isChecked,totalPrice
    // 2. cartProducts 有该商品,让商品的数量加1,选中,计算小计
    const prod = state.cartProducts.find(item => item.id === product.id)
    if (prod) {
      prod.count++
      prod.isChecked = true
      prod.totalPrice = prod.count * prod.price
    } else {
      state.cartProducts.push({
        ...product,
        count: 1,
        isChecked: true,
        totalPrice: product.price
      })
    }
  },
  // 删除购物车中的商品
  deleteFromCart (state, prodId) {
    // 根据商品ID,找到商品在列表中的下标
    const index = state.cartProducts.findIndex(item => item.id === prodId)
    index !== -1 && state.cartProducts.splice(index, 1)
  },
  // 改变所有商品的isChecked属性
  updateAllProductChecked (state, checked) {
    state.cartProducts.forEach(prod => {
      prod.isChecked = checked
    })
  },
  // 改变某个商品的checked属性
  updateProductChecked (state, { checked, prodId}) {
    // 找到商品对象
    const prod = state.cartProducts.find(prod => prod.id === prodId)
    prod && (prod.isChecked = checked)
  },
  // 更新商品的数量和总价
  updateProduct (state, { prodId, count }) {
    const prod = state.cartProducts.find(prod => prod.id === prodId)
    if (prod) {
      prod.count = count
      prod.totalPrice = count * prod.price
    }
  }
}
const actions = {}

export default {
  // 开启命名空间
  namespaced: true,
  state,
  getters,
  mutations,
  actions
}

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import products from './modules/products'
import cart from './modules/cart'

Vue.use(Vuex)
// vuex中的插件
const myPlugin = store => {
  store.subscribe((mutation, state) => {
    if (mutation.type.startsWith('cart/')) {
      // 购物车中的数据存储到本地
      window.localStorage.setItem('cart-products', JSON.stringify(state.cart.cartProducts))
    }
  })
}

export default new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    products,
    cart
  },
  plugins: [myPlugin]
})

views/products

<template>
  <div>
    <el-breadcrumb separator="/">
      <el-breadcrumb-item><a href="#/">首页</a></el-breadcrumb-item>
      <el-breadcrumb-item><a href="#/">商品列表</a></el-breadcrumb-item>
    </el-breadcrumb>
    <el-table
      :data="products"
      style="width: 100%">
      <el-table-column
        prop="title"
        label="商品">
      </el-table-column>
      <el-table-column
        prop="price"
        label="价格">
      </el-table-column>
      <el-table-column
        prop="address"
        label="操作">
        <!-- <template slot-scope="scope"> -->
        <template v-slot="scope">
          <el-button @click="addToCart(scope.row)">加入购物车</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
import { mapState, mapActions, mapMutations } from 'vuex'
export default {
  name: 'ProductList',
  computed: {
    ...mapState('products', ['products'])
  },
  methods: {
    ...mapActions('products', ['getProducts']),
    ...mapMutations('cart', ['addToCart'])
  },
  created () {
    this.getProducts()
  }
}
</script>

<style></style>

views/carts

<template>
  <div>
    <el-breadcrumb separator="/">
      <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>购物车</el-breadcrumb-item>
    </el-breadcrumb>
    <el-table
      :data="cartProducts"
      style="width: 100%"
    >
      <el-table-column
        width="55">
        <template v-slot:header>
          <el-checkbox v-model="checkedAll" size="mini">
          </el-checkbox>
        </template>
        <!--
          @change="updateProductChecked"  默认参数:更新后的值
          @change="updateProductChecked(productId, $event)"  123, 原来那个默认参数
            当你传递了自定义参数的时候,还想得到原来那个默认参数,就手动传递一个 $event
         -->
        <template v-slot="scope">
          <el-checkbox
            size="mini"
            :value="scope.row.isChecked"
            @change="updateProductChecked({
              prodId: scope.row.id,
              checked: $event
            })"
          >
          </el-checkbox>
        </template>
      </el-table-column>
      <el-table-column
        prop="title"
        label="商品">
      </el-table-column>
      <el-table-column
        prop="price"
        label="单价">
      </el-table-column>
      <el-table-column
        prop="count"
        label="数量">
        <template v-slot="scope">
          <el-input-number :value="scope.row.count" @change="updateProduct({
            prodId: scope.row.id,
            count: $event
          })" size="mini"></el-input-number>
        </template>
      </el-table-column>
      <el-table-column
        prop="totalPrice"
        label="小计">
      </el-table-column>
      <el-table-column
        label="操作">
        <template>
          <el-button size="mini">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <div>
      <p>已选 <span>{{ checkedCount }}</span> 件商品,总价:<span>{{ checkedPrice }}</span></p>
      <el-button type="danger">结算</el-button>
    </div>
  </div>
</template>

<script>
import { mapState, mapMutations, mapGetters } from 'vuex'
export default {
  name: 'Cart',
  computed: {
    // 购物车列表数据
    ...mapState('cart', ['cartProducts']),
    // checkedCount:选中商品的个数
    // checkedPrice:选中商品的价格
    ...mapGetters('cart', ['checkedCount', 'checkedPrice']),
    // 全选
    checkedAll: {
      get () {
        return this.cartProducts.every(prod => prod.isChecked)
      },
      // 当全选的按钮选中时,每条商品的状态需要选中
      set (value) {
        this.updateAllProductChecked(value)
      }
    }
  },
  methods: {
    ...mapMutations('cart', [
      'updateAllProductChecked',
      'updateProductChecked',
      'updateProduct'
    ])
  }
}
</script>

<style></style>

views/components/pop-cart.js

<template>
  <el-popover
    width="350"
    trigger="hover"
  >
    <el-table :data="cartProducts" size="mini">
      <el-table-column property="title" width="130" label="商品"></el-table-column>
      <el-table-column property="price" label="价格"></el-table-column>
      <el-table-column property="count" width="50" label="数量"></el-table-column>
      <el-table-column label="操作">
        <template v-slot="scope">
          <el-button @click="deleteFromCart(scope.row.id)" size="mini">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <div>
      <p>{{ totalCount }} 件商品 共计¥{{ totalPrice }}</p>
      <el-button size="mini" type="danger" @click="$router.push({ name: 'cart' })">去购物车</el-button>
    </div>
    <el-badge :value="totalCount" class="item" slot="reference">
      <el-button type="primary">我的购物车</el-button>
    </el-badge>
  </el-popover>
</template>

<script>
import { mapState, mapGetters, mapMutations } from 'vuex'
export default {
  name: 'PopCart',
  computed: {
    ...mapState('cart', ['cartProducts']),
    ...mapGetters('cart', ['totalCount', 'totalPrice'])
  },
  methods: {
    ...mapMutations('cart', ['deleteFromCart'])
  }
}
</script>

<style>

</style>

vuex中的插件使用:
// 定义插件
const myPlugin = store => {
	// 当store初始化后调用
	// store中subscribe方法是用来订阅mutation
	store.subscribe((mutation, state) => {
		// 每次mutation之后调用
		// mutation的格式为 {type, payload}
	})

}

// 注册插件:
const store = new Vuex.Store({
	plugins: [myPlugin]
})

模拟实现vuex

myVuex/index.js

let _Vue = null
class Store {
  constructor (options) {
    const {
      state = {},
      getters = {},
      mutations = {},
      actions = {}
    } = options
    // _Vue.observable(state):把数据变成响应式的数据
    this.state = _Vue.observable(state)
    this.getters = Object.create(null)
    Object.keys(getters).forEach(key => {
      Object.defineProperty(this.getters, key, {
        get: () => getters[key](state)
      })
    })
    this._mutations = mutations
    this._actions = actions
  }

  commit (type, payload) {
    this._mutations[type](this.state, payload)
  }

  dispatch (type, payload) {
    this._actions[type](this, payload)
  }
}

function install (Vue) {
  _Vue = Vue
  _Vue.mixin({
    beforeCreate () {
      if (this.$options.store) {
        _Vue.prototype.$store = this.$options.store
      }
    }
  })
}

export default {
  Store,
  install
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值