动机

当市面上支流的组件库不能满足咱们业务需要的时候,那么咱们就有必要开发一套属于本人团队的组件库。

环境

开发环境:

  • vue 3.0
  • vue/cli 4.5.13
  • nodeJs 12.16.3
  • npm 6.14.4

步骤

创立我的项目

应用 vue-cli 创立一个 vue3 我的项目,假如我的项目名为 custom-npm-ui

$ vue create custom-npm-ui

手动抉择设置。

布局目录

├─ build         // 打包脚本,用于寄存打包配置文件│  ├─ rollup.config.js    ├─ examples      // 原 src 目录,改成 examples 用于示例展现│  ├─ App.vue│  ├─ main.ts├─ packages      // 新增 packages 目录,用于编写寄存组件,如button│  ├─ SubmitForm│  │  ├─ src/│  │  ├─ index.ts│  ├─ index.ts├─ typings      // 新增 typings 目录, 用于寄存 .d.ts 文件,把 shims-vue.d.ts 挪动到这里│  ├─ shims-vue.d.ts├─ .npmignore    // 新增 .npmignore 配置文件├─ vue.config.js // 新增 vue.config.js 配置文件

src 目录改为 examples ,并将外面的 assetscomponents 目录删除,移除 App.vue 里的组件援用。

我的项目配置

vue.config.js

新增 vue.config.js 配置文件,适配从新布局后的我的项目目录:

// eslint-disable-next-line @typescript-eslint/no-var-requiresconst path = require('path')module.exports = {  // 批改 pages 入口  pages: {    index: {      entry: "examples/main.ts", //入口      template: "public/index.html", //模板      filename: "index.html" //输入文件    }  },  // 扩大 webpack 配置  chainWebpack: (config) => {    // 新增一个 ~ 指向 packages 目录, 不便示例代码中应用    config.resolve.alias      .set('~', path.resolve('packages'))  }}

.npmignore

新增 .npmignore 配置文件,组件公布到 npm中,只有编译后的公布目录(例如lib)、package.jsonREADME.md才是须要被公布的,所以咱们须要设置疏忽目录和文件

# 疏忽目录.idea.vscodebuild/docs/examples/packages/public/node_modules/typings/# 疏忽指定文件babel.config.jstsconfig.jsontslint.jsonvue.config.js.gitignore.browserslistrc*.map

或者配置pkg#files:

  "files": [    "lib/",    "package.json",    "README.md"  ],

装置依赖后的目录构造:

└─custom-npm-ui        │  package.json        │  README.md        └─lib                index.css                index.esm.js                index.min.js

tsconfig.json

批改 tsconfig.json 中 paths 的门路

"paths": {  "@/*": [    "src/*"  ]}

改为

    "paths": {      "~/*": [        "packages/*"      ]    }

Notes:typescript反对的别名。

批改 include 的门路

  "include": [    "src/**/*.ts",    "src/**/*.tsx",    "src/**/*.vue",    "tests/**/*.ts",    "tests/**/*.tsx"  ]

改为

  "include": [    "examples/**/*.ts",    "examples/**/*.tsx",    "examples/**/*.vue",    "packages/**/*.ts",    "packages/**/*.tsx",    "packages/**/*.vue",    "typings/**/*.ts",    "typings/shims-vue.d.ts",    "tests/**/*.ts",    "tests/**/*.tsx"  ]

package.json

批改 package.json 中公布到 npm 的字段

  • name:包名,该名字是惟一的。可在npm近程源搜寻名字,如果存在则需换个名字。
  • version:版本号,每次公布至 npm 须要批改版本号,不能和历史版本号雷同。
  • description:形容。
  • main:入口文件,该字段需指向咱们最终编译后的包文件。
  • typings:types文件,TS组件须要。
  • keyword:关键字,以空格拆散心愿用户最终搜寻的词。
  • author:作者信息
  • private:是否公有,须要批改为 false 能力公布到 npm
  • license: 开源协定

参考设置:

{  "name": "custom-npm-ui",  "version": "0.1.0",  "private": false,  "description": "基于ElementPlus二次开发的前端组件库",  "main": "lib/index.min.js",  "module": "lib/index.esm.js",  "typings": "lib/index.d.ts",  "keyword": "vue3 element-plus",  "license": "MIT",  "author": {    "name": "yourname",    "email": "youremail@163.com"  } }

在 package.json 的 scripts 新增编译和公布的命令

