前言
最近遇到个需求,需要一个类似于钉钉头部的菜单栏,因为钉钉的头部可以通过函数来渲染需要组件的样式,以及回调函数。所以,第一眼想到了,用单粒模式来模仿这个。那么我们就需要一个全局组件
组件编写过程
首先我们编写一个基础的vue组件,来作为基础的布局。
<template>
<div class="header-container-wrapper">
<plugin :render="renderLeftFunc" v-show="this.renderLeft"></plugin>
<div class="path-nav-name">
{{ navPath }}
</div>
<plugin :render="renderRightFunc" v-show="this.renderRight"></plugin>
</div>
</template>
<script>
import plugin from "./render.js";
export default {
components: {
plugin
},
computed: {
renderLeftFunc() {
return this.renderLeft || function() {};
},
renderRightFunc() {
return this.renderRight || function() {};
}
},
data() {
//header.js中data的数据定义一样,这里只是方便观察修改组件
return {
navPath: "",
showLeftContent: false,
showRightContent: false,
renderLeft: null,
renderRight: null,
pluginName: ""
};
}
};
</script>
<style scoped lang="scss">
.header-container-wrapper {
display: flex;
align-items: center;
justify-content: space-around;
height: 60px;
.left-container,
.right-container {
padding: 16px 24px;
height: 100%;
}
.path-nav-name {
font-weight: 700;
font-size: 22px;
color: #333333;
margin-left: auto;
flex: 1 1 auto;
text-align: center;
}
}
</style>
为了能在函数中使用render语法渲染组件,这里封装一个render.js来渲染组件
export default {
//vue2的render示例
functional: true,
name: "plugin",
props: {
render: Function,
},
render: (h, ctx) => {
return ctx.props.render(h)
}
}
这个时候我们通过vue.extend这个操作语句来生产一个组件对象。并且我们可以在这个对象上操作前面的基础组件。
import Vue from 'vue';
import Header from "./index.vue";
const Component = Vue.extend(Header);
let instance = null;
function HeaderBox (tarEl, options) {
if (!instance) {
instance = new Component({
el: document.createElement('div'),
data () {
return options;
}
});
//将头部元素插入到第一位
//返回生成的元素,然后把它挂载到需要挂载到dom元素上
tarEl.insertBefore(instance.$el, tarEl.firstChild)
} else {
// 存在实例,则合并options,更新视图
Object.assign(instance, options);
}
}
//添加返回按钮
HeaderBox.addLeftBtn = (renderObj, callback) => {
if (instance) {
instance.showLeftContent = true;
instance.renderLeft = () => {
let h = instance.$createElement;
return h(
"div",
{
class: "left-container",
on: {
click: callback
}
},
[
h("Icon", {
props: {
type: "ios-arrow-back"
},
style: "font-size:24px;"
}),
h(
"span",
{
style: "font-size:18px;"
},
"返回"
)
]
);
};
}
}
//移除返回按钮
HeaderBox.removeLeftBtn = () => {
if (instance) {
instance.renderLeft = null;
}
}
HeaderBox.addTopTitle = (title) => {
if (instance) {
instance.navPath = title;
}
}
HeaderBox.removeTopTitle = () => {
if (instance) {
instance.navPath = ""
}
}
export default HeaderBox;
上面的代码示例中,我们通过instance对象可以操控到基础组件里面的data定义的参数。通过操作data里面的参数,传递render
语法来控制渲染组件。其中h由于没有自动注入,所以直接使用时undefined,为了让他能正常工作,参考官网给的代码: const h = this.createElement
;我们通过instance
对象上的$createElement
来赋值h。
最后我们通过在main.js中全局注册
import HeaderBox from "@/components/header/header.js";
Vue.prototype.$HeaderBox = HeaderBox;
这样的话任何页面都能通过this.$HeaderBox.addTopTitle("测试表头");这样来生成自己的样式,也可以通过扩展header.js里面的HeaderBox对象的属性来扩充基础的组件。
使用案例如下代码
//vue2 中的操作
mounted(){
// 第一次使用初始化,就是你要挂载到哪里到节点下
this.$HeaderBox(this.$refs.rightHeader);
this.$nextTick(() => {
this.$HeaderBox.addLeftBtn({}, () => {
this.$router.go(-1);
});
});
}
评论区