前端開發規範
前端開發
1、HTML規範
1.1.使用正確的HTML5文檔類型
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="renderer" content="webkit"> <!-- 360使用極速模式渲染 -->
<title>Document</title>
</head>
<body>
</body>
</html>
1.2 兼容的meta屬性
<meta name="renderer" content="webkit"> <!-- 360使用極速模式渲染 -->
1.3 統一使用4空格的tab縮進
1.4.避免不規範的元素嵌套
錯誤示例:
<ul>
<div>導航欄標題</div>
<li>子項1</li>
<li>子項2</li>
<ul>
正確示例:
<div>導航欄標題</div>
<ul>
<li>子項1</li>
<li>子項2</li>
<ul>
ul
標籤只允許存在li
作爲其直接子標籤。錯誤示例中的代碼雖然可以正常渲染,但是由於不同瀏覽器容錯機制不一致,錯誤的示例代碼可能會導致額外的hack。
1.5.自定義屬性採用data-*
格式
錯誤示例:
<div ng-click="click()">
正確示例:
<div data-ng-click="click()">
data-*
是HTML5的標準規範,如不採用data-*
格式,IDE會顯示警告信息,避免警告信息影響正常的debug,建議採用data-*
格式自定義屬性。
1.6.布爾類型的屬性,建議不添加屬性值
錯誤示例:
<input type="checkbox" checked="true">
<input type="checkbox" checked="flase">
<input type="checkbox" checked="checked">
正確示例:
<input type="checkbox" checked>
獲取選擇選中狀態,使用dom節點對象的checked屬性,或者jQuery的is方法,不要使用attr('checked')進行判斷。
錯誤做法:
var $checkbox = $('input[checkbox]');
$checkbox.attr("checked"); // 不準確,甚至會取得非預期值
正確做法:
$checkbox.is(':checked');
$checkbox.check = true;
$checkbox.check;
2、CSS規範
2.1.確定全局唯一的元素,可以使用id,否則使用class
否則會導致後期維護成本過大。
2.2.選擇器應儘量避免嵌套,最多不過3級別
不好的做法:
body #container .head .title {
// code here
}
推薦做法:
.h-title {
// code here
}
嵌套過多,會導致css權重過大,子class跟父class緊耦合,後期維護成本大。
3、JavaScript規範
3.1.字符串統一使用單引號包含
不好的示例:
var msg = "Hello JavaScript!";
正確示例:
var msg = 'Hello JavaScript!';
使用單引號的最主要原因是,避免額外的單引號\雙引號轉義。
var dom = “<div data-ng-click=\"click();\">雙引號\"\"不建議</div>”;
vs
var dom = '<div data-ng-click="click();">雙引號""不建議</div>';
兩種寫法對比,優劣一目瞭然。
3.2.避免使用JavaScript拼接HTML
使用JavaScript拼接HTML,會有以下問題:
* 極容易產生XSS漏洞;
* 關注點不分離,升級維護極其困難;
* 開發效率低下,代碼不健壯,非常容易產生bug;
* Debug變的異常艱難;
不好的示例:
var names = ['tiger','apple', '<script>alert("xss");</script>'];
var dom = '<ul>';
names.forEach(function(name,index){
dom += '<li>' + name + '</li>';
})
dom += '</ul>';
$('body').html(dom);
上面的代碼會導致一下問題:
* names[2]插入頁面後,<script>alert("xss");<script>
會被當成腳本執行,容易被用戶利用,入侵系統;
* 代碼升級維護困難,如果要修改樣式,或者渲染的對象更加複雜(多層嵌套),維護這樣的代碼不僅效率低下,容易出錯,簡直就是噩夢;
* 產生bug的情況下,調試異常複雜;
正確示例:
AngularJS寫法:
<ul>
<li data-ng-repeat="name in names">{{ name }}</li>
</ul>
如果使用jQuery情況下,可以使用前端模板引擎,例如doT.js、jQuery Template等。
3.3 JavaScript語法技巧
使用這些小技巧,可以讓代碼更加簡潔、容易理解。
3.3.1 默認參數賦值
冗長寫法:
function fun(param) {
if(param == null) {
param = {name:'tiger'};
}
}
簡潔寫法:
function fun(param) {
param = param || {name:'tiger'};
}
3.3.2 判空執行
冗長寫法:
function fun() {
if(flag) {
callback();
}
}
簡潔寫法:
function fun() {
flag && callback();
}
3.3.3 自調用函數
冗長寫法:
function fun(){
// code here
}
fun();
簡潔寫法:
(function(){
// code here
})();
3.3.4 數組迭代
冗長寫法:for
簡潔寫法:filter、some、every、forEach、map
3.3.4.2 filter:對數組進行過濾,並返回新的數組
var s = [1,2,3,4,5];
var ns = s.filter(function(item,index) {
return item > 3;
})
console.log(ns); // [4,5]
3.3.4.3 some:判斷數組中是否有符合條件的元素
var s = [1,2,3,4,5];
var b = s.some(function(item,index) {
return item > 3;
})
console.log(b); // true
3.3.4.4 every:判斷數組中所有元素是否都符合條件
var s = [1,2,3,4,5];
var b = s.every(function(item,index) {
return item > 3;
})
console.log(b); // false
3.3.4.5 map:對數組中的元素進行處理,並返回新的數組
var s = [1,2,3,4,5];
var ns = s.map(function(item,index) {
return item * 2;
})
console.log(ns); // [2,4,6,8,10]
3.3.4.6 forEach:迭代數組元素
var s = [1,2,3,4,5];
var ns = s.map(function(item,index) {
console.log(item); // 依次打印1,2,3,4,5
})
3.4. 前端開發的JavaScript技巧
3.4.1 使用Angular時,ajax請求請使用ng自帶的$http,避免使用jQuery的ajax
$http的ajax回調會自動更新DOM節點,而jQuery的回調沒有這個特徵,需要手動apply。
<div>{{ user.username }}</div>
正確的jQuery寫法:
$.post('/login',{username:'tiger',passwd='123456'},function(rs){
$scope.user = rs.user;
$scope.$apply();
})
正確的AngularJS寫法:
var params = {username:'tiger',passwd='123456'}
$http({method : 'POST',url : '/login', data: $.param(params)
}).then(function(rs) {
$scope.user = rs.data.user;
});
如果是GET請求,data則改成params,同時不需要$.param序列化,params的參數放url上,而data是放request body中。
var params = {username:'tiger',passwd='123456'}
$http({method : 'GET',url : '/login', params: params
}).then(function(rs) {
$scope.user = rs.data.user;
});
3.4.2 HTTP請求中,獲取使用GET,提交、修改和刪除使用POST
這樣更加符合REST的語義,如果POST使用GET代替,在開發階段就難以調試,而且無法使用GET的特性進行一系列調優,例如緩存等。
HTTP中,GET/POST/DEL等的區別:http://www.cnblogs.com/zhangpengshou/archive/2012/07/09/2583096.html
3.4.3 獲取ajax的表單參數,jQuery可以將form表單序列化,Angualr可以綁定對象
推薦的jQuery寫法:
<form id="loginFrom">
<input name="username" type="text" />
<input name="password" type="passwrod"/>
</form>
<script>
var params = $('#loginForm').serialize();
$.post('/login',params,function(rs){
// callback
});
</script>
推薦的AngularJS寫法:
<form>
<input type="text" data-ng-model="loginForm.username" />
<input type="passwrod" data-ng-model="loginForm.password"/>
</form>
<script>
$http({
method : 'POST',
url : '/login',
data: $.param($scope.loginForm)
}).then(function(rs) {
// callback
});
</script>
3.4.4 jQuey調用AngularJS的方法
思路:取得AngularJS作用域下的節點找到scope,通過scope調用方法:
var container = $('#container'); // #containe要在data-ng-app的子節點下
angular.element(container).scope().fun(); // 調用AngularJS下面的fun方法;
強烈推薦使用Angular開發,jQuery的功能基本95%以上都可以用Angular代替,代碼量更少,更容易維護。
4、AngularJS與JAVA EnumMap實現前後端統一
public enum ChargeControlMode {
BMS("bms", Constant.CHARGE_CONTROL_MODE_BMS),
POWER("power", Constant.CHARGE_CONTROL_MODE_POWER),
MONEY("money", Constant.CHARGE_CONTROL_MODE_MONEY),
TIME("time", Constant.CHARGE_CONTROL_MODE_TIME);
...
public static Map<String, String> buildEnumElementMap() {
Map<String, String> chargeControlModeMap = new HashMap<String, String>();
chargeControlModeMap.put(ChargeControlMode.BMS.name(), Constant.CHARGE_CONTROL_MODE_BMS);
chargeControlModeMap.put(ChargeControlMode.POWER.name(), Constant.CHARGE_CONTROL_MODE_POWER);
chargeControlModeMap.put(ChargeControlMode.MONEY.name(), Constant.CHARGE_CONTROL_MODE_MONEY);
chargeControlModeMap.put(ChargeControlMode.TIME.name(), Constant.CHARGE_CONTROL_MODE_TIME);
return chargeControlModeMap;
}
}
@SuppressWarnings("rawtypes")
@RequestMapping(value = "/buildAllEnumsMap", method = RequestMethod.GET)
public ResponseEntity<Map<String, Map>> buildAllEnumsMap() {
Map<String, Map> map = buildEnumElementsMap();
if (map.isEmpty()) {
return new ResponseEntity<Map<String, Map>>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<Map<String, Map>>(map, HttpStatus.OK);
}
/**
* 構造充電樁參數相關的枚舉元素類型的Map<String, Map><br>
* key->枚舉類型的名稱,如BillingType<br>
* vale-> 是一個Map,包含枚舉類型的所有元素,key是枚舉元素名稱的變量,value是具體值<br>
* @return 返回充電樁參數相關的枚舉類型Map<String, Map>
*/
@SuppressWarnings("rawtypes")
private Map<String, Map> buildEnumElementsMap() {
Map<String, Map> map = new HashMap<String, Map>();
//計費方式
map.put(BillingType.class.getSimpleName(), BillingType.buildEnumElementMap());
//充電控制模式
map.put(ChargeControlMode.class.getSimpleName(), ChargeControlMode.buildEnumElementMap());
return map;
}
that.buildAllEnumsMap = function(){
$.eompTools.blockUI();
$.eompAjaxService.doGet('/em/rest/stakeParm/buildAllEnumsMap', {}, function(resdata) {
if (resdata) {
that.enumsMap = resdata;
}
$.eompTools.unblockUI();
});
};
<label class="col-md-4 control-lable" for="file"> 控制模式: </label>
<span class="col-md-6 control-lable" for="file">
{{ctrl.enumsMap.ChargeControlMode[ctrl.parameters.chargeConfig.chargeControlMode]}}
等價於
{{ctrl.enumsMap.ChargeControlMode['TIME']}}
</span>
==> 控制模式:時間控制