【业务方案】业务组件库的搭建与发布

4,417 阅读10分钟

写在前面


 一直以来,前端所做的事情比较重复和枯燥,确切的来说并没有什么太大的技术含量(仅从我这个初级前端角度来说)。特别是现在前端工程化带来的各种工具的使用,前端开发越来越像搭积木。找到合适的模块,按照UI图进行拼装,然后在这基础上修修补补。具体表现之一就是UI组件库的使用,饿了么的UI库很强大,经常被拿来「开箱即用」。这样的确是提高了开发效率,但是居安思危来看,前端工程师存在的意义也大大降低了。

知其然,更要知其所以然。 这句话是老话了,但是大多数人都做不到,包括我在内。正好公司这次项目大迭代,需要抽出一些公用的业务组件。趁此机会,我的思路是搭建一个公用的业务组件库,并将其发布到 NPM 上面。这样子。不同的项目可以直接引入 npm 包,做到组件的通用性。

 按照这个思路,网上找了不少文章,发现有着和我同样想法的人真不少。基本都写出了一个demo,但是缺点也有。那就是大部分都是个人实践,并不是工作需要,所以一些细节方面并不严谨。这也是可以理解的,毕竟不是工作上的任务,所以会随意点。综合这些前辈的实践,我开始尝试写出一个业务组件库。

了解NPM


 在此之前,我对于npm的理解就是一个node包管理工具,每次运行新项目都要首先npm install安装好相关依赖,然后npm run dev运行。至于还有yarn也只知道皮毛,类似于npm的包管理工具。据说它比npm好用,下载速度快。除此之外,还有淘宝镜像的修改。托管npm包的服务器在国外,如果不fq的话,安装依赖会很慢很慢。出于这个痛点,淘宝在国内服务器上做了一个npm包的备份,每隔5分钟(没记错的话)更新一下资源。这样子,国外可以使用cnpm代替npm,进行项目依赖的安装,显著提升了速度。

 现在了解到了更多关于NPM的东西。首先NPM除了是包管理工具之外,它还是有着自己的网站,负责包的服务器托管和对外宣传。

NPM官网  在官网的搜索框里,可以搜索npm现存的所有package,如果你发布了自己的 npm 包,也可以直接搜索出来。当你想要发布自己的 npm 包时,你首先需要做的是注册一个账号,可以拿常用的邮箱进行注册,这里要注意的是注册账号的验证邮件可能被邮箱当做垃圾邮件

搭建组件库


  1. 初始化项目

 因为公司前端项目是基于 Vue 的,所以搭建的组件库也是基于 Vue,所以要使用 vue-cli 来初始化项目。这里首先就出了一个问题,那就是安装 vue-cli。因为我的电脑之前没有安装 vue-cli,所以运行npm i -g @vue/cli,安装了最新的4.0.X版本。这里要注意,4.0.x版本生成的项目结构和3.X.X版本有很大差别。

// 安装vue-cli
npm i -g @vue/cli
// 初始化项目
vue create xhx-ui
然后看自己喜欢选择配置,几下回车之后一个清爽的初始项目就好了
  1. 修改目录结构

 我们在开发组件库,是需要实时进行预览的,不能等到发到npm上面再预览。所以本地项目中需要提供开发样例,测试组件的效果。将src改为example用来写开发样例,测试组件效果,然后在根目录下新建一个packages文件夹,用来放置编写的业务组件。

目录结构  如上图,左边是修改前的目录结构,右边是修改后的目录结构。这样的目录结构是模仿饿了么UI的,在小白的时候可以先从模仿别人成功的例子学习经验。

  1. 添加配置文件

 在修改了目录结构之后,你会惊奇的发小项目运行不了了。没关系,这很正常,毕竟 src都不见了,路径什么的肯定得报错。所以我们现在来解决这个问题。在根目录下新建一个 vue.config.js文件(新项目是没有这个文件的),并写入以下内容:

const path = require('path')

