vue3学习笔记

新建vue3项目

使用 npm:

# npm 6.x
$ npm init vite@latest <project-name> --template vue

# npm 7+,需要加上额外的双短横线
$ npm init vite@latest <project-name> -- --template vue

$ cd <project-name>
$ npm install
$ npm run dev

一、动态指令

使用中比较少用

<template>
  <div>
    <img alt="Vue logo" src="./assets/logo.png"  @click="changeBtn" />
    
    <div :[arributeName]="di"></div>
    <button @[eventName]="changeBtn">按钮</button>

  </div>
</template>

<script>
export default {
  data(){
    return{
      di:'d1',
      arributeName:'class',
      eventName:'click'
    }
  },
  methods:{
    changeBtn(){
      this.arributeName="id"
    }
  }
}
</script>

<style>
.d1{
  width: 100px;
  height: 100px;
  background: red;
}
#d1{
  width: 100px;
  height: 100px;
  background: goldenrod;
}
</style>

Vue3的七种组件通信方式

采用<script setup>这种组合式API写法,相对于选项式来说,组合式API这种写法更加自由,具体可以参考Vue文档对两种方式的描述。

七种组件通信方式:

  • props

  • emit

  • v-model

  • refs

  • provide/inject

  • eventBus

  • vuex/pinia(状态管理工具)

上图中,列表和输入框分别是父子组件,根据不同传值方式,可能谁是父组件谁是子组件会有所调整。

Props方式

Props方式是Vue中最常见的一种父传子的一种方式,使用也比较简单。

我们将数据以及对数据的操作定义在父组件,子组件仅做列表的一个渲染;

父组件代码如下:

<template>
  <!-- 子组件 -->
  <child-components :list="list"></child-components>
  <!-- 父组件 -->
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="请输入"
    />
    <div class="input-group-append">
      <button @click="handleAdd" class="btn btn-primary" type="button">
        添加
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')
// add 触发后的事件处理函数
const handleAdd = () => {
  list.value.push(value.value)
  value.value = ''
}
</script>

子组件只需要对父组件传递的值进行渲染即可,代码如下:

<template>
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in props.list" :key="i">{{ i }}</li>
  </ul>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
  list: {
    type: Array,
    default: () => [],
  },
})
</script>

emit方式

emit方式也是Vue中最常见的组件通信方式,该方式用于子传父;

根据上面的demo,我们将列表定义在父组件,子组件只需要传递添加的值即可。

子组件代码如下:

<template>
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="请输入"
    />
    <div class="input-group-append">
      <button @click="handleSubmit" class="btn btn-primary" type="button">
        添加
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref, defineEmits } from 'vue'
const value = ref('')
const emits = defineEmits(['add'])
const handleSubmit = () => {
  emits('add', value.value)
  value.value = ''
}
</script>

在子组件中点击【添加】按钮后,emit一个自定义事件,并将添加的值作为参数传递。

父组件代码如下:

<template>
  <!-- 父组件 -->
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
  </ul>
  <!-- 子组件 -->
  <child-components @add="handleAdd"></child-components>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
// add 触发后的事件处理函数
const handleAdd = value => {
  list.value.push(value)
}
</script>

在父组件中只需要监听子组件自定义的事件,然后执行对应的添加操作。

v-model方式

v-model是Vue中一个比较出色的语法糖,就比如下面这段代码

<ChildComponent v-model:title="pageTitle" />

就是下面这段代码的简写形式

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

v-model确实简便了不少,现在我们就来看一下上面那个demo,如何用v-model实现。

子组件:

<template>
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="请输入"
    />
    <div class="input-group-append">
      <button @click="handleAdd" class="btn btn-primary" type="button">
        添加
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref, defineEmits, defineProps } from 'vue'
const value = ref('')
const props = defineProps({
  list: {
    type: Array,
    default: () => [],
  },
})
const emits = defineEmits(['update:list'])
// 添加操作
const handleAdd = () => {
  const arr = props.list
  arr.push(value.value)
  emits('update:list', arr)
  value.value = ''
}
</script>

