Vue正式篇

🍓Vue脚手架

🍒搭建Vue脚手架
  1. 下载安装node.jshttps://nodejs.org/en/download/
  2. 配置node.js环境变量
  3. cmd窗口输入node --version
  4. 配置npm淘宝镜像:npm config set registry https://registry.npm.taobao.org
  5. 按照官方文档执行以下命令https://cli.vuejs.org/zh/
    npm install -g @vue/cli
  6. 关掉cmd,重新打开cmd,输入命令vue,查看是否安装成功
  7. 在桌面创建一个Vue项目模板
    • 输入:vue create 项目名
    • Please pick a preset: (Use arrow keys)选择Vue2版本
    • 在项目的根目录下执行以下命令启动项目:npm run serve
    • 前往浏览器输入http://localhost:8080/,成功访问
🍒脚手架目录结构
	|—— node_modules
	|—— public
	|	|—— favicon.ico:页签图标(名字和路径不要随便修改)
	|	|—— index.html:主页面(名字和路径不要随便修改)
	|—— src
	|	|—— assets:存放静态资源
	|	|	|—— logo.png
	|	|—— component:存放组件
	|	|	|—— HelloWorld.vue
	|	|—— App.vue:汇总所有组件
	|	|—— main.js:入口文件(名字和路径不要随便修改)
	|—— .gitignore:git版本管制忽略的配置
	|—— babel.config.js:babel的配置文件
	|—— pakage.json:应用包配置文件
	|—— README.md:应用描述文件
	|—— package-lock.json:包版本控制文件
	|—— vue.config.js:vue脚手架配置文件(自己添加,名字固定,不能随便取)
🍒脚手架文件执行顺序
  1. 执行npm run serve
  2. 程序来到src/main.js文件(入口文件),将App组件放入容器
  3. 程序来到public/index.html
🍒入口文件main.js

涉及技术:

  • ES6模块化暴露方式:
    • 默认暴露 export default xxx(暴露一个的推荐使用)
      引入方式:import xxx from xxx
    • 分别暴露 export const xxx = xxx
      引入方式:import {xxx} from xxx
    • 统一暴露 export {xxx}
      引入方式:import {xxx} from xxx
  • render
    • render是一个有返回值的函数
    • render里的参数是一个函数
    • 通过render参数的函数来注册App组件,解析模板
    • 脚手架默认引入的是一个阉割版的/vue/dist/vue.runtime.esm.js,没有模板解析的功能,只能通过render属性来解决
//引入Vue
import Vue from 'vue'
//引入App组件,它是所有组件的父组件
import App from './App.vue'
//关闭vue生产提示
Vue.config.productionTip = false
//创建Vue实例对象
new Vue({
	el:'#app',
	//render简写
	//render: h => h(App),
	
	//render完整写法:
	render(createElement){
		return createElement(App)
	}
})
🍒Vue文件
<!--组件的模板,注意只有一个根标签,并且根标签上不能使用Vue指令-->
<template>
	
</template>


<script>
	//组件交互相关的代码
	export default {
		name:'School',//组件名字,一般与Vue文件的名字相同
		data(){		//组件管理的数据,必须是函数式的
			return {
				name:'vue大学',
				address:'北京'
			}
		}
	}
</script>

<!--
	设置组件的样式
	scoped:让样式在局部生效,防止组件之间的样式相互冲突,否则不加则是全局样式
-->
<style scoped>
	
</style>
🍒App.vue
<template>
	<div>
		<School></School>
		<hr>
		<Student></Student>
	</div>
</template>

<script>
	import School from './components/School.vue'		//import引入组件文件,需要再前面加上./
	import Student from './components/Student.vue'
	export default {
		name:'App',
		components:{		//注册组件
			School,
			Student
		}
	}
</script>

<style>
</style>

🍓Vue文件的命名规则

  1. 全小写schoolinfo
  2. 每个单词开头大写:SchoolInfo
  3. -衔接:school-info

🍓ref属性

  1. 注意:refvm特有的属性,不是指令,所以前面不需要加v-
  2. 用于给元素或者子组件注册引用信息
  3. 如果注册的是HTML标签,则返回真实DOM元素
  4. 如果注册的是VC组件,则返回的是组件的vc实例(这里可以参数实现子传父)

🌷🌷🌷

<template>
	<div>
		<h3 ref="title">{{title}}</h3>
		<School ref="school"></School>
		<button ref="btn" type="button" @click="showDOM">点我获取DOM元素</button>
	</div>
</template>

<script>
	import School from './components/School.vue'
	export default {
		name:'App',
		data(){
			return {
				title:'学校信息'
			}
		},
		methods:{
			showDOM(){
				console.log(this.$refs.title)
				console.log(this.$refs.school)
				console.log(this.$refs.btn)
			}
		},
		components:{
			School
		}
	}
</script>

打印信息:
在这里插入图片描述

🍓props属性

  • 作用:用于组件动态接收外部传递的参数(可以传对象、数组、函数等等)(父传子)

  • 传递参数:

    <School 
    	studentName="张三" 
    	:studentAge="23" 
    	schoolName="Vue大学" 
    	schoolAddress="北京">
    </School>
    

    注意:

    • 对于非字符串类型的参数,需要在参数前面加上:
    • 对于值是由Vue管理的变量,需要Vue去解析的,需要在参数前面加上:
  • 接收参数:

    • 第一种:没有任何限制
      props:['studentName','studentAge','schoolName','schoolAddress']
    • 第二种:限制参数类型
      props:{
      	studentName:String,
      	studentAge:Number,
      	schoolName:String,
      	schoolAddress:String
      }
      
    • 第三种:限制参数类型;参数是否必传;参数默认值
      props:{
      	studentName:{
      		type:String,
      		required:true		//是否必传,默认非必传
      	},
      	studentAge:{
      		type:Number,
      		default:18			//设置参数默认值
      	},
      	schoolName:{
      		type:String,
      		required:true
      	},
      	schoolAddress:{
      		type:String,
      		required:true
      	}
      }
      
  • 备注:

    • propsdata的执行顺序是:先执行props,再执行data,所以可以将props的值赋予给data
    • props的参数,也是会放到vc实例上,模板可以直接使用,但是props参数是只读,不能更改props参数的值,如果想要更改,可以将其赋予给data,再对data做操作
    • 对于引用类型的数据是可以更改值的,因为引用类型的值发生变化,但是引用类型指定的地址没有发生改变,所以是允许的(但不推荐这样做)
    • 参数名:参数名不能是vue的保留字,比如key、ref
    • 因为可以传递函数,所以如果想要实现子传父,父组件只要把函数传给子组件,子组件通过调用函数传递参数,实现子传父