"scripts": {    "build": "yarn build:clean && yarn build:lib && yarn build:esm-bundle && rimraf lib/demo.html",    "build:clean": "rimraf lib",    "build:lib": "vue-cli-service build --target lib --name index --dest lib packages/index.ts",    "build:esm-bundle": "rollup --config ./build/rollup.config.js"}

其中 build:lib 是利用 vue-cli 进行 umd 形式打包,build:esm-bundle 是利用 rollup 进行 es 形式打包。

build:lib具体参数解析如下:

  • --target: 构建指标,默认为利用模式。改为 lib 启用库模式。
  • --name: 输入文件名
  • --dest : 输入目录,默认 dist。改成 lib
  • [entry]: 入口文件门路,默认为 src/App.vue。这里咱们指定编译 packages/ 组件库目录。

build:esm-bundle打包后的资源在webpack2+rollup环境中可通过pkg#module配置载入应用。

rollup.config.js

新增 build/rollup.config.jsrollup 打包脚本:

import cjs from "@rollup/plugin-commonjs"; // commonjs转es module —— rollup只反对es moduleimport resolve from "@rollup/plugin-node-resolve"; // 搭配@rollup/plugin-commonjs应用// import ts from '@rollup/plugin-typescript' // 【报错】应用ts报错import typescript from "rollup-plugin-typescript2"; // 解析TS语法import vue from "rollup-plugin-vue"; // 解析vueimport babel from "@rollup/plugin-babel";import scss from "rollup-plugin-scss"; // 解析scss// import requireContext from "rollup-plugin-require-context"; // 【不可用】反对webpack的require.context API —— 须要装置npm install --save-dev generate-source-map@0.0.5import { writeFileSync, existsSync, mkdirSync } from "fs";const extensions = [".js", ".ts", ".vue"];export default {  input: "packages/index.ts",  output: [    {      file: "lib/index.esm.js", // 多文件输入的话,须要应用dir代替file      format: "es",      globals: {        vue: "Vue", // 通知rollup全局变量Vue即是vue      },    },  ],  extensions,  plugins: [ // 程序很重要    scss({      output: function (styles, styleNodes) {        if (!existsSync("lib/")) {          mkdirSync("lib/");        }        writeFileSync("lib/index.css", styles);      },    }),    vue({      compileTemplate: true,    }),    // requireContext(),    resolve({      jsnext: true,      main: true,      browser: true,      extensions,    }),    cjs(),    typescript(),    babel({}),  ],  external: ["vue", "element-plus"],};

开发组件

注意事项

  • 组件内不能应用懒加载
  • 组件不能应用require.context()对立治理
  • 不反对JSX语法编写模版 —— 更好的抉择React

依赖装置

环境依赖

$ npm i -D rimraf rollup @rollup/plugin-commonjs @rollup/plugin-node-resolve rollup-plugin-typescript2 rollup-plugin-vue @rollup/plugin-babel rollup-plugin-scss

开发依赖

$ npm i -D element-plus@1.0.2-beta.69 babel-plugin-import

配置.babel.config.js

module.exports = {  presets: ["@vue/cli-plugin-babel/preset"],  plugins: [    [      "import",      {        libraryName: "element-plus",      },    ],  ],};

更新examples/main.ts

import { createApp } from "vue";import App from "./App.vue";import "element-plus/lib/theme-chalk/index.css";createApp(App).mount("#app");

编写组件

在 packages 目录下新建 index.ts 文件和 SubmitForm/ 文件夹,在 SubmitForm 下新建 index.tssrc/index.vue,构造如下:

.├── SubmitForm│   ├── SubmitForm.stories.ts│   ├── index.ts│   └── src│       ├── FormRender.vue│       ├── fileds│       │   ├── Color.vue│       │   ├── Datetime.vue│       │   ├── Radio.vue│       │   ├── Select.vue│       │   ├── Switch.vue│       │   ├── Text.vue│       │   ├── Upload.vue│       │   ├── hooks│       │   │   └── useValueHandleHook.ts│       │   └── index.ts│       ├── index.vue│       ├── schemas│       │   ├── baseSchema.ts│       │   └── schemasProp.ts│       └── store│           └── index.ts├── common│   └── getType.ts└── index.ts

packages/SubmitForm/src/index.vue

