preview

Github address if you like to give a star to encourage ha, do not use in the production environment, for learning & communication ~~

Complete project directory structure

After git clone is installed locally, run NPM run serve to develop the local component library. NPM run docs:dev to develop the official website of the component library. Vue is used to test a single component in SRC /demo.vue and then import it into.vuepress/ Components and put it on the component library website.

│ ├─ Exercises - Dist │ │ ├─ Assets // Vuepress │ │ ├─ Components │ │ ├─ Dist │ │ ├─ Assets │ │ │ │ ├ ─ CSS │ │ │ │ ├ ─ img │ │ │ │ └ ─ js │ │ │ ├ ─ components │ │ │ │ ├ ─ basic │ │ │ │ ├ ─ form │ │ │ │ ├ ─ navigation │ │ │ │ ├ ─ notice │ │ │ │ └ ─ other │ │ │ └ ─ guide │ │ │ │ │ ├ ─ config. Js / / vurepess configuration changes the entrance, including the sidebar on the left, upper right nav navigation menu, favicon etc │ │ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ └ ├─ favicon │ ├─img │ ├─ js │ ├─ favicon │ ├─ ├─ favicon │ ├─img │ ├─ js │ ├─ Markdown │ ├─ Components │ ├─basic │ ├─form │ ├─navigation │ ├─ Notice │ ├─ ├─design │ ├─ color │ ├─ ├─ SRC // ├─ Button │ ├─ Cascader │ ├─ Collapse │ ├─ Container │ ├─ ├─ Form │ ├─ Icon │ ├─ Layout │ ├─ Notice │ ├ ins │ ├ ins │ ├─ Slide │ ├─ TAB │ ├─step │ ├─sticky │ ├.js ├─package.json // Related to NPM release, record version number, package entry file addressCopy the code

Learning how to make component libraries is rewarding

  • Learn component packaging skills, good interface design, master component design routines
  • Solid JS/CSS foundation
  • In-depth understanding of VUE

Production process

  1. Component design/development
  2. Release NPM
  3. Make official website display

Component design/development

Frequent vUE apis include

  • $children: Gets the children of the current component.
  • $parent: Gets the parent of the current component.
  • $options: Initialization options for the current Vue instance, which can be used to obtain the component name.
  • $refs: An object that holds all DOM elements and component instances that have registered the REF feature.
  • $el: The root DOM element used by Vue instances.
  • Provide & Inject: This pair of options needs to be used together to allow an ancestor component to inject a dependency to all of its descendants, not reactive. The injected object can be an eventBus of a VUE instance.
  • $ON: The component listens for custom events.
  • $emit: The component fires a custom event.
  • Sync: In a one-way data stream, the parent component is listening for the child component to modify the props, and the parent component is modifying the props passed in. Sync does not require the parent component to explicitly listen for custom events within the component to change the value. The parent component simply writes x.sinc =”bindValue”. Note that the event triggered by the child component must be “update:x”.
  • Updated lifecycle hook function, called after virtual DOM re-rendering and patting due to data changes, may be used in parent-child component communication.
  • BeforeDestoryed/deStory lifecycle hook function, all event listeners of the component after deStory are removed. Note: If you add event listeners to the DOM inside the component, you will need to manually touch the listener when the component is destroyed. In addition to component destruction, dom elements remain on the page and need to be removed manually by calling the native JS API, Node.remove () to remove the DOM node.

Native JS apis include:

  • Target.addeventlistener (type, Listener [, useCapture])/removeEventListener Since this is a basic part of the DOM2 specification, almost all browsers support this. And no special cross-browser compatibility code is required.
  • Node.contains() returns a Boolean value indicating whether the passed Node is a descendant of the Node. Mostly used for event listening to determine whether the target area is clicked.
  • Window. scrollY gets the vertical scrolling distance of the document.
  • Element. GetBoundingClientRect () returns the Element size and its position relative to the viewport, returns an object, including the width/height/left/right/top/bottom. It is mainly used for calculation and positioning.

Summary of Technical Points

The idea of component design includes single data stream/eventBus event center, the core of which is component communication.

  • Single data flow: Data changes are unidirectional, that is, only the parent component can modify the data through props, and the child component cannot actively modify the data. An example of this is in the collapse/ TAB/Slide component, where the parent component controls the selected value. The idea of one-way data flow makes data modification better designed and logic clearer.
  • Vue plug-in Development: When to develop with plug-ins? When a component is not called explicitly in code, it is not written directly to the template, but is mounted to the document by calling methods on the Vue prototype chain. Such as modal modal boxes/Toast popovers. The basic idea behind the plug-in design is to expose a install method that adds a custom method X to the vue prototype chain, which introduces component A, Get the component Constructor from vue.extend (a), get the component instance VM from new Constructor({propsData}), and mount the component instance to the document.
import modal from '.. /notice/modal'

