现在前端工程师的要求愈来愈高了,须要掌握的技术点愈来愈多了,会一些基本的前端技能彻底适应不了快速变化的前端领域了。接下来我将从零实现一个本身的UI组件库并发布到npm上,提供给须要的朋友参考也总结下本身对封装组件的理解方便之后复习。html
本项目以button
按钮为例,详细的记录一下封装一个Button UI组件每个步骤以及须要注意的地方:前端
检查 node 环境配置
先本地全局安装node环境,vue的运行是依赖于node
的npm
的管理工具来实现的,node下载地址。下载好node以后,打开cmd管理工具,输入node -v
,回车,查看node版本号,出现版本号则说明安装成功,注意:node 的版本要在 8.9 或更高版本 (推荐 8.11.0+)vue
node -v npm -v
Vue 版本
若是你已经全局安装了旧版本的 vue-cli (1.x 或 2.x),你须要先经过 npm uninstall vue-cli -g 或 yarn global remove vue-cli 卸载它, Vue CLI 的包名称由 vue-cli 改为了 @vue/cli。node
npm install -g @vue/cli # OR yarn global add @vue/cli
vue --version | vue -V
建立项目
vue create mc-ui OR vue ui 也可使用UI图形化界面建立项目
注意:因为咱们是开发一个第三方依赖库,咱们选择 Manually select features。webpack
(*) Babel ( ) TypeScript ( ) Progressive Web App (PWA) Support ( ) Router ( ) Vuex (*) CSS Pre-processors (*) Linter / Formatter ( ) Unit Testing ( ) E2E Testing
系统默认的包含了基本的 Babel + ESLint 设置的 preset,咱们只须要选择CSS配置。移动键盘上下键选择须要的特性,按下键盘空格键便可选中git
Sass/SCSS (with dart-sass) Sass/SCSS (with node-sass) Less Stylus
因为Element UI中的样式采用Sass,因此咱们选择第一项便可
为何不选择第二项呢?
由于dart-sass比node-sass更好下载github
ESLint with error prevention only ESLint + Airbnb config ESLint + Standard config ESLint + Prettier
因我的喜爱选择便可,我比较喜欢第三种web
(*) Lint on save ( ) Lint and fix on commit
选择Ctrl+S保存时检测代码格式便可面试
In dedicated config files In package.json
因我的喜爱,我比较喜欢选择第二种vue-cli
Save this as a preset for future projects? (y/N)
看项目须要,我这里选择 N。回车后,系统会自动帮咱们把选择的配置集成到模板中,而后生成一个完整的项目。
咱们大体按照Element UI的 源码 目录进行咱们本身的UI库项目开发。因此删除系统自动为咱们建立的src、assets等目录,在根目录中建立一个packages目录用来存放咱们要开发的UI组件;在根目录建立一个test目录用于测试咱们本身开发的UI组件。
因为咱们更改了原项目的目录结构,使得系统本地运行以及打包找不到对应的目录,咱们须要在项目的根目录中建立一个vue.config.js文件夹手动的去修改webpack配置,使得系统本地运行和打包正常。
// vue.config.js const path = require('path'); module.exports = { pages: { index: { entry: 'test/main.js', template: 'public/index.html', filename: 'index.html' } }, chainWebpack: config => { config.module .rule('js') .include.add(path.resolve(__dirname, 'packages')).end() .use('babel') .loader('babel-loader') .tap(options => { return options; }) } }
<mc-botton class="mc-bottom"></mc-botton>
的实现<template>
<template> <button class="mc-button"> <span> <slot></slot> // slot表示插槽 </span> </button> </template>
注意:为何要在slot插槽外面加个span标签呢?为了美观,每一个自定义button组件间相互有点间距🤭
<script>
export default { name: 'McButton' }
注意:组件中填写name属性,是为了封装组件提供install方法时,动态获取每一个组件名进行组件注册
<style>
.mc-button { box-sizing: border-box; outline: none; margin: 0; transition: 0.1s; font-weight: 500; -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; .... }
注意:user-select属于CSS3的属性,值为none表示禁止用户选中文字
<mc-botton type="xxx" class="mc-bottom"></mc-botton>
的实现常见的类型有:primary / success / warning / danger / info / text
<template>
<button class="mc-button" :class="[ `mc-button--${type}` ]"> ... </button>
注意:为何绑定class时采用数组形式,而不用对象形式,由于会有多个动态绑定的属性,且使用对象的形式,这样就只有数组里能够放多个动态属性,且属性能够为对象
<script>
props: { type: { type: String, default: 'default' } }
注意:这里接收属性为何不用数组的形式?由于咱们是封装组件给别人用的,因此要限制一些条件,不能让用户随意的输入
<style>
该功能时,样式没有改动
<mc-botton plain class="mc-bottom"></mc-botton>
的实现<template>
<button class="mc-button" :class="[ `mc-button--${type}`, { 'is-plain': plain } ]"> ... </button>
注意:为何绑定class时采用数组形式,而不用对象形式,由于会有多个动态绑定的属性,且使用对象的形式,这样就只有数组里能够放多个动态属性,且属性能够为对象
<script>
plain: { type: Boolean, default: false }
<style>
&.is-plain:hover, &.is-plain:focus { background: #fff; border-color: #409eff; color: #409eff; }
<mc-botton round class="mc-bottom"></mc-botton>
的实现<template>
<button class="mc-button" :class="[ `mc-button--${type}`, { ... 'is-round': round } ]"> ... </button>
<script>
round: { type: Boolean, default: false }
<style>
&.is-round { border-radius: 20px; padding: 12px 23px; }
<mc-botton class="mc-bottom is-circle"></mc-botton>
的实现<template>
<button class="mc-button" :class="[ `mc-button--${type}`, { ... 'is-circle': circle } ]"> ... </button>
<script>
circle: { type: Boolean, default: false }
<style>
&.is-circle { border-radius: 50%; padding: 12px; }
<mc-botton class="mc-bottom" disabled></mc-botton>
的实现<template>
<button ... :disabled="disabled" :class="[ `mc-button--${type}`, { ... 'is-disabled': disabled } ]"> ... </button>
<script>
disabled: { type: Boolean, default: false }
注意:添加是否被禁用时有两个做用:第1、让用户不能点击;第2、改变按钮样式
<mc-botton icon="el-icon-check" class="mc-bottom"></mc-botton>
的实现<template>
<button ... > <i :class="icon" v-if="icon"></i> <!-- 若是没有传入插槽的时候才显示 --> <span v-if="$slots.default"><slot></slot></span> </button>
<script>
icon: { type: String, default: '' }
<style>
.mc-button [class*=mc-icon-]+span { margin-left: 5px; }
i标签上加v-if的目的:让用户便可以上传图标也能够上传文字或者上传文字和图标
注意:既传图标也传文字的话,图标和文字隔的很近,咱们须要特殊处理下; 可是若是特性处理的话,单独的图标会不居中显示;因此咱们要使用$slots获取包含有插槽的才让其显示处理
<mc-botton class="mc-bottom" @click="handleClick"></mc-botton>
的实现<template>
<button ... @click="handleClick" > ... </button>
<script>
methods: { handleClick (e) { this.$emit('click', e) // 向父组件派发一个click事件,e表示携带的参数 } }
在Vue-cli3的 官方文档 中有个构建目标
有明确的说明怎么打包成一个应用或者一个库!此时,咱们须要在package.json中添加一条打包命令
vue-cli-service build --target lib 指定打包的文件
而后控制台执行yarn lib
便可将咱们的组件库包括字体图标一块儿打包生成一个dist文件夹
将代码上传到github
首先登陆github 官网 建立一个新的仓库,而后复制新仓库的git地址。 而后在咱们本地建立的项目根目录执行git init,初始化git,而后再终端执行如下命令:
git remote add origin git add . git commit -am ":rocket: project init" git push -u origin master
你们有没有发现我提交的commit不同,其实就是用到了gitmoji这个依赖而已,简单说下他的用法:
将代码发布到npm
因为咱们开发的组件库是给别人用的,咱们没有必要把全部的代码都发布到npm上。因此咱们须要在项目的根目录建立一个.npmignore的文件,忽略那些文件上传
// .npmignore # 忽略目录 test/ packages/ public/ # 忽略指定文件 vue.config.js babel.config.js *.map .editorconfig.js
注意:因为咱们要上传到npm上,因此咱们本地npm的源要使用npm的源,不能使用淘宝源或其余的。
查看一下本地电脑全部的源:
一切准备工做作好后,打开终端,执行npm login进行登陆
执行npm login命令,系统会提示输入帐户和密码。若是没有npm帐户,请注册 → npm官网
若帐户登陆成功后,就能够再次执行 npm publish 进行发布
vue 版的git地址:https://github.com/tangmengcheng/mc-ui.git vue + ts 版的git 地址:https://github.com/tangmengcheng/ts-ui.git 若是对须要的小伙伴有帮助,欢迎小伙伴们点赞、评论加关注!🤭star~~~
相信只要从头看到尾的小伙伴就会发现,封装一个组件很容易,主要的工做在于CSS样式上。只要本身有时间,而后根据像Element、iView、Antd等优秀的第三方UI库的源码,也能够实现一套属于本身的UI库。不论是面试仍是本身公司内部须要搭建本身的UI库,只要小伙伴们掌握了封装组件的原理,其余的都问题不大。但愿有须要的小伙伴手动敲一遍,加深对封装组件的过程。💪
若是以为本文还不错,记得点个赞哦!
欢迎你们加入,一块儿学习前端,共同进步!