導讀:本文摘自阿里內網的無線前端博客
《無線前端的圖片相關工作流程梳理》。其實是一個月前寫的,鑒于團隊在中國第二屆 CSS Conf上做了《手機淘寶CSS實踐啟示錄》的分享,而圖片工作流程梳理是其中的一個子話題,故在此一并分享出來,希望仍可以給大家一些經驗和啟發。另外,考慮到這是一篇公開分享,原版內容有部分刪節和調整,里面有一些經驗和產出是和我們的工作環境相關的,不完全具有普遍性,還請見諒。
今天很榮幸的跟大家分享一件事情,就是經過差不多半年多的努力,尤其是最近 2 周的“突擊掃尾”,無線前端團隊又在工具流程方面有了一個不小的突破:我們暫且稱其為“圖片工作流”梳理。
圖片!圖片!圖片!
要說最近 1 年里,無線前端開發的一線同學最“難搞”的幾件事,圖片處理絕對可以排在前三。
首先,我們首先要從視覺稿 (絕大部分出自 photoshop) 里把圖片合理的分解、測量、切割、導出——俗稱“切圖”
然后,我們要把切好的圖放入頁面代碼中,完成相關的本地調試
第三步,把本地圖片通過一個內部網站 (名叫 TPS) 上傳到我們的圖片 CDN 上,并復制圖片的 CDN 地址,把本地調試用的相對路徑替換掉
第四步,不同的圖片、不同的外部環境下 (比如 3g 還是 wifi),我們需要給圖片不一樣的尺寸、畫質展現,并有一系列的配置需要遵循
如果視覺稿有更改 (不要小看這件事,微觀上還是很頻繁的哦),不好意思,從第一步開始再重新走一遍……
這里面“難搞”在哪些地方呢?我們逐一分析一下:
“切圖”的效率并不高,而且每一步都很容易出現返工或再溝通
打開 TPS 網站上傳圖片放到前端開發流程中并不是一個連貫流暢的步驟,而且 GUI 相比于命令行工具的缺陷在于無法和其它工具更好的集成
替換 CDN 圖片路徑的工作機械而繁瑣,并且代碼中替換后的圖片地址失去了原本的可讀性,非常容易造成后期的維護困惑甚至混亂
適配工作異常繁雜和辛苦,也很容易漏掉其中的某個環節
視覺變更的成本高,web 的快速響應的特點在喪失
所以可能把這些東西畫成一張圖表的話:

隊的單點突破
在最近半年的一段時間里,無線前端團隊先后發起了下面幾項工作,從某個點上嘗試解決這些問題:
lib.flexible
首
先,我們和 UED 團隊共同協商約定了一套 REM 方案 (后更名為 flexible 方案,進而演進為 lib.flexible
庫),通過對視覺稿的產出格式的約定,從工作流程的源頭把控質量,同時在技術上產出了配套的 lib.flexible
庫,可以“抹平”不同設備屏幕的尺寸差異,同時對清晰度進行了智能判斷。這部分工作前端的部分是 @wintercn 寒老師和
@terrykingcha 共同創建的。

覺稿輔助工具普及
其
次,我們于去年 12 月開始啟動了一個“視覺稿工具效率提升”的開放課題,由團隊的 @songsiqi 負責牽頭,我們從課題的一開始就確立了
KPI 和 roadmap,經過一段時間的調研和落實,收羅了很多實用的輔助工具幫助我們提升效率,同時布道給了整個團隊。比如
cutterman、parker、Size Marks 等
img-uploader
在
@hongru 去年主持完成的一系列 One-Request 前端工具集當中,有一個很有意義的名叫 or-uploadimg
的圖片上傳工具。它把 TPS
的圖片上傳服務命令化了。這給我們對圖片上傳工作批量化、集成化提供了一個非常重要的基礎!這個工具同時也和淘寶網前端團隊的另一個 TPS
圖片上傳工具有異曲同工之妙。大概用法是這樣的,大家可以感受一下:

隨后團隊又出現了這一工具的 gulp 插件,可以對圖片上傳的工作流程做一個簡單的集成,具體集成方式是分析網頁的 html/css 代碼,找到其中的相對圖片地址并上傳+替換 CDN URL。

lib.img
是團隊 @chenerlang666
主持開發的一個基礎庫,它是一套圖片自動處理優化方案。可以同時解決屏幕尺寸判斷、清晰度判斷、網絡環境判斷、域名收斂、尺寸后綴計算、畫質后綴計算、銳
化度后綴計算、懶加載等一系列圖片和性能相關的問題。這個庫的意義和實用性都非常之高,并且始終保持著快速的業務響應和迭代周期,也算是無線前端團隊的一
個明星作品,也報送了當年度的無線技術金碼獎。

px2rem
px2rem 是 @頌奇 主持開發的另一個小工具,它因 lib.flexible 方案而生,因為我們統一采用 rem 單位來最終記錄界面的尺寸,且對于個別1像素邊框、文本字號來說,還有特殊的規則作為補充 (詳見 lib.flexible 的文檔)。

同樣的,它也有 gulp / browser 的各種版本。
img4dpr
img4dpr
則是一個可以把 CSS 中的 CDN URL 自動轉成 3 種 dpr 下不同的尺寸后綴。算是對 lib.img
的一個補充。如果你的圖片不是產生在 <img> 標簽或 JavaScript 中,而是寫在了 CSS 文件里,那么即使是
lib.img 恐怕也無能為力,img4dpr 恰恰是在解決這個問題。

事兒了嗎?
看上去,團隊為團隊做了很多事情,每件事情都在單點上有所突破,解決了一定的問題。
但我們并沒有為此停止思考
有
一個很明顯的改進空間在這里:今天我們的前端開發流程是一整套工程鏈路,每個環節之間都緊密相扣,
解決了單點的問題并不是終點,基于場景而不是功能點的思考方式,才能夠把每個環節都流暢的串聯起來,才能給前端開發者在業務支持的過程當中提供完美高效暢
通無阻的體驗——這是我們為之努力的更大的價值!也是我認為真正“臨門一腳”的最終價值體現!
基于場景的思維方式
這種思維方式聽上去很玄幻,其實想做到很簡單,我們不要單個兒看某個工具好不好用,牛不牛掰,模擬真實工程場景,創建個新項目,從
“切圖”的第一步連續走到發布的最后一步,看看中間哪里斷掉了?哪里銜接的不自然?哪里不完備?哪里重復設計了?哪里可以整合?通常這些問題都會變得一目
了然。
首先,在 Photoshop 中“切圖”本身的過程對于后續的開發流程來說是相對獨立的,所以這里并沒有做更多的融合 (從另外一個角度看,這里其實有潛在的改造空間,如何讓“切圖”的工作也能集成到前端工具鏈路中,這值得我們長期思考)
然后,從圖片導出產生的那一刻起,它所經歷的場景大概會是這么幾種:

