基于vue-cli前端组件库搭建

最近在公司做了一个公共组件库脚手架,踩了一些坑,所以写文章记录一下。

该组件库基于组件库iview做的二次封装业务组件,实现的功能有如下:

  • 热更新开发
  • 打包编译、发布
  • 组件文档
  • eslint、prettier、styelint、husky

基于vue2 , vue-cli,vuepress,eslint,prettier,stylelint, husky等技术

1 .首先看一下目录结构。

lua 复制代码
business-component
|- build  ----------------------------------------------------------------------webpack配置
  |- webpack.config.js
|- docs --------------------------------------------------------------------- -vuepress文档
  |- .vuepress
    |- config.ts
    |- enhanceApp.js
  |- guide
    |- button.md   --------------------------------------------------------------button文档
  |- README.md
|- examples -------------------------------------------------------------------本地开发页面
 |- App.vue
 |- main.ts
|- lib ------------------------------------------------------------------------打包后的文件
  |- web-design.css
  |- web-design.umd.js
|- packages --------------------------------------------------------------------组件源文件  |- components
    | - button
  | - style
    | - index.less
  | - index.ts
|- public   ----------------------------------------------------------------------静态资源
|- vue.config.js
|- package.json

2.打包配置

  • dev配置

    ini 复制代码
    const path = require('path');
    
    module.exports = {
      entry: {
        index: path.resolve(__dirname, '../examples/main.ts'),
      },
    };
  • prod配置

    java 复制代码
    module.exports = {
      entry: {
        index: '../packages/components/button/index.js',
      },
      output: {
        //  文件名称
        filename: '[name].js',
        //  构建依赖类型
        libraryTarget: 'umd',
        //  库中被导出的项
        libraryExport: 'default',
        //  引用时的依赖名
        library: 'WebDesign',
      },
    };
  • example

    java 复制代码
    const path = require('path');
    module.exports = {
      entry: {
        index: path.resolve(__dirname, '../examples/components/index.ts'),
      },
      output: {
        //  文件名称
        filename: '[name].js',
        //  构建依赖类型
        libraryTarget: 'umd',
        //  库中被导出的项
        libraryExport: 'default',
        //  引用时的依赖名
        library: 'WebDesign',
      },
    };
  • vue.config.js

    arduino 复制代码
    const { defineConfig } = require('@vue/cli-service');
    const path = require('path');
    const devWebpackConfig = require('./build/webpack.config.dev.js');
    const proWebpackConfig = require('./build/webpack.config.prod.js');
    const exampleWebpackConfig = require('./build/webpack.config.example.js');
    
    function getWebpackConfig() {
      if (process.env.NODE_ENV === 'example') {
        return exampleWebpackConfig;
      } else if (process.env.NODE_ENV === 'development') {
        return devWebpackConfig;
      } else {
        return proWebpackConfig;
      }
    }
    module.exports = defineConfig({
      transpileDependencies: true,
    
      configureWebpack: getWebpackConfig(),
      css: {
        extract: {
          filename: 'css/[name].css', //在lib文件夹中建立 theme 文件夹中,生成对应的css文件。
        },
      },
      productionSourceMap: false,
      // 扩展 webpack 配置
      /**
       * 删除splitChunks,因为每个组件是独立打包,不需要抽离每个组件的公共js出来。
       * 删除copy,不要复制public文件夹内容到lib文件夹中。
       * 删除html,只打包组件,不生成html页面。
       * 删除preload以及prefetch,因为不生成html页面,所以这两个也没用。
       * 删除hmr,删除热更新。
       * 删除自动加上的入口App。
       */
      chainWebpack: config => {
        // @ 默认指向 src 目录,这里要改成 examples
        // 另外也可以新增一个 ~ 指向 packages
        config.resolve.alias.set('@', path.resolve('examples')).set('~', path.resolve('packages'));
        // 把 packages 和 examples 加入编译,因为新增的文件默认是不被 webpack 处理的
        config.module
          .rule('js')
          .include.add(/packages/)
          .end()
          .include.add(/examples/)
          .end()
          .use('babel')
          .loader('babel-loader')
          .tap(options => {
            // 修改它的选项...
            return options;
          });
    
        if (process.env.NODE_ENV === 'development') {
          config.optimization.delete('splitChunks');
          config.plugins.delete('copy');
          config.entryPoints.delete('app');
        } else {
          config.optimization.delete('splitChunks');
          config.plugins.delete('copy');
          config.plugins.delete('html');
          config.plugins.delete('preload');
          config.plugins.delete('prefetch');
          config.plugins.delete('hmr');
          config.entryPoints.delete('app');
        }
      },
    });
  1. 组件示例