🌷🌷🌷
Student.vue

<template>
	<div>
		<h3>学生姓名:{{showName}}</h3>
		<h3>学生年龄:{{showAge}}</h3>
	</div>
</template>

<script>
	export default {
		name:'Student',
		data(){
			return {
				showName:this.name,
				showAge:this.age
			}
		},
		props:{
			name:String,
			age:Number
		}
	}
</script>

School.vue

<template>
	<div>
		<h3>学校名称:{{name}}</h3>
		<h3>学校地址:{{address}}</h3>
		<hr>
		<Student :name="studentName" :age="studentAge"></Student>
	</div>
</template>

<script>
	import Student from './Student.vue'
	export default {
		name:'School',
		data(){
			return {
				name:this.schoolName,
				address:this.schoolAddress
			}
		},
		components:{
			Student
		},
		props:{
			studentName:String,
			studentAge:Number,
			schoolName:String,
			schoolAddress:String
		}
	}
</script>

App.vue

<template>
	<div>
		<h3 ref="title">{{title}}</h3>
		<School studentName="张三" :studentAge="23" schoolName="Vue大学" schoolAddress="北京"></School>
	</div>
</template>

<script>
	import School from './components/School.vue'
	export default {
		name:'App',
		data(){
			return {
				title:'学校信息'
			}
		},
		components:{
			School
		}
	}
</script>

三个组件的关系:

传参
传参
App.vue
School.vue
Student.vue

🍓组件自定义事件

  1. 绑定自定义事件:
    <School propBtn="none" v-on:自定义事件名称="回调函数"></School>
    <School propBtn="none" @自定义事件名称="回调函数"></School>
    this.$refs.实例名.$on('自定义事件名称',this.回调函数)			//这个绑定的回调函数this是App
    this.$refs.实例名.$once('自定义事件名称',this.回调函数)		//这个绑定的回调函数this是App,事件只触发一次
    this.$refs.实例名.$on('自定义事件名称',function(){})			//这个回调函数的this是绑定事件的组件实例
    this.$refs.实例名.$on('自定义事件名称',()=>{})				//这个回调箭头函数的this是App(箭头函数没有自己的this,往外找)
    
  2. 触发自定义绑定事件:
    this.$emit('自定义事件名称',事件参数...)
  3. 销毁自定义事件
    • 销毁该实例下所有绑定的自定义事件
      this.$off()
    • 销毁指定的一个自定义事件
      this.$off('自定义事件名称')
    • 销毁指定的多个自定义事件
      this.$off(['自定义事件名称1','自定义事件名称2'...])
    • 自定义事件与原始事件的区别:
      • vc或vm实例被销毁后,原始事件依然有效,自定义事件与实例一起被销毁了
      • 自定义事件只能绑定vc组件实例,原始事件如果想要绑定到vc组件实例上,需要加上事件修饰符native @click.native="回调函数"

🌷🌷🌷
School.vue

<template>
	<div>
		<h3>学校名称:{{name}}</h3>
		<h3>学校地址:{{address}}</h3>
		<button type="button" @click="sendSchoolNamebyProps(name)" :style="{display: propBtn}">通过props点我向App传输学校名称</button>
		<button type="button" @click="sendSchoolNamebycustomEvent(name)" :style="{display: customEventBtn}">通过自定义事件点我向App传输学校名称</button>
		<button type="button" @click="destroyCustomEvent">销毁当前School实例的自定义事件</button>
	</div>
</template>

<script>
	export default {
		name:location.href.slice(location.href.lastIndexOf('/')+1,location.href.lastIndexOf('.')),
		data(){
			return {
				name:'vue大学',
				address:'北京'
			}
		},
		props:['sendSchoolNamebyProps','propBtn','customEventBtn'],
		methods:{
			sendSchoolNamebycustomEvent(name){
				//触发事件
				this.$emit('sendMsg',name)
			},
			destroyCustomEvent(){
				//销毁自定义事件
				this.$off()
			}
		}
	}
</script>

App.vue

<template>
	<div>
		<h3 ref="title">{{title}}</h3>
		<h5>通过props属性向App传递参数</h5>
		<School :sendSchoolNamebyProps="sendSchoolNamebyProps" customEventBtn="none"></School>
		<hr>
		<h5>通过ref属性向App传递参数</h5>
		<School ref="school" propBtn="none" customEventBtn="none"></School>
		<hr>
		<h5>通过组件自定义绑定事件向App传递参数(全写)</h5>
		<School propBtn="none" v-on:sendMsg="sendSchoolNamebycustomEvent"></School>
		<hr>
		<h5>通过组件自定义绑定事件向App传递参数(简写)</h5>
		<School propBtn="none" @sendMsg="sendSchoolNamebycustomEvent"></School>
		<h5>通过组件自定义绑定事件向App传递参数(与ref属性结合使用,绑定事件更加灵活)</h5>
		<School ref="sch" propBtn="none"></School>
		<hr>
		<h5>通过组件自定义绑定事件向App传递参数(简写2)</h5>
		<School propBtn="none" @sendMsg="sendSchoolNamebycustomEvent"></School>
		<h5>通过组件自定义绑定事件向App传递参数(与ref属性结合使用,绑定事件更加灵活)</h5>
		<School ref="sch2" propBtn="none"></School>
		<hr>
		<h5>通过组件自定义绑定事件向App传递参数(简写3)</h5>
		<School propBtn="none" @sendMsg="sendSchoolNamebycustomEvent"></School>
		<h5>通过组件自定义绑定事件向App传递参数(与ref属性结合使用,绑定事件更加灵活)</h5>
		<School ref="sch3" propBtn="none"></School>
	</div>
