从生产到消费,设计基于物料的前端开发链路-------------引用
什么是物料?
物料(Material)这个概念在前端领域大家都不陌生。让我们从前端应用的构成上说起,在 DOM 中 节点(Node)是最小单位,再之上是元素(Element)。React 带来了组件(Component) 的概念,一个组件是由一个或多个元素构成的,组件是元素的超集。
- 
// 在项目中定义一个组件
- 
export default function Component(props) {
- 
return (<h1>Hello, {props.name}</h1>);
- 
}
- 
- 
// 在项目中使用该组件
- 
import Component from './Component';
- 
function Home() {
- 
return (<div><Component name="ICE" /></div>);
- 
}
面向特定的前端领域,前端的组成部件在设计和交互上是可枚举、可抽象以及通用的,于是一些组件成为了该领域的前端开发的基石。它们就是基础组件(Base Component)。例如在企业级中后台领域中的组件库 Fusion Design / Ant Design 。
- 
// 在基础组件库中定义一个组件
- 
export function Button(props)
- 
// 一些处理逻辑
- 
return (<button {...props}>{props.children}</button>);
- 
}
- 
- 
// 在项目中使用基础组件库的的组件
- 
import { Button } from '@alifd/next';
- 
function Home() {
- 
return (<div>
- 
<Button type="normal">普通按钮</Button>
- 
<Button type="primary">主要按钮</Button>
- 
<Button type="secondary">次要按钮</Button>
- 
</div>);
- 
}
基础组件粒度小,强调通用性,难以覆盖所有场景。对于垂直业务而言,会有特定交互逻辑或数据处理逻辑,例如阿里的花名选择器、钉钉的唤醒图标、淘宝的小蜜机器人等等,它们就是业务组件(Business Component)。通常面向一个垂直业务,会有一个业务组件库,例如小二工作台业务组件。
- 
import { Button } from '@alifd/next';
- 
export default function DingTalk(orginProps) {
- 
const props = {
- 
...orginProps,
- 
/* Business Logic */
- 
};
- 
return (<Button {...props}>{ /* Business Logic */ }</Button>);
- 
}
- 
- 
// 在项目中使用业务组件
- 
import DingTalk from '@ali/ding-talk';
- 
function Home() {
- 
return (<div>
- 
<DingTalk userId="foo" />
- 
</div>);
- 
}
对区块进行组合,就形成了页面(Page)。页面是一个浏览器窗口中所有功能的集合。一个或多个页面则组成了应用(Application)。开发应用的组织模式,就是前端项目(Project)。
自此,我们完成了对前端应用的析构,组件(基础/业务)、区块、页面这些构成应用的不同粒度单元就是物料。
为什么要基于物料?
基于组件进行前端开发已经是业界的共识了。实际场景中,区块、页面、脚手架在业务域中也有广泛的复用价值,各业务域存在的设计和交互规范,可以让前端对 UI 的抽象到更高更细的层次。因此如果能够抽丝剥茧,将业务域的物料进行整合,形成一套物料源,在业务域内进行流通,能够在业务域内有广泛的覆盖度进行复用和组合。物料这一层抽象,对于团队的分工协作,提升前端系统的可维护性,也大有裨益。
如何基于物料呢?需要有一定的开发模式。
我们对不同粒度的物料进行整合形成物料源(Material Collection),把物料源的组织模式称为物料项目(Material Project),它和前端项目中的物料对应关系如下:

然后设计基于物料的前端开发链路和角色,借助工具保障和提效各环节,从而保障生产质量、提升生产效率
初始化物料项目
物料项目中包含业务组件、区块、页面和脚手架多种粒度的物料。前面讲到了需要配套物料开发工具,我们提供的是物料开发命令行工具:Iceworks CLI,通过 $ iceworks init 创建一个物料项目,其目录结构如下:
- 
.
- 
├── .eslintrc.js
- 
├── .stylelintrc.js
- 
├── README.md
- 
├── package.json
- 
├── blocks/ 区块集合
- 
│ └── ExampleBlock/ 单个区块包
- 
│ └── package.json
- 
├── components/ 组件集合
- 
| └── ExampleComponent/ 单个业务组件包
- 
| └── package.json
- 
├── pages/ 页面模板集合
- 
| └── ExamplePage/ 单个页面包
- 
| └── package.json
- 
└── scaffolds/ 脚手架集合
- 
└── ExampleScaffold/ 单个脚手架包
- 
└── package.json项目内的 components/blocks/pages/scaffolds 文件夹是单种物料类型的集合,其子文件夹是一个个单独的物料包。
项目的 package.json 中重要字段是 materialConfig,它标明了当前物料项目是使用哪个物料模板资源包初始化的,后续也会用这个物料模板资源包来生成单个物料。
- 
{
- 
"materialConfig": {
- 
"template": "@icedesign/ice-react-ts-material-template",
- 
"type": "react"
- 
}
- 
}
Iceworks CLI 当前默认提供了多语言(TypeScript/JavaScript)以及多 DSL(React/Vue/Rax)的物料模板资源包,如果不满足(例如要开发基于 Angular 的物料项目),则可以通过开发相应的物料模板资源包,然后在初始化物料项目时指定使用的资源包来满足:$ iceworks init material-collection npmName。
单个物料包的开发
 初始化物料项目完成,进入到单个物料包的开发流程。通过 $iceworks add 向物料项目添加单个物料包

