首页 > web前端 > Vue.js > 手把手教你从零开始搭建一套ui组件库

手把手教你从零开始搭建一套ui组件库

藏色散人
发布: 2023-04-13 17:42:21
转载
3273 人浏览过

本篇文章给大家带来了关于ui组件的相关知识,其中主要跟大家聊聊怎么从零开始搭建一套ui组件库,有代码示例,感兴趣的朋友下面一起来看一下吧,希望对大家有帮助。

手把手教你从零开始搭建一套ui组件库

1. 环境准备

我们在编写我们组件库的组件前,首先需要一套环境,包括下面几项:

  1. 需要为组件库单独创建一个新的项目
  2. 需要规划合适的目录结构
  3. 需要定义组件文档的编写
  4. 需要搭建完善的单元测试

1.1 项目的搭建

我们目前的项目是基于vue2的版本,所以本次组件库项目也将使用 2.0版本的vue cli来创建。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// 全局安装 vue-cli

 

npm install --global vue-cli

 

 

 

// 基于 webpack 创建一个的新项目

 

vue init webpack my-project

 

 

 

// 安装依赖

 

npm install

 

 

 

// 运行

 

npm run dev

登录后复制

安装过程相关选项如下:

我们默认安装jest做为我们组件库的单元测试框架,代码检查工具默认eslint

1.2 目录的优化

创建项目成功后,现在我们新项目的目录结构应该是这样的:

  • build 打包相关目录以及配置
  • config 配置文件目录
  • node_modules 项目中安装的依赖模块
  • src 源码目录
  • static 静态文件目录
  • test 单元测试目录

我们需要对现有目录做一些调整,首先我们接触过一些主流的ui组件库比如 vant/ant,我们知道在这些组件库的官网上都提供了很直观的示例页面,此时我们的组件库将src目录改名为examples,作为我们的官方示例目录。

另外我们新增一个packages目录用户存放我们的组件。

现在我们目录结构变成如下:

此时如果重新运行项目会发现报错,因为我们src目录名变了,而webpack配置中默认的入口文件还是src/main.js,我们需要更改下配置,在build/webpack.base.conf文件中将src替换成examples。同时,我们需要将新增的 packages目录加入到webpack的编译队列。

更改后的 webpack.base.conf 应该是这样的。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

'use strict'

 

const path = require('path')

 

const utils = require('./utils')

 

const config = require('../config')

 

const vueLoaderConfig = require('./vue-loader.conf')

 

 

 

function resolve (dir) {

 

  return path.join(__dirname, '..', dir)

 

}

 

 

 

const createLintingRule = () => ({

 

  test: /.(js|vue)$/,

 

  loader: 'eslint-loader',

 

  enforce: 'pre',

 

  include: [resolve('examples'), resolve('packages'),resolve('test')],

 

  options: {

 

    formatter: require('eslint-friendly-formatter'),

 

    emitWarning: !config.dev.showEslintErrorsInOverlay

 

  }

 

})

 

 

 