export default {
  install (vue, options) {
    const Construtor = vue.extend(modal)

    letModalVm // ensures that there is only one modal instance globallylet lastOption
    vue.prototype.$modal = (options) => {

    if(lastOption ! == JSON.stringify(options)) { //! modalVm modalVm = new Construtor({ propsData: options }) modalVm.$mount()

        document.body.append(modalVm.$el)
      }
      lastOption = JSON.stringify(options)
      modalVm.isVisible = true}}}Copy the code
  • EventBus: When do I use eventBus? Multiple child components need to be notified when a state change occurs. In the TAB component, if the selected value changes, tab-head needs to sense the change and animate the prompt to slide under the selected TAB, Tab-Item needs to sense the change to make the text look like the selected TAB, and Tab-Pane needs to sense the change to make the selected panel appear.
  • Recursion: used in the design of cascading components. Similarly, fn calls itself setTimout(fn,millseconds) to achieve the recursive effect of setInterval. A component can recursively call itself as long as it provides the name attribute internally. Allows a component template to recursively call itself. By providing the name option to facilitate debugging, you can see component labels in the console for more semantic information.
  • Media Query & Flex Layout: The principle of reactive layout is media query and percentage layout, between a certain size when a class name is in effect; Most of the layout related work is done in Flex, which is very useful. See ruan Yifeng teacher tutorial in detail
Component type component A single data stream Vue plug-in development eventBus Native JS manipulates DOM & events recursive Media query & Flex layout
basis The button button
basis Icon icon
basis The grid mesh yes
basis Layout layout yes
The form The input fields
The form Cascader cascade selector yes yes
The form Form form
The form Datepicker datepicker yes
navigation TAB TAB yes
navigation Step step adjustable
notice A bigger tip yes yes
notice Popover pop-up yes
notice Modal modal dialog yes yes
other Collapse panel yes yes
other Slide round figure yes
other Sticky viscous

Component design three elements

  1. Props: For example, use antD or Ele. me to make it easy to use and scalable, check the types and valid values, and set the default values.
  2. Slot: Slot content distribution, using scoped slots to make slot access to component internal methods, allowing user-defined content pages to call component internal methods, such as popover popovers where users want to add a button to manually close them.
  3. Event: indicates a component event. From the user’s point of view, for example in the Datepicker component the user wants to operate when the date panel is open or closed. This is typically used with interactive UI components.

For example

Complex component datepicker development ideas

  1. So I’m going to develop on top of the existing popover component and I’m going to click on an element A(input field) and I’m going to pop up element B (date panel)

  2. The Generate Date panel generates 7*6=42 dates, and the 6 lines are to ensure that a full month is displayed on the panel. The easiest way to do this is to use a time stamp, and you can figure out the timestamp of the first day of the month and the day of the week, and you can figure out all 42 dates at once. There is no need to calculate the last month and the next month in three sections, such a problem is to consider the boundary situation, such as the occurrence of the last year and the next year, trouble is easy to bug. These 42 dates are computed using visibleDays.

    visibleDays () {
       let { year, month } = this.display
       letdefaultObj = new Date(year, month, 28) var curMonthFirstDay = helper.getMonthFirstDay(defaultObj) var curMonthFirstDayDay = helper.getDay(curMonthFirstDay) = = = 0? 7 : helper.getDay(curMonthFirstDay)letX = curmonthFirstDayDay1 var arr = []for (let i = 0; i < 42; i++) {
         arr.push(new Date(curMonthFirstDay.getTime() + (-x + i) * 3600 * 24 * 1000))
       }
       return arr
     },
    Copy the code
  3. Props accepts value of type date () as the date on the date panel and adds a class to the render, adding ‘today’,’selected-date’,’available’,’prev-month’,’next-month’, Make style distinctions

  4. The implementation selects the date to tell the parent component to modify the props that was passed to it, and to use it when using our component. The basic thing here is that v-model=”x” on the component will be resolved as :value=”a” @input=”a=$event”. At the same time, the data displayed in the input box on the panel also changes, so it is represented by computed properties, such as formattedValue in computed.

     formattedValue: {
         return this.value instanceof Date ? helper.getFormatDate(this.value) : ' '
     }
    Copy the code
  5. We need to know which year and which month are currently displayed. This data is maintained internally by the component, so we declare a display object in data

    display: {
         year: (this.value && this.value.getFullYear()) || new Date().getFullYear(),
         month: (this.value && this.value.getMonth()) || new Date().getMonth()
       }
    Copy the code

    When you click on it, you change the year/month of the display object, because visibleDays is also calculated and depends on the display object, so if you click on the last year/month, the next year/month, the rendering date will also change.

  6. To achieve the selection of year panel production, generate 12 years, click the first (12) year render out 12 years (next). We simply bind events to the first and last DOM elements of the rendered year, and the event listener passes in the value of the currently clicked element to calculate the last or next 12 years. In the same way, use $emit to notify the parent component to change value when clicking on year

  7. The implementation selects the month to write the 12 months directly, and uses $emit to notify the parent component to change the value when clicking on the month

  8. Added $emit to notify parent components to change value, new Date() and ” when clicking the Today and Clear buttons on the live panel

  9. After the user selects the date, the panel should be closed. After the user selects the year, click the blank area around the date panel to close. After the second click, the panel should be displayed by default

  10. The user can change the value in the input field. If it is valid, $emit will notify the parent component to change the value. If it is invalid, it will change back to the original value when it loses focus. Value =”formattedValue”, but since formattedValue is a calculated property, it depends on this.value. This. value will not change if the user enters an invalid value, so the interface will not be updated, so you need to manually change the value of value.

    setValueManually ($event) {
       if(! helper.isValidDate($event)) {
         this.$refs.inputWrapper.$refs.input.value = this.isDate(this.value) ? helper.getFormatDate(this.value) : ' '
         return
       }
       this.$emit('input', new Date($event))}Copy the code
  11. Improvements to add component custom events to pop up date panel and close date panel, i.e. call $emit to trigger ‘showDatepicker’ and ‘closeDatepicker’ events.