</template>

<script>
	import School from './components/School.vue'
	export default {
		name:'App',
		data(){
			return {
				title:'学校信息'
			}
		},
		components:{
			School
		},
		methods:{
			sendSchoolNamebyProps(name){
				console.log('通过props传递的name:' + name)
			},
			sendSchoolNamebycustomEvent(name){
				console.log('通过自定义事件传递的name:' + name)
				console.log(this)
			}
		},
		mounted(){
			console.log('通过ref传递的name:' + this.$refs.school.name)
			//绑定回调函数
			setTimeout((item)=>{
				this.$refs.sch.$on('sendMsg',this.sendSchoolNamebycustomEvent)
			},3000)
			
			this.$refs.sch2.$on('sendMsg',function(){
				console.log('通过自定义事件传递的name:' + this.name)
				console.log(this)
			})
			
			this.$refs.sch3.$on('sendMsg',()=>{
				console.log('通过自定义事件传递的name:' + this.name)
				console.log(this)
			})
		}
	}
</script>

🍓全局事件总线

  • 作用:一般用于两个没有任何关系的组件之间进行通信,因为全局资源是比较紧张的,能不用就不要用,实在没办法就使用它
  • 步骤:
    • main.js里面定义全局事件总线
      beforeCreate(){
      	Vue.prototype.$bus = this
      }
      
    • 在需要接收数据的组件里给$bus绑定自定义事件和回调函数
    • 在发送数据的组件里触发$bus的事件
    • Vue实例即将被销毁之前解绑事件,节省资源

