Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

preface

I have always wanted to implement a component library of my own, so I made one myself some time ago. Before building it, I thought it was not difficult. When I actually started to implement it, there would be all kinds of problems. Fortunately, they were solved step by step.Chinese document Technical preparation: Vue2.x, VueTestUtils(test), Vuepress(documentation)

Other preparations: NPM account, Ali cloud server, domain name, etc

The build process

Initialize the

With vue-CLI initialization project, my options are Sass, Jest (test), etc. @vue/test-utils projects initialized with scaffolding are automatically installed with official documentation; Run the NPM install -d vuepress command to install vuepress.

Add the following command to the scripts of package.json

"scripts": {
    "serve": "vue-cli-service serve".// Project running
    "build": "vue-cli-service build".// Package the project
    "test:unit": "vue-cli-service test:unit"./ / test
    "docs:build": "vuepress build docs".// Package the Vuepress document and put it on the server
    "docs:dev": "vuepress dev docs".// Run locally to view Vuepress documentation
    "lib": "vue-cli-service build --target lib packages/index.js" // Package for NPM publishing, with the path changed to its own path
  },
Copy the code
The directory structure
|-- xixixi-ui
    |-- .npmignore Publish the configuration to NPM
    |-- babel.config.js
    |-- jest.config.js // Test the configuration
    |-- README.md
    |-- vue.config.js 
    |-- docs Vuepress document contents, folder names and file locations follow the official configuration
    |   |-- README.md
    |   |-- .vuepress
    |   |   |-- config.js
    |   |   |-- components // The vue code in this folder can be used directly in MD without being imported again
    |   |   |   |-- xxx-button.vue
    |   |   |   |-- xxx-checkbox.vue
    |   |   |-- public // Static resources (static resources must be placed here)
    |   |       |-- favicon.ico
    |   |       |-- fonts // Alibaba vector icon
    |   |-- views // Single MD file page
    |       |-- components
    |       |   |-- basic
    |       |   |   |-- Button.md
    |       |   |   |-- Icon.md
    |       |-- guide
    |           |-- install.md
    |           |-- start.md
    |-- packages // Each component of the component library
    |   |-- index.js // Export each component for vue.use()
    |   |-- button
    |   |   |-- button.vue
    |   |   |-- index.js
    |-- src 
    |   |-- App.vue
    |   |-- main.js
    |   |-- views
    |       |-- Home.vue
    |-- tests // Test the code
        |-- button.spec.js
        |-- unit
            |-- example.spec.js
Copy the code
Write a component

Here, the Button code is used as an example, and CSS is omitted

// button.vue
<template>
  <button class="xxx-button" :class="[`xxx-button--${type}`,`xxx-button--${size}`, { 'is-plain': plain, 'is-round': round, 'is-circle': circle, 'is-disabled': disabled }]"
    :disabled="disabled"
    @click="handleClick"
  >
    <i v-if="icon" :class="icon"></i>
    <span v-if="$slots.default"><slot></slot></span>
  </button>
</template>

<script>
export default {
  name: 'XxxButton'.props: {
    type: {
      type: String.default: 'default'
    },
    plain: {
      type: Boolean.default: false
    },
    round: {
      type: Boolean.default: false
    },
    circle: {
      type: Boolean.default: false
    },
    icon: {
      type: String.default: ' '
    },
    disabled: {
      type: Boolean.default: false
    },
    size: {
      type: String.default: 'default'}},methods: {
    handleClick (e) {
      this.$emit('click', e)
    }
  }
}
</script>
Copy the code

Export the corresponding button

// button/index.js
import Button from './button.vue'
export default Button
Copy the code
Expose components globally and locally
// package/index.js
import Button from './button'
import Dialog from './dialog'.import '.. /src/assets/fonts/iconfont.css'

// Store a list of components
const components = [
  Button,
  Dialog,
  / /... Several others are skipped here
]
const install = function (Vue, options = {}) {
  // Register all components globally
  components.forEach((item) = > {
    Vue.component(item.name, item)
  })
}

Vue.prototype.$message = Message;
Vue.prototype.$notice = Notice;

 
// Check whether the file is imported directly, if so, do not call vue.use ()
if (typeof window! = ='undefined' && window.Vue) {
  install(window.Vue)
}

export default {
  install,
  Button,
  Dialog,
  / /... Several others are skipped here
}
Copy the code
Unit test the component. See VueTestUtils for more detailsThe official documentation

Configure the jest. Config.js file

module.exports = {
  preset: '@vue/cli-plugin-unit-jest'.transform: {
    "^.+\\.js$": "<rootDir>/node_modules/babel-jest"
  },
  // Custom path abbreviation, the class starts with alias in Webpack
  moduleNameMapper: {
    "^@pack(.*)$": "<rootDir>/packages$1"
  },
  // Match the spec.js and test.js files in the tests directory to test. The default value is file tests in the tests/unit file
  "testMatch": [
    "<rootDir>/tests/**/*.spec.js"."<rootDir>/tests/**/*.test.js"]},Copy the code

Unit test the Button component

button.spec.js
import { mount } from '@vue/test-utils'
import Button from '@pack/button/button.vue' // @pack path is configured in jest.config.js

describe('button.vue'.() = >{
    it('button class'.async() = > {const wrapper  = mount(Button)
        await wrapper.setProps({ type: 'warning' })
        expect(wrapper.classes()).toContain('xxx-button--warning')
    }),
    it('Button trigger click event'.async() = > {const wrapper  = mount(Button,{
            slots: {default:"Button"
            }
        })
        wrapper.vm.$emit('handleClick'.123)
        await wrapper.vm.$nextTick() // Wait for the event processing to complete
        expect(wrapper.emitted().handleClick).toBeTruthy()
    })
})
Copy the code
Write component documentation with Vuepress (optionally StroyBook) refer to another of my documents hereportal

By default, you already have a server and some knowledge of Nginx. Use the NPM run docs:build command to package the documents. The packed files will be stored in the docs/.vuepress/dist folder, put the packed files in the corresponding directory of your cloud server, and configure the corresponding Nginx (different configurations for different people are not expanded here).

NPM release
  1. Package. The json'private':falseA private,'version' : '0.1.0 from'Different warranty versions
  2. Make sure there is no corresponding private package named in NPM, and if there is, modify package.jsonname
  3. runnpm run libCommand, packaged into a package that can be published to NPM
  4. To register an NPM account, type in CMD under this headingnpm loginEnter your account, password, and email address to log in. (In this case, ensure that the NPM config address is the source ADDRESS of the NPM)
  5. usenpm publishThe package.json command is used to publish its own NPM packageversionversion
conclusion

In the process of building the whole component from 0, everything seems very simple, but when some things are really operated, there will be all kinds of problems. Some things I have never used before, such as Vuepress, NPM release, etc., all of which can be more or less referenced from the Internet. However, the situation will be different from that of the project itself. As long as you make a bold attempt, it will always be solved. I try to develop this component library in the period of time, reference to a number of group price library and documentation to solve the problems I encountered, I hope to see you here, to you also help!