Qing面向工程的移動(dòng) Web 前端模版
Qing 是一套基礎(chǔ)開發(fā)模版,來源于我們?cè)谑謾C(jī)與 PC 端上的大量工程實(shí)踐。Qing 所提供不是冷冰冰的文件, 而是一套Web前端解決方案,所以Qing不只是關(guān)注項(xiàng)目的初始狀態(tài),而是整體的工作流程, 這是Qing與現(xiàn)有開源的開發(fā)模版顯著差異的一點(diǎn)。Qing的體驗(yàn)必須是高效且愉悅的,拒絕繁瑣與重復(fù)。 其足夠的Qing量,只需30分鐘內(nèi)即可掌握最先進(jìn)的Web開發(fā)技能。
以下是Qing所基于的開發(fā)理念:
-
移動(dòng)端優(yōu)先,兼容PC端
-
向前看齊,基于ES5開發(fā)
-
模塊化Web開發(fā)過程
-
自動(dòng)構(gòu)建與部署集成, 基于Mod.js工具
基于未來趨勢(shì)的開發(fā)理念,Qing旨在提供工程化方案。
平臺(tái)與瀏覽器版本兼容:
-
iOS 4.0+
-
Android 2.2+
-
IE 6+
-
Chrome
-
Firefox
-
Safari
開始使用
可以通過以下任意一種方式開始使用Qing模版:
-
下載最新Qing模版包, 解壓至目標(biāo)目錄
-
如果已安裝git,可使用git clone源碼至目標(biāo)目錄:
$ git clone https://github.com/AlloyTeam/Qing.git
-
如果已安裝了Mod.js, 推薦在目標(biāo)目錄執(zhí)行:
$ m download AlloyTeam/Qing
第一次使用m download命令,需要先安裝mod-tar插件:
$ npm install mod-tar -g
-
如果您是一位女開發(fā),請(qǐng)忽略下文直接聯(lián)系筆者,深圳優(yōu)先。
模版結(jié)構(gòu)
團(tuán)隊(duì)的協(xié)作離不開一些基本的約定,Qing約定以下文件目錄結(jié)構(gòu):
. ├── css │ ├── main.css │ └── normalize.css ├── img ├── js │ ├── main.js │ └── vendor ├── tpl ├── .editorconfig ├── index.html └── Modfile.js
-
目錄
css托管樣式文件 -
目錄
img托管圖片文件 -
目錄
js托管JavaScript文件 -
目錄
tpl托管模版文件 -
.editorconfig約定團(tuán)隊(duì)基礎(chǔ)代碼風(fēng)格 -
index.html是入口HTML文件 -
Modfile.js是Mod.js配置文件
模塊化編程指引
Qing推薦模塊化的開發(fā)過程,模塊化開發(fā)后無論在代碼可維護(hù)性與復(fù)用,還是團(tuán)隊(duì)協(xié)作上都將變的更加直觀、輕松與高效。
CSS模塊化
通過原生CSS內(nèi)置的@import機(jī)制管理CSS模塊,在構(gòu)建過程中會(huì)自動(dòng)合并壓縮(在下文的優(yōu)化章節(jié)也有說明):
@import "normalize.css";@import "widget1.css";@import "widget2.css";@import "widget3.css";
JS模塊化
約定引入AMD規(guī)范來管理JS模塊,關(guān)于第一次接觸AMD的讀者,筆者推薦可以先Google了解后再進(jìn)行下一步:
// main.jsdefine(["./app"], function(app){
app.init()})
// app.jsdefine(function(){
return {
init: function(){}
}})
HTML模塊化
HTML模塊指代HTML模版文件,通過requirejs-tmpl插件將HTML分模塊管理,requirejs-tmpl沒有默認(rèn)打包在Qing模版中,可手動(dòng)下載requirejs-tmpl插件至js目錄,或通過執(zhí)行m download:tmpl命令自動(dòng)安裝插件:
<!-- tpl/headerTpl.html --><header><%= title %></header> <!-- HTMl模版可依賴其他HTML模塊 --><%@ ./navTpl.html %>
<!-- tpl/navTpl.html --><a href="<%= url %>">View On Github</a>
<!-- tpl/footerTpl.html --><footer><%= copyright %></header>
在HTML模版的引入是基于requirejs的插件機(jī)制,所以在具體路徑前需加上tmpl!前綴,表示其是HTML模版,例如:tmpl!../tpl/headerTpl.html。 引用的模版已通過插件自動(dòng)編譯,得到的函數(shù)如headerTpl直接傳入需要綁定的數(shù)據(jù)即可:
// js/app.jsdefine(["tmpl!../tpl/headerTpl.html", "tmpl!../tpl/footerTpl.html"], function(headerTpl, footerTpl){
var html1 = headerTpl({title: "Hello Qing", url: "http://github.com/AlloyTeam/Qing"})
var html2 = footerTpl({copyright: "AlloyTeam"})
// balabala})
自動(dòng)化工具的環(huán)境安裝
Mod.js是基于Node.js的工作流工具,安裝Node.js環(huán)境后使用NPM安裝Mod.js:
$ npm install modjs -g
一鍵構(gòu)建
成功安裝Mod.js后, 進(jìn)入Modfile所在的項(xiàng)目根目錄,只需執(zhí)行m命令,一切如此簡單,如假包換的一鍵構(gòu)建:
$ m
執(zhí)行完成后會(huì)在當(dāng)前目錄下生成dist目錄輸出構(gòu)建后的結(jié)果。
性能優(yōu)化
瀏覽器第一次請(qǐng)求服務(wù)器的過程至少需經(jīng)過3RTTs:DNS域名解析1RTT;TCP連接建立1RTT;HTTP請(qǐng)求并且返回第一個(gè)比特的數(shù)據(jù)1RTT。 而這在移動(dòng)基站網(wǎng)絡(luò)下請(qǐng)求則顯得異常緩慢,在我們的監(jiān)測中,在2G網(wǎng)絡(luò)下僅DNS時(shí)間即可達(dá)到200ms,性能不容樂觀。 所以盡可能快的完成頁面加載在移動(dòng)端顯得更加重要,而如何合理的減少頁面初始資源請(qǐng)求數(shù)是加快頁面加載最有效的方式:
合并JS模塊
Qing支持傳統(tǒng)的手動(dòng)模塊加載管理與基于AMD的模塊加載管理方式,同時(shí)我們推薦使用Require.js作為開發(fā)過程中的模塊加載工具。
<!-- JS模塊模塊手動(dòng)管理 --> <script src="js/fastclick.js"></script> <script src="js/spin.js"></script> <script src="js/main.js"></script>
傳統(tǒng)的手動(dòng)添加模塊會(huì)自動(dòng)合并,其按照合并連續(xù)引入資源的規(guī)則進(jìn)行,最終輸出:
<script src="js/89ef9b6e.fastclick_main_3_520.js"></script>
<!-- data-main屬性值為執(zhí)行入口JS文件地址 --> <script data-main="js/main" src="http://requirejs.org/docs/release/2.1.6/minified/require.js"></script>
通過模塊加載器方式,Qing會(huì)自動(dòng)移除模塊加載器本身,其并不打包進(jìn)最終輸出的文件:
<script src="js/89ef9b6e.main.js"></script>
Qing默認(rèn)開啟的是移除define生成模塊管理器無依賴代碼的stripDefine優(yōu)化模式。stripDefine優(yōu)化模式的配置在Modfile.js的build任務(wù)中:
build: {
src: "./index.html",
stripDefine: true}
在stripDefine優(yōu)化模式下,基于AMD規(guī)范文件:
// base/clone.jsdefine(function(){
return function(obj){
return Object.create(obj)
}})
// foo.jsdefine(['./base/clone'], function(clone){
return clone({foo:1})})
// bar.jsdefine(['./base/clone'], function(clone){
return clone({bar:2})})
// main.jsdefine('./foo', './bar'], function(foo, bar){
foo.bar = 2;
bar.foo = 1;})
編譯后會(huì)在移除define的同時(shí)將模塊代碼轉(zhuǎn)換為變量聲明格式的代碼:
(function(window, undefined){
var base_clone = (function(){
return function(obj){
return Object.create(obj)
}
})();
var foo = (function(clone){
return clone({bar:2})
})(base_clone);
var foo = (function(clone){
return clone({foo:1})
})(base_clone);
var main = (function(foo, bar){
foo.bar = 2;
bar.foo = 1;
})(foo, bar);})(this)
合并CSS @imports
在頁面中引入了樣式文件css/main.css:
<link rel="stylesheet" href="css/main.css">
而 css/main.css中使用了CSS@import機(jī)制來引入其他模塊的樣式文件:
@import "foo.css";@import "bar.css";@import "baz.css";
使用CSS原生@import機(jī)制模塊化開發(fā)CSS是Qing推薦的方式,然不做優(yōu)化直接發(fā)布到線上必然有性能問題,這是絕不允許的。
Qing在構(gòu)建的時(shí)候會(huì)自動(dòng)偵測所有引入的樣式文件是否使用了@import,并進(jìn)行合并優(yōu)化。
合并連續(xù)引入資源
當(dāng)頁面中引入了多個(gè)樣式文件或腳本文件:
<link rel="stylesheet" href="css/base.css"> <link rel="stylesheet" href="css/typo.css"> <link rel="stylesheet" href="css/main.css"> <script src="js/fastclick.js"></script> <script src="js/spin.js"></script> <script src="js/main.js"></script>
構(gòu)建程序會(huì)將多個(gè)連續(xù)的靜態(tài)資源文件進(jìn)行合并:
<link rel="stylesheet" href="css/89ef9b6e.base_main_3_630.css"> <script src="js/89ef9b6e.fastclick_main_3_520.js"></script>
data-rev配置
Qing會(huì)自動(dòng)給所有優(yōu)化后的靜態(tài)資源加上類似 89ef9b6e. 的指紋標(biāo)示前綴來區(qū)分版本,此行為是默認(rèn)打開, 可以通過data-no-rev聲明來關(guān)閉,也可以data-rev聲明開啟。
<html data-no-rev><link rel="stylesheet" href="css/main.css"> <script data-rev src="js/main.js"></script><img src="img/foo.png">
如上通過在HTML標(biāo)簽中<html data-no-rev>設(shè)置全局的策略,同時(shí)可在具體的標(biāo)簽上覆蓋全局設(shè)置,如上構(gòu)建后的結(jié)果:
<html><link rel="stylesheet" href="css/main.css"><script src="js/89ef9b6e.main.js"></script> <img src="img/foo.png">
data-stand-alone配置
有時(shí)要求某個(gè)基礎(chǔ)庫文件如jQuery能被不同頁面復(fù)用引入,而不是分別被打包在頁面級(jí)別的資源包內(nèi), 如此利用瀏覽器天然的緩存機(jī)制使無需重新請(qǐng)求相同的資源內(nèi)容, Qing在默認(rèn)構(gòu)建約定的基礎(chǔ)上同時(shí)提供了基于DOM的data-stand-alone配置。
<script data-stand-alone src="vendor/jquery-2.0.3.js"></script> <script src="js/foo.js"></script><script src="js/bar.js"></script> <script src="js/baz.js"></script>
構(gòu)建結(jié)果:
<script data-stand-alone src="vendor/92cf6237.jquery-2.0.3.js"></script> <script src="js/92cf6237.foo_baz_3_168.js"></script>
data-group配置
如何重復(fù)利用瀏覽器的并發(fā)請(qǐng)求數(shù)但同時(shí)考慮不至于有過多請(qǐng)求數(shù)上的負(fù)載,在不同場景下優(yōu)化策略會(huì)有不同: 當(dāng)需兼容IE老版本的情況下,初始并發(fā)請(qǐng)求數(shù)不推薦超過2個(gè),但同時(shí)我們推薦單個(gè)資源包的大小Gzip前不超過200k, 所以通常如何來控制打包粒度是需要監(jiān)控?cái)?shù)據(jù)來支撐的。Qing在構(gòu)建中提供了data-group分組參數(shù)來輔助打包粒度的控制:
<script data-group=1 src="js/foo.js"></script> <script data-group=1 src="js/bar.js"></script> <script data-group=1 src="js/baz.js"></script> <script data-group=2 src="js/qux.js"></script> <script data-group=2 src="js/quux.js"></script> <script data-group=2 src="js/corge.js"></script>
構(gòu)建結(jié)果:
<script data-group=1 src="js/92cf6237.foo_baz_3_168.js"></script> <script data-group=2 src="js/090430cf.qux_corge_3_171.js"></script>
data-url-prepend配置
資源CDN化是基本的優(yōu)化策略,
<html data-url-prepend="http://cdn1.qq.com/"> <script data-group=1 src="js/foo.js"></script> <script data-group=1 src="js/bar.js"></script> <script data-group=1 src="js/baz.js"></script> <script data-group=2 data-url-prepend="http://cdn2.qq.com/" src="js/qux.js"></script> <script data-group=2 src="js/quux.js"></script> <script data-group=2 src="js/corge.js"></script>
構(gòu)建結(jié)果:
<html> <script data-group=1 src="http://cdn1.qq.com/js/92cf6237.foo_baz_3_168.js"></script> <script data-group=2 src="http://cdn2.qq.com/js/090430cf.qux_corge_3_171.js"></script>
內(nèi)嵌靜態(tài)資源
所謂減少請(qǐng)求數(shù)最優(yōu)的目標(biāo)就是沒有請(qǐng)求,Qing提供了基于QueryString的embed配置使支持在構(gòu)建時(shí)將靜態(tài)資源內(nèi)嵌于HTML中, 如此便可優(yōu)化至最理想的情況:只需下載必不可少的HTML資源文件。
內(nèi)嵌樣式
<link rel="stylesheet" href="css/base.css?embed"> <link rel="stylesheet" href="css/typo.css"> <link rel="stylesheet" href="css/main.css">
構(gòu)建結(jié)果:
<style>css/base.css...css/typo.css...css/main.css...</style>
內(nèi)嵌腳本
<script src="js/fastclick.js?embed"></script> <script src="js/spin.js"></script> <script src="js/main.js"></script>
構(gòu)建結(jié)果:
<script>js/fastclick.js...js/spin.js...js/main.js...</script>
內(nèi)嵌圖片
內(nèi)嵌CSS里
#foo {
background: url('../img/icon.png?embed') no-repeat;
height: 24px;
width: 24px}
構(gòu)建結(jié)果:
#foo {
background: url(data:image/png;base64,iVBORw0...) no-repeat;
height: 24px;
width: 24px}
內(nèi)嵌HTML里
<img src="./img/icon.png?embed">
構(gòu)建結(jié)果:
<img src="data:image/png;base64,iVBORw0...">
基礎(chǔ)庫
Qing總是想法設(shè)法的讓開發(fā)過程更自動(dòng)更流暢,在Qing模版的Modfile.js中提供了以下第三方庫的下載配置:
-
FastClick
-
Spin.js
-
Zepto
-
jQuery 1.x
-
jQuery 2.x
-
require.js 2.1.9
-
requirejs-tmpl
截取Modfile.js中關(guān)于第三方庫的配置,src表示源地址,dest表示下載目錄, 除了tmpl插件下載至js/目錄其他所有第三方庫都默認(rèn)下載至js/vendor/目錄:
{
options: {
dest: "js/vendor/"
},
fastclick: {
src: "https://raw.github.com/ftlabs/fastclick/master/lib/fastclick.js"
},
spin: {
src: "https://raw.github.com/fgnass/spin.js/gh-pages/dist/spin.js"
},
zepto: {
src: "http://zeptojs.com/zepto.js"
},
jquery1: {
src: "http://code.jquery.com/jquery-1.10.2.js"
},
jquery2: {
src: "http://code.jquery.com/jquery-2.0.3.js"
},
requirejs: {
src: "http://requirejs.org/docs/release/2.1.9/comments/require.js"
},
tmpl: {
dest: 'js/',
src: "https://raw.github.com/modulejs/requirejs-tmpl/master/tmpl.js"
}}
下載全部庫至本地方式非常簡單,只需在根目錄下執(zhí)行:
$ m vendor
如只需下載Zepto:
$ m download:zepto