在子组件中我们首先定义propsemits,然后添加完成之后emit指定事件。

注:update:*是Vue中的固定写法,*表示props中的某个属性名。

父组件中使用就比较简单,代码如下:

<template>
  <!-- 父组件 -->
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
  </ul>
  <!-- 子组件 -->
  <child-components v-model:list="list"></child-components>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
</script>

refs方式

在使用选项式API时,我们可以通过this.$refs.name的方式获取指定元素或者组件,但是组合式API中就无法使用哪种方式获取。如果我们想要通过ref的方式获取组件或者元素,需要定义一个同名的Ref对象,在组件挂载后就可以访问了。

示例代码如下:

<template>
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in childRefs?.list" :key="i">
      {{ i }}
    </li>
  </ul>
  <!-- 子组件 ref的值与<script>中的保持一致 -->
  <child-components ref="childRefs"></child-components>
  <!-- 父组件 -->
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const childRefs = ref(null)
</script>

子组件代码如下:

<template>
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="请输入"
    />
    <div class="input-group-append">
      <button @click="handleAdd" class="btn btn-primary" type="button">
        添加
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref, defineExpose } from 'vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')
// add 触发后的事件处理函数
const handleAdd = () => {
  list.value.push(value.value)
  value.value = ''
}
defineExpose({ list })
</script>

setup组件默认是关闭的,也即通过模板ref获取到的组件的公开实例,不会暴露任何在<script setup>中声明的绑定。如果需要公开需要通过defineExpose API暴露。

provide/inject方式

provideinject是Vue中提供的一对API,该API可以实现父组件向子组件传递数据,无论层级有多深,都可以通过这对API实现。示例代码如下所示:

父组件

<template>
  <!-- 子组件 -->
  <child-components></child-components>
  <!-- 父组件 -->
  <div class="child-wrap input-group">
    <input
      v-model="value"
      type="text"
      class="form-control"
      placeholder="请输入"
    />
    <div class="input-group-append">
      <button @click="handleAdd" class="btn btn-primary" type="button">
        添加
      </button>
    </div>
  </div>
</template>
<script setup>
import { ref, provide } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')
// 向子组件提供数据
provide('list', list.value)
// add 触发后的事件处理函数
const handleAdd = () => {
  list.value.push(value.value)
  value.value = ''
}
</script>

子组件

<template>
  <ul class="parent list-group">
    <li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
  </ul>
</template>
<script setup>
import { inject } from 'vue'
// 接受父组件提供的数据
const list = inject('list')
</script>

值得注意的是使用provide进行数据传递时,尽量readonly进行数据的包装,避免子组件修改父级传递过去的数据。

事件总线

Vue3中移除了事件总线,但是可以借助于第三方工具来完成,Vue官方推荐mitttiny-emitter

在大多数情况下不推荐使用全局事件总线的方式来实现组件通信,虽然比较简单粗暴,但是长久来说维护事件总线是一个大难题,所以这里就不展开讲解了,具体可以阅读具体工具的文档。

状态管理工具

Vuex和Pinia是Vue3中的状态管理工具,使用这两个工具可以轻松实现组件通信,由于这两个工具功能比较强大,这里就不做展示了,具体可以查阅文档。

事件

组件

全局组件

 传参使用content-abc接受值使用驼峰接受

NON-propds 

 

 通信

 插槽

 报错

下面的正确

简写#header

 作用域插槽

 app.component("list",{
        data(){
            return{
                list:['1','2','3']
            }
        },
        template: `
        <slot v-for="item in list" :item="item"></slot>
        `
    })

显示效果

具名插槽使用

显示效果

 

使用

 

显示效果

 

