Vue进阶脚手架开发

1、分析脚手架

├── node_modules 
├── public
│   ├── favicon.ico: 页签图标
│   └── index.html: 主页面
├── src
│   ├── assets: 存放静态资源
│   │   └── logo.png
│   │── component: 存放组件
│   │   └── HelloWorld.vue
│   │── App.vue: 汇总所有组件
│   │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件 
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件

2、render函数

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

Vue.config.productionTip = false

new Vue({
  el:"#app",
  render(createElement){
    return createElement('h1',"你好啊")
  }
})

在这里插入图片描述
render是函数,能创建元素,再精简点

  render: createElement=>createElement('h1',"你好啊")
new Vue({
  el:"#app",
  render:h=>h(App)
  
})

关于不同版本的Vue:
1.vue.js与vue.runtime.xxx.js的区别:
(1).vue.js是完整版的Vue,包含:核心功能+模板解析器
(2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。

3、默认配置

Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpakc 配置,
终端输入vue inspect > output.js
使用vue.config.js可以对脚手架进行个性化定制,详情见官网

module.exports = {
    pages: {
      index: {
        //入口
        entry: 'src/main.js',
      },
    },
    lintOnSave:false//关闭语法检查
  }

4、ref属性

1.获取原生dom元素原生里用id来操作,vue里面用ref,被用来给元素或子组件注册引用信息(id的替代者)
2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3. 使用方式:

  1. 打标识:<h1 ref="xxx">.....</h1><School ref="xxx"></School>
  2. 获取:this.$refs.xxx
<template>
	<div>
		<h1 v-text="msg" ref="title"></h1>
		<button ref="btn" @click="showDOM">点我</button>
		<School></School>
		<Student></Student>
	</div>
</template>

<script>
	//引入组件
	import School from './components/School'
	import Student from './components/Student'

	export default {
		name:'App',
		data(){
			return{
				msg:"2021/9/14"
			}
		},
		components:{
			School,
			Student
		},
		methods:{
			showDOM:function(){
				console.log(this.$refs.title);
				console.log(this.$refs.btn);
			}
		}
	}
</script>

在这里插入图片描述

5、props配置

  1. 功能:让组件接收外部传过来的数据
  2. 传递数据:<Demo name="xxx"/>
  3. 接收数据:
    1. 第一种方式(简单声明接收):props:['name','age','sex']
    2. 第二种方式(限制数据类型):
props:{
			name:String,
			age:Number,
			sex:String
		}
  1. 第三种方式(限制类型、限制必要性、指定默认值):
 props:{
      	name:{
      	type:String, //类型
      	required:true, //必要性
      	default:'老王' //默认值
      	}
      }

示例

//app.vue
<template>
	<div>
		<Student name="李四" sex="女" :age="18+1"></Student>
	</div>
</template>

<script>
	//引入组件
	import Student from './components/Student'

	export default {
		name:'App',
		components:{
			Student
		}
	}
</script>

//student.vue
<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生年龄:{{age}}</h2>
	</div>
</template>

<script>
	 export default {
		name:'Student',
		props:['name','age','sex']
	}
</script>

动态绑定,计算双引号里面的值

	<Student name="李四" sex="女" :age="18+1"></Student>

在这里插入图片描述

<Student name="李四" sex="女" age="18+1"></Student>

在这里插入图片描述

如果组件中也定义了相同名称的数据

<script>
	 export default {
		name:'Student',
		data(){
			return{
				age:12
			}
		},
		props:{
			name:String,
			age:Number,
			sex:String
		}

	}
</script>

在这里插入图片描述
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告。

<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生年龄:{{age}}</h2>
		<button @click="showAge">点我</button>
	</div>
</template>

<script>
	 export default {
		name:'Student',
		methods:{
			showAge:function(){
				this.age++
			}
		},
		//props:['name','age','sex'],//简单接收
		props:{
			name:String,
			age:Number,
			sex:String
		}

	}
</script>

在这里插入图片描述
若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。

<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生年龄:{{thisAge}}</h2>
		<button @click="showAge">点我</button>
	</div>
</template>

<script>
	 export default {
		name:'Student',
		data(){
			return{
				thisAge:this.age
			}
		},
		methods:{
			showAge:function(){
				this.thisAge++
			}
		},
		//props:['name','age','sex'],//简单接收
		props:{
			name:String,
			age:Number,
			sex:String
		}

	}
</script>

Cate.vue
<template>
  <div class="cate">
      <h3>title</h3>
        <ul>
            <li v-for="item in listData">{{item}}</li>
        </ul>
  </div>
</template>

<script>
export default {
name:"Cate",
props:["listData","title"]
}
</script>

<style>
.cate{
    background-color:skyblue;
    width: 200px;
    height: 300px;
}
h3{
    text-align: center;
}

</style>
//App.vue
<template>
  <div class="container">
	  <Cate :listData="foods" title="foods"/>
	  <Cate :listData="games" title="games"/>
	  <Cate :listData="films" title="films"/>
  </div>
</template>

<script>
import Cate from './components/Cate'
export default {
name:'App',
components:{
	Cate
},
data(){
	return{
		foods:["火锅","烧烤","炸鸡","泡面"],
		games:["超级玛丽","奥比岛","洛克王国","摩尔庄园"],
		films:["教父","峰暴","一天","巧克力工厂"],
		
	}
}
}
</script>

<style>
.container{
	display: flex;
	justify-content: space-around;
}
</style>

6、mixin混入

多个组件共享一个配置

  1. 功能:可以把多个组件共用的配置提取成一个混入对象
  2. 使用方式:
    第一步定义混合:
   {
       data(){....},
       methods:{....}
       ....
   }
<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生年龄:{{sex}}</h2>
			<button @click="showName">点我</button>
	</div>
</template>

<script>
	import {mixin}from'../mixin.js'
	 export default {
		name:'Student',
		data(){
			return{
				name:"张三",
				sex:"男"
			}
		},
		mixins:[mixin]
		

	}
</script>

//minin.js
export const mixin={methods:{
    showName:function(){
        console.log(this.name)
    }
}}

第二步使用混入:
全局混入:Vue.mixin(xxx)在main.js中写入
局部混入:mixins:['xxx']在组件中写

7、插件

  1. 功能:用于增强Vue
  2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
  3. 定义插件:
//plugins.js
   对象.install = function (Vue, options) {
       // 1. 添加全局过滤器
       Vue.filter(....)
   
       // 2. 添加全局指令
       Vue.directive(....)
   
       // 3. 配置全局混入(合)
       Vue.mixin(....)
   
       // 4. 添加实例方法
       Vue.prototype.$myMethod = function () {...}
       Vue.prototype.$myProperty = xxxx
   }
  1. 使用插件:Vue.use()
//main.js


//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入插件
import plugins from './plugins'
//关闭Vue的生产提示
Vue.config.productionTip = false

//应用(使用)插件
Vue.use(plugins,1,2,3)
//创建vm
new Vue({
	el:'#app',
	render: h => h(App)
})

8、scoped样式

  1. 作用:让样式在局部生效防止冲突
  2. 写法:<style scoped>
  3. 写在App.vue不太适用
    安装less-loader
npm i less-loader@7.2.3

查看插件版本

npm view less-loader versions
<style lang="less" scoped>
	.demo{
		background-color: pink;
		.atguigu{
			font-size: 40px;
		}
	}
</style>

9、浏览器缓存

  1. 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
  2. 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
  3. 相关API:
    xxxxxStorage.setItem('key', 'value');该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
    JSON.stringify将 JavaScript 值转换为 JSON 字符串
    JSON.parse将数据转换为 JavaScript 对象
    xxxxxStorage.getItem('person');
    该方法接受一个键名作为参数,返回键名对应的值。
    xxxxxStorage.removeItem('key');
    该方法接受一个键名作为参数,并把该键名从存储中删除。
    xxxxxStorage.clear()
    该方法会清空存储中的所有数据。
  4. 备注:
    1. SessionStorage存储的内容会随着浏览器窗口关闭而消失。
    2. LocalStorage存储的内容,需要手动清除才会消失。
    3. xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。
    4. JSON.parse(null)的结果依然是null。

10、组件自定义事件

通过父组件给子组件传递函数类型的props实现:子给父传递数据

//School.vue
<template>
	<div>
		<h2>学校:{{name}}</h2>
		<h2>地址:{{address}}</h2>
		<button @click="sendSchoolName">点我</button>
	</div>
</template>

<script>
	 export default {
		name:'School',
		data(){
			return{
				name:"一中",
				address:"广州"
			}
		},
		props:['getSchoolName'],
		methods: {
			sendSchoolName(){//传递过来了以后要调用
				this.getSchoolName(this.name)
			}
		},
	}
</script>
//App.vue
<template>
	<div>
		<Student></Student>
		<hr>//通过父组件给子组件传递函数类型的props实现:子给父传递数据

		<School :getSchoolName="getSchoolName"></School>
	</div>
</template>

<script>
	//引入组件
import Student from './components/Student.vue'
import School from './components/School.vue'

	export default {
		name:'App',
		components:{
			Student,
			School
		},
		methods:{
			getSchoolName:function(name){
				console.log("App收到了name",name);
			},
		}
	}
</script>

在这里插入图片描述

通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用@或v-on)

