本文將會講述一個完整的跨端桌面應用 程式碼畫板 的構建,會涉及到整個軟體開發流程,從開始的設計、編碼、到最後產品成型、包裝等。
本文不僅僅是一篇技術方面的專業文章,更會有很多產品方面的設計思想和將技術轉換成生產力的思考,我將結合我自己的使用場景完全的講解整個開發流程,當然涉及到設計方面的不一定具有普遍實用性,多數情況下都是我自己的一些喜好,我只關心自己的需求。
同時本文只從整體上講思路,也會有個別的技術細節和常規套路,有興趣的也可以直接去 github 上看 原始碼,文章會比較長,如果你只想知道一些拿來即用的「乾貨」,或許這篇文章並不是一個好的選擇
一、定位需求
事情的起因是這樣的,因為我們內部會有一些培訓會議。會經常現場演示一些程式碼片段。比如說我們講到 React 的時候會現場寫一些元件,讓大家能直觀的感受到 React 的一些功能。
但是通常由於條件所所限,會議總會遇到一些意外。比如斷網、投影解析度低看不清文字等
起初我們用的是線上版的 codepen,但是感覺並不是那麼好用。比如不能方便的修改字型大小,必須要在連網的情況下才能使用。另外它的 UI 設計不是很緊湊,通常我們展示程式碼的時候都投影是寸土寸金的,應該有一個簡潔又不失功能的 UI 介面,能全屏展示…
於是我解決自己實現一個這樣的輪子,那麼大概的需求目標是有了:
- 離線可用
- 可以改變介面字型大小
- 更加簡潔的 UI
- …
二、整體設計
應用風格
程式碼畫板解決的是 臨時性 的一些 演示程式碼 的需求,所以它的本質屬性是一個拿來即用的工具,它不應該有更復雜的功能,比如使用者登入、程式碼片段的管理等。這些需求不是它要解決的。程式碼畫板會提供一個簡單的匯出成 HTML 檔案的功能,可以方便使用者儲存整個 HTML 檔案。
既然是用來演示程式碼的,那麼它的介面上應該只有兩個東西,一個是 程式碼,一個就是 預覽。像程式碼/控制檯切換的功能都做成 tab 的形式,正常情況不需要讓他們展示出來。像 codepen 那樣把所有的程式碼編輯器功能都展示出來我認為是不對的。
codepen 的介面給人感覺非常複雜,有很多功能點。當然我並不是在批評它,codepen 做為一個需要商業化運營的軟體,勢必會做的非常複雜,這樣才能滿足更多使用者的需求。然而程式設計師寫軟體則可以完全按照自己的想法來,哪怕這個應用只給自己一個人用呢。
桌面應用的設計
桌面應用的設計和 web 介面的設計還是有些細微區別的,同樣的基於 electron 的應用,有的應用會讓人感覺很「原生」,有的則一眼就能看出來是用 CSS 畫的。我在設計程式碼畫板的時候也儘量向原生靠近,避免產生落差感。比如禁用滑鼠手型圖示、在按鈕或者非可選元素上禁止使用者選擇:
cursor: default;
user-select: none
複製程式碼
因為實際上使用者在使用一款應用的時候感性的因素影響佔很大一部分,比如說有人不喜歡 electron 可能就是因為看到過 electron 裡面嵌一個完整的 web 頁面的操作,這就讓人很反感。但是這不是 electron 的問題,而是應用設計者的問題。
應用標識的設計
說實話應用 logo 設計我也是業餘水平,但是聊勝於無。既然水平不行,那就儘量設計的不難看就行了。可以參考一些好的設計。我用 sketch 畫出 logo 的外形,sketch 有很多 macOS 的模組可以從網上下載下來,直接基於模板修改就可以了。
程式碼畫板主要的介面是分割開的兩個皮膚,左邊是程式碼,右邊是預覽。所以我就大概畫了一個形狀
這個 logo 有個問題就是線條過多,小尺寸的時候看不清楚。這個問題我暫時先忽略了,畢竟我還不是專業的,後續有好的創意可以再改
預設設定
程式碼畫板也 不會有 設定介面,因為常用的設定都預定義好了,你不需要配置。頂多改變下程式碼字型的大小。使用編輯器的通用快捷鍵 command
++/-
就解決了,或者插入三方庫,直接使用編輯器的通用命令快捷鍵 command
+p
調出。我們的思路就是把複雜的東西幫使用者隱藏在後臺,觀眾只需要關注演員臺上的一分鐘,而不必瞭解其它細節。
快捷鍵/可用性
由於程式碼畫板的介面非常簡單,在一些細小的必要功能就得新增一些快捷鍵。比如:切換 HTML/CSS/JS/Console 程式碼編輯器,我在每個 tab 上加了數字標號,暗示它是有順序有快捷鍵的,而且這個切換方式和 Chrome tab 切換的邏輯一致,使用 command
+數字
就可以實現,萬一還是有人不會用的話,可以去看幫助文件。裡面有所有的快捷鍵。
介面中間的分割條可以自定義拖動,雙擊重置平分介面
剛開始的時候我把每個 tab 頁籤都分割成單獨的皮膚,因為我覺得這個能拖動自定義皮膚大小的互動實在是太爽了,忍不住想去拖動它。但是後來想想,其實並沒有必要,我們寫程式碼時應該更專注於程式碼本身,如果只有兩個皮膚,那麼這個介面無論是認知還是使用起來就沒有任何困難。
因為我們並不需要把一堆的功能的介面摔給使用者,讓他們自己去選擇。
三、技術調研
實現控制檯
通過使用流行的幾款線上程式碼執行工具,我發現他們有一個共同的問題:控制檯很難用。無法像 Chrome Console 那樣展示任意型別的 JS 值。比如我想 log 一段巢狀的 JS 物件:
console.log({ a: { b: 1, c: { d: [1, 2, 3] } }})
複製程式碼
大多數都展示成這樣的:
[object Object] {
a: [object Object] {
b: 1
}
}
複製程式碼
Chrome 是這樣的:
顯然 Chrome 控制檯中更直觀。所以我們需要在前面的基礎上加一個需求,即:實現一個基於 DOM 的日誌展示介面(無限級聯選擇)
日誌介面應該有下面這些功能:
- 展示任意 JS 型別的資料
- Primitive 型別的資料顯示不同的顏色(number - 藍色,string - 綠色)
- Object 型別預設摺疊起來,點選按鈕展示子級,屬性過多需要展示縮略資訊
- 陣列前應該有長度標記
- 能展示 JS 執行時的報錯 Error 資訊
整合現代化的前端框工作流
現代化的前端寫頁面肯定不是 HTML/CSS/JS 一把梭了,至少應該有 Sass/Babel 的支援吧。
Sass 巢狀能讓你少寫很多選擇器,當然 Less 也可以,但是在我們的這個應用裡面區別不大,一般來說臨時性的寫一些程式碼很少會用到它們的細節功能。有 變數 和 選擇器 巢狀就夠了
Babel 主要是解決了寫 React 的問題,不用再安裝一大堆的構建工具了,直接使用 UMD
的 React/ReactDOM
就可以了,而且 electron 內嵌的 chromium 也支援了 es6 的 class 寫法,實際上 Babel 主要的目的還是用來轉譯 JSX
注意這裡是有一個我認為是 剛性 的需求,比如臨時忽然有個想法,或者想驗證一段程式碼的話,正常情況是使用你的編輯器,新建 demo.html/demo.css/demo.js 等這些操作。但是這些動作太浪費時間了。有了程式碼畫板以後,直接打應用就可以開始 coding 了,真正能做到開箱即用。
提高程式的擴充套件性
我們在寫 demo 頁面時通常是要引用很多第三方類庫的,比如:Bootstrp/jQuery 等。我希望有一種方法可以方便的引用到這些庫,直接把庫檔案的 link/script 標籤插入到程式碼畫板的 HTML 中,但是前端框架真的是太多了,又不能一個個去扣來寫死到頁面,就算是寫死了隨著框架版本的升級,可能就無法滿足我們的需求。
以前寫頁面時經常會用到 bootcdn,無意中發現它提供了相關 API,可以直接拿來使用。接下來就得想辦法讓使用者通過介面選擇即可。
這個 API 有三層資料結構:庫 - 版本 - 資源連結
。這個功能要用介面來實現肯定會非常臃腫,介面上可能會放很多按鈕。這就違背了「更簡潔」的需求目標。
這時就得參考下我們經常使用的一些軟體是如何解決 簡潔性 和 功能性 需求之間的矛盾問題的,我比較喜歡 Sublime Text 的一些介面設計,Command Palette 是我經常使用的,所以我決定再模擬一個 Command Palette 來實現插入第三方庫的需求。而且重要的是這個 Command Palette 並不一定只用來實現這一個功能,或者後期會有一些別的功能需要新增,那這個 Command Palette 也是個很好的入口。
使用 electron 實現桌面應用
實現離線可用很多方法,比如使用 PWA 技術。但是 PWA 並不能給我帶來一種原生應用的那種可靠感,相反 electron 剛好可以解決我的顧慮。同時它可以把你的應用打包成各個平臺(macOS/Window/Linux)的原生應用。唯一的缺點就是安裝包確實很大,一般來講一個 electron 應用 安裝完 至少要 100 多兆,不過我覺得還能接受,畢竟硬碟儲存現在已經很廉價了。
有人可能對 electron 有抗拒,覺得 electron 應用太龐大、佔系統資源什麼的,不過我們做的這個應用並不需要常駐系統,臨時性的使用一下,用完就關閉,正常寫生產環境的程式碼肯定還是要換回 編輯器/IDE 的。同時因為 electron 降低了寫桌面應用的門檻,確實有很多人把一個完整的線上的網頁直接嵌進去,這也是有問題的。
electron 還有一個好處,因為它完全基於 HTML/CSS/JS 來實現 UI(可以使用 Chrome only 的一些新功能),那我們理論上可以在做桌面應用時順手把 web 應用也做了。這就可以同時支援各個系統下的原生應用,並且有 web 線上版本。如果你不願意使用原生應用,直接登入 web.code-sketch.com 使用線上版也沒是一種選擇。這樣就使得我們的應用具有真正的 跨端 能力。
由於我們團隊都使用了 macbook,所以我優先支援 macOS 的開發,另外 macOS Mojave 的系統級別的暗色主題我也比較喜歡,剛好實現支援 mojave 暗色主題這個需求也做上。
三、框架的選擇
大方向確定了,像框架選擇這個就簡單了,基於 electron 的應用,需要你區分開 render/main process 來選擇。
Render process
渲染程式 就是 electron 中介面的實現部分 ,一般來說就是一個 webview,選自己喜歡的框架即可。我使用 React 來實現介面。樣式方面就不再使用框架了,因為我們的介面原則上沒有複雜的元素,直接手寫 CSS,300 行內基本上就可以解決問題。可能有人會覺得這不可能,實際情況是當你寫樣式只跑在 Chrome 裡面的時候那感覺完全爽到飛起,CSS variable/flex/grid/calc/vh/rem 什麼的都可以拿來用,實現一個功能的成本就降低了很多。
我使用 Codemirror 來做為主介面的程式碼編輯器,Monaco 也是一個好選擇,但是它有點過於龐大了,而且如果想要自定義功能得自己寫很多實現
主介面上的分割元件,使用了 React-split。
Main process
主程式 就是 electron 應用程式的程式,主要的區別在於主程式中可以呼叫一些與原生作業系統互動的 API,比如對話方塊、系統風格主題等。並且有 node 的執行時,可以引用 NPM 包。當然渲染程式也可以有 node 支援,但是我建議渲染程式中就只放一些純前端的邏輯,這樣的話方便後期把應用分離成 web 版
因為我們要整合 Sass 編譯功能,如果你也經歷過 node-sass 的各種問題,那就應該果斷選擇 dart-sass — 使用 dart 實現,編譯成了原生的 JS,沒有依賴問題。dart-sass 我放在了 main process 中,因為我試過放在 render process 中會有各種報錯。如果 web 端要實現這個功能就需要其它的解決辦法了,比如做成一個 http 服務,讓 web 調 http 服務。
Babel 的話我是放在了 渲染程式 中以 script 標籤的方式呼叫,這樣即使在 web 端 Babel 編譯也是可用的。
總之如果你使用 electron 構建應用並且引入的第三方 NPM 包可以 支援 執行在客戶端(瀏覽器)上,那就儘量把包放在渲染程式裡面。
構建工具
我使用 Parcel 來構建 React 而不是 Create React App。後者用來寫個小應用還可以,稍微大一點的,需要定製化一些東西你就得 eject 出來一大堆 webpack 配置檔案,即便是我已經用 webpack 開發過幾個專案了,但是說實話我還是沒用會 webpack。寫 webpack 配置的時間足夠我自己寫 npm script 來滿足自己的需求了。
原生應用打
使用 electron-builder 來打包到平臺原生應用,並且如果你有 Apple 開發者賬號的話應用還可以提交到 AppStore 上去。
我目前的打包引數是這麼配置的:
{
"build": {
"productName": "Code Sketch",
"extends": null,
"directories": { "output": "release" },
"files": [
"icon.icns",
"main.js",
"src/*.js",
"所有需要的檔案",
"package.json",
"node_modules/@babel",
"node_modules/sass"
],
"mac": {
"icon": "icon.icns",
"category": "public.app-category.productivity",
"target": [ "dmg" ]
}
}
}
複製程式碼
在你的 package.json 中新增 build 欄位,productName, directories 這些按自己需要更改即可
四、分離開發環境
區分開開發環境
程式碼畫板專案開過過程中涉及兩個關鍵環境
- Parcel 構建環境(渲染程式):Parcel 可以為你提供一些現在 JS 的轉譯工作,因此你可以放心使用例如 ES6 的 JS 新特性
- Node.JS 執行環境(主程式+渲染程式):這個取決於你的 electron 版本中整合的是 node 版本,比如:Node 10 中就沒有 ES Module,這意味著你如果要在 electron 主程式 是無法識別
import
這樣的語句的,但是渲染程式由於你使用了 Parcel 編譯,則無需考慮
這裡溫馨提示下:想要做到 electron 中的 渲染程式與主程式之間共享 JS 程式碼是非常困難的。就算是有辦法也會特別的彆扭,我的建議是儘量分離這兩個程式中的程式碼,主程式主要做一些系統級別的 API 呼叫、事件分發等,業務邏輯儘量放在渲染程式中去做
如果非要共享,那建議單獨做成一個 NPM 包分別做為主程式執行時依賴,和渲染程式的 Parcel 編譯依賴,唯一的缺點就是實際上共享的程式碼會有兩份。
渲染程式中呼叫 node API 可能會和 Parcel 打包工具衝突,一般在呼叫比如檔案模組時,可以加上 window.require(‘fs’)
這樣就可以相容兩個環境:
get ipc() {
if (window.require) {
return window.require('electron').ipcRenderer
} else {
return { on() {}, send() {}, sendToHost() {} }
}
}
this.ipc.send('event', data)
複製程式碼
這樣的話你在瀏覽器端除錯也不會產生報錯。一般情況下,建議當你用渲染程式中的 JS 引用(require
)包的時候都加上 window.
字首就可以了。因為渲染程式中 window 是全域性變數,呼叫 require
和呼叫 window.require
是等價的
開發流程
通常在測試的時候應用會呼叫一些 electron 內建的系統級別 API,這部分呼叫通常需要啟動 electron,但是有時候只有渲染程式中 UI 介面上的改動,就不用再啟動 electron 了,直接在瀏覽器裡面測試即可。使用 Parcel 執行一個本地的服務,這樣就可以在瀏覽器裡面除錯頁面。整個開發過程需要兩個命令(NPM Script):
啟動 Parcel 編譯伺服器
"scripts": {
"start": "./node_modules/.bin/parcel index.html -p 2044"
}
複製程式碼
除錯 electron 原生功能,注意設定 ELECTRON_START_URL
"scripts": {
"dev": "ELECTRON_START_URL=http://localhost:2044 yarn electron",
}
複製程式碼
技術難點
整個應用只有兩個功能是需要我們自己寫程式碼實現的:日誌控制檯,Sublime 命令列。我們分別來分析下這兩個模組的難點。
日誌控制檯 的難點在於,我們需要列印任意型別的 JS 值。如果你對 JS 瞭解比較多的話自然會想到在 JS 中所有的東西都是 物件,即 Object,那麼實際上當你想列印一個變數的時候,其實你只要把整個 Object 遞迴的遍歷出來,然後做成一個無限級的下拉選單就可以了。看起來大概想下面這樣:
Sublime 命令列 實際上開發起來還是比較簡單的,使用 React 很簡單就實現了功能,比較麻煩的是呼叫 bootcdn 的介面,過程中我發現介面返回資料量還是挺大的,有必要做上一層 localStorage 快取,加快二次開啟速度。
然而在使用的過程中你會發現當我想插入一個前端庫需要很多操作,因為有 三級選擇:庫-版本-CDN 連結。雖然這個流程解決了 所有使用者 的使用問題,但是卻損害了 大部分 使用者的體驗。這個時候插入一個常用庫的成本就很高了,所以我們就要加上一些快捷入口,來實現一鍵插入流行框架。
我們寫程式碼的思路是滿足所有使用者的使用需求,但是一個好產品的思路是先滿足大多數使用者(80%)的常規需求,再讓其餘的使用者(20%)可以有選擇
還有一個問題比較典型就是 React 這類框架在渲染大列表並且進行過濾(關鍵字查詢)時效能的問題。注意這個效能問題 並不是 引入框架產生的,真正的原因是當你渲染的 HTML 節點數以千計的時候,批量操作 DOM 會使得 DOM Render 特別慢。
所以說當我們遇到效能問題的時候應該去查詢問題的根源,而不是停留在框架使用上,實際上在 DOM 操作這個層面來講 jQuery 提供了更多的效能優化,比如自身的快取系統,以致於當你在使用的時候很難發現有效能問題。但是在類 React 框架中它們框架本身的重點並不在於解決你應用的效能問題。
類似我們上面講到的,實際上 jQuery 幫助你遮蔽了很多舞臺背後的東西,以致於你可以不用操心技術細節,你甚至可以把 jQuery 當做一個 產品 來使用,而類 React 框架你卻要親力親為的用他來設計你的程式碼。
話題再轉回效能問題。這時候需要我們去實現一個類似於 react-window 的功能,讓列表元素根據滾動按需載入。這可能是一種通用的解決大列表載入的方案,但是我的解決方法更粗暴,因為我們的下拉過濾功能使用時使用者只關注 最佳的匹配項 即可,後面匹配程度不高的項可以直接限制數量裁剪就行了嘛。很少有使用者會一直滾動到下面去查詢某個選項,如果有,那就說明我們這個匹配做的有問題。
slice() {
const idx = (this.props.itemsPerPage || 50) * (this.state.activeFrame + 1)
return this.props.items.slice(0, idx)
}
複製程式碼
整個匹配篩選的狀態大概是這樣的:
this.state = {
// 當前第N步選擇
step: 0,
// 當前步驟資料
items: [],
// 是否顯示
active: false,
// 當前選中項
current: {},
// 過濾關鍵字
keyword: ''
}
複製程式碼
這個 items
是當前步驟的所有資料,實際上我們這個元件是支援無限級的擴充套件的,那麼我們通過元件的 props 傳入所有層級的資料,然後持久儲存在記憶體中。這個 所有層級的資料 是資料結構層面的,實際上它可能是通過非同步介面獲取的。
再來看看我們元件提供的所有 props
:
static defaultProps = {
step: 0,
active: false,
data: [[]], // 無限層級資料 [[], [], [], ...]
// 資料的主鍵,用於鉤子函式返回使用者選擇的結果集
pk: 'id',
autoFocus: true,
activeCls: 'active',
delay: 300,
defaultSelected: 0,
placeholder: '',
async: false,
alias: [],
done: () => {}
}
複製程式碼
這些資料都可以通過元件的 props 傳入,這就意味著我們的這個元件才是真正的元件,別人也可以使用這樣的功能,而他們並不用在意裡面的細節,使用者只需要做好類似呼叫自己介面的這種業務邏輯。
元件的呼叫大概是這樣的:
<CommandPalette step={0}
key="CommandPalette"
async={injectData}
done={this.done.bind(this)}
alias={alias}
aliasClick={this.aliasClick.bind(this)}
data={[ [], [], [] ]}
複製程式碼
async
這個 props 實際上是一個非同步呼叫的鉤子方法,它會回傳給你元件上當前操作的相關資料狀態,通過這些資料使用者就可以按自己的需求在不同的步驟上呼叫不同的方法
export const injectData = (step, item, results, cb) => {
const API = 'https://api.bootcdn.cn/libraries'
if (step === 0) {
fetchData(`${API}.min.json`)
.then(processLibraryData)
.then(cb)
} else if (step === 1) {
// ...
} else if (step === 2) {
// ...
}
}
複製程式碼
另外關於 React 這裡安利下自己翻譯過的一個教程:React 模式,裡面講到 18 種短小精悍的 React 模式案例,非常簡單易懂。
還有一個小竅門,我們在適配暗色主題時,傳統的方法是直接寫兩套主題 CSS 程式碼,實際上我們要使用 CSS Variable 的話完全沒必要生成兩套了,背景色,字型都做成 CSS 變數,切換的時候只需要動態往頁面插入更新過的 CSS 變數值即可
系統的一些引數想直接傳給渲染程式也是比較麻煩的,我的做法是直接從主程式中的 loadUrl 方法上以 queryString 的方式傳到渲染頁面的 URL 上
const query = {
theme: osTheme,
app_path: app.getAppPath(),
home_dir: app.getPath('home')
}
mainWindow.loadURL(process.env.ELECTRON_START_URL ? url.format({
slashes: true,
protocol: 'http:',
hostname: 'localhost',
port: 2044,
query
}) : url.format({
slashes: true,
protocol: 'file:',
pathname: path.resolve(app.getAppPath(), './dist/index.html'),
query
}))
複製程式碼
像程式執行時的一些引數(比如程式的根目錄)也可以這麼動態傳過去,而且還有一個好處就是你甚至可以在渲染程式中測試與這些引數相關的功能。
五、宣傳
demo 視訊錄製
我會把最終所有功能的使用方法錄製成一個視訊,萬一有人不不想下載你的軟體,只是要了解一下,這就是個很好的方法。我同時上傳到了 Youtube 和 bilibili 這兩個平臺,其它的都有廣告就沒必要了
使用 Quicktime Player 即可,錄製完使用 iMovie 轉碼成兩倍速率的 mp4。如果你有興趣還可以加上一段音樂什麼的,讓視訊看起來更靈動
域名申請
域名是一個能讓使用者記住你產品的方法,如果你做的是一個成型的產品,那就一定要申請個域名。
我總是有這樣的體驗,有的時候看到一個非常不錯的產品但由於當時沒需求就忽略了,想起來或者突然有需求的時候缺記不起來名字叫什麼了。
事實上程式碼畫板最開始我給他起的名字是 code playground,這個更直觀,但是名字太長,而且想用到的一些域名呀、Github 名、NPM 包都被註冊了。
想來想去就換成了 code sketch,這和符合我們的設計初衷,即:一邊是程式碼,一邊是效果/草圖
域名申請我一般會上 Godaddy,不用備案,.com 域名一年 ¥65.00,然後 DNS 伺服器轉到了 cloudflare,後續域名也會直接轉到 cloudflare。因為據說以後在 cloudflare 上續費域名最便宜
網站搭建
宣傳網站直接放在 github pages 上,做個自定義域即可,實在是太方便了。而且還有 SSL 支援,Github 真的是業界良心
web 版的程式碼畫板,由於我們把渲染程式中的程式碼分離開發,所以直接把 parcel 打包出來的靜態檔案也做成 github pages 就可以了,爽歪歪,網站就等於一分錢不花了。後續做一些 web 版的增強功能時,可以做成前後端分離的 http 服務,這就是後話了
加入 Google analytics 程式碼
GA 可以讓你瞭解網站的使用者分佈情況,清楚的知道網站訪問的波動。比如說你把自己的連結放到某個網站上分享了,GA 裡面就能看出來所有的推薦來源和波動,對於運營來說是非常有必要的
廣告語
這個我還真想了好長時間,基於我對於程式碼畫板的定義,我覺得它應該是一個我們有一個想法的時候需要快速去實現一個 demo 的地方,想來想去就定了一段看起來文鄒鄒的話,雖然聽名字根本不知道它是幹啥用的,但是沒關係,程式設計師寫東西就是要有個性,因為我的受眾只有自己。
First place where the code was written... 一個你最初寫程式碼的地方...
六、彙總使用到的庫與工具
麻雀雖小,五臟俱全。我們來看下程式碼畫板總共用到了多少東西:
- 框架/庫
- electronjs
- react
- babeljs
- NPM 模組
- codemirror 及其外掛
- react-split
- sublime-command-palette
- 打包/工具
- parceljs
- electron-builder
- bootcdn
- 設計與素材
- sketch
- Free AppIcon Generator
- Inconsolata 字型
- Gallary CSS 純 CSS 實現的焦點圖,用於宣傳頁展示
七、結語總結
實事上我自己的開發這個應用的時候並沒有嚴格按照這篇文章的順序執行,而是想到一些實現一些,可能一個功能實現了後來覺得不好又幹掉了,是不斷的取捨、提煉的結果。
開發中我也不斷的問自己這個功能是否有必要,如果可有可無那是不是可以去掉,這樣才能使得使用者更加關注於程式碼本身。
整個開發過程中自己實現的功能模組並不多,只有控制檯、命令列視窗是自己實現的,其它的功能基本上都是靠社群現有的工具庫來完成的,從這一點來說前端技術的生態還是挺好的。這使得當我從整體上構思一個產品時我不必在意那些細節,雖然過程中還是能感覺到前端工具/庫的割裂感,但是整體而言還是向好的,畢竟工具對於開發者只是一種選擇的。
八、引用