「前端」尚妆 UI 组件库工程实践(weex vue)
本文来自尚妆前端团队南洋
发表于尚妆github博客,欢迎订阅!
前言
尚妆大前端团队使用 weex 进行三端统一开发有一段时间了,截止本文发表「达人店」APP大部分页面都已经用 weex 进行了重构,在此期间也积累了一些基础组件和业务组件。
之前维护组件的方式是在达人店项目的工程内维护一个 components 文件夹,随日常开发迭代,并行需求与开发人员的增多,这种维护方式也暴露出一些问题。
1、开发人员可以随意跟随需求开发修改 components 内的组件,破坏约定好的规范,或埋入 bug。
2、定义组件缺少规范,比如在某个需求开发中, A 开发人员觉得这个功能可以抽离成组件,就直接在 components 内定义并使用,但实际却是伪需求,用了一次就再也没有人使用,造成 components 组件库的部分冗余。
3、组件抽离过程无法协同使用,比如 A 开发同学切了个特性分支 feature/A
,并根据项目抽了个通用组件 ComponentA
,B 开发切了个特性分支 B,也想使用这个 ComponentA
组件,但此时两人在不同分支,代码并不能共享。
4、。。。
基于上述不便之处,我们尝试将 components 抽离出来,放到内部私有 npm 仓库中以 npm 包的形式去维护。
也就是我们将 spon-ui(内部组件库名称)
作为单独的一个项目去维护,加以约束形成组件库开发规范,能有效的解决上述问题。
此文就是此次抽离过程的一些实践,包含了组件的调试、文档调试、npm使用、组件 发布等内容。当然 weex 的语法同 vue,这些实践也同样适用于 vue。
1、组件库的调试
先看下 spon-ui
组件库项目的目录结构。
|- spon-ui
||-- build
||-- docs
||-- examples
||-- packages
|||--- weex-field
||||---- index.js
||||---- field.vue
||||---- example.vue
||||---- readme.md
||||---- package.json
||-- src
- build 中存放一些脚本执行文件,用于工程的调试、发布。
- docs 中存放文档调试的脚本,生成一个文档调试服务器。
- examples 中存放组件调试的脚本,生成一个组件调试服务器。(不存放组件例子)
- packages 存放真实组件,以及组件的文档和例子。
- src 存放组件可以使用的公共方法。
组件的调试
examples 文件夹内就是组件调试的相关脚本,这个文件夹在组建开发过程中是不需要变动的,只是定义了调试服务器的一些逻辑。并不包含真实的组件例子。
而真实的例子存放在相应组件目录下,example.vue
中引入当前目录下的 vue 组件,调试时是针对 example.vue
进行调试,因为调试组件需要模拟使用组件的场景(改变传入值,用户交互等)。
当执行 npm run dev:components
时,开发同学会看到浏览器打开页面:
选择想要调试的组件,比如说 weex-dialog ,进入到 weex-dialog 的调试界面。
开发同学此时修改 packages 目录中的 weex-dialog 的组件内容,会实时看到修改内容,进行调试。
console 中输出二维码
另外我们开发的组件是基于 weex 的,意味着开发的组件需要支持三端(iOS android H5),所以在 console 中会打印当前组件js的二维码,用于 native 调试。
如何在console中输出二维码也是个小trick,首先利用js的二维码库将资源生成二维码图,然后利用console输出背景图的机制打印二维码。
console.log("%c", "padding:75px 80px 75px;line-height:160px;background:url(" + base64 + ") no-repeat;background-size:160px");
整个调试页面是通过单页面的形式展现的,使用 vue-router
进行路由控制,weex 也支持 vue-router
,所以这个单页面在 native 中也能良好运行。
自动生成组件相关信息
在每次执行 npm run dev:components
命令时,会根据 packages 目录下的组件自动生成 nav-list.js
文件,这个索引文件用来定义 vue-router
的路由信息,以及调试主页的组件列表。这样做可以完全将调试过程抽离成黑盒,开发人员只需关注 packages
目录下的开发即可。
const routes = navList.map((item) => {
const path = item.path;
return {
path,
// 需要加vue后缀,不然webpack会将examples下的所有文件都require一下
component: require('examples/' + item.exampleRequire + '.vue'),
};
});
routes.push({
path: '/',
component: require('./app.vue'),
})
// 组件列表也通过 nav-list.js 渲染
<spon-cell-group>
<spon-cell
v-for="(page, jndex) in item.list"
:key="jndex"
:title="page.title"
:is-link="true"
@click="changePage(page)"
></spon-cell>
</spon-cell-group>
webpack require 动态的资源
本文使用 webpack 3.x.x
上节提到的 require 动态的模块时,如果不表明文件类型,webpack会将该目录下所有资源都 require 一遍,造成的问题是如果目录下有某类型的文件,而又没有使用对应的loader,在编译过程就会报错。上节中如果不加 .vue
后缀, webpack会将 examples 目录下所有资源都require一遍。
所以在定义各路由的component时,需要加上 vue 后缀,查找vue文件。
component: require('examples/' + item.exampleRequire + '.vue'),
};
webpack的文档说明在 https://webpack.js.org/guides/dependency-management/#require-context
在 webpack 的官方文档里列出了动态 require 的原理,对于 require("./template/" + name + ".ejs");
含表达式的引用,webpack 解析此处的 require,得到两个信息:
1、 目录为 ./template
2、匹配规则为 /^.*\.ejs$/
然后 webpack 会根据这两个信息得到一个 context module,这个模块包含了 ./template
目录下所有以 .ejs
为后缀的模块。
{
"./table.ejs": 42,
"./table-row.ejs": 43,
"./directory/folder.ejs": 44
}
还有一个 require.context()
方法可以自定义动态引用的规则,文档中也有示例,官网给出了一个基于此的demo,引入一个目录中所有符合规则的模块。
function importAll (r) {
r.keys().forEach(r);
}
importAll(require.context('../components/', true, /\.js$/));
文档的调试
组件开发的差不多了,就要编写相应的文档,方便同事小伙伴使用,执行 npm run dev:docs
会开启文档调试服务器,方便开发同学编写文档。
文档服务器的逻辑放在 docs
目录下,同样与组件代码解耦,左侧的组件信息动态取自 packages 目录下的组件信息,右侧的组件预览直接使用 examples
目录下的组件调试逻辑,中间的部分取自 组件中的 readme.md
文件。
整个文档应用也是基于 vue + vue-router
开发。
<div class="nav-bar-container">
<page-nav></page-nav>
</div>
<div class="document-area-container markdown-body">
<router-view></router-view>
</div>
<div class="mock-phone-container">
<page-preview :component-name="componentName"></page-preview>
</div>
<router-view>
就是对应的路由所展示的文档内容,相应的在定义路由信息时需要确定路由以及路由所对应的 readme.md
路径。
const routes = navList.map((item) => {
const path = item.path;
return {
path,
component: require('mds/' + item.mdRequire + '.md'),
};
});
const router = new VueRouter({
routes,
});
markdown 转换 vue
在引用组件时使用了 .md
后缀,这里是采用了 vue-markdown-loader
饿了么出品的loader。这个loader还是借助vue-loader,首先会将 md 的内容转换成 html ,然后再转换成 vue 所需要的单文件形式给vue-loader。
var renderVueTemplate = function(html, wrapper) {
// 本文作者注
// 传入的html是根据 markdown插件将md转换而来
var $ = cheerio.load(html, {
decodeEntities: false,
lowerCaseAttributeNames: false,
lowerCaseTags: false
});
...
// 本文作者注
// 将html转换成 vue-loader 所需的字符串形式
result =
`<template><${wrapper}>` +
$.html() +
`</${wrapper}></template>\n` +
output.style +
'\n' +
output.script;
return result;
};
var result =
'module.exports = require(' +
loaderUtils.stringifyRequest(
this,
'!!vue-loader!' +
markdownCompilerPath +
'?raw!' +
filePath +
(this.resourceQuery || '')
) +
');';
// 本文作者注
// 将转换好的字符串传给 vue-loader
return result;
2、基于 npm 脚本实现工程化
"scripts": {
"bootstrap": "npm i",
"dev:components": "node build/bin/dev-entry.js",
"dev:docs": "node build/bin/docs-dev-entry.js",
"build:docs": "node build/bin/docs-build.js",
"pub:docs": "npm run bootstrap && npm run clean && node build/bin/release.js",
"pub:components": "node build/bin/prepublish.js && lerna publish --skip-npm --skip-git && node build/bin/publish.js",
"clean": "rm -rf docs/dist && rm -rf docs/deploy",
"add": "node build/bin/add.js"
},
本项目中将所有常用的命令都进行了抽离,开发同学使用的命令最后暴露出4个:
npm run dev:components 组件的调试
npm run dev:docs 文档的调试
npm run pub:docs 文档的发布
npm run pub:components 组件的发布
推荐看阮一峰的博客 npm scripts 使用指南 ,将npm 脚本很细致的介绍了一遍。
自动生成脚手架
npm run add
会自动添加一个组件所需的脚手架信息,方便开发同学添加新组件。
这里推荐使用 json-templater/string
模块处理 string 模板的问题。
脚手架文件中的某些值会根据组件名的不同而不同,根据组件名自动生成对应的脚手架内容,更加方便开发。
npm link
组件在本地开发完成了,例子和文档都编写完毕,但不知在真实项目中使用会不会出现奇怪bug。
最原始的方法可以将组件复制到项目中的npm包中进行真实调试。
当然 npm 也提供了 方法专门解决这种问题。
1、首先在 spon-ui
组件库的根目录执行 npm link
2、回到项目目录,执行 npm link spon-ui
,两条命令就能将项目中原本引用的spon-ui 映射到本地的spon-ui
目录中去。
3、npm unlink
取消软链。
3、源码依赖
上节提到的npm 脚本并没有提到组件打包的流程,因为如果在组件这层就进行打包,会增加一些webpack的冗余代码,增加字节,而且这个组件库目前完全属于内部项目使用,打包环境在项目中就存在,没有必要提前进行打包。
所以发布出去的组件包就是packages下的所有组件,项目中所依赖的都是组件的源码,称为源码依赖。
要做到源码依赖,需要修改业务项目中(非本组件项目)的babel的配置。排除掉 spon-ui
组件
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules(?!\/.*(spon-ui).*)/,
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
],
},
滴滴有篇webpack 应用编译优化之路有讲到源码依赖所带来的好处。
4、发布组件
我们使用了 lerna 来管理组件的发布,lerna 有两种发布方式,一种是一个项目的所有组件作为一个发布包,还有一种可以将一个项目中的多个组件分别发布。
我们使用了第一种,即所有组件统一成一个发布包。这种方式发挥不出 lerna 的威力,但是作为发布前的版本号管理还是不错的。未来如果要将各个组件单独发布,改一下配置就ok。
版本管理
测试版本的管理
在前文就提到过目前组件库的开发还是依赖于需求的迭代,小团队没有人专门开发组件,组件的开发会跟随需求的迭代而迭代。
那么在前期组件变更需求通过评审会后,就会跟随项目正式进入开发流程。项目开发会区分测试环境和预发全量环境,那么组件的版本号也需要区分测试环境和全量环境。
npm publish --tag
介绍一下 publish 的 tag,发布的 npm 包默认会有一个 latest
标签,每次执行 npm publish
都会自动将 tag 设置为 latest,也可以理解为稳定版,所以我们要做的是再添加一个 tag
npm publish --tag dev
这个命令代表添加一个名为 dev
的tag,并将此次发布的版本号贴上 dev
标签。
执行 npm dist-tag ls spon-ui
可以查看当前的标签所对应的版本号信息。
➜ spon-ui git:(master) npm dist-tag ls spon-ui
dev: 0.1.0-12
latest: 0.1.0
在项目中安装spon-ui的时候,根据情况分别执行
npm i spon-ui@dev
npm i spon-ui@latest
5、npm5 package-lock.json
组件发布完成了,就可以在项目中使用了,我们从npm3.x更新到了npm5,但是发现执行 npm i
时的现象跟网络上的科普文不太一致。
有提到不管怎么修改package.json文件,重复执行npm i,npm都会根据lock文件描述的版本信息进行下载。
也有提到重复npm i时,npm会不顾lock的信息,根据package.json中的包Semantic versioning 版本信息下载更新模块(lock貌似没啥用了)。
查阅资料得知,自npm 5.0版本发布以来,npm i的规则发生了三次变化。
1、npm 5.0.x 版本,不管package.json怎么变,npm i 时都会根据lock文件下载
https://github.com/npm/npm/issues/16866
这个 issue 控诉了这个问题,明明手动改了package.json,为啥不给我升级包!然后就导致了5.1.0的问题...
2、5.1.0版本后 npm install 会无视lock文件 去下载最新的npm
然后有人提了这个issue https://github.com/npm/npm/issues/17979
控诉这个问题,最后演变成5.4.2版本后的规则。
3、5.4.2版本后 https://github.com/npm/npm/issues/17979
大致意思是,如果改了package.json,且package.json和lock文件不同,那么执行npm i
时npm会根据package中的版本号以及语义含义去下载最新的包,并更新至lock。
如果两者是同一状态,那么执行npm i
都会根据lock下载,不会理会package实际包的版本是否有新。
总结
以上就是我们将UI组件从项目中迁移出来单独以npm包的形式去维护的实践过程,不完美还有待时间的考验,希望其中的一些内容能帮助到大家。
「前端」尚妆 UI 组件库工程实践(weex vue)的更多相关文章
- [转载]前端——实用UI组件库
https://www.cnblogs.com/xuepei/p/7920888.html Angular UI 组件 ngx-bootstrap 是一套Bootstrap 组件 官网:https:/ ...
- 16款优秀的Vue UI组件库推荐
16款优秀的Vue UI组件库推荐 Vue 是一个轻巧.高性能.可组件化的MVVM库,API简洁明了,上手快.从Vue推出以来,得到众多Web开发者的认可.在公司的Web前端项目开发中,多个项目采用基 ...
- 强烈推荐优秀的Vue UI组件库
Vue 是一个轻巧.高性能.可组件化的MVVM库,API简洁明了,上手快.从Vue推出以来,得到众多Web开发者的认可.在公司的Web前端项目开发中,多个项目采用基于Vue的UI组件框架开发,并投入正 ...
- ui组件库
基于Vue的Quasar Framework 中文网 http://www.quasarchs.com/ quasarframework/quasar: Quasar Frameworkhttps:/ ...
- 【转】优秀的Vue UI组件库
原文来源:https://www.leixuesong.com/3342 Vue 是一个轻巧.高性能.可组件化的MVVM库,API简洁明了,上手快.从Vue推出以来,得到众多Web开发者的认可.在公司 ...
- 【转】前端——实用UI组件库
Angular UI 组件 ngx-bootstrap 是一套Bootstrap 组件 官网:https://valor-software.com/ngx-bootstrap/#/ github: h ...
- 前端——实用UI组件库
Angular UI 组件 ngx-bootstrap 是一套Bootstrap 组件 官网:https://valor-software.com/ngx-bootstrap/#/ github: h ...
- 前端笔记之Vue(四)UI组件库&Vuex&虚拟服务器初识
一.日历组件 new Date()的月份是从0开始的. 下面表达式是:2018年6月1日 new Date(2018, 5, 1); 下面表达式是:2018年5月1日 new Date(2018, 4 ...
- Web 前端 UI 组件库文档自动化方案 All In One
Web 前端 UI 组件库文档自动化方案 All In One 需求 自动化 动态 好用 markdown element-ui 中示例和说明按照一定规则写在md文件中,调用md-loader将md文 ...
随机推荐
- Tika提取文件元数据
Tika可以从文件中提取元数据. 什么是元数据: 元数据是文件所提供的的附件信息即文件的属性. word文档的元数据: Tika提取元数据: 我们可以使用文件parse()方法提取元数据,传递一个空的 ...
- 深入理解hadoop之HDFS
深入理解hadoop之HDFS 刚刚才写完关于mapreduce的一篇博文,趁热打铁接下来聊聊HDFS.本博文参考资料为HADOOP权威指南第3版完版,博文如有错漏之处,敬请指正. HDFS即Hado ...
- PHP扩展之 Imagick安装
最近的PHP项目中,需要用到切图和缩图的效果,在本地windows开发环境,安装过程遇到好多问题,在此与大家分享. php官网里,一大群老外也看不懂这玩意怎么装,主要原因在于,php版本庞杂,还有x8 ...
- Go学习笔记【一、概述】
工作需要,即将要把部分查询计算的逻辑翻译成Go语言,来提升查询效率 在此需要对Go语言进行学习,先把入门教程浏览了一遍,发现有些语法和C很类似,还有部分类似于Python,还没有深入学习,先开个坑,业 ...
- MySQL8.x msi版安装教程
一.下载MySQL 官网下载地址 https://dev.mysql.com/downloads/windows/installer/8.0.html 下载第二个即可(虽然只有32位的 但是会同时安 ...
- Linux下Mysql 不能访问新数据文件夹问题
新挂载的盘,打算将数据文件夹配置到 /data/mysql,却无法启动mysqld. 除了将目录授权给mysql用户和组以外 chown -R mysql:mysql /data/mysql 太需要将 ...
- 如何设置树莓派 -Zero 自启动连接WIFI
1. 首先我们需要一台可以读取树莓派跟文件系统的Linux虚拟机(比如Ubuntu) 将树莓派SD卡系统插入电脑,识别并打开rootfs文件夹,切换到`rootfs/etc/wpa_supplican ...
- 8.Netty发送对象
1.Netty发送对象简介: Netty中,通讯的双方建立连接后,会把数据按照ByteBuf的方式进行传输,例如http协议中,就是通过HttpRequestDecoder对 ByteBuf数 据流进 ...
- Alpha版本第一周小结
姓名 学号 周前计划安排 每周实际工作记录 自我打分(百分制) HTB 061126 1.博客撰写,分配任务 2.编码实现各个模块的功能 1.撰写博客 2.已初步实现各个模块的功能,对某些数据处理还存 ...
- VUE-文本-事件-属性指令
一.Vue文本指令 文本指令: 1.{{ }} 2.v-text:不能解析html语法的文本,会原样输出 3.v-html:能解析html语法的文本 4.v-once:处理的标签的内容只能被解析一次 ...