🌷🌷🌷
main.js(定义全局事件总线$bus

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

Vue.config.productionTip = false

new Vue({
	el:'#app',
	render:h => h(App),
	beforeCreate(){
		Vue.prototype.$bus = this
	},
})

App.vue(省略掉部分旁支末节,想要看全部可以去看源码)(定义全局事件)

export default {
	name:'App',
	methods:{
		//创建全局事件总线的回调函数
		addTodo(content){
			const newTodo = {
				id:'005',
				content,
				isCompleted :false
			}
			this.todoList.unshift(newTodo)
		},
		updateTodo(id){
			this.todoList.forEach((item)=>{
				if(item.id === id){
					item.isCompleted = !item.isCompleted
				}
			})
		},
		clearTodo(){
			if(confirm('是否清除已完成任务?')){
				this.todoList = this.todoList.filter((item)=>{
					return !item.isCompleted
				})
			}
		},
		deleteTodo(id){
			if(confirm('是否删除该任务?')){
				this.todoList = this.todoList.filter((item)=>{
					return item.id != id
				})
			}
		},
		allHandle(val){
			this.todoList.forEach((item)=>{item.isCompleted=val})
		}
	},
	//绑定全局事件
	mounted(){
		this.$bus.$on('addTodo',this.addTodo)
		this.$bus.$on('clearTodo',this.clearTodo)
		this.$bus.$on('allHandle',this.allHandle)
		this.$bus.$on('updateTodo',this.updateTodo)
		this.$bus.$on('deleteTodo',this.deleteTodo)
	},
	beforeDestroy(){
		//销毁全局事件总线
		this.$bus.$off(['addTodo','clearTodo','allHandle','updateTodo','deleteTodo'])
	}
}

TodoItem.vue(与App.vue没有任何关系,利用全局总线事件向App.vue传递参数id

export default {
	name:'TodoItem',
	methods:{
		updateTodo(id){
			this.$bus.$emit('updateTodo',id)
		},
		deleteTodo(id){
			this.$bus.$emit('deleteTodo',id)
		}
	}
}

🍓消息的订阅与发布

  • 作用:实现组件之间的通信
  • 步骤:
    • 下载依引入pubsub-js(第三方插件,与vue无关)
      npm i pubsub-js
    • 在需要接收消息的组件订阅消息(注意:订阅消息的回调函数的第一个参数是订阅名称,后面的参数就是你自己定义的回调函数)
      this.pid = pubsub.subscribe('订阅名称',回调函数)
    • 在发送消息的组件发布消息
      pubsub.publish('订阅名称',参数)

🌷🌷🌷
App.vue(订阅消息,创建回调函数,销毁消息)

import pubsub from 'pubsub-js'
export default {
	name:'App',
	methods:{
		//定义回调函数
		addTodo(msgName,content){
			const newTodo = {
				id:'005',
				content,
				isCompleted :false
			}
			this.todoList.unshift(newTodo)
		},
		updateTodo(msgName,id){
			this.todoList.forEach((item)=>{
				if(item.id === id){
					item.isCompleted = !item.isCompleted
				}
			})
		},
		clearTodo(msgName){
			if(confirm('是否清除已完成任务?')){
				this.todoList = this.todoList.filter((item)=>{
					return !item.isCompleted
				})
			}
		},
		deleteTodo(msgName,id){
			if(confirm('是否删除该任务?')){
				this.todoList = this.todoList.filter((item)=>{
					return item.id != id
				})
			}
		},
		allHandle(msgName,val){
			this.todoList.forEach((item)=>{item.isCompleted=val})
		}
	},
	mounted(){
		//订阅消息
		this.addTodoPid = pubsub.subscribe('addTodo',this.addTodo)
		this.clearTodoPid = pubsub.subscribe('clearTodo',this.clearTodo)
		this.allHandlePid = pubsub.subscribe('allHandle',this.allHandle)
		this.updateTodoPid = pubsub.subscribe('updateTodo',this.updateTodo)
		this.deleteTodoPid = pubsub.subscribe('deleteTodo',this.deleteTodo)
	},
	beforeDestroy(){
		//销毁
		pubsub.unsubscribe(this.addTodoPid)
		pubsub.unsubscribe(this.clearTodoPid)
		pubsub.unsubscribe(this.allHandlePid)
		pubsub.unsubscribe(this.updateTodoPid)
		pubsub.unsubscribe(this.deleteTodoPid)
	}
}

TodoItem.vue,(发布消息,注意: 回调函数只能传递一个参数)

import pubsub from 'pubsub-js'
export default {
	name:'TodoItem',
	data(){
		return {
			
		}
	},
	props:['todoItem'],
	methods:{
		updateTodo(id){
			//发布消息
			pubsub.publish('updateTodo',id)
		},
		deleteTodo(id){
			pubsub.publish('deleteTodo',id)
		}
	}
}

🍓组件之间的参数传递

🍒子传父
  1. props(需要父先向子传递一个函数)
  2. ref
  3. 组件自定义绑定事件
  4. 全局事件总线(不推荐,浪费资源)
  5. 消息的订阅与发布
🍒父传子
  1. props
  2. 全局事件总线(不推荐,浪费资源)
  3. 消息的订阅与发布
🍒两个无任何关系的组件
  1. 全局事件总线
  2. 消息的订阅与发布
  3. Vuex

🍓mixins属性

  • 作用:将组件中的一些公共部分提取出来写到一个js文件中复用
  • 使用方式:
    • 全局引入:Vue.mixin(xxx)
    • 局部引入:minins:[xxx](推荐)
  • 备注:
    如果引入mixins属性的公共配置有部分配置在组件中已经配置过了,则以组件配置的为主,如果是周期函数,则全部执行,并且minxins配置的周期函数先执行

🌷🌷🌷
公共部分js(定义公共方法和生命周期函数)

export const mixin = {	//需要使用export暴露这个对象
	methods:{
		showInfo(){
			alert(this.showName)
		}
	},
	mounted(){
		console.log('Hello world!')
	}
}

组件引入并使用这个公共部分js

import {mixin} from '../assets/mixin.js'	//引入
export default {
	name:Student,
	mixins:[mixin]	//使用
}	

🍓插件

  • 功能:用于增强Vue
  • 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的参数
  • 定义插件:对象.install = function(Vue,options){}
  • 使用插件:Vue.use(插件名,options)

🌷🌷🌷
涉及技术:Vue过滤器(可以前往本博主下的Vue入门去看)
定义插件

import dayjs from './dayjs.min.js'

export default {
	install(Vue){
		Vue.filter('nowTime',function(val,format='YYYY-MM-DD HH:mm:ss'){
			return dayjs(val).format(format)
		})
	}
} 

引入插件main.js

import Vue from 'vue'
import App from './App.vue'
import plugins from './assets/plugins.js'

Vue.config.productionTip = false

Vue.use(plugins)	//引入插件

new Vue({
	el:'#app',
	render:h => h(App)
})

使用插件

<template>
	<div>
		<h3>现在是:{{time | nowTime}}</h3>
	</div>
</template>

<script>
	export default {
		name:'School',
		data(){
			return {
				time:Date.now()
			}
		}
	}
</script>

🍓$set方法

作用:向Vue已经管理的对象中添加属性,需要使用this.$set(对象,'属性',值),否则不会产生数据代理,则不会引起页面的变化

🌷🌷🌷
添加性别属性

export default {
	name:'App',
	data(){
		return {
			student:{
				name:"张三",
				age:23
			}
		}
	},
	methods:{
		addSex(){
			this.$set(this.student,'sex','男')
		}
	}
}

🍓$nextTick方法

作用:对于需要对编译挂载后的DOM进行操作,比如input获取焦点,这部分代码需要放到vue属性$nextTick的回调函数中去,否则会因为代码执行顺序时机不对,而不生效

🌷🌷🌷
点击获得焦点的事件函数

editTodo(){
	this.$nextTick(function(){
		this.$refs.inputTodo.focus()
	})
}

🍓动画和过渡

🍒动画

步骤分析:

  1. 给需要做动画的元素加上条件渲染属性v-show
  2. 给需要做动画的元素外面包裹transition或者transition-group标签
  3. 定义一个css动画
  4. 绑定动画,设置动画执行效果
    css固定类名
    1. v[name]-enter-active:进入
    2. v[name]-leave-active:离开

🌷🌷🌷

<template>
	<div>
		<h2>动画</h2> <br>
		<button type="button" @click="isShow = !isShow">显示/隐藏</button>
		<!--step 2-->
		<transition appear name="hello">
			<!--step 1-->
			<h1 v-show="isShow">你好啊!</h1>
		</transition>
	</div>
</template>

<script>
	export default {
		name:'App',
		data(){
			return {
				isShow:true
			};
		}
	}
</script>

<style scoped>
	h1 {
		background-color: orange;
	}

	/**
	 * 进入
	 * step 4-1
	 */
	.hello-enter-active {
		animation: donghua 1s linear;
	}
	
	/**
	 * 离开
	 * step 4-2
	 */
	.hello-leave-active {
		animation: donghua 1s linear reverse;
	}
	
	/**
	 * step 3
	 */
	@keyframes donghua {
		from {
			transform: translateX(-100%);
		}
		to {
			transform:translateX(0)
		}
	}
</style>

transitiontransition-group的区别:

  1. transition下面只能有一个根元素
  2. transition-group下面可以有多个根元素,每个根元素必须有一个唯一的key
  3. 他们的属性:
    1. name:根据name的不同,css选择器也不同,以name-开头,没有namecss选择器以默认v-开头
    2. appear:是否需要初始化显示,默认不需要
    3. enter-active-class:进入动画类(第三方库使用)
    4. leave-active-class:离开动画类(第三方库使用)
🍒过渡

步骤分析:

  1. 给需要做动画的元素加上条件渲染属性v-show
  2. 给需要做动画的元素外面包裹transition或者transition-group标签
  3. 定义css过渡起点和终点
  4. 定义css过渡方式
    css固定类名
    1. v-enter:进入起点
    2. v-enter-to:进入终点
    3. v-leave:离开起点
    4. v-leave-to:离开终点

🌷🌷🌷

<template>
	<div>
		<h2>过渡</h2> <br>
		<!--step 2-->
		<transition-group appear name="hello2">
			<!--step 1-->
			<h1 v-show="isShow" :key="1">你好啊!</h1>
			<h1 v-show="isShow" :key="2">Vue大学!</h1>
		</transition-group>
	</div>
</template>

<script>
	export default {
		name:'App',
		data(){
			return {
				isShow:true
			};
		}
	}
</script>

<style scoped>
	h1 {
		background-color: orange;
	}
	
	/**
	 * 定义过渡效果
	 * step 4
	 */
	.hello2-enter-active,.hello2-leave-active {
		transition: 1s linear;
	}
	
	/**
	 * 过渡进入起点、过渡离开终点step 1
	 * step 3
	 */
	.hello2-enter , .hello2-leave-to{
		transform: translateX(-100%);
	}
	
	/**
	 * 过渡进入终点、过渡离开起点
	 * step 3
	 */
	.hello2-enter-to ,.hello2-leave{
		transform: translateX(0);
	}
</style>
🍒动画库animate.css

步骤分析:

  1. animate.css引入下载:npm install animate.css
  2. animate官网:https://animate.style/
  3. 步骤:
    1. 给需要做动画的元素加上条件渲染属性v-show
    2. 给需要做动画的元素外面包裹transition或者transition-group标签
    3. 引入动画库import 'animate.css'
    4. 设置transition属性:
      name="animate__animated animate__bounce" enter-active-class="animate__backInLeft" leave-active-class="animate__backOutRightnpm"
      

🌷🌷🌷

<template>
	<div>
		<h2>第三方动画库animate.css</h2> <br>
		<!--step 3-2,3-4-->
		<transition appear name="animate__animated animate__bounce" enter-active-class="animate__backInLeft" leave-active-class="animate__backOutRight">
			<!--step 3-1-->
			<h1 v-show="isShow" :key="1">你好啊!</h1>
		</transition>
	</div>
</template>

<script>
	//step 3-3
	import 'animate.css'
	export default {
		name:'App',
		data(){
			return {
				isShow:true
			};
		}
	}
</script>

🍓代理

跨域产生的原因:

违背了三个原则:同协议;同主机;同端口号
跨域的请求发出去了,服务器收到了请求,并且把数据发送到了浏览器上,但是浏览器发现你违背了三个原则,所以没有给你

解决跨域的办法:

  1. cors(后端人员解决,一劳永逸)
    由服务器在请求上加几个响应头
  2. jsonp(需要前后端一起协调才行)
  3. 代理服务器(前端)

配置代理方式一(简易):

  1. Vue脚手架的配置文件配置vue.config.js下配置
    js devServer:{ proxy:'http://后端服务器IP:后端服务器端口' }
  2. 发送请求时,填写代理服务器的ip和端口

不完美的地方:

  1. 代理服务器接收到请求后,先去本服务器上寻找资源,当本服务器没有时,才去被代理的后端服务器寻找资源(不能灵活的控制请求是否需要走代理)
  2. 一个前端项目只能配置一个代理

完美配置代理方式二:

devServer:{
	proxy:{
		'/代理前缀':{
			target:'http://后端服务器IP:后端服务器端口',
			pathRewrite:{'^/代理前缀':''},	//代理服务器请求真实服务器时将代理前缀去掉
			ws:true,//用于支持websocket	//默认true
			changeOrigin:true	//true:告诉服务器我的请求来自于他自己;false:告诉服务器我的请求真实所来自的地方,默认true,用于控制请求头中的host值
		}
	}
}

🍓插槽

步骤分析:

  1. 在需要插入的组件里加上<slot></slot>告诉Vue需要把东西插到哪里
  2. 在引入的组件标签写入你需要插入的东西

🌷🌷🌷
配置需要插槽的组件

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!--step 1-->
		<slot></slot>
	</div>
</template>

<script>
	export default {
		name:'Category',
		props:['title']
	}
</script>

向此插槽插入代码

<template>
	<div class="container">
	    <Category title="美食">
			<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
		</Category>
		<Category title="游戏">
			<ul>
				<li v-for="(itme,index) in games" :key="index">{{itme}}</li>
			</ul>
		</Category>
		<Category title="电影">
			<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
		</Category>
	</div>
</template>

<script>
	import Category from './components/Category.vue'
	export default {
		name:'App',
		data(){
			return {
				games:['王者荣耀','斗罗大陆','英雄联盟','剑侠情缘','部落冲突']
			}
		},
		components:{
			Category
		}
	}
</script>
🍒作用域插槽

作用域插槽:

  1. Appcategory传递结构,categoryApp传递数据
  2. category传递数据
    <slot :param1="val1" :param2="val2"></slot>
  3. App接收数据
    方式1:
    <template scope="param">
    	<div>{{param.val1}}</div>
    	<div>{{param.val2}}</div>
    </template>
    
    方式2:
    结构赋值:(此为ES6的结构赋值,不知道的童鞋可以去看下ES6
    <template scope="{param1}">
    	<div>{{param1}}</div>
    	<div>{{param}}</div>
    </template>
    

🌷🌷🌷
配置需要插槽的组件

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!--传递参数,可以传递多个-->
		<slot :games="games" :msg="msg"></slot>
	</div>
</template>

<script>
	export default {
		name:'Category',
		data(){
			return {
				games:['王者荣耀','斗罗大陆','英雄联盟','剑侠情缘','部落冲突'],
				msg:'海岛奇兵'
			}
		},
		props:['title'],
	}
</script>

向此插槽插入代码

<template>
	<div class="container">
		<Category title="游戏">
			<!--赋值方式1-->
			<template scope="games">
				<ul>
					<!--与结构赋值的区别:games.games-->
					<li v-for="(itme,index) in games.games" :key="index">{{itme}}</li>
				</ul>
			</template>
		</Category>
		<Category title="游戏">
			<!--赋值方式2:结构赋值-->
			<template scope="{games}">
				<ol>
					<li style="color: red;" v-for="(itme,index) in games" :key="index">{{itme}}</li>
				</ol>
			</template>
		</Category>
		<Category title="游戏">
			<!--赋值方式2:多个参数结构赋值-->
			<template scope="{games,msg}">
				<h4 v-for="(item,index) in games" :key="index">{{item}}</h4>
				<h4>{{msg}}</h4>
			</template>
		</Category>
	</div>
</template>

<script>
	import Category from './components/Category.vue'
	export default {
		name:'App',
		components:{
			Category
		}
	}
</script>

🍓Vuex

Vuex是专门在Vue中实现集中式状态(数据)管理的一个插件
Vuex作用:存放多个组件都要使用的数据,实现数据共享(读写)。
Vuex的工作原理:
在这里插入图片描述

  • Actions:·
    • vuex实例通过调用Dispatch访问Actions里面的函数。
    • Actions所管理的函数都包含一个参数content(上下文),函数里通过content调用commits访问Mutations里的函数。
    • 一般会在Actions里面发送Ajax请求,Actions负责与服务端进行交互。
    • Actions里面一般负责做一些业务逻辑判断。
  • Mutations
    • Mutations里面所管理的函数都包含一个参数state,是VuexState
    • Mutations负责加工State里面的数据,如果组件传递来的操作不需要与服务端进行交互,且没有任何复杂的业务逻辑,也可以直接用vuex实例通过commit跳过Actions直接调用Mutations函数。
  • State
    • 用于管理共享数据的,相当于Vue里的data,当State发生了变化,Vuex会帮你重新渲染使用到State数据的组件。
  • getters
    • 非必须要使用的配置项。getters所管理的函数,只有一个参数state,是一种相当于Vue里的计算属性的作用。

使用步骤:

  1. 下载Vuex插件
    npm i vuex
  2. 创建/src/store/index.js,写入以下代码
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
    	actions:{},
    	mutations:{},
    	state:{}
    })
    
    注意:必须创建store对象之前使用插件,即创建之前执行Vue.use(Vuex)
  3. main.js中引入store,并把store注入到Vue实例中去

🌷🌷🌷
index.js创建vuex实例store

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

Vue.use(Vuex)

export default new Vuex.Store({
	actions:{
		oddNUMADD(content,data){
			if(content.state.sum % 2){
				content.commit('addNum', data)
			}
		},
		rankADD(content,data){
			setTimeout(()=>{
				content.commit('addNum',data)
			},1000)
		}
	},
	mutations:{
		addNum(state,data){
			state.sum += data
		},
		cutNUM(state,data){
			state.sum -= data
		},
	},
	state:{
		sum:0
	},
	getters:{
		bigSUM(state){
			return state.sum * 10
		}
	}
})

main.js注入storevue

import Vue from 'vue'
import App from './App.vue'
import store from './store/index.js'

Vue.config.productionTip = false

const vm  =  new Vue({
	el:'#app',
	render:h => h(App),
	store,
	beforeCreate(){
		Vue.prototype.$bus = this
	}
})

Count.vue组件引用和修改共享属性sum

<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<h1>放大十倍的求和为:{{bigSUM}}</h1>
		<select v-model.number="selectNUM">
			<option value ="1">1</option>
			<option value ="2">2</option>
			<option value ="3">3</option>
		</select>
		<button type="button" @click="addNUM">+</button>
		<button type="button" @click="cutNUM">-</button>
		<button type="button" @click="oddNUMADD">当前求和为奇数再加</button>
		<button type="button" @click="rankADD">等一等再加</button>
	</div>
</template>

<script>
	import {mapState,mapGetters} from 'vuex'
	export default {
		name:'Count',
		data(){
			return {
				selectNUM:1
			}
		},
		methods:{
			addNUM(){
				this.$store.commit('addNum',this.selectNUM)
			},
			cutNUM(){
				this.$store.commit('cutNUM',this.selectNUM)
			},
			oddNUMADD(){
				this.$store.dispatch('oddNUMADD',this.selectNUM)
			},
			rankADD(){
				this.$store.dispatch('rankADD',this.selectNUM)
			},
		},
		computed:{
			sum(){
				return this.$store.state.sum
			},
			bigSUM(){
				return this.$store.getters.bigSUM
			}
		}
	}
</script>

Count.vue精简版
计算属性的优化:

  1. 使用mapState映射生成获取state里面的值的计算属性的代码
  2. 使用mapGetters映射生成获取getters里面的值的计算属性的代码
  3. 两种写法:对象写法和数组写法;数组写法要求对象写法的keyval相同

commitdispatch的优化:

  1. commit的优化:
    使用mapMutations优化
  2. dispatch的优化:
    使用mapActions优化
<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<h1>放大十倍的求和为:{{bigSUM}}</h1>
		<select v-model.number="selectNUM">
			<option value ="1">1</option>
			<option value ="2">2</option>
			<option value ="3">3</option>
		</select>
		<button type="button" @click="addNUM(selectNUM)">+</button>
		<button type="button" @click="cutNUM(selectNUM)">-</button>
		<button type="button" @click="oddNUMADD(selectNUM)">当前求和为奇数再加</button>
		<button type="button" @click="rankADD(selectNUM)">等一等再加</button>
	</div>
</template>

<script>
	import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
	export default {
		name:'Count',
		data(){
			return {
				selectNUM:1
			}
		},
		methods:{
			//写法一
			// addNUM(){
			// 	this.$store.commit('addNum',this.selectNUM)
			// },
			// cutNUM(){
			// 	this.$store.commit('cutNUM',this.selectNUM)
			// },
			
			//写法二(对象写法)
			//...mapMutations({addNUM:'addNUM',cutNUM:'cutNUM'}),
			
			//写法三(数组写法)
			...mapMutations(['addNUM','cutNUM']),
			
			//写法一
			// oddNUMADD(){
			// 	this.$store.dispatch('oddNUMADD',this.selectNUM)
			// },
			// rankADD(){
			// 	this.$store.dispatch('rankADD',this.selectNUM)
			// },
			
			//写法二(对象写法)
			//...mapActions({oddNUMADD:'oddNUMADD',rankADD:'rankADD'})
			
			//写法三(数组写法)
			...mapActions(['oddNUMADD','rankADD'])
		},
		computed:{
			//写法一
			// sum(){
			// 	return this.$store.state.sum
			// },
			// bigSUM(){
			// 	return this.$store.getters.bigSUM
			// }
			//写法二:对象写法
			// ...mapState({sum:'sum'}),
			// ...mapGetters({bigSUM:'bigSUM'})
			//写法三:数组写法
			...mapState(['sum']),
			...mapGetters(['bigSUM'])
		}
	}
</script>
🍒Vuex的模块化开发
  1. 将对应的模块数据拉取出来
    //注意:引入state是默认是当前模型自己局部的state,所以不需要加名称空间去指定,如果不是,则需要标注state是属于哪个名称空间的。
    const countModel = {
    	namespaced:true,		//这边必须写,否则名称空间不能识别
    	actions:{
    		
    	},
    	mutations:{
    		
    	},
    	state:{
    
    	},
    	getters:{
    				
    	}
    }
    
  2. 导入模块到vuex
    import Vue from 'vue'
    import Vuex from 'vuex'
    import countModel from './Count.js'
    
    Vue.use(Vuex)
    
    
    export default new Vuex.Store({
    	modules:{
    		countModel
    	}
    })
    
  3. 获取对应模块的数据
    1. 原始写法
      addNUM(){
      	this.$store.commit('countModel/addNum',this.selectNUM)
      }
      oddNUMADD(){
      	this.$store.dispatch('countModel/oddNUMADD',this.selectNUM)
      }
      sum(){
      	return this.$store.state.countModel.sum
      }
      bigSUM(){
      	return this.$store.getters['countModel/bigSUM']		
      }
      
    2. 对象
      ...mapMutations('countModel',{addNUM:'addNUM',cutNUM:'cutNUM'}),
      ...mapActions('countModel',{oddNUMADD:'oddNUMADD',rankADD:'rankADD'})
      ...mapState('countModel',{sum:'sum'}),
      ...mapGetters('countModel',{bigSUM:'bigSUM'})
      
    3. 数组
      ...mapMutations('countModel',['addNUM','cutNUM']),
      ...mapActions('countModel',['oddNUMADD','rankADD']),
      ...mapState('countModel',['sum']),
      ...mapGetters('countModel',['bigSUM'])
      

🍓路由

  1. 使用步骤:
    1. 下载vue-router
      npm i vue-router
    2. 应用插件:
      import VueRouter from 'vue-router'
      Vue.use(VueRouter)
      
    3. 编写router配置项
      export default new VueRouter({
      	routes:[
      		{
      			path:'/About',
      			component:About
      		}
      	]
      })
      
    4. 实现切换
      <router-link class="list-group-item" active-class="active" to="/About">About</router-link>
      <router-link class="list-group-item" active-class="active" to="/Home">Home</router-link>
      
    5. 指定显示位置
      <router-view></router-view>(最好与router-link写在同一个组件里面,这样对应关系不会乱套)
  2. 由路由管理的组件叫路由组件,放在/pages目录下,其他的组件叫一般组件,放在componenets目录下
  3. 被切换掉的组件会被默认销毁,切换过来的组件会重新挂载
  4. 每个路由组件都有自己的$route属性,里面存储着自己的路由信息
  5. 整个应用只有一个router,可以通过路由组件的$router属性获取到

🌷🌷🌷
main.js加载路由插件

import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import router from './router/index.js'

Vue.config.productionTip = false
Vue.use(VueRouter)

const vm  =  new Vue({
	el:'#app',
	render:h => h(App),
	router,
	beforeCreate(){
		Vue.prototype.$bus = this
	}
})

index.js配置路由规则

import VueRouter from 'vue-router'
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'

export default new VueRouter({
	routes:[
		{
			path:'/About',
			component:About
		},
		{
			path:'/Home',
			component:Home
		}
	]
})

Navigation.vue切换路由

<template>
	<div class="row">
		<div class="col-xs-2 col-xs-offset-2">
		  <div class="list-group">
			<!--路由切换-->
			<router-link class="list-group-item" active-class="active" to="/About">About</router-link>
			<router-link class="list-group-item" active-class="active" to="/Home">Home</router-link>
		  </div>
		</div>
		<!--切换后的页面展示-->
		<router-view></router-view>
	</div>
</template>

<script>
	export default {
		name:'Navigation',
	}
</script>
🍒路由嵌套

index.js配置多级路由

import VueRouter from 'vue-router'
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import Message from '../pages/home/Message.vue'
import News from '../pages/home/News.vue'

export default new VueRouter({
	routes:[
		{
			path:'/about',
			component:About
		},
		{
			path:'/home',
			component:Home,
			children:[		//多级路由
				{
					path:'message',		//注意:子级路由不需要加/
					component:Message
				},
				{
					path:'news',
					component:News
				}
			]
		}
	]
})

实现切换

<!--子级路由跳转需要带上父级路由的完整路径-->
<router-link class="list-group-item" active-class="active" to="/Home/News">News</router-link>

<router-link class="list-group-item" active-class="active" to="/Home/Message">Message</router-link>
🍒路由传参
  • query传参:
    1. 写法1(字符串写法)
      <router-link :to="`/home/message/detail?id=${item.id}&title=${item.title}`">{{item.title}}</router-link>
      
    2. 写法2(对象写法)
      <router-link :to="{
      						path:'/home/message/detail',
      						query:{
      							id:item.id,
      							title:item.title
      						}
      					}">
      {{item.title}}
      </router-link>
      
  • param传参:
    步骤:
    1. 在路由里面声名参数占位符:
      {
      	name:'messageDetail'
      	path:'detail',
      	component:Detail/:id/:title
      }
      
    2. 传参:
      字符串写法:
      <router-link :to="`/home/message/detail/${item.id}/${item.title}`">{{item.title}}</router-link>
      
      对象传参:
      <router-link :to="{
      						name:'messageDetail',		//注意:携带params参数是不允许用path的,只能用name
      						params:{
      							id:item.id,
      							title:item.title
      						}
      					}">
      {{item.title}}
      </router-link>
      
  • 接收参数:
    $route.query.id
    $route.query.title
    
  • 接收参数简写:
    1. 在路由中添加属性props
      //写法一
      {
      	path:'detail',
      	component:Detail,
      	props({query}){
      		return query
      	}
      }
      //写法二
      {
      	path:'detail',
      	component:Detail,
      	props({params}){
      		return params
      	}
      }
      
    2. 在目标组件Detail使用props接收对应的参数
      props:['id','title']
      
