1. $on 和 $emit
对事件的定义和消费进行解耦
<div id="test">
<button @click="boost">触发事件</button>
</div>
<script>
new Vue({
el: '#test',
created() {
this.$on('my_events', this.handleEvent1) //定义事件,并交由指定方法处理
this.$on('my_events', this.handleEvent2)
},
methods: {
handleEvent1(param) {
console.log('my_event1 事件被处理了' + param)
},
handleEvent2(param) {
console.log('my_event2 事件被处理了' + param)
},
boost() {
this.$emit('my_events', '参数') //消费事件
}
}
});
</script>
子组件到父组件通讯
<hello @pfn="parentFn"></hello>
<script>
Vue.component('hello', {
template: '<button @click="fn">按钮</button>',
methods: {
// 子组件:通过$emit调用
fn() {
this.$emit('pfn', '这是子组件传递给父组件的数据')
}
}
})
new Vue({
methods: {
// 父组件:提供方法
parentFn(data) {
console.log('父组件:', data)
}
}
})
</script>
非父子组件通讯
var bus = new Vue()
// 在组件 B 绑定自定义事件
bus.$on('id-selected', function (id) {
// ...
})
// 触发组件 A 中的事件
bus.$emit('id-selected', 1)
//可以传事件数组
this.$on(['my_events1', 'my_events2'], this.handleEvent1);
console.log(this._events);
2. Vue.directive
自定义指令
<body>
<div id="test" v-loading="isLoading"> <!--使用v-loading指令-->
<div>{{data}}</div>
<button @click="update">更新</button>
</div>
<script>
//自定义v-loading指令,可生成通用 loading 插件,实现无侵入式的 loading
Vue.directive('loading', function update(el, binding, vnode) {
//el 指令绑定的元素,可以直接操作dom;
//binding是一个对象,binding.value是指令绑定的值
console.log(el, binding, vnode)
if (binding.value) {
const div = document.createElement('div')
div.innerText = '加载中...'
div.setAttribute('id', 'loading')
div.style.position = 'fixed'
div.style.left = 0
div.style.top = 0
div.style.width = '100%'
div.style.height = '100%'
div.style.display = 'flex'
div.style.justifyContent = 'center'
div.style.alignItems = 'center'
div.style.color = 'white'
div.style.background = 'rgba(0,0,0,.7)'
document.body.append(div)
} else {
const div = document.getElementById('loading')
div && document.body.removeChild(div)
}
})
new Vue({
el: '#test',
data() {
return {
isLoading: false,
data: ''
}
},
methods: {
update() {
this.isLoading = true
setTimeout(() => {
this.data = '用户数据'
this.isLoading = false
}, 3000)
}
}
})
</script>
</body>
3. Vue.component
自定义组件
<body>
<div id="root">
<Test :msg="message"></Test>
</div>
<script>
//自定义 Test 组件
Vue.component('Test', {
template: '<div>{{msg}}</div>',
props: {
msg: {
type: String,
default: 'default message'
}
}
})
new Vue({
el: '#root',
data() {
return {
message: 'Test Component'
}
}
})
</script>
</body>
4. Vue.extend
生成组件的构造函数
<body>
<div id="root">
<Test :msg="message"></Test>
</div>
<script>
const component = Vue.extend({
template: '<div>{{msg}}</div>',
props: {
msg: {
type: String,
default: 'default message'
}
}
})
Vue.component('Test', component)
new Vue({
el: '#root',
data() {
return {
message: 'Test Component'
}
}
})
</script>
</body>
组件挂载,给 vue 添加了一个新的 api
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
#loading-wrapper {
position: fixed;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .7);
color: #ffffff;
}
</style>
</head>
<body>
<div id="root">
<button @click="showLoading">显示Loading</button>
</div>
<script>
const LoadingComponent = Vue.extend({
template: '<div id="loading-wrapper">{{msg}}</div>',
props: {
msg: {
type: String,
default: 'loading...'
}
}
}, 'LoadingComponent')
function Loading(msg) {
const div = document.createElement('div')
div.setAttribute('id', 'loading-wrapper')
document.body.append(div)
//实例化一个LoadingComponent对象,挂载到loading-wrapper上,覆盖上面代码创建的div
new LoadingComponent({
props: {
msg: {
type: String,
default: msg
}
}
}).$mount('#loading-wrapper')
//返回一个移除loading的方法
return () => {
document.body.removeChild(document.getElementById('loading-wrapper'))
}
}
Vue.prototype.$loading = Loading
//使用
new Vue({
el: '#root',
methods: {
showLoading() {
const hide = this.$loading('正在加载,请稍等...')
setTimeout(() => {
hide()
}, 3000)
}
}
})
</script>
</body>
</html>
5. Vue.use
对比上述的组件的挂载,使用的方式变为 Vue.use(插件名)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
#loading-wrapper {
position: fixed;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .7);
color: #ffffff;
}
</style>
</head>
<body>
<div id="root">
<button @click="showLoading">显示Loading</button>
</div>
<script>
const loadingPlugin = function(vm) {
const LoadingComponent = vm.extend({
template: '<div id="loading-wrapper">{{msg}}</div>',
props: {
msg: {
type: String,
default: 'loading...'
}
}
}, 'LoadingComponent')
function Loading(msg) {
const div = document.createElement('div')
div.setAttribute('id', 'loading-wrapper')
document.body.append(div)
new LoadingComponent({
props: {
msg: {
type: String,
default: msg
}
}
}).$mount('#loading-wrapper')
return () => {
document.body.removeChild(document.getElementById('loading-wrapper'))
}
}
vm.prototype.$loading = Loading
}
//使用
Vue.use(loadingPlugin)
new Vue({
el: '#root',
methods: {
showLoading() {
const hide = this.$loading('正在加载中,请稍后...')
setTimeout(() => {
hide()
}, 3000)
}
}
})
</script>
</body>
</html>
6. provide 和 inject
实现跨组件通信的方案
<body>
<div id="root">
<Test></Test>
</div>
<script>
function registerPlugin() {
Vue.component('Test', {
template: '<div>{{message}}<Test2 /></div>',
provide() {
return {
elTest: this
}
},//fuction 的用途是为了获取运行时的环境,否则 this 将指向 window
data() {
return {
message: 'message from Test'
}
},
methods: {
change(component) {
this.message = 'message from' + component
}
}
})
Vue.component('Test2', {
template: '<Test3 />'
})
Vue.component('Test3', {
template: '<button @click="changeMessage">change</button>',
inject: ['elTest'],
methods: {
changeMessage() {
this.elTest.change(this.$options._componentTag)
}
}
})
}
Vue.use(registerPlugin)
new Vue({
el:'#root'
})
</script>
</body>
7. filters
{{过滤内容 | 过滤条件}}
<body>
<div id="root">
{{message | lower}}
</div>
<script>
new Vue({
el: '#root',
filters: {
lower(value) {
return value.toLowerCase()
}
},
data() {
return {
message: 'Hello Vue'
}
}
})
</script>
</body>
8. watch
8.1 常见使用
<div id="root">
<input v-model="message">
<span>{{copyMessage}}</span>
</div>
<script>
new Vue({
el: '#root',
watch: {
message(value) {
this.copyMessage = value
}
},
data() {
return {
message: '',
copyMessage: ''
}
}
})
</script>
8.2 绑定方法
<div id="root2">
<input v-model="message">
<span>{{copyMessage}}</span>
</div>
<script>
new Vue({
el: '#root2',
watch: {
message: 'handleMessage' //传入方法名
},
data() {
return {
message: 'hello vue',
copyMessage: ''
}
},
methods: {
handleMessage(value) {
this.copyMessage = value
}
}
})
</script>
8.3 deep + handler
<div id="root3">
<input v-model="deepMessage.a.b">
<span>{{copyMessage}}</span>
</div>
<script>
new Vue({
el: '#root3',
watch: {
deepMessage: {
handler: 'handleDeepMessage', //对象中,方法名传给 handler
deep: true
}
},
data() {
return {
deepMessage: {
a: {
b: 'Deep Message'
}
},
copyMessage: ''
}
},
methods: {
handleDeepMessage(value) {
this.copyMessage = value.a.b
}
}
})
</script>
8.4 immediate
一加载完就监听默认值
<div id="root4">
<input v-model="message">
<span>{{copyMessage}}</span>
</div>
<script>
new Vue({
el: '#root4',
watch: {
message: {
handler: 'handleMessage',
immediate: true
}
},
data() {
return {
message: 'hello vue',
copyMessage: ''
}
},
methods: {
handleMessage(value) {
this.copyMessage = value
}
}
})
</script>
8.5 绑定多个 handler
<div id="root5">
<input v-model="message">
<span>{{copyMessage}}</span>
</div>
<script>
new Vue({
el: '#root5',
watch: {
message: [{
handler: 'handleMessage' //第一次处理
}, 'handleMessage2', //第二次处理
function(value) { //第三次处理
this.copyMessage = this.copyMessage + '...'
}]
},
data() {
return {
message: 'hello vue',
copyMessage: ''
}
},
methods: {
handleMessage(value) {
this.copyMessage = value
},
handleMessage2(value) {
this.copyMessage = this.copyMessage + '*'
}
}
})
</script>
8.6 监听对象属性
比 deep 节约性能
<div id="root6">
<input v-model="deepMessage.a.b">
<span>{{copyMessage}}</span>
</div>
<script>
new Vue({
el: '#root6',
watch: {
'deepMessage.a.b':'handleDeepMessage'
},
data() {
return {
deepMessage: {
a: {
b: 'Deep Message'
}
},
copyMessage: ''
}
},
methods: {
handleDeepMessage(value) {
this.copyMessage = value
}
}
})
</script>
9 class 和 style 绑定的高级用法
9.1 class
<div id="root">
<div :class="['active','normal']">数组绑定多个 class</div>
<div :class="[{active:isActive},'normal']">数组包含对象绑定 class</div>
<div :class="[showWarning(),'normal']">数组包含方法绑定 class</div>
</div>
<script>
new Vue({
el: '#root',
data() {
return {
isActive: true,
}
},
methods: {
showWarning() {
return 'warning'
}
}
})
</script>
9.2 style
<div id="root">
<div :style="[warning,bold]">数组绑定多个 style</div>
<div :style="[warning,mix()]">数组包含方法绑定 style</div>
<!--优先执行最后一个值,浏览器不兼容向左依次适配-->
<div :style="{display:['-webkit-box','-ms-flexbox','flex']}">style多重值</div>
</div>
<script>
new Vue({
el: '#root',
data() {
return {
warning: {
color: 'orange'
},
bold: {
fontWeight: 'bold'
}
}
},
methods: {
mix() {
return {
...this.bold,
fontSize: 'x-large'
}
}
}
})
</script>
10 Vue.observable
<div id="root">
{{message}}
<button @click="change">Change</button>
</div>
<script>
const state = Vue.observable({ message: 'Vue 2.6' })
const mutation = {
setMessage(value) {
state.message = value
}
}
new Vue({
el: '#root',
computed: {
message() {
return state.message
}
},
methods:{
change(){
mutation.setMessage('Vue 3.0')
}
}
})
</script>
11 v-solt
只能用在 component 和
<template>上,简写#
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<div>案例1:slot 的基本用法</div>
<Test>
<template v-slot:header="{user}">
<div>自定义header({{user.a}})</div>
</template>
<template v-slot="{user}">
<div>自定义body({{user.b}})</div>
</template>
</Test>
</div>
<div id="root2">
<div>案例2:Vue2.6新特性 - 动态slot</div>
<Test>
<template v-slot:[section]="{section}">
<div>this is {{section}}</div>
</template>
</Test>
<button @click="change">switch header and body</button>
</div>
<script>
Vue.component('Test', {
template:
'<div>' +
'<slot name="header" :user="obj" :section="\'header\'">' +
'<div>默认header</div>' +
'</slot>' +
'<slot :user="obj" :section="\'body\'">默认body</slot>' +
'</div>'
})
new Vue({
el: '#root',
data() {
return {
obj: { a: 1, b: 2 }
}
},
methods: {
change() {
return ''
}
}
})
new Vue({
el: '#root2',
data() {
return {
section: 'header'
}
},
methods: {
change() {
this.section === 'header' ?
this.section = 'default' :
this.section = 'header'
}
}
})
</script>
</body>
</html>