从设计到产品
从设计到产品
最近上的一些课的笔记,从 0 开始设计项目的角度去看产品。
设计系统
设计系统(design system) 不是 系统设计(system design),前者更偏向于 UI/UX 设计部分,后者更偏向于实现部分。
个人觉得,前端开发与 UI/UX 设计之间的差别很大程度上取决于公司的规模,不过不管怎么说,在团队中没有 UI/UX 设计师的情况下,前端开发就不得不硬着头皮上了……如果本身就具有相关经验还好,如果没有的话,可以参考一下比较主流的系统设计进行实现:
-
IBM Carbon Design System
-
Material Design
-
Apple Design
-
Fluent Design System
-
Atlassian Design System
-
Uber Design System
-
Shopify Design System
其实从一些文档上大概滚过一遍后就会发现,设计系统是一个很复杂的东西,最简单的包含了颜色定义(color theme & contrast)、间距(padding & margin),图标(icon)、字体(Typography)和可访问性(accessibility)。除此之外更加复杂的自然还有组件化、动画等设计。
有一个完善的设计系统体系,并拿出对应的设计(Figma,PSD,Xd 等),是产品落地的第一步。最完善的情况是有独立的 Ui 和 UX 队伍,那么作为前端开发我们只需要拿到完整且完善的设计图,设计并提取共用的 CSS(包括 spacing、color 甚至是 animation 等),实现页面需求。
在没有 UX 的情况下,我们需要与 stakeholders 和 UI 队伍进行讨论,尤其是一些 UI 上看起来很酷炫,实现上非常有难度的功能。
在没有 UI/UX 的情况下,那么作为前端工程师的我们可能只能硬着头皮上了……
下面的项目以该 figma 文件为基准:https://www.figma.com/file/EX8VxcTtAatzI2PBLb361g/designsystems.engineering?node-id=99-0
系统化 CSS
注释的工具为:VS Code CSS Comments
在有了完善的设计系统的情况下,可以考虑将 CSS 提取出去做成一个项目,然后让 View layer 去导入即可。
这里的结构参考了一本书:Atomic Design,GitHub 上可以免费阅读。
因为这是一个独立的 CSS 项目,为了方便管理变量会使用 CSS 预处理,SCSS,其项目结构如下:
SCSS 的实现应当根据设计系统进行,以 color.scss
为例:
/*=============================================
= Foundation - colors =
=============================================*//*** This file defines the actual colors that will be used for styling. They will default to the palette* we defined in the _variable.scss file. This is our default palette, and devs can override this* with their own variables.*//*=============================================
= Global text colors =
=============================================*/
$body-text-color: var(--dse-body-text-color, $dark) !default;
$body-bg-color: var(--dse-body-bg-color, $white) !default;/*=============================================
= Buttons =
=============================================*/
$btn-primary-color: var(--dse-btn-primary-color, $white) !default;
$btn-primary-bg: var(--dse-btn-primary-bg, $green) !default;
$btn-primary-bg-hover: var(--dse-btn-primary-bg-hover, $green-light) !default;/*=============================================
= Forms =
=============================================*/
$form-border-color: var(--dse-form-border-color, $white-dark) !default;
$form-bg-color: var(--dse-form-bg-color, $white) !default;
$form-bg-option-selected: var(--dse-form-bg-option-selected, $green) !default;
$form-color-option-selected: var(--dse-form-color-option-selected,$white
) !default;
$form-bg-color-hover: var(--dse-form-bg-color-hover, $white-dark) !default;
$form-color: var(--dse-form-color, $dark) !default;
$form-bg: var(--dse-form-bg, $white) !default;
$form-error-color: var(--dse-form-error-color, $red) !default;
$form-error-border: var(--dse-form-error-border, $red) !default;
$form-border-focus-color: var(--dse-form-border-focus-color, $green) !default;/*=============================================
= App Bar =
=============================================*//*===== End of App Bar ======*/
package.json 中的内容如下:
{"name": "@proj/scss","version": "1.0.0","main": "index.js","license": "MIT","dependencies": {"normalize-scss": "^7.0.1"},"devDependencies": {"husky": "^8.0.0","node-sass": "^8.0.0","prettier": "^2.8.7","stylelint": "^15.5.0","stylelint-config-prettier": "^9.0.5","stylelint-config-sass-guidelines": "^10.0.0","stylelint-prettier": "^3.0.0"},"scripts": {"lint": "stylelint './**/*.scss'","lint:fix": "yarn lint --fix","prepare": "husky install","build": "node src/scripts/build.js"}
}
运行 yarn build
会将所有的 SCSS 打包为 CSS 进行导出,脚本 build.js 的内容如下:
const fs = require('fs');
const path = require('path');
const sass = require('node-sass');const compile = (input, output) => {const res = sass.renderSync({data: fs.readFileSync(path.resolve(input)).toString(),outputStyle: 'expanded',outFile: 'global.css',includePaths: [path.resolve('src')],});fs.writeFileSync(path.resolve(output), res.css.toString());
};const getComponents = () => {let allComponents = [];const types = ['atoms', 'molecules', 'organisms'];types.forEach((type) => {const allFiles = fs.readdirSync(`src/${type}`).map((file) => ({input: `src/${type}/${file}`,output: `lib/${file.slice(0, -4) + 'css'}`,}));allComponents = [...allComponents, ...allFiles];});return allComponents;
};try {fs.mkdirSync(path.resolve('lib'));
} catch (e) {}compile('src/global.scss', 'src/lib/global.css');getComponents().forEach((component) => {compile(component.input, component.output);
});
这部分主要是将 SCSS 打包成全局使用的 global.css
,以及对应模块的 css。
配置 monorepo
既然已经将 CSS 打包出去了,那么就需要在另外一个项目中引用。
使用原生 node 管理器,如 npm,yarn,进行 monorepo 的管理笔记在这:使用 node 管理器管理 monorepo,这里为了方便会尝试使用 Lerna。
下面的命令分别会下载 lerna,初始化 lerna,以及删除所有的 node_modules,随后重新下载 dependencies,这部分的 hoist 在之前的笔记也有。
➜ senior git:(main) ✗ yarn add -D lerna
➜ senior git:(main) ✗ yarn lerna init
➜ senior git:(main) ✗ rm -rf ./**/node_modules
➜ senior git:(main) ✗ yarn
修改配置文件:
-
package.json
workspace 的配置在之前的笔记中讲过
{"name": "senior","devDependencies": {"lerna": "^6.6.1"},"private": "true","workspaces": {"packages": ["packages/*"],"nohoist": ["**/normalize-scss"]},"scripts": {"build": "yarn lerna run build"} }
没有 hoist normalize-scss 的原因跟使用相关,官方文档建议说使用
@import "[path to]/normalize-scss/sass/normalize";
的语法,所以我这里使用的路径是:node_modules/normalize-scss/sass/normalize/import-now
,如果 hoist 的话无法直接找到 node-sass。如果之后的项目可能会使用 node-sass,那么可以修改一下相对路径,并且去除nohoist
的选项。"yarn lerna run build"
是最近文档上的运行方式,如果是旧版应该使用的是"yarn lerna run-build"
,当然具体还是要查看文档实现。 -
lerna.json
这里就加了
npmClient
和stream
,其他均为默认配置{"$schema": "node_modules/lerna/schemas/lerna-schema.json","useWorkspaces": true,"version": "0.0.0","npmClient": "yarn","stream": true }
添加 React 组件
这个 React 相当于对应一个 UI 库,实现的部分基本等同于 SCSS 中所实现的组件,供实际实现 business logic 的 React 去使用,大概构造如下:
这里的样式全部都在 SCSS 中实现,react 中只负责定义组件的展现,如:
import React, { FC } from 'react';interface ColorProps {hexCode: string;width: string;height: string;
}const Color: FC<ColorProps> = ({ hexCode, width, height }) => {return <div style={{ backgroundColor: hexCode, width, height }}></div>;
};export default Color;
这种实现相对适合 UI 逻辑较为复杂一些的页面,比如说需要基于 react-table 之上实现一个 wrapper,然后这个封装的组件可能被多于一个项目使用。playgrounds 中是使用封装好组件的 business logic 所在的地方。
补充一下 rollup 的配置,这个配置还是有些问题的,不过要使用 rollup 的时候再研究吧:
import TS from 'rollup-plugin-typescript2';
import path from 'path';export default {input: ['src/index.ts', 'src/atoms/Color/index.ts'],output: {dir: 'lib',format: 'esm',sourcemap: true,},plugins: [TS()],external: ['react'],
};
这里是简单的引入了 Button 的组件
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Color } from '@proj/react/lib';
import '@proj/scss/lib/Button.css';const container = document.getElementById('root') as HTMLElement;
const root = createRoot(container);root.render(<Color hexCode="#000" width="1rem" height="1rem"></Color>);
效果如下:
这样的话重写样式其实也会方便很多。
设置开发环境
目前是所有的 build 脚本全都写好了,但是开发环境没写——react 部分使用 parcel 和 rollup 偷懒了,所以这里就通过 lerna 去把开发环境补全。
这里偷个懒,scss 的项目因为是使用自己的脚本 build 的,很难使用现有封装好的工具去监测文件的修改,所以使用 nodemon 去实现 --watch
功能。
-
scss
"scripts": {"dev": "nodemon --watch src --exec yarn build -e scss" }
-
react provider
"dev": "yarn build --watch"
-
react consumer
"dev": "parcel src/index.html -p 3000"
随后可以在有 lerna.json
的根目录下运行 yarn dev
,运行结果大致如下:
yarn run v1.22.19
$ yarn lerna run dev
$ /__________/node_modules/.bin/lerna run dev
lerna notice cli v6.6.1> Lerna (powered by Nx) Running target dev for 2 projects:- @proj/react- @proj/scss——————————————————————————————————————————————————————————————————————————————> @proj/react:dev> @proj/scss:dev@proj/react: $ yarn build --watch
@proj/scss: $ nodemon --watch src --exec yarn build -e scss
@proj/react: $ rollup -c --watch
@proj/scss: [nodemon] 2.0.22
@proj/scss: [nodemon] to restart at any time, enter `rs`
@proj/scss: [nodemon] watching path(s): src/**/*
@proj/scss: [nodemon] watching extensions: scss
@proj/scss: [nodemon] starting `yarn build`
@proj/react: rollup v3.21.1
@proj/react: bundles src/index.ts, src/atoms/Button/index.ts → lib...
@proj/scss: $ node src/scripts/build.js
@proj/react: created lib in 1s
@proj/scss: [nodemon] clean exit - waiting for changes before restart
@proj/scss: [nodemon] restarting due to changes...
@proj/scss: [nodemon] starting `yarn build`
@proj/scss: $ node src/scripts/build.js
@proj/scss: [nodemon] clean exit - waiting for changes before restart
@proj/scss: [nodemon] restarting due to changes...
@proj/scss: [nodemon] starting `yarn build`
@proj/scss: $ node src/scripts/build.js
@proj/scss: [nodemon] clean exit - waiting for changes before restart
从我个人来说这是一个比较方便的实现,如果想要更完整和统一的配置,也可以 webpack5 的 module federation。
限定 CSS
现在又有一个问题了,那么就是 scss 中已经限定了样式的格式:
$spacing: (none: 0,xxxs: 0.25rem,// 4pxxxs: 0.5rem,// 8pxxs: 0.75rem,// 12pxsm: 1rem,// 16pxmd: 1.5rem,// 24pxlg: 2rem,// 32pxxl: 3rem,// 48pxxxl: 4.5rem,// 72pxxxxl: 6rem,// 96px
) !default;@each $size, $value in $spacing {.dse-width-#{$size} {width: $value;}.dse-height-#{$size} {height: $value;}
}
这一点也可以通过 TypeScript 实现,首先定义有效的距离变凉,这块依旧在 react provider 中实现:
-
spaces.ts
定义所有的尺寸
const spaces = {xxxs: 'xxxs',xxs: 'xxs',xs: 'xs',sm: 'sm',md: 'md',lg: 'lg',xl: 'xl',xxl: 'xxl',xxxl: 'xxxl', };export default Object.freeze(spaces);
-
index.ts
负责所有 export 的地方
import Color from './atoms/Color'; import Spacing from './foundation/spacing';export { Color, Spacing };
-
color.tsx
设定允许接受值的范围:
import React, { FC } from 'react'; import Spacing from '../../foundation/spacing';interface ColorProps {hexCode: string;width?: keyof typeof Spacing;height?: keyof typeof Spacing; }const Color: FC<ColorProps> = ({hexCode,width = Spacing.md,height = Spacing.md, }) => {const className = `dse-width-${width} dse-height-${height}`;return (<divclassName={className}style={{ backgroundColor: hexCode, width, height }}></div>); };export default Color;
通过 TS 的类型检查可以获取这里限定的值:
如果在 Consumer 这里乱使用值的话,TS 就会开启静态检查,从而提醒报错:
Consumer 部分代码:
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Color } from '@proj/react/lib';import '@proj/scss/lib/Utilities.css';const container = document.getElementById('root') as HTMLElement;
const root = createRoot(container);root.render(<Color hexCode="#000" width={'lg'} height={'sm'}></Color>);
⚠️:可以看到上面对尺寸的定义都是纯 TS,并不涉及到任何 react 的部分,因此可以单独提取出来做成 interface/definition,这样的话如果项目中使用 Vue、Angular 的话,也可以使用这些规范。
同样,如果有 UI 组件可能要同时兼容多个框架的需求,最好也将 scss 和 UI 实现分离(比如 react 和 react native,这两个 css 的实现就不太一样,很可能产生无法兼容的情况)。
单元测试
之前在笔记当中也有提过测试的部分,整合一下的话是两种:
- UI 测试主要可以用 react-testing-library
- 功能测试(mock)可以用 jest
storybook
这个也是 UI 库中比较重要的一个组成部分,之前也有在 React + TS + TDD 扫雷游戏学习心得 中试过水,这里就不多赘述了。
CI/CD
这个主要是 github actions 的东西……目前没怎么用过,打算之后找点资料看看。
reference
-
sh: husky: command not found
解决方案:
如果使用
nvm
,在根目录下新建一个.huskyrc
,放入以下内容:export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
-
lerna Building All Projects
-
Atomic Design
相关文章:

从设计到产品
从设计到产品 最近上的一些课的笔记,从 0 开始设计项目的角度去看产品。 设计系统 设计系统(design system) 不是 系统设计(system design),前者更偏向于 UI/UX 设计部分,后者更偏向于实现部分。 个人觉得,前端开发与 UI/UX 设…...
《疯狂Python讲义》值传递的细节
函数的参数包含着整个程序的规范性,之前还是没有那么去注意重要的细节,读完书中函数值传递篇章,还是有所收获的。 参数有两种形式,一种是形参一种是实参,形参可以理解为实参的载体,函数当中的关键词也是描…...

【7. ROS 中的 IMU 惯性测量单元消息包】
欢迎大家阅读2345VOR的博客【6. 激光雷达接入ROS】🥳🥳🥳 2345VOR鹏鹏主页: 已获得CSDN《嵌入式领域优质创作者》称号👻👻👻,座右铭:脚踏实地,仰望星空&#…...
pcie m.2固态硬盘装机后无法识别到启动盘
1、第一种情况《系统版本过低》 原因: 使用m.2固态硬盘的电脑,最好安装iwn8.1以上的系统,因为win7系统及其win xp系统 没有自带NVME驱动。 搞定办法: 比较简单的方式就是直接开运行快启动u盘启动盘制作工具将系统升级到win10系…...
Java Web应用开发 ——第四章:JavaBean技术测验
一.单项选择题(共13题,55.9分) 1 在 JSP 中调用 JavaBean 时不会用到的标记是:( ) A、 < jsp:javabean> B、 < jsp:useBean> C、 < jsp:setProperty> D、 < jsp:getProperty> 正确答案&a…...