注意点:
1)组件中定义具名插槽使用<slot name='插槽名'></slot>
2)其它组件中使用具名插槽时必须使用template进行包裹并写上插槽名
使用具名插槽时每次都要写v-slot特别麻烦,可以使用#进行简写,如下:**
<template #header>
    <header>我是头部区域</header>
</template>
<template #footer>
    <footer>我是底部区域</footer>
</template>
 

 动态组件、异步组件

   <!--    动态组件和异步组件-->
<!--    <input-item v-show="currentItem==='input-item'"></input-item>-->
<!--    <world-item v-show="currentItem==='world-item'"></world-item>-->
    
 <!--    动态组件根据数据的变化。给合component标签来随时切换组件的显示-->
    <keep-alive>  <!-- 缓冲组建的内容-->
        <component :is="currentItem"></component>
    </keep-alive>
methods:{
            changeBtn(){
                if(this.currentItem==='input-item'){
                    this.currentItem='world-item'
                }else{
                    this.currentItem='input-item'
                }
                console.log(123)
            },
        },
//动态组件
    app.component("input-item",{
        template:`<input />`
    })
    app.component("world-item",{
        template:`<div>hello world</div>`
    })
//异步组件


//异步组件,过四秒之后显示组件的内容
    const asyncCommonItem=Vue.defineAsyncComponent( () => {
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve({
                    template:`<div>This a async component</div>`
                })
            },4000)
        })
    })

//异步组件
    app.component('async-common-item',asyncCommonItem)

补充语法:

v-once:只渲染一次

ref(获取dom节点):mounted中获取dom

获取组件使用:调用组件中的方法

this.$refs.commone.methedsName()

 父组件传值给孙子组件 provide/inject  

  <h2>父组件传值给孙子组件 provide/inject</h2>
    <div>
        <parent-item :count="count">
        </parent-item>
    </div>


  const Counter={
        data(){
            return {
                num:0,
                num1:'a',
                currentItem:'input-item',
                count:2121
            }
        },

        //
        provide: {
            count:2121
        },
        //如果想要将data中的数据进行传值
         provide(){
           return{
            count:this.count
           }
            
        },

}

 //组件嵌套传值
    app.component("child-item",{
        // props:['currentItem'],
        inject:['count'],
        template:`<input :value="count"/>`
    })
    app.component("parent-item",{
        // props:['count'],
        template:`<div >parent-item<child-item></child-item> </div>`
    })

vue动画:

添加class类名

 

 背景色渐变

 

 另一种实现方法

list动画、通过数据控制动画

 <h2>list动画</h2>
    <div>
        <transition-group>
            <span class="list-item" v-for="(item,index) in list" :key="item" @click="delItem(index)"> {{item}}</span>
        </transition-group>

    </div>
    <button @click="addBtn">按钮</button>



    <h2>状态动画,svg通过数据改变控制动画</h2>


    <span> {{animateNumber}}</span>
    <button @click="addBtn">按钮</button>
   .list-item{
        display: inline-block;
        padding: 10px;
    }
    .v-enter-from{
        opacity: 0;
        transform: translateY(30px);
    }
    .v-enter-active{
        transition: all .5s ease-in;
    }
    .v-enter-to{
        opacity: 0;
        transform: translateY(0);
    }
    /*其他列表项移动*/
    .v-move{
        transition: all .5s ease-in;
    }
    .v-leave-from{
        opacity: 1;
        transform: translateY(0);
    }
    .v-leave-active{
        transition: all .5s ease-out;
    }
    .v-leave-to{
        opacity: 0;
        transform: translateY(30px);
    }
data(){
            return {
                num1:'a',
                currentItem:'input-item',
                count:2121,
                list:['1','2','3'],
                num:1,
                animateNumber:1,
            }
        },
        methods:{
            addBtn(){
                this.list.unshift(this.list.length+1)
                console.log(this.animateNumber)
                this.num=10
                if(this.animateNumber<this.num){
                     let animation=setInterval(()=>{
                        this.animateNumber+=1
                        if(this.animateNumber===10){
                            clearInterval(animation)
                        }
                    },100)

                }
            },
            delItem(index){
                console.log(index)
                this.list.splice(index,1)
            }
        },