xml 复制代码
<script lang="tsx">
import { defineComponent } from 'vue';
export default defineComponent({
  name: 'WebButton', // 注意这个name是必须的
  props: {
    disabled: {
      type: Boolean,
      default: false,
    },
  },
  render() {
    const { disabled } = this.$props;
    return (
      <button class={['ts-button', { 'is-disabled': disabled }].join(' ')} disabled={disabled}>
        <span>
          <slot>测试按钮</slot>
        </span>
      </button>
    );
  },
});
</script>
<style lang="less" src="../../../css/button.less"></style>

入口文件

javascript 复制代码
import Button from './button';

const components = [Button];

// 全局引入: 引入的组件是个对象时,必须要有install函数
const install = Vue => {
  components.forEach(component => {
    Vue.component(component.name, component);
  });
};

/* istanbul ignore if */
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue);
}

export default {
  install,
  Button,
};

运行npm run build,就能打包出组件库代码,vue-cli支持打包出umd模块。

  1. example示例,main.ts实现
javascript 复制代码
import Vue from 'vue';
import App from './App.vue';
// 批量注册组件
import WebDesignComponent from '../packages/components';
import '../packages/css/index.less';
Vue.use(WebDesignComponent);
console.log(WebDesignComponent);
Vue.config.productionTip = false;

new Vue({
  render: h => h(App),
}).$mount('#app');

App.vue的实现

xml 复制代码
<template>
  <div id="app">
    <div class="mb10">
      <WebButton>按钮组件测试</WebButton>
      <WebButton disabled type="primary">按钮组件测试</WebButton>
      <WebButton type="success">按钮组件测试</WebButton>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin-top: 60px;
}
.mb10 {
  margin-bottom: 10px;
}
</style>

运行npm run dev就能运行example中的代码,调试组件。运行npm run build:example就能将示例中的组件打包,这在后面的vuepress中会用到的。

5.vuepress的实现

config.ts的代码

php 复制代码
import { defineConfig } from 'vuepress/config';
export default defineConfig({
  // ...
  themeConfig: {
    // logo: '/assets/img/logo.png',
    nav: [{ text: 'Home', link: '/' }],
    repo: 'vuejs/vuepress',
    editLinks: true,
    docsDir: 'packages/docs/docs',
    sidebar: [
      {
        title: '按钮', // 必要的
        path: '/guide/button', // 可选的, 标题的跳转链接,应为绝对路径且必须存在
        collapsable: false, // 可选的, 默认值是 true,
        sidebarDepth: 1, // 可选的, 默认值是 1
      },
      {
        title: '虚拟表格', // 必要的
        path: '/guide/virtual-table', // 可选的, 标题的跳转链接,应为绝对路径且必须存在
        collapsable: false, // 可选的, 默认值是 true,
        sidebarDepth: 1, // 可选的, 默认值是 1
      },
    ],
    search: true,
  },
  plugins: [
    // require('./my-plugin.js')
  ],
});

enhanceApp.js

dart 复制代码
// 引入打包后的样式和文件。
import '../../lib/web-design.css';
export default async ({
    Vue,
    isServer,
}) => {
    // Vue.component('WebButton', WebButton);
    if(!isServer) {
        await import('../../lib/web-design.umd.js').then(vueComp => {
            Vue.use(vueComp);
        })
    }
}

运行npm run docs:dev就能启动vuepress文档了

参考文献:

相关推荐
_大学牲2 小时前
从 0 到上架:用 Flutter 一天做一款功德木鱼
前端·flutter·apple
嚴寒2 小时前
2025最终!Mac配置Flutter全平台开发环境完整指南(亲测有效)
前端·flutter
hi大雄2 小时前
如何用Claude Code 生成顶级UI ❇️
前端
拖拉斯旋风2 小时前
深入理解 CSS 选择器的底层逻辑:从层叠到优先级的本质
前端·css
半桶水专家2 小时前
npm run 的工作原理和工作流程
前端·npm·node.js
北辰浮光2 小时前
npm install core-js不成功
前端·javascript·npm
东华帝君3 小时前
React源码解读
前端
Mintopia3 小时前
🌱 AIGC 技术的轻量化趋势:Web 端“小而美”模型的崛起
前端·javascript·aigc
开发者小天3 小时前
React中的useRef的用法
开发语言·前端·javascript·react.js