写在前面
一直以来,前端所做的事情比较重复和枯燥,确切的来说并没有什么太大的技术含量(仅从我这个初级前端角度来说)。特别是现在前端工程化带来的各种工具的使用,前端开发越来越像搭积木。找到合适的模块,按照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现存的所有package,如果你发布了自己的 npm 包,也可以直接搜索出来。当你想要发布自己的 npm 包时,你首先需要做的是注册一个账号,可以拿常用的邮箱进行注册,这里要注意的是注册账号的验证邮件可能被邮箱当做垃圾邮件。
搭建组件库
-
初始化项目
因为公司前端项目是基于 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
然后看自己喜欢选择配置,几下回车之后一个清爽的初始项目就好了
-
修改目录结构
我们在开发组件库,是需要实时进行预览的,不能等到发到npm上面再预览。所以本地项目中需要提供开发样例,测试组件的效果。将src
改为example
用来写开发样例,测试组件效果,然后在根目录下新建一个packages
文件夹,用来放置编写的业务组件。
如上图,左边是修改前的目录结构,右边是修改后的目录结构。这样的目录结构是模仿饿了么UI的,在小白的时候可以先从模仿别人成功的例子学习经验。
-
添加配置文件
在修改了目录结构之后,你会惊奇的发小项目运行不了了。没关系,这很正常,毕竟 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。
-
安装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
}
}
}
特别声明:由于cssnext
和cssnano
都具有autoprefixer
,事实上只需要一个,所以把默认的autoprefixer
删除掉,然后把cssnano
中的autoprefixer
设置为false
。
由于配置文件修改了,所以重新跑一下npm run serve
。项目就可以正常看到了。
打开控制台就能看到,px 已经被转成 vw 了。
编写组件
一般来说,组件指的是基础 UI 组件和业务组件。前者就是类似于饿了么UI一样,封装好了一些类似 Button 等常用的基础组件。这次我写的组件就没有这么难了,就是业务组件:如优惠券组件、问答组件等等。
- 开发组件
在新建的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
};
这个文件的作用就是引入编写的各个小组件,统一注册对外暴露。
- 预览组件
我们在开发自定义的组件的时候,难免会遇到一些问题,所以一个完整的本地调试是很有必要的。否则发布到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')
然后就正常使用组件。
- 打包组件
组件发布到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)的区别,暂且知道有这么回事就行,不用深究。
- 发布组件
万事俱备,就差发布了。先修改一下 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 包名是否有人用,有的话就换一个。
- 更新组件
随着组件的开发迭代,我们会需要更新组件。更新组件流程很简单:
- 打包为npm包
npm run lib
- 发布npm包
-
登录(未登录需登录验证npm账号)
npm login
-
更新版本号(xx.xx.xx)
npm version patch
修改版本号最后一位,表示一些补丁和修复
npm version minor
修改版本号第二位,表示一个小版本的更新
npm version major
修改版本号第一位,表示一个大版本的更新
- 发布
npm publish
总结
到此为止,一个简易的npm包算是发布完成了。反思自己,不懂的东西还很多,努力探究技术。追求真爱和自由!