Vue3.0
已经发布了很长一段时间了,最近也在想着怎么去做工程化的东西,于是就想自己弄一个组件库捎带深入学习Vue3.0
,于是便有了今天的这篇文章。
Git地址:https://github.com/ZhQuella/AzUIFront
技术栈
包管理工具:
开发模块:
打包工具:
单元测试:
语法工具:
语言:
为什么要使用Yarn
看到包管理工具使用的事yarn
或许很多小伙伴不太能理解为什么要使用yarn
,说到这里可能就需要了解一下前端的工程化的概念了,什么是前端的工程化?工程化分别为模块化
、组件化
、规范化
和自动化
。
模块化
模块化
是指将一个文件拆分成多个相互依赖的文件,最后进行统一的打包和加载,这样能够很好的保证高效的多人协作。 JavaScript
模块化:CommonJS
、AMD
、CMD
以及ES6 Module
。 CSS
模块化:Sass
、Less
、Stylus
、BEM
、CssModules
等。其中预处理器和 BEM 都会有的一个问题就是样式覆盖。 资源模块化
:任何资源都能以模块的形式进行加载,目前大部分项目中的文件、CSS、图片等都能直接通过 JS 做统一的依赖关系处理
组件化
不同于模块化,模块化是对文件、对代码和资源拆分,而组件化则是对UI层面
的拆分。
组件化并不是前端所特有的,一些其他的语言或者桌面程序等,都具有组件化的先例。确切的说,只要有UI层的展示,就必定有可以组件化的地方。简单来说,组件就是将一段UI样式和其对应的功能作为独立的整体去看待,无论这个整体放在哪里去使用,它都具有一样的功能和样式,从而实现复用,这种整体化的细想就是组件化。不难看出,组件化设计就是为了增加复用性,灵活性,提高系统设计,从而提高开发效率。
组件系统是一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。我们会需要对页面进行拆分,将其拆分成一个一个的零件,然后分别去实现这一个个零件,最后再进行组装。
组件化的抽象设计是很重要的,不仅增加了复用性提高了工作效率,从某种程度上来说也反应了程序员对业务和产品设计的理解,一旦有问题或者需要功能扩展时,你就会发现之前的设计是多么的有意义。
规范化
规范化指的是我们在工程开发初期以及开发期间制定的系列规范,其中又包含了:
- 项目目录结构
- 对于编码这块的约束,一般我们都会采用一些强制措施,比如
ESLint
、StyleLint
等。 - 联调规范
- 文件命名规范
- 样式管理规范:目前流行的样式管理有
BEM
、Sass
、Less
、Stylus
、CSS Modules
等方式 - 工作流:其中包含分支命名规范、代码合并规范等
- 定期代码审查
自动化
从最早先的grunt
、gulp
等,再到目前的webpack
、parcel
。这些自动化工具在自动化合并、构建、打包都能为我们节省很多工作。而这些只是前端自动化其中的一部分,前端自动化还包含了持续集成、自动化测试等方方面面。
想必大家对与工程化也有了一定的了解,那么会过头来继续说为什么要使用yarn
,上面说到了模块化
如果我们想一下,组件的开发与组件文档的展示和组件库的本身是存在一切关联的,但是关联性并没有那么大,可以使用yarn
的工作空间来分隔他们使他们本事作为一个独立的个体而存在,yarn
工作流与npm
类似都是使用package.json
文件,yarn
会重组node_modules
文件,且不会与npm
冲突。yarn
的工作空间能够更好的帮助我们管理多个项目,可以在多个子项目中使用独立的package.json
管理项目依赖。yarn
会根据就依赖关系帮助你分析所有子项目的共用依赖,保证所有的项目公用的依赖只会被下载和安装一次。
初始化项目
初始化创建项目:
mkdir AzUiFont
cd AzUiFont
yarn init -y
创建.gitignore
node_modules
coverage
dist
es
lib
package-lock.json
修改package.json
{
"name": "az-ui-font",
"private": true,
"version": "0.0.1",
"main": "index.js",
"license": "MIT"
}
搭建初始目录结构:
├─packages // 项目总目录
│ ├─azui // 组件库目录
│ ├─docs // 展示文档目录
│ └─play // 组件库开发展示目录
├─script // 运行脚本
├─.gitignore
├─index.html
├─package.json
├─README.md
└─tsconfig.json
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AzUIFront</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
tsconfig.json
{
"compilerOptions": {
"rootDir": ".",
"sourceMap": false,
"target": "esnext",
"module": "esnext",
"jsx": "preserve",
"moduleResolution": "node",
"strictNullChecks": true,
"noUnusedLocals": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"lib": ["esnext", "dom"],
"types": ["jest", "node"]
}
}
代码规范约束
这里使用eslint
对代码规范进行约束,安装如下工具:
yarn add eslint -D -W
yarn add eslint-formatter-pretty -D -W
yarn add eslint-plugin-json -D -W
yarn add eslint-plugin-prettier -D -W
yarn add eslint-plugin-vue -D -W
yarn add @vue/eslint-config-prettier -D -W
yarn add babel-eslint -D -W
yarn add prettier -D -W
添加.eslintrc.js
module.exports = {
"root": true,
"env": {
"browser": true,
"es2020": true,
"node": true,
"jest": true
},
"globals": {
"ga": true,
"chrome": true,
"__DEV__": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
'plugin:json/recommended',
'plugin:vue/vue3-essential',
'@vue/prettier'
],
"parserOptions": {
"ecmaVersion": 12,
"parser": "@typescript-eslint/parser",
"sourceType": "module"
},
"plugins": [
"vue",
"@typescript-eslint"
],
"rules": {
}
};
添加.eslintignore
*.sh
*.md
*.ico
*.css
*.md
*.scss
*.woff
*.ttf
*/*/shims.d.ts
*/*/index.html
*/*/yarn-error.log
packages/azui/rollup-plugin-vue/index.d.ts
node_modules
lib
coverage
dist
在package.json
添加如下命令:
package.json
{
"scripts": {
"lint": "eslint --no-error-on-unmatched-pattern --ext .vue --ext .ts --ext .tsx packages/**/ --fix"
},
"devDependencies": {
"eslint": "^7.24.0",
"eslint-config-prettier": "^8.3.0",
"eslint-formatter-pretty": "^4.0.0",
"eslint-plugin-jest": "^24.3.5",
"eslint-plugin-json": "^2.1.2",
"eslint-plugin-prettier": "^3.4.0",
"prettier": "^2.2.1"
}
}
代码约束部分已经处理完成,接下来处理组件部分
组件库部分
执行命令
cd .\packages\azui\
yarn init -y
package.json
{
"name": "azui",
"version": "0.0.1",
"private": true,
"license": "MIT"
}
目录结构如下
├─rollup-plugin-vue
└─src
├─packages
│ └─Button
│ ├─Button.vue
│ ├─index.ts
│ └─__tests__
└─styles
集成Babel
到项目中,需要安装如下依赖:
yarn add babel -D -W
yarn add babel-plugin-syntax-dynamic-import -D -W
yarn add babel-plugin-syntax-jsx -D -W
yarn add babel-preset-env -D -W
yarn add @babel/plugin-proposal-optional-chaining -D -W
yarn add @babel/preset-env -D -W
yarn add @vue/babel-plugin-jsx -D -W
添加.babelrc
{
"presets": [["@babel/preset-env", { "targets": { "node": "current" } }]],
"plugins": [
"syntax-dynamic-import",
["@vue/babel-plugin-jsx"],
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator"
],
"env": {
"utils": {
"presets": [
[
"env",
{
"loose": true,
"modules": "commonjs",
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}
]
],
"plugins": [
[
"module-resolver",
{
"root": ["azui"],
"alias": {
"azui/src": "az-ui/lib"
}
}
]
]
},
"test": {
"plugins": ["istanbul"],
"presets": [["env", { "targets": { "node": "current" } }]]
},
"esm": {
"presets": [["@babel/preset-env", { "modules": false }]]
}
}
}
集成自动化测试(单元测试)
yarn add jest -D -W
yarn add vue-jest@5.0.0-alpha.5 -D -W
yarn add babel-jest -D -W
yarn add @vue/compiler-sfc@3.0.2 -D -W
yarn add @vue/test-utils@next -D -W
yarn add typescript -D -W
添加jest.config.js
module.exports = {
testEnvironment: "jsdom", // 默认JSdom
roots: ["<rootDir>/src/packages"], //
transform: {
"^.+\\.vue$": "vue-jest", // vue单文件
"^.+\\js$": "babel-jest", // esm最新语法 import
},
moduleFileExtensions: ["vue", "js", "json", "jsx", "ts", "tsx", "node"],
testMatch: ["**/__tests__/**/*.spec.js"],
// 别名
moduleNameMapper: {
"^azui(.*)$": "<rootDir>$1",
"^main(.*)$": "<rootDir>/src$1",
},
};
package.json
{
"scripts": {
"test": "jest --runInBand"
}
}
样式打包,添加如下依赖:
yarn add gulp -D -W
yarn add gulp-autoprefixer -D -W
yarn add gulp-sass -D -W
yarn add gulp-cssmin -D -W
yarn add cp-cli -D -W
yarn add tslib -D -W
package.json
{
"scripts": {
"build:theme": "gulp build --gulpfile gulpfile.js"
}
}
使用Rollup打包组件,安装如下依赖:
yarn add rollup -D -W
yarn add rollup-plugin-peer-deps-external -D -W
yarn add rollup-plugin-scss -D -W
yarn add rollup-plugin-terser -D -W
yarn add rollup-plugin-vue -D -W
yarn add @rollup/plugin-node-resolve -D -W
yarn add @rollup/plugin-commonjs -D -W
yarn add @rollup/plugin-json -D -W
yarn add @rollup/plugin-replace -D -W
yarn add @rollup/plugin-babel -D -W
yarn add rollup-plugin-vue -D -W
yarn add rollup-plugin-typescript2 -D -W
添加rollup.config.js
import path from "path";
import scss from "rollup-plugin-scss";
import peerDepsExternal from "rollup-plugin-peer-deps-external";
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import json from "@rollup/plugin-json";
import replace from "@rollup/plugin-replace";
import babel from "@rollup/plugin-babel";
import { terser } from "rollup-plugin-terser";
import ts from "rollup-plugin-typescript2";
import pkg from "./package.json";
const vuePlugin = require("./rollup-plugin-vue/index");
const getPath = (_path) => path.resolve(__dirname, _path);
const name = "AzUi";
const createBanner = () => {
return `/*!
* ${pkg.name} v${pkg.version}
* (c) ${new Date().getFullYear()} Aaron
* @license MIT
*/`;
};
const extensions = [".js", ".ts", ".tsx", ".scss"];
const tsPlugin = ts({
tsconfig: getPath("./tsconfig.json"),
extensions,
});
const createBaseConfig = () => {
return {
input: "src/index.ts",
external: ["vue"],
plugins: [
peerDepsExternal(),
babel(),
resolve({
extensions,
}),
commonjs(),
json(),
tsPlugin,
vuePlugin({
css: true
}),
scss({
output: process.env.NODE_ENV === 'development'?
'./dist/lib/index.css':
false,
watch: ["./src/styles"]
})
],
output: {
sourcemap: false,
banner: createBanner(),
externalLiveBindings: false,
globals: {
vue: "Vue"
}
}
};
};
function mergeConfig(baseConfig, configB) {
const config = Object.assign({}, baseConfig);
// plugin
if (configB.plugins) {
baseConfig.plugins.push(...configB.plugins);
}
// output
config.output = Object.assign({}, baseConfig.output, configB.output);
return config;
}
function createFileName(formatName) {
return `dist/az-ui.${formatName}.js`;
}
// es-bundle
const esBundleConfig = {
plugins: [
replace({
__DEV__: `(process.env.NODE_ENV !== 'production')`,
}),
],
output: {
file: createFileName("esm-bundler"),
format: "es",
},
};
// es-browser
const esBrowserConfig = {
plugins: [
replace({
__DEV__: true,
}),
],
output: {
file: createFileName("esm-browser"),
format: "es",
},
};
// es-browser.prod
const esBrowserProdConfig = {
plugins: [
terser(),
replace({
__DEV__: false,
}),
],
output: {
file: createFileName("esm-browser.prod"),
format: "es",
},
};
// cjs
const cjsConfig = {
plugins: [
replace({
__DEV__: true,
}),
],
output: {
file: createFileName("cjs"),
format: "cjs",
},
};
// cjs.prod
const cjsProdConfig = {
plugins: [
terser(),
replace({
__DEV__: false,
}),
],
output: {
file: createFileName("cjs.prod"),
format: "cjs",
},
};
// global
const globalConfig = {
plugins: [
replace({
__DEV__: true,
"process.env.NODE_ENV": true,
}),
],
output: {
file: createFileName("global"),
format: "iife",
name
},
};
// global.prod
const globalProdConfig = {
plugins: [
terser(),
replace({
__DEV__: false,
}),
],
output: {
file: createFileName("global.prod"),
format: "iife",
name
},
};
const formatConfigs = [
esBundleConfig,
esBrowserProdConfig,
esBrowserConfig,
cjsConfig,
cjsProdConfig,
globalConfig,
globalProdConfig,
];
function createPackageConfigs() {
return formatConfigs.map((formatConfig) => {
return mergeConfig(createBaseConfig(), formatConfig);
});
}
export default createPackageConfigs();
package.json
{
"scripts": {
"build": "rollup -c"
}
}
组件库部分已经完成,接下来配置文档部分:
文档部分
执行命令
cd ../..
cd .\packages\docs\
创建目录结构:
├─public
├─scripts
└─src
├─assets
├─components
└─__docs__
安装如下依赖:
yarn add @vitejs/plugin-vue -D -w
yarn add markdown-it-containe -D -w
yarn add node-sass -D -w
yarn add sass -D -w
yarn add sass-loader -D -w
yarn add vite -D -w
yarn add vite-plugin-vuedoc -D -w
yarn add vue@next -S -W
yarn add vue-router@4 -S -W
package.json
{
"name": "@azui/docs",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "tsrv test"
},
"dependencies": {
"azui": "0.0.1",
"vue": "^3.0.7",
"vue-router": "^4.0.4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.1.5",
"markdown-it-container": "^3.0.0",
"node-sass": "^5.0.0",
"sass": "^1.32.11",
"sass-loader": "^11.0.1",
"vite": "^2.0.5",
"vite-plugin-vuedoc": "^3.1.2"
}
}
添加vite.config.ts
import { defineConfig } from "vite";
import { createPlugin, vueDocFiles } from "vite-plugin-vuedoc";
import markdownItContainer from "markdown-it-container";
import vue from "@vitejs/plugin-vue";
import vitePluginSyncmd from "./scripts/vitePluginSyncmd";
const containers = ["success", "warning", "info", "error"].map((type) => {
return [
markdownItContainer,
type,
{
validate: function (params: string) {
const str = params.trim();
if (str === type || str.startsWith(`${type} `)) {
return [str, str === type ? "" : str.substr(type.length + 1)];
}
return null;
},
render: function (tokens: any[], idx: number) {
const str = tokens[idx].info.trim();
const m = [str, str === type ? "" : str.substr(type.length + 1)];
if (tokens[idx].nesting === 1) {
// opening tag
return `<p>${type}--${m[1]}`;
} else {
// closing tag
return "</p>";
}
},
},
];
});
export default defineConfig({
server: {
port: 3000,
},
assetsInclude: ["src/assets"],
optimizeDeps: {
exclude: ["azui"],
},
plugins: [
vitePluginSyncmd(),
createPlugin({
markdownIt: {
plugins: [...containers],
},
highlight: {
theme: "one-dark",
},
}),
vue({
include: [...vueDocFiles],
}),
],
});
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"lib": ["esnext", "dom"],
"types": ["vite/client"],
"baseUrl": "."
},
"include": ["./shims.d.ts", "src/**/*"],
"exclude": ["node_modules", "dist"]
}
shims.d.ts
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
declare module '*.md' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/src/assets/icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
scripts/vitePluginSyncmd.ts
import { Plugin } from "vite";
import chokidar from "chokidar";
import path from "path";
import fs from "fs-extra";
function docFileName(path: string) {
const ret = path.split("/__docs__/");
if (ret.length === 2) {
return ret;
}
return [null, null];
}
function syncdocServer({ root }) {
const componentsDir = path.join(root, "../elenext/src/components");
const docsPath = (file) => path.join(root, "src/__docs__", file);
const watcher = chokidar.watch(`${componentsDir}/**/__docs__/*.md`);
watcher
.on("add", async (path) => {
const [, file] = docFileName(path);
if (file) {
try {
await fs.copy(path, docsPath(file));
} catch (err) {
console.error(err);
}
}
})
.on("change", async (path) => {
const [, file] = docFileName(path);
if (file) {
try {
await fs.copy(path, docsPath(file));
} catch (err) {
console.error(err);
}
}
})
.on("unlink", async (path) => {
const [, file] = docFileName(path);
if (file) {
try {
await fs.remove(docsPath(file));
} catch (err) {
console.error(err);
}
}
});
}
function vitePluginSyncmd(): Plugin {
return {
name: "Syncmd",
configureServer(server) {
syncdocServer({ root: server.config.root });
},
};
}
export default vitePluginSyncmd;
src/__docs__/Button.zh-CN.md
---
title: Button
wrapperClass: md-button
---
# Button 按钮
常用的操作按钮
## 按钮颜色
使用`color`属性来定义 Button 的颜色
`color`: 'primary' | 'success' | 'info' | 'warning' | 'danger'
## Button Props
| 参数 | 说明 | 类型 | 默认值 |
| ----------- | -------------- | ---------------------------------------------------------------- | ------ |
| color | 类型 | 'primary' / 'success' / 'warning' / 'danger' / 'info' / 'string' | - |
components/AppLayout.vue
<template>
<div class="demo-layout">
<div>
<div class="demo-header">
<div class="layout-center">
<div align="middle">
<div :flex="1">
<Logo />
</div>
<div>
<div mode="horizontal">
<div>
<input v-model="data.primaryColor" />
</div>
<div>
<a href="https://github.com/JasKang/elenext" target="__blank">GitHub</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<div class="layout-center">
<div align="top" :wrap="false">
<div :flex="'200px'">
<div style="padding-top: 40px">
<div mode="vertical" :current-path="route.path">
<template v-for="menu in menus" :key="menu.title">
<div :title="menu.title">
<div v-for="item in menu.items" :key="item" :path="`/${item.name.toLowerCase()}`">
{{ `${item.name}-Aaron` }}
</div>
</div>
</template>
</div>
</div>
</div>
<div :flex="1">
<div class="site-content">
<router-view />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive } from 'vue'
import { useRoute } from 'vue-router'
import menus from '../menus'
export default defineComponent({
name: 'AppLayout',
setup() {
const route = useRoute()
const data = reactive({
primaryColor: '#409eff',
})
return {
route,
menus,
data,
}
},
})
</script>
<style lang="scss">
.demo-layout {
height: 100vh;
}
.layout-center {
max-width: 1200px;
width: 100vw;
margin: 0 auto;
}
.site-content {
width: 100%;
padding: 20px;
// max-width: 900px;
margin: 0 auto;
}
.demo-aside {
border-right: solid 1px #e6e6e6;
}
.demo-header {
border-bottom: solid 1px #e6e6e6;
// box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
</style>
App.vue
<template>
<div class="box">111</div>
<router-view />
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'App',
components: {}
})
</script>
main.ts
import 'vite-plugin-vuedoc/style.css';
import { createApp } from 'vue';
import AzUI from 'azui';
import 'azui/dist/lib/index.css';
import { router } from './router';
import App from './App.vue';
const app = createApp(App);
app.use(AzUI);
app.use(router);
app.mount('#app');
menus.ts
import { Component, defineAsyncComponent } from 'vue'
import Button from './__docs__/Button.zh-CN.md'
type MenuItemType = {
name: string
component: (() => Promise<Component>) | Component
}
type MenuType = {
title: string
items: MenuItemType[]
}
export default [
{
title: 'Basic',
items: [
{ name: 'Button', component: Button },
]
}
] as MenuType[]
router.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import AppLayout from './components/AppLayout.vue'
import menus from './menus'
export const router = createRouter({
history: createWebHistory(),
strict: true,
routes: [
{ path: '/'},
{
path: '/component',
name: 'Layout',
component: AppLayout,
redirect: '/button',
children: menus.reduce((prev, item) => {
const _routes = item.items.map(i => {
console.log(i.component)
return {
path: `/${i.name.toLowerCase()}`,
name: i.name,
component: i.component,
}
})
prev.push(..._routes)
return prev
}, [] as RouteRecordRaw[]),
},
],
})
文档部分完成,接下来处理组件开发部分,其实组件开发部分可以和文档部分放在一起,使用不同的路由,但是两者的功能不相同,所以单独配置了一个子项目。
开发部分
执行如下命令:
cd ../..
cd .\packages\play\
目录结构:
├─public
├─scripts
├─index.html
└─src
├─App.vue
└─main.ts
package.json,相关依赖自行安装
{
"name": "@azui/play",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "tsrv test"
},
"dependencies": {
"azui": "0.0.1",
"vue": "^3.0.7"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.1.5",
"node-sass": "^5.0.0",
"sass": "^1.32.11",
"sass-loader": "^11.0.1",
"vite": "^2.0.5"
}
}
shims.d.ts
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
declare module '*.md' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"lib": ["esnext", "dom"],
"types": ["vite/client"],
"baseUrl": "."
},
"include": ["./shims.d.ts", "src/**/*"],
"exclude": ["node_modules", "dist"]
}
vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
server: {
port: 8080,
},
optimizeDeps: {
exclude: ["azui"],
},
plugins: [
vue()
]
});
这样的话组件开发部分也就配置好了,现在就是启动项目的时候,返回到根目录,在根目录的package.json
中添加scripts
:
项目启动
root package.json
{
"scripts": {
"dev": "yarn workspace azui run dev",
"start": "yarn workspace @azui/docs run dev",
"play": "yarn workspace @azui/play run dev",
"test": "yarn workspace azui run test",
"lint": "eslint --no-error-on-unmatched-pattern --ext .vue --ext .ts --ext .tsx packages/**/ --fix"
}
}
需要注意的是,一定要先运行yarn dev
根据dev
来运行打包出开发环境所需要的组件,之后再运行start
或者play
,记的有两个命令行窗口运行哦,如果直接运行start
和play
会抛出错误:
[plugin:vite:import-analysis]
Failed to resolve entry for package "azui".
The package may have incorrect main/module/exports specified in its package.json.
这是因为在项目中无法找到所依赖的组件库~这一点需要特殊注意。
结束语
要搭建一个相对完善的组件库,都是需要经过一系列项目的沉淀的。目前而言,组件库的开发流程上依然会存在一些问题,比如版本管理、升级回退等。时间是最好的老师,相信在后面的迭代中,我们依然能够保持初心与热情,积极探索与发现,构建出更加完善的前端工程体系。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。