业务组件
业务组件的开发与常见的 React 组件开发无太大差别。Iceworks 主要提供了组件本地开发调试和构建的能力。一个业务组件的物料包的组织如下:
- 
.
- 
├── README.md 文档
- 
├── build.json 构建配置
- 
├── demo 使用示例,一个 md 文件一个示例
- 
│ └── usage.md
- 
├── package.json
- 
├── src 源代码
- 
│ ├── index.scss
- 
│ └── index.tsx
- 
└── tsconfig.json
需要特别说明的是:组件的 package.json 中的 componentConfig 是 Iceworks 专用的:
- 
{
- 
"componentConfig": {
- 
"name": "ExampleComponent", // 生成代码时使用的导入名
- 
"title": "demo component", // 用于展示标题
- 
"category": "Information" // 用于展示时进行的分类名,任意值
- 
}
- 
}
以开发一个业务组件为例的命名行执行过程:
- 
$ iceworks add component
- 
$ cd components/ExampleComponent
- 
$ npm install
- 
$ npm run start # 启动本地调试
- 
$ npm publish # 开发完成,执行 npm 发布
页面模板
页面物料和区块物料一样,是以源代码复制的方式被前端项目使用的。执行 $ iceworks add page 向物料项目添加一个页面模板资源包,其目录结果如下:
- 
.
- 
├── README.md
- 
├── build.json
- 
├── config 模板配置文件
- 
│ ├── mock.js 模拟配置
- 
│ └── settings.json 配置设置
- 
├── package.json
- 
├── src 模板源文件
- 
│ ├── components
- 
│ │ └── User
- 
│ │ └── index.tsx.ejs
- 
│ └── index.tsx.ejs
- 
└── tsconfig.json
在页面资源包中,src 内存放的都是模板文件,模板使用 ejs 语法。一个模板示例(模板里面有 isShowUser 和 title 两个模板变量):
- 
import React, { useEffect, useState } from 'react';
- 
<% if (isShowUser) { %>
- 
import User from './components/User';
- 
- 
async function fetchUser() {
- 
return { name: 'ICE', age: '18' };
- 
}
- 
<% } %>
- 
- 
export default function() {
- 
<% if (isShowUser) { %>
- 
const [ user, setUser ] = useState({});
- 
useEffect(() => {
- 
async function initUser() {
- 
setUser(await fetchUser());
- 
}
- 
initUser();
- 
}, []);
- 
<% } %>
- 
- 
return (
- 
<>
- 
<div><%= title %></div>
- 
<% if (isShowUser) { %>
- 
<User {...user} />
- 
<% } %>
- 
</>
- 
);
- 
}
在 config/mock.js 中声明本地调试使用的模板变量模拟数据
- 
export default {
- 
isShowUser: true,
- 
title: '标题'
- 
};
在 config/setting.json 中声明模板变量的 Schema,Schema 字段使用 Formily Schema 协议,用于生成前台配置化表单:
- 
{
- 
"schema": {
- 
"title": "用户任务列表",
- 
"description": "显示用户信息",
- 
"type": "object",
- 
"required": [
- 
"isShowUser"
- 
],
- 
"properties": {
- 
"isShowUser": {
- 
"type": "boolean",
- 
"title": "是否显示用户信息",
- 
"default": true
- 
},
- 
"title": {
- 
"type": "string",
- 
"title": "标题"
- 
}
- 
}
- 
}
- 
}
页面物料开发完成,执行 npm publish,将会依次执行:
- 
构建测试:检测是否有语法错误。 
- 
生成缩略图:用于在物料中心和物料面板进行展示。 
- 
发布 npm 包:发布 npm 目的是为了托管源代码到 npm,后续 Iceworks 将使用 npm 的 tarball 下载该源代码并使用。 
自此,完成了页面物料的创建、开发和发布全流程。
 
                    
                     
                    
                 
                    
                
 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号