提升组件库通用能力 - NutUI 在线主题定制功能探索
开发背景
NutUI 作为京东风格的组件库,已具备 H5 和多端小程序开发能力。随着业务的不断发展,组件库的应用场景越来越广。在公司内外面临诸如科技、金融、物流等各多个大型团队使用时,单一的京东 APP 视觉虽可以一键进行换肤操作,但是对于更个性化的定制需求(组件级样式、规范、尺寸等)近千行的主题样式变量对开发者来说工作量是非常大的。为提升开发体验,提高开发者效率,加强换肤功能以及实现「组件级式定制」功能迫在眉睫。
设计目标
允许用户在开发阶段切换不同主题风格的皮肤,也允许开发者对指定的组件直接进行样式修改,以满足不同设计风格的移动端业务场景。
效率提升
官网会提供多套主题供开发者选择,同时开发者也可以在多套主题基础上进行实时编辑修改,完成后下载配置变量,应用在项目中即可,非常易上手。完成一个全局样式配置仅需1分钟。
相对这种场景下的需求开发是比较快的,能够降低开发成本。
组件粒度
主题定制配置层分为全局基本变量、组件基本变量,开发者可以修改全局,比如组件库的全局主题颜色,字体等样式。组件层的配置可以更细致,比如 Button 按钮成功类型的圆角边框尺寸
通用扩展能力
现阶段官方会提供一些优质主题集成到官网的,对于社区开发者、开发团队、如果您的团队定制的样式主题文件受众非常之广,可以联系我们,将您的主题内置到官方 npm 包中,造福更多的开发者
开发者如何使用
视频教程
NutUI 一分钟快速在线主题定制 https://www.bilibili.com/video/BV1fi4y1D7qb
1、打开在线配置网站,按照下方图片进行修改预览下载
2、本地项目配置
修改本地项目 webpack 或者 vite 的配置文件将下载后的 custom_theme.sass
文件,集成到项目中比如assets/styles/custom_theme.sass
- vite 构建工具使用示例 vite.config
// https://vitejs.dev/config/
export default defineConfig({
//...
css: {
preprocessorOptions: {
scss: {
// 默认京东 APP 10.0主题 > @import "@nutui/nutui/dist/styles/variables.scss";
// 京东科技主题 > @import "@nutui/nutui/dist/styles/variables-jdt.scss";
additionalData: `@import "./assets/styles/custom_theme.scss";@import "@nutui/nutui/dist/styles/variables.scss";`
}
}
}
})
- webpack 构建工具使用示例
{
test: /\.(sa|sc)ss$/,
use: [
{
loader: 'sass-loader',
options: {
// 默认京东 APP 10.0主题 > @import "@nutui/nutui/dist/styles/variables.scss";
// 京东科技主题 > @import "@nutui/nutui/dist/styles/variables-jdt.scss";
data: `@import "./assets/styles/custom_theme.scss";@import "@nutui/nutui/dist/styles/variables.scss";`,
}
}
]
}
- taro 小程序使用示例
修改 config/index.js
文件中配置 scss
文件全局覆盖如:
const path = require('path');
const config = {
deviceRatio: {
640: 2.34 / 2,
750: 1,
828: 1.81 / 2,
375: 2 / 1
},
sass: {
resource: [
path.resolve(__dirname, '..', 'src/assets/styles/custom_theme.scss')
],
// 默认京东 APP 10.0主题 > @import "@nutui/nutui-taro/dist/styles/variables.scss";
// 京东科技主题 > @import "@nutui/nutui-taro/dist/styles/variables-jdt.scss";
data: `@import "@nutui/nutui-taro/dist/styles/variables.scss";`
},
// ...
实现原理解析
整个组件库主题定制模块,实现可以分为两个方向,一个是内部的组件库设计(供开发者使用配置每个样式变量),另一个是在线配置官网(供开发者便捷的修改),接下来依次按照设计图来阐述。
组件库内部设计
首先源码内部style
文件夹下,分别存在variables.scss
、variables-jdt.scss
多个文件对应的不同的官方主题,每个主题的全局的variables.scss
文件,内部其实按标准的规则存放存放通用样式变量和每个组件的样式变量,像下面一样
// --------base begin-------
// 主色调
$primary-color: #fa2c19 !default;
$primary-color-end: #fa6419 !default;
// 辅助色
$help-color: #f5f5f5 !default;
// 标题常规文字
$title-color: #1a1a1a !default;
// 副标题
$title-color2: #666666 !default;
// 次内容
$text-color: #808080 !default;
//...
// Font
$font-size-0: 10px !default;
$font-size-1: 12px !default;
$font-size-2: 14px !default;
$font-size-3: 16px !default;
$font-size-4: 18px !default;
$font-weight-bold: 400 !default;
$font-size-small: $font-size-1 !default;
$font-size-base: $font-size-2 !default;
$font-size-large: $font-size-3 !default;
$line-height-base: 1.5 !default;
// --------base end-------
// button
$button-border-radius: 25px !default;
$button-border-width: 1px !default;
$button-default-bg-color: $white !default;
$button-default-border-color: rgba(204, 204, 204, 1) !default;
$button-default-color: rgba(102, 102, 102, 1) !default;
//...
// icon
// ...
这里啰嗦一句,可以看到每一行后面都有一个 !default
,这个是必不可少的,如果不加,开发者本地项目是无法覆盖这个变量的
https://www.sass.hk/docs/#t6-9 Tips: 可以在变量的结尾添加 !default 给一个未通过 !default 声明赋值的变量赋值,此时,如果变量已经被赋值,不会再被重新赋值,但是如果变量还没有被赋值,则会被赋予新的值。
对于每一个组件的内部,例如button/index.scss
下是这样引用height: $button-default-height;
.nut-button {
position: relative;
display: inline-block;
flex-shrink: 0;
height: $button-default-height;
// ...
}
其实最终组件库构建成 npm 包时,将主题的全局的variables.scss
等主题文件暴露给开发者,然后开发者根据需求替换其中的样式变量,至此组件库内部实现主题定制就实现了
可视化配置官网
源码抢先看:https://github.com/jdf2e/nutui/tree/theme/src/sites/doc/components/ThemeSetting
整体实现流程如下,接下来依次阐述
- 将
variables.scss
源文件,通过组件配置数据 + 正则匹配拆分,得到这样的数据结构
// 主色调
$primary-color: #fa2c19 !default;
$primary-color-end: #fa6419 !default;
//...
// button
$button-border-radius: 25px !default;
$button-border-width: 1px !default;
//...
// icon
// ...
[
{name: 'Base', lowerCaseName: 'base', key: '$primary-color', rawValue: '#fa2c19', computedRawValue: ''}
{name: 'Base', lowerCaseName: 'base', key: '$primary-color-end', rawValue: '#fa6419', computedRawValue: ''}
// ...
{name: 'Button', lowerCaseName: 'button', key: '$button-border-width', rawValue: '1px', computedRawValue: ''}
{name: 'Button', lowerCaseName: 'button', key: '$button-border-radius', rawValue: '25px', computedRawValue: ''}
//{name: 'components1', lowerCaseName: 'components1', key: '$components1-border-radius', rawValue: 'xx', computedRawValue: ''}
//...
]
const findStyle = (componentName: string) => {
// https://raw.githubusercontent.com/jdf2e/nutui/next/src/packages/styles/variables.scss
// var pattern = /\$button.*;/g;
var p = new RegExp(`\\$${componentName}.*;`, 'g');
let parray: any[] = varcss.match(p) || [];
// 需要包含换行
let commponetns = parray.map((item) => {
let cArray = item.split(':');
let name = cArray[0],
value: string = cArray[1].replace(' !default;', '').trim();
return {
name: componentName,
key: name,
rawValue:value,
computedRawValue: ''
}
});
}
components.map(item=>{ findStyle(item.name) });
- 接下来根据组件不同展示该组件下所有变量,监听组件切换切换或者编辑,进行实时编译
const cssText = computed(() => {
const variablesText = store.variables.map(({ key, value }) => `${key}:${value}`).join(';');
cachedStyles = cachedStyles || extractStyle(store.rawStyles);
return `${variablesText};${cachedStyles}`;
});
const formItems = computed(() => {
const name = route.path.substring(1);
return store.variables.filter(({ lowerCaseName }) => lowerCaseName === name);
});
watch(
() => cssText.value,
(css) => {
clearTimeout(timer);
timer = setTimeout(() => {
const Sass = (window as any).Sass;
let beginTime = new Date().getTime();
console.log('sass编译开始', beginTime);
Sass &&
Sass.compile(css, async (res: Obj) => {
await awaitIframe();
const iframe = window.frames[0] as any;
if (res.text && iframe) {
console.log('sass编译成功', new Date().getTime() - beginTime);
if (!iframe.__styleEl) {
const style = iframe.document.createElement('style');
style.id = 'theme';
iframe.__styleEl = style;
}
iframe.__styleEl.innerHTML = res.text;
iframe.document.head.appendChild(iframe.__styleEl);
} else {
console.log('sass编译失败', new Date().getTime() - beginTime);
console.error(res);
}
if (res.status !== 0 && res.message) {
console.log(res.message);
}
});
}, 300);
},
{ immediate: true }
);
- 下载配置变量操作
由于变量文件近千行,以后可能还会更大,直接采用Blob
文件流进行生成下载。
downloadScssVariables() {
if (!store.variables.length) {
return;
}
let temp = '';
const variablesText = store.variables
.map(({ name, key, value }) => {
let comment = '';
if (temp !== name) {
temp = name;
comment = `\n// ${name}\n`;
}
return comment + `${key}: ${value};`;
})
.join('\n');
download(`// NutUI主题定制\n${variablesText}`, 'custom_theme.scss');
}
function download(content: string, filename: string) {
const eleLink = document.createElement('a');
eleLink.download = filename;
eleLink.style.display = 'none';
const blob = new Blob([content]);
eleLink.href = URL.createObjectURL(blob);
document.body.appendChild(eleLink);
eleLink.click();
document.body.removeChild(eleLink);
}
总结
文章详细介绍了 NutUI 的「主题定制」和「组件级样式定制」功能实现机制。「主题定制」能实现简单的颜色切换,「组件级样式定制」功能更强大,通过将组件的样式变量暴露出来开发者几乎可以任意修改自己想要的设计风格(组件尺寸、字体、边距)。通过强大的主题定制可以让组件库的使用不局限于原设计者的设计范畴,可灵活扩展组件,让组件库的应用范围更广,能满足更广泛的业务场景。
期待您的使用与反馈 ️~
提升组件库通用能力 - NutUI 在线主题定制功能探索的更多相关文章
- 微信小程序一键生成源码 在线制作定制功能强大的微信小程序
微信小程序发展到现在,短短的一年不到的时间(很快就要迎来微信小程序周年庆),在快迎来周年庆之际,百牛信息技术bainiu.ltd特记录一下这个发展的历程,用于将来见证小程序发展的辉煌时刻,我们还能知道 ...
- 小技巧!CSS 提取图片主题色功能探索
本文将介绍一种利用 CSS 获取图片主题色的小技巧.一起看看~ 背景 起因是微信技术群里有个同学发问,有什么方法能够获取图片的主色呢?有一张图片,获取他的主色调: 利用获取到的这个颜色值,来实现类似这 ...
- beeshell —— 开源的 React Native 组件库
介绍 beeshell 是一个 React Native 应用的基础组件库,基于 0.53.3 版本,提供一整套开箱即用的高质量组件,包含 JavaScript(以下简称 JS)组件和复合组件(包含 ...
- 滴滴开源 Vue 组件库— cube-ui
cube-ui 是滴滴去年底开源的一款基于 Vue.js 2.0 的移动端组件库,主要核心目标是做到体验极致.灵活性强.易扩展以及提供良好的周边生态-后编译. 自 17 年 11 月开源至今已有 5 ...
- HslCommunication组件库使用说明 (转载)
一个由个人开发的组件库,携带了一些众多的功能,包含了数据网络通信,文件上传下载,日志组件,PLC访问类,还有一些其他的基础类库. nuget地址:https://www.nuget.org/packa ...
- HslCommunication组件库使用说明
一个由个人开发的组件库,携带了一些众多的功能,包含了数据网络通信,文件上传下载,日志组件,PLC访问类,还有一些其他的基础类库. nuget地址:https://www.nuget.org/packa ...
- 如何快速构建React组件库
前言 俗话说:"麻雀虽小,五脏俱全",搭建一个组件库,知之非难,行之不易,涉及到的技术方方面面,犹如海面风平浪静,实则暗礁险滩,处处惊险- 目前团队内已经有较为成熟的 Vue 技术 ...
- 为公司架构一套高质量的 Vue UI 组件库
有没有曾遇过,产品要我们实现一个功能,但是 iview 或者 elementui 不支持,我们然后义正言辞的说,不好意思,组件库不支持,没法做到. 有没有曾和设计师争论得面红耳赤,其实也是因为组件库暂 ...
- wussUI v1.0.0小程序UI组件库 第一期开发已完成
经过了两个月不到的开发时间,我们phonycode团队顺利的发布了小程序的UI组件库 wuss-ui 的第一个版本.目前大体预览如下 介绍 wussUI 现在有大概27个组件左右, 目前基础组件都有了 ...
随机推荐
- C# 不区分大小写替换文本
C# .NET类库自带的str.Replace() 方法替换文本不能区分大小写.我们可以自己编写一个扩展方法,支持文本忽略大小写替换.以下扩展方法实现了使用正则表达式忽略大小写替换文本. public ...
- C# 实例解释面向对象编程中的里氏替换原则
在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解.灵活和可维护.这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原 ...
- Numpy的各种下标操作
技术背景 本文所使用的Numpy版本为:Version: 1.20.3.基于Python和C++开发的Numpy一般被认为是Python中最好的Matlab替代品,其中最常见的就是各种Numpy矩阵类 ...
- 02.Oracle之安装与配置
1.Oracle简介 Oracle是世界上最早的商品化的关系型数据库管理系统,是数据库专业厂商ORACLE(中文名字叫甲骨文)公司开发的,也是当前应用最为广泛.功能最强大.具有面向对象特点.采用了客户 ...
- django模型01
1.开发流程 - 配置数据库 - 定义模型类 - 生成迁移文件 - 执行迁移生成数据表 - 使用模型类进行增删改查操作 2.ORM - **概述**:对象->关系->映射 - **任务** ...
- 创建sqlsession工具类
//1.sqlsession的获取: //类:GetSqlSession, 返回sqlsession对象,无参 public class GetSqlSession { public static S ...
- Kafka经典三大问:数据有序丢失重复
Kafka经典三大问:数据有序丢失重复 在kafka中有三个经典的问题: 如何保证数据有序性 如何解决数据丢失问题 如何处理数据重复消费 这些不光是面试常客,更是日常使用过程中会遇到的几个问题,下面分 ...
- 【C#DLR 动态编程】CallSite<T>
CallSite<T>译为"动态(调用)站点",它是DLR中的核心组件之一
- 给bootstrap-table填坑
由于设计变更,需要把数据由分页展示改为全部展示(才3500条数据),结果chrome浏览器页面卡顿,火狐浏览器直接卡死! console.time分析之后,竟然是bootstrap-table插件的坑 ...
- 哈工大 NLP 实验一 汉语分词系统
NLP实验代码可见github:NLP实验代码整理 本实验会查重,而且写起来难度比较大,建议早一些开始.实验报告要用顶会论文形式呈现,建议使用overleaf里的ACL论文latex模板比较方便一点.