Vue2阶段总结

一、 Vue简介

vue就是一个js库,并且无依赖别的js库,直接引入一个js文件就可以使用,与传统JS和JQuery框架不同,Vue的渐进式框架表示开发者可以由简单组件写起,渐渐搭建出一个复杂的前端平台。
形成Vue渐进式框架的核心概念为:组件化,MVVM,响应式,和生命周期。
Vue一切是数据为核心,使用数据来驱动视图刷新,我们不建议去操作dom

为什么要用Vue?

1. 组件化
Vue将组成一个页面的HTML,CSS和JS合并到一个组件中,可以被其他组件或页面引入而重复利用。通常每个.Vue文件作为一个组件导出,组件可以作为基础组件(如按钮)或一个页面(如登录页面)。组件化很好的将一个庞大复杂的前端工程拆分为一个个组件,重复利用的性质也大大提高了开发的效率。

2.MVVM 数据双向绑定
MVVM模式(全称为Model-View-ViewModel)为Vue实现数据双向绑定。在MVVM中,View为视图层,ViewModel为业务逻辑层,Model为数据层。
什么是数据双向绑定呢?当用户使View变化时(如填写表单),变化会自动同步到ViewModel处理相应逻辑,并将变化更新到Model数据库。反之,若服务端数据变化(如股价波动),变化会自动同步到ViewModel处理相应逻辑,并将变化同步到View展现给用户。
在用Vue之前,我完成HTML和JS之间的交互需要使用大量的DOM操作来实现动态加载。MVVM的数据双向绑定减少了DOM操作,更高效地实现了视图和数据的交互。同时,MVVM使界面、交互和数据层分离,便于设计人员负责设计界面,后端开发人员提供数据接口,而前端开发人员专注于业务交互逻辑的实现。
在这里插入图片描述
在这里插入图片描述

MVVM模型:

  • M:模型(Model),data中的数据
  • V:视图(View),模板代码
  • VM:视图模型(ViewModel),Vue实例

3.响应式 虚拟DOM

对于DOM来说,当HTML的一个元素(如div)需要响应数据更改时,会刷新整个页面,导致效率堪忧。对于虚拟DOM,浏览器会将HTML文件转换为JS文件并复制一个额外使用(虚拟)。对于任何更改,虚拟DOM都将复制的JS与原始JS进行比较,只重新加载更改的部分,局部修改到真实DOM上。

在Vue中,每个绑定data属性的组件都有一个Watcher检测data属性的变化。一旦检测到改变,则重新渲染该组件,这就是响应式。

`
4. 生命周期
最后,每个Vue组件都有生命周期,过程为创建 -> 挂载 -> 更新 -> 销毁。开发者可以通过钩子函数(如mounted)在组件生命周期中的不同时刻进行操作。下面是一张Vue生命周期的完整图解。


插值表达式

{{ 表达式 }}


扩展:实例属性大全
this.$data: 访问组件实例的数据对象。
this.$props: 访问组件实例的 props 对象。
this.$el: 获取组件实例的根 DOM 元素。
this.$options: 获取当前组件实例的初始化选项。
this.$slots: 获取当前组件的 slot 内容。
this.$refs: 获取组件引用的 DOM 元素或子组件实例。
this.$emit: 触发自定义事件。
this.$on: 监听自定义事件。
this.$once: 监听一次性的自定义事件。
this.$off: 移除自定义事件监听器。
this.$nextTick: 在下次 DOM 更新循环结束之后执行回调。
this.$mount: 手动挂载一个未挂载的实例。
this.$destroy: 完全销毁一个实例。
this.$set: 为响应式对象添加一个属性,确保新添加的属性也是响应式的。
this.$delete: 删除响应式对象的属性。

二、响应式,数据驱动视图

响应式系统的原理:

Vue.js 的响应式系统是基于 Object.defineProperty() 方法实现的,这个方法可以在对象上定义新属性或修改现有属性,并在属性被访问或修改时执行相应的操作。

响应式属性的定义:

在 data() 函数中定义的属性会自动变成响应式属性。Vue.js 会在内部将这些属性转换成响应式的,以便跟踪属性的变化并更新相关的视图。

其他响应式属性的定义方式:

除了 data() 函数,Vue.js 还提供了 reactive()、ref()、computed() 等函数来定义响应式属性。这些方式都可以创建可被 Vue.js 追踪的响应式属性。
响应式属性的更新:
当响应式属性的值发生变化时,Vue.js 会自动检测到变化,并触发相关的视图更新。这是 Vue.js 响应式系统的核心功能之一。

性能优化:

Vue.js 的响应式系统可以精确地跟踪哪些部分需要更新,从而提高应用的性能。这是因为 Vue.js 只会更新受影响的部分,而不是全局刷新。

深层次的响应式:

Vue.js 的响应式系统不仅可以追踪直接定义的属性,还可以自动追踪对象或数组内部属性的变化,这被称为"深层次的响应式"。


三、键盘监听

@keyup.enter = add

methods: {
  fn(e) {
    if(e.key == 'Enter){
      console.log('键盘回车触发',this.username)
    }
  }
}

四、创建实例化:

<div id = "App"></div>
<script>
  const app = new Vue({
    el: '#App'
    data: {
      imgUrl: './imgs/10-01.png'
    }
  })
</script>

五、脚手架实例化:

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

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

六、指令:

v-html: 填充内容(带样式标签)
v-text: 填充内容(纯文本)
v-show:
<div v-show = "show">hello vue</div>
<button @click = "isShow = !isShow">changeshow</button>
v-if/v-else:
<div v-if= 'number === 1'>hello vue {{ numbser }}</div>
<div v-else-if="number === 2">hello world{{ number }}</div>
<div v-else>hello someone{{ number }}</div>
v-on: 事件监听

写变量

v-on:click = "count++"
@click = ""

写函数

@click = "fn"
methods: {
  fn(){
    this.isShow = !this.isShow
  }
}

扩展

// click事件只能点击一次
<a v-on:click.once = "doThis"></a>
// 按键修饰符,只有在 keyCode 是 13 时调用 vm.submit()
<input v-on:keyup.13 = "submit">
可以阻止的监听事件:

.stop 阻止冒泡
.prevent 阻止默认事件
.capture 阻止捕获
.self 只监听触发该元素的事件

v-for 循环 key:唯一标识,必须要写
<div v-for= "(item,index) in [1,2,3]" :key = "index">{{ item }}</div>

// 渲染对象   一般更推荐用id作为key(唯一),使用index作为key(如果进行增删数组会不对应)
<div v-for="(val,key) in {one:1, tow:2}" :key = "key"></div>
v-bind 动态设置标签属性

操纵属性

v-bind:src = ""
:src = ""
<img v-bind:src = "imgUrl" :title = "msg"></img>
// 这个地方是因为挂载实例化对象,并没有用到脚手架的内容,所以用data对象来引用
data: {
  imgUrl: './.../../'
  msg: "dadwa"
}

操纵css样式

// 动态的添加
<a :class = "{active:false}"></a> // 默认为不添加,根据active的属性来决定
操纵style
<div class = "box" :style= "{CSS属性名1: CSS属性值, CSS属性名2: CSS属性值}">

七、阻止冒泡事件

e.stopPropagation()


八、实现父子组件之间的通信

(1).v-model 实现数据双向绑定

在父组件中使用 v-model 绑定一个 prop 到子组件上:

<child-component v-model="parentData"></child-component>

在子组件内部,v-model 会被解析为:

<input :value="value" @input="$emit('input', $event.target.value)">

v-model.trim 去空格
v-model.number 转数字

(2).使用sync修饰符

使用sync修饰符,必须要写update前缀:
在子组件中通过update前缀通知父组件更新 父组件中isShow方法中的值

methods: {
  closeDialog(){
    this.$emit('update:isShow',false)
  }
}

// sync 修饰符是一个用于实现父子组件之间双向数据绑定的语法糖。(子修改父)
// 它通常用于实现父组件向子组件传递数据,并且允许子组件修改该数据并反馈给父组件。
父组件向子组件传递一个 prop,并使用 .sync 修饰符。(父组件可以直接更新传递给子组件的 prop 值)
子组件可以修改这个 prop 的值,也可以监听父组件数据变化并更新自己的数据。
子组件通过触发 update:propName 事件来通知父组件数据已经更新。
父组件监听到这个事件后,会自动更新自己的数据,完成从子到父的数据反馈。

父组件向子组件传递一个 prop,并在 prop 名称后添加 .sync 修饰符。例如:

(3).使用ref和$refs

在子组件上添加 ref 属性:

<child-component ref="childRef"></child-component>

通过 $refs 访问子组件实例:

export default {
  data() {
    return {
      message: 'Hello, world!'
    }
  },
  methods: {
    sayHello() {
      console.log(this.message)
    }
  }
}

在父组件中,可以通过

this.$refs.childRef.message

访问子组件的 message 数据,
或者调用 this.$refs.childRef.sayHello() 方法。

九、扩展: v-model相当于 :value @input 实现原理

原理:Vue是数据驱动视图,当 :value改变,也就是数据发生改变,页面会自动改变
当页面输入改变,数据会自动改变 @input

解析一下:

1.父传子:

父组件data数据包中定义自定义属性、方法: :value、:cityId… @change = “handleChange”

子组件定义 Props来接收
props: {
  cityId: String
}
子组件接收后渲染数据:
<select :value = "cityId"></select>

:value @input

2.子传父:
子组件发生监听事件:
<select  @change = "handleChange"></select>
在子组件的methods方法中定义监听响应事件,给父组件传递:
methods: {
  handleChange (e) {
    this.$emit('父组件事件名', e.target.value)
  }
}
在父组件中:

$event用于事件监听事件中,用于获取当前点击事件的形参,也就是在子组件中传递的 e.target.value

<father @父组件事件名 = selectId = $event></father>
举例:删除功能的实现:
<button @click = "del(item.id)"></button>
<button @click = "add()"></button>
<button @click = "clear()"></button>

const app = new Vue({
  // 设置挂载点
  el:#app,
  data: {
    list: [
      { id: 1, name: '跑步一公里'},
      { id: 3, name: '游泳100米'}
    ]
  },
  methods: {
    del(id){
      this.list = this.list.filter(item => item.id !== id)
    },
    add(){
      if(this.todoName.trim() === '') {
        alert('请输入任务名称')
        return
      }
      this.list.unshift({
        id: +new Date(),
        name: this.todoName
      })
      this.todoName = ''
    },
    clear(){
      this.list = []
    }
  }
})

十、Vue异步更新 $nextTick

当页面中发生点击事件,渲染出了新的 DOM 元素(比如一个输入框)后,Vue 会先更新 DOM,然后才会执行 n e x t T i c k 回调函数。这样就能确保 t h i s . nextTick 回调函数。这样就能确保 this. nextTick回调函数。这样就能确保this.refs.inp 能够拿到新渲染出来的输入框 DOM 元素,并成功调用 focus() 方法,让输入框获取焦点。
this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …nsole.log(this.refs.inp.focus())
})


十一、实现非父子通信

1.provide & inject 作用:跨层级共享数据
  • 父组件 provide 提供数据:
export default {
  provide () {
    return {
      // 普通类型(非响应式)
      color: this.color
      // 复杂类型(响应式)
      userInfo: this.userInfo
    }
  }
}
  • 子/孙组件inject取值使用:
export default {
  inject: ['color','userInfo'],
  created () {
    console.log(this.color, this.userInfo)
  }
}
2.全状态管理插件Vuex
  • 安装vuex插件
yarn add vuex@3
  • 在sotre/index.js中导出:
// 声明
Vue.use(Vuex)
// 初始化一个空仓库
const store = new Vuex.Store()

// 导出给main.js使用
export default store
  • 在main.js中导入并挂载:
new Vue ({
  render: h=> h(App),
  store
}).$mount('#app')
十二、Vuex核心概念:
  • state
    表示存储在 Vuex 中的数据。它类似于组件中的数据,但是全局可访问。在 Vuex 中,State 是一个包含了所有应用级别状态的对象。
  • mutations
    用于修改 Vuex 的 State。Mutation 是一个包含了操作 State 的方法的对象。每个 Mutation 方法都接收一个 State 对象作为第一个参数,并可以接收一个可选的 Payload 对象作为第二个参数,用于传递数据。
  • actions
    用于进行异步操作和触发 Mutation。Action 类似于 Mutation,但是可以用于处理异步操作,例如发起网络请求或进行复杂的业务逻辑。Action 通过调用 Mutation 来修改 State,但是它可以包含任意异步操作。
  • getters
    用于从 Vuex 的 State 中派生出一些衍生数据。Getter 可以被视为 Vuex 中的计算属性,它会基于 State 的值进行计算,并返回一个新的衍生数据。
  • module
    用于将 Vuex 的 State、Getter、Mutation 和 Action 分割为模块化的结构。Module 允许将 Vuex 的逻辑划分为更小的单元,每个模块可以具有自己的 State、Getter、Mutation 和 Action。模块可以嵌套,形成层次化的结构。

十三、Vuex两种访问方式:

1.直接访问:
$store.state.全局数据名称
2.使用辅助函数:
import { mapState, mapMutations, mapActions, mapGetters }

开启命名空间

namespace:true
...mapState(['count'])

模块名,([‘xxx’])]

...mapState('user',['userInfo'])

...mapMutations(['subCount','changeTitle'])
...mapActions(['changeCountAction'])
...mapGetters(['filterList'])

十四、局部注册指令 设置directives属性

在组件内部注册自定义指令,只能在当前组件中使用。

export default {
  directives: {
    // 注册一个局部指令 `v-focus`
    focus: {
      // 钩子函数 'mounted'
      // 当绑定元素的组件被挂载后调用
      mounted(el) {
      // 获取绑定的 DOM 元素
      // 并调用 focus() 方法,使其获取焦点
        el.focus()
      }
    }
  }
}

使用:

<input v-focus>

示例:注册一个loading局部组件:

directives: {

   // 名为 'loading' 的自定义指令
  loading: {
    // 'inserted' 钩子函数(元素被加入到页面中的时候被触发)
    // 当绑定元素插入到 DOM 时调用
    inserted(el,binding) {
      // 检查 binding.value 的值
      // 如果为 true,则给元素添加 'loading' 类
      // 如果为 false,则从元素中移除 'loading' 类
      binding.value > el.classList.add('loading') : el.classList.remove('loading')
    },
    
    // 'update' 钩子函数(绑定的值发生改变时调用)
    // 因为只有update可以实时响应页面,所以在下面判断,一旦加载完毕就移除loading类
    update(el,binding) {
      // 同样检查 binding.value 的值
      // 并根据值的变化来添加或移除 'loading' 类
      binding.value ? el.classList.add('loading') : el.classList.remove('loading')
    }
  }
}

十五、全局注册指令

在 Vue 实例的 directive 方法中注册全局指令,所有组件都可以使用。
在main.js中:

Vue.directive('focus', {
  // binding:额外选项可以不写
  // 指令所在的DOM元素,被插入到页面中时触发
  inserted (el, binding) {
    el.focus()
  }
})

可以写多个 Vue.directive 注册多个全局自定义指令

十六、插槽

在组件内部预留一块区域,供父组件填充内容。这样可以增强组件的可复用性和灵活性。

1.默认插槽

在组件模板中用 元素表示默认插槽的位置。

2.具名插槽

在组件模板中,用 定义具名插槽。
在使用组件的时候,用 v-slot:slotName 或 #slotName 来指定内容填充的插槽。

3.作用域插槽

在子组件中,用 的方式将数据暴露给父组件。
在使用组件的时候,用 v-slot=“slotProps” 的方式接收子组件传递的数据,并在父组件模板中使用 slotProps.data 访问。

十七、生命周期

生命周期函数(钩子函数)

创建阶段(Creation)

beforeCreate: 在实例初始化之后,数据观测 (data observation) 和事件配置 (event setup) 之前被调用。
created: 在实例创建完成后被立即调用。

挂载阶段(Mounting)

beforeMount: 在挂载开始之前被调用,相关的 render 函数首次被调用。
mounted: 在挂载完成后被调用,实例已经被完全挂载到 DOM 中。

更新阶段(Updating)

beforeUpdate: 在数据更新之前、DOM 重新渲染之前被调用。
updated: 在数据更新完成、DOM 重新渲染之后被调用。

销毁阶段(Destruction)

beforeDestroy: 在实例销毁之前调用。实例仍然完全可用。
destroyed: 在实例销毁之后调用。所有的事件监听器会被移除,所有的子实例也会被销毁。

十八、axios异步通讯框架

async created(){
  const res = await axios.get('http:xxxxx')
  // 将数据更新给data中的list
  this.list = res.data.data
}
async的用法:

作为一个关键字放到函数前面,这样普通函数就变为了异步函数(必须配合await同时写才生效)

await的用法:

异步执行:异步函数会在执行过程中以异步的方式执行,不会阻塞后续代码的执行。这意味着在遇到
await 关键字时,异步函数会暂停执行并等待被 await 的表达式返回结果,然后继续执行后续代码。

async function fetchData() {
    try {
    // 发起网络请求获取数据
    const response = await fetch('https://api.example.com/data');

     // 将响应数据解析为 JSON 格式
    const data = await response.json();

    } catch (error) {
    // 捕获并处理错误
    console.error(error);
  }
}
  • await 关键字等待 fetch 函数返回的 Promise 对象的解析,即等待网络请求的响应。
  • 响应被赋值给 response变量,可以通过该变量获取响应的状态、头部和数据等信息。
  • await 关键字等待 json 方法返回的 Promise对象的解析,即等待数据的解析过程完成。
  • 解析后的数据被赋值给 data 变量,可以进一步处理或使用该数据。

十九、工程化开发和脚手架

在 Vue.js 中,组件的注册通常是在组件的选项对象(options)中完成的。而将组件的选项对象作为默认导出是一种常见的写法

  • Vue2底层基于webpack
  • 组件命名规范: 大驼峰命名法
export default{
  name: 'HomeView'
  // 组件注册
  components: {
    vHead,
    vSidebar,
    vTags
  }
}
  • 局部注册:在使用的组件中导入并注册
  • 全局注册:在main.js中导入并注册

二十、组件化开发和根组件

一个页面可以拆分成一个个组件,每个组件有着自己独立的结构、样式、行为
好处:便于维护,利于复用->提升开发效率
ctrl+c: 停止当前服务(关闭项目)

二十一、脚手架目录文件介绍:

  • src:源代码目录

  • assets: 静态资源目录->存放图片、字体等

  • components: 组件目录->存放通用组件

  • utils:存放各种通用工具函数和工具类,提高代码复用性和可维护性。(axios的封装)

  • API:统一管理和封装项目中的 API 请求(存放封装好的请求函数,每个页面发生的请求函数的封装)

  • store:集中管理应用程序的状态和数据。(Vuex)

二十二、scoped语法:

局部样式,防止组件与组件之间样式冲突

二十三、data是一个函数

data() {
  retur {
    count: 100
  }
}

二十四、router路由

在main.js中引入:

import router from './router/router.js';

挂载

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

当你使用 Vue Router 来进行路由管理时,你可以在应用程序的模板中使用 标签来指定路由视图的位置。

在router.js中:

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    //设置路由重定向
    redirect: './dashboard'
  },
  {
    path: '/',
    component: () => import('../components/common/Home.vue')
  }
]

const router = new VueRouter({
  mode: 'history'
  base: process.env.BASE_URL
  routes
})

export default router

二十五、路由的导航守卫

它的作用便是通过跳转或取消的方式守卫导航。所以我们可以通过 router.beforeEach 来注册一个全局前置守卫,对路由的跳转进行一些设置。
重点解析:
next() 函数是 Vue Router 的导航守卫中的一个重要概念。
next() 函数的作用是:

  1. 允许用户继续导航到所请求的路由。
  2. 可以改变导航的目标路由,比如重定向到其他页面。

举例:
在这里插入图片描述

  1. 如果 role 不存在(也就是用户未登录),并且当前路由不是 /login,则使用 next(‘/login’)
    进行重定向,让用户跳转到登录页面。
  2. 如果 role 存在(用户已登录),或者当前路由就是 /login,则使用 next() 允许用户继续导航到所请求的路由。

next() 函数是 Vue Router 导航守卫中的一个关键概念,它可以控制导航的流程,决定是否允许用户继续访问当前路由,或者重定向到其他路由。

完整代码:
在 src/main.js 中输入以下代码:

import Vue from 'vue';
import App from './App.vue';
import router from './router';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI, {
  size: 'small',
});

Vue.config.productionTip = false;

//设置全局前置守卫
//这个守卫方法会接受三个参数:
// to:Route :即将要去往的路由对象,通过这个参数来设置路由的去处。
// from:Route :当前导航正要离开的路由,通过这个参数来设置路由的出发处。
// next:Function :这个方法是必须要进行调用的,通过这个参数来resolve这个钩子函数
// next():进行下一个钩子,也就是下一个应该去跳转的路由
// next(false):中断当前导航。
// next('/'):跳转到一个不同的地址,当前导航中断,进行新的导航
// next(error):终止该导航,并将错误传递给router.onError()注册的回调。

router.beforeEach((to, from, next) => {
  //对页面的标题进行设置,获取路由元信息当中的title
  document.title = `${to.meta.title} | vue2-elementui-admin`;
  //获取本地存储的用户名
  const role = localStorage.getItem('ms_username');
  //判断是否存在用户名以及下一个钩子是否是跳转到 '/login'
  if (!role && to.path !== '/login') {
    //跳转到 '/login'
    next('/login');
  } else {
    next();
  }
});

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

二十六、声明式导航,跳转

  • 查询参数传参
  • 动态路由传参

1.路径式

(1).查询参数传参(query传参)

query解析:

this.$router.push(`/search?key=${this.inpValue}`)

methods :{
        goSearch(){
            //完整写法,更适合传参
           this.$router.push ({
              path: '路由路径'
              query:{
                参数名:参数值,
                参数名:参数值
                key: this.inpValue  //假设传递的有一个参为key,需要存储这个key    
            }
        })
    }
}

接收:

$route.query.参数名
(2).动态路由传参:
this.$router.push({
  path: '/路径/参数值'
})
methods :{
    //简写:
    goSearch(){
      this.$router.push(`/search/${this.inpValue}`)                    
    }
    //完整写法:
    this.$router.push({
        path: `/search/${this.inpValue}`    
    })            
  }

2.name 命名式

(1).name 查询参数传参(query传参)
 this.$router.push({
     name:'路由名字',
     query:{
         参数名1'参数1'
         参数名2'参数值2'    
     } 
 })

接收:

 $route.query.参数名
(2).动态路由传参
methods:{
    goSearch(){
        this.$router.push({
        //name: '路由名字'
        name:'search'
        //要写参数名和参数值要和路由对应
        params:{
             参数名:'参数值',
             // 配置好的路由路径为words,所以这个地方配置参数就是words
             words:this.inpValue       
        }
    })    
  }
}

接收

<div>{{ $route.params.words }}</div>

二十七、打包发布

vue脚手架工具已经提供了打包命令:

vue build
yarn build

默认情况下:
dist目录下打包的所有文件去加载其他文件时,用的是绝对路径,一旦是绝对路径,就需要放到服务器根目录才能打开。如果我们希望放到服务器子目录也可以运行:
在vue.config.js中配置一个相对路径即可:

module.exports = defineConfig({
  publicPath: './',
  transpileDependencies: true
})

假设我有一百多个页面,此时打包完毕后,运行加载是非常缓慢的(首屏加载速度过慢,单页应用程序,一次性就把所有资源加载完毕了)
将不同的组件分割成不同的代码块,访问到哪个路由,就加载哪个路由: 好处: 实现我们基本的打包 将一些高版本语法进行降级 对一些性能做一些优化
对资源进行了拆分

二十八、使用路由懒加载实现按需加载:

只有访问到路由的时候,才会去加载对应的组件
在这里插入图片描述
修改后:
在这里插入图片描述

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值