开发一个简单的 Vue UI 组件库(一)

·  阅读 560

之前一直在使用 Element UI ,不得不说饿了么团队真的很厉害,人总是有好奇心的,想着自己什么时候也试着做一套自己的组件库,刚好团队需要一套简单的组件库,就做了一些尝试。原本不好意思拿出来讲,想着自己无非是把别人写了一万遍的东西,我又写了第一万零一遍,后来朋友劝我说,别人有时候并不是看你做的东西有多好,只是想看你有没有这方面的经验,后来想想,说的在理,那就把自己的半吊子东西和大家分享一下吧。

前言

在使用 Vue CLI 初始化项目时,我使用的是 webpack 模板,在开发组件库的时候,如何打包是个很重要的问题,因为要满足按需加载和开发预览的需求。 开发预览的问题比较容易解决,将原本的 webpack 开发环境下的配置修改一下entry就可以解决,按需加载则比较麻烦,需要把每一个开发的组件单独打包,我的思路是将webpack 配置为多入口、多出口就ok了。 剩下的问题就是开发组件中遇到的具体问题和将整个组件库打包用于全引入了,后面一一分享。

基础配置

初始化项目:

# 2.x 版本
# 全局安装 vue-cli 工具
npm install -g vue-cli
# 初始化项目
vue init webpack MyComponent
复制代码

之前,我一直使用的是vue-cli(2.x) 现在已经是3.x,初始化项目会和2.x有一些不同。

# 3.x 版本
# 全局安装 vue-cli 工具
npm install -g @vue/cli
npm install -g @vue/cli-init
# 初始化项目
vue init webpack MyComponent
复制代码

为了开发时预览,我将src改为example用来写开发样例,测试组件效果,然后再新建一个src文件夹,修改后的目录结构:

目录结构

这个时候,再修改build文件夹下webpack.base.conf.js,只需要修改 webpack 配置中的entry就好了,这个文件中的信息是作为 webpack 打包的默认配置存在的,后面按照自己需要覆盖这个文件的配置信息就好了。

module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {
    app: './src/main.js'
  }
  // ...
}

// 修改后
module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {
    app: './example/main.js'
  }
  // ...
}
复制代码

这个时候运行npm run dev命令就可以预览效果了,会看到经典的 Welcome to Your Vue.js App。

开发组件

在新建的src文件夹中新建index.js,这个会在后面的全引入中用到,接着新建一个components文件夹用来存放所有组件,我们先创建一个MyButton组件,文件目录如下:

my-button

其中,my-button里的index.js是为后面按需加载准备的。

main.vue内容:

 <!--main.vue-->
<template>
  <button class="my-button"
          :class="['my-button-size-'+size]"
          @click="handleClick">
    <slot></slot>
  </button>
</template>

<script>
export default {
  name: 'MyButton',
  props: {
    // size 属性是用来控制按钮的大小样式,通过不同 size 设置不同的类名
    size: {
      type: String,
      default: 'big'
    }
  },
  methods: {
    /**
     * 用于触发 my-button 实例上的 click 事件
     */
    handleClick () {
      this.$emit('click')
    }
  }
}
</script>

<style scoped>
  /* 初始化 button 的样式 */
  .my-button {
    display: inline-block;
    line-height: 1;
    cursor: pointer;
    background: #fff;
    border: 1px solid #ddd;
    color: #000;
    text-align: center;
    outline: none;
    font-size: 14px;
    border-radius: 4px;
  }

  /* button big 的样式 */
  .my-button.my-button-size-big {
    padding: 12px 20px;
  }

  /* button mid 的样式 */
  .my-button.my-button-size-mid {
    padding: 10px 20px;
  }

  /* button small 的样式 */
  .my-button.my-button-size-small {
    padding: 10px 10px;
  }
</style>

复制代码

my-button中的index.js内容,其实如果组件库只是需要满足全引入,这个index.js完全可以省略掉,后面按需引入会对这个文件做一些修改:

