During my previous projects, I had access to several excellent component libraries, such as Ant Design, Element UI, Vant, etc., which were rich in components and functionality. Recently learning Vue3, I decided to encapsulate a few simple widgets myself to consolidate my knowledge and learn how to encapsulate my own component library.

Project structures,

  • Use Vite to build your own official website

Install the create – vite – app

yarn global add create-vite-app
Copy the code

Create a project

 cva project-name 
 / / or
 create-vite-app project-name 
 // Then follow the instructions
 cd project-name
 npm install
 npm run dev
Copy the code

The above steps will get the project started.

  • Vue Router 4 is introduced for page switching
Yarn install [email protected]Copy the code

Steps to initialize vue-Router

  1. Creating a History object
  2. Creating a Router Object
  3. app.use(router)
  4. add<router-view>The page corresponding to the route is displayed
  5. add<router-link>To Attribute is used to add the path of the route

The code is as follows:

Create the History object and router object

import {createWebHashHistory, createRouter} from 'vue-router';
import Home from './views/Home.vue';
import Doc from './views/Doc.vue';

const history = createWebHashHistory();
export const router = createRouter({
  history: history,
  routes: [
    {path: '/', component: Home},
    {
      path: '/doc',
      component: Doc,
    },
  ],
});
Copy the code
// main.ts
app.use(router);
Copy the code

Add the router – the view

<template>
    <router-view />
</template>

Copy the code

Add the router – the link

< li > < the router - link to = "/ doc" > document < / router - the link > < / li >Copy the code

At this point, the construction of the whole project is basically completed.

Knowledge point record

1. Setup (

The setup execution time is executed before the beforeCreate. When using the setup function, it will receive two arguments:

  • props: property passed by the component;propsIs responsive and will be updated as new props are passed in. Because it’s reactive, soYou cannot destruct using ES6Deconstruction will eliminate its responsiveness.
  • context:contextIs a plain JavaScript object that exposes the component’s three properties:attrs,slotsandemit(Non-responsive object).

You can use the setup() function to access a component’s property (props, attrs, slots, and emit), use it in conjunction with templates, and use the render function.

2

Takes an internal value and returns a reactive and mutable REF object. The ref object has a single property.value pointing to an internal value.

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1
Copy the code

3. Provide and inject

Typically, when we need to pass data from a parent component to a child component, we use props. Imagine a structure where you have deeply nested components, and deep child components that require only parts of the parent component. In this case, it can be cumbersome to still pass prop down the chain of components.

In this case, we can use a pair of provide and Inject. No matter how deep the component hierarchy is, a parent component can act as a dependent provider for all its children. This feature has two parts: the parent component has a provide option to provide data, and the child component has an Inject option to start using that data.

const app = Vue.createApp({}) app.component('todo-list', { data() { return { todos: ['Feed a cat', 'Buy tickets'] } }, provide: { user: 'John Doe' }, template: ` <div> {{ todos.length }} </div> ` }) app.component('todo-list-statistics', { inject: ['user'], created() {console.log(' Injected property: ${this.user} ') // Injected property: John Doe}})Copy the code

4. Use of V-Model

The V-Model simplifies data binding between parent and child (requiring the event name to be Update :x), which is also different from Vue 2 (no.sync).

Usage:

<Switch :value="y" @update:value="y = $event" />Copy the code

5. The Attribute inheritance

When the component returns a single root node, non-prop attributes are automatically added to the attributes of the root node. The same rule applies to event listeners. If you don’t want the component’s root element to inherit attributes, you can set inheritAttrs: false in the component’s options. For example, a common case where attribute inheritance is disabled is when the attribute needs to be applied to elements other than the root node.

Use $attrs or context.attrs to retrieve all attributes by setting the inheritAttrs option to false; Batch bind attributes using v-bind=”$attrs”. Can use… The remaining operators (ES6) separate attributes and bind them to other elements.

Encapsulating Components

  • UI library CSS considerations
  1. You can’t usescopedBecause:data-v-xxxIn thexxxEach run may be different, so we must output a stable class selector to make it easy for users to override;
  2. A prefix must be added. Add a unique prefix that is not easily overridden.
  3. CSS principle of least impact: Styles added to a component library should not affect the user’s style.
  • The Button component

Add different themes, sizes, whether to disable, and so on to the button based on the parameters passed in from the outside. Mainly on the DESIGN of CSS.

<template>
  <button class="easy-button" :class="classes" :disabled="disabled">
    <span v-if="loading" class="easy-loadingIndicator"></span>
    <slot/>
  </button>
</template>
Copy the code

Usage:

<Button size="small" disabled> </Button>Copy the code
  • The Switch component

The value that controls the state of the component is passed in externally and bidirectional bound through the V-Model.

<template>
  <button class="easy-switch"
          @click="toggle"
          :class="{'easy-checked': value}">
    <span></span>
  </button>
</template>

<script lang="ts">
export default {
  props: {
    value: Boolean,
  },
  setup(props, context) {
    const toggle = () => {
      context.emit("update:value", !props.value);
    };
    return {toggle};
  }
};
</script>
Copy the code

Usage:

<template> <Switch v-model:value="bool" /> </template> <script lang="ts"> import Switch from '.. /lib/Switch.vue' import { ref } from 'vue' export default { components: { Switch, }, setup() { const bool = ref(false) return { bool } } } </script>Copy the code
  • Dialog component

This component implements the basic style and frame of the modal box, passing in the parts by the user. Named slots are used.

<div class="easy-dialog"> <header> <slot name="title"/> <span @click="close" class="easy-dialog-close"></span> </header>  <main> <slot name="content"/> </main> <footer> <Button theme="main" @click="ok">OK</Button> <Button @click="cancel">Cancel</Button> </footer> </div>Copy the code

Specify name with v-slot

<Dialog v-model:visible="x" :closeOnClickOverlay="false" :ok="f1" :cancel="f2"> <template v-slot:content> <div> </div> </template> <template V-slot :title> <strong> Title </strong> </template> </Dialog>Copy the code

Wrap Dialog with Teleport

Teleport translates to Teleport, and Teleport provides a clean way to control which parent node in the DOM renders HTML without having to resort to global state or split it into two components. Because of the cascading context, when we use a Dialog, other elements may be overlaid on top of the Dialog. The nesting of child elements makes it difficult to set z-index. Teleport can help us choose to render the Dialog to any specified location. Simply put, you want to continue to use dialogs within the component, but you also want to render DOM structures that are not nested within the component’s DOM.

  • Tabs component

Usage:

<template> <Tabs V-model :selected="x"> <Tab title=" navigation 2"> <Tab title=" navigation 2"> </Tab> </Tabs> </template>Copy the code

You need to use context.slots.default() to get the content from slots. Determine its contents and render only if the subcomponent is Tab. Displays content based on incoming external selected. You need to iterate through all the titles of the child component, mark the one that corresponds to selected, and then add the selected style.

<div class="easy-tabs">
  <div class="easy-tabs-nav" ref="container">
    <div class="easy-tabs-nav-item"
         v-for="(t, index) in titles"
         :ref="el => { if(t===selected) selectedItem = el }"
         @click="select(t)"
         :class="{selected: t === selected}"
         :key="index">
      {{ t }}
    </div>
    <div class="easy-tabs-nav-indicator" ref="indicator"></div>
  </div>
  <div class="easy-tabs-content">
    <component :is="current" :key="current.props.title"/>
  </div>
</div>
Copy the code

Above is the integral design idea of a few components. Detailed code can be viewed by clicking on the source code. Preview link.

conclusion

Encapsulating component libraries can be developed more efficiently, and they should be summarized and expanded in future study.