<script lang="ts">import { computed, defineComponent } from "vue";import { ElForm } from "element-plus";import { FormPropsType } from "./schemas/schemasProp";import FormRender from "./FormRender.vue";import { values, updateValues } from "./store";export default defineComponent({  name: "SubmitForm",  props: FormPropsType,  emits: ["runtimeChange"],  components: {    "el-form": ElForm,    FormRender,  },  setup(props, { emit }) {    const schemasCpt = computed(() => props.schema);    const defaultValueCpt = computed(() => props.defaultValue);    function handleRuntimeChange(name: string, value: any) {      updateValues(name, value);      emit("runtimeChange", name, value);    }    return {      schemasCpt,      defaultValueCpt,      values,      handleRuntimeChange,    };  },});</script><template>  <el-form label-width="100px" :model="values">    <template v-for="(schema, idx) of schemasCpt" :key="idx">      <FormRender        :schema="schema"        :defaultValue="defaultValueCpt"        v-bind="$attrs"        :onRuntimeChange="handleRuntimeChange"      />    </template>  </el-form></template>

packages/SubmitForm/index.ts,独自组件的入口文件,在其余我的项目能够应用 import { SubmitForm } from 'custom-npm-ui' 形式进行单个组件援用

import type { App } from "vue";import SubmitForm from "./src/index.vue";// 定义 install 办法,接管 Vue 作为参数。如果应用 use 注册插件,那么所有的组件都会被注册SubmitForm.install = function (Vue: App) {  // 遍历注册全局组件  Vue.component(SubmitForm.name, SubmitForm);};export default SubmitForm;

packages/index.ts 作为组件库的入口文件,能够在其余我的项目的 main.ts 引入整个组件库,内容如下

import type { App } from "vue";import SubmitForm from "./SubmitForm";const components = [SubmitForm];// 定义 install 办法,接管 Vue 作为参数。如果应用 use 注册插件,那么所有的组件都会被注册const install = function (Vue: App): void {  // 遍历注册全局组件  components.map((component) => Vue.component(component.name, component));};export {  // 以下是具体的组件列表  SubmitForm,};export default {  // 导出的对象必须具备 install,能力被 Vue.use() 办法装置  install,};

这样,咱们就实现一个简略的 SubmitForm 组件,后续须要扩大其余组件,依照 SubmitForm 的构造进行开发,并且在 index.ts 文件中 components 组件列表增加即可。

编写示例调试

examples/main.ts

import { createApp } from "vue";import App from "./App.vue";import CustomeUI from "~/index";import "element-plus/lib/theme-chalk/index.css";createApp(App).use(CustomeUI).mount("#app");

examples/App.vue 删除我的项目初始化的 HelloWorld 组件

<script lang="ts">import { defineComponent, reactive, ref, toRaw } from "vue";import formSchema from "./layouts/case.layout";export default defineComponent({  name: "App",  components: {},  setup() {    const submitFormRef = ref();    const schema = reactive(formSchema);    const defaultValues = reactive({});    function formRuntimeChange(name: string, value: any) {      console.log(name, " = ", value);    }    function submit() {      console.log(submitFormRef.value.values);    }    return {      submitFormRef,      schema,      defaultValues,      formRuntimeChange,      submit,    };  },});</script><template>  <submit-form    ref="submitFormRef"    :schema="schema"    :defaultValue="defaultValues"    @runtimeChange="formRuntimeChange"  >  </submit-form>  <button @click="submit">保留</button></template><style lang="scss">#app {  font-family: Avenir, Helvetica, Arial, sans-serif;  -webkit-font-smoothing: antialiased;  -moz-osx-font-smoothing: grayscale;  text-align: center;  color: #2c3e50;  margin-top: 60px;}</style>

启动我的项目,测试一下

$ npm run serve

组件开发实现后,执行编译库命令:

$ npm run build

引入打包后的文件回归测试一下,没有问题再公布到 npm 仓库。

在示例入口 main.ts 援用咱们的组件库:

import { createApp } from "vue";import App from "./App.vue";import CustomeUI from "../lib/index.esm.js";import "element-plus/lib/theme-chalk/index.css";createApp(App).use(CustomeUI).mount("#app");

编写申明文件

创立目录构造

.├── typings│   ├── index.d.ts│   ├── component.d.ts│   └── packages│       └── submit-form.vue.d.ts├── common│   └── getType.ts└── index.ts

更新package.json配置

// package.json{  ...  "typings": "./typings/index.d.ts",  "files": [    "lib/",    "package.json",    "typings/"  ],  "publishConfig": {    "registry": "https://abc.com/"  }}

外围文件