//App.vue
<template>
	<div>
		<Student @dianji="getStudentName"/>//父组件给子组件绑定一个自定义事件实现
	</div>
</template>

<script>
	//引入组件
import Student from './components/Student.vue'
	export default {
		name:'App',
		components:{
			Student,
		},
		methods:{
			getStudentName(name,...params){
				console.log('App收到了学生名:',name,params)
				this.studentName = name
			},
		}
	}
</script>

//Student.vue
<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生年龄:{{sex}}</h2>
		<button @click="sendStudentlName">点我</button>
	</div>
</template>

<script>
	import {mixin}from'../mixin.js'
	 export default {
		name:'Student',
		data(){
			return{
				name:"张三",
				sex:"男"
			}
		},
		methods:{
		sendStudentlName(){
		//需要绑定点击触发
		//触发Student组件实例身上的dianji事件
		//子给父传递数据
		this.$emit('dianji',this.name,666,888,900)
			},
		}

	}
</script>

在这里插入图片描述

通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref

//App.vue
<template>
	<div>
			<Student ref="student"/>
	</div>
</template>

<script>
	//引入组件
import Student from './components/Student.vue'
import School from './components/School.vue'

	export default {
		name:'App',
		components:{
			Student,
			School
		},
		methods:{
			getStudentName(name,...params){
				console.log('App收到了学生名:',name,params)
				this.studentName = name
			},
		},
		mounted() {
		//给student标签绑定一个点击事件,然后这个事件会调用方法
			this.$refs.student.$on('dianji',this.getStudentName) //绑定自定义事件
			// this.$refs.student.$once('atguigu',this.getStudentName) //绑定自定义事(一次性)
		},
	}
