性為 HTML 元素提供附加信息。
HTML 屬性
HTML 標簽可以擁有屬性。屬性提供了有關 HTML 元素的更多的信息。
屬性總是以名稱/值對的形式出現,比如:name="value"。
屬性總是在 HTML 元素的開始標簽中規定。
屬性實例
HTML 鏈接由 <a> 標簽定義。鏈接的地址在 href 屬性中指定:
<a >This is a link</a>
更多 HTML 屬性實例
屬性例子 1:
<h1> 定義標題的開始。
<h1 align="center"> 擁有關于對齊方式的附加信息。
屬性例子 2:
<body> 定義 HTML 文檔的主體。
<body bgcolor="yellow"> 擁有關于背景顏色的附加信息。
屬性例子 3:
<table> 定義 HTML 表格。(您將在稍后的章節學習到更多有關 HTML 表格的內容)
<table border="1"> 擁有關于表格邊框的附加信息。
HTML 提示:使用小寫屬性
屬性和屬性值對大小寫不敏感。
不過,萬維網聯盟在其 HTML 4 推薦標準中推薦小寫的屬性/屬性值。
而新版本的 (X)HTML 要求使用小寫屬性。
始終為屬性值加引號
屬性值應該始終被包括在引號內。雙引號是最常用的,不過使用單引號也沒有問題。
在某些個別的情況下,比如屬性值本身就含有雙引號,那么您必須使用單引號,例如:
name='Bill "HelloWorld" Gates'
HTML 屬性參考手冊
我們的完整的 HTML 參考手冊提供了每個 HTML 元素可使用的合法屬性的完整列表:
下面列出了適用于大多數 HTML 元素的屬性:
種包管理器到ESLint,從CommonJS到AMD,再從ES6模塊到Babel和Webpack,好多工具啊!
好累……
是的,今天我覺得很疲勞。我不禁想,我本應該繼續我的銷售職業,不應該抄近路做前端開發。但我意識到,前端開發是給勇敢者準備的,而勇敢者絕不會放棄,他們才是人生贏家。
所以我決定做人生贏家,我要寫點東西給前端開發和工具鏈疲勞的受害者們看。我要寫一下我是怎樣將初學者級別的代碼變成令人贊嘆的產品級代碼的,以及這個過程中我用到的工具。
現在開始吧!
我們在做的東西
其實沒什么令人激動的東西。我們做了個Web應用,從某個API讀取一些隨機的用戶,然后顯示在前端上。它沒有路由的能力。本文的最終目標是讓你熟悉前端的工具鏈。
我們的AngularJS代碼中沒有使用樣板代碼,所以我們不會被CLI的那些黑科技搞得暈頭轉向。注意我用的是AngularJS,不是Angular。使用AngularJS的原因是我找不到任何關于AngularJS工具鏈和打包的文章。
首先在根目錄下建一個index文件。
<html>
<head>
<title>Random User!</title>
<link rel="stylesheet" >
</head>
<body>
<div class="container">
<h1 class="text-center">Random User!</h1>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.0/angular.min.js"></script>
</body>
</html>
中規中矩的老式代碼。這段代碼從CDN上加載AngularJS文件和一個最小化的CSS框架,然后開始一點點編寫JavaScript代碼并添加到index中。
但隨著應用程序的增長,我們有必要跟蹤所有的依賴(這里的依賴就是Angular)。
使用包管理器
許多人使用包管理器跟蹤項目所需的依賴的版本。包管理器的最大賣點就是,它會訪問依賴的GitHub主頁,下載到你的文件夾里,并且跟蹤下載的版本。這樣可以保證,移動代碼位置或者下載其他版本的依賴不會造成代碼無法工作。
代碼管理器有過duojs、jspm、bower、npm,現在還有Yarn。現在去裝一個yarn,我們稍后會用到。
向應用程序里添加依賴時,yarn會下載所需的文件,并保存到node_modules文件夾中。之后,需要用到依賴時,可以在index文件里引入:
裝好這個后,我們再往根目錄下添加app.js、userController.js和userFactory.js文件,然后全都鏈接到index文件里。
/**
* /app.js
*/
var app = angular.module("RandomApp", []);
// /userFactory.js
app.factory("UserF", function($http) {
var UserF = {};
UserF.getUsers = function(){
return $http({
method: 'GET',
url: 'https://www.reqres.in/api/users',
})
};
return UserF;
});
// /userController.js
app.controller("userController", function($scope, UserF){
$scope.users = [];
UserF.getUsers()
.then(function(res) {
$scope.users = res.data.data;
})
});
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Random User!</title>
<link rel="stylesheet" >
</head>
<body>
<div class="container" ng-app="RandomApp">
<h1 class="text-center">Random User!</h1>
<div ng-controller="userController">
<div ng-repeat="user in users">
<div class="card">
<div class="card-image">
<img ng-src="{{user.avatar}}" class="img-responsive">
</div>
<div class="card-header">
<div class="card-title h5">{{user.first_name}} {{user.last_name}}</div>
</div>
</div>
</div>
</div>
</div>
<script src="node_modules/angular/angular.min.js"></script>
<script src="app.js"></script>
<script src="userController.js"></script>
<script src="userFactory.js"></script>
</body>
</html>
index越來越大了。他遇到了他自己的問題。他的手心開始出汗,膝蓋發軟,胳膊也越來越沉重……
這種方式的問題
所有的script標簽必須按照固定的順序。app.js生成app,然后附加到全局的window對象上。這個app變量會被其他腳本文件用到。這種情況通常被稱為“全局命名空間污染”。
如果你還在用這種方式,就趁早改了吧。它的問題是,不管我們什么時候打開哪個文件,都無法得知app變量的值究竟是什么。
這段代碼的另一個語義上的問題是,它使用了匿名函數。匿名函數是JavaScript的天使,也是魔鬼。
永遠不要忘記給匿名函數起名字。這樣以后調試代碼會變得容易很多。
那么,要是有個JS警察負責找出這些問題,豈不是很好?
ESLint
ESLint是個清理器。就像個嚴格的結對編程的伙伴一樣。清理器會在你運行應用程序之前就幫你調試代碼,并且強迫你和你的團隊遵循整潔代碼的實踐。誰說這樣的老師不存在的?
配置ESLint
我們使用Airbnb的樣式配置,在我們編寫代碼時進行檢查,并指出不當的地方。上面的命令會將配置安裝到node_modules目錄下,但我們得告訴ESLint怎么用這個配置。建立一個名為.eslintrc.json的文件,內容如下:
// .eslintrc.json
{
"extends": [
"airbnb/legacy"
],
"env": {
"browser": true
}
}
extends列表告訴ESLint在它自己的默認規則之上使用Airbnb的規則。env變量告訴ESLint不要抱怨沒有初始化的window變量等。要清理所有文件,可以使用通配符 * 。
現在運行下ESLint看看有什么結果。
這些都是Airbnb樣式指南定義的規則。我留給你自己去改正這些文件。一開始就有個清理器是最理想的。當然,你可以關閉某個特定的規則。
比如,如果你喜歡不用分號,或者使用雙引號而不是單引號,你可以關閉相應的規則。ESLint很靈活,允許你這么做。
模塊
現在來討論下模塊。在創建大規模應用時,我們要求代碼有良好的結構,以便于以后的擴展。
我們把代碼放到不同的模塊中,以實現代碼分割的目的。JavaScript直到ES6才支持模塊。但模塊化的概念早在ES6之前就出現了。
CommonJS
在ES6之前,人們使用CommonJS標準。你可以寫一段代碼,然后告訴環境導出這段代碼。之后就能使用像RequireJS之類的庫導入模塊了。
// util.js
module.export = {
noop: function(){},
validateUrl: function(s){
return s.matches(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/)
}
};
// postController.js
var validateUrl = require('./util').validateUrl;
var handleSubmit = function handleSubmit(e) {
if(!validateUrl(e.target.value)) {
return;
}
submitUrl(e.target.value);
}
如果你玩過Node,你會覺得這段代碼看起來很眼熟。不過這個標準有個缺陷——它是同步的。
也就是說,只有在validateUrl被require了之后,postController的第3行的handleSubmit才會被執行。在這之前代碼會暫停。
這種體系在Node.js中沒什么問題。Node可以在服務器啟動之前加載所有依賴,比如配置日志文件、連接云端的數據庫、配置秘鑰等。但在前端,這種方法并不是太理想。
異步模塊定義(Asynchronous Module Definition,AMD)
顧名思義,這種方式會異步加載模塊,比CommonJS的方式好一些。下面是使用AMD的代碼(我加了幾個函數)。看著眼熟嗎?
define(['validateSpam', 'blockUser', function(validateSpam, blockUser){
return {
noop: function(){},
validateUrl: function(s) {
return s.matches(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/)
},
validateSpammyComment: function validateSpammyComment(comment, userID) {
if(validateSpam(comment)) {
blockUser(userID);
return false;
}
return true;
}
}])
第1行看起來就像AngularJS中的依賴注入一樣。
ES6模塊
TC39委員會看到開發者們使用外部庫之后,他們深切感受到JavaScript需要支持模塊。因此他們在ES6中引入了模塊功能。現在使用ES6吧!
function noop(){};
function validateUrl(s) {
return s.matches(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/)
}
export {
noop,
validateUrl
}
import { validateUrl } from './util';
var handleSubmit = function handleSubmit(e) {
if(!validateUrl(e.target.value)) {
return;
}
submitUrl(e.target.value);
}
不需要再調用外部庫。import和export都是原生支持的。但依然有些版本的瀏覽器并不能完全支持ES6的所有功能。
但這種瀏覽器之間的不一致并不能阻止程序員編寫下一代的JavaScript。像babel等工具可以掃描所有JavaScript,并將它們轉譯成能兼容所有瀏覽器的代碼。因此,你的代碼甚至可以支持IE之類的老瀏覽器。
Babel和ES6
好了,現在我們把舊的JavaScript轉換成新的JavaScript。先做一點改動,以便添加模塊功能。現在我們先不管清理器……讓它去抱怨吧。
// /userFactory.js
let angular = window.angular;
let app = angular.module('RandomApp');
/**
* A User factory which gets the user list
* @param $http
*/
let userFactory = $http => {
let UserF = {};
UserF.getUsers = () => $http({
method: 'GET',
url: 'https://www.reqres.in/api/users'
});
return UserF;
};
app.factory('UserF', userFactory);
// /userController.js
let angular = window.angular;
let app = angular.module('RandomApp');
/**
* Controls the user
* @param $scope
* @param UserF
*/
let userController = ($scope, UserF) => {
$scope.users = [];
UserF.getUsers().then(res => $scope.users = res.data.data);
};
userController.$inject = ['$scope', 'UserFactory'];
app.controller('userController', userController);
這段代碼的問題
這段代碼無法工作。因為ES6的let關鍵字創建的變量是代碼塊上下文內的變量,而在同一個上下文內無法重復定義代碼塊級別的變量。
別忘了:我們還在全局上下文里呢。現在來改正這個問題。
我希望重構代碼的原因是,我想引入babel,這樣可以親眼看看babel的魔法。現在可以在終端里安裝babel:yarn add babel-cli babel-preset-env
這行命令會安裝babel-cli和babel-preset-env。
babel插件和預設
JavaScript代碼會通過一系列轉換器,而我們可以選擇需要什么轉換器。你可以讓它把箭頭函數轉換成匿名函數,轉換擴展運算符(spread),轉換for...of循環,等等。這些轉換器叫做插件。
你可以選擇任何你想要的插件。成組的插件叫做預設。babel-preset-env會給babel一個靈活的目標。
它并不是指定某個特定版本的JavaScript,而是告訴babel自動跟蹤最新的n個版本瀏覽器。
現在來設置babel配置文件:.babelrc,把它放到根目錄下。
{
"presets": [
["env", {
"targets": {
"browsers": "last 2 versions"
}
}]
]
}
現在從終端運行如下命令,babel就能正常工作。輸入以下命令:
node_modules/.bin/babel *.js
很方便對吧?babel會在轉換之前預覽以下。
現在喘口氣,考慮下我們學過的東西。我們將一個JavaScript文件分解成了許多個文件。我們添加了清理器,防止寫出不合規范的代碼。
我們使用未來的JavaScript語法,并將其轉換成特定版本的瀏覽器能理解的東西。我們污染了全局命名空間,但暫時還能接受,我們稍后會解決這個問題。
如果有個工具能自動完成這一切就好了。我們給它所有代碼,自動運行清理器找出所有錯誤,然后轉譯成瀏覽器兼容的代碼。沒錯,確實有這么個工具。現在把這些東西都自動化吧。
用Webpack進行構建
首先,把所有JS文件都移動到一個目錄下。我們使用標準的命名方式,將文件夾命名為build。同時,我們重構下JavaScript文件,這樣所有文件都能被構建到同一個文件下。
// /build/userController.js
/**
* Controls the user
* @param $scope
* @param UserF
*/
let userController = ($scope, UserF) => {
$scope.users = [];
UserF.getUsers().then(res => $scope.users = res.data.data);
};
userController.$inject = ['$scope', 'userFactory'];
export default userController;
// /build/userFactory.js
/**
* A User factory which gets the user list
* @param $http
*/
let userFactory = $http => {
let UserF = {};
UserF.getUsers = () => $http({
method: 'GET',
url: 'https://www.reqres.in/api/users'
});
return UserF;
};
userFactory.$inject = ['$http'];
export default userFactory;
/// /build/app.js
import angular from 'angular';
import userController from './userController';
import userFactory from './userFactory';
angular.module('RandomApp', [])
.factory('userFactory', userFactory)
.controller('userController', userController);
現在創建webpack.config.js文件:
var path = require('path');
module.exports = {
mode: 'development', // tells webpack that this is a development build. the 'production' switch will minify the code among other things
devtool: 'cheap-eval-source-map', // generate source maps for better debugging and dont take much time.
context: __dirname, // since this runs in a node environment, webpack will need the current directory name
entry: './build/app.js', // take this file and add to the bundled file whatever this file imports
output: {
path: path.join(__dirname, 'dist'), // output this in a dist folder
filename: 'bundle.js' // and name it bundle.js
},
// read medium post to know what's module and devServer because I dont have much room for comments
module: {
rules: [{
enforce: 'pre',
loader: 'eslint-loader',
test: /\.js$/
}, {
loader: 'babel-loader',
test: /\.js$/
}]
},
devServer: {
publicPath: '/dist/',
filename: 'bundle.js',
historyApiFallback: true,
overlay: true
}
};
如果現在運行webpack,就會看到所有文件都被打包成一個文件,放到dist目錄下:
webpack配置揭秘
祝賀你,給自己點獎勵吧。現在我們把所有文件都打包到一起,它幾乎可以用于生產環境了。
現在來討論下這個配置。我會逐一介紹每個配置項的意思。更詳細的資料可以參考手冊(https://webpack.js.org/)。
大多數配置項我都給出了注釋。這里說說沒加注釋的那些。
Webpack加載器(module對象)
這個可以想像成一條流水線上的一串代碼加載單元。最后一個加載器(這里是babel-loader)會最先被Webpack用來加載代碼。
我們要求webpack遍歷所有代碼,然后用babel-loader將代碼轉譯成ES5。
加載器對象還需要一個test設置項。它用這個設置項來匹配所有它應該負責的文件(本例中用一段正則表達式匹配了所有擴展名為.js的文件)。
轉譯之后,就執行下一個加載器(這里是eslint-loader)。最后,把所有改變從內存中寫到文件中,然后輸出到output對象指定的文件里。
但這并不是我們的配置文件的行為。我們在ESLint加載器上加了個enforce: pre,因為我們希望先運行清理器。
這是因為輸出的結果只有一個文件,而且如果使用最小化和混淆功能的話,這個文件通常會變成人類無法閱讀的格式(生產環境中經常會如此)。
清理器看這個文件就會瘋了。這不是我們想要的,所以我們希望webpack先運行清理器,再進行轉譯。
除此之外,你還可以使用好幾個加載器,可以加載樣式表文件、SVG圖像,以及字體。有個我總會用到的加載器就是html-loader。
HTML加載器
在Angular下,我們通常會在directive/component中包含模板文件,因此可以在Webpack中使用html-loader進行加載。
templateUrl: './users/partial/user.tpl.html' // 把這種寫法改成:templateUrl: require('./users/partial/user.tpl.html')
Webpack由一個超大規模的社區支持,他們寫了很多優秀的加載器,以及很完善的文檔。不管你有什么需求,很可能已經有現成的加載器了。
Webpack開發服務器(devServer)
Webpack開發服務器是個獨立于Webpack的模塊。它會啟動自己的服務器,然后監視任何文件的改動。如果文件發生變化,Webpack開發服務器就會重新打包并自動刷新頁面。
如果發生錯誤,它會在屏幕上顯示一個覆蓋層(通過overlay配置項設置),并直接在瀏覽器中顯示錯誤信息。而且它速度非常快,因為一切都在內存中完成,不會訪問硬盤。
當然,為了運行webpack開發服務器,你首先得有一個基礎的構建好的文件(即,至少要運行webpack一次以生成構建好的文件)。
一旦生成之后,就可以運行該命令。該命令會啟動服務器并提供靜態文件,打開瀏覽器(默認是8080端口),并持續監視任何變動。
搞定了!
不過這并不是結局。還有許多你能做的事情。在工作中,我們使用Flow在編碼時進行靜態類型檢查。
靜態類型檢查器可以檢查代碼,并在發生錯誤時發出警告,比如調用函數時提供了錯誤類型的參數等。Flow也可以集成到Webpack中。
我們還使用Prettier實現編碼時的自動格式化。它能讓代碼更可讀。
傻瓜都能寫計算機能看懂的代碼。
好的程序員寫人類能看懂的代碼。
—— Martin Fowler。
我要把這句話貼在我的桌子上。祝賀你!你成功了!
如果你讀完了這篇超長的文章,我要跟你擊掌向你道喜,你是人生贏家。JavaScript對我而言并不容易。
我很希望我在第一個項目中編寫UI時能懂得這些東西。但我估計這就是前端開發對我的意義。持續學習,持續進步。
原文:https://medium.freecodecamp.org/the-brain-fatigued-javascripters-guide-to-modern-frontend-tooling-in-2018-9818a04e9ec5
作者:Amin Mohamed Ajani,Javascript開發人員,曾為FireFox開發工具貢獻代碼。用戶界面工程師,擅長iQuanti,reactjs,nodejs和TheIsmaili。
譯者:彎月,責編:胡巍巍
“征稿啦”
CSDN 公眾號秉持著「與千萬技術人共成長」理念,不僅以「極客頭條」、「暢言」欄目在第一時間以技術人的獨特視角描述技術人關心的行業焦點事件,更有「技術頭條」專欄,深度解讀行業內的熱門技術與場景應用,讓所有的開發者緊跟技術潮流,保持警醒的技術嗅覺,對行業趨勢、技術有更為全面的認知。
如果你有優質的文章,或是行業熱點事件、技術趨勢的真知灼見,或是深度的應用實踐、場景方案等的新見解,歡迎聯系 CSDN 投稿,聯系方式:微信(guorui_1118,請備注投稿+姓名+公司職位),郵箱(guorui@csdn.net)。
、唯一定律
無論有多少人共同參與同一項目,一定要確保每一行代碼都像是唯一個人編寫的。
二、HTML
2.1 語法
(1)用兩個空格來代替制表符(tab) -- 這是唯一能保證在所有環境下獲得一致展現的方法。
(2)嵌套元素應當縮進一次(即兩個空格)。
(3)對于屬性的定義,確保全部使用雙引號,絕不要使用單引號。
(4)不要在自閉合(self-closing)元素的尾部添加斜線 -- HTML5 規范中明確說明這是可選的。
(5)不要省略可選的結束標簽(closing tag)(例如,</li> 或 </body>)。
2.2 Example
三、HTML5 doctype
為每個 HTML 頁面的第一行添加標準模式(standard mode)的聲明,這樣能夠確保在每個瀏覽器中擁有一致的展現。
四、語言屬性
根據 HTML5 規范:
強烈建議為 html 根元素指定 lang 屬性,從而為文檔設置正確的語言。這將有助于語音合成工具確定其所應該采用的發音,有助于翻譯工具確定其翻譯時所應遵守的規則等等。
五、IE 兼容模式
IE 支持通過特定的 <meta> 標簽來確定繪制當前頁面所應該采用的 IE 版本。除非有強烈的特殊需求,否則最好是設置為 edge mode,從而通知 IE 采用其所支持的最新的模式。
六、字符編碼
通過明確聲明字符編碼,能夠確保瀏覽器快速并容易的判斷頁面內容的渲染方式。這樣做的好處是,可以避免在 HTML 中使用字符實體標記(character entity),從而全部與文檔編碼一致(一般采用 UTF-8 編碼)。
七、引入 CSS 和 JavaScript 文件
根據 HTML5 規范,在引入 CSS 和 JavaScript 文件時一般不需要指定 type 屬性,因為 text/css 和 text/javascript 分別是它們的默認值。
八、實用為王
盡量遵循 HTML 標準和語義,但是不要以犧牲實用性為代價。任何時候都要盡量使用最少的標簽并保持最小的復雜度。
九、屬性順序
9.1 從大到小
HTML 屬性應當按照以下給出的順序依次排列,確保代碼的易讀性。
(1)class
(2)id, name
(3)data-*
(4)src, for, type, href, value
(5)title, alt
(6)role, aria-*
9.2 Example
9.3 說明
class 用于標識高度可復用組件,因此應該排在首位。id 用于標識具體組件,應當謹慎使用(例如,頁面內的書簽),因此排在第二位。
十、布爾(boolean)型屬性
10.1 注意
(1)布爾型屬性可以在聲明時不賦值。XHTML 規范要求為其賦值,但是 HTML5 規范不需要。
(2)元素的布爾型屬性如果有值,就是 true,如果沒有值,就是 false。
(3)如果屬性存在,其值必須是空字符串或 [...] 屬性的規范名稱,并且不要在首尾添加空白符。
簡單來說,就是不用賦值。
10.2 Example
十一、減少標簽的數量
編寫 HTML 代碼時,盡量避免多余的父元素。很多時候,這需要迭代和重構來實現。
十二、減少 JavaScript 生成的標簽
通過 JavaScript 生成的標簽讓內容變得不易查找、編輯,并且降低性能。能避免時盡量避免。
最后,小編還給大家準備了web前端的學習資料
獲取方式:請大家轉發+關注并私信小編關鍵詞:“資料”即可獲取前端自學教程一套。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。