// typings/index.d.tsimport type { App } from "vue";export * from "./component.d";export declare const install: (app: App, opt: any) => void;declare const _default: {  install: (app: App<any>, opt: any) => void;};export default _default;
// typings/component.d.tsexport { default as SubmitForm } from "./packages/submit-form.d";
// typings/packages/submit-form.d.tsimport {  DefineComponent,  ComponentOptionsMixin,  VNodeProps,  AllowedComponentProps,  ComponentCustomProps,  EmitsOptions,  ComputedGetter,  WritableComputedOptions,} from "vue";import { FormRowType } from "../schemas/schemasProp";declare const _default: DefineComponent<  Record<string, unknown>,  {    refName: string;    schema: FormRowType[];    defaultValue: Record<string, any>;  },  Record<string, unknown>,  Record<string, ComputedGetter<any> | WritableComputedOptions<any>>, // computed  Record<string, () => void>, // methods  ComponentOptionsMixin,  ComponentOptionsMixin,  EmitsOptions,  string,  VNodeProps & AllowedComponentProps & ComponentCustomProps,  Readonly<Record<string, unknown> & Record<string, unknown>>,  Record<string, unknown>>;export default _default;

公布组件

配置NPM仓库地址

组件开发并测试通过后,就能够公布到 npm 仓库提供给其余我的项目应用了,首先编写.npmrc文件配置要上传的源地址:

registry=https://abc.com

更举荐更新pkg#publishConfig指定仓库地址:

  "publishConfig": {    "registry": "https://abc.com"  },

Notes:应用.npmrc配置NPM仓库地址,nrm无奈切换源。

获取NPM账号、明码

在npm官网注册即可

登录npm账号

在我的项目中 terminal 命令窗口登录 npm 账号

$ npm loginUsername:Password:Email:(this IS public)

输出在 npm的账号、明码、邮箱

公布

$ npm publish

组件文档

创立Storybook敌对型环境

在我的项目中 terminal 命令窗口执行命令:

$ npx -p @storybook/cli sb init

storybook是一个能够辅助UI开发的工具,是一个UI组件的开发环境。

sb init初始化过程中,storybook会查看现有我的项目的dependencies,而后根据我的项目的现有框架,提供最佳的组装形式。

Storybook初始化做了以下步骤:

  • 装置Storybook须要的依赖
  • 更新pkg#run-script
  • 减少预设的配置文件

    • .storybook/main.js
    • .storybook/preview.js
  • 减少示例模版stories/

更新package.json

