从零做一个自己的ui组件库

ui组件库

Posted by czk on July 28, 2020

前言

很多时候面对日常的工作业务需求,或者面对新项目时往往都是npm i element-ui -S安装element或者Ant的组件库,但如果自己想要面对特殊的需求需要定制组件往往需要不断修改,改完又往往忘记了整理,最后就是不断的重复。

你所写的每一个 vue 文件都可以视为一个组件,随着业务的不断深入,页面写多了想将组件集合起来封装成一个组件库的想法也不断加剧。仅在此写下组件库的构建思路和历程,供自己记忆学习,也希望能帮助到你。

目录构建

  • 推荐使用@vue/cli 进行项目的构建npm install -g @vue/cli
  • 命令行中输入vue reate orange_collect快速初始化一个项目。

  • 将src的目录文件改为examples,用来写组件相关文档和事例。
  • 再新建一个src目录,里面只放一个main.js用来作为打包入口
  • 在根目录下新建一个 packages 文件夹,用来进行组件的编写。
  • 在根目录下新建一个vue.config.js文件,配置如下
      const NODE_ENV = 'development' // 开发环境 打包doc
      // const NODE_ENV = 'lib' // 组件库 上传npm
      const components = require('./build/compontents.json');
      const path = require('path')
      const baseConfig = {
          chainWebpack: config => {
              config.resolve.alias
              .set('@', path.resolve('examples'))
              .set('~', path.resolve('packages'))
              config.module
                  .rule('js')
                  .include
                  .add('/packages')
                  .end()
                  .use('babel')
                  .loader('babel-loader')
                  .tap(options => {
                      // 修改它的选项...
                      return options
                  })
          },
          //如有scss配置需求
          // css: {
          //   extract: false,
          //   loaderOptions: {
          //     // 给 sass-loader 传递选项
          //     sass: {
          //       data: `@import "public/css/ui.scss";`
          //     }
          //   },
          // }
      }
      const devConfig = {
          pages: {
              index: {
                  entry: 'examples/main.js',
                  template: 'public/index.html',
                  filename: 'index.html',
              }
          },
          publicPath: './',
          outputDir: 'docs',
          ...baseConfig
      }
      const buildConfig = {
          configureWebpack: {
              entry: components,
              output: {
                  filename: '[name].js',
                  libraryTarget: 'commonjs2',
              },
          },
          outputDir: 'lib',
          productionSourceMap: false,
          ...baseConfig
      }
        
      module.exports = NODE_ENV === 'development' ? devConfig : buildConfig;
    

    这里通过判断NODE_ENV来判断项目的打包方式,在development下把examples 加入编译,将examples/main.js设置为文件入口。设置examplespackages的别名。

此时运行npm run serve已可以正常启动项目。将HelloWorld页面稍作修改,添加样式和布局。

  • router中添加页面路由
      import Vue from 'vue'
      import Router from 'vue-router'
      import Home from '../components/HelloWorld.vue'
        
      //组件
      import component from './component'
        
      Vue.use(Router)
        
      export default new Router({
        routes: [
          {
            path: '/',
            name: 'home',
            component: Home,
            redirect: '/component/installation',
            children: [
              //组件
              ...component,
            ]
          },
        ]
      })
    

    相关界面如上,需要样式和布局代码可以去我的github上查看,再次不在赘述。

组件编写