其中 (upload time) 指的是我有機會在這個時機把圖片上傳到 CDN 并把代碼里的圖片地址替換掉;(* resize) 指的是我有機會在這個時機把圖片的域名收斂/尺寸/畫質/銳化度等需求處理掉。
經過這樣一整理,我們很容易發現問題:
圖片上傳存在很多種可選的時機,并沒有形成最佳實踐
有些鏈路完全沒有機會做必要的處理 (如 to HTML -> src 的鏈路無法優化圖片地址)
有些鏈路處理圖片的邏輯并不夠智能 (比如需要手動確定優化圖片選項的鏈路)
圖片上傳 CDN 之后必須手動替換掉源代碼里的圖片路徑,這個問題在任何一個鏈路里都沒有得到解決
CSS 相關的小工具很多,比較零散,學習和使用的成本在逐步變高變復雜
沒有統一完善的項目腳手架,大家創建新項目都需要初始化好多小工具的 gulp 配置 (當然有個土辦法就是從就項目里 copy 一份 package.json 和一份 gulpfile.js)
基于場景的“查漏補缺”
在完善場景的“最后一公里”,我們做了如下的工作:
把所有的 CSS 工具集成到了 postcss,再通過 postcss 的 gulp 插件、webpack 插件、browserify
插件令其未來有機會靈活運用到多種場景而不需要做多種工具鏈的適配,即:postcss-px2rem、postcss-img4dpr,同時額外的,借
此機會引入 postcss-autoprefixer,讓團隊拜托舊的 webkit 前綴,擁抱標準的寫法
把
圖片上傳的時機由最早的 or-imgex-gulp 在最后階段分析網頁的html/css代碼上傳替換其中的圖片,變為在 images
目錄下約定一個名為 _cdnurl.json 的文件,記錄圖片的 hash 值和線上 CDN 地址,并寫了一個
@ali/gulp-img-uploader 的 gulp 插件,每次運行的時候會便利 images 文件夾中的圖片,如果出現新的 hash
值,就自動上傳到 CDN,并把相應生成的 CDN URL 寫入 _cdnurl.json
同
時,這個文件可以引入到頁面的 JavaScript 環境中,引入到 img4dpr 工具中,引入到 lib.img 的邏輯中,讓
HTML/CSS/JavaScript 的各種使用圖片的場景都可以訪問到 _cdnurl.json 中記錄的本地圖片路徑和線上地址的對應關系
這也意味著 lib.img, img4dpr 需要做相應的改動,同時
頁面本身要默認把 _cdnurl.json 的信息引入以做準備
創建一個 lib.cdnurl 的庫,在圖片未上傳的情況下,返回本地路徑,在已經上傳的情況下,返回 CDN URL,這樣通過這個庫作支持,外加 lib.img、img4dpr,開發者可以做到在源代碼中完全使用本地路徑,源代碼的可讀性得到了最大程度的保證
基于 adam 創建一個包含全套工具鏈路的項目模板 (腳手架)
上述幾件事我們于上周一做了統一討論和分工,這里要感謝 @mingelz @songsiqi @chenerlang666 的共同努力!!
夾帶私貨 (偷笑)
我在這個過程中,融入了之前一段時間集中實踐的 vue 和 webpack 的工程體系,在 vue 的基礎上進行組件化開發,在 webpack 的基礎上管理資源打包、集成和發布,最終合并在了最新的 just-vue 的 adam template 里面。
之前不是在文章的最后賣了個“最后一公里”的關子嗎,這里介紹的圖片工作流改進就是其中的一部分:)
同
時,我基于 lib.img 的思路,結合 vue.js 自身的特點,寫了一個 v-src 的 directive,在做到 lib.img 里
[data-src] 相同目的的同時,更好的融入了 vue.js 的體系,同時加入了更高集成度的功能,稍后會再介紹。
夾帶了私貨之后是不是我就沒法用了?
最后我想強調的是,除了自己的這些“私貨”之外,上面提到的幾個改進點和這些個人的內容是完全解耦的,如果你不選擇 vue.js 或 webpack 而是別的同類型工具或自己研發的一套工具,它依然可以靈活的融入你的工作流程中。
最終效果
我們在團隊內部把這些工作流程以腳手架的方式進行了沉淀,并放在了團隊內部叫做 adam 的 generator 平臺上 (后續會有介紹) 取名叫做 just-vue (時間倉促,adam 和相關的 generator 未來會在適當的時機開放出來)。大致用法:
安裝 adam 和 just-vue 模板:
tnpm install -g @ali/adam
adam tmpl add <just-vue git repo>
交互式初始化新項目:

目錄結構剖析
然后大家會看到項目目錄里默認就有:
gulpfile.js,里面默認寫好了圖片批量上傳并更新 _cdnurl.json、webpack 打包、htmlone 合并 等常見任務
images 目錄,里面放好了關鍵的 _cdnurl.json,還有幾張圖片作為示例,它們的 hash 和 CDN URL 已經寫好了
src/main.*,
主頁面入口,包括一個 htmlone 文件 (main.html),一個 webpack 文件 (main.js) 和一個 vue 主文件
(main.vue),默認引入了需要的所有樣式和腳本,比如 lib.img, lib.flexible, lib.cdnurl,
_cdnurl.json, v-src.js 等,我們將來主要的代碼都會從 main.vue 寫起——額外的,我們為 MT
模板開發者貼心的引入了默認的 mock 數據的 <script data-mt-variable="data"> 標簽,不需要 MT
模板開發環境的將其刪掉即可
components 目錄,這里會把我們拆分下來的子組件都放在這里,我們示范性的放了一個 foo.vue 的組件在里面,并默認引入了 lib.cdnurl 庫
lib 這里默認放入了 lib.img, lib.cdnurl, v-src.js
幾個庫,這幾個庫在未來逐步穩定之后都會通過 tnpm + CommonJS 的方式進行管理,目前團隊 tnpm + CommonJS
的組件整合還需要一定時間,這里是個方便調整迭代的臨時狀態。
然后,我們來看一看 main.vue 里的細節,這才是真正讓你真切感受到未來開發體驗的地方。
圖片工作場景
首先,新產生任何圖片,盡管丟到 images 目錄,別忘了起個好理解的文件名
CSS 中的圖片
然后,在 main.vue 的第 11 行看到了一個 CSS 的 background-image 的場景,我們只是把 url(../images/taobao.jpg) 設為其背景圖片:
background-image: none;
完成了!就這樣!你在發布之前不需要再關注額外的事情了。沒有手動上傳圖片、沒有另外的GUI、沒有重命名、沒有 CDN 地址替換、沒有圖片地址優化、沒有不可讀的代碼
HTML 中的圖片
我們再來看看 HTML 里的圖片,來到 39 行:
<img id="test-img" v-src="../images/one.png" size="cover">
一個 [v-src] 特性搞定!就這樣!你在發布之前不需要再關注額外的事情了 (這里 [size] 特性提供了更多的圖片地址優化策略,篇幅有限,大家感興趣可以移步到 lib/vue-src.js 看其中的實現原理)。
JavaScript 中的圖片
最后再看看在 JavaScript 里使用圖片,來到 68 行:
this.$el.style.backgroundImage = 'url(' + cdn('../images/logo.png') + ')'
只加入了一步 cdn(...) 的圖片生成,也搞定了!就這樣!你在發布之前不需要再關注額外的事情了。
發布
那有人可能會懷疑: “那你都說發布之前很方便,發布的時候會不會太麻煩啊?”
好問題,發布就兩行命令:
# 圖片增量上傳、webpack 打包、htmlone 合并,最終生成在 dist 目錄gulp# 交互式上傳到 awpawp
正常的命令行反應是類似這樣的:

你甚至可以寫成一行:
gulp && awp
最終這個初始化工程的示例頁面的效果如下