...  "scripts": {    "storybook": "start-storybook -p 6006 -h 0.0.0.0", // 启动本地服务以预览    "build-storybook": "build-storybook"  // 构建  },...
$ npm run storybook # 启动本地服务拜访storybook我的项目

更新目录构造

├─ .storybook         // 预设的配置文件│  ├─ main.js    // 入口文件│  ├─ preview.js // 管制Stories的出现、全局资源的加载├─ stories      // 示例模版
main.js
module.exports = {  "stories": [  // Storybook会抓取、载入配置门路下的指定文件渲染展现    "../stories/**/*.stories.mdx",    "../stories/**/*.stories.@(js|jsx|ts|tsx)"  ],  "addons": [  // Storybook所用插件 —— Storybook性能加强    "@storybook/addon-links",    "@storybook/addon-essentials"  ],  "framework": "@storybook/vue3" // Storybook所用框架 —— Vue环境反对}

编写示例

入口配置

更新.storybook/main.js

module.exports = {  "stories": [  // Storybook会抓取、载入配置门路下的指定文件渲染展现    "../packages/**/*.stories.@(js|jsx|ts|tsx)",    "../stories/**/*.stories.mdx",    "../stories/**/*.stories.@(js|jsx|ts|tsx)"  ],  ...}

组件Story编写

import SubmitForm from "./index"; // 引入组件import { SchemaType, RuleTrigger } from "./src/schemas/baseSchema";const caseSchema = [ // 示例数据  {    key: "moduleName",    name: "title",    type: SchemaType.Text,    label: "栏目名称",    placeholder: "请输出栏目名称",    attrs: {      //    },    rules: [      {        required: true,        message: "栏目名称必填~",        trigger: RuleTrigger.Blur,      },    ],  },  ...];export default {  title: "ui组件/SubmitForm", // 展现题目:应用门路定义命名空间 —— 分组、分类  component: SubmitForm,};const Template = (args: any) => ({ // 渲染组件  components: { SubmitForm },  setup() {    return {      ...args,    };  },  template: '<submit-form :schema="schema"></submit-form>',});export const 根本利用 = Template.bind({}); // 组件利用示例(根本利用 as any).args = {  schema: caseSchema,  ref: "submitFormRef",};

能够应用props&computed去承接args这样更合乎Vue3的书写格局:

// 后续的补充内容,和此处上下文无关。const Template = (args: any) => ({  props: Object.keys(args),  components: { SubmitForm, ElButton },  setup(props) {    const refName = computed(() => props.refName)    const submitFormRef = ref();    function submit() {      console.log(submitFormRef.value.values);    }    function onRuntimeChange(name: string, value: any) {      console.log(name, " = ", value);    }    return {      submit,      onRuntimeChange,      [refName.value]: submitFormRef,      ...props,    };  },  template: `      <submit-form :schema="schema" :ref="refName" @runtimeChange="onRuntimeChange"></submit-form>      <el-button @click="submit">提交</el-button>    `,});

全局依赖配置

因为示例代码中依赖element-plus,通过上述展示的页面没有款式,所以,StoryBook渲染须要额定引入element-plus主题:

// preview.jsimport "element-plus/lib/theme-chalk/index.css";export const parameters = {  actions: { argTypesRegex: "^on[A-Z].*" },  controls: {    matchers: {      color: /(background|color)$/i,      date: /Date$/,    },  }}

启动本地服务

更新命令脚本
  // package.json  "scripts": {    "storybook": "start-storybook -p 6006 -h 0.0.0.0",    "build-storybook": "build-storybook"  },

-h 0.0.0.0以反对局域网拜访。

执行命令
$ npm run storybook
成果展现

Stories中应用第三方UI库

ElementPlus为例:

全局配置

如果babel.config没有配置按需加载,可间接编辑.storybook/preview.js

// .storybook/preview.jsimport elementPlus from 'element-plus';import { app } from '@storybook/vue3'app.use(elementPlus);export const decorators = [  (story) => ({    components: { story, elementPlus },    template: '<elementPlus><story/></elementPlus>'  })];import "element-plus/lib/theme-chalk/index.css";export const parameters = {  actions: { argTypesRegex: "^on[A-Z].*" },  controls: {    matchers: {      color: /(background|color)$/i,      date: /Date$/,    },  }}

Notes:配置按需加载后,import elementPlus from 'element-plus';导入elementPlus报错:elementPlus is not defined —— 全局加载、按需加载不能在同一我的项目中应用。

按需加载

在须要应用ElementPlusStories中间接引入即可:

// packages/SubmitForm/SubmitForm.stories.tsimport { ElButton } from 'element-plus';import SubmitForm from "./index";import { SchemaType, RuleTrigger } from "./src/schemas/baseSchema";const caseSchema = [  {    key: "moduleName",    name: "title",    type: SchemaType.Text,    label: "栏目名称",    placeholder: "请输出栏目名称",    attrs: {      //    },    rules: [      {        required: true,        message: "栏目名称必填~",        trigger: RuleTrigger.Blur,      },    ],  },  ...];export default {  title: "ui组件/SubmitForm",  component: SubmitForm,};const Template = (args: any) => ({  components: { SubmitForm, ElButton },  setup() {    return {      ...args,    };  },  template: '<submit-form :schema="schema" ref="submitFormRef"></submit-form><el-button @click="submit">提交</el-button>',});export const 根本利用 = Template.bind({});(根本利用 as any).args = {  schema: caseSchema,};
示例代码增加交互
// packages/SubmitForm/SubmitForm.stories.tsimport { ElButton } from "element-plus";import { ref } from "vue";import SubmitForm from "./index";import { SchemaType, RuleTrigger } from "./src/schemas/baseSchema";const caseSchema = [  {    key: "moduleName",    name: "title",    type: SchemaType.Text,    label: "栏目名称",    placeholder: "请输出栏目名称",    attrs: {      //    },    rules: [      {        required: true,        message: "栏目名称必填~",        trigger: RuleTrigger.Blur,      },    ],  },  ...];export default {  title: "ui组件/SubmitForm",  component: SubmitForm,};const Template = (args: any) => ({  components: { SubmitForm, ElButton },  setup() {    const { refName } = args;    const submitFormRef = ref();    function submit() {      console.log(submitFormRef.value.values);    }    function onRuntimeChange(name: string, value: any) {      console.log(name, " = ", value);    }    return {      submit,      onRuntimeChange,      [refName]: submitFormRef,      ...args,    };  },  template: `      <submit-form :schema="schema" :ref="refName" @runtimeChange="onRuntimeChange"></submit-form>      <el-button @click="submit">提交</el-button>    `,});export const 根本利用 = Template.bind({});(根本利用 as any).args = {  refName: "submitFormRef",  schema: caseSchema,};

这里做了两件事:

  • 减少提交按钮
  • 减少数据提交交互

配置参数详情

默认文档展现

默认查看到的文档参数是以下样子:

参数配置

通过配置argTypes能够补充参数信息:

// packages/SubmitForm/SubmitForm.stories.ts...export default {  title: "ui组件/SubmitForm",  component: SubmitForm,  argTypes: {    refName: {      description: '表单组件援用',      type: {        required: true,      },      table: {        defaultValue: {          summary: 'defaultNameRef',        }      },      control: {        type: 'text'      }    },    schema: {      type: {        required: true,      },      table: {        type: {          summary: '渲染表单所需JSON构造',          detail: 'JSON构造蕴含表单渲染、交互所须要的必要字段,也蕴含表单的校验规定',        },        defaultValue: {          summary: '[]',          detail: `[              {                key: "moduleName",                name: "title",                type: SchemaType.Text,                label: "栏目名称",                placeholder: "请输出栏目名称",                attrs: {                  //                },                rules: [                  {                    required: true,                    message: "栏目名称必填~",                    trigger: RuleTrigger.Blur,                  },                ],              }            ]          `        }      }    },    runtimeChange: {      description: '实时监听表单的更新',      table: {        category: 'Events',      },    }  }};...

具体配置见链接。

现实成果

文档部署

执行命令:

$ npm run build-storybook

生成动态页面,间接部署动态页面即可。

目录构造:

│  0.0a0da810.iframe.bundle.js│  0.0a0da810.iframe.bundle.js.LICENSE.txt│  0.0a0da810.iframe.bundle.js.map│  0.799c368cbe88266827ba.manager.bundle.js│  1.9ebd2fb519f6726108de.manager.bundle.js│  1.9face5ef.iframe.bundle.js│  1.9face5ef.iframe.bundle.js.LICENSE.txt│  1.9face5ef.iframe.bundle.js.map│  10.07ff4e93.iframe.bundle.js│  10.a85ea1a67689be8e19ff.manager.bundle.js│  11.f4e922583ae35da460f3.manager.bundle.js│  11.f4e922583ae35da460f3.manager.bundle.js.LICENSE.txt│  12.1415460941f0bdcb8fa8.manager.bundle.js│  2.8a28fd4e.iframe.bundle.js│  2.8a28fd4e.iframe.bundle.js.LICENSE.txt│  2.8a28fd4e.iframe.bundle.js.map│  3.50826d47.iframe.bundle.js│  4.779a6efa.iframe.bundle.js│  5.f459d151315e6780c20f.manager.bundle.js│  5.f459d151315e6780c20f.manager.bundle.js.LICENSE.txt│  6.3bd64d820f3745f262ff.manager.bundle.js│  7.3d04765dbf3f1dcd706c.manager.bundle.js│  8.b541eadfcb9164835dfc.manager.bundle.js│  8.c6cb825f.iframe.bundle.js│  9.411ac8e451bbb10926c7.manager.bundle.js│  9.51f84f13.iframe.bundle.js│  9.51f84f13.iframe.bundle.js.LICENSE.txt│  9.51f84f13.iframe.bundle.js.map│  favicon.ico│  iframe.html│  index.html // 入口页面│  main.4c3140a78c06c6b39fba.manager.bundle.js│  main.e86e1837.iframe.bundle.js│  runtime~main.1e621db5.iframe.bundle.js│  runtime~main.91a0c7330ab317d35c4a.manager.bundle.js│  vendors~main.0d1916dd840230bedd21.manager.bundle.js│  vendors~main.0d1916dd840230bedd21.manager.bundle.js.LICENSE.txt│  vendors~main.8b18b60f.iframe.bundle.js│  vendors~main.8b18b60f.iframe.bundle.js.LICENSE.txt│  vendors~main.8b18b60f.iframe.bundle.js.map│└─static    └─media            element-icons.5bba4d97.ttf            element-icons.dcdb1ef8.woff

参考文档

  • Storybook官网

参考文章很多,如狐疑内容参考,请分割,会思考减少到参考文档中