从零实现一个Vue的组件库

# 从零实现一个Vue的组件库

从零实现一个Vue的组件库, 包含组件调试,全局引入,按需引入,组件文档和发布到npm,基于此项目可以搭建属于你自己的组件库。

# 主要目录一览

...
├─packages  // 组件目录
├─examples  // 调试组件的目录
├─docs      // 文档目录
├─dist      // 发布到npm的目录
├─deploy.sh // 自动部署文档命令
├─gulpfile.js // gulp打包css相关设置
├─vue.config.js  // vue-cli配置文件
├─webpack.component.js  // 组件库打包成设置
...

# 新建vue项目

使用vue-cli生成vue项目,根据命令行提示,选择默认配置,vue版本选择2.x

# 安装vue-cli,已安装直接进入下一步
npm install -g @vue/cli
# OR
yarn global add @vue/cli

# 使用vue命令创建项目
vue create ex-repo

调整生成后的的目录结构,增加packages目录用来放组件,将src目录改成example目录,方便本地开发测试组件。 需要注意的是vue-cli默认入口文件是src/main.js,修改目录结构后,需要在vue.config.js中修改入口信息,如下:

// vue.config.js
...
pages: {
    index: {
      entry: 'examples/main.js',
      template: 'public/index.html',
      filename: 'index.html'
    }
  }
...

调整后执行npm run serve 就能跑起来了,接下来编写组件

# 添加组件

packages目录中添加组件,这里以一个button按钮作为示例,此时packages的结构如下

├─packages
|    ├─lib  // 组件结构
|    |  ├─index.js  // 提供了一个install方法注册所有组件,将所有引入的组件暴露出去
|    |  ├─button
|    |  |   ├─index.js  // 提供了一个install方法注册当前组件
|    |  |   ├─src
|    |  |   |  └main.vue  // 组件内容
|    ├─css  
|    |  ├─button.scss // 组件样式
|    |  └index.scss // 全局引入样式

# 编写组件

packages/lib/添加button组件目录,button目录下的index.js 引入src/mian.vue文件并添加一个install方法暴露出来,如下:

导出组件

// packages/lib/button/index.js
import XButton from './src/main.vue'

XButton.install = function install(vue) {
  vue.component(XButton.name, XButton)
}

export default XButton

编写结构和样式

<!-- 样式路径 packages/css/button.scss -->
<!-- packages/lib/button/src/main.vue -->
<template>
  <button :class="['x-button', type ? 'x-button--' + type : '']">
    <slot/>
  </button>
</template>

<script>
export default {
  name: "xButton",
  props: {
    type: { 
      type: String, 
      default: 'default' 
    }
  }
}
</script>

# 测试组件

examples/main.js中引入packages中的组件,进行测试

import Vue from 'vue'
import App from './App.vue'

// 引入packages中 未编译的源文件
import '../packages/css/index.scss' 
import ExComponent from '../packages/lib/index'

Vue.use(ExComponent)

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

此时能看到的效果应该是这样的:

default primary success info warning danger

# 打包组件库

# 前端模块化几种方案

模块化主要是用来抽离公共代码,隔离作用域,避免变量冲突等。将一个复杂的系统分解为多个模块以方便编码

  • CommonJS 同步加载,CommonJS API是以在浏览器环境之外构建JS生态系统为目标而产生的项目
  • AMD 异步加载
  • UMD 兼容AMD,CommonJS 模块化语法。
  • CMDsea.js提出的按需解析加载模块
  • ES6 Module 加载引用,编译时加载(静态执行)

# webpack打包组件

在项目根目录下新建wepack配置文件webpack.component.js,将组件打包成UMD格式,配置如下

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin')  // 清理文件夹
const { VueLoaderPlugin } = require('vue-loader')  // 解析vue文件
const glob = require("glob");