設計變更了?
這條鏈路是我們之前最不愿意面對的,今天,我們來看看這條鏈路變成了什么,假設有一張設計圖要換:
在 Photoshop 里把圖重新切下來
同名圖片文件放入 images 文件夾
運行 gulp && awp
就這樣!
額外的,如果尺寸有變化,就加一步:更改相應的 CSS 尺寸代碼
總結
在
整個團隊架構的過程中,大家都在不斷嘗試,如何以更貼近開發者真實場景的方式,還原真實的問題,找出切實有效的解決方案,而不僅僅是單個功能或特性。這樣
我們往往會找到問題的關鍵,用最精細有效的方式把工作的價值最大化。其實“基于場景的思維方式”不只是流程設計的專利,我們業務上的產品設計、交互設計更
需要這樣的思維。我個人也正是受到了一些產品經理朋友們的思維方式的影響,把這種方式運用在了我自己的工作內容當中。希望我們產出的這套方案能夠給大家創
造一些價值,更是向大家傳遞我們的心得體會,希望這樣的思維方式和做事方式可以有更多更廣的用武之地。
來源:https://github.com/amfe/article/issues/8
- PHP技術文章
- PHP中session和cookie的區別
- php設計模式(一):簡介及創建型模式
- php設計模式結構型模式
- Php設計模式(三):行為型模式
- 十款最出色的 PHP 安全開發庫中文詳細介紹
- 12個提問頻率最高的PHP面試題
- PHP 語言需要避免的 10 大誤區
- PHP 死鎖問題分析
- 致PHP路上的“年輕人”
- PHP網站常見安全漏洞,及相應防范措施總結
- 各開源框架使用與設計總結(一)
- 數據庫的本質、概念及其應用實踐(二)
- PHP導出MySQL數據到Excel文件(fputcsv)
- PHP中14種排序算法評測
- 深入理解PHP原理之--echo的實現
- PHP性能分析相關的函數
- PHP 性能分析10則
- 10 位頂級 PHP 大師的開發原則
- 30條爆笑的程序員梗 PHP是最好的語言
- PHP底層的運行機制與原理
- PHP 性能分析與實驗——性能的宏觀分析
- PHP7 性能翻倍關鍵大揭露
- 鳥哥:寫在PHP7發布之際一些話
- PHP與MySQL通訊那點事
- Php session內部執行流程的再次剖析
- 關于 PHP 中的 Class 的幾點個人看法
- PHP Socket 編程過程詳解
- PHP過往及現在及變革
- PHP吉祥物大象的由來
- PHP生成靜態頁面的方法
- 吊炸天的 PHP 7 ,你值得擁有!
- PHP開發中文件操作疑難問答
- MongoDB PHP Driver的連接處理解析
- PHP 雜談《重構-改善既有代碼的設計》之二 對象
- 在php中判斷一個請求是ajax請求還是普通請求的方法
- 使用HAProxy、PHP、Redis和MySQL支撐10億請求每周架構細節
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重構-改善既有代碼的設計
- PHP場景中getshell防御思路分享
- 移動互聯時代,你看看除了PHP你還會些什么
- 安卓系統上搭建本地php服務器環境
- PHP中常見的緩存技術!
- PHP里10個鮮為人知但卻非常有用的函數
- 成為一名PHP專家其實并不難
- PHP 命令行?是的,您可以!
- PHP開發提高效率技巧
- PHP八大安全函數解析
- PHP實現四種基本排序算法
- PHP開發中的中文編碼問題
- php.get.post
- php發送get、post請求的6種方法簡明總結
- 中高級PHP開發者應該掌握哪些技術?
- 前端開發
- web前端知識體系大全
- 前端工程與性能優化(下)
- 前端工程與性能優化(上)
- 2016 年技術發展方向
- Web應用檢查清單
- 如何成為一名優秀的web前端工程師
- 前端組件化開發實踐
- 移動端H5頁面高清多屏適配方案
- 2015前端框架何去何從
- 從前端看“百度遷徙”的技術實現(一)
- 從前端看“百度遷徙”的技術實現(二)
- 前端路上的旅行
- 大公司里怎樣開發和部署前端代碼?
- 5個經典的前端面試問題
- 前端工程師新手必讀
- 手機淘寶前端的圖片相關工作流程梳理
- 一個自動化的前端項目實現(附源碼)
- 前端代碼異常日志收集與監控
- 15年雙11手淘前端技術總結 - H5性能最佳實踐
- 深入理解javascript原型和閉包系列
- 一切都是對象
- 函數和對象的關系
- prototype原型
- 隱式原型
- instanceof
- 繼承
- 原型的靈活性
- 簡述【執行上下文】上
- 簡述【執行上下文】下
- this
- 執行上下文棧
- 簡介【作用域】
- 【作用域】和【上下文環境】
- 從【自由變量】到【作用域鏈】
- 閉包
- 完結
- 補充:上下文環境和作用域的關系
- Linux私房菜