整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          阿里重磅開源中后臺 UI 解決方案 Fusion

          018 年 12 月 16 號,Fusion 在 OSC 深圳源創(chuàng)會年終盛典上正式開源, 這個在阿里內部跑了三年的產品,終于對外了。

          可能一些業(yè)界的同學已經在某些渠道聽說過阿里的 Fusion Design

          • 比如這篇 16 年就傳得很火的《阿里最新神器-fusion design》
          • 比如知乎上面 17 年討論得很熱烈的話題 《阿里巴巴的 Fusion Design 是如何運作的?》
          • 也有可能來自 16 年 d2 論壇上周源同學的對外分享

          距離上次對外發(fā)聲,Fusion 在阿里內部又走了兩年,這兩年內我們技術領域又有了一些突破。這次重點介紹我們的突破吧。不過介紹前還是要說下 Fusion 是做什么的,畢竟還是有很多同學是第一次聽說的!

          1. Fusion 是做什么的

          1.1 解決設計師和前端工作協同的問題

          一般一個項目的上線流程基本都要經歷,評審、設計、開發(fā)、測試 這幾個階段。

          而各個階段又可以再深入進去的拆分,大致如下:

          • 評審: 業(yè)務交互(產品功能交互),業(yè)務邏輯的評審;
          • 設計:設計規(guī)范(設計師對整個產品在視覺規(guī)范上面的定義),視覺設計(繪制視覺稿),標注稿(產出標注搞給到前端)
          • 開發(fā):前端一般都會有一套組件庫;但是組件庫可能和自己業(yè)務線的品牌并不是對應的(比如阿里橙、天空藍,基本沒類業(yè)務線都會有個自己的品牌),所以第一步需要在組件層面做 UI 的定制,然后是業(yè)務邏輯的開發(fā)。
          • 測試:最常見的就是設計師和前端坐在一起兩天專門做 UI 還原度 review;業(yè)務邏輯測試是比做流程不多說。

          這里重點說下流程中的兩個問題。

          重復工作

          這里面標黃了的部分表示可能是各個業(yè)務線間的重復工作:

          • 比如中后臺 UI 的交互都是確定的
          • 比如設計規(guī)范都可以在一個集成的規(guī)范上面做擴展
          • 比如標注可以通過插件統(tǒng)一解決,而視覺還原、還原度 review 可以交給設計師去關心

          協同問題

          協同遇到的問題,上面知乎鏈接里面周源的回答已經非常清楚了,我這里再大概總結下。

          • 因為使用的工具不同對概念的認知不同

          對陰影的理解不同

          ·設計師的理想和前端的現實問題之間的差別

          同樣 margin-top:12px 但是得到的間距不一致

          • 每隔一段時間品牌就會升級一次,基礎 UI 翻新,會有較大的工作量
          • 設計師間約定的規(guī)范沒有很好的落實,已經設計好的設計稿大家共享不便
          • 已經開發(fā)好的組件沒有形成很好的復用

          ·

          1.2 Fusion 工作流

          去除重復流程,只關注業(yè)務

          • 設計師更加方便的做設計
          • 而前端只需關注業(yè)務邏輯

          ·

          通過抽象骨架 DPL -> 通過平臺定制產出定制好 UI 的組件、模板 -> 流入設計師的工具里面直接拖拽使用 -> 前端直接使用定制好的組件(不需關注組件 UI)

          • 設計師使用的同一套規(guī)范的組件,產出的設計稿都是同一套規(guī)范。(這里使用 sketch 插件名字叫 FusionCool)
          • 前端不需要關注組件 UI 還原度。(還原度有問題 = 設計配置的問題)

          ·

          1.3 不需要再做從 0~1 的事情

          ·

          • 設計端使用 sketch 插件(FusionCool)在 sketch 既能設計頁面,又能沉淀已經設計完成的模板
          • 開發(fā)端使用 開發(fā)工具 (Iceworks)在項目中既能使用現成的模塊,又能沉淀已經開發(fā)完成的模塊

          那未來在 Fusion 模塊模板達到極度豐富后,使用可以方便的找到各個領域的模塊模板來使用,不需要從 0 開始搭建。

          2. Fusion 能力點

          一個平臺,兩個端

          一個平臺:fusion.design

          兩個工具:

          • 開發(fā)者工具 Iceworks
          • 設計師工具 FusionCool

          2.1 一個平臺

          https://fusion.design 可以定制自己的 Design System(以下簡稱 DS)

          創(chuàng)建自己的 Design System

          每個人或者團隊都可以通過 https://fusion.design/sites/new 可以創(chuàng)建自己的站點

          站點提供三種能力:文檔編輯、主題管理、物料托管。

          文檔編輯

          存儲設計師文檔和開發(fā)文檔。

          主題配置管理

          集成了可以管理、定制、發(fā)布組件的主題的能力(下文會簡稱為配置平臺)。

          物料托管

          可以使用開發(fā)好的區(qū)塊、模板。

          區(qū)塊 - 代碼片段

          模板 - 腳手架

          2.2 兩個工具端

          設計師工具 - FusionCool

          主題發(fā)布完成后就到了 Sketch 的插件端 FusionCool,設計師可以在 FusionCool 里面搜索 iconfont 所有素材、使用配置好的組件、使用站點的模塊模板。

          開發(fā)者工具 - Iceworks

          Iceworks 是淘寶飛冰團隊開發(fā)的面向前端開發(fā)者的 GUI 工具,開發(fā)者無須關注環(huán)境的問題,并且有海量物料可用。目前已經和 Fusion 的物料體系打通,可以輕松使用 Fusion 站點的物料。

          每個站點有自己的物料源

          可直接在 Iceworks 配置物料源地址

          項目開發(fā)

          3. Fusion 的技術實現

          Next 組件

          Fusion Next 是基于 React 實現的一套 PC 端的組件庫,這套組件庫已經在阿里內部服務了三年。

          github 地址:https://github.com/alibaba-fusion/next

          這次開源出來的版本是最近一年基于之前兩年的使用經驗、問題反饋進行重新整理和優(yōu)化過。具備以下特性。

          易用性

          對比上一個版本 80 + 功能,進行 300+ 優(yōu)化,組件整體代碼體積卻減小 30%

          • next.min.js 910KB -> 702KB
          • next.min.css 428KB -> 337KB

          一共 50+ 組件,打包下來卻只有 700 多 K,這個目前在業(yè)界比較少組件有能力做到這點。組件之間依賴關系清晰,復用度高也是體積小的原因。

          穩(wěn)定性

          組件單測覆蓋率近 90%,提供服務以來沒有產生過起線上事故。

          能力增強

          國際化、RTL、無障礙能力全面支持。另外針對中后臺表單大數據量場景做了大量性能優(yōu)化,比如普通 table 隨著數據不斷增長 render 會越來越慢,大致如下:

          Next 引入了 virtual-list ,目前用在了 table 和 select 這兩個使用頻率較高的組件。因為在大數據量 (測試過 1w 節(jié)點)下只渲染需要展示的節(jié)點(比如 20 個),所以可以將渲染時長永遠的控制在 0.3s 之內。

          FusionCool

          代碼到視覺稿的無損還原

          突破 html2svg 的弊端,做到無損還原

          早在一年前我們是把設計師在主題配置平臺(直接在 web 頁面配置組件的主題)的組件直接通過 html2svg 技術直接把組件直接轉換為 svg 文件,從而讓設計師可以直接在 sketch 里面使用。但是這種方案存在的弊端就是還原度不夠(大概 95% 還原度)。

          html2svg 的還原度問題

          主要原因是 html 采用盒模型 和 svg 的轉換并不是一一對應的,所以這里永遠有修不完的 bug。雖然 95% 是好的,但是對于設計端來說是完全不能忍受的。

          所以 Fusion 項目小組經過近半年的努力終于突破了還原度的問題,流程圖如下:

          從配置平臺導出的不再是 html,而是 DesignToken (設計變量),FusionCool 底層使用 Airbnb 提供的 react-sketch 能力寫成的一份 Next 組件,直接通過 DesignToken 覆蓋默認變量,最終在 Sketch 端實時渲染。

          組件的類型、大小、內容都可以直接在面板配置

          圖表配置可以直接喚起配置面板

          sketch 端的任何點擊都可以通過 Event 的方式在 FusionCool 產生配置面板。

          4. Fusion 未來

          • 我們會在這半年內讓 Fusion 的物料倉庫能夠極度豐富,可以覆蓋到各行業(yè)領域。讓設計師和開發(fā)者都不需要再做 0-1 的事情。
          • 我們期望能夠突破視覺稿轉代碼的技術難題,讓視覺稿到可用代碼成為可能。

          相關鏈接

          Fusion 站點:https://fusion.design/

          next github 倉庫: https://github.com/alibaba-fusion/next

          方案支持顏色自定義,高亮行數自定義,可高亮多行,可定義多個顏色,主要實現方式是css樣式,代碼簡單易懂,下面看效果和代碼吧:

          當前效果:



          預期效果:

          代碼:這是HTML代碼 綁定這個屬性:row-class-name=“tableRowClassName”

          ```

          <el-table

          :data="tableData"

          style="width: 100%"

          :row-class-name="tableRowClassName">

          <el-table-column

          prop="date"

          label="日期"

          width="180">

          </el-table-column>

          <el-table-column

          prop="name"

          label="姓名"

          width="180">

          </el-table-column>

          <el-table-column

          prop="address"

          label="地址">

          </el-table-column>

          </el-table>

          ```

          這是JS代碼 注冊這個方法tableRowClassName(),這里傳的rowIndex就是需要高亮的數據在數組里面的下標,返回的class類名就是你自己定義的class類名。

          ```javascript

          methods: {

          tableRowClassName({row, rowIndex}) {

          if (rowIndex === 1) {

          return 'warning-row';

          } else if (rowIndex === 3) {

          return 'success-row';

          }

          return '';

          }

          },

          Table 要展示統(tǒng)計行應該是個很常見的業(yè)務需求,本文將介紹一種實現此功能的方法。注意,此方法不兼容 IE 瀏覽器。

          場景介紹

          公司存量項目使用的 antd 版本為 3.x,此版本的 antd 并沒有支持統(tǒng)計行功能,只有一個 footer 屬性可以在表格底部增加一行內容,但是這個并不能滿足統(tǒng)計行的要求(無論是單元格對齊還是橫向滾動),所以需要想個其他方法去實現統(tǒng)計行。

          同時對于公司內新項目來說,用的是 antd 5.x 的版本,此版本支持統(tǒng)計行功能,但是需要使用 Summary 組件去構造統(tǒng)計行,功能靈活但是使用上會麻煩些。

          所以就思考有沒有能在不同 antd 版本下都實現統(tǒng)計行的技術方案,同時使用上又比較簡單。

          實現方案

          首先來分析下統(tǒng)計行的特點:

          • 統(tǒng)計行是獨立的一行數據,會使當前頁的數據量 +1,如一頁 10 條數據的話,加上統(tǒng)計行則為 11 條。
          • 通常表格豎向滾動時,統(tǒng)計行會固定在表格尾部。
          • 樣式和交互需要和表格為一個整體,包括單元格、橫向滾動等等。

          對于第一點,如果 antd 的 Table 組件設置了分頁功能,比如 pagination: { pageSize: 10 },此時就無法展示額外的統(tǒng)計行,所以我們不使用 Table 的分頁功能,自己通過組合 Pagination 組件來實現分頁,數據傳參方面, TabledataSource 除了當前頁數據,在拼上統(tǒng)計行數據即可。

          第二點,固定在尾部,對于現代 css 來說,直接使用 position: sticky 實現即可。

          第三點的話,在第一點提到的方案中已經實現。

          整體來說,就是實現以下兩個功能點:

          • 不使用 Table 內置的分頁,使用 TablePagination 組合來完成。
          • 使用 position: sticky 完成固定行能力。

          具體實現

          在 antd Table API 基礎上,新增一個 summaryFixed API,代表統(tǒng)計行(即最后一行)固定在表格底部,默認為 false,不影響沒有統(tǒng)計行的列表展示。

          首先我們實現下統(tǒng)計行固定功能。

          import React from "react";
          import classNames from "classnames";
          import { Table as AntdTable } from "antd";
          import "./table.css";
          
          export default function Table({
            summaryFixed,
            ...restProp
          }) {
            return (
              <div
                className={classNames("my-enhance-table", {
                  "my-enhance-table-summary-fixed": summaryFixed,
                })}
              >
                <AntdTable {...restProp} />
              </div>
            );
          }
          

          代碼中使用到了 classnames 庫,在 summaryFixedtrue 的時候,就會加上對應類名,接著就根據 my-enhance-table-summary-fixed 類名設置對應 css。

          .my-enhance-table.my-enhance-table-summary-fixed {
            tr.ant-table-row:last-child {
              background: #fff;
              position: sticky; /* 設置為 sticky 布局 */
              bottom: 0;        /* 固定在底部 */
              z-index: 2;
              &:not(:first-child) > td {
                border-top: 1px solid #f0f0f0;
              }
            }
            tr.ant-table-row:nth-last-child(2) {
              > td {
                border-bottom: 0;
              }
            }
          }
          

          上述 css 中通過 tr.ant-table-row:last-child 選擇器選中最后一行并設置為 sticky 布局,其他 css 代碼是處理邊框問題的。

          固定統(tǒng)計行功能實現完成后,接著通過組合 TablePagination 組件的方式實現統(tǒng)計行的展示。

          import React, { useRef, useEffect } from "react";
          import isEmpty from "lodash/isEmpty";
          import classNames from "classnames";
          import { Table as AntdTable, Pagination } from "antd";
          import "./table.css";
          
          export default function Table({
            dataSource,
            columns,
            pagination,
            onChange,
            summaryFixed,
            ...restProp
          }) {
            const onTableChange = (pagination, filters, sorter, extra) => {
              // 處理排序和篩選之類功能
            };
          
            const onPaginationChange = (current, pageSize) => {
              // 處理翻頁功能
            };
          
            return (
              <div
                className={classNames("my-enhance-table", {
                  "my-enhance-table-summary-fixed": summaryFixed,
                })}
              >
                <AntdTable
                  {...restProp}
                  pagination={false}
                  dataSource={dataSource}
                  columns={columns}
                  onChange={onTableChange}
                />
                {pagination !== false && (
                  <Pagination {...pagination} onChange={onPaginationChange} />
                )}
              </div>
            );
          }
          

          Tablepagination 設置為 false,并將外部的 pagination 參數傳給傳入到 Pagination 組件,我們就可以在 pageSize 為 10 的情況下,讓 dataSource 內容全部展示(即使 dataSource 的數據量超過 10)。

          最后我們需要處理下翻頁和排序等相關操作:

          ...
          
          const initPagination = { current: 1, pageSize: 10, total: 0 };
          
          // 獲取 columns 中排序參數,區(qū)分首次和后續(xù)更新
          const getSorterParam = (columns, isInit = false) => {
            const sorterParam = {};
            for (let index = 0; index < columns.length; index++) {
              const { sorter, defaultSortOrder, sortOrder, dataIndex, key } =
                columns[index] || {};
              const order = isInit
                ? sortOrder != null
                  ? sortOrder
                  : defaultSortOrder
                : sortOrder;
              if (sorter && order) {
                sorterParam.field = dataIndex || key;
                sorterParam.columnKey = dataIndex || key;
                sorterParam.order = order;
                sorterParam.column = columns[index];
                return sorterParam;
              }
            }
            return sorterParam;
          };
          
          export default function Table({
            dataSource,
            columns,
            pagination,
            onChange,
            summaryFixed,
            ...restProp
          }) {
            const sorterParam = useRef(getSorterParam(columns, true));
            const filtersParam = useRef({});
            const paginationParam = useRef(pagination || initPagination);
          
            useEffect(() => {
              paginationParam.current = pagination || initPagination;
            }, [pagination]);
          
            useEffect(() => {
              // 排序可控模式下更新排序
              if (columns.some((col) => !!col.sortOrder)) {
                sorterParam.current = getSorterParam(columns);
              }
            }, [columns]);
          
            const onTableChange = (pagination, filters, sorter, extra) => {
              if (!isEmpty(sorter)) {
                sorterParam.current = sorter;
              }
              if (!isEmpty(filters)) {
                filtersParam.current = filters;
              }
              if (onChange) {
                onChange(
                  isEmpty(pagination) ? paginationParam.current : pagination,
                  filtersParam.current,
                  sorterParam.current,
                  extra
                );
              }
            };
          
            const onPaginationChange = (current, pageSize) => {
              paginationParam.current = { ...paginationParam.current, current, pageSize };
              pagination?.onChange?.(current, pageSize);
              onTableChange(paginationParam.current, undefined, undefined, {
                currentDataSource: dataSource,
                action: "paginate",
              });
            };
            
            return (
              ...
            )
          }
          
          

          無論是翻頁還是排序或篩選,我們都需要記錄下對應的值。因為翻頁功能是單獨的 Pagination 組件提供的,所以觸發(fā)翻頁時,我們需要用到之前的排序或篩選參數,通過 onChange 一起給到外部;觸發(fā)排序或篩選的情況同理,需要用到最近一次的翻頁參數。

          最終完整代碼可查看:https://codesandbox.io/p/sandbox/antd-table-tong-ji-xing-gu-ding-grlz88?file=%2FTable.js%3A19%2C27

          總結

          通過組合 Pagination 組件來實現超過當前每頁條數的數據量展示,同時配合現代 css 的 position: sticky 能力,我們實現了讓 Table 表格支持統(tǒng)計行功能。當然,上述代碼有需要優(yōu)化的地方,比如翻頁后回到表格頂部等等。


          主站蜘蛛池模板: 国产一区二区三区影院| 国产福利日本一区二区三区| 国产高清在线精品一区二区| 亚洲国产AV无码一区二区三区| 无码一区二区三区中文字幕| 午夜AV内射一区二区三区红桃视| 97久久精品无码一区二区天美| 亚洲av无一区二区三区| 国产aⅴ一区二区三区| 久久99久久无码毛片一区二区| 精品国产福利第一区二区三区| 亚洲AV无码一区二区二三区入口| 99精品一区二区三区无码吞精| 日韩美女视频一区| 免费观看一区二区三区| 亚洲中文字幕在线无码一区二区 | 鲁丝片一区二区三区免费| 秋霞午夜一区二区| 精品无码综合一区二区三区| 一区二区国产精品| 制服丝袜一区在线| 国产主播一区二区三区 | 精品一区二区三区免费| 亚洲国产欧美国产综合一区| 久99精品视频在线观看婷亚洲片国产一区一级在线 | 日本一区二区三区中文字幕| 国产午夜精品一区二区三区| 一本大道在线无码一区| 国产一区二区三精品久久久无广告| 国产精品香蕉在线一区| 本免费AV无码专区一区| 精品国产一区二区三区久久| 亚洲午夜精品一区二区公牛电影院 | 久草新视频一区二区三区| 中文字幕av日韩精品一区二区| 中文字幕一区二区三区有限公司| 亚洲AV成人精品日韩一区| 亚洲乱码一区二区三区在线观看 | 国产一区高清视频| 精品无码一区二区三区爱欲九九| 免费萌白酱国产一区二区|