• vite2 + vue3 开发组件库


    使用vite2 + vue3开发一个组件库

    完成后的效果图展示:

     在线体验

    GitHub 地址

    一、搭建一个 Vite 项目

    参照vite官网

    通过附加的命令行选项直接指定项目名称和你想要使用的模板。例如,要构建一个 Vite + Vue 项目

    # yarn
    yarn create vite my-baseui --template vue
    

      

    接下来执行

    cd my-baseui
    yarn 
    yarn dev
    

     

    运行后打开页面长这样子:

     在根目录下创建packages文件夹,目录结构如下:

     描述:

    • packages/components: 存放组件。
    • packages/styles: 存放全局样式和组件样式。
    • packages/index.js

    二、组件编写

    一、components中,编写一个测试的button组件

    二、components中的 button组件index.vue代码

    <template>
      <button
        class="m-button"
        :class="styleClass"
        :disabled="disabled"
        :round="round">
        <slot></slot>
      </button>
    </template>
     
    <script>
    import { computed } from 'vue';
    export default {
      name: 'm-button',
      props: {
        type: {
          type: String,
          default: 'default',
          validator(value) {
            return ['default','primary', 'success', 'info', 'warning', 'danger','text'].indexOf(value) > -1;
          }
        },
        disabled: {
          type: Boolean,
          default: false
        },
        round: {
          type: Boolean,
          default: false
        }
      },
      setup(props) {
     
        const styleClass = computed(() => {
          return {
            [`m-button--${props.type}`]: props.type,
            'is-disabled': props.disabled,
            'is-round': props.round
          }
        })
     
        return {
          styleClass
        }
      }
    }
    </script>
     
    <style lang="scss" scoped>
    @import '../../styles/components/button.scss';
    </style>
    

      

    三、styles中的button样式代码

    .m-button {
      display: inline-block;
      padding: 12px 20px;
      margin-right: 10px;
      border: 1px solid #d9d9d9;
      background: #fff;
      color: #333;
      cursor: pointer;
    }
    .is-disabled{
      color: #c0c4cc;
      cursor: not-allowed;
      background-color: #fff;
      border-color: #ebeef5;
    }
    .m-button--primary{
      color: #fff;
      background-color: #409eff;
      border-color: #409eff;
    }
    .m-button--success {
      color: #fff;
      background-color: #67c23a;
      border-color: #67c23a;
    }
    .m-button--info {
      color: #fff;
      background-color: #909399;
      border-color: #909399;
    }
    .m-button--warning {
      color: #fff;
      background-color: #e6a23c;
      border-color: #e6a23c;
    }
    .m-button--danger {
      color: #fff;
      background-color: #f56c6c;
      border-color: #f56c6c;
    }
    .m-button--text {
      border-color: transparent;
      color: #409eff;
      background: transparent;
      padding-left: 0;
      padding-right: 0;
    }
    // 圆角
    .is-round{
      border-radius: 20px;
    }
    

      

    四、button组件中 index.js文件,用来把写的组件暴露出去

    import mButton from './index.vue'
    mButton.install = app => {
      app.component(mButton.name, mButton)
    } 
    export default mButton;
    

      

    五、packages文件夹下新建index.js文件,用来管理所有的组件

    import mButton from "./components/button/index";
    import mInput from "./components/input/index";
     
    // 组件列表
    const components = [
      mButton,
      mInput
    ]
     
    // 定义 install 方法,接收 Vue 作为参数。如果使用 use 注册插件,那么所有的组件都会被注册
    const install = (Vue) => {
      // 判断是否安装
      if (install.installed) return
      // 遍历注册全局组件
      components.map(component => Vue.component(component.name, component))
    }
     
    if (typeof window !== 'undefined' && window.Vue) {
      install(window.Vue);
    }
     
    export default {
      install,
      mButton,
      mInput
    }
    

      

    三、组件引入

    一、在src 的main.js中引入

    import { createApp } from 'vue'
    import App from './App.vue'
    
    import myBaseui from "../packages/index";
    
    const app = createApp(App);
    app.use(myBaseui)
    app.mount('#app')
    

      

    二、在src/App.vue中使用组件

    改写 src/App.vue,引入button

    <template>
      <div>
        <m-button>默认按钮</m-button>
        <m-button type="primary">主要按钮</m-button>
        <m-button type="success">成功按钮</m-button>
      </div>
    </template>
    

     浏览器上查看效果,可以看到已经成功了

    一个组件库有很多个组件,每个组件都应该有它独立的文档。这个文档不仅有对组件各项功能的描述,更应该具有组件预览、组件代码查看等功能。同时为了良好的组件开发体验,我们希望这个文档是实时的,这边修改代码,那边就可以在文档里实时地看到最新的效果。接下来我们就来实现这么一个功能。

    组件的文档一般是用 Markdown 来写。这里我们使用一个插件 vite-plugin-md

    vite pulgin 将 md 文件转换成 vue 组件渲染的主要流程是:

    1. 配置 vue router 路由,指向 .md 文件
    2. 编写 vite 插件将 .md 文件解析成 vue 文件字符串
    3. 最后由 vite 的插件@vitejs/plugin-vue 将 vue 文件字符串编译成函数组件返回给前端

    在src下,新建router/index.js文件

    四、配置 vue router 路由,指向 .md 文件

    import { createRouter, createWebHashHistory } from "vue-router";
    
    const routes = [
      {
        path: '/',
        name: '组件页面',
        component: () => import('@/views/home.vue'),
        children: [
          {
            path: '/button',
            name: 'Button 按钮',
            component: () => import('/packages/components/button/doc/doc.md')
          },
          {
            path: '/input',
            name: 'Input 输入框',
            component: () => import('/packages/components/input/doc/doc.md')
          }
        ]
      }
    ]
    
    const router = createRouter({
      history: createWebHashHistory(),
      routes,
    });
    
    export default router;
    

    main.js中引入路由

    import { createApp } from 'vue'
    import App from './App.vue'
    import router from "./router";
    
    import myBaseui from "../packages/index";
    
    const app = createApp(App);
    app.use(router)
    app.use(myBaseui)
    app.mount('#app')

    这里路由引入的是Markdown 文件,这个在默认的 Vite 配置里是无效的,我们需要引入 vite-plugin-md 插件来解析 Markdown 文件并把它变成 Vue 文件。

    五、编写 vite 插件将 .md 文件解析成 vue 文件

    vite.config.js 如下:

    import { defineConfig } from 'vite'
    import Vue from '@vitejs/plugin-vue';
    import Markdown from 'vite-plugin-md';
    import { resolve } from "path";
    
    // https://vitejs.dev/config/
    export default defineConfig({
      base: '/my-baseui/',
      plugins: [
        Vue({
          include: [/\.vue$/, /\.md$/],
        }),
        Markdown(),
      ],
      resolve: {
        alias: {
          "@": resolve(__dirname, "src"),
          '~': resolve(__dirname, "packages")
        }
      },
    })

    这样配置以后,任意的 Markdown 文件都能像一个 Vue 文件一样被使用了

     

    接下来,编写home.vue页面,增加侧边栏和主区域

    <template>
      <div class="main-container">
        <div class="sidebar">
          <ul v-for="item in routes" :key="item">
            <li 
              v-for="(ele,index) in item.children" 
              :key="ele"
              :class="{'active': mIndex == index}"
              @click="goPath(ele,index)">
              {{ele.name}}
            </li>
          </ul>
        </div>
        <main class="app-main">
          <router-view></router-view>
        </main>
      </div>
    </template>
    
    <script setup>
    import { computed, ref, reactive, onMounted } from 'vue';
    import { useRouter } from 'vue-router';
    
    const router = useRouter()
    const mIndex = ref(sessionStorage.getItem("mIndex") || '0');
    
    const routes = computed(() => router.options.routes)
    
    const goPath = (ele,index) => {
      mIndex.value = index
      router.push({
        path:ele.path
      })
      sessionStorage.setItem("mIndex", index);
    }
    
    </script>
    
    <style lang="scss" scoped>
    .main-container{
      display: flex;
      justify-content: space-between;
      overflow: hidden;
      .sidebar{
         200px;
        height: 100%;
        border-right: 1px solid #eee;
        text-align: center;
        ul{
          li{
            height: 50px;
            line-height: 50px;
            cursor: pointer;
          }
          .active{
            color: #409eff;
            background-color: #ECF5FF;
            border-right: 1px solid #409eff;
          }
        }
      }
      .app-main{
        flex: 1;
        padding: 20px 50px;
        overflow-y: auto;
      }
    }
    </style>
    

    把App.vue改下

    <template>
      <router-view></router-view>
    </template>
    
    <script setup>
    
    </script>
    
    <style>
    
    </style>
    

      

    往 /packages/components/button/doc/doc.md 里面随便写点东西,

    完成后,浏览器查看有效果了。

    vite-plugin-md 是支持在 Markdown 里面写 setup 函数的!因此我们可以把需要执行 JS 逻辑的代码封装成一个组件,然后在 Markdown 里通过 setup 来引入。

    在packages/components/button/doc 目录下新建一个 demo.vue

    <template>
      <div>
        <m-button>默认按钮</m-button>
        <m-button type="primary">主要按钮</m-button>
        <m-button type="success">成功按钮</m-button>
        <m-button type="danger">危险按钮</m-button>
      </div>
    </template>
    

      

    然后在md文件中引入

    <script setup>
    import demo from './demo.vue';
    </script>
    
    # Button 按钮
    <demo/>
    

      

    完成后,查看页面显示:

    六、代码预览功能

    接下来我们来实现代码预览功能,虽然说代码预览也很简单,可以直接在 Markdown 中贴代码,但是代码又写一遍就显得很繁琐了

    有没有方法可以直接把demo.vue中的代码展示出来呢?

    答案是可以的。

    在 Vite 的开发文档里有记载到,它支持在资源的末尾加上一个后缀来控制所引入资源的类型。比如可以通过 import xx from 'xx?raw' 以字符串形式引入 xx 文件。基于这个能力,我们可以编写一个 <Preview /> 组件来获取所需要展示的文件源码。

    一、新建一个 Preview.vue 文件

    <template>
      <div class="pre-code-box">
        <transition name="slide-fade">
          <pre
            class="language-html"
            v-if="showCode"
          >
            <code class="language-html">{{ sourceCode }}</code>
          </pre>
        </transition>
        <div class="showCode" @click="showOrhideCode">
          <i :class="iconClass"></i>
          <span>{{ showCode ? "隐藏代码" : "显示代码" }}</span>
        </div>
      </div>
    </template>
    
    <script setup>
    import { computed, onMounted, ref } from "vue";
    
    const props = defineProps({
      compName: {
        type: String,
        default: "",
        require: true,
      },
      demoName: {
        type: String,
        default: "",
        require: true,
      },
    });
    
    const showCode = ref(false);
    const sourceCode = ref("");
    
    const iconClass = computed(() => {
      return [
        'iconfont',
        showCode.value ? 'icon-arrow-up-filling' : 'icon-arrow-down-filling'
      ]
    })
    
    const showOrhideCode = () => {
      showCode.value = !showCode.value;
    }
    
    const getSourceCode = async () => {
       let msg = await import(/* @vite-ignore */ `/packages/components/${props.compName}/doc/${props.demoName}.vue?raw`)
       // console.log(msg.default);
       sourceCode.value = msg.default
    }
    
    onMounted(() => {
      getSourceCode()
    })
    
    </script>
    

     

    这里需要加 @vite-ignore 的注释是因为 Vite 基于 Rollup,在 Rollup 当中动态 import 是被要求传入确定的路径,不能是这种动态拼接的路径。此处加上该注释则会忽略 Rollup 的要求而直接支持该写法。

    但是这样的写法,在开发模式下可行,build打包到线上后报错,拿不到资源,我们可以通过判断环境变量,在 build 模式下通过 fetch 请求文件的源码来绕过,我们后面再讲。

    二、在md文件中引入preview

    <script setup>
    import demo from './demo.vue';
    import preview from '@/components/preview.vue';
    </script>
    
    # Button 按钮
    <demo/>
    
    <preview compName="button" demoName="demo"/>
    

      

    查看页面效果:

    我们发现,样式没有高亮效果,接下来下载插件 highlight.js 来实现代码高亮

    三、main.js中引入 highlight.js ,样式有很多种,我们随便找个看看效果

    import { createApp } from 'vue'
    import App from './App.vue'
    import router from "./router";
    
    import myBaseui from "../packages/index";
    import hljs from "highlight.js";
    import "highlight.js/styles/color-brewer.css";
    
    const app = createApp(App);
    app.use(router)
    app.use(myBaseui)
    app.directive("highlight", function (el) {
      const blocks = el.querySelectorAll("pre code");
      blocks.forEach((block) => {
        hljs.highlightBlock(block);
      });
    });
    app.mount('#app')
    

      

    preview.vue中加入 v-highlight 自定义指令

    <pre
      class="language-html"
      v-if="showCode"
      v-highlight
     >
        <code class="language-html">{{ sourceCode }}</code>
     </pre>

    调整后,页面高亮效果出来了

    到这里,我们的组件库基本结构算是完成了,接下来,我们上传到github,部署静态站点。

    七、部署站点

    查看vite文档,在vite.config.js 中加入 base: '/my-baseui/',

    执行npm run build 时,默认会打包成dist文件,我们把输出目录改成docs

    vite.config.js加如下配置:

    build: {
        outDir: 'docs'
    },
    

     

     接下来,在你的项目中,创建一个 deploy.sh 脚本,包含以下内容(注意高亮的行,按需使用),运行脚本来部署站点

    #!/usr/bin/env sh
    
    # 发生错误时终止
    set -e
    
    # 构建
    npm run build:docs
    
    # 进入构建文件夹
    cd docs
    
    # 如果你要部署到自定义域名
    # echo 'www.example.com' > CNAME
    
    git init
    git add -A
    git commit -m 'deploy'
    
    # 如果你要部署在 https://<USERNAME>.github.io
    # git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git main
    
    # 如果你要部署在 https://<USERNAME>.github.io/<REPO>
    git push -f git@github.com:wangibook/my-baseui.git master:gh-pages
    
    cd -
    

    packages.json中的 scripts 修改下:

    "scripts": {
        "dev": "vite",
        "build:docs": "vite build",
        "preview": "vite preview",
        "deploy": "bash deploy.sh"
      },
    

    之后执行 npm run deploy 命令时,会把docs文件夹打包上传到github的 gh-pages 分支上

    切换到gh-pages分支,可以看到看到内容就是我们打包的docs文件夹下的内容,

    点击Settings--->Pages---->Source选择gh-pages分支然后点击链接https://wangibook.github.io/my-baseui/

     此时预览代码会报错,拿不到资源,其原因是由于 Rollup 无法进行静态分析,因此无法在构建阶段处理需要动态 import 的文件,导致会出现找不到对应资源。

    我们可以通过判断环境变量,在 build 模式下通过 fetch 请求文件的源码来绕过。

    把preview.vue做下修改:

    const isDev = import.meta.env.MODE === 'development';
    
    const getSourceCode = async () => {
      if(isDev) {
        let msg = await import(/* @vite-ignore */ `/packages/components/${props.compName}/doc/${props.demoName}.vue?raw`)
        // console.log(msg.default);
        sourceCode.value = msg.default
      } else {
        sourceCode.value = await fetch(`/my-baseui/components/${props.compName}/doc/${props.demoName}.vue`).then(res => res.text());
      }
    }
    

      

    打包后的文件夹里并没有components文件,我们得把packages下的components文件加入进去

    每次复制粘贴这个components就会显得很麻烦,在这里可以用一个插件自动帮我们实现,下载插件 copy-dir

    新建config/copyDocs.js

    let copydir = require('copy-dir')
    
    copydir.sync(
      process.cwd() + '/packages/components',
      process.cwd() + '/docs/components',
      {
        utimes: true,
        mode: true,
        cover: true
      },
      function (err) {
        if (err) throw err
        console.log('done')
      }
    )
    

    再次执行构建命令 npm run deploy, 等一会儿查看部署的站点,代码预览可以拿到资源了。

    八、分开文档和库的构建逻辑

    我们目前打包出来的文件是交互文档docs,为了构建一个 my-baseui 组件库并发布到 npm,可供别人下载使用,我们需要将构建的逻辑分开

    在根目录下添加一个 /build 目录,依次写入 base.config.js,dist.config.js,docs.config.js,分别为基础配置、库配置和文档配置

    base.config.js:

    基础配置,需要确定路径别名、配置 Vue 插件和 Markdown 插件用于对应文件的解析。

    import { defineConfig } from 'vite';
    import Vue from '@vitejs/plugin-vue';
    import Markdown from 'vite-plugin-md';
    import { resolve } from "path";
    
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [
        Vue({
          include: [/\.vue$/, /\.md$/],
        }),
        Markdown(),
      ],
      resolve: {
        alias: {
          "@": resolve(__dirname, "../src")
        }
      },
    })
    

      

    dist.config.js:

    import baseConfig from './base.config';
    import { defineConfig } from 'vite';
    
    export default defineConfig({
      ...baseConfig,
      build: {
        outDir: 'dist',
        rollupOptions: {
          // 请确保外部化那些你的库中不需要的依赖
          external: ["vue"],
          output: {
            // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
            globals: {
              vue: "Vue",
            }
          },
        },
        lib: {
          entry: "./packages/index.js",
          name: "my-baseui",
        },
      }
    });
    

      

    docs.config.js:

    import baseConfig from './base.config';
    import { defineConfig } from 'vite';
    
    export default defineConfig({
      ...baseConfig,
      base: '/my-baseui/',
      build: {
        outDir: 'docs',
      },
    });
    

      

    完成了上面这些构建配置以后,修改一下packages.json

    "scripts": {
        "dev": "vite --host",
        "build:docs": "vite build --config ./build/docs.config.js && node ./config/copyDocs.js",
        "build:dist": "vite build --config ./build/dist.config.js",
        "preview": "vite preview",
        "deploy": "bash deploy.sh"
      },
    

      

     执行npm run build:docs 结果

     执行npm run build:dist 结果

    九、上传npm

    一、在package.json中添加发布的一些信息

    描述:

    description:描述信息

    main:入口

    keywords:关键词

    npm发布时,要将package.json中的private属性值改为false

    README.md中写使用说明

    # my-baseui
    
    ## 安装
    使用npm 或 yarn 安装
    
    ```
    npm install my-baseui
    yarn add my-baseui
    ```
    
    ## 引入 my-baseui
    ### 完整引入
    #### 需要注意的是 css 样式文件需要单独引入。
    在 main.js 中写入以下内容:
    ```js
    import { createApp } from 'vue'
    import App from './App.vue'
    // 导入组件库
    import myBaseui from 'my-baseui'
    import 'my-baseui/dist/style.css';
    
    const app = createApp(App)
    app.use(myBaseui)
    app.mount('#app')
    ```
    <br/>
    
    ## 愉快开始
    
    #### 至此 my-baseui 就引入完成并且可以使用了。
    
    ```html
    <!-- html -->
    <m-button>默认按钮</m-button>
    <m-button type="primary">主要按钮</m-button>
    ```
    
    <br/>
    

      

    发布npm

    执行npm login 命令,输入用户名和密码,输入密码时是看不到的

    之后提示输入email,成功后你的邮箱会收到一个one-time password,填入这个一次性密码

    登录之后,执行npm publish进行发布(每次进行发布的时候记得改下版本号)

    发布成功后,到npm上,查看头像---->packages,就可以看到发布的包了。

    至此,大体就算完成了,后面就是完善每个组件。

    如果你感兴趣的话,请前往 GitHub 查看源码和完整文档

     github地址: https://github.com/wangibook/my-baseui

  • 相关阅读:
    分组排序并显示序号
    power-design--tables-export-usage
    cache implement
    get system properties
    jbpm
    JVM内存管理机制和垃圾回收机制
    java读取excel
    Java编程中“为了性能”尽量要做到的一些地方
    json串与java对象互转
    apidoc的使用
  • 原文地址:https://www.cnblogs.com/wangdashi/p/16378015.html
Copyright © 2020-2023  润新知