'use strict'
// my-button 中的 index.js
import main from './src/main'

export default main
复制代码

src中的index.js内容:

'use strict'
// src 中的 index.js
import MyButton from './components/my-button'

const components = [
  MyButton
]

// 官方原文:Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象
const install = function (Vue) {
  components.forEach(component => {
    // Vue.component:注册或获取全局组件
    Vue.component(component.name, component)
  })
}

// 在全局引入组件库时,使用 Vue.use 安装插件,必须提供 install 方法,这里 export default 就是用于 Vue.use
export default {
  install
}
复制代码

这时再修改example文件夹中的文件用于预览效果,修改后的目录结构:

example

主要的修改是引入组件,添加路由信息,写组件的 demo。

main.js中添加两行代码全局引入开发的组件:

// ...
// 引入开发的组件库,
import MyComponent from '../src'

// 使用 Vue.use 安装开发的组件库
Vue.use(MyComponent)
// ...
复制代码

这样可以在example自由使用MyButton了。

<template>
  <div class="example-button">
    <my-button @click="clickFunc">
      这是一个按钮
    </my-button>
    <my-button size="mid" @click="clickFunc">
      这是一个按钮
    </my-button>
    <my-button size="small" @click="clickFunc">
      这是一个按钮
    </my-button>
  </div>
</template>

<script>
export default {
  name: 'example-button',
  methods: {
    clickFunc () {
      alert('Click')
    }
  }
}
</script>

<style scoped>

</style>
复制代码

效果预览:

button

这是一个非常简单的组件,许多地方没有做,比如:按钮的hover效果,active效果……距离一个成熟的组件还有很大的距离,不过大致思路是这样了,后面可以按照自己的需要添加其他的东西。

Webpack 打包用于全引入

做完以上的工作的确是开发了一个只有一个组件的组件库,这时候需要将组件库打包,先将组件库打包在一个新的目录dist中。

先附上一个连接:webpack 打包 JavaScript library

Webpack 的配置比较复杂,我的大致思路是将原本的entry改为src中的index.js,按照 Webpack 打包 Library 的规范修改必要配置就可以了。

用于打包组件库在build文件夹下的webpack.publish.conf.js

// ...
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')

baseWebpackConfig.entry = {}

const webpackConfig = merge(baseWebpackConfig, {
  // ...
  devtool: config.publish.productionSourceMap ? '#source-map' : false,
  entry: {
    myComponent: './src/index.js'
  },
  output: {
    path: config.publish.distRoot + '/dist',
    filename: 'index.js',
    publicPath: config.publish.assetsPublicPath,
    library: 'my-component',
    libraryTarget: 'umd',
    umdNamedDefine: true
  },
  externals: {
    vue: {
      root: 'Vue',
      commonjs: 'vue',
      commonjs2: 'vue',
      amd: 'vue'
    }
  },
  // ...
})

// ...
复制代码

每一个配置的用途都可以在 Webpack 官网上找到,就不多说了,附上连接:Webpack 配置

最容易被忽略的是build文件夹下,utils.js会因process.env.NODE_ENV环境变量不同,为 Webpack 各个 loader 生成的路径不同。

最后再修改一下package.json

{
    // ...
    "private": false,
    "main": "dist/index.js",
    "scripts": {
        // ...
        "build:my-component": "node build/publish.js"
        // ...
    }
}
复制代码

遮样一个非常小的组件库就完成了,目前只写了全引入的部分,后面会把按需引入的部分加上。

最后贴上github的地址:MyComponent,细节的地方可以看源代码,之前做的组件库因为不是个人成果,里面也有设计同事的工作成果,还是不拿出来给自己贴金了,哈哈哈~

距离一个成熟的组件库还差得非常非常远,不过有这样的经历看起别人成熟的组件库的源代码时会更轻车熟路,希望对大家有帮助~

分类:
前端
标签:
分类:
前端
标签: