This article will introduce how to develop a component in the component library, including:
- How to debug components locally in real-time
- How to support global imports in the component library
- How to name components using the setup syntax sugar
- How to develop a component
Directory Structure
Create two packages named components
and utils
under the packages
directory. The components
package will store our components, while the utils
package will store common methods and utilities. Run pnpm init
in both directories and rename their package names to @stellarnovaui/components
and @stellarnovaui/utils
.
{
"name": "@stellarnovaui/components",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Create src
folder under the components
directory to store all components. The final directory structure will be:
packages/
└── components/
└── src/
└── button/
├── button.vue
├── index.ts
├── package.json
utils/
play/
Of course, this is just the current structure. It will be adjusted later to include directories for styles, tests, and other files.
Button Component
Create a simple button component in the button.vue file:
<template>
<button>Test Button</button>
</template>
Then export the Button in button/index.ts
import Button from "./button.vue"
export { Button }
export default Button
Because we will have many components later, such as Icon, Upload, Select, etc., we need to export all components collectively in components/src/index.ts
.
export * from './button'
Finally, export all components in components/index.ts
to make them available for external use.
export * from './src/index'
Next, we'll test the component library in the play
project set up in the previous article. First, install the @stellarnovaui/components
package locally in the play
project.
pnpm add @stellarnovaui/components
Using the Button Component in app.vue
<script lang="ts" setup>
import { Button } from "@stellarnovaui/components";
</script>
<template>
<div>
<Button />
</div>
</template>
Once you start the project, you should see the Button
component. Additionally, any changes you make to the Button
component will be reflected immediately due to hot module replacement (HMR).
app.use
Global Component Registration
Sometimes, when we use components, we want to directly use app.use()
to mount the entire component library. When using app.use()
, it actually calls the install
method of the passed parameter. Therefore, we first add an install
method to each component, and then export the entire component library. We modify button/index.ts
as follows:
import _Button from "./button.vue";
import type { App, Plugin } from "vue";
type SFCWithInstall<T> = T & Plugin;
const withInstall = <T>(comp: T) => {
(comp as SFCWithInstall<T>).install = (app: App) => {
const name = (comp as any).name;
// register component
app.component(name, comp as SFCWithInstall<T>);
};
return comp as SFCWithInstall<T>;
};
export const Button = withInstall(_Button);
export default Button;
Modify components/index.ts
to the following:
import * as components from "./src/index";
export * from "./src/index";
import { App } from "vue";
export default {
install: (app: App) => {
for (let c in components) {
app.use(components[c]);
}
},
};
At this point, we need to give button.vue
a name, such as ea-button
, so that it can be used as the component name when globally mounted.
<template>
<button>Test Button</button>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "sn-button",
setup() {
return {};
},
});
</script>
At this point, globally mount the component library in play/main.ts
.
import { createApp } from "vue";
import App from "./app.vue";
import stellarnovaui from "@stellarnovaui/components";
const app = createApp(App);
app.use(stellarnovaui);
app.mount("#app");
Use the sn-button
component in App.vue
, and you will find that the component library has been successfully mounted.
<script lang="ts" setup>
</script>
<template>
<div>
<sn-button />
</div>
</template>
However, this global component does not have any property hints, so we need to use Volar in VSCode to add hinting for the global component.
Firstly, install @vue/runtime-core
.
pnpm add @vue/runtime-core -D -w
Create components.d.ts file under the src directory.
import * as components from "./index";
declare module "@vue/runtime-core" {
export interface GlobalComponents {
EaButton: typeof components.Button;
EaIcon: typeof components.Icon;
}
}
export {};
At this point, the globally imported components also have hinting functionality.
Note: When users use the component library, they need to configure types: ["stellarnovaui/lib/src/components"]
in their tsconfig.json
to enable the hinting functionality.
"compilerOptions": {
//...
"types": ["easyest/lib/src/components"]
},
Use Setup sugar
We all know that using the setup syntax for Vue component development is very convenient. However, there is one issue: how do we name the components when using the setup syntax?
In fact, there are two solutions. One is to add another <script>
tag for naming, for example in input.vue
.
<template>
<button>Test Button</button>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "sn-button"
});
</script>
<script lang="ts" setup></script>
Obviously, this approach is not elegant.
The second solution is to use the unplugin-vue-define-options
plugin. we need to configure it in the play
project.
Firstly, install unplugin-vue-define-options
globally, as we will also need this plugin for packaging configuration later. Installing the latest version may show errors. For now, use // @ts-ignore
to ignore the errors.
pnpm add unplugin-vue-define-options -D -w
Then import this plugin in play/vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// @ts-ignore
import DefineOptions from "unplugin-vue-define-options/vite";
export default defineConfig({
plugins: [vue(), DefineOptions()],
});
Now we can use defineOptions to name component directly.
<template>
<button>Test Button</button>
</template>
<script lang="ts" setup>
defineOptions({ name: "sn-button" });
</script>
Component Development
We all know that a component needs to accept some parameters to achieve different effects. For example, the Button component needs to accept properties like type
, size
, and round
. Here, we will temporarily accept only the type
property to develop a simple Button component.
We can assign different class names to the Button component based on the passed type
.
// button.vue
<template>
<button class="sn-button" :class="buttonStyle"><slot /></button>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "sn-button",
});
</script>
<script lang="ts" setup>
import "../style/index.less";
import { computed } from "vue";
type ButtonProps = {
type?: string;
};
const buttonProps = defineProps<ButtonProps>();
const buttonStyle = computed(() => {
return { [`sn-button--${buttonProps.type}`]: buttonProps.type };
});
</script>
Here, we create a style file. Create a style
folder under the button
directory to store the styles for the Button component.
button/style/index.less
.sn-button {
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #fff;
border: 1px solid #dcdfe6;
color: #606266;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: none;
margin: 0;
transition: 0.1s;
font-weight: 500;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px;
}
.sn-button.sn-button--primary {
color: #fff;
background-color: #409eff;
border-color: #409eff;
&:hover {
background: #66b1ff;
border-color: #66b1ff;
color: #fff;
}
}
now change app.vue in play project:
<script lang="ts" setup>
</script>
<template>
<div>
<sn-button type="primary">Test Button</sn-button>
</div>
</template>
Since component development may involve a lot of content, we won't go into detail here. Instead, we'll briefly introduce the general approach to component development. In the future, we will specifically develop some commonly used components.
Top comments (0)