本文目录:
- 1.
$emit
和$on
- 2.Vue.directive
- 3.Vue-extend进阶用法:自定义API
- 4.Vue.use:挂载插件
- 5.provide和inject
- 6.过滤器filter
- 7.监听器 watch的6种用法
- 8.class 和 style 绑定的高级用法
- 9.Vue.observable
1.$emit
和$on
以下代码, 点击按钮后, 控制台上会打印什么?
<html>
<head>
<title>$emit 和 $on</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<button @click="boost">触发事件</button>
</div>
<script>
new Vue({
el: '#root',
data() {
return {
message: 'hello vue'
}
},
created() {
this.$on('my_events', this.handleEvents)
},
methods: {
handleEvents(e) {
console.log(this.message, e)
},
boost() {
this.$emit('my_events', 'my params')
}
}
})
</script>
</body>
</html>
输出结果是hello vue my params
原理分析:
通过$on
成功将自定义事件myEvent挂载到vue实例上,并且指定了ABC为自定义事件触发的回调函数。
this.$on
('自定义事件的名字myEvent',回调函数ABC)
通过$emit
触发自定义事件ABC,并且将'my params'作为自定义事件的实参传递出去。
this $emit
('自定义事件ABC',传递出去的实参)
好处:
Vue通过上面的操作,将事件定义和事件触发解耦,提升了灵活性。
2.Vue.directive
除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令。
有的情况下,仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
<div id="root">
<div v-loading="isLoading">{{data}}</div>
<button @click="update">更新</button>
</div>
第一个参数loading和元素中的v-loading中的loading必须是一致的
<script>
Vue.directive('loading', {
update(el, binding, vnode) {
if (binding.value) {
const div = document.createElement('div')
div.innerText = '加载中...'
div.setAttribute('id', 'loading')
div.style.position = 'absolute'
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 {
document.body.removeChild(document.getElementById('loading'))
}
}
})
new Vue({
el: '#root',
data() {
return {
isLoading: false,
data: ''
}
},
methods: {
update() {
this.isLoading = true
setTimeout(() => {
this.data = '用户数据'
this.isLoading = false
}, 3000)
}
}
})
</script>
当点击按钮的时候,页面呈现以下状态:
3秒后,loading消失,按钮上方会多出来“用户数据”四个字。
自定义指令中传递的三个参数
el: 指令所绑定的元素,可以用来直接操作DOM。
binding: 一个对象,包含指令的多个属性信息。
vnode: Vue编译生成的虚拟节点。
除了 el 之外,其它参数都应该是只读的,切勿进行修改。
如果需要在钩子之间(自定义指令的生命周期)共享数据,建议通过元素的 dataset 来进行。
自定义指令的生命周期
自定义指令有五个生命周期(也叫钩子函数),分别是 bind, inserted, update, componentUpdated, unbind
- bind:只调用一次,指令第一次绑定到元素时调用
- inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)
- update:被绑定于元素所在的模板更新时调用,
- componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
- unbind:只调用一次,指令与元素解绑时调用。
3.Vue-extend进阶用法:自定义API
<html>
<head>
<title>Vue.extend 用法2</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: #fff;
}
</style>
</head>
<body>
<div id="root">
<button @click="showLoading">显示Loading</button>
</div>
<script>
function Loading(msg) {
const LoadingComponent = Vue.extend({
template: '<div id="loading-wrapper">{{msg}}</div>',
props: {
msg: {
type: String,
default: msg
}
},
name: 'LoadingComponent'
})
const div = document.createElement('div')
div.setAttribute('id', 'loading-wrapper')
document.body.append(div)
new LoadingComponent().$mount('#loading-wrapper')
return () => {
document.body.removeChild(document.getElementById('loading-wrapper'))
}
}
Vue.prototype.$loading = Loading
new Vue({
el: '#root',
methods: {
showLoading() {
const hide = this.$loading('正在加载,请稍等...')
setTimeout(() => {
hide()
}, 2000)
}
}
})
</script>
</body>
</html>
点击按钮,出现lodaing图,2秒后loading自动消失
关键点
1.自定义一个组件LoadingComponent,并且实现这个租金啊,并将其通过$mount
挂载到vue上(loading
自定义指令的方法:通过改变某个状态值来实现元素的变动
自定义api的方法:完全主动触发,并且能够很方便的封装为插件,功能更强大,更为推荐
4.Vue.use:挂载插件
我们将loading封装为一个插件,并通过vue.use加载
<html>
<head>
<title>Vue.use 用法</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: #fff;
}
</style>
</head>
<body>
<div id="root">
<button @click="showLoading">显示Loading</button>
</div>
<script>
const loadingPlugin = {
install: 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()
}, 2000)
}
}
})
</script>
</body>
</html>
在开发中经常用到Vue.use(VueRouter)、Vue.use(MintUI)。但是用 axios时,就不需要用 Vue.use(axios),这是因为开发者在封装 axios 时,没有写 install 这一步。
install 是默认的方法。当外界在 use 这个组件的时候,就会调用本身的 install 方法,同时传一个 Vue 这个类的参数。
5.provide和inject
已经有了props和vuex,为什么还需要provide?
props跨组件通信很麻烦,vuex学习成本高
<html>
<head>
<title>组件通信 provide 和 inject</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<Test></Test>
</div>
<script>
function registerPlugin() {
Vue.component('Test', {
template: '<div>{{message}}<Test2 /></div>',
provide() {
return {
elTest: this
}
}, // function 的用途是为了获取运行时环境,否则 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>
</html>
上面的代码Test1通过provide把自身映射为elTest,经过传递,Test3页可以直接通过inject接收到elTest
6.过滤器filter
<html>
<head>
<title>过滤器 filter</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<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>
</html>
通过过滤器lower可以很方便的去改变数据。
上面演示的是组件过滤器的用法,另外还可以定义全局过滤器
Vue.filter('globalFilter',function(value){
return value + '111'
))
7.监听器 watch的6种用法
6种方法的DOM元素都是一样的
<div id="root1">
<h3>Watch 用法5:绑定多个 handler</h3>
<input v-model="message">
<span>{{copyMessage}}</span>
</div>
id值root对应的是root1~root6
root1.监听message属性,当message发生变化的话,触发逻辑代码
root2.message对应的字符串,vue会去methods中寻找对应的方法
root3.deepMessage后面跟的是一个对象,handler对应的是methods中的方法,deep为true代表深度监听
root4:immediate属性代表页面加载完成后就立刻执行一次监听事件
root5:messag后面跟的是一个数组,数组中可以依次绑定多个事件
root6:通过'deepMessage.a.b'来深度监听某个特定属性,而不是全部
<script>
new Vue({
el: '#root',
watch: {
message(value) {
this.copyMessage = value
}
},
data() {
return {
message: 'Hello Vue',
copyMessage: ''
}
}
})
new Vue({
el: '#root2',
watch: {
message: 'handleMessage'
},
data() {
return {
message: 'Hello Vue',
copyMessage: ''
}
},
methods: {
handleMessage(value) {
this.copyMessage = value
}
}
})
new Vue({
el: '#root3',
watch: {
deepMessage: {
handler: 'handleDeepMessage',
deep: true
}
},
data() {
return {
deepMessage: {
a: {
b: 'Deep Message'
}
},
copyMessage: ''
}
},
methods: {
handleDeepMessage(value) {
this.copyMessage = value.a.b
}
}
})
new Vue({
el: '#root4',
watch: {
message: {
handler: 'handleMessage',
immediate: true,
}
},
data() {
return {
message: 'Hello Vue',
copyMessage: ''
}
},
methods: {
handleMessage(value) {
this.copyMessage = value
}
}
}),
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 + '*'
}
}
})
new Vue({
el: '#root6',
watch: {
'deepMessage.a.b': 'handleMessage'
},
data() {
return {
deepMessage: { a: { b: 'Hello Vue' } },
copyMessage: ''
}
},
methods: {
handleMessage(value) {
this.copyMessage = value
}
}
})
</script>
</body>
</html>
8.class 和 style 绑定的高级用法
<html>
<head>
<title>class 和 style 绑定的高级用法</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<div :class="['active', 'normal']">数组绑定多个class</div>
<div :class="[{active: isActive}, 'normal']">数组包含对象绑定class</div>
<div :class="[showWarning(), 'normal']">数组包含方法绑定class</div>
<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 {
isActive: true,
warning: {
color: 'orange'
},
bold: {
fontWeight: 'bold'
}
}
},
methods: {
showWarning() {
return 'warning'
},
mix() {
return {
...this.bold,
fontSize: 20
}
}
}
})
</script>
</body>
</html>
9.Vue.observable
随着组件的细化,很多时候都需要处理组件状态共享的情况, Vuex可以轻松解决这类问题,不过就像 Vuex官方文档所说的,如果应用不够大,为避免代码繁琐冗余,最好不要使用它,接下来介绍的是 vue.js 2.6 新增加的 Observable API ,通过使用这个 api 我们可以应对一些简单的跨组件数据状态共享的情况。
observable()方法,用于设置监控属性,这样就可以监控viewModule中的属性值的变化,从而就可以动态的改变某个元素中的值,监控属性则通过返回一个函数给viewModule对象中的属性,从而来监控该属性。返回的对象可以直接用于渲染函数methods和计算属性computed内,并且会在发生改变时触发相应的更新。
创建store.js文件
import Vue from 'vue'
export const state = Vue.observable({
screenCars: {},
})
export const mutations = {
updateScreenCars (payload) {
state.screenCars = Object.assign({}, payload)
},
}
index.vue组件中触发:
<template>
<div>
<el-button @click="toSave">保存</el-button>
</div>
</template>
<script>
import {state, mutations} from './store.js'
export default {
name: 'table_form',
computed: {
screenCars() {
return state.screenCars
},
},
methods: {
setTest: mutations.updateScreenCars ,
toSave () {
this.setTest({a:1})
},
},
}
</script>