🍒路由命名

作用:用于解决路径相对复杂的路由简写形式
步骤:

  1. 在路由配置里面先命名:
    {
    	name:'homeMessage',
    	path:'message',
    	component:Message
    }
    
  2. 使用对象来标识路由
    //原先写法:
    to="/home/message"
    //使用路由名称写法:
    :to="{name:'homeMessage'}"
    
🍒路由replace和路由push

作用:控制浏览器的浏览记录;默认是追加pushreplace模式是替换,开启replace后,无法使用浏览器倒退键,倒退到上一个浏览记录

  • push模式:默认模式。可以使用浏览器倒退键,倒退到上一个浏览记录
  • replace模式:在router-link添加属性replace可以把push转为replace模式。不可以使用浏览器倒退键,倒退到上一个浏览记录
<router-link replace :to="`/home/message/detail?id=${item.id}&title=${item.title}`">{{item.title}}</router-link>
🍒编程式路由导航

编程式相比router-link模式来说要更加灵活,可以写一定的业务逻辑。

<template>
	<div>
	  <ul>
		<li v-for="item in message" :key="item.id">
			<!--router-link模式-->
			<router-link :to="`/home/message/detail?id=${item.id}&title=${item.title}`">{{item.title}}</router-link>&nbsp;&nbsp; 
			<!--编程式-->
			<button type="button" @click="push(item.id,item.title)">push</button>
			<button type="button" @click="replace(item.id,item.title)">replace</button>
		</li>
	  </ul>
	  <hr>
	  <router-view></router-view>
	</div>