常规组件编写

  • 我们组件的编写都在packages文件夹中进行,举个🌰(例子),现编写一个测试的button组件。
      ├─packages                        
      │  └─orange_btn                      #组件模块名称文件夹
      │     ├─src 
      │     |  ├─orange_btn.vue                 
      │     └─index                        
      ├─index.js
    
  • 新建一个如上的目录结构,接下来我们就需要在orange_btn.vue中编写相关的业务代码。通过props来定义组件的相关组件和状态扩展。
      export default {
          props: {
          type: {
            type: String,
            default: "default"
          },
          size: String,
          icon: {
            type: String,
            default: ""
          }
        },
        name: "orange-button",
        data() {
          return {};
        },
    
  • 值得注意的是在orange_btn.vue中 name属性尤为重要它是必须要写的,是判断是否唯一组件的标识,所以你写的应该是不重名的。之后name将用来判定组件,如这里的nameorange-button在之后完 成组件的注册并且最终使用的时候则是<orange-button></orange-button>

组件图标的引入和使用

  • 建议使用iconfont矢量图库将ui的图标上传,或在图标库中寻找合适的图标后,下载至本地。
  • 将图中的字体文件夹引入项目
  • 在packages文件夹的index.js和examples的main.js中对字体文件进行引入import './assets/font/iconfont.css'
  • 使用iconfont+类名<i class="iconfont xxxx"/>

组件暴露

  • 每一个组件都需要如此步骤,先建立组件同名的文件夹,然后在orange_btn文件夹下的index·js下编写暴露代码,用于安装单个组件
      import BlackBtn from './src/orange_btn.vue'
        
      // 为组件提供 install 安装方法,供按需引入
      BlackBtn.install = function (Vue) {
        Vue.component(BlackBtn.name, BlackBtn)
      }
    
      // 默认导出组件
      export default BlackBtn
    
  • 在packages文件夹下新建一个index.js用来管理,可以避免我们的组件越来越多,需要重复引入,循环安装所有组件。
      import orange_btn from './orange_btn'
      // 存储组件列表
      const components = [
        orange_btn
      ]
      // 定义 install 方法,接收 Vue 作为参数。如果使用 use 注册插件,则所有的组件都将被注册
      const install = function (Vue) {
        // 判断是否安装
        if (install.installed) return
        // 遍历注册全局组件
        components.map(component => Vue.component(component.name, component))
      }
        
      // 判断是否是直接引入文件
      if (typeof window !== 'undefined' && window.Vue) {
        install(window.Vue)
      }
        
      export default {
        // 导出的对象必须具有 install,才能被 Vue.use() 方法安装
        install,
        // 以下是具体的组件列表
        orange_btn
      }
    

    组件引入

  • 在examples用来写组件相关文档和事例。在examples的main.js中引入

  • 使用<orange-button></orange-button>已可以在页面中使用,由此可以编写相关的文档说明。

打包配置

  • 在项目组件编写过后,就可以将组件打包📦上传至npm使用
  • vue.config.js下的NODE_ENV改为lib模式
  • 新建build文件夹,写一个方法
      const fs = require('fs-extra');
      const path = require('path');
        
      function isDir(dir) {
          return fs.lstatSync(dir).isDirectory();
      }
        
      function transformStr3(str) {
          var re = /-(\w)/g;
          return str.replace(re, function ($0, $1) {
              return $1.toUpperCase();
          });
      }
        
      // function firstUpperCase(str) {
      //     return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase());
      // }
        
      const json = {};
      const dir = path.join(__dirname, '../packages');
      const files = fs.readdirSync(dir);
      files.forEach(file => {
          const absolutePath = path.join(dir, file);
          if (isDir(absolutePath)) {
              let fileKey = transformStr3(file.replace('orange_', ''))
              // console.log(fileKey)
              json[fileKey] = `/Users/chenzhikun/project/GitHub/orange_ui/packages/${file}/index.js`; //mac
              // json[fileKey] = `D:/projects/orange_ui/packages/${file}/index.js`;  //windows
          }
      });
    
  • 通过运行方法,获得组件的路径之后在vue.config.js中引入const components = require('./build/compontents.json')
  • 运行npm run build可以得到一个lib文件,这个文件就是我们最终要上传到npm的文件。此时文件目录如下。

npm发布

  • npm发布时,要将package.json中的private属性值改为false并修改项目的版本号
  • 我们没有必要把所有的代码都发布到npm上。在项目的根目录创建一个.npmignore的文件,对多余的文件进行忽略.
      .DS_Store
      node_modules
      /dist
        
      # local env files
      .env.local
      .env.*.local
        
      # Log files
      npm-debug.log*
      yarn-debug.log*
      yarn-error.log*
        
      # Editor directories and files
      .idea
      .vscode
      *.suo
      *.ntvs*
      *.njsproj
      *.sln
      *.sw*
        
      # 以下是新增的
      # 要忽略目录和指定文件
      build/
      examples/
      packages/
      public/
      src/
      util/
      vue.config.js
      babel.config.js
      *.map
      *.html
    
  • 执行npm who am i命令, 如果没有登陆则执行npm login命令,系统会提示输入账户和密码。如果没有则需要前往npm官网进行注册。
  • 登陆之后执行 npm publish 进行版本发布。
  • 在npm发布完成之后就可以在任意一项目中使用npm i orange_collect来安装项目,之后在main.js中引入
      import orange from 'orange_collect'
      import 'orange_collect/lib/orange_collect.css'
        
      Vue.use(orange)
    

    此时的组件均能通过标签正常的使用。

    小结

    从零新建一个自己的组件库至此就已经完成了,如果想我一样团队中有需要封装自己的组件代码库,则可以试着像我一样写一个轮子一起用,我则是想收集各类有意思的按钮进行自己的封装。 github地址