• 技术文章 >web前端 >Vue.js

    手把手教你从零开始搭建一套ui组件库

    藏色散人藏色散人2023-04-13 17:42:21转载206

    本篇文章给大家带来了关于ui组件的相关知识,其中主要跟大家聊聊怎么从零开始搭建一套ui组件库,有代码示例,感兴趣的朋友下面一起来看一下吧,希望对大家有帮助。

    1. 环境准备

    我们在编写我们组件库的组件前,首先需要一套环境,包括下面几项:

    1. 需要为组件库单独创建一个新的项目
    2. 需要规划合适的目录结构
    3. 需要定义组件文档的编写
    4. 需要搭建完善的单元测试

    1.1 项目的搭建

    我们目前的项目是基于vue2的版本,所以本次组件库项目也将使用 2.0版本的vue cli来创建。

    // 全局安装 vue-cli
    
    npm install --global vue-cli
    
    
    
    // 基于 webpack 创建一个的新项目
    
    vue init webpack my-project
    
    
    
    // 安装依赖
    
    npm install
    
    
    
    // 运行
    
    npm run dev

    安装过程相关选项如下:

    我们默认安装jest做为我们组件库的单元测试框架,代码检查工具默认eslint

    1.2 目录的优化

    创建项目成功后,现在我们新项目的目录结构应该是这样的:

    • build 打包相关目录以及配置
    • config 配置文件目录
    • node_modules 项目中安装的依赖模块
    • src 源码目录
    • static 静态文件目录
    • test 单元测试目录

    我们需要对现有目录做一些调整,首先我们接触过一些主流的ui组件库比如 vant/ant,我们知道在这些组件库的官网上都提供了很直观的示例页面,此时我们的组件库将src目录改名为examples,作为我们的官方示例目录。

    另外我们新增一个packages目录用户存放我们的组件。

    现在我们目录结构变成如下:

    此时如果重新运行项目会发现报错,因为我们src目录名变了,而webpack配置中默认的入口文件还是src/main.js,我们需要更改下配置,在build/webpack.base.conf文件中将src替换成examples。同时,我们需要将新增的 packages目录加入到webpack的编译队列。

    更改后的 webpack.base.conf 应该是这样的。

    'use strict'
    
    const path = require('path')
    
    const utils = require('./utils')
    
    const config = require('../config')
    
    const vueLoaderConfig = require('./vue-loader.conf')
    
    
    
    function resolve (dir) {
    
      return path.join(__dirname, '..', dir)
    
    }
    
    
    
    const createLintingRule = () => ({
    
      test: /.(js|vue)$/,
    
      loader: 'eslint-loader',
    
      enforce: 'pre',
    
      include: [resolve('examples'), resolve('packages'),resolve('test')],
    
      options: {
    
        formatter: require('eslint-friendly-formatter'),
    
        emitWarning: !config.dev.showEslintErrorsInOverlay
    
      }
    
    })
    
    
    
    module.exports = {
    
      context: path.resolve(__dirname, '../'),
    
      entry: {
    
        app: './examples/main.js' // 打包入口
    
      },
    
      output: {
    
        path: config.build.assetsRoot,
    
        filename: '[name].js',
    
        publicPath: process.env.NODE_ENV === 'production'
    
          ? config.build.assetsPublicPath
    
          : config.dev.assetsPublicPath
    
      },
    
      resolve: {
    
        extensions: ['.js', '.vue', '.json'],
    
        alias: {
    
          'vue$': 'vue/dist/vue.esm.js',
    
          '@': resolve('examples'),
    
        }
    
      },
    
      module: {
    
        rules: [
    
          ...(config.dev.useEslint ? [createLintingRule()] : []),
    
          {
    
            test: /.vue$/,
    
            loader: 'vue-loader',
    
            options: vueLoaderConfig
    
          },
    
          {
    
            test: /.js$/,
    
            loader: 'babel-loader',
    
            include: [resolve('examples'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
    
          },
    
          {
    
            test: /.(png|jpe?g|gif|svg)(?.*)?$/,
    
            loader: 'url-loader',
    
            options: {
    
              limit: 10000,
    
              name: utils.assetsPath('img/[name].[hash:7].[ext]')
    
            }
    
          },
    
          {
    
            test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/,
    
            loader: 'url-loader',
    
            options: {
    
              limit: 10000,
    
              name: utils.assetsPath('media/[name].[hash:7].[ext]')
    
            }
    
          },
    
          {
    
            test: /.(woff2?|eot|ttf|otf)(?.*)?$/,
    
            loader: 'url-loader',
    
            options: {
    
              limit: 10000,
    
              name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
    
            }
    
          }
    
        ]
    
      },
    
      node: {
    
        // prevent webpack from injecting useless setImmediate polyfill because Vue
    
        // source contains it (although only uses it if it's native).
    
        setImmediate: false,
    
        // prevent webpack from injecting mocks to Node native modules
    
        // that does not make sense for the client
    
        dgram: 'empty',
    
        fs: 'empty',
    
        net: 'empty',
    
        tls: 'empty',
    
        child_process: 'empty'
    
      }
    
    }

    重新运行,编译通过。

    1.3 组件文档的编写

    在搭建完基础的代码环境后,我们要考虑我们新增组件的组件文档如何编写。

    我们推荐使用 markdown来编写组件文档,然后我们如何在vue中使用markdown来编写我们的组件文档呢?这里我们推荐一个好用的工具。

    vue-markdown-loader

    1.3.1 安装方式

    # vue1版本
    
    npm i vue-markdown-loader@0 -D
    
    
    
    # vue2版本
    
    npm i vue-markdown-loader -D
    
    npm i  vue-loader vue-template-compiler -D

    1.3.2 webpack 配置

    我们在对webpack.base.conf作如下修改:

    const VueLoaderPlugin = require('vue-loader/lib/plugin');
    
    
    
      module: {
    
        rules: [
    
          ...,
    
          {
    
            test: /.md$/,
    
            use: [
    
              {
    
                loader: 'vue-loader'
    
              },
    
              {
    
                loader: 'vue-markdown-loader/lib/markdown-compiler',
    
                options: {
    
                  raw: true
    
                }
    
              }
    
            ]
    
          },
    
          ...
    
          ]
    
          },
    
     plugins: [new VueLoaderPlugin()]

    1.3.3 编写组件文档

    在我们配置完工具后,我们开始测试下组件文档的编写,

    首先,我们在examples目录下新增一个docs文件夹,用于存放我们的组件文档。

    新建一个test.md

     # hello world

    接下来我们在router文件夹新增一个 docs.js路由文件,用来存放我们组件文档的路径,并将它引入到根路由文件中。

    const docs = [
    
     {
    
     path: '/test',
    
     name: 'test',
    
     component: r => require.ensure([], () => r(require('../docs/test.md')))
    
     }
    
     ]
    
    export default docs

    浏览器中运行,我们便可以看到我们组件库的第一个组件文档...

    以上完成,我们组件库的环境基本搭建完成了,接下来我们尝试开始写一个新的组件。

    1. 组件创建

    我们先从一个基础的button组件开始。

    首先我们在之前创建的packages中新增如下结构:

    • sg-button 组件目录
    • index.js 组件安装入口程序
    • src 组件源码

    2.1 组件vue源码

    这里我在src/index.vue中简单实现了一个button组件,支持三种大小的按钮,

    <template>
    
        <div :class="[size]"  @click="click()">
    
            <span><slot></slot></span>
    
        </div>
    
    </template>
    
    <script>
    
     /**
    
     * 全局统一弹窗
    
     */
    
    export default {
    
      name: 'sgButton',
    
      props: {
    
        size: {
    
          type: String,
    
          default: ''
    
        } // 按钮大小 :small large
    
      },
    
      methods: {
    
        click () {
    
          this.$emit('click')
    
        }
    
      }
    
    }
    
    </script>
    
    <style  scoped>
    
    .container{
    
        height: 50px;
    
        display: flex;
    
        justify-content: center;
    
        align-items: center;
    
        border: 1px solid #ccc;
    
    }
    
    .container.small{
    
        height: 40px;
    
    }
    
    .container.large{
    
        height: 60px;
    
    }
    
    </style>

    2.2 组件导出

    然后我们要怎么用这个组件呢?

    考虑的是组件库,所以我们需要让我们的组件支持全局引入和按需引入,如果全局引入,那么所有的组件需要要注册到Vue component 上,并导出:

    我们需要在组件的入口文件index.js添加如下代码:

     // 导入组件,组件必须声明 name
    
    import sgButton from './src'
    
    
    
     // 为组件提供 install 安装方法,供按需引入
    
    sgButton.install = function (Vue) {
    
      Vue.component(sgButton.name, sgButton)
    
    }
    
    
    
     // 导出组件
    
    export default sgButton

    然后我们在packages目录下新增入口文件,统一处理导出所有组件:

     // 导入button组件
    
    import sgButton from './sg-button'
    
    
    
     // 组件列表
    
    const components = [
    
      sgButton
    
    ]
    
    
    
     // 定义 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,
    
      // 以下是具体的组件列表
    
      sgButton
    
    }

    2.3 组件引入

    按需引入:

    import sgUi from '../packages/index'
    
    
    
    Vue.use(sgUi.sgButton)

    全部引入:

    import sgUi from '../packages/index'
    
    
    
    Vue.use(sgUi)

    2.4 测试代码

    我们在examples目录的入口文件中全局引入了组件库

     // The Vue build version to load with the `import` command
    
     // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
    
    import Vue from 'vue'
    
    import App from './App'
    
    import router from './router'
    
    import sgUi from '../packages/index'
    
    
    
    Vue.config.productionTip = false
    
    
    
    Vue.use(sgUi)
    
     /* eslint-disable no-new */
    
    new Vue({
    
      el: '#app',
    
      router,
    
      components: { App },
    
      template: '<App/>'
    
    })

    然后我们编写一个vue页面来看看是否引入成功。

    首先examples中新增pages目录,存放我们以后为每个组件单独编写的示例页面,新增examples/pages/buttonExample/index.vue 页面

    <template>
    
        <div class="container">
    
          <sg-button>默认按钮</sg-button>
    
          <sg-button :size="'large'">大按钮</sg-button>
    
          <sg-button :size="'small'">小按钮</sg-button>
    
        </div>
    
    </template>
    
    <script>
    
     /**
    
     * button 示例
    
     */
    
    export default {
    
      name: 'buttonExample',
    
    
    
      methods: {
    
    
    
      }
    
    }
    
    </script>

    在这里我们直接调用了三种尺寸的button,运行看下效果:

    效果完美,代表我们组件库第一个组件以及整体流程打通!

    1. 组件库发布

    之前的环节,我们成功实现了我们组件库的第一个组件,但考虑到这只是组件库,组件库内能调用肯定是不够的,类似 vant/ant 这些组件库,我们怎么让其他用户可以使用我们的组件库组件内?

    我们可以考虑发布到npm上,后续项目需要的话,我们直接通过npm安装引入的方式来调用。

    发布到npm的方法也很简单, 首先我们需要先注册去npm官网注册一个账号, 然后控制台登录即可,最后我们执行npm publish即可.具体流程如下:

    // 本地编译组件库代码
    
    yarn lib
    
    // 登录
    
     npm login
    
     // 发布
    
     npm publish
    
     // 如果发布失败提示权限问题,请执行以下命令
    
     npm publish --access public
    1. 单元测试

    Vue Test Utils 安装

    以上就是手把手教你从零开始搭建一套ui组件库的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:juejin,如有侵犯,请联系admin@php.cn删除
    专题推荐:前端 JavaScript Vue.js
    上一篇:一文聊聊vue3中的ref、toRef、toRefs 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • 17个Vue3实用UI组件库(Web+移动)分享• 7 个很棒且实用的React 组件库(压箱底分享)• 【由浅入深】vue组件库实战开发总结分享• uniapp用什么组件库• 如何在Vue.js项目中集成WEUI组件库
    1/1

    PHP中文网