CTF权威指南 笔记 -第二章二进制文件- 2.4 -动态链接
目录 静态文件的缺点 动态链接 位置无关代码 延迟绑定 _dl_runtime_reslove 函数定义 深入审视 静态文件的缺点 随着可执行文件的增加 静态链接带来的浪费空间问题就会愈发严重 如果大部分可执行文件都需要glibc 那么在链接的时候就需要把 libc.a链接进去 如果一个libc…...

C++:计算机操作系统:多线程:高并发中的线程
高并发中的线程 一切要从CPU说起PC 程序计数器从CPU到操作系统从进程到线程 从这篇开始,我将会开启高性能,高并发系列,本篇是给系列的开篇,主要关注 多线程以及线程池。 一切要从CPU说起 你可能会有疑问,讲多线程为何…...

大数据Doris(十一):Aggregate 数据模型
文章目录 Aggregate 数据模型 一、导入数据聚合 二、保留明细数据...

osg::Drawable类通过setDrawCallback函数设置回调函数的说明
osg::Drawable类可以通过该类的setDrawCallback函数设置回调函数类对象。被设置的回调类对象必须从osg::Drawable::DrawCallback类派生,并重写drawImplementation函数,以实现自己特定的需求。这个回调函数在每次帧事件中都会被调用(如:在帧的…...

Python基础合集 练习17(类与对象)
class Dog: pass papiDog() print(papi) print(type(papi)) 构建方法 创建类过后可以定义一个特殊的方法。在python中构建方法是__init__(),init()必须包含一个self参数 class pig(): #def__init__(self) -> None: print(‘你好’) pipgpig() 属性和方法 cl…...

再多猜一次就爆炸(小黑子误入)
目录 猜数字游戏 游戏设计思路 1.电脑随机生成一个数 2.猜数字 3.输入我是ikun,泰裤辣! 否则电脑将在一分钟后关机 游戏运行效果 源码 代码分析 代码实现关键语句 strcmp() rand()与srand() 时间戳time() 寄语 猜数字游戏 游戏设计思路 1.电脑随机生…...
图像超分辨率简单介绍
文章目录 图像超分辨率简单介绍什么是图像超分辨率?常见的图像超分辨率算法插值算法基于边缘的图像重建算法局部线性嵌入(LLE)拉普拉斯正则化 基于深度学习的超分辨率算法超分辨率CNN超分辨率GAN 步骤1. 收集数据2. 选择算法3. 训练模型4. 测…...

【Liunx】进程的程序替换——自定义编写极简版shell
目录 进程程序替换[1~5]1.程序替换的接口(加载器)2.什么是程序替换?3.进程替换的原理4.引入多进程5.系列程序替换接口的详细解析(重点!) 自定义编写一个极简版shell[6~8]6.完成命令行提示符7.获取输入的命令…...

c++标准模板(STL)(std::array)(三)
定义于头文件 <array> template< class T, std::size_t N > struct array;(C11 起 std::array 是封装固定大小数组的容器。 此容器是一个聚合类型,其语义等同于保有一个 C 风格数组 T[N] 作为其唯一非静态数据成员的结构体。不同于 C 风格数组…...

c#笔记-创建一个项目
创建一个项目 创建控制台程序 在你安装完成Visual Studio后打开它,你会的到一个启动窗口 点击创建新项目,选择右上角c#的没有Framework的控制台应用。 项目名称,位置自己随意。 目标框架选择NET7.0。 项目创建完成后应该你的界面应该类似…...

Photoshop如何使用图像调色之实例演示?
文章目录 0.引言1.将一张偏冷调的图像调整成暖调2.将图像调整成不同季节色彩倾向3.变换花朵的颜色4.创建人像轮廓风景5.修饰蓝天白云6.调换花草颜色 0.引言 因科研等多场景需要进行绘图处理,笔者对PS进行了学习,本文通过《Photoshop2021入门教程》及其配…...

IDEA中使用Git提交代码提示:您即将把CRLF行分隔符提交到Gt仓库。 建议将core.autocrlf Git特性设置为trUe,以免发生行分隔符问题。
IDEA中使用Git提交代码提示:您即将把CRLF行分隔符提交到Gt仓库。 建议将core.autocrlf Git特性设置为trUe,以免发生行分隔符问题。 问题背景: 在IDEA中,使用Git提交代码到远程仓库时,结果弹出一个警告窗口 问题原因: …...
ArduPilot之开源代码LibrarySketches设计
ArduPilot之开源代码Library&Sketches设计 1. 简介1.1 Core libraries1.2 Sensor libraries1.3 Other libraries 2. 源由3. Library Sketches设计3.1 设计框架3.2 Example Sketches3.3 AP_Common Sketches3.3.1 配置sitl环境3.3.2 编译AP_Common3.3.3 运行AP_Common3.3.4 代…...

第一章:概述
1,因特网概述 1.网络、互联网和英特网 网络(Network)由若干结点(Node)和连接这些结点的链路(Link)组成。 多个网络还可以通过路由器互连起来,这样就构成了一个覆盖范围更大的网络,即互联网(或互连网)。因此,互联网是“网络的网络…...

MySQL --- DDL图形化工具表结构操作
一. 图形化工具 1. 介绍 前面我们讲解了DDL中关于数据库操作的SQL语句,在我们编写这些SQL时,都是在命令行当中完成的。大家在练习的时候应该也感受到了,在命令行当中来敲这些SQL语句很不方便,主要的原因有以下 3 点:…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...