const list = {};
// 解析获取组件结构
async function makeList(dirPath,list){  
  const files = glob.sync(`${dirPath}/**/index.js`);
  for(let file of files){
    const output = file.split(/[/.]/)[2];
    list[output] = `./${file}`;
  }
}

makeList('packages/lib',list);
// list 
// { 
//   button: './packages/lib/button/index.js',
//   index: './packages/lib/index.js' 
// }

module.exports = {
  entry: list,
  mode: 'production', // production development
  output: {
    filename: '[name].umd.js',
    path: path.resolve(__dirname, 'dist'), //输出在当前dist目录
    library: 'mui',
    libraryTarget: 'umd' // 选择umd格式
  },
  plugins: [
    new CleanWebpackPlugin(),
    new VueLoaderPlugin()
  ],
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: [
          {
            loader: 'vue-loader',
          }
        ]
      }
    ]
  },
};

安装完对应依赖后,在package.json中增加命令

...
"scirpts": {
  "build:js": "webpack --config ./webpack.component.js"
}
...

在命令行中执行npm run "build:js后,能看会出现dist目录如下

├─dist
|  ├─button.umd.js
|  ├─index.umd.js

# gulp 打包css

新建gulpfile.js文件配置插件来打包css,配置内容如下:

const gulp = require("gulp")

// gulp-sass手册地址 https://www.npmjs.com/package/gulp-sass
const sass = require('gulp-sass')(require('sass'))

const minifyCSS = require('gulp-minify-css')
const del = require('del');

gulp.task("sass", async function() {
  await del(['dist/css']);    
  return gulp.src("packages/css/**/*.scss")    
    .pipe(sass())
    .pipe(minifyCSS())    
    .pipe(gulp.dest("dist/css"))
})

需要注意的是,样式使用的是scssscss文件编译使用的是gulp-sass, gulp-sass会依赖node-sass这个包,安装这个包容易出现问题,而且官方已经在2020.10.27宣布弃用, 推荐使用dart-sass。 那怎么让包名是node-sass,但实际使用的是dart-sass呢?解决方式是,对包重命名,npm6.9以上支持,使用方式是这样npm install node-sass@npm:dart-sass

安装完对应依赖后,在package.json中增加命令

...
"scirpts": {
  "build": "npm run build:js && npm run build:css",
}
...

在命令行中执行npm run "build:css后,能看会出现dist目录如下

├─dist
|  ├─css
|  |  ├─button.css
|  |  └index.css

# 测试打包后的组件

package.json中增加命令

...
"scirpts": {
  "build": "npm run build:js && npm run build:css",
}
...

执行npm run build后,现在来测试一下打包后的组件,修改examples/main.js,引入编译后的文件,如下

...

// 引入编译后的文件
import '../dist/css/index.css'  // 引入本地编译的文件
import ExComponent from '../dist/index.umd.js'
Vue.use(ExComponent)
...

此时启动的example项目呈现应跟之前一致

# 发布组件到npm

  • 先到npm上检查包名是否存在,已存在的包名不能注册

  • package.jsonfiles字段设置要发布的文件,不设置默认为所有文件

    // package.json 这里只上传了编译后的`dist`
    ...
      "files": [
        "dist"
      ]
    ...
    
  • npm login 登录你的npm账号

  • npm public 推上去后就可以使用npm 安装了

# 发布后使用使用

在项目中使用npm i ex-component安装包就可以使用了。

// 全部引入
import 'ex-component/dist/css/index.css'
import ExComponent from "ex-component";
Vue.use(ExComponent);

// 按需引入
import 'ex-component/dist/css/button.css'
import { XButton } from "ex-component";
Vue.use(XButton);

# 搭建组件库文档站点

# 使用vuepress

安装vuepress npm install -D vuepress 新建docs目录,用于存放文档跟vuepress配置

  • 初始化文档目录
  • 编写按钮组件文档
  • 应用demo插件,使用reco主题
  • 在github上创建github Pages
  • 将文档发布到 githud.io