module.exports = {
    // 修改 pages 入口
    pages: {
        index: {
            // 入口
            entry: 'examples/main.js',
            // 模板
            template: 'public/index.html',
            // 输出文件
            filename: 'index.html'
        }
    },
    // 扩展 webpack 配置
    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
            })
    }
}

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

  1. 安装Sass插件

     Sass是css的预处理器之一,其作用是为 CSS 增加一些编程的特性,将 CSS 作为目标生成文件。然后开发者就只要使用 Sass 进行 CSS 的编码工作,大大提升了开发效率。
npm i node-sass sass-loader --save-dev

 那么如何在项目中使用呢?

<style scoped lang='scss'>

</style>

5. 页面适配方案:px转vw

 本组件库是用于移动端的,所以当然需要考虑到移动端的样式适配。我看了目前主流的适配方案,最终选择了 px转vw 方案。选择这个方案的理由是,可以直接对照设计图写px,而不需要换算到 rem。另外,在移动端,vw的使用更适合保持不同尺寸屏幕的样式统一。但是 vw 方案会有一个bug,当你使用图片时,可能会出现部分手机浏览器中图片消失的问题。解决问题也很简单,在全局加入下面的 css 代码。

img {
    content: normal !important;
}

 下面进行相关插件的安装:

  • postcss-url
  • postcss-import
  • postcss-cssnext
  • postcss-aspect-ratio-mini
  • postcss-px-to-viewport-opt
  • postcss-viewport-units
  • postcss-write-svg
  • cssnano

安装成功之后,在项目根目录下的package.json文件中,可以看到新安装的依赖包:

接下来在根目录下新建postcss.config.js文件,对新安装的 postcss 插件进行配置:

module.exports = {
  "plugins": {
    "postcss-import": {},
    "postcss-url": {},
    "postcss-aspect-ratio-mini": {},
    // to edit target browsers: use "browserslist" field in package.json
    "postcss-write-svg": {
      uft8: false
    },
    "postcss-cssnext": {},
    "postcss-px-to-viewport-opt": {
      viewportWidth: 750,                      // 设计稿宽度
      // viewportHeight: 1230,                 // 设计稿高度,可以不指定
      unitPrecision: 3,                        // px to vw无法整除时,保留几位小数
      viewportUnit: 'vw',                      // 转换成vw单位
      selectorBlackList: ['.ignore', '.hairlines','.vux-number-selector-plus','.vux-number-selector-sub'], // 不转换的类名
      minPixelValue: 1,                        // 小于1px不转换
      mediaQuery: false,                       // 允许媒体查询中转换
      exclude: /(\/|\\)(node_modules)(\/|\\)/,
      keepComment: 'no'
    },
    "postcss-viewport-units": {},
    "cssnano": {
      preset: "advanced",
      autoprefixer: false,                     // 和cssnext同样具有autoprefixer,保留一个
      "postcss-zindex": false,
      reduceIdents: false ,                    // 解决了 animation-name 被重写的 bug  https://github.com/cssnano/cssnano/issues/247
    }
  }
}

 特别声明:由于cssnextcssnano都具有autoprefixer,事实上只需要一个,所以把默认的autoprefixer删除掉,然后把cssnano中的autoprefixer设置为false。 由于配置文件修改了,所以重新跑一下npm run serve。项目就可以正常看到了。

 打开控制台就能看到,px 已经被转成 vw 了。

编写组件


 一般来说,组件指的是基础 UI 组件和业务组件。前者就是类似于饿了么UI一样,封装好了一些类似 Button 等常用的基础组件。这次我写的组件就没有这么难了,就是业务组件:如优惠券组件、问答组件等等。

  1. 开发组件

 在新建的packages文件夹中新建index.js,这个会在后面的全局引入中用到,接着新建一个Test文件夹用来存放test组件,其中 Test 里面的index.js是为后面按需加载准备的(暂时忽略)。文件目录如下:

 其中packages下面的index.js文件代码如下:

// 引入编写的组件
import Test from './test'