</template>

<script>
	export default {
		name:'Message',
		data(){
			return {
				message:[
					{
						id:'001',
						title:'消息001'
					},
					{
						id:'002',
						title:'消息002'
					},
					{
						id:'003',
						title:'消息003'
					}
				]
			}
		},
		methods:{
			push(id,title){
				this.$router.push({
					path:'/home/message/detail',
					query:{
						id,
						title
					}
				})
			},
			replace(id,title){
				this.$router.replace({
					path:'/home/message/detail',
					query:{
						id,
						title
					}
				})
			}
		}
	}
</script>
🍒路由浏览记录前进后退
  • 后退:this.$router.back()
  • 前进:this.$router.forward()
  • 前进3格:this.$router.go(3)
  • 后退3格:this.$router.go(-3)
🍒缓存路由技术

作用:阻止组件在进行路由切换的时候被销毁,用于防止在页面上填写了某些form表单,在切换后被清理了。
实现方式:

<!--指定单个组件不能被销毁-->
<keep-alive include="组件Name">
	<router-view></router-view>
</keep-alive>

<!--指定多个组件不能被销毁-->
<keep-alive :include="['组件Name1','组件Name2']">
	<router-view></router-view>
</keep-alive>

<!--所有组件都不会被销毁-->
<keep-alive>
	<router-view></router-view>