Release NPM

  1. Package the code using vue CLI3’s library mode and modify the “build” in package.json: “Vue-cli-service build –target lib –name sakura SRC /index.js”, package and output umD build version for referencevue cli. What is the umd? Unified module definition, compatible with common. Js (Node side specification), AMD(browser side specification), ES6(node side not fully supported) and other modular solutions, to ensure that the code can be run in a variety of environments.
    File Size Gzipped dist/sakura.umd.min.js 13.28 KB 8.42 KB dist/sakura.umd.js 20.95 KB 10.22 KB dist/sakura.common.js Dist/Sakura. CSS 0.33 KB 0.23 KBCopy the code
  2. In package.json, specify the module entry “main”:”dist/sakura.umd.min.js”
     "name": "heian-sakura-ui"."version": "0.0.6"."private": false."main":"dist/sakura.umd.min.js"."description": "an UI framework based on Vue.js".Copy the code
  3. Register a user on NPM
  4. From the command line, note that “version”: “0.0.x” in package.json must be changed each time you publish, and “private” must be set to false to publish
    NPM adduser // Prompts for the registered user name NPM publishCopy the code

Website production

Using the vue press

  1. Used in the original project

    # install dependencies
    npm install -D vuepress
    Create a docs directory
    mkdir docs
    Copy the code

    Do script configuration in package.json

    {
    "scripts": {
      "docs:dev": "vuepress dev docs"."docs:build": "vuepress build docs"}}Copy the code

    Then run NPM run docs:dev to access it

  2. Simple configuration: Create the config.js file under docs/.vuepress

    module.exports = {
        base:'/sakura-ui/',
        title: 'Sakura UI',
        description: 'Inspiration from heian sakura',
        head: [
          ['link', { rel: 'icon', href: '/favicon.ico' }]
        ],
        themeConfig: {
          nav: [
            { text: 'Home', link: '/' },
            { text: 'Github', link: 'https://github.com/Firenzia/sakura-ui/' },
          ],
          sidebar: [
              {
                  title: 'Development Guide',
                  collapsable: true,
                  children: [
                    'views/guide/install.md'.'views/guide/get-started.md'
                  ]
                },
                {
                  title: 'design',
                  collapsable: true,
                  children: [
                    'views/design/color/',
                  ]
                },
                {
                  title: 'components',
                  collapsable: true,
                  children: [
                    'views/components/basic/'.'views/components/form/'.'views/components/navigation/'.'views/components/notice/'.'views/components/other/']},]}}Copy the code
  3. The official website for using Vue components states that all *. Vue files found in.vuepress/ Components are automatically registered as global asynchronous components and can be referenced in markdown. The code in the vue file is highlighted and I used VUe-HighlightJS

  4. Since all pages need to be rendered by the Node.js server to generate static HTML, for components that are less SSR friendly (such as those that contain custom instructions), you can wrap them in the built-in ClientOnly component. Also note that because it is SSR, the component internal beforeCreate, created lifecycle hook functions do not access the browser/DOM API and can only be called in beforeMount and Mounted.

    ---
    title: 'the Basic basis'
    sidebarDepth: 2
    ---
    # # Icon Icon< ClientOnly > < sakura - icon / > < font size = 5 > Attributes < / font > | parameters | | | | | alternatives, a default value type | : -- -- -- -- -- - | -- -- -- -- -- - | -- - | -- -- -- -- -- - | -- - | | name | | icon name string | | - | | color | icon color, support common color and hexadecimal color | string | | - | < / ClientOnly >Copy the code
  5. Override the default theme styles. Add style.styl under.vuepress to override the default theme styles.

  6. Deploying to Github is clear on the github website, click here. Sh is added in the root directory of the project and run the./deploy.sh command in Windows to publish the project to Github Pages.

conclusion

If you can see here, thank you very much, the first time to write an article, I hope you can give more comments. There are still many details to be improved in the component library. For example, I have not standardized the CSS class name, most of the components are tested by themselves without measuring complex or special scenes, and many functions are not yet supported. Through this period of time to make component library, my own technology has been improved, the display of the official website into my own ideas and design, I hope you enjoy ~~ thank you!