专栏首页全栈前端精选从零实现一套属于自己的UI框架-发布到npm

从零实现一套属于自己的UI框架-发布到npm

汤小梦 https://juejin.im/post/5e200ee86fb9a02fdd38986d

不知不觉马上就要放假了,写一篇文章祝愿 2020 越来越好!

如今前端工程师的要求越来越高了,需要掌握的技术点越来越多了,会一些基本的前端技能完全适应不了快速变化的前端领域了。接下来我将从零实现一个自己的UI组件库并发布到npm上,提供给需要的朋友参考也总结下自己对封装组件的理解方便以后复习。

本项目以button按钮为例,详细的记录一下封装一个Button UI组件每一个步骤以及需要注意的地方:

效果

需要技能

  1. 需要掌握 Vue 的基本语法
  2. 组件之间的通信
  3. 插槽的用法
  4. vue-cli3 创建项目、打包
  5. npm & git 的用法

创建项目

检查 node 环境配置

先本地全局安装node环境,vue的运行是依赖于nodenpm的管理工具来实现的,node下载地址。下载好node之后,打开cmd管理工具,输入node -v,回车,查看node版本号,出现版本号则说明安装成功,注意:node 的版本要在 8.9 或更高版本 (推荐 8.11.0+)

    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。

  • 安装新版本
    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。

  1. 选择那些特性需要安装在项目中
     (*) Babel
     ( ) TypeScript
     ( ) Progressive Web App (PWA) Support
     ( ) Router
     ( ) Vuex
     (*) CSS Pre-processors
     (*) Linter / Formatter
     ( ) Unit Testing
     ( ) E2E Testing

系统默认的包含了基本的 Babel + ESLint 设置的 preset,我们只需要选择CSS配置。移动键盘上下键选择需要的特性,按下键盘空格键即可选中

  1. 安装哪一种 CSS 预处理语言
      Sass/SCSS (with dart-sass)
      Sass/SCSS (with node-sass)
      Less
      Stylus

由于Element UI中的样式采用Sass,所以我们选择第一项即可 为什么不选择第二项呢?因为dart-sass比node-sass更好下载

  1. 选择代码风格
      ESLint with error prevention only
      ESLint + Airbnb config
      ESLint + Standard config
      ESLint + Prettier

因个人喜好选择即可,我比较喜欢第三种

  1. 那种方式进行代码格式检测
     (*) Lint on save
     ( ) Lint and fix on commit

选择Ctrl+S保存时检测代码格式即可

  1. 配置文件生成方式
      In dedicated config files
      In package.json

因个人喜好,我比较喜欢选择第二种

  1. 是否保存预配置
    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>的实现
  1. <template>
    <template>
      <button class="mc-button">
        <span>
    	  <slot></slot> // slot表示插槽
    	</span>
      </button>
    </template>

注意:为什么要在slot插槽外面加个span标签呢?为了美观,每个自定义button组件间相互有点间距?

  1. <script>
    export default {
      name: 'McButton'
    }

注意:组件中填写name属性,是为了封装组件提供install方法时,动态获取每个组件名进行组件注册

  1. <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表示禁止用户选中文字

  1. 效果

basic_button

常见类型

  • 基础<mc-botton type="xxx" class="mc-bottom"></mc-botton>的实现

常见的类型有:primary / success / warning / danger / info / text

  1. <template>
    <button 
        class="mc-button" 
        :class="[
          `mc-button--${type}`
        ]">
        ...
    </button>

注意:为什么绑定class时采用数组形式,而不用对象形式,因为会有多个动态绑定的属性,且使用对象的形式,这样就只有数组里可以放多个动态属性,且属性可以为对象

  1. <script>
    props: {
        type: {
          type: String,
          default: 'default'
        }
    }

注意:这里接收属性为什么不用数组的形式?因为我们是封装组件给别人用的,所以要限制一些条件,不能让用户随意的输入

  1. <style>

该功能时,样式没有改动

  1. 效果

type_button

朴素按钮

  • 基础<mc-botton plain class="mc-bottom"></mc-botton>的实现
  1. <template>
    <button 
        class="mc-button" 
        :class="[
          `mc-button--${type}`,
          {
        		'is-plain': plain
           }
        ]">
        ...
    </button>

注意:为什么绑定class时采用数组形式,而不用对象形式,因为会有多个动态绑定的属性,且使用对象的形式,这样就只有数组里可以放多个动态属性,且属性可以为对象

  1. <script>
    plain: {
      type: Boolean,
      default: false
    }
  1. <style>
    &.is-plain:hover,
      &.is-plain:focus {
        background: #fff;
        border-color: #409eff;
        color: #409eff;
    }
  1. 效果

plain_button

添加圆角

  • 基础<mc-botton round class="mc-bottom"></mc-botton>的实现
  1. <template>
    <button 
        class="mc-button" 
        :class="[
          `mc-button--${type}`,
          {
                ...
                'is-round': round
           }
        ]">
        ...
    </button>
  1. <script>
    round: {
          type: Boolean,
          default: false
    }
  1. <style>
    &.is-round {
        border-radius: 20px;
        padding: 12px 23px;
     }
  1. 效果

round_button

添加圆形

  • 基础<mc-botton class="mc-bottom is-circle"></mc-botton>的实现
  1. <template>
    <button 
        class="mc-button" 
        :class="[
          `mc-button--${type}`,
          {
                ...
                'is-circle': circle
           }
        ]">
        ...
    </button>
  1. <script>
    circle: {
          type: Boolean,
          default: false
    }
  1. <style>
    &.is-circle {
        border-radius: 50%;
        padding: 12px;
    }
  1. 效果