// 所有组件列表
const components = [
  Test
]

// 定义 install 方法,接收 Vue 作为参数
const install = function(Vue) {
  // 判断是否安装,安装过就不继续往下执行
  if (install.installed) return;
  install.installed = true;
  // 遍历注册所有组件
  components.map(component => Vue.component(component.name, component));
  // 下面这个写法也可以
  // components.map(component => Vue.use(component))
};

// 检测到 Vue 才执行,毕竟我们是基于 Vue 的
if (typeof window !== "undefined" && window.Vue) {
  install(window.Vue);
}

export default {
  install,
  // 所有组件,必须具有 install,才能使用 Vue.use()
  ...components
};

这个文件的作用就是引入编写的各个小组件,统一注册对外暴露。

  1. 预览组件

 我们在开发自定义的组件的时候,难免会遇到一些问题,所以一个完整的本地调试是很有必要的。否则发布到npm上再改来改去,造成了很多无效的版本的浪费。前面已经说了我们将最开始的src文件夹重命名为example,这个文件夹里就是本地调试组件的代码。

 这里的代码就是我们一般的Vue文件里面的src源码文件夹,只需要将我们开发的组件当成是本地组件,引入使用即可。首先在main.js文件中引入组件,并注册组件:

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

Vue.config.productionTip = false
// 注册组件库
Vue.use(XhxUI)

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

然后就正常使用组件。

  1. 打包组件

 组件发布到npm上之前,需要进行打包。在 vue-cli4 中我们通过以下命令可以将一个单独的入口打包成一个库:

// target: 默认为构建应用,改为 lib 即可启用构建库模式
// name: 输出文件名
// dest: 输出目录,默认为 dist,这里我们改为 lib
// entry: 入口文件路径
vue-cli-service build --target lib --name lib [entry]

 要注意的是在库模式中,打包出来的库中是不包含 Vue 的。 然后我们修改一下 package.json 文件,就像下面这样:  接着执行 npm run lib 就能生成库啦,看看左侧的目录是不是多了个 lib 文件夹,那个就是我们要发布的东西。 补充下,lib 目录下面的 js 之所以有好几种,是因为有两种规范(common 和 umd)、是否压缩(min)和映射(map)的区别,暂且知道有这么回事就行,不用深究。

  1. 发布组件

 万事俱备,就差发布了。先修改一下 package.json 文件:

{ 
  "name": "xr-ui",
  "version": "0.3.0",
  "description": "基于 vue-cli4 的 UI 组件库",
  "main": "lib/xr-ui.umd.min.js",  // 这是 lib 目录下的其中一个
  "keywords": "xr-ui",
  "private": false,
  "license": "MIT"
}

 在根目录下新建一个 .npmignore 文件,内容和 .gitignore 差不多:

# 这是复制 .gitignore 里面的
.DS_Store
node_modules
/dist

# local env files
.env.local
.env.*.local

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

# 以下是新增的
# 要忽略目录和指定文件
examples/
packages/
public/
vue.config.js
babel.config.js
*.map
*.html

 最后执行 npm login 登入最开始注册好的 npm 账号,再执行 npm publish 发布即可,就这么简单的两步就可以,过一会在 npm 上就能搜到了。当然前提是你有个 npm 账号,没有的话去注册一个吧,很 easy 的,然后还要搜下你的 npm 包名是否有人用,有的话就换一个。

  1. 更新组件

 随着组件的开发迭代,我们会需要更新组件。更新组件流程很简单:

  1. 打包为npm包

npm run lib

  1. 发布npm包
  • 登录(未登录需登录验证npm账号) npm login

  • 更新版本号(xx.xx.xx)

npm version patch修改版本号最后一位,表示一些补丁和修复

npm version minor修改版本号第二位,表示一个小版本的更新

npm version major修改版本号第一位,表示一个大版本的更新

  • 发布 npm publish

总结

 到此为止,一个简易的npm包算是发布完成了。反思自己,不懂的东西还很多,努力探究技术。追求真爱和自由!