</keep-alive>
🍒路由组件的生命周期

由路由管理的组件叫路由组件,路由组件有两个独有的生命周期函数

  • activated:组件被激活时调用
  • deactivated:组件销毁时调用
🍒meta属性

路由之中用来存放每个路由组件独有的信息,一般与下面的路由守卫配合使用。

🍒路由守卫
🍅全局路由守卫

全局路由守卫作为全局,写在路由配置文件index.js
全局路由守卫分前置路由守卫和后置路由守卫。

  • 前置路由守卫:跳转之前调用

    • 参数:
      • to:目标路由
      • from:当前路由
      • next:放行函数
    • 实现代码
      router.beforeEach((to,from,next)=>{
      	
      })
      
  • 后置路由守卫:跳转成功之后调用

    • 参数:
      • to:目标路由
      • from:当前路由
    • 实现代码
      router.afterEach((to,from)=>{
      			
      })
      

🌷🌷🌷
index.js配合meta属性设置跳转组件页面的标题和是否需要权限校验。

import VueRouter from 'vue-router'
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import Message from '../pages/home/Message.vue'
import News from '../pages/home/News.vue'
import Detail from '../pages/home/message/Detail.vue'

const router = new VueRouter({
	routes:[
		{
			path:'/about',
			component:About,
			meta:{
				title:'关于'
			}
		},
		{
			path:'/home',
			component:Home,
			children:[
				{
					name:'homeMessage',
					path:'message',
					component:Message,
					children:[
						{
							path:'detail',
							component:Detail,
							props({query}){
								return query
							}
						}
					],
					meta:{	//自定义信息
						isAuth:true,	//自定义一个属性是否需要鉴权
						title:'消息'
					}
				},
				{
					name:'homeNews',
					path:'news',
					component:News,
					meta:{	//自定义信息
						isAuth:true,
						title:'新闻'
					}
				}
			],
			meta:{
				title:'主页'
			}
		}
	]
})

