从设计到产品

news2024/12/24 2:59:37

从设计到产品

最近上的一些课的笔记,从 0 开始设计项目的角度去看产品。

设计系统

设计系统(design system) 不是 系统设计(system design),前者更偏向于 UI/UX 设计部分,后者更偏向于实现部分。

个人觉得,前端开发与 UI/UX 设计之间的差别很大程度上取决于公司的规模,不过不管怎么说,在团队中没有 UI/UX 设计师的情况下,前端开发就不得不硬着头皮上了……如果本身就具有相关经验还好,如果没有的话,可以参考一下比较主流的系统设计进行实现:

  1. IBM Carbon Design System

    在这里插入图片描述

  2. Material Design

    在这里插入图片描述

  3. Apple Design

    在这里插入图片描述

  4. Fluent Design System

    在这里插入图片描述

  5. Atlassian Design System

    在这里插入图片描述

  6. Uber Design System

  7. 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

    这里就加了 npmClientstream,其他均为默认配置

    {
      "$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,
  // 4px
  xxs: 0.5rem,
  // 8px
  xs: 0.75rem,
  // 12px
  sm: 1rem,
  // 16px
  md: 1.5rem,
  // 24px
  lg: 2rem,
  // 32px
  xl: 3rem,
  // 48px
  xxl: 4.5rem,
  // 72px
  xxxl: 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 (
        <div
          className={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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/479605.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用 ESP32 设计智能手表 – 第 1 部分制作表盘

相关设计资料下载ESP32 智能手表带心率、指南针设计资料(包含Arduino源码+原理图+Gerber+3D文件).zip 人们可以使用智能手表轻松快速地访问消息、警报、健康信息和其他高级信息。虽然智能手表作为独立设备在形式上是革命性的,但当与人们携带的其他设备(例如智能手机或平板…

作为一个大学生你应该知道的事情

作为一个大学生你应该知道的事情 大学生毕业去向 今天&#xff0c;我们不写技术&#xff0c;来谈一谈大学生的毕业现状&#xff1a; 以下内容为本人的一些观点和看法&#xff0c;仅限于沟通交流。 大学生毕业去向 大学生的毕业去向大致可以分为&#xff1a;就业、自由职业、慢…

Python之常用设计模式

1、 设计模式 2、接口 interface.py #! /usr/bin/env python # -*- coding: utf-8 -*- # Date: 2018/12/1# class Payment: # def pay(self, money): # raise NotImplementedErrorfrom abc import ABCMeta, abstractmethod# 接口 # class Payment(metaclassABCMet…

低频量化之 可转债 配债数据及策略 - 全网独家

目录 历史文章可转债配债数据 待发转债&#xff08;进展统计&#xff09;待发转债&#xff08;行业统计&#xff09;待发转债&#xff08;5证监会通过&#xff0c;PE排序&#xff09;待发转债&#xff08;5证监会通过&#xff0c;安全垫排序&#xff09;待发转债&#xff08;5证…

JVM--一文精通

调整JVM堆内存 在确定JVM堆内存大小时&#xff0c;需要考虑以下因素&#xff1a; 应用程序的内存需求。操作系统和其他应用程序所需的内存。JVM的运行参数和GC算法。 根据通常的经验&#xff0c;可以将JVM最大堆内存设置为操作系统可用内存的约70%。也就是说&#xff0c;在1…

Kube-OVN组件

文章目录 介绍ovn架构kube-ovn架构kube-ovn数据流向软件版本及注意事项高可用部署安装kube-ovn运维查看Pod日志组件监控kubectl ko插件 网络相关 介绍 kube-ovn是基于ovn开发的。https://man7.org/linux/man-pages/man7/ovn-architecture.7.html ovn架构 组件说明&#xff1a…

PySide6/PyQT多线程之 信号与槽 / (Signal Slot)的高效利用

前言 PySide6/PyQT信号槽是一种事件处理方式&#xff0c;允许程序中的对象发送和接收信号。 在 PySide6/PyQT 精进的过程中&#xff0c;一定躲不开 信号和槽 这座大山&#xff0c;这是一个比较有意思的知识点&#xff1a; 初接触的看不懂&#xff0c;觉得复杂&#xff1b;看得…

【Linux】win10远程控制Linux服务器 - 内网穿透实现公网远程

Yan-英杰的主页 悟已往之不谏 知来者之可追 C程序员&#xff0c;2024届电子信息研究生 目录 前言 视频教程 1. ubuntu安装XRDP 2.局域网测试连接 3. Ubuntu安装cpolar内网穿透 4.cpolar公网地址测试访问 5.固定域名公网地址 [TOC] 转载自远程穿透文章&#xff1a;Wi…

Redis的两种持久化方案 RDB AOF

文章目录 1.RDB持久化1.1.执行时机1.2.RDB原理1.3.小结 2.AOF持久化2.1.AOF原理2.2.AOF配置2.3.AOF文件重写 3.RDB与AOF对比 Redis有两种持久化方案&#xff1a; RDB持久化AOF持久化 1.RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xff09…

【三十天精通Vue 3】 第二十三天 Vue 3的错误处理详解

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: 三十天精通 Vue 3 文章目录 引言一、Vue 3 错误处理概览1. 错误处理的重要性2. Vue 3 中的错误…

EMS快递批量分析物流信息状况

众所周知“邮政快递”是在快递行业里面也是算一家行业龙头&#xff0c;中国邮政特快专递/EMS&#xff0c;邮政平邮小包&#xff0c;邮政挂件等&#xff0c;都是属于中国邮政集团&#xff0c;邮政速度快&#xff0c;价格也是比较实惠一家&#xff0c;所以很多产家或电商 、供应商…

大数据-玩转数据-FLINK快速上手

一、环境准备 ⚫系统环境为 Windows 10。 ⚫需提前安装 Java 11。 ⚫集成开发环境&#xff08;IDE&#xff09;使用 IntelliJ IDEA&#xff0c;具体的安装流程参见 IntelliJ 官网。 ⚫安装 IntelliJ IDEA 之后&#xff0c;还需要安装一些插件——Maven 和 Git。Maven 用来管理…

Java实现宿舍管理系统的设计与实现【附源码】

本科生毕业论文&#xff08;设计&#xff09; 宿舍管理系统的设计与实现 目 录 摘 要 I Abstract II 1 引言 1 1.1 研究背景 1 1.2 国内外研究现状 1 1.3 研究目的与意义 1 2 开发工具和相关技术 2 2.1 Eclipse 2 2.2 MySQL 2 2.3 Bootstrap 3 2.4 Tomcat 3 3 系统分析 3 3.1…

Java基础学习(11)

Java基础学习 一、集合进阶1.1 数据结构&#xff08;树&#xff09;1.1.1 二叉查找树1.1.2 二叉树的遍历 1.2 平衡二叉树树的演变 1.3 数据结构 ( 平衡二叉树 )左旋1.4 数据结构 ( 平衡二叉树 )右旋数据结构 (平衡二叉树)需要转的四种情况 1.5 红黑树添加节点规则 二、Set系列集…

基础算法(五):DFS、BFS与剪枝

前言 前面的基础算法笔记已经断更好久了&#xff0c;因为荔枝觉得还是得先学一下基础的数据结构知识之后才能更好的入门算法。在这篇文章中荔枝主要记录DFS和BFS的相关基础知识、答题的模板以及自己的一些浅薄理解&#xff0c;同样的也会涉及到相关的剪枝操作。 一、搜索算法概…

java基础学习-7

文章目录 异常小总结编译时异常和运行时异常小总结 异常在代码中的两个作用异常的方式JVM虚拟机默认处理异常的方式自己处理&#xff08;捕获异常&#xff09;灵魂四问抛出异常异常中的常见方法小总结小练习 自定义异常 File----路径File的概述和构造方法小总结 File的成员方法…

线性表之顺序表(C语言实现)

前言 &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f; c语言初阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:讲解数据结构的入门知识,线性结构之顺序表. 金句分享:…

机器学习笔记 基于深度学习的边缘检测

一、Holistically-Nested Edge Detection 边缘检测是视觉工作中十分常用的技术,传统边缘检测已经包含了很多经典的诸如Canny、Robert等等,都是各有擅场,不过有一点问题,就是很多参数需要人工调试,所以深度学习研究人员提出了基于卷积神经网络的边缘检测算法。 即HED,该算…

uniapp拍照离线定位,获取图片信息,经纬度解析地址

✅作者简介&#xff1a; 我是痴心阿文&#xff0c;你们的学友哥&#xff0c;今天给大家分享uniapp拍照离线定位&#xff0c;获取图片信息&#xff0c;经纬度解析地址 &#x1f4c3;个人主页:痴心阿文的博客_CSDN博客-Vue.js,数组方法,前端领域博主 &#x1f525;本文前言&#…

【数据结构】超详细之顺序表(利用C语言实现)

文章目录 前言一、顺序表是什么&#xff1f;二、顺序表的实现步骤 1.顺序表的初始化以及开辟空间2.实现顺序表的头插、尾插以及打印3.实现顺序表的头删、尾删以及打印4.实现顺序表的查找5.实现顺序表指定位置插入6.实现顺序表指定位置删除7.释放内存总结 前言 数据结构是一个程…