前言
上一篇文章中我们实现了组件库的局部引入,对于性能的提升有较大的改善,这样看来好像我们已经完成了一个简单的组件库的开发,是不是直接 publish 到 npm 库就可以直接使用了呢?其实并非如此,我们还需要处理一个比较重要的细节 —— 我们发现上一章节中在引入组件库的时候会在 main.js
中加一行代码:import '@/index.less'
。这一行代码在此处的意义是什么呢?
1. 组件库的样式处理
在 demo
项目中,如果我们屏蔽 import '@/index.less'
这行代码,在启动服务会发现样式并没有生效。我们先看一下这行代码的内容:
// components 目录下
@import './button/index.less';
@import './icon/index.less';
@import './nav-bar/index.less';
相当于已经开发完成的button
、icon
、nav-bar
等组件都是通过这里来引用,之前在组件开发过程中,我们将每个组件目录的vue 文件
和 less 文件
分开,然后在components 下的index.less 中再全部引用。所以如果在入口文件没有添加代码 import '@/index.less'
,相当于每个组件都没有引入less 样式代码,因此ui样式就不会生效。最好的办法就是可以将样式相关的代码全部提取处理处理出来,然后统一在项目入口引用,市面上类似的ui组件库大多采取这种方法。
2. 提取样式代码
对于样式代码的处理,我们的思路就是通过提取不同组件下的less文件,然后集中打包到一个预制文件目录 theme 下,layui
之前是通过采用 gulp
打包的方式实现的,使用Gulp打包样式的主要原因如下:
- 可以解决浏览器缓存问题、依赖问题,并通过合并和压缩文件来优化性能。此外,Gulp还可以处理代码语法,解决不同浏览器的兼容问题
- Gulp作为一个基于流的自动化构建工具,通过配置一系列任务来规范前端开发流程。它适合多页面应用开发,能够读取数据源,通过管道处理后输出到目标路径。
- Gulp的插件系统支持各种任务,如编译Sass、Less、压缩图片等,使得样式处理变得高效和灵活
本地安装gulp
插件,然后在根目录下新增文件 gulpfile.js
, 在 package.json
中新增脚本命令: "build:style": "gulp build"
, 执行 npm run build:style
会自动执行 gulpfile.js
中的脚本gulpfile.js代码如下:
const glob = require("glob")
const fs = require("fs")
const path = require("path")
const { series, src, dest } = require("gulp")
const less = require('gulp-less');
const autoprefixer = require('gulp-autoprefixer');
const cssnano = require('gulp-cssnano');
const replace = require('gulp-replace');
const clean = require("gulp-clean")
const rename = require("gulp-rename")
const merge = require("merge-stream")
// 清理代码
function clear() {
return src("./themes").pipe(clean())
}
// 复制components 目录下的所有 less 文件
function copyLess() {
return src(["./components/**/*.less"]).pipe(dest("./themes/less"))
}
// 如果没有less文件,补充空的less文件,不然按需引入的时候找不到文件会报错
function addBlankLess() {
const blankFile = src("./public/blank.less")
const files = glob.sync("./components/!(utils|style)/")
const array = []
files.forEach((file) => {
const lessPath = path.join(__dirname, file, 'index.less')
if (!fs.existsSync(lessPath)) {
const fileName = file.match(/components\/(.*)\//)[1]
console.log('file:::::', fileName)
const stream = blankFile.pipe(rename('index.less')).pipe(dest('./themes/less/' + fileName))
array.push(stream)
}
})
return merge(array);
}
addBlankLess()
// 复制fonts文件
function copyFont() {
return src(["./components/icon/fonts/**"]).pipe(dest("./themes/less/icon/fonts"))
}
// 编译less文件
function compile() {
return src(["./themes/less/**/index.less"])
.pipe(less()) // less编译成css
.pipe(autoprefixer()) // 兼容浏览器加前缀
.pipe(cssnano()) // 压缩文件
.pipe(dest("./themes/default"))
}
// 复制fonts文件到default
function copyDefaultFont() {
return src(["./themes/less/icon/fonts/**"]).pipe(dest("./themes/default/icon/fonts"))
}
// 编译后的index.css下的font路径错误,需进行处理替换
function replaceFonts() {
return src(["./themes/default/index.css"]).pipe(replace(/fonts\//g,'icon/fonts/')).pipe(dest('./themes/default'))
}
// 样式处理序列任务
const cssArr = [copyLess,addBlankLess, copyFont, compile, copyDefaultFont, replaceFonts];
// themes文件夹存在执行清理操作
if (fs.existsSync(path.join(__dirname, './themes'))) {
cssArr.unshift(clear);
}
// commonjs 默认导出modules.exports对象,而exports指向该对象,所以不能直接给exports赋值,会破坏了引用关系
exports.build = series(...cssArr); // series() 方法让任务按顺序执行
3.初版本的发布及项目使用
上一步执行 npm run build:style
之后,会生成 themes
目录,该目录是提取的所有的样式代码,我们将
lib
、 packages
、 themes
等文件汇总在一个单独的文件下,其结构如下:
all_pabst_ui
├─ .npmrc
├─ lib
├─ package.json
├─ packages
└─ themes
根据每个公司自己的发布私有npm镜像库的规则,可以自己处理发布流程:我们此处需要添加 .npmrc
文件配置发布相关的用户和密码等加密信息,还需要在 package.json
中配置版本信息、组件库描述信息等。完成发布之后,在通过 npm i xxx
的方式在demo 项目中安装试用:
// main.js
...
import 'all_pabst_ui/themes/less/index.less'
import Button from 'all_pabst_ui/packages/button'
import Icon from 'all_pabst_ui/packages/icon'
import Navbar from 'all_pabst_ui/packages/nav-bar'
import Test from 'all_pabst_ui/packages/test'
Vue.component(Button.name, Button)
Vue.component(Icon.name, Icon)
Vue.component(Navbar.name, Navbar)
Vue.component(Test.name, Test)
...