文章目录
一、$emit 和 $on
实例:$emit 和 $on
$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>
1.$on
通过断点调试分析
- $on传入两个参数,绑定的事件名称和事件方法。
- 源码中第一步创建获取vue实例:
var vm = this
- 第二步 判断事件名称是否是数组
- 是,使用迭代的方式继续调用$on
- 否,
vm._events[event]
判空,为空则赋予它一个空数组,然后把需要绑定的事件方法push进去
- 最后将处理后的vue实例返回
通过第二步可知,可以多个事件绑定一个执行方法,也可以一个事件同时绑定多个执行方法
2.$emit
通过断点调试分析
- $emit传入一个参数,绑定事件方法。
- 源码中第一步创建获取vue实例:
var vm = this
- 第二步,将事件名称转为小写赋予变量lowerCaseEvent:
var lowerCaseEvent = event.toLowerCase()
- 第三步:做一些检查(略)
- 第四步:从
vm._events[event]
中拿出事件方法,判空 - 第五步:是多个事件方法就转数组
- 。。。
通过分析可知,$emit中出现error并不会中断执行只是使用try-catch抛出异常
二、directive 用法
实例:directive 用法
<html>
<head>
<title>directive 用法</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<div v-loading="isLoading">{{data}}</div>
<button @click="update">更新</button>
</div>
<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>
</body>
</html>
- update的参数:
- el:需要进行loading操作的DOM对象
- binding:更新状态(是否在更新)
- vnode:转化后的虚拟DOM对象
- 若直接传入function:如下,会直接把指令添加到bind和update生命周期当中
- Vue.directive(‘loading’, {update(…) {}})
- Vue.directive(‘loading’, function update(…) {})
需要改造的地方:
else {
const div = document.getElementById('loading')
div && document.body.removeChild(div)
}
三、Vue.component 用法
<html>
<head>
<title>Vue.component 用法</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<Test :msg="message"></Test>
</div>
<script>
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>
</html>
源码分析
- 定义组件需要传入组件名称和一个匿名对象
- vue.js:5216:
ASSET_TYPES.forEach(function (type) {
- type为component
- vue.js:5216:
Vue[type] = function (id,definition) {
- id为组件名称;definition为传入的匿名对象
- 使用正则表达式检验名称合法性
- 接下来判断类型的同时使用isPlainObject函数判断指定参数是否是一个纯粹的对象
- vue.js:69:
function isPlainObject (obj) {return _toString.call(obj) === '[object Object]'}
- vue.js:59:
var _toString = Object.prototype.toString;
- vue.js:69:
- vue.js:5230:
definition = this.options._base.extend(definition);
- vue.js:5132:
Vue.extend = function (extendOptions) {
- vue.js:5146:
var Sub = function VueComponent (options) {this._init(options);};
- 构造方法进行组件实例化
四、Vue.extend 用法
<html>
<head>
<title>Vue.extend 用法</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<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'
}
},
name: 'Test'
})
Vue.component('Test')
new Vue({
el: '#root',
data() {
return {
message: "Test Extend Component"
}
}
})
</script>
</body>
</html>
主要用来生成组件的构造函数VueComponent
- vue.js:5132:
Vue.extend = function (extendOptions) {
- vue.js:5146:
var Sub = function VueComponent (options) {this._init(options);};
- 构造方法进行组件实例化
其实本实例作用和上一个相同
五、Vue.extend 进阶用法
<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>
给vue实例添加自定义api,实现$loading
:
- 使用
Vue.prototype.$loading = Loading
绑定方法 - 将字符串
'正在加载,请稍等...'
作为参数msg
传入Loading
方法中 - 将
Vue.extend
的返回值赋值给变量LoadingComponent
const div = document.createElement('div')
创建divdiv.setAttribute('id', 'loading-wrapper')
起名为loading-wrapperdocument.body.append(div)
进行挂载new LoadingComponent().$mount('#loading-wrapper')
实例化一个LoadingComponent
对象,并挂载到前面创建的div上(直接覆盖,因此id必须相同,否则后面移除的时候找不到)
const hide = this.$loading('正在加载,请稍等...')
,将this.$loading
的返回值() => {document.body.removeChild(document.getElementById('loading-wrapper'))
赋值给变量hide
,并在赋值过程中执行this.$loading
(this.$loading
执行过程中return的内容不执行)hide()
,延时执行完毕,执行匿名方法() => {document.body.removeChild(document.getElementById('loading-wrapper'))
(this.$loading
执行过程中return的内容),移除挂在的div- ``
将一个方法赋值给一个变量的过程中,方法会执行一次,变量获取到的是方法的返回值,而方法的返回值是一个匿名方法(箭头函数),因此变量名即这个匿名函数的方法名,所以这个变量可做方法来用
六、Vue.use 用法
实例: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
进行加载
传送门:Vue.use实现原理 ;Vue.use源码分析
b=null;
var a=(b||(b=[]));
console.log(a);