maxin混入

组件内部data优先级高于mixin(混入)的data的优先级

局部

const myMixin={
        //data混入,会被组件内部的data覆盖
        data() {
            return {
                number: 2,
                count2: 3
            };
        },
    //    生命周期函数混入,不会覆盖,先执行mixin里的生命周期函数,在执行组件内部的
    //    组件内部的methods,高于mixin中的方法
    } 

const Counter={
        data(){
            return {
                num1:'a',
                currentItem:'input-item',
                count:2121,
                list:['1','2','3'],
                num:1,
                animateNumber:1,

            //    minix混入
                number:1,

            }
        },
        // template: `
        // <child />
        // `,

        // mixins: [myMixin],


.......

const app =Vue.createApp(Counter)
    // 子组件中使用mixin时也需要在子组件中注册后使用
    app.component('child',{
        // mixins: [myMixin],
        template:`<span>{{count2}}</span>`
    })

全局mixin

 //全局mixin
    app.mixin({
        //data混入,会被组件内部的data覆盖
        data() {
            return {
                number: 2,
                count2: 3
            };
        },
        //    生命周期函数混入,不会覆盖,先执行mixin里的生命周期函数,在执行组件内部的
        //    组件内部的methods,高于mixin中的方法
    })

自定义属性mixin

<div id="point">
    <span>{{this.$options.number}}</span>
</div>
自定义属性,组件里的自定义属性优先级高于mixin里的

 const myMixin={
        //不在data里,的属性称为自定义属性
        number:1
    }
    const Counter={
        number: 3,
        mixins: [myMixin],
        methods:{
            delItem(index){
            }
        },
    }

    // 设计模式: 面向数据编程 MVVM: M->model数据  v->view视图  vm->viewmodel 视图数据连接层
    const app =Vue.createApp(Counter)

    //修改优先级方式
    app.config.optionMergeStrategies.number=(mixinVal,appVal)=>{
        return mixinVal|| appVal
    }

自定义指令

全局

//自定义指令
    // app.directive('focus',{
    //     mounted(el) {
    //         el.focus()
    //     }
    // })

使用时直接v-focus

局部

const directives={
        focus:{
            mounted(el) {
                el.focus()
            }
        }

    }
    const Counter={
        directives:directives,
      
    }

使用时

 <input  v-focus />

动态修改值改变显示

<div v-pos="top" class="head">
            <input />
        </div>
 const Counter={
        data(){
          return {
              top:100
          }
        },
        // directives:directives,
        mounted(){
            // this.$refs.input.focus()
        }
    }
    // 设计模式: 面向数据编程 MVVM: M->model数据  v->view视图  vm->viewmodel 视图数据连接层
    const app =Vue.createApp(Counter)
    //自定义指令
    app.directive('focus',{
        mounted(el) {
            el.focus()
        },
        beforeUpdate(){

        },
        updated(){

        },
        beforeUnmount(){

        },
        unmounted(){

        }
    })
//接收参数,动态改变
    app.directive('pos',{
        mounted(el,binding){
            el.style.top=(binding.value+'px')
        },
        updated(el,binding){
            el.style.top=(binding.value+'px')
        },
    })
// 等价写法
    app.directive('pos',(el,binding)=>{
        el.style.top=(binding.value+'px')
    },)

使用arg

   <div v-pos:left="distance" class="head">
            <input />
        </div>
 app.directive('pos',(el,binding)=>{
        console.log(binding)
        el.style[binding.arg]=(binding.value+'px')
    },)

vue3 特性

Teleport

当我们需要在真个浏览器显示遮罩层时,可使用:

下面为不使用时在组件中显示遮罩,没有显示全屏遮罩

   <div class="area">
        <button @click="hello=!hello">按钮mask</button>
        <div class="mask" v-show="hello"></div>
    </div>

<div class="area">
        <button @click="hello=!hello">按钮mask</button>
        <telePort to="body">
            <div class="mask" v-show="hello"></div>
        </telePort>

    </div>

使用后,显示效果,to表示相对于那个元素显示

 

render-function

使用render替换template

<div id="point">
    <my-title :level="1">
        hello
    </my-title>
</div>


<script>
    const Counter={
        data(){
            return {
                hello:false
            }
        }
    }
    // 设计模式: 面向数据编程 MVVM: M->model数据  v->view视图  vm->viewmodel 视图数据连接层
    const app =Vue.createApp(Counter)
    app.component('my-title',{
        props:['level'],
        render(){
            const {h} = Vue
            return h('h'+this.level,{},this.$slots.default())
        }
        

    })
    const vm=app.mount('#point')

template在底层被编译会生成render函数,render调用Vue中的h方法返回一个虚拟dom

虚拟DOM:一个dom节点的js对象表述(真是dom的js映射)

//    真实DOM
<div> hello </div>
//转化为虚拟dom,跨平台(weex),编译快
{
    tagName:'div',
        text:'hello',
    attributes:{}
}
render(){
            const {h} = Vue
            //虚拟DOM
            //h(标签,属性,可写成数组[])
            return h('h'+this.level,{},[this.$slots.default(),h('h4',{},'dell')])
        }

 插件的定义和使用

    //plugin 也是把通用的功能封装起来
    const myPlugin={
        install(app,options){
            app.provide('name','dell lee')
            app.directive('focus',{
                mounted(el){
                   el.focus()
                }
            })
            app.mixin({
                mounted(){
                    console.log('mixin mounted')
                }
            })
            app.config.globalProperties.$myhello = "see hello"
            console.log(app,options)
        }
    }
    const Counter={

    }
    // 设计模式: 面向数据编程 MVVM: M->model数据  v->view视图  vm->viewmodel 视图数据连接层
    const app =Vue.createApp(Counter)

    app.use(myPlugin,{name:'dell'})
    app.component('my-title',{
        props:['level'],
        inject:['name'],
        mounted(){
          console.log(this.$myhello ,'---')
        },
        template:`<div>{{name}}</div> <input  v-focus />`


    })

    const vm=app.mount('#point')

数据校验实现

不封装成插件写法

const Counter={
        data(){
            return {
                name:'li lee',
                age:25
            }
        },
        rules:{
            age:{
                validate:(age) => age >20,
                message:'too young!!'
            }
        }

    }
    // 设计模式: 面向数据编程 MVVM: M->model数据  v->view视图  vm->viewmodel 视图数据连接层
    const app =Vue.createApp(Counter)
    app.mixin({
        created(){
            for(let key in this.$options.rules){
                const item =this.$options.rules[key]
                    console.log('item----',item)
                this.$watch(key,(value)=>{
                    let result=item.validate(value)
                    if(!result){
                        console.log(item.message)
                    }
                })
            }

        }
    })

封装成插件写法

const validatorPlugin =(app,options)=>{
        app.mixin({
            created(){
                for(let key in this.$options.rules){
                    const item =this.$options.rules[key]
                    console.log('item----',item)
                    this.$watch(key,(value)=>{
                        let result=item.validate(value)
                        if(!result){
                            console.log(item.message)
                        }
                    })
                }

            }
        })
    }
    // 设计模式: 面向数据编程 MVVM: M->model数据  v->view视图  vm->viewmodel 视图数据连接层
    const app =Vue.createApp(Counter)
    app.use(validatorPlugin)

 setup

<div id="point">
    <div @click="handleClick">name:{{name}} age:{{age}}</div>
</div>


<script>
    //template->render->h函数->虚拟dom(js对象)->真实(DOM)->展示到页面
    
    const Counter={
        methods:{
          test(){
              console.log(this.$options)
          }
        },
        mounted(){
           this.test()
        },
        //created 实例被完全初始化之前、setup里不可调用外部的方法,因为拿不到app的实例,return的内容可被模板使用
        setup(props,context){
            return {
                name:'li lee',
                age:25,
                handleClick:()=>{
                    alert(123)
                }
            }
        }

ref、reactive响应式引用的用法和实例

//ref、reactive响应式引用
    //原理通过proxy数据进行封装,待数据变化时,触发模板等内容的更新
    //ref 处理基础类型的数据
    const Counter={
        setup(props,context){
            const {ref} =Vue
            // proxy,dell 变成proxy({value:'dell'})这样一个响应式应用
            let name=ref('dell');
            setTimeout(()=>{
                name.value='lee'
            },2000)
            return { name }
        }
    }
    // 设计模式: 面向数据编程 MVVM: M->model数据  v->view视图  vm->viewmodel 视图数据连接层
    const app =Vue.createApp(Counter)

ref 处理基础类型的数据

reactive,处理非基础类型的数据

<div id="point">
    <div> {{nameObj.name}}{{nameArry[0]}}</div>

</div>
const Counter={
        setup(props,context){

            const { reactive,readonly ,toRefs} =Vue
            let nameObj=reactive({name:'delll'});
            let nameArry=reactive([123]);
            setTimeout(()=>{
                nameObj.name='lee'
                nameArry[0]=456
            },2000)
            // torefs,会把proxy({value:'dell'})转化成 {name:proxy({value:'dell'})}
            const { name } =toRefs(nameObj)
            return { name ,nameArry}

        }
    }

Composition Api开发todoList

不封装写法

<template>

<div id="point">
    <input :value="inputValue" @input="inputChangeValue"/>
    <div>{{inputValue}}</div>
    <button @click="btn">提交</button>
    <div>
        <ul>
            <li v-for="(item,index) in list" :key="index">{{item}}</li>
            <li>2</li>
        </ul>
    </div>
</div>

  <script>

const Counter={
        setup(props,context){
             const {ref,reactive}=Vue
             let inputValue=ref(123)
             let list =reactive([])
             const inputChange=(e)=>{
                 inputValue.value=e.target.value
             }
             function btn(){
                 list.push(inputValue.value)
             }
             return {inputValue,inputChange,btn,list}
        }

    }

封装后的写法

<template>

<div id="point">
    <input :value="inputValue" @input="inputChangeValue"/>
    <div>{{inputValue}}</div>
    <button @click="()=>addItemList(inputValue)">提交</button>
    <div>
        <ul>
            <li v-for="(item,index) in list" :key="index">{{item}}</li>
            <li>2</li>
        </ul>
    </div>
</div>

 <script>

 //ref、reactive响应式引用
    //原理通过proxy数据进行封装,待数据变化时,触发模板等内容的更新
    //ref 处理基础类型的数据

    //关于list的操作的内容进行了封装
    const listRelativeEffect = () => {
        const { reactive } =Vue
        let list = reactive([])
        //传参item
        const addItemList = (item) => {
            list.push(item)
        }
        return {list,addItemList}
    }
    // 封装关于inputValue的操作封装
    const inputRelativeEffect=()=>{
        const { ref } =Vue
        let inputValue = ref('默认值')
        const inputChangeValue=(e)=>{
            inputValue.value=e.target.value
        }
        return {inputValue,inputChangeValue}
    }
    const Counter={
        setup(props,context){
            //    封装后的写法,流程调度中转
            const {addItemList,list}=listRelativeEffect()
            const {inputValue,inputChangeValue}=inputRelativeEffect()
            return{ addItemList,list,inputValue,inputChangeValue}

        }

    }

vuex

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值