circle_button

是否禁用

  • 基础<mc-botton class="mc-bottom" disabled></mc-botton>的实现
  1. <template>
    <button 
        ...
        :disabled="disabled"
        :class="[
          `mc-button--${type}`,
          {
                ...
                'is-disabled': disabled
           }
        ]">
        ...
    </button>
  1. <script>
    disabled: {
          type: Boolean,
          default: false
    }

注意:添加是否被禁用时有两个作用:第一、让用户不能点击;第二、改变按钮样式 3. 效果

添加图标

  • 基础<mc-botton icon="el-icon-check" class="mc-bottom"></mc-botton>的实现
  1. <template>
    <button
        ...
        >
        <i :class="icon" v-if="icon"></i>
        <!-- 如果没有传入插槽的时候才显示 -->
        <span v-if="$slots.default"><slot></slot></span>
    </button>
  1. <script>
    icon: {
        type: String,
        default: ''
    }
  1. <style>
    .mc-button [class*=mc-icon-]+span {
      margin-left: 5px;
    }

i标签上加v-if的目的:让用户即可以上传图标也可以上传文字或者上传文字和图标

注意:既传图标也传文字的话,图标和文字隔的很近,我们需要特殊处理下; 但是如果特性处理的话,单独的图标会不居中显示;所以我们要使用$slots获取包含有插槽的才让其显示处理

  1. 效果

icon_button

点击事件

  • 基础<mc-botton class="mc-bottom" @click="handleClick"></mc-botton>的实现
  1. <template>
    <button
        ...
        @click="handleClick"
        >
        ...
    </button>
  1. <script>
    methods: {
        handleClick (e) {
            this.$emit('click', e) // 向父组件派发一个click事件,e表示携带的参数
        }
    }
  1. 效果

click_button

打包

在Vue-cli3的 官方文档 中有个构建目标有明确的说明怎么打包成一个应用或者一个库!此时,我们需要在package.json中添加一条打包命令

    vue-cli-service build --target lib 指定打包的文件

然后控制台执行yarn lib即可将我们的组件库包括字体图标一起打包生成一个dist文件夹

上传github & 发布 npm

将代码上传到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这个依赖而已,简单说下他的用法:

  1. 全局安装这个依赖 npm install -g gitmoji-cli
  2. commit 提交代码时,加上表情符号就好。例如:":bug: xxxx", 这样你提交的代码注释前面就会加一个emoji图标,很炫酷的哦,小伙伴们可以试一下?
  3. 常见的emoji有::art: 、 :ambulance: 、:lipstick:等等

将代码发布到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 进行发布

  • 注意
  1. 一定要在package.json的scripts中添加main方便其他人下载时找到对应打包的文件
  2. 上传到npm上时,要将package.json中的private属性值改为false
  3. 修改源码后发布到npm时一定要更改项目的版本号

总结

相信只要从头看到尾的小伙伴就会发现,封装一个组件很容易,主要的工作在于CSS样式上。只要自己有时间,然后根据像Element、iView、Antd等优秀的第三方UI库的源码,也可以实现一套属于自己的UI库。不管是面试还是自己公司内部需要搭建自己的UI库,只要小伙伴们掌握了封装组件的原理,其他的都问题不大。希望有需要的小伙伴手动敲一遍,加深对封装组件的过程。?

加油

本文分享自微信公众号 - 全栈前端精选(isNealyang),作者:汤小梦

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-01-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 用故事解读 MobX 源码(一)autorun

    温馨提示:因微信中外链都无法点击,请通过文末的” “阅读原文” 到技术博客中完整查阅版;(本文整理自技术博客)

    Nealyang
  • 如何优雅处理前端异常?

    前端一直是距离用户最近的一层,随着产品的日益完善,我们会更加注重用户体验,而前端异常却如鲠在喉,甚是烦人。

    Nealyang
  • 未来可期的HTTP/3

    2015 年 HTTP/2 标准发表后,大多数主流浏览器也于当年年底支持该标准。此后,凭借着多路复用、头部压缩、服务器推送等优势,HTTP/2 得到了越来越多开...

    Nealyang
  • npm 切换源

    咻一咻
  • 解决calamari无法获取节点信息的bug

    一直在做calamari的相关的一些打包和安装的工作,都是业余弄的东西,所以并没有仔细的进行功能点的验证测试,正好ceph社区群里面有人问了个问题

    用户2772802
  • 英特尔暂停发放CPU补丁, Linux之父怒批“Spectre补丁是彻底的垃圾”

    英特尔周一表示用户应当停止在受影响的设备上,部署上个月由安全人员发现的芯片安全Meltdown和Spectre漏洞补丁,因为目前发现了超出预期的重启问题以及其他...

    FB客服
  • CRM客户关系管理系统(四)

    zhang_derek
  • 大数据-WordCount在一堆给定的文本文件中统计输出每一个单词出现的总次数

    cwl_java
  • ASP.NET MVC5+EF6+EasyUI 后台管理系统(63)-Excel导入和导出

    昨天文章太过仓促没有补充导出的示例源码,在者当时弄到到很晚没时间做出导出功能,对阅读理解造成影响,现补充一份示例源码,顺便补充导出的功能说明,望理解 示例代...

    用户1149182
  • ASP.NET MVC5+EF6+EasyUI 后台管理系统(87)-MVC Excel导入和导出

    前言: 导入导出实在多例子,很多成熟的组建都分装了导入和导出,这一节演示利用LinqToExcel组件对Excel的导入,这个是一个极其简单的例子。 我...

    用户1149182

扫码关注云+社区

领取腾讯云代金券