侧边栏壁纸
博主头像
MicroMatrix博主等级

曲则全,枉则直,洼则盈,敝则新,少则得,多则惑。是以圣人抱一为天下式。不自见,故明;不自是,故彰;不自伐,故有功;不自矜,故长。夫唯不争,故天下莫能与之争。古之所谓“曲则全”者,岂虚言哉!诚全而归之。

  • 累计撰写 80 篇文章
  • 累计创建 21 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

记录一次vue2函数式组件开发+单粒模式

蜗牛
2022-05-20 / 0 评论 / 0 点赞 / 6 阅读 / 6265 字 / 正在检测是否收录...

前言

最近遇到个需求,需要一个类似于钉钉头部的菜单栏,因为钉钉的头部可以通过函数来渲染需要组件的样式,以及回调函数。所以,第一眼想到了,用单粒模式来模仿这个。那么我们就需要一个全局组件

组件编写过程

首先我们编写一个基础的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);
      });
    });
}
0

评论区