//全局前置路由守卫
// to:目标路由
// from:当前路由
// next:放行函数
router.beforeEach((to,from,next)=>{
	if(to.meta.isAuth){
		if(localStorage.getItem('user') === 'admin'){
			//满足条件放行
			next()
		}else{
			alert('权限不足')
		}
	}else{
		//满足条件放行
		next()
	}
})

//后置路由守卫
// to:目标路由
// from:当前路由
router.afterEach((to,from)=>{
	document.title = to.meta.title || 'Vue路由守卫'
})


export default router
🍅独享路由守卫

独享路由守卫作为局部,写在路由配置文件index.js的路由配置规则routes中。
独享路由守卫没有独享的后置路由守卫,只有独享的前置路由守卫。
代码实现:

{
	name:'homeNews',
	path:'news',
	component:News,
	beforeEnter(to,from,next){	//独享路由守卫
		next()	//放行
	}
}
🍅组件内路由守卫

组件内路由守卫作为局部,写在路由组件之中。
代码实现:

//通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from,next){
	next()
},
//通过路由规则,离开该组件之前被调用
beforeRouteLeave(to,from,next){
	next()
}
🍒路由的工作模式

使用mode属性指定路由的工作模式:hashhistory

  • hash模式:(推荐)
    http://localhost:8080/#/home/message
    在地址栏后面加##后面的路径都是hash值,他们不会发送到浏览器,只在前端内部运行
  • history模式:
    http://localhost:8080/home/message
    每一步操作的路由路径都会直接拼在地址栏的后面,这样当你刷新的时候,浏览器就会根据这个路径去请求服务器,而服务器并没有这个路径的静态资源,那就会404
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值