vue2中如何动态渲染组件
- 动态渲染组件
- 代码解读
- 通过函数调用渲染组件
- 封装一个函数调用的二次确认弹窗
- 如何让外部知道用户点击了取消还是确定呢?
- 思考
- 小结
vue2 的项目中,main.js 文件中有一个挂载 App.vue
组件的方法:
new Vue({name: 'Root',render: h => h(App),
}).$mount('#app')
new Vue
创建了一个 Vue 实例,通过实例方法 $mount
方法将 App
组件挂载到 #app
上,这样就实现了App.vue 的渲染。
App.vue 渲染出来的模板会把
#app
替换掉。
App.vue 导出后是一个普通对象,没有
$mount
方法,而 Vue 实例有$mount
方法。
问题:我们可以通过这样的方式动态地渲染一个组件吗?
答案是可以的。 通过 Vue.extend
+ $mount
动态挂载组件。
动态渲染组件
既然可动态渲染一个组件,把这个过程封装成一个函数,可通过函数调用的方式渲染组件。
下面的代码,当用户点击按钮时,会动态渲染一个 el-button
组件。
<script>import {Button} from 'element-ui'import Vue from 'vue'export default {name: 'DemoOne',methods: {showElButton() {const SubVue = Vue.extend(Button)const ButtonInstance = new SubVue({propsData: {type: 'primary'},})ButtonInstance.$slots = {default: 'el-button',}ButtonInstance.$mount(this.$refs.container)},},}
</script>
<template><div><h2>动态挂载组件</h2><button @click="showElButton">显示el-button</button><div><div ref="container"></div></div></div>
</template>
代码解读
Vue.extend 是什么作用?
Vue.extend
接收一个组件对象,用于创建一个 Vue 的子类。 Vue.extend
返回的是一个 Vue 的子类,而不是一个 Vue 的实例。
new SubVue 是什么作用?
new SubVue
创建了一个 Button
组件的实例 ButtonInstance
,方便调用实例方法。
ButtonInstance. m o u n t ( t h i s . mount(this. mount(this.refs.container) 是什么作用?
将 ButtonInstance
挂载到 this.$refs.container
上,即将 el-button
渲染到页面上。
通过 Vue.extend
和 $mount
方法,实现了动态地渲染组件。
了解了动态渲染组件的原理,我们可以通过函数调用的方式,动态地渲染组件。
通过函数调用渲染组件
上面的例子中 showElButton
方法局限了在组件内部,我们可以将这个方法抽离出来,挂载到 Vue 原型上,就可以在全局调用。
类似这样调用: this.$showElButton(params)
// showElButton.js
import {Button
} from 'element-ui'
import Vue from 'vue'export function showElButton(target, {type = 'primary',label = 'el-button'
} = {}) {const SubVue = Vue.extend(Button)const ButtonInstance = new SubVue({propsData: {type},})ButtonInstance.$slots = {default: label,}ButtonInstance.$mount()target.appendChild(ButtonInstance.$el)
}
代码解读
没有给 $mount
方法传递挂载的目标元素,而是在挂载目标元素中追加了 ButtonInstance.$el
,实现每点击一次渲染一个。
在 main.js 中挂载方法:
Vue.prototype.$showElButton = showElButton
使用 this.$showElButton
方法:
<script>export default {name: 'DemoOne',methods: {showElButton() {this.$showElButton(this.$refs.container)},},}
</script><template><div><h2>动态挂载组件</h2><button @click="showElButton">显示el-button</button><div><div ref="container"></div></div></div>
</template>
效果
封装一个函数调用的二次确认弹窗
了解了如何通过函数调用渲染组件,可以封装一个二次确认弹窗, 用户确认后再进行下一步操作。
弹窗组件如下: ConfirmModal.vue
<template><div v-if="show" class="modal"><div ref="myModalContent" class="modal-content"><div>{{ title }}</div><slot name="default"><div>{{ content }}</div></slot><slot name="footer"><div class="footer"><button @click="onCancel">取消</button><button @click="onConfirm">确定</button></div></slot></div></div>
</template><script>export default {name: 'ConfirmModal',props: {title: {type: String,default: '确定操作吗?',},content: {type: String,default: '',},},data() {return {show: false,}},methods: {onCancel() {this.show = false},onConfirm() {this.show = false},},}
</script><style lang="less">.modal {position: fixed;left: 0;top: 0;z-index: 99; // 确保弹窗在顶层width: 100%;height: 100%;background-color: #e4e4e4c7;&-content {position: relative;left: 50%;top: 50%;width: 20vw;height: 100px;z-index: 999; //transform: translate(-50%, -50%);display: flex;flex-direction: column;justify-content: space-between;// align-items: flex-end;background-color: #fff;.footer {display: flex;justify-content: flex-end;}}}
</style>
showConfirm
:
import Confirm from './ConfirmModal.vue'
import Vue from 'vue'const ConfirmClass = Vue.extend(Confirm)let confirmInstance = nullconst showConfirm = (content = '插槽内容', title = '弹窗标题', options = {}) => {if (!confirmInstance) {confirmInstance = new ConfirmClass({el: document.createElement('div'),propsData: {content: content,title,},})}// NOTE 设置组件 data 为 true,内部 div 渲染confirmInstance.show = true// console.log(confirmInstance)document.body.appendChild(confirmInstance.$el)return confirmInstance
}export default showConfirm
关键代码解读
// 设置 confirmInstance 的 data 为 true,内部 div 渲染
confirmInstance.show = true
// 将 confirmInstance.$el 挂载到 body 上
document.body.appendChild(confirmInstance.$el)
在 main.js 中挂载方法:
import showConfirm from './showConfirm'
Vue.prototype.$showConfirm = showConfirm
使用 this.$showConfirm
方法:
<template><div><el-button type="danger" @click="onRemoveSomething">删除</el-button></div>
</template><script>export default {name: 'Home',data() {return {}},methods: {onRemoveSomething() {this.$showConfirm('删除后不能恢复', '确定删除吗?')},},}
</script>
效果:
如何让外部知道用户点击了取消还是确定呢?
还有一个关键问题,如何让外部知道用户点击了取消还是确定呢?
方法1:通过回调函数
给 showConfirm
函数传递一个回调函数,当用户点击确定时,执行回调函数。
改造 ConfirmModal.vue,内部的方法不要写死,而是在 showConfirm
函数中提供。
// ConfirmModal.vue 内部不提供 onCancel 和 onConfirm 方法
methods: {// onCancel() {// console.log(this.$options)// this.show = false// return false// },// onConfirm() {// return true// },
}
// showConfirm 提供 onCancel 和 onConfirm 方法
const showConfirm = (content = '插槽内容',title = '弹窗标题',options = {}
) => {if (!confirmInstance) {confirmInstance = new ConfirmClass({el: document.createElement('div'),propsData: {content: content,title,},})}// 设置组件 data 为 true,内部 div 渲染confirmInstance.show = true// onConfirm 和 onCancel 方法confirmInstance.onConfirm = () => {options.onConfirm()confirmInstance.show = false}confirmInstance.onCancel = () => {options.onCancel()confirmInstance.show = false}console.log(confirmInstance)document.body.appendChild(confirmInstance.$el)return confirmInstance
}
使用 showConfirm
函数:
this.$showConfirm('删除后不能恢复', '确定删除吗?', {onConfirm: () => {console.log('确定')},onCancel: () => {console.log('取消')},
})
方法2: showConfirm 返回一个 Promise
先看使用方式:
this.$showConfirm('删除后不能恢复', '确定删除吗?').then(isOk => {if (isOk) {console.log('确定')} else {console.log('取消')}
})
showConfirm 函数:
const showConfirm = (content = '插槽内容', title = '弹窗标题', options = {}) => {if (!confirmInstance) {confirmInstance = new ConfirmClass({el: document.createElement('div'),propsData: {content: content,title,},})}// 设置组件 data 为 true,内部 div 渲染confirmInstance.show = true// 返回一个 Promiseconst p = new Promise((resolve, reject) => {confirmInstance.onConfirm = () => {confirmInstance.show = falseresolve(true)}confirmInstance.onCancel = () => {confirmInstance.show = falseresolve(false)}})document.body.appendChild(confirmInstance.$el)return p
}
相比回调函数,Promise 更加直观,使用更加方便。
思考
element-ui 的 this.$message
方法,是如何实现调用后在页面上显示消息的?
小结
- 了解 new Vue 的作用;
- 结合 Vue.extend 和 $mount 方法,实现动态渲染组件;
- 通过函数调用的方式,动态渲染组件;
- 封装一个二次确认弹窗,用户确认后再进行下一步操作;