14

Vue3.0 has been released for a long time, and recently I was thinking about how to do engineering things, so I wanted to get a component library by myself to learn more about Vue3.0 , so I have today’s article.

Git address: https://github.com/ZhQuella/AzUIFront

Technology stack

Package management tools:

yarn

Development module:

Vite
Vue 3.0

Packaging tools:

Gulp
Rollup

unit test:

Jest

Grammar tools:

ESLint

Language:

TypeScript

Why use Yarn

Seeing the use of package management tools yarn perhaps many friends do not understand why yarn , you may need to understand the concept of front-end engineering. What is front-end engineering? The engineering is modularization, componentization, standardization and automation.

Modular

Modularization refers to splitting a file into multiple interdependent files, and finally performing unified packaging and loading, which can ensure efficient multi-person collaboration.
JavaScript modular: CommonJS , AMD , CMD and ES6 Module .
CSS modularity: Sass , Less , Stylus , BEM , CssModules etc. One of the problems that both the preprocessor and BEM have is style coverage.
Resource Modularization: Any resource can be loaded in the form of a module. At present, most of the files, CSS, pictures, etc. in the project can be directly processed through JS for unified dependency processing

Componentization

Different from modularization, modularization is the separation of files, codes and resources, while componentization is the UI level.
Componentization is not unique to the front end. Some other languages or desktop programs have precedents for componentization. To be precise, as long as there is a display of the UI layer, there must be a place for componentization. Simply put, a component is to treat a piece of UI style and its corresponding function as an independent whole. No matter where the whole is used, it has the same function and style, so as to achieve reuse. This integrated Thinking about it is componentization. It is not difficult to see that component design is to increase reusability, flexibility, and improve system design, thereby improving development efficiency.
The component system is an important concept because it is an abstraction that allows us to build large applications using small, independent, and usually reusable components. We will need to split the page, split it into parts one by one, and then realize the parts one by one, and finally assemble it.
Component-based abstract design is very important. It not only increases reusability and improves work efficiency, but also reflects the programmer’s understanding of business and product design to a certain extent. Once there is a problem or need to expand the function, you You will discover how meaningful the previous design is.

Normalization

Standardization refers to the series of specifications that we formulated in the early stage and during the development of the project, which also includes:

  1. Project directory structure
  2. Regarding the constraints of coding, generally we will adopt some mandatory measures, such as ESLint , StyleLint etc.
  3. Joint Commissioning Specifications
  4. File naming convention
  5. Style management standard: currently popular style management has BEM , Sass , Less , Stylus , CSS Modules etc.
  6. Workflow: It includes branch naming specifications, code merging specifications, etc.
  7. Regular code review
automation

From the earliest grunt , gulp etc., to the current webpack , parcel . These automated tools can save us a lot of work in automating merging, building, and packaging. And these are only part of front-end automation, front-end automation also includes continuous integration, automated testing and other aspects.


I think everyone has a certain understanding of engineering, so I will go yarn and continue to talk about why 06089787a8e6af should be used. The above mentioned modularization. If we think about it, there is everything in the development of components, the display of component documents and the component library itself. Related, but the relationship is not so big, you can use the yarn to separate them so that they can exist as an independent individual. The yarn workflow is npm using the package.json file, and yarn will reorganize the node_modules file, and Will not conflict npm yarn can better help us manage multiple projects. Independent package.json can be used in multiple sub-projects to manage project dependencies. yarn will help you analyze the common dependencies of all sub-projects based on the dependencies, and ensure that all common dependencies of the projects will only be downloaded and installed once.

Initialize the project

Initially create the project:

mkdir AzUiFont
cd AzUiFont
yarn init -y

Create .gitignore

node_modules
coverage
dist
es
lib

package-lock.json

Modify package.json

{
  "name": "az-ui-font",
  "private": true,
  "version": "0.0.1",
  "main": "index.js",
  "license": "MIT"
}

Build the initial directory structure:

├─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"]
  }
}

Code specification constraints

Here, eslint is used to constrain the code specification, and the following tools are installed:

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

Add .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": {
    }
};

Add .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

Add the following command in 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"
  }
}

The code constraint part has been processed, and the component part will be processed next

Component library section

Excuting an order

cd .\packages\azui\
yarn init -y

package.json

{
  "name": "azui",
  "version": "0.0.1",
  "private": true,
  "license": "MIT"
}

The directory structure is as follows

├─rollup-plugin-vue
└─src
    ├─packages
    │  └─Button
    │      ├─Button.vue
    │      ├─index.ts
    │      └─__tests__
    └─styles

To integrate Babel into the project, the following dependencies need to be installed:

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

Add .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 }]]
    }
  }
}

Integrated automated testing (unit testing)

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

Add 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"
  }
}

Style packaging, add the following dependencies:

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"
  }
}

Use Rollup to package components and install the following dependencies:

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

Add 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"
  }
}

The component library part has been completed, and then configure the document part:

Documentation section

Excuting an order

cd ../..
cd .\packages\docs\

Create the directory structure:

├─public
├─scripts
└─src
    ├─assets
    ├─components
    └─__docs__

Install the following dependencies:

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"
  }
}

Add 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[]),
    },
  ],
})

The document part is completed, and then the component development part is processed. In fact, the component development part can be put together with the document part and use different routes, but the functions of the two are different, so a sub-project is configured separately.

Development part

Execute the following commands:

cd ../..
cd .\packages\play\

Directory Structure:

├─public
├─scripts
├─index.html

└─src
    ├─App.vue
    └─main.ts

package.json, related dependencies are installed by themselves

{
  "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()
  ]
});

In this case part of the component development also configured, now is the time to start the project returns to the root directory in the root directory package.json added in scripts :

Project begining

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"
  }
}

It should be noted that you must first run yarn dev according to dev to run the components required for the development environment, and then run start or play , remember that there are two command line windows to run, if you run start and play directly, it will throw error:

[plugin:vite:import-analysis] 
Failed to resolve entry for package "azui". 
The package may have incorrect main/module/exports specified in its package.json.

This is because the dependent component library cannot be found in the project~ this requires special attention.

Concluding remarks

To build a relatively complete component library, it needs to go through a series of projects. At present, there are still some problems in the development process of the component library, such as version management, upgrade rollback, etc. Time is the best teacher. I believe that in the subsequent iterations, we can still maintain our original intention and enthusiasm, actively explore and discover, and build a more complete front-end engineering system.


Aaron
4k 声望6.1k 粉丝

Easy life, happy elimination of bugs.