module.exports = {

 

  context: path.resolve(__dirname, '../'),

 

  entry: {

 

    app: './examples/main.js' // 打包入口

 

  },

 

  output: {

 

    path: config.build.assetsRoot,

 

    filename: '[name].js',

 

    publicPath: process.env.NODE_ENV === 'production'

 

      ? config.build.assetsPublicPath

 

      : config.dev.assetsPublicPath

 

  },

 

  resolve: {

 

    extensions: ['.js', '.vue', '.json'],

 

    alias: {

 

      'vue$': 'vue/dist/vue.esm.js',

 

      '@': resolve('examples'),

 

    }

 

  },

 

  module: {

 

    rules: [

 

      ...(config.dev.useEslint ? [createLintingRule()] : []),

 

      {

 

        test: /.vue$/,

 

        loader: 'vue-loader',

 

        options: vueLoaderConfig

 

      },

 

      {

 

        test: /.js$/,

 

        loader: 'babel-loader',

 

        include: [resolve('examples'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]

 

      },

 

      {

 

        test: /.(png|jpe?g|gif|svg)(?.*)?$/,

 

        loader: 'url-loader',

 

        options: {

 

          limit: 10000,

 

          name: utils.assetsPath('img/[name].[hash:7].[ext]')

 

        }

 

      },

 

      {

 

        test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/,

 

        loader: 'url-loader',

 

        options: {

 

          limit: 10000,

 

          name: utils.assetsPath('media/[name].[hash:7].[ext]')

 

        }

 

      },

 

      {

 

        test: /.(woff2?|eot|ttf|otf)(?.*)?$/,

 

        loader: 'url-loader',

 

        options: {

 

          limit: 10000,

 

          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')

 

        }

 

      }

 

    ]

 

  },

 

  node: {

 

    // prevent webpack from injecting useless setImmediate polyfill because Vue

 

    // source contains it (although only uses it if it's native).

 

    setImmediate: false,

 

    // prevent webpack from injecting mocks to Node native modules

 

    // that does not make sense for the client

 

    dgram: 'empty',

 

    fs: 'empty',

 

    net: 'empty',

 

    tls: 'empty',

 

    child_process: 'empty'

 

  }

 

}

登录后复制

重新运行,编译通过。

1.3 组件文档的编写

在搭建完基础的代码环境后,我们要考虑我们新增组件的组件文档如何编写。

我们推荐使用 markdown来编写组件文档,然后我们如何在vue中使用markdown来编写我们的组件文档呢?这里我们推荐一个好用的工具。

vue-markdown-loader

1.3.1 安装方式

1

2

3

4

5

6

7

8

9

10

11

# vue1版本

 

npm i vue-markdown-loader@0 -D

 

 

 

# vue2版本

 

npm i vue-markdown-loader -D

 

npm i  vue-loader vue-template-compiler -D

登录后复制

1.3.2 webpack 配置

我们在对webpack.base.conf作如下修改:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

const VueLoaderPlugin = require('vue-loader/lib/plugin');

 

 

 

  module: {

 

    rules: [

 

      ...,

 

      {

 

        test: /.md$/,

 

        use: [

 

          {

 

            loader: 'vue-loader'

 

          },

 

          {

 

            loader: 'vue-markdown-loader/lib/markdown-compiler',

 

            options: {

 

              raw: true

 

            }

 

          }

 

        ]

 

      },

 

      ...

 

      ]

 

      },

 

 plugins: [new VueLoaderPlugin()]

登录后复制

1.3.3 编写组件文档

在我们配置完工具后,我们开始测试下组件文档的编写,

首先,我们在examples目录下新增一个docs文件夹,用于存放我们的组件文档。

新建一个test.md

1

# hello world

登录后复制

接下来我们在router文件夹新增一个 docs.js路由文件,用来存放我们组件文档的路径,并将它引入到根路由文件中。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

const docs = [

 

 {

 

 path: '/test',

 

 name: 'test',

 

 component: r => require.ensure([], () => r(require('../docs/test.md')))

 

 }

 

 ]

 

export default docs

登录后复制

浏览器中运行,我们便可以看到我们组件库的第一个组件文档...

以上完成,我们组件库的环境基本搭建完成了,接下来我们尝试开始写一个新的组件。

  1. 组件创建

我们先从一个基础的button组件开始。

首先我们在之前创建的packages中新增如下结构:

  • sg-button 组件目录
  • index.js 组件安装入口程序
  • src 组件源码

2.1 组件vue源码

这里我在src/index.vue中简单实现了一个button组件,支持三种大小的按钮,

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

<template>

 

    <div :class="[size]"  @click="click()">

 

        <span><slot></slot></span>

 

    </div>

 

</template>

 

<script>

 

 /**

 

 * 全局统一弹窗

 

 */

 

export default {

 

  name: &#39;sgButton&#39;,

 

  props: {

 

    size: {

 

      type: String,

 

      default: &#39;&#39;

 

    } // 按钮大小 :small large

 

  },

 

  methods: {

 

    click () {

 

      this.$emit(&#39;click&#39;)

 

    }

 

  }

 

}

 

</script>

 

<style  scoped>

 

.container{

 

    height: 50px;

 

    display: flex;

 

    justify-content: center;

 

    align-items: center;

 

    border: 1px solid #ccc;

 

}

 

.container.small{

 

    height: 40px;

 

}

 

.container.large{

 

    height: 60px;

 

}

 

</style>

登录后复制

2.2 组件导出

然后我们要怎么用这个组件呢?

考虑的是组件库,所以我们需要让我们的组件支持全局引入和按需引入,如果全局引入,那么所有的组件需要要注册到Vue component 上,并导出:

我们需要在组件的入口文件index.js添加如下代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

// 导入组件,组件必须声明 name

 

import sgButton from &#39;./src&#39;

 

 

 

 // 为组件提供 install 安装方法,供按需引入

 

sgButton.install = function (Vue) {

 

  Vue.component(sgButton.name, sgButton)

 

}

 

 

 

 // 导出组件

 

export default sgButton

登录后复制

然后我们在packages目录下新增入口文件,统一处理导出所有组件:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

// 导入button组件

 

import sgButton from &#39;./sg-button&#39;

 

 

 

 // 组件列表

 

const components = [

 

  sgButton

 

]

 

 

 

 // 定义 install 方法,接收 Vue 作为参数。如果使用 use 注册插件,那么所有的组件都会被注册

 

const install = function (Vue) {

 

  // 判断是否安装

 

  if (install.installed) return

 

  // 遍历注册全局组件

 

  components.map(component => Vue.component(component.name, component))

 

}

 

 

 

 // 判断是否是直接引入文件

 

if (typeof window !== &#39;undefined&#39; && window.Vue) {

 

  install(window.Vue)

 

}

 

 

 

export default {

 

  // 导出的对象必须具有 install,才能被 Vue.use() 方法安装

 

  install,

 

  // 以下是具体的组件列表

 

  sgButton

 

}

登录后复制

2.3 组件引入

按需引入:

1

2

3

4

5

import sgUi from &#39;../packages/index&#39;

 

 

 

Vue.use(sgUi.sgButton)

登录后复制

全部引入:

1

2

3

4

5

import sgUi from &#39;../packages/index&#39;

 

 

 

Vue.use(sgUi)

登录后复制

2.4 测试代码

我们在examples目录的入口文件中全局引入了组件库

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

// The Vue build version to load with the `import` command

 

 // (runtime-only or standalone) has been set in webpack.base.conf with an alias.

 

import Vue from &#39;vue&#39;

 

import App from &#39;./App&#39;

 

import router from &#39;./router&#39;

 

import sgUi from &#39;../packages/index&#39;

 

 

 

Vue.config.productionTip = false

 

 

 

Vue.use(sgUi)

 

 /* eslint-disable no-new */

 

new Vue({

 

  el: &#39;#app&#39;,

 

  router,

 

  components: { App },

 

  template: &#39;<App/>&#39;

 

})

登录后复制

然后我们编写一个vue页面来看看是否引入成功。

首先examples中新增pages目录,存放我们以后为每个组件单独编写的示例页面,新增examples/pages/buttonExample/index.vue 页面

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

<template>

 

    <div class="container">

 

      <sg-button>默认按钮</sg-button>

 

      <sg-button :size="&#39;large&#39;">大按钮</sg-button>

 

      <sg-button :size="&#39;small&#39;">小按钮</sg-button>

 

    </div>

 

</template>

 

<script>

 

 /**

 

 * button 示例

 

 */

 

export default {

 

  name: &#39;buttonExample&#39;,

 

 

 

  methods: {

 

 

 

  }

 

}

 

</script>

登录后复制

在这里我们直接调用了三种尺寸的button,运行看下效果:

效果完美,代表我们组件库第一个组件以及整体流程打通!

  1. 组件库发布

之前的环节,我们成功实现了我们组件库的第一个组件,但考虑到这只是组件库,组件库内能调用肯定是不够的,类似 vant/ant 这些组件库,我们怎么让其他用户可以使用我们的组件库组件内?

我们可以考虑发布到npm上,后续项目需要的话,我们直接通过npm安装引入的方式来调用。

发布到npm的方法也很简单, 首先我们需要先注册去npm官网注册一个账号, 然后控制台登录即可,最后我们执行npm publish即可.具体流程如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// 本地编译组件库代码

 

yarn lib

 

// 登录

 

 npm login

 

 // 发布

 

 npm publish

 

 // 如果发布失败提示权限问题,请执行以下命令

 

 npm publish --access public

登录后复制
  1. 单元测试

Vue Test Utils 安装

以上是手把手教你从零开始搭建一套ui组件库的详细内容。更多信息请关注PHP中文网其他相关文章!

相关标签:
来源:juejin.im
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
前端
来自于 1970-01-01 08:00:00
0
0
0
连接 Node.Js Express 后端与 React 前端
来自于 1970-01-01 08:00:00
0
0
0
javascript - 关于前后端协作
来自于 1970-01-01 08:00:00
0
0
0
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板