</script>

//Student.vue
<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生年龄:{{sex}}</h2>
		<button @click="sendStudentlName">点我</button>
	</div>
</template>

<script>
	import {mixin}from'../mixin.js'
	 export default {
		name:'Student',
		data(){
			return{
				name:"张三",
				sex:"男"
			}
		},
		methods:{
		sendStudentlName(){
		//触发Student组件实例身上的dianji事件
		this.$emit('dianji',this.name,666,888,900)
			},
		}

	}
</script>

在这里插入图片描述
一个是通过vue的@绑定自定义事件,一个是通过ref属性获取到dom元素然后绑定自定义事件

  1. 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。
  2. 触发自定义事件:this.$emit('dianji',数据)
  3. 解绑自定义事件this.$off('dianji')
<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生年龄:{{sex}}</h2>
		<button @click="sendStudentlName">点我输出</button>
		<button @click="unbind">点我解绑</button>
	</div>
</template>
<script>
	 export default {
		name:'Student',
		data(){
			return{
				name:"张三",
				sex:"男"
			}
		},
		methods:{
		sendStudentlName(){
		//触发Student组件实例身上的dianji事件
		this.$emit('dianji',this.name,666,888,900)
			},
		unbind()
		{
		this.$off("dianji")//解绑一个事件
		this.$off(["dianji","demo"])//解绑多个事件
		this.$off()//解绑所有事件
		}
		},
	}
</script>

如果调用this.destory,那么所有自定义属性都会被解绑,因为绑定的实例对象已经被销毁了
4. 组件上也可以绑定原生DOM事件,需要使用native修饰符,不然会默认认为绑定的是自定义事件。

<Student ref="student" @click.native="方法名"></Student>
  1. 注意:通过this.$refs.xxx.$on('dianji',回调函数)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!

11、全局事件总线

  1. 一种组件间通信的方式,适用于任意组件间通信。
  2. 安装全局事件总线:
//App.vue
   new Vue({
   	......
   	beforeCreate() {//this指向这个vue实例对象
   		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
   	},
       ......
   }) 
  1. 使用事件总线:
    1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
  methods(){
    demo(data){......}
  }
  ......
  mounted() {
    this.$bus.$on('xxxx',this.demo)
  }
  1. 提供数据:this.$bus.$emit('xxxx',数据)
  2. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件

实现Student、School组件间的相互通信,Student组件传递信息给School组件,School组件绑定自定义方法,然后Student组件emit触发,并且传递参数进行通信,

//main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	beforeCreate() {
		Vue.prototype.$bus = this //安装全局事件总线
	},
})
//Student.vue
<template>
	<div class="student">
		<h2>学生姓名:{{name}}</h2>
		<h2>学生性别:{{sex}}</h2>
		<button @click="sendStudentName">把学生名给School组件</button>
	</div>
</template>

<script>
	export default {
		name:'Student',
		data() {
			return {
				name:'张三',
				sex:'男',
			}
		},
		mounted() {
			// console.log('Student',this.x)
		},
		methods: {
			sendStudentName(){
				this.$bus.$emit('hello',this.name)
			}
		},
	}
</script>
//School.vue
<template>
	<div class="school">
		<h2>学校名称:{{name}}</h2>
		<h2>学校地址:{{address}}</h2>
	</div>
</template>
<script>
	export default {
		name:'School',
		data() {
			return {
				name:'二中',
				address:'北京',
			}
		},
		mounted() {
			// console.log('School',this)
			this.$bus.$on('hello',(data)=>{
				console.log('我是School组件,收到了数据',data)
			})
		},
		beforeDestroy() {
			this.$bus.$off('hello')
		},
	}
</script>

12、消息的订阅和发布

  1. 一种组件间通信的方式,适用于任意组件间通信。
  2. 使用步骤:
    1. 安装pubsubnpm i pubsub-js
    2. 引入: import pubsub from 'pubsub-js'
    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
    4. 提供数据:pubsub.publish('xxx',数据)
    5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。

School组件订阅然后Student组件发布信息实现组件间的通信
语法:
(1) import PubSub from ‘pubsub-js’ // 引入
(2) PubSub.subscribe(‘msgName’, functon(msgName, data){ })
(3) PubSub.publish(‘msgName’, data): 发布消息, 触发订阅的回调函数调用
(4) PubSub.unsubscribe(token): 取消消息的订阅

//School.vue
<template>
	<div>
		<h2>学校:{{name}}</h2>
		<h2>地址:{{address}}</h2>
	</div>
</template>

<script>
import pubsub from 'pubsub-js'
	 export default {
		name:'School',
		data(){
			return{
				name:"一中",
				address:"广州"
			}
		},
		mounted(){
			this.pubId =pubsub.subscribe('hello',(a,b)=>{
				console.log("11111111111",a,b)
			})
		},
		beforeDestroy() {
			pubsub.unsubscribe(this.pubId)//取消订阅
		},
	}
</script>

//Student.vue
<template>
	<div>
		<h2>学生姓名:{{name}}</h2>
		<h2>学生年龄:{{sex}}</h2>
<button @click="sendStudentName">把数据传递给School组件</button>
	</div>
</template>

<script>
import pubsub from 'pubsub-js'
	 export default {
		name:'Student',
		data(){
			return{
				name:"张三",
				sex:"男"
			}
		},
	mounted(){
	},
	methods:{
		sendStudentName(){
		pubsub.publish('hello',this.name)
		}
	}
	}
</script>

在这里插入图片描述

13、this.$nextTick

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
<template>
    <button ref="tar" 
	    type="button" 
	    name="button" 
	    @click="testClick">{{content}}</button>
</template>
 
<script>
    export default {
        data () {
            return {
                content: '初始值'
            }
        }
     methods: {
       testClick(){
         this.content = '改变了的值'
         // 这时候直接打印的话,由于dom元素还没更新
         // 因此打印出来的还是未改变之前的值
         console.log(this.$refs.tar.innerText) // 初始值
       }
     }
    }
</script>

this.$nextTick这个方法作用是当数据被修改后使用这个方法会回调获取更新后的dom再渲染出来

