如何快速为团队打造自己的组件库(下)—— 基于 element-ui 为团队打造自己的组件库
文章已收录到 github,欢迎 Watch 和 Star。
简介
在了解 Element 源码架构 的基础上,接下来我们基于 element-ui 为团队打造自己的组件库。
主题配置
基础组件库在 UI 结构上差异很小,一般只是在主题色上会有较大差异,毕竟每个团队都有了 UI 风格。比如,我们团队的 UI 设计稿其实是基于 Ant Design 来出的,而组件库是基于 Element-UI 来开发,即使是这种情况,对组件本身的改动也很少。所以,基于开源库打造团队自己的组件库时,主题配置就很重要了。
element-ui 的一大特色就是支持自定义主题,它通过在线主题编辑器、Chrome 插件或命令行主题工具这三种方式来定制 element-ui 所有组件的样式。那么 element-ui 是怎么做到这一点的呢?
因为 element-ui 组件样式中的颜色、字体、线条等样式都是通过变量的方式引入的,在 packages/theme-chalk/src/common/var.scss
中可以看到这些变量的定义,这就为自定义主题提供了方便,因为我们只需要修改这些变量,就可以实现组件主题的改变。
在线主题编辑器和 Chrome 插件支持实时预览。并且可以下载定制的样式包,然后使用。在线主题编辑器和 Chrome 插件的优点是可视化,简洁明了,但是有个最大的缺点就是,最后下载出来的是一个将所有组件样式打包到一起的样式包,没办法支持按需加载,不推荐使用。这里我们使用命令行主题工具来定制样式。
命令行主题工具
初始化项目目录并安装主题生成工具(element-theme)
mkdir theme && cd theme && npm init -y && npm i element-theme -D
安装白垩主题
npm i element-theme-chalk -D
初始化变量文件
node_modules/.bin/et -i
命令执行以后可能会得到如下报错信息
原因是 element-theme 包中依赖了低版本的 graceful-fs,低版本 graceful-fs 在高版本的 node.js 中不兼容,最简单的方案是升级 graceful-fs。
在项目根目录下创建
npm-shrinkwrap.json
文件,并添加如下内容:{
"dependencies": {
"graceful-fs": {
"version": "4.2.2"
}
}
}
运行
npm install
重新安装依赖即可解决,然后重新执行node_modules/.bin/et -i
,执行完以后会在当前目录生成element-variables.scss
文件。修改变量
直接编辑
element-variables.scss
文件,例如修改主题色为红色,将文件中的$--color-primary
的值修改为red
,$--color-primary: red !default;
。文件中写了很好的注释,并且样式代码也是按照组件来分割组织的,所以大家可以对照设计团队给到的设计稿来一一修改相关的变量。如果实在觉得看代码比较懵,可以参照在线主题编辑器,两边的变量名是一致的。
题外话:element-ui 还提供了两个资源包,供设计团队使用,所以最理想的是,让设计团队根据 element-ui 的资源包出设计稿,这样两边就可以做到统一,研发团队的工作量也会降低不少。比如我们团队就不是这样,设计团队给到的设计稿是基于 Ant Design 出的,研发组件库时改动的工作量和难度就会相对比较大。所以研发、设计、产品一定要进行很好的沟通。
编译主题
修改完以后,保存文件,然后执行以下命令编译主题,会产生一个 theme 目录。生产出来都是 CSS 样式文件,文件名和组件名一一对应,支持按需引入(指定组件的样式文件)和全量引入(index.css)。
生产未压缩的样式文件
node_modules/.bin/et --out theme-chalk
生产经过压缩的样式文件
node_modules/.bin/et --minimize --out theme-chalk
帮助命令
node_modules/.bin/et --help
启用
watch
模式,实时编译主题node_modules/.bin/et --watch --out theme-chalk
使用自定义主题
用新生成的主题目录(theme-chalk)替换掉框架中的
packages/theme-chalk
目录。重命名老的 theme-chalk 为 theme-chalk.bak,不要删掉,后面需要用建议将生成主题时用到的 element-variables.scss 文件保存在项目中,因为以后你可能会需要重新生成主题
修改
/examples/entry.js
、/examples/play.js
、/examples/extension/src/app.js
中引入的组件库的样式// 用新的样式替换旧的默认样式
// import 'packages/theme-chalk/src/index.scss
import 'packages/theme-chalk/index.css
修改
/build/bin/iconInit.js
中引入的图标样式文件// var fontFile = fs.readFileSync(path.resolve(__dirname, '../../packages/theme-chalk/src/icon.scss'), 'utf8');
var fontFile = fs.readFileSync(path.resolve(__dirname, '../../packages/theme-chalk/icon.css'), 'utf8');
修改
/examples/docs/{四种语言}/custom-theme.md
// @import "~element-ui/packages/theme-chalk/src/index";
@import "~element-ui/packages/theme-chalk/index";
执行
make dev
启动开发环境,查看效果
到这一步,主题配置就结束了,你会发现,element-ui 官网的组件样式基本上和设计稿上的一致。但是仔细对比后,会发现有一些组件的样式和设计稿有差异,这时候就需要对这些组件的样式进行深度定制,覆写不一致的样式。
其实这块儿漏掉了 /build/bin/new.js 中涉及的样式目录,这块儿的改动会放到后面
样式深度定制
上一步的主题配置,只能解决主题相关的样式,但是有些组件的有些样式不属于主题样式,如果这部分样式刚好又和设计稿不一致的话,那就需要重写这部分样式去覆盖上一步的样式。
以下配置还支持为自定义组件添加样式
样式目录
将
主题配置
步骤中备份的/packages/theme-chalk.bak
重命名为/packages/theme-lyn
,作为覆写组件和自定义组件的样式目录删掉
/packages/theme-lyn/src
目录的所有文件你会写 scss ?
- 忽略掉下一步,然后后续步骤你只需将对应的 less 操作换成 sass 即可
你不会写 scss,扩展其它语法,假设你会写 less
在项目根目录执行以下命令,然后删掉 gulp-sass
npm i less less-loader gulp-less -D && npm uninstall gulp-sass -D
如果一会儿启动开发环境以后,报错 “TypeError: this.getOptions is not a function”,则降级 less-loader 版本,比如我的版本是:less@3.7.1、less-loader@7.3.0
在
/packages/theme-lyn
目录下执行以下命令,然后删掉 gulp-sassnpm i gulp-less -D && npm uninstall gulp-sass -D
将
/packages/theme-lyn/gulpfile.js
更改为以下内容'use strict'; /**
* 将 ./src/*.less 文件编译成 css 文件输出到 ./lib 目录
* 将 ./src/fonts/中的所有字体文件输出到 ./lib/fonts 中,如果你没有覆写字体样式的需要,则删掉拷贝字体样式部分
*/
const { series, src, dest } = require('gulp');
const less = require('gulp-less');
const autoprefixer = require('gulp-autoprefixer');
const cssmin = require('gulp-cssmin');
const path = require('path') function compile() {
return src('./src/*.less')
.pipe(less({
paths: [ path.join(__dirname, './src') ]
}))
.pipe(autoprefixer({
browsers: ['ie > 9', 'last 2 versions'],
cascade: false
}))
.pipe(cssmin())
.pipe(dest('./lib'));
} function copyfont() {
return src('./src/fonts/**')
.pipe(cssmin())
.pipe(dest('./lib/fonts'));
} // 也可以在这里扩展其它功能,比如拷贝静态资源 exports.build = series(compile, copyfont);在
build/webpack.demo.js
中增加解析less
文件的规则{
test: /\.less$/,
use: [
isProd ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
'less-loader'
]
}
假如你要覆写 button 组件的部分样式
在
/packages/theme-lyn/src
目录下新建button.less
文件,编写覆写样式时请遵循如下规则- 组件样式的覆写,最好遵循 BEM 风格,目的是提供良好的命名空间隔离,避免样式打包以后发生意料之外的覆盖
- 只覆写已有的样式,可以在组件上新增类名,但不要删除,目的是兼容线上代码
// 这里我要把主要按钮的字号改大有些,只是为了演示效果
.el-button--primary {
font-size: 24px;
}
改造
build/bin/gen-cssfile.js
脚本/**
* 将各个覆写的样式文件在 packages/theme-lyn/src/index.less 文件中自动引入
*/ var fs = require('fs');
var path = require('path'); // 生成 theme-lyn/src 中的 index.less 文件
function genIndexLessFile(dir) {
// 文件列表
const files = fs.readdirSync(dir);
/**
* @import 'x1.less';
* @import 'x2.less;
*/
let importStr = "/* Automatically generated by './build/bin/gen-cssfile.js' */\n"; // 需要排除的文件
const excludeFile = ['assets', 'font', 'index.less', 'base.less', 'variable.less']; files.forEach(item => {
if (excludeFile.includes(item) || !/\.less$/.test(item)) return; // 只处理非 excludeFile 中的 less 文件
importStr += `@import "./${item}";\n`;
}); // 在 packages/theme-lyn/src/index.less 文件中写入 @import "xx.less",即在 index.less 中引入所有的样式文件
fs.writeFileSync(path.resolve(dir, 'index.less'), importStr);
} genIndexLessFile(path.resolve(__dirname, '../../packages/theme-lyn/src/'));在项目根目录下执行以下命令
npm i shelljs -D
新建
/build/bin/compose-css-file.js
/**
* 负责将打包后的两个 css 目录(lib/theme-chalk、lib/theme-lyn)合并
* lib/theme-chalk 目录下的样式文件是通过主题配置自动生成的
* lib/theme-lyn 是扩展组件的样式(覆写默认样式和自定义组件的样式)
* 最后将样式都合并到 lib/theme-chalk 目录下
*/
const fs = require('fs');
const fileSave = require('file-save');
const { resolve: pathResolve } = require('path');
const shelljs = require('shelljs'); const themeChalkPath = pathResolve(__dirname, '../../lib/theme-chalk');
const themeStsUIPath = pathResolve(__dirname, '../../lib/theme-lyn'); // 判断样式目录是否存在
let themeChalk = null;
let themeStsUI = null;
try {
themeChalk = fs.readdirSync(themeChalkPath);
} catch (err) {
console.error('/lib/theme-chalk 不存在');
process.exit(1);
}
try {
themeStsUI = fs.readdirSync(themeStsUIPath);
} catch (err) {
console.error('/lib/theme-lyn 不存在');
process.exit(1);
} /**
* 遍历两个样式目录,合并相同文件,将 theme-lyn 的中样式追加到 theme-chalk 中对应样式文件的末尾
* 如果 theme-lyn 中的文件在 theme-chalk 中不存在(比如扩展的新组件),则直接将文件拷贝到 theme-chalk
*/
const excludeFiles = ['element-variables.css', 'variable.css'];
for (let i = 0, themeStsUILen = themeStsUI.length; i < themeStsUILen; i++) {
if (excludeFiles.includes(themeStsUI[i])) continue; if (themeStsUI[i] === 'fonts') {
shelljs.cp('-R', pathResolve(themeStsUIPath, 'fonts/*'), pathResolve(themeChalkPath, 'fonts'));
continue;
} if (themeStsUI[i] === 'assets') {
shelljs.cp('-R', pathResolve(themeStsUIPath, 'assets'), themeChalkPath);
continue;
} if (themeChalk.includes(themeStsUI[i])) {
// 说明当前样式文件是覆写 element-ui 中的样式
const oldFileContent = fs.readFileSync(pathResolve(themeChalkPath, themeStsUI[i]), { encoding: 'utf-8' });
fileSave(pathResolve(themeChalkPath, themeStsUI[i])).write(oldFileContent).write(fs.readFileSync(pathResolve(themeStsUIPath, themeStsUI[i])), 'utf-8').end();
} else {
// 说明当前样式文件是扩展的新组件的样式文件
// fs.writeFileSync(pathResolve(themeChalkPath, themeStsUI[i]), fs.readFileSync(pathResolve(themeStsUIPath, themeStsUI[i])));
shelljs.cp(pathResolve(themeStsUIPath, themeStsUI[i]), themeChalkPath);
}
} // 删除 lib/theme-lyn
shelljs.rm('-rf', themeStsUIPath);改造 package.json 中的 scripts
{
"gen-cssfile:comment": "在 /packages/theme-lyn/src/index.less 中自动引入各个组件的覆写样式文件",
"gen-cssfile": "node build/bin/gen-cssfile",
"build:theme:comment": "构建主题样式:在 index.less 中自动引入各个组件的覆写样式文件 && 通过 gulp 将 less 文件编译成 css 并输出到 lib 目录 && 拷贝基础样式 theme-chalk 到 lib/theme-chalk && 拷贝 编译后的 theme-lyn/lib/* 目录到 lib/theme-lyn && 合并 theme-chalk 和 theme-lyn",
"build:theme": "npm run gen-cssfile && gulp build --gulpfile packages/theme-lyn/gulpfile.js && cp-cli packages/theme-lyn/lib lib/theme-lyn && cp-cli packages/theme-chalk lib/theme-chalk && node build/bin/compose-css-file.js",
}
执行以下命令
npm run gen-cssfile
改造
/examples/entry.js
和/examples/play.js
// 用新的样式替换旧的默认样式
// import 'packages/theme-chalk/src/index.scss
import 'packages/theme-chalk/index.css // 在这行下面引入自定义样式
// 引入自定义样式
import 'packages/theme-lyn/src/index.less'
访问官网,查看 button 组件的覆写样式是否生效
自定义组件
组件库在后续的开发和迭代中,需要两种自定义组件的方式:
增加新的 element-ui 组件
element-ui 官网可能在某个时间点增加一个你需要的基础组件,这时你需要将其集成进来
增加业务组件
基础组件就绪以后,团队就会开始推动业务组件的建设,这时候就会向组件库中增加新的组件
新的 element-ui 组件
element-ui 提供了增加新组件的脚本,执行 make new <component-name> [中文名]
即可生成新组件所需的所有文件以及配置,比如:make new button 按钮
,有了该脚本可以让你专注于组件的编写,不需要管任何配置。
/build/bin/new.js
但是由于我们调整了框架主题库的结构,所以脚本文件也需要做相应的调整。需要将 /build/bin/new.js
文件中处理样式的代码删掉,样式文件不再需要脚本自动生成,而是通过重新生成主题的方式实现。
// /build/bin/new.js 删掉以下代码
{
filename: path.join('../../packages/theme-chalk/src', `${componentname}.scss`),
content: `@import "mixins/mixins";
@import "common/var";
@include b(${componentname}) {
}`
},
// 添加到 index.scss
const sassPath = path.join(__dirname, '../../packages/theme-chalk/src/index.scss');
const sassImportText = `${fs.readFileSync(sassPath)}@import "./${componentname}.scss";`;
fileSave(sassPath)
.write(sassImportText, 'utf8')
.end('\n');
Makefile
改造 Makefile
文件,在 new
配置后面增加 && npm run build:file
命令,重新生成组件库入口文件,不然不会引入新增加的组件。
new:
node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS)) && npm run build:file
增加新组件
完成上述改动以后,只需两步即可完成新 element-ui 组件的创建:
执行
make new <component-name> [组件中文名]
命令新建新的 element-ui 组件这一步会生成众多文件,你只需要从新的 element-ui 源码中将该组件对应的代码复制过来填充到对应的文件即可
重新生成主题,然后覆盖现在的
/packages/theme-chalk
业务组件
新增的业务组件就不要以 el 开头了,避免和 element 组件重名或造成误会。需要模拟 /build/bin/new.js
脚本写一个新建业务组件的脚本 /build/bin/new-lyn-ui.js
,大家可以基于该脚本去扩展。
/build/bin/new-lyn-ui.js
'use strict';
/**
* 新建组件脚本,以 lyn-city 组件为例
* 1、在 packages 目录下新建组件目录,并完成目录结构的基本创建
* 2、创建组件文档
* 3、组件单元测试文件
* 4、组件样式文件
* 5、组件类型声明文件
* 6、并将上述新建的相关资源自动添加的相应的文件,比如组件组件注册到 components.json 文件、样式文件在 index.less 中自动引入等
* 总之你只需要专注于编写你的组件代码即可,其它一概不用管
*/
console.log();
process.on('exit', () => {
console.log();
});
if (!process.argv[2]) {
console.error('[组件名]必填 - Please enter new component name');
process.exit(1);
}
const path = require('path');
const fs = require('fs');
const fileSave = require('file-save');
const uppercamelcase = require('uppercamelcase');
// 组件名称 city
const componentname = process.argv[2];
// 组件中文名 城市列表
const chineseName = process.argv[3] || componentname;
// 组件大驼峰命名 City
const ComponentName = uppercamelcase(componentname);
// 组件路径:/packages/city
const PackagePath = path.resolve(__dirname, '../../packages', componentname);
const Files = [
// packages/city/index.js 的内容
{
filename: 'index.js',
content: `import ${ComponentName} from './src/main';
/* istanbul ignore next */
${ComponentName}.install = function(Vue) {
Vue.component(${ComponentName}.name, ${ComponentName});
};
export default ${ComponentName};`
},
// packages/city/src/main.vue 组件定义
{
filename: 'src/main.vue',
content: `<template>
<div class="lyn-${componentname}"></div>
</template>
<script>
export default {
name: 'Lyn${ComponentName}'
};
</script>`
},
// 组件中文文档
{
filename: path.join('../../examples/docs/zh-CN', `${componentname}.md`),
content: `## ${ComponentName} ${chineseName}`
},
// 组件单元测试文件
{
filename: path.join('../../test/unit/specs', `${componentname}.spec.js`),
content: `import { createTest, destroyVM } from '../util';
import ${ComponentName} from 'packages/${componentname}';
describe('${ComponentName}', () => {
let vm;
afterEach(() => {
destroyVM(vm);
});
it('create', () => {
vm = createTest(${ComponentName}, true);
expect(vm.$el).to.exist;
});
});
`
},
// 组件样式文件
{
filename: path.join(
'../../packages/theme-lyn/src',
`${componentname}.less`
),
content: `@import "./base.less";\n\n.lyn-${componentname} {
}`
},
// 组件类型声明文件
{
filename: path.join('../../types', `${componentname}.d.ts`),
content: `import { LynUIComponent } from './component'
/** ${ComponentName} Component */
export declare class Lyn${ComponentName} extends LynUIComponent {
}`
}
];
// 将新组件添加到 components.json
const componentsFile = require('../../components.json');
if (componentsFile[componentname]) {
console.error(`${componentname} 已存在.`);
process.exit(1);
}
componentsFile[componentname] = `./packages/${componentname}/index.js`;
fileSave(path.join(__dirname, '../../components.json'))
.write(JSON.stringify(componentsFile, null, ' '), 'utf8')
.end('\n');
// 在 index.less 中引入新组件的样式文件
const lessPath = path.join(
__dirname,
'../../packages/theme-lyn/src/index.less'
);
const lessImportText = `${fs.readFileSync(
lessPath
)}@import "./${componentname}.less";`;
fileSave(lessPath).write(lessImportText, 'utf8').end('\n');
// 添加到 element-ui.d.ts
const elementTsPath = path.join(__dirname, '../../types/element-ui.d.ts');
let elementTsText = `${fs.readFileSync(elementTsPath)}
/** ${ComponentName} Component */
export class ${ComponentName} extends Lyn${ComponentName} {}`;
const index = elementTsText.indexOf('export') - 1;
const importString = `import { Lyn${ComponentName} } from './${componentname}'`;
elementTsText =
elementTsText.slice(0, index) +
importString +
'\n' +
elementTsText.slice(index);
fileSave(elementTsPath).write(elementTsText, 'utf8').end('\n');
// 新建刚才声明的所有文件
Files.forEach(file => {
fileSave(path.join(PackagePath, file.filename))
.write(file.content, 'utf8')
.end('\n');
});
// 将新组建添加到 nav.config.json
const navConfigFile = require('../../examples/nav.config.json');
Object.keys(navConfigFile).forEach(lang => {
const groups = navConfigFile[lang].find(item => Array.isArray(item.groups))
.groups;
groups[groups.length - 1].list.push({
path: `/${componentname}`,
title:
lang === 'zh-CN' && componentname !== chineseName
? `${ComponentName} ${chineseName}`
: ComponentName
});
});
fileSave(path.join(__dirname, '../../examples/nav.config.json'))
.write(JSON.stringify(navConfigFile, null, ' '), 'utf8')
.end('\n');
console.log('DONE!');
Makefile
在 Makefile
中增加如下配置:
new-lyn-ui:
node build/bin/new-lyn-ui.js $(filter-out $@,$(MAKECMDGOALS)) && npm run build:file
help:
@echo " \033[35mmake new-lyn-ui <component-name> [中文名]\033[0m\t--- 创建新的 LynUI 组件 package. 例如 'make new-lyn-ui city 城市选择'"
icon 图标
element-ui 虽然提供了大量的 icon,但往往不能满足团队的业务需求,所有就需要往组件库中增加业务 icon,这里以 Iconfont 为例。不建议直接使用设计给的图片或者 svg,太占资源了。
登陆 -> 资源管理 -> 我的项目 -> 新建项目
注意,这里为 icon 设置前缀时不要使用 el-icon-,避免和 element-ui 中的 icon 重复。这个项目就作为团队项目使用了,以后团队所有的业务图标都上传到该项目,所以最好注册一个团队账号。
新建成功后,点击
上传图标至项目
,选择上传图标
,上传设计给的 svg(必须是 svg),根据需要选择保留颜色或不保留并提交
上传完毕,编辑、检查没问题后,点击
下载至本地
复制其中的
iconfont.ttf
和iconfont.woff
到/packages/theme-lyn/src/fonts
目录下新建
/packages/theme-lyn/src/icon.less
文件,并添加如下内容@font-face {
font-family: 'iconfont';
src: url('./fonts/iconfont.woff') format('woff'), url('./fonts/iconfont.ttf') format('truetype');
font-weight: normal;
font-display: auto;
font-style: normal;
} [class^="lyn-icon-"], [class*=" lyn-icon-"] {
font-family: 'iconfont' !important;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
vertical-align: baseline;
display: inline-block; /* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale
} /**
* 示例:
* .lyn-icon-iconName:before {
* content: "\unicode 16进制码"
* }
* .lyn-icon-add:before {
* content: "\e606"
* }
*/
执行
npm run gen-cssfile
更新
/build/bin/iconInit.js
文件为以下内容'use strict'; var postcss = require('postcss');
var fs = require('fs');
var path = require('path'); /**
* 从指定的 icon 样式文件(entry)中按照给定正则表达式(regExp)解析出 icon 名称,然后输出到指定位置(output)
* @param {*} entry 被解析的文件相对于当前文件的路径,比如:../../packages/theme-chalk/icon.css
* @param {*} regExp 被解析的正则表达式,比如:/\.el-icon-([^:]+):before/
* @param {*} output 解析后的资源输出到相对于当前文件的指定位置,比如:../../examples/icon.json
*/
function parseIconName(entry, regExp, output) {
// 读取样式文件
var fontFile = fs.readFileSync(path.resolve(__dirname, entry), 'utf8');
// 将样式内容解析为样式节点
var nodes = postcss.parse(fontFile).nodes;
var classList = []; // 遍历样式节点
nodes.forEach((node) => {
// 从样式选择器中根据给定匹配规则匹配出 icon 名称
var selector = node.selector || '';
var reg = new RegExp(regExp);
var arr = selector.match(reg); // 将匹配到的 icon 名称放入 classList
if (arr && arr[1]) {
classList.push(arr[1]);
}
}); classList.reverse(); // 希望按 css 文件顺序倒序排列 // 将 icon 名称数组输出到指定 json 文件中
fs.writeFile(path.resolve(__dirname, output), JSON.stringify(classList), () => { });
} // 根据 icon.css 文件生成所有的 icon 图标名
parseIconName('../../packages/theme-chalk/icon.css', /\.el-icon-([^:]+):before/, '../../examples/icon.json') // 根据 icon.less 文件生成所有的 sts icon 图标名
parseIconName('../../packages/theme-lyn/src/icon.less', /\.lyn-icon-([^:]+):before/, '../../examples/lyn-icon.json')执行
npm run build:file
,会看到在/examples
目录下生成了一个lyn-icon.json
文件在
/examples/entry.js
中增加如下内容import lynIcon from './lyn-icon.json';
Vue.prototype.$lynIcon = lynIcon; // StsIcon 列表页用
在
/examples/nav.config.json
中业务配置部分增加lyn-icon
路由配置{
"groupName": "LynUI",
"list": [
{
"path": "/lyn-icon",
"title": "icon 图标"
}
]
}
增加文档
/examples/docs/{语言}/lyn-icon.md
,添加如下内容查看官网 看图标是否生效
后续如需扩展新的 icon
在前面新建的 iconfont 项目中上传新的图标,然后点击
下载至本地
,将其中的iconfont.ttf
和iconfont.woff
复制/packages/theme-lyn/src/fonts
目录下即可(替换已有的文件)在
/packages/theme-lyn/src/icon.less
中设置新的 icon 样式声明执行
npm run build:file
查看官网 看图标添加是否成功
升级 Vue 版本
element-ui 本身依赖的是 vue@^2.5.x,该版本的 vue 不支持最新的 v-slot
插槽语法(v-slot 是在 2.6.0 中新增的),组件的 markdown 文档中使用 v-slot
语法不生效且会报错,所以需要升级 vue 版本。涉及三个包:vue@2.6.12、@vue/component-compiler-utils@3.2.0、vue-template-compiler@^2.6.12。执行以下命令即可完成更新:
删除旧包
npm uninstall vue @vue/component-compiler-utils vue-template-compiler -D
安装新包
npm install vue@^2.6.12 @vue/component-compiler-utils@^3.2.0 vue-template-compiler@^2.6.12 -D
更新 package.json 中的 peerDependencies
{
"peerDependencies": {
"vue": "^2.6.12"
}
}
扩展
到这里,组件库的架构调整其实已经完成了,接下来只需组织团队成员对照设计稿进行组件开发就可以了。但是对于一些有洁癖的开发者来说,其实还差点。
比如:
团队的组件库不想叫 element-ui,有自己的名称,甚至整个组件库的代码都不想出现 element 字样
element-ui 的某些功能团队不需要,比如:官网项目(examples)中的主题、资源模块、chrome 插件(extension)、国际化相关(只保留中文即可)
静态资源,element-ui 将所有的静态资源都上传到自己的 CDN 上了,我们去访问其实优点慢,可以将相关资源挪到团队自己的 CDN 上
工程代码质量问题,element-ui 本身提供了 eslint,做了一点代码质量的控制,但是做的不够,比如格式限制、自动格式化等,可以参考 搭建自己的 typescript 项目 + 开发自己的脚手架工具 ts-cli 中的 代码质量 部分去配置
替换官网 logo、渲染信息等
element-ui 样式库的优化,其实 element-ui 的样式存在重复加载的问题
虽然它通过 webpack 打包已经解决了一部分问题,但是某些情况还是会出现重复加载,比如 table 组件中使用 checkbox 组件,就会加载两次 checkbox 组件的样式代码。有精力的同学可以去研究研究
你的业务只需要 element-ui 的部分基础组件,把不需要的删掉,可以降低组件库的体积,提升加载速度
...
这些工作有一些是对官网项目(examples)的裁剪,有一些是项目整体优化,还有一些是洁癖,不过,相信凡是进行到这一步的同学,都已经为团队构建出了自己的组件库,解决以上列出的那些问题完全不再话下,这里就不一一列出方法了。
链接
- Element 源码架构 思维导图版
- 组件库专栏
- 如何快速为团队打造自己的组件库(上)—— Element 源码架构
- Element 源码架构 视频版,关注微信公众号,回复: "Element 源码架构视频版" 获取
文章已收录到 github,欢迎 Watch 和 Star。
如何快速为团队打造自己的组件库(下)—— 基于 element-ui 为团队打造自己的组件库的更多相关文章
- vue+element ui 的上传文件使用组件
前言:工作中用到 vue+element ui 的前端框架,使用到上传文件,则想着封装为组件,达到复用,可扩展.转载请注明出处:https://www.cnblogs.com/yuxiaole/p/9 ...
- 打造静态分析器(二)基于Asp.Net Core 3.0的AspectCore组件检测
上一篇,我们打造了一个简单的分析器,但是我们实际使用分析器就是为了对项目做分析检测,增加一些非语法的自检的 比如Asp.Net Core 3.0的替换依赖注入检测 设计分析 我们创建一个默认的Asp. ...
- 基于element ui的级联选择器组件实现的分类后台接口
今天在做资产管理系统的时候遇到一个分类的级联选择器,前端是用的element的组件,需要后台提供接口支持. 这个组件需要传入的数据结构大概是这样的,详细的可参考官方案例: [{ value: ...
- Vue + Element UI 实现权限管理系统 (功能组件封装)
组件封装 为了避免组件代码的臃肿,这里对主要的功能部件进行封装,保证代码的模块化和简洁度. 组件结构 组件封装重构后,试图组件结构如下图所示 代码一览 Home组件被简化,包含导航.头部和主内容三个组 ...
- Vue+element UI实现表格数据导出Excel组件
介绍 这是一个可以将页面中的表格数据导出为Excel文件的功能组件,该组件一般与表格一起使用,将表格数据传给组件,然后通过点击组件按钮可将表格中的数据导出成Excel文件. 使用方法 由于封装该组件内 ...
- krry-transfer ⏤ 基于 element 的升级版穿梭框组件发布到 npm 啦
博客地址:https://ainyi.com/81 基于 element ui 的==升级版穿梭框组件==发布到 npm 啦 看过我之前博客的同学或许知道我之前写过关于 element 穿梭框组件重构 ...
- 基于VueJS的render渲染函数结合自定义组件打造一款非常强大的IView 的Table
基于VueJS的render渲染函数结合自定义组件打造一款非常强大的IView 的Table https://segmentfault.com/a/1190000015970367
- 【原创】强撸基于 .NET 的 Redis Cluster 集群访问组件
Hello 大家好,我是TANZAME,我们又见面了.今天我们来聊聊怎么手撸一个 Redis Cluster 集群客户端,纯手工有干货,您细品. 随着业务增长,线上环境的QPS暴增,自然而然将当前的单 ...
- 循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码
VUE+Element 前端应用,比较不错的一点就是界面组件化,我们可以根据重用的指导方针,把界面内容拆分为各个不同的组合,每一个模块可以是一个组件,也可以是多个组件的综合体,而且这一个过程非常方便. ...
- vue 快速入门 系列 —— vue 的基础应用(下)
其他章节请看: vue 快速入门 系列 vue 的基础应用(下) 上篇聚焦于基础知识的介绍:本篇聚焦于基础知识的应用. 递归组件 组件是可以在它们自己的模板中调用自身的.不过它们只能通过 name 选 ...
随机推荐
- linux修改默认的SSH远程端口22
1.编辑sshd_config文件 [root@localhost ~]# vim /etc/ssh/sshd_config 搜索 #Port 22行,删除开头的 # 字符,然后将其替换为要使用的端 ...
- shell中的2>/dev/null
1.文件描述符Linux系统预留可三个文件描述符:0.1和2,他们的意义如下所示:0--标准输入(stdin)1--标准输出(stdout)2--标准错误(stderr) 标准输出--stdout假设 ...
- [ css ] 实现漂亮的输入框动画(借鉴自panjiachen的后台管理项目)
效果预览 HTML <div class="l-custom-input"> <input size="large" id="l-i ...
- Java 总结 数据底层原理 【包括 ArrayList、LinkedList、hash table、HashMap、Hashtable、ConcurrentHashMap、hash code、HashSet、LinkedHashMap、LinkedHashSet】
1.ArrayList (1)底层是由动态数组实现的[使用了List接口]. (2)动态数组是长度不固定,随着数据的增多而变长. (3)如果不指定,默认长度为10,当添加的元素超过当前数组的长度时,会 ...
- SpringBoot学习笔记三之表述层
注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6803355920697917965/ 首先配置learn-admin-webui中的web.xml文件 配置Con ...
- 关于Java注释报错的一个问题
做作业时发现一个问题,注释会报错 主要代码如下: String str = "C:\\users\\Default.migrated"; str += "\\HelloW ...
- FIS本地发布-其他同事通过IP访问
方法很简单,只需在fis的配置文件那里进行修改即可. 文件路径在 C:\Users\Su\AppData\Roaming\npm\node_modules\fis\node_modules\fis-c ...
- 灵雀云Kube-OVN进入CNCF沙箱,成为CNCF首个容器网络项目
昨日,云原生计算基金会 (CNCF) 宣布由灵雀云开源的容器网络项目Kube-OVN 正式进入 CNCF 沙箱(Sandbox)托管.这是全球范围内首个被CNCF纳入托管的开源CNI网络项目,也是国内 ...
- springBoot--原理分析
起步依赖分析 分析spring-boot-starter-parent 按住Ctrl点击pom.xml中的spring-boot-starter-parent,跳转到了spring-boot-star ...
- [CISCN2019 华东南赛区]Web11
[CISCN2019 华东南赛区]Web11 写在前面 参考文章:Smarty SSTI 1.{php}{/php} Smarty已经废弃{php}标签,强烈建议不要使用.在Smarty 3.1,{p ...