最近在开发vue项目,其中一些组件采用自研的方式开发,在这里对于一些通用性较强的组件进行一些总结,本文记录一下vue-menu菜单组件的开发过程和npm包发布过程。

一、组件的开发

1、需求分析

组件需要的功能:

(1)点击或者鼠标hover某一元素后弹出下拉菜单;

(2)下拉菜单项具备点击功能;

(3)下拉项可以置灰,点击无效;

(4)可以为下拉菜单添加自定义的类;

2、创建项目:

vue init webpack vue-menu

设置项目配置,完成后再src文件夹下创建vmenu文件夹,包含一个index.js文件和一包含main.vue的src文件夹。

构建项目目录如图:

其中main.vue为下拉菜单的源代码文件,用来组织菜单结构,index.js作为组件的入口文件。

2.1 main.vue文件

main.vue文件中以ul标签和li标签的形式展示用户传入的list数据,并绑定li的点击事件,具体代码实现如下

main.vue模板:

<template>
    <div id='v-menu-wrap-div' class='v-menu-wrap' :style='{left:rect.left +"px",top:rect.top + rect.height + "px"}' >
        <ul ref='v-menu-ul' class='vmenu-operate-ul' :class='ulClass'>
	    <li v-for='liItem in list' @click='clickLiBtn(liItem)' :class='[liItem.disabled ===true ? "disabled":"",liItem.class]'>
		<span v-if='liItem.icon' class='v-menu-span-icon operate-icon' :class='liItem.icon'></span>
		<span>{{liItem.text}}</span>
	    </li>
	</ul>
    </div>
</template>

main.vue的script标签:

<script>
	export default{
		name:'vMenu',
		methods:{
			clickLiBtn(li){
				if(li.disabled){    //如果有disabled属性,则返回
					return;
				}else if(li.clickFun){
					li.clickFun(li);
				}
			}
		},
		data: function(){
			return {
			}
		},

	}
</script>

修改样式:

<style >
	div.v-menu-wrap{
		position:absolute;
		z-index:9999;
	}
	ul.vmenu-operate-ul{
		list-style: none;
		color:black;
		float: left;
    	min-width: 160px;
   	 	padding: 5px 0;
   	 	margin: 2px 0 0;
    	font-size: 14px;
    	text-align: left;
    	background-color: #fff;
		box-shadow: 0 6px 12px rgba(0,0,0,.175);
		border: 1px solid rgba(0,0,0,.15);
			
	}
	ul.vmenu-operate-ul li{
		line-height:20px;
		display: list-item;
		padding:3px 20px;
		cursor:pointer;
	}
	ul.vmenu-operate-ul li:hover{
		color: #fff;
		background-color: #39ace5;
	}
	ul.vmenu-operate-ul li.disabled,ul.vmenu-operate-ul li.disabled:hover{
		background-color:white;
		cursor:default;
	}
	ul.vmenu-operate-ul li.disabled span,ul.vmenu-operate-ul li.disabled:hover span{
		color: rgb(167, 167, 167);
	}
</style>

2.2 index.js入口文件

入口文件需包含一个导出对象,对象有一个install方法,在使用Vue.use('xxx')时,会调用该install方法,方法的第一个参数为Vue实例:

import vMenu from './src/main';

vMenu.install = function(Vue){

};

export default vMenu;

补充实例方法:

/**
 * @file :vue的menu菜单实现,通过实例$menu(options)调用
 * @Author: panjianfeng
 * @e-mail: jianfeng418@sina.com
 * @createDate:2018-11-29
 */
import vMenu from './main/main.vue';

const install = function(Vue){
	Vue.prototype.$menu = function(event,options){
		var rect = {};
		['top','left'].forEach(function(property){
			var scroll = property === 'top' ? 'scrollTop' :'scrollLeft' ;
			rect[property] = event.currentTarget.getBoundingClientRect()[property] + document.body[scroll] + document.documentElement[scroll];
			

		});
		rect['left'] = rect['left'] + 160 > document.body.offsetWidth ? document.body.offsetWidth-160 : rect['left'];
		['height','width'].forEach(function(property){
			rect[property] = event.currentTarget.getBoundingClientRect()[property];
		});
		if(options){
			options.rect = rect;
			options.ulClass = options.ulClass ? options.ulClass :'';
		}else{
			let node = document.querySelector('#v-menu-wrap-div');
			if(node && node.parentNode){
				node.parentNode.removeChild(node);
			};
			return ;
		}
		var vmenu = Vue.extend(vMenu);
		var componentMenu = new vmenu({
			data:options
		}).$mount();
		let node = document.querySelector('#v-menu-wrap-div');
		if(node){
			node.parentNode.removeChild(node);
		}

		Vue.nextTick(function(){
			document.body.appendChild(componentMenu.$el);
		});
		const removeNode = function(){
			let node = document.querySelector('#v-menu-wrap-div');
			if(node){
				node.parentNode.removeChild(node);
				document.onclick = null;
				document.removeEventListener('click',removeNode,true);
			}
		}
		
		document.addEventListener('click',removeNode,true);
		
		
	};
};

export default install;

2.3 测试

<template>
  <div class="hello">
    <h3>{{ msg }}</h3>
    <span @click='showMenu'>点我下拉菜单</span>
  </div>
</template>

<script>
......
methods:{
    showMenu(event){
      this.$menu(event,{
        list:[{
          icon:'',
          text:'vue',
          clickFun:this.showAlert,
        },{
          icon:'',
          text:'javascript',
          clickFun:this.showAlert,
        },{
          icon:'',
          text:'html',
          disabled:true,
          clickFun:this.showAlert,
        },{
          icon:'',
          text:'css',
          disabled:false,
          clickFun:this.showAlert,
        }],

        ulClass:'menuclass'
      })
    },
    showAlert(item){
      alert(item.text)
    }
</script>

效果如下:

至此,组件开发工作完成。

github完整代码:

GitHub - jianfeng418/vue-menu: menu width vue

二、npm包发布

1、 通过webpack-simple新建项目,

vue init webpack-simple vue-menu

通过webpack-simple创建一个简易的项目,用于对源代码进行压缩,方便上传,使用。

1.1 将vue-menu的源代码拷贝到src文件夹下:

 1.2 修改webpack.config.js:

注意:该例子中将文件输出到dist文件夹下,而项目的.gitignore文件默认把dist/文件夹忽略,因此,修改.gitignore文件,删除/dist/行:

1.3 npm run build

运行该命令,构建生产项目后,查看dist文件夹,产生两个文件

1.4 修改package.json文件

name:修改为唯一的名称,和现有npm库不能冲突。该名称即为npm包的名称,用户通过该名称下载组件使用。

private:改为false;

main:用户引用组件时,系统通过该字段去寻找文件,即用户实际引用的文件,这里改成构建后的压缩文件;

1.5 登录npm账户

npm login

如果没有npm账号,先去npm官网注册自己的用户账号;

输入用户名、密码、邮箱后,显示登录成功的信息;

可通过npm whoami 命令查看当前npm用户:

1.6 发布npm

通过npm publish命令发布自己的包;注意包的名称必须是现有npm库不存在的名称;否则会发布失败;

登录npm官网,登录自己账号可以看到已经发布的npm包;

1.7 验证

下载刚刚上传的包:

npm install vue-menu-jf -S 

在new Vue前引入vMenu,在项目中引用:

import vMenu from 'vue-menu-jf'

Vue.use(vMenu);

验证成功,完成。

1.8 npm包升级

在升级已经发布的npm包时,需要更改package.json中的version字段,同样使用npm publish 命令发布。

Logo

前往低代码交流专区

更多推荐