methods:{
    testClick() {
        this.content = '改变了的值'
        let that = this
        this.$nextTick(() => {
            // dom元素更新后执行,因此这里能正确打印更改之后的值
            console.log(that.$refs.tar.innerText) // 改变了的值
        })
    }
}

14、插槽

作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于父组件 ===> 子组件

14.1、默认插槽

如果组件标签里没有插入内容的时候,不会显示
如果有插入内容,会显示
父组件中:

//App.vue
 <Category>
       <div>html结构1</div>
    </Category>

当使用的组件标签中插入内容时,插入的内容会被放到组件的slot标签中
子组件中:

//Category.vue
<template>
    <div>
       <!-- 定义插槽 -->
       <slot>我是默认内容</slot>
    </div>
</template>

像下面组件标签中加入了图片标签

//App.vue
<template>
  <div class="container">
	  <Category :listData="foods" title="foods">
		  <img src="https://z3.ax1x.com/2021/09/25/4rtPMV.png" alt="">
	  </Category>
  </div>
</template>

在这里插入图片描述
如果组件标签内没有没有放标签

//App.vue
<template>
  <div class="container">
	  <Category :listData="foods" title="foods">
	  </Category>
  </div>
</template>

组件标签中没有插入其他标签
在这里插入图片描述

14.2、具名插槽

具名插槽则是当子组件需要显示不同的效果时使用具名插槽,通过name属性给插槽命名。

父组件中:

写法一:

//App.vue
   <Category>
        <template slot="center">
          <div>html结构1</div>
        </template>
    </Category>

写法二:

//App.vue
   <Category>
        <template v-slot:footer>
           <div>html结构2</div>
        </template>
    </Category>

子组件中:

//Category.vue
   <template>
        <div>
           <!-- 定义插槽 -->
           <slot name="center">插槽默认内容...</slot>
           <slot name="footer">插槽默认内容...</slot>
        </div>
    </template>

具体代码

//Cate.vue
<template>
  <div class="cate">
      <h3>title</h3>
       <slot name="center">我是默认值1111</slot>
       <slot name="footer">我是默认值2222</slot>
         </div>
</template>

<script>
export default {
name:"Cate",
props:["listData","title"]
}
</script>

<style>
.cate{
    background-color:skyblue;
    width: 200px;
    height: 300px;
}
h3{
    text-align: center;
}

</style>
//App.vue
<template>
  <div class="container">
	  <Cate title="foods">
		<img slot="center" src="https://z3.ax1x.com/2021/09/25/4rtPMV.png" alt="">
		 <template v-slot:footer>
			 <h4>更多图片</h4>
		</template>
	  </Cate>
  </div>
</template>

<script>
import Cate from './components/Cate'
export default {
name:'App',
components:{
	Cate
},
data(){
	return{
		foods:["火锅","烧烤","炸鸡","泡面"],
		games:["超级玛丽","奥比岛","洛克王国","摩尔庄园"],
		films:["教父","峰暴","一天","巧克力工厂"],
		
	}
}
}
</script>

<style>
.container{
	display: flex;
	justify-content: space-around;
}
img{
	width: 100%;
}
</style>>

在这里插入图片描述
在这里插入图片描述

14.3、作用域插槽

理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
需要组件的使用者收到了数据,怎么使用取决于组件的使用者
(games数据在Cate组件中,但使用数据所遍历出来的结构由App组件决定)
作用域插槽语法,必须要使用template

 <template scope="scopeData">
			 {{scopeData}}
		 </template>
//App.vue
<template>
  <div class="container">
	 <Cate>
		 <template scope="scopeData">
			 {{scopeData}}
		 </template>
	 </Cate>
  </div>
</template>

<script>
import Cate from './components/Cate'
export default {
name:'App',
components:{
	Cate
}
}
</script>
//Cate.vue
<template>
  <div class="cate">
      <h3>{{title}}</h3>
		<slot :games="games">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
</div>
</template>

<script>
export default {
name:"Cate",
props:["title"],
data(){
    	return{
		games:["超级玛丽","奥比岛","洛克王国","摩尔庄园"],
	}
}
}
</script>
 <template scope="scopeData">
			 {{scopeData}}
		 </template>

这个收到是一个对象,就是下面图所示,我们需要的是.games
在这里插入图片描述

 <template scope="scopeData">
			 {{scopeData.games}}
 </template>

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值