這個來自以前作的培訓,刪減了一些業務相關的,參考了不少資料( 參考資料列表),謝謝前輩們,麼麼噠 😘
任何五花八門的技術,最終仍是要在實踐中落地。現代的軟件開發,大部分講求的不是高難度高精尖,而是效率和質量。javascript
這裏主要來講說現代前端技術在項目中的實踐。css
開發規範是開發工程師之間交流的另外一種語言,它在必定程度上決定了代碼是否具備一致性和易維護性,統一的開發規範經常能夠下降代碼的出錯機率和團隊開發的協做成本。html
就拿命名規範來講,若是沒有規範,你會常常看到這樣的代碼:前端
var a1,a2,temp1,temp2,woshimt;複製代碼
開發規範制定的重要性不言而喻,使用怎樣的規範又成爲了另外一個問題,由於編程規範並不惟一。通俗地講,規範的差異不少時候只是代碼寫法的區別,不一樣的規範都有各自的特色,大部分沒有優劣之分。通常在選擇時不必糾結於使用哪種規範, 只要團隊成員都承認並達成一致就行。vue
實際上,咱們平時所說的開發規範更多時候指的是狹義上的編碼規範,廣義上的開發規範包括實際項目開發中可能涉及的全部規範,如項目技術選型規範、組件規範、接口規範、模塊化規範等。因爲每一個團隊使用的項目技術實現不同,規範也可能千差萬別,但不管是哪種規範, 在一個團隊中儘量保持統一。java
這裏是一個規範的例子:guide.aotu.io/docs/index.…node
若是使用框架,各個框架會有本身的最佳實踐,通常來講參考官方的最佳實踐,結合本身團隊的習慣便可。react
好比Vue:cn.vuejs.org/v2/style-gu…webpack
在現代軟件開發中,自動化構建已經成爲一個不可缺乏的部分。web
對於編譯型語言來講,通常都會經過命令行或者IDE先進行編譯,而後在不一樣平臺上安裝運行。而前端代碼不須要軟件編譯,Javascript算是解釋型語言,瀏覽器變解析邊執行,因此前端的自動化構建和傳統語言略有不一樣。
前端構建工具的做用主要是對項目源文件或資源進行文件級處理,將文件或資源處理成須要的最佳輸出結構和形式。
在處理過程當中,咱們能夠對文件進行模塊化引入、依賴分析、資源合併、壓縮優化、文件嵌入、路徑替換、生成資源包等多種操做,這樣就能完成不少本來須要手動完成的事情,極大地提升開發效率。
在沒有自動化構建工具以前,前端在上線前的處理通常是這樣的:
整個過程每一個步驟會用到相應的工具,好比:CSSLint、JSLint、Uglyfy、HTMLMin、CssMinify、imagemin等,繁瑣且浪費時間。
並且還有一些附加的構建要求,好比代碼一旦修改就要自動校驗,自動測試,刷新瀏覽器等,這種在幾年前基本上沒法實現。
漸漸地,出現了一些自動化構建的工具。
Grunt 是比較早期的工具,它經過安裝插件和配置任務,來執行自動化構建。好比:
module.exports = function(grunt) {
grunt.initConfig({
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
globals: {
jQuery: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['jshint']);
};複製代碼
這裏就是監控js文件的變化,一旦改版,就執行jshint,也就是語法校驗。
Grunt有很強的生態,可是它運用配置的思想來寫打包腳本,一切皆配置,因此會出現比較多的配置項,諸如option,src,dest等等。並且不一樣的插件可能會有本身擴展字段,致使認知成本的提升,運用的時候要搞懂各類插件的配置規則。
Grunt的速度也比較慢,他是一個任務一個任務依次執行,會有不少IO操做。如今基本上用的人比較少了。
Gulp 用代碼方式來寫打包腳本,而且代碼採用流式的寫法,只抽象出了gulp.src, gulp.pipe, gulp.dest, gulp.watch 接口,運用至關簡單,使用 Gulp 的代碼量能比 Grunt 少一半左右。
var gulp = require('gulp');
var pug = require('gulp-pug');
var less = require('gulp-less');
var minifyCSS = require('gulp-csso');
var concat = require('gulp-concat');
var sourcemaps = require('gulp-sourcemaps');
gulp.task('html', function(){
return gulp.src('client/templates/*.pug')
.pipe(pug())
.pipe(gulp.dest('build/html'))
});
gulp.task('css', function(){
return gulp.src('client/templates/*.less')
.pipe(less())
.pipe(minifyCSS())
.pipe(gulp.dest('build/css'))
});
gulp.task('js', function(){
return gulp.src('client/javascript/*.js')
.pipe(sourcemaps.init())
.pipe(concat('app.min.js'))
.pipe(sourcemaps.write())
.pipe(gulp.dest('build/js'))
});
gulp.task('default', [ 'html', 'css', 'js' ]);複製代碼
Gulp 基於並行執行任務的思想,經過一個pipe方法,以數據流的方式處理打包任務,中間文件只生成於內存,不會產生多餘的IO操做,因此 Gulp 比 Grunt 要快不少。
Grunt 和 Gulp 能夠算是第一代的自動化構建工具。如今前端主要使用的是 Webpack。
其實對比 Gulp 來講,Webpack 並非一個徹底的替代平,Gulp 是任務運行工具,它只是一個自動執行可重複活動的應用程序,它的用途更加的普遍,由於自動任務的範圍更廣。
相對Gulp來講, Webpack是一個靜態模塊打包器(static module bundler),主要目的是幫助程序模塊及其依賴構建靜態資源。可是由於前端自動化構建的主要任務其實就是靜態資源的構建,因此Webpack基本均可以完成。所以 Gulp 如今的使用比較少了。
其實 Webpack 之因此流行,是由於以前的工具對模塊化的支持不足,之前的工具大部分是以文件爲單位的,而現代JS開發,都是基於模塊的,模塊依賴的識別是須要語法語義分析的,像 Gulp 之類的工具,只是一個自動執行的工具,無法很好的識別全部的模塊依賴,因此繼續使用會限制書寫的方式和項目結構,配置起來也更加繁瑣。
Webpack 把全部的代碼或圖片都當作資源,它會從一個或多個入口文件開始找起,找到全部的資源依賴,而後作語法分析,去除掉不用的或重複的,最終按照配置要求生成處理過的文件。
一個典型的Webpack配置文件:
var webpack = require('webpack');
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin')
var CleanWebpackPlugin = require('clean-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const VENOR = [
"lodash",
"react",
"redux",
]
module.exports = {
entry: {
bundle: './src/index.js',
vendor: VENOR
},
// 若是想修改 webpack-dev-server 配置,在這個對象裏面修改
devServer: {
port: 8081
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].[chunkhash].js'
},
module: {
rules: [{
test: /\.js$/,
use: 'babel-loader'
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: [{
loader: 'url-loader',
options: {
limit: 10000,
name: 'images/[name].[hash:7].[ext]'
}
}]
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{
// 這邊其實還可使用 postcss 先處理下 CSS 代碼
loader: 'css-loader'
}]
})
},
]
},
plugins: [
// 抽取共同代碼
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor', 'manifest'],
minChunks: Infinity
}),
// 刪除不須要的hash文件
new CleanWebpackPlugin(['dist/*.js'], {
verbose: true,
dry: false
}),
new HtmlWebpackPlugin({
template: 'index.html'
}),
// 生成全局變量
new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify("process.env.NODE_ENV")
}),
// 分離 CSS 代碼
new ExtractTextPlugin("css/[name].[contenthash].css"),
// 壓縮提取出的 CSS,並解決ExtractTextPlugin分離出的 JS 重複問題
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true
}
}),
// 壓縮 JS 代碼
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
]
};複製代碼
打包後生成:
最近,React,Vue、Ember、Preact、D三、Three.js、Moment 等衆多知名項目都使用了 Rollup 這個構建工具。
Rollup 可使用 ES2015的語法來寫配置文件,而 Webpack 不行:
// rollup.config.js
import babel from 'rollup-plugin-babel';
export default {
input: './src/index.js',
output: {
file: './dist/bundle.rollup.js',
format: 'cjs'
},
plugins: [
babel({
presets: [
[
'es2015', {
modules: false
}
]
]
})
]
}複製代碼
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
'index.webpack': path.resolve('./src/index.js')
},
output: {
libraryTarget: "umd",
filename: "bundle.webpack.js",
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015']
}
}
]
}
};複製代碼
舉個簡單的例子,兩個文件:
//some-file.js
export default 10;
// index.js
import multiplier from './some-file.js';
export function someMaths() {
console.log(multiplier);
console.log(5 * multiplier);
console.log(10 * multiplier);
}複製代碼
經過 Rollup 和 Webpack 打包以後,分別長成下面這樣:
// bundle.rollup.js — ~245 bytes
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var multiplier = 10;
function someMaths() {
console.log(multiplier);
console.log(5 * multiplier);
console.log(10 * multiplier);
}
exports.someMaths = someMaths;複製代碼
// bundle.webpack.js — ~4108 bytes
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
.........複製代碼
能夠看到 Webpack 打包後的代碼基本上不具有可讀性,尺寸也有些大。
因此對於主要是給其餘人使用的純JS庫或框架來講,Rollup 比 Webpack 更適合。
前端性能優化是一個很寬泛的概念,不過最終目的都是提高用戶體驗,改善頁面性能。
性能優化是個頗有意思的事情,不少人經常不遺餘力進行前端頁面優化,但卻忽略了這樣作的效果和意義。
一般前端性能能夠認爲是用戶獲取所須要頁面數據或執行某個頁面動做的一個實時性指標,通常以用戶但願獲取數據的操做到用戶實際得到數據的時間間隔來衡量。例如用戶但願獲取數據的操做是打開某個頁面,那麼這個操做的前端性能就能夠用該用戶操做開始到屏幕展現頁面內容給用戶的這段時間間隔來評判。
用戶的等待延時能夠分紅兩部分:可控等待延時和不可控等待延時。可控等待延時能夠理解爲能經過技術手段和優化來改進縮短的部分,例如減少圖片大小讓請求加載更快、減小HTTP請求數等。不可控等待延時則是不能或很難經過先後端技術手段來改進優化的,例如鼠標點擊延時、CPU計算時間延時、ISP ( Internet Service Provider,互聯網服務提供商)網絡傳輸延時等。前端中的全部優化都是針對可控等待延時這部分來進行的。
Performance Timing API是一個支持Internet Explorer9以上版本及WebKit內核瀏覽器中用於記錄頁面加載和解析過程當中關鍵時間點的機制,它能夠詳細記錄每一個頁面資源從開始加載到解析完成這一過程當中具體操做發生的時間點,這樣根據開始和結束時間戳就能夠計算出這個過程所花的時間了。
以前咱們介紹 Chrome 網絡面板的時候說過一個請求的生命週期:
能夠經過 Performance Timing API 捕獲到各個階段的時間,經過計算各個屬性的差值來評測性能,好比:
var timinhObj = performance.timing;複製代碼
DNS查詢耗時 :domainLookupEnd - domainLookupStart
TCP連接耗時 :connectEnd - connectStart
request請求耗時 :responseEnd - responseStart
解析dom樹耗時 : domComplete - domInteractive
白屏時間 :responseStart - navigationStart
domready時間 :domContentLoadedEventEnd - navigationStart
onload時間 :loadEventEnd - navigationStart複製代碼
以前有說過,使用 Chrome 開發者工具的 Audit 面板或者 Performance 面板,能夠評估性能。
在關鍵邏輯之間手動埋點計時,好比:
let timeList = []
timeList.push({ tag: 'xxxBegin', time: +new Date })
...
timeList.push({ tag: 'xxxEnd', time: +new Date })複製代碼
這種方式經常在移動端頁面中使用,由於移動端瀏覽器HTML解析和JavaScript執行相對較慢,一般爲了進行性能優化,須要找到頁面中執行JavaScript 耗時的操做,若是將關鍵JavaScript的執行過程進行埋點計時並上報,就能夠輕鬆找出JavaScript 執行慢的地方,並有針對性地進行優化。
能夠經過 Chrome 的網絡面板,或者 Fiddler 之類的工具查看時序圖,來分析頁面阻塞:
前端優化的策略很是多,主要的策略大概能夠歸爲幾大類:
在前端頁面中,一般建議儘量合併靜態資源圖片、JavaScript或CSS代碼,減小頁面請求數和資源請求消耗,這樣能夠縮短頁面首次訪問的用戶等待時間。
應儘可能減少每一個HTTP請求的大小。如減小不必的圖片、JavaScript、 CSS及HTML代碼,對文件進行壓縮優化,或者使用gzip壓縮傳輸內容等均可以用來減少文件大小,縮短網絡傳輸等待時延。
在HTML文件中引用外部資源能夠有效利用瀏覽器的靜態資源緩存。
當 link 標籤的 href 屬性爲空,或script、 img、iframe標籤的src屬性爲空時,瀏覽器在渲染的過程當中仍會將href屬性或src屬性中的空內容進行加載,直至加載失敗,這樣就阻塞了頁面中其餘資源的下載進程,並且最終加載到的內容是無效的,所以要儘可能避免。
爲HTML內容設置Cache-Control或Expires能夠將HTML內容緩存起來,避免頻繁向服務器端發送請求。前面講到,在頁面Cache-Control或Expires頭部有效時,瀏覽器將直接從緩存中讀取內容,不向服務器端發送請求。好比:
<meta http-equiv="Cache -Control" content="max-age=7200" />複製代碼
合理設置Etag和Last-Modified使用瀏覽器緩存,對於未修改的文件,靜態資源服務器會向瀏覽器端返回304,讓瀏覽器從緩存中讀取文件,減小Web資源下載的帶寬消耗並下降服務器負載。
頁面每次重定向都會延長頁面內容返回的等待延時,一次重定向大約須要600毫秒的時間開銷,爲了保證用戶儘快看到頁面內容,要儘可能避免頁面重定向。
瀏覽器在同一時刻向同一個域名請求文件的並行下載數是有限的,所以能夠利用多個域名的主機來存放不一樣的靜態資源,增大頁面加載時資源的並行下載數,縮短頁面資源加載的時間。一般根據多個域名來分別存儲JavaScript、CSS和圖片文件。好比京東:
若是條件容許,能夠利用CDN網絡加快同一個地理區域內重複靜態資源文件的響應下載速度,縮短資源請求時間。
CDN Combo是在CDN服務器端將多個文件請求打包成一個文件的形式來返回的技術,這樣能夠實現HTTP鏈接傳輸的一次性複用,減小瀏覽器的HTTP請求數,加快資源下載速度。好比:
//g.alicdn.com/??kissy/k/6.2.4/seed-min.js,tbc/global/0.0.8/index-min.js,tms/tb-init/6.1.0/index-min.js,sea/sitenav-global/0.5.2/global-min.js複製代碼
對於返回內容相同的請求,不必每次都直接從服務端拉取,合理使用AJAX緩存能加快AJAX響應速度並減輕服務器壓力。好比:
const cachedFetch = (url, options) => {
let cacheKey = url
let cached = sessionStorage.getItem(cacheKey)
if (cached !== null) {
let response = new Response(new Blob([cached]))
return Promise.resolve(response)
}
return fetch(url, options).then(response => {
if (response.status === 200) {
let ct = response.headers.get('Content-Type')
if (ct && (ct.match(/application\/json/i) || ct.match(/text\//i))) {
response.clone().text().then(content => {
sessionStorage.setItem(cacheKey, content)
})
}
}
return response
})
}複製代碼
使用XMLHttpRequest時,瀏覽器中的POST方法發送請求首先發送文件頭,而後發送HTTP正文數據。而使用GET時只發送頭部,因此在拉取服務端數據時使用GET請求效率更高。
HTTP請求一般默認帶上瀏覽器端的Cookie一塊兒發送給服務器,因此在非必要的狀況下,要儘可能減小Cookie來減少HTTP請求的大小。對於靜態資源,儘可能使用不一樣的域名來存放,由於Cookie默認是不能跨域的,這樣就作到了不一樣域名下靜態資源請求的Cookie隔離。
這樣有利於favicon.ico的重複加載,由於通常一個Web應用的favicon.ico是不多改變的。
異步的JavaScript 資源不會阻塞文檔解析,因此容許在瀏覽器中優先渲染頁面,延後加載腳本執行。好比:
<script src="main.js" defer></script>
<script src="main.js" async></script>複製代碼
使用async時,加載和渲染後續文檔元素的過程和main.js的加載與執行是並行的。使用defer 時,加載後續文檔元素的過程和main.js的加載也是並行的,可是main.js的執行要在頁面全部元素解析完成以後纔開始執行。
使用異步Javascript,加載的前後順序被打亂,要注意依賴問題。
對於頁面中加載時間過長的CSS或JavaScript文件,須要進行合理拆分或延後加載,保證關鍵路徑的資源能快速加載完成。
CSS中的@import能夠從另外一個樣式文件中引入樣式,但應該避免這種用法,由於這樣會增長CSS資源加載的關鍵路徑長度,帶有@import的CSS樣式須要在CSS文件串行解析到@import時纔會加載另外的CSS文件,大大延後CSS渲染完成的時間。
針對移動端,爲了進一步提高頁面加載速度,能夠考慮將頁面的數據請求儘量提早,避免在JavaScript加載完成後纔去請求數據。一般數據請求是頁面內容渲染中關鍵路徑最長的部分,並且不能並行,因此若是能將數據請求提早,能夠極大程度.上縮短頁面內容的渲染完成時間。
因爲移動端網絡速度相對較慢,網絡資源有限,所以爲了儘快完成頁面內容的加載,須要保證首屏加載資源最小化,非首屏內容使用滾動的方式異步加載。通常推薦移動端頁面首屏數據展現延時最長不超過3秒。目前中國聯通3G的網絡速度爲338KB/s (2.71Mb/s), 不能保證客戶都是流暢的4G網絡,因此推薦首屏全部資源大小不超過1014KB,即大約不超過1MB。
在移動端資源加載中,儘可能保證JavaScript資源並行加載,主要指的是模塊化JavaScript資源的異步加載,使用並行的加載方式可以縮短多個文件資源的加載時間。
一般爲了在HTML加載完成時能使瀏覽器中有基本的樣式,須要將頁面渲染時必備的CSS和JavaScript經過 style 內聯到頁面中,避免頁面HTML載入完成到頁面內容展現這段過程當中頁面出現空白。好比百度:
<!Doctype html><html xmlns=http://www.w3.org/1999/xhtml><head>
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
<meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1">
<meta content=always name=referrer>
<link rel="shortcut icon" href=/favicon.ico type=image/x-icon>
<link rel=icon sizes=any mask href=//www.baidu.com/img/baidu_85beaf5496f291521eb75ba38eacbd87.svg>
<title>百度一下,你就知道 </title>
<style id="style_super_inline">
body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,form,fieldset,legend,button,input,textarea,th,td{margin:0;padding:0}html{color:#000;overflow-y:scroll;overflow:-moz-scrollbars}body,button,input,select,textarea{font:12px arial}
...複製代碼
設置文件資源的DNS預解析,讓瀏覽器提早解析獲取靜態資源的主機IP,避免等到請求時才發起DNS解析請求。一般在移動端HTML中能夠採用以下方式完成。
<!-- cdn域名預解析-->
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//cdn.domain.com">複製代碼
對於移動端首屏加載後可能會被使用的資源,須要在首屏完成加載後儘快進行加載,保證在用戶須要瀏覽時已經加載完成,這時候若是再去異步請求就顯得很慢。
一般狀況下,咱們認爲TCP網絡傳輸的最大傳輸單元(Maximum Transmission Unit, MTU)爲1500B,即一個RTT ( Round-Trip Time,網絡請求往返時間)內能夠傳輸的數據量最大爲1500字節。所以,在先後端分離的開發模式中,儘可能保證頁面的HTML內容在1KB之內,這樣整個HTML的內容請求就能夠在一個RTT內請求完成,最大限度地提升HTML載入速度。
通常推薦將全部CSS資源儘早指定在HTML文檔中, 這樣瀏覽器能夠優先下載CSS並儘早完成頁面渲染。
JavaScript資源放到HTML文檔底部能夠防止JavaScript的加載和解析執行對頁面渲染形成阻塞。因爲JavaScript資源默認是解析阻塞的,除非被標記爲異步或者經過其餘的異步方式加載,不然會阻塞HTML DOM解析和CSS渲染的過程。
在HTML中直接縮放圖片會致使頁面內容的重排重繪,此時可能會使頁面中的其餘操做產生卡頓,所以要儘可能減小在頁面中直接進行圖片縮放。
HTML中標籤元素越多,標籤的層級越深,瀏覽器解析DOM並繪製到瀏覽器中所花的時間就越長,因此應儘量保持DOM元素簡潔和層級較少。
table 內容的渲染是將table的DOM渲染樹所有生成完並一次性繪製到頁面上的,因此在長表格渲染時很耗性能,應該儘可能避免使用它,能夠考慮使用列表元素 ul 代替。儘可能使用異步的方式動態添加iframe,由於iframe內資源的下載進程會阻塞父頁面靜態資源的下載與CSS及HTML DOM的解析。
長時間運行的JavaScript會阻塞瀏覽器構建DOM樹、DOM渲染樹、渲染頁面。因此,任何與頁面初次渲染無關的邏輯功能都應該延遲加載執行,這和JavaScript資源的異步加載思路是一致的。
CSS表達式或CSS濾鏡的解析渲染速度是比較慢的,在有其餘解決方案的狀況下應該儘可能避免使用。
除了上面說到的使用Cache-Control、Expires、 Etag 和Last-Modified來設置HTTP緩存外,在移動端還可使用localStorage 等來保存AJAX返回的數據,或者使用localStorage保存CSS或JavaScript靜態資源內容,實現移動端的離線應用,儘量減小網絡請求,保證靜態資源內容的快速加載。
對於移動端或Hybrid應用,能夠設置離線文件或離線包機制讓靜態資源請求從本地讀取,加快資源載入速度,並實現離線更新。
在移動端,一般要保證頁面中一切用到的圖片都是通過壓縮優化處理的,而不是以原圖的形式直接使用的,由於那樣很消耗流量,並且加載時間更長。
在頁面使用的背景圖片很少且較小的狀況下,能夠將圖片轉化成base64編碼嵌入到HTML頁面或CSS文件中,這樣能夠減小頁面的HTTP請求數。須要注意的是,要保證圖片較小,通常圖片大小超過2KB就不推薦使用base64嵌入顯示了。
使用具備較高壓縮比格式的圖片,如webp 等。在同等圖片畫質的狀況下,高壓縮比格式的圖片體積更小,可以更快完成文件傳輸,節省網絡流量。不過注意 webp 的兼容性,除了Chrome其餘瀏覽器支持很差。
爲了保證頁面內容的最小化,加速頁面的渲染,儘量節省移動端網絡流量,頁面中的圖片資源推薦使用懶加載實現,在頁面滾動時動態載入圖片。好比京東首頁滾動。
介紹響應式時說過,針對不一樣的移動端屏幕尺寸和分辨率,輸出不一樣大小的圖片或背景圖能保證在用戶體驗不下降的前提下節省網絡流量,加快部分機型的圖片加載速度,這在移動端很是值得推薦。
在頁面中儘量使用iconfont 來代替圖片圖標,這樣作的好處有如下幾個:使用iconfont體積較小,並且是矢量圖,所以縮放時不會失真;能夠方便地修改圖片大小尺寸和呈現顏色。
可是須要注意的是,iconfont引用不一樣webfont格式時的兼容性寫法,根據經驗推薦儘可能按照如下順序書寫,不然不容易兼容到全部的瀏覽器上。
@font-face {
font-family: iconfont;
src: url("./iconfont.eot") ;
src: url("./iconfont.eot?#iefix") format("eot"),
url("./iconfont.woff") format("woff"),
url("./iconfont.ttf") format("truetype");
}複製代碼
加載的單張圖片通常建議不超過30KB,避免大圖片加載時間長而阻塞頁面其餘資源的下載,所以推薦在10KB之內。若是用戶,上傳的圖片過大,建議設置告警系統 。
腳本類涉及到代碼的優化,這裏只簡單列一些:
在移動端設置Viewport能夠加速頁面的渲染,同時能夠避免縮放致使頁面重排重繪。好比:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">複製代碼
頁面的重排重繪很耗性能,因此必定要儘量減小頁面的重排重繪。
使用CSS3動畫時能夠設置 transform: translateZ(0)
來開啓移動設備瀏覽器的GPU圖形處理,加速,讓動畫過程更加流暢。
選擇Canvas或requestAnimationFrame等更高效的動畫實現方式,儘可能避免使用setTimeout、setInterval 等方式來直接處理連續動畫。
部分狀況下能夠考慮使用SVG代替圖片實現動畫,由於使用SVG格式內容更小,並且SVG DOM結構方便調整。
在DOM渲染樹生成後的佈局渲染階段,使用float的元素佈局計算比較耗性能,推薦使用固定佈局或flex-box彈性佈局的方式來實現頁面元素佈局。
在條件容許的狀況下能夠考慮使用SPDY協議來進行文件資源傳輸,利用鏈接複用加快傳輸過程,縮短資源加載時間。HTTP2在將來也是能夠考慮嘗試的。
使用後端數據渲染的方式能夠加快頁面內容的渲染展現,避免空白頁面的出現,同時能夠解決移動端頁面SEO的問題。若是條件容許,後端數據渲染是一個很不錯的實踐思路。
能夠嘗試使用Native View等來避免HTML DOM性能慢的問題,目前使用React Native、Weex等已經能夠將頁面內容渲染體驗作到接近客戶端Native應用的體驗了。
這裏列舉了一部分優化的策略,世界上沒有十全十美的事情,在作到了極致優化的同時也會付出很大的代價,這也是前端優化的一個問題。理論上這些優化都是能夠實現的,可是做爲工程師,要懂得權衡。優化提高了用戶體驗,使數據加載更快,可是項目代碼卻可能打亂,異步內容要拆分出來,首屏的一個雪碧圖可能要分紅兩個,頁面項目代碼的數量和維護成本可能成倍增長,項目結構也可能變得不夠清晰。
任何一部分優化均可以作得很深刻,但不必定都值得,在優化的同時也要儘可能考慮性價比,這纔是處理前端優化時應該具備的正確思惟。
在現代互聯網產品的開發迭代中,對前端用戶數據的統計分析嚴重影響着最終產品的成敗。談到前端數據,涉及的方面就比較廣了。網站用戶數據統計分析一般能夠反映出網站的用戶規模、用戶使用習慣、用戶的內容偏好等,瞭解了這些就能幫助咱們調整產品策略、改進產品需求、提升產品質量,除此以外用戶數據的統計甚至也會直接和廣告收入相關聯。
一般頁面上用戶訪問統計主要包括PV(Page View)、UV(Unique Visitor)、VV (Visit View)、IP(訪問站點的不一樣IP數)等。
PV通常指在一天時間以內頁面被全部用戶訪問的總次數,即每一次頁面刷新都會增長一次PV。PV做爲單個頁面的統計量參數,一般用來統計獲取關鍵入口頁面或臨時推廣性頁面的訪問量或推廣效果,因爲PV的統計通常是不作任何條件限制的,能夠人爲地刷新來提高統計量,因此單純靠PV是沒法反應頁面被用戶訪問的具體狀況的。
UV是指在一天時間之訪問內頁的不一樣用戶個數,和PV不一樣的是,若是一個頁面在同一天內被某個相同用戶屢次訪問,只計
算一次UV。
UV能夠認爲是前端頁面統計中一個最有價值的統計指標,由於其直接反應頁面的訪問用戶數。目前有較多站點的UV是按照一天以內訪問目標頁面的IP數來計算的,所以也能夠根據UV來統計站點的周活躍用戶量和月活躍用戶量。
嚴格來說,根據一天時間內訪問目標頁面的IP數來計算UV是不嚴謹的,由於在辦公區或校園局域網的狀況下,多個用戶訪問互聯
網網站的IP多是同一個,但實際上的訪問用戶卻有不少。因此爲了獲得更加準確的結果,除了根據IP,還須要結合其餘的輔助信息來識別統計不一樣用戶的UV,好比有兩種經常使用的方式:
因而可知,雖然UV是網站統計的一個很重要的統計量,但通常狀況下是沒法用於精確統計的,因此一般須要結合PV、UV來一塊兒分析網站被用戶訪問的狀況。此外,咱們還能夠對站點一天的新訪客數、新訪客比率等進行統計,計算第一次訪問網站的新用戶數和比例,這對判斷網站用戶增加也是頗有意義的。
PV和UV更可能是針對單頁面進行的統計,而VV則是用戶訪問整個網站的統計指標。例如用戶打開站點,並在內部作了屢次跳轉操做,最後關閉該網站全部的頁面,即爲一次VV。
IP是一天時間內訪問網頁或網站的獨立IP數,通常服務器端能夠直接獲取用戶訪問網站時的獨立IP,統計也比較容易處理。須要注意IP統計與UV統計的區別和聯繫。
對於較小的項目團隊來講,或許獲得頁面或網站的PV、UV、VV、IP這些基本的統計數據就能夠了。其實相對於訪問量的統計,用戶行爲分析纔是更加直接反映網頁內容是否受用戶喜歡或知足用戶需求的一個重要標準,用戶在頁面上操做的行爲有不少種,每種操做均可能對應頁面上不一樣的展現內容。若是咱們能知道用戶瀏覽目標頁面時全部的行爲操做,必定程度上就能夠知道用戶對頁面的哪些內容感興趣,對哪些內容不感興趣,這對產品內容的調整和改進是頗有意義的。通常用於分析用戶行爲的參數指標主要包括:頁面點擊量、用戶點擊流、用戶訪問路徑、用戶點擊熱力圖、用戶轉換率、用戶訪問時長分析和用戶訪問內容分析等。
頁面點擊量用來統計用戶對於頁面某個可點擊或可操做區域的點擊或操做次數。以點擊的狀況爲例,統計頁面上某個按鈕被點擊的次數就能夠經過該方法來計算,這樣經過統計的結果能夠分析出頁面上哪些按鈕對應的內容是用戶可能感興趣的。
點擊流用來統計用戶在頁面中發生點擊或操做動做的順序,能夠反映用戶在頁面上的操做行爲。因此統計上報時須要在瀏覽器上先保存記錄用戶的操做順序,例如在關鍵的按鈕中埋點,點擊時向localStorage中記錄點擊或操做行爲的惟一id,在用戶一次VV結束或在下一次VV開始時進行點擊流上報,而後經過後臺歸併統計分析。
用戶訪問路徑和用戶點擊流有點相似,不過用戶訪問路徑不針對用戶的可點擊或操做區域埋點,而是針對每一個頁面埋點記錄用戶訪問不一樣頁面的路徑。上報信息的方法和用戶點擊流上報相同,經常也是在一次VV結束或下一次VV開始時,上報用戶的訪問路徑。
用戶點擊熱力圖是爲了統計用戶的點擊或操做發生在整個頁面哪些區域位置的一種分析方法,通常是統計用戶操做習慣和頁面某些區域內容是否受用戶關注的一種方式。
這種統計方法獲取上報點的方式主要是捕獲鼠標事件在屏幕中的座標位置進行上報,而後在服務端進行計算歸類分析並繪圖。
對用戶轉化率的分析一 般在一些臨時推廣頁面或拉取新用戶宜傳頁面上比較經常使用,這裏統計也很簡單,例如要統計某個新產品推廣頁面的用戶轉化率,經過計算通過該頁面註冊的用戶數相對於頁面的PV比例就能夠得出。
用戶轉化率 = 經過該頁面註冊的用戶數 / 頁面PV
相對來講,用戶轉化率分析的應用場景比較單一。還有另外一種導流的頁面統計分析和該頁面的功能相似,不過其做用是將某個頁面的用戶訪問流量引導到另外一個頁面中,導流轉化率能夠用經過源頁面導入的頁面訪問PV相對於源頁面的總PV比例來表示。
導流轉化率 = 經過源頁面導入的頁面訪問PV / 源頁面PV
本質上,關鍵的統計分析還是對現有頁面訪問量進行對比和計算而得出的,並非統計出來的。
用戶訪問時長和內容分析則是統計分析用戶在某些關鍵內容頁面的停留時間,來判斷用戶對該頁面的內容是否感興趣,從而分析出用戶對網站可能感興趣的內容,方便之後精確地向該用戶推薦他們感興趣的內容。
後端開發通常在程序運行出現異常時能夠經過寫服務器日誌的方式來記錄錯誤的信息,而後下載服務器日誌打開查看是哪裏的問題並進行修復。可是若是是前端頁面運行出現了問題,咱們卻不能打開用戶瀏覽器的控制檯記錄來查看代碼中到底出現了什麼錯誤。
通常狀況下,在前端開發中,前端工程師按照需求完成頁面開發,經過產品體驗確認和測試,頁面就能夠上線了。但不幸的是,產品很快就收到了用戶的投訴。用戶反映頁面點擊按鈕沒反應,咱們本身試了一下卻一切正常,因而追問用戶所用的環境,最後結論是用戶使用了一個很是小衆的瀏覽器打開頁面,由於該瀏覽器不支持某個特性,所以頁面報錯,整個頁面中止響應。在這種狀況下,用戶反饋的投訴花掉了咱們不少時間去定位問題,然而這並非最可怕的,更讓咱們擔心的是更多的用戶遇到這種場景後便會直接拋棄這個有問題的「垃圾產品」。
這個問題惟一的解決辦法就是在儘可能少的用戶遇到這樣的場景時就把問題即時修復掉,保證儘可能多的用戶能夠正常使用。首先須要在少數用戶使用產品出錯時知道有用戶出錯,並且儘可能定位到是什麼錯誤。因爲用戶的運行環境是在瀏覽器端的,所以能夠在前端頁面腳本執行出錯時將錯誤信息上傳到服務器,而後打開服務器收集的錯誤信息進行分析來改進產品的質量。要實現這個過程,咱們必須考慮下面幾個問題。
瀏覽器提供了try.. .catch和window. onerror的兩種機制來幫助咱們獲取用戶頁面的腳本錯誤信息。
window.onerror = function (msg, url, lineNo, columnNo, error) {
// ... handle error ...
return false;
}複製代碼
若是捕獲到了具體的錯誤或棧信息,就能夠將錯誤信息進行上報了,如出錯信息、錯誤行號、列號、用戶瀏覽器信息等,經過建立HTTP請求的方式便可將它們發送到日誌收集服務器。固然錯誤信息上報設計時須要注意一點:頁面的訪問量可能很大,若是到達百萬級、千萬級,那麼就須要按照必定的條件上報,例如根據必定的機率進行上報,不然大量的錯誤信息上報請求會佔用日誌收集服務器的不少資源和流量。
爲了方便查看收集到的這些信息,咱們一般能夠創建一個簡單的內容管理系統(Content Management System,CMS)來管理查看錯誤日誌,對同一類型的錯誤作歸併統計,也能夠創建錯誤量實時統計來查看錯誤量的即時變化狀況。當某個版本發佈後,若是收到的錯誤量明顯增長,就須要格外注意。另一點要注意的是,上報錯誤信息機制是用來輔助產品質量改進的,不能由於在頁面中添加了錯誤信息收集和上報而影響了原有的業務模塊功能。
若是要進一步完善地檢測頁面的異常信息,能夠嘗試對靜態資源文件加載失敗的狀況進行監控。例如在CDN網絡中,可能由於部分機器故障,致使用戶加載不到<img>、<script>等靜態資源,可是開發者不必定能復現,並且沒法第一時間知道靜態資源加載失敗了。這種狀況下這就須要在頁面上自動捕獲文件加載失敗的異常來進行處理,能夠對<img>或<script>標籤元素的readyChange進行是否加載成功的判斷。不幸的是,只有部分IE瀏覽器支持<img>或<script>的readyState,所以通常還須要結合其餘方式,如onload,針對不一樣瀏覽器分開處理。
開發者怎樣知道用戶端打開頁面時的性能如何呢,一個可行的方法就是將頁面性能數據進行上報統計,例如將PerformanceTiming 數據、開發者本身埋點的性能統計數據經過頁面JavaScript統一上報到遠程服務器,在服務器端統計計算性能數據的平均值來評判前端具體頁面的性能狀況。
以上介紹的是前端頁面數據統計和分析的主要內容,在實際項目中能夠根據產品或開發須要來進行調整。須要注意的是,不要過分設計,例如對於訪問量不多的網站進行大量的用戶行爲分析可能就得不償失了。
搜索引擎優化簡稱SEO。對於不少網站來講,搜索引擎是最重要的入口,提高天然排名至關於提高網站的曝光度,做爲前端工程師,瞭解搜索引擎優化方面的相關知識是很重要的。
title. keywords、 description 是能夠在HTML的<meta>標籤內定義的,有助於搜索引擎抓取到網頁的內容。要注意的是,通常title的權重是最高的,也是最重要的。keywords 相對權重較低,能夠做爲頁面的輔助關鍵詞搜索。description的描述通常會直接顯示在搜索結果的介紹中,可使用戶快速瞭解頁面內容的描述文字,因此要儘可能讓這段文字可以描述整個頁面的內容,增長用戶進入頁面的機率。
通常title的設置要儘可能可以歸納頁面的內容,可使用多個title關鍵字組合的形式,並用分隔符鏈接起來。分隔符通常有 「_」、「-」、「 」、「,」等,其中「_」分隔符比較容易被百度搜索引擎檢索到,「-」分隔符則容易被谷歌搜索引擎檢索到,「,」 則在英文站點中使用比較多,可使用空格。
title 的長度在桌面瀏覽器端通常建議控制在 30個字之內,在移動端控制在20個字之內,若長度超出時瀏覽器會默認截斷並顯示省略號。
關於title格式的優化設置能夠遵循如下規則:
對於網站不一樣頁面title的定義能夠設置以下:
例如某個博客的名稱爲極限前端,那麼其首頁的title就能夠以下編寫:
<!-- 很差的title設置-->
<title>極限前端</title>
<title>極限前端_ front end</title>
<!-- 良好的title設置-->
<title>極限前端_首頁_ 前端技術知識_某某某的博客</title>複製代碼
keywords是目前用於頁面內容檢索的輔助關鍵字信息,容易被搜索引擎檢索到,因此恰當的設置頁面keywords內容對於頁面的SEO也是很重要的,並且keywords自己的使用也比較簡單。
在搜索引擎檢索結果中,description 更重要的做用是做爲搜索結果的描述,而不是做爲權值計算的重要參考因素。description 的長度在桌面瀏覽器頁面中通常爲78箇中文字符,移動端爲50個,超過則會自動截斷並顯示省略號。以下定義title、keywords、description 比較合適:
<!--很差的title. keywords、 description優化設置-->
<title>極限前端</title>
<meta name="keywords" content="極限前端">
<meta name="description" content="極限前端">
<!--良好的title. keywords、 description優化設置-->
<title>前端搜索引擎優化基礎_極限前端_前端技術知識_某某某的博客</title>
<meta name="keywords" content="現代前端技術, 前端頁面SEO優化, 極限前端, 某某某的博客">
<meta name="description" content="本章講述了前端搜索引擎優化基礎實踐技術。">複製代碼
title、keywords. description 的設置對頁面SEO具備重要意義,但除了頁面title、keywords、description外,還有頁面結構語義化設計,由於搜索引擎分析頁面內容時能夠解析語義化的標籤來獲取內容,並賦予相關的權重,所以語義化結構的頁面就比所有爲<div>標籤元素佈局的頁面更容易被檢索到。
若是頁面兼容性條件容許,儘可能使用HTML5語義化結構標籤。使用<header>、 <nav>、 <aside>、 <article>、 <footer>等標籤增長頁面的語義化內容,可讓搜索引擎更容易獲取頁面的結構內容。
建議每一個頁面都有一個惟一的<h1>標題, 但通常<h1>內容並非網站的標題。<h1>做爲頁面最高層級的標題可以更容易被搜索引擎收錄,並賦予頁面相對較高權重的內容描述。通常設置首頁的<h1>標題爲站點名稱,其餘內頁的<h1>標題則能夠爲各個內頁的標題,如分類頁用分類的名字、詳情頁用詳情頁標題等。
由於SEO的須要,應該儘可能保證搜索引擎抓取到的頁面是有內容的,可是以AJAX技術實現的SPA應用在SEO上不具備優點,所以要儘可能避免這樣的頁面實現方式。
通常要求<img>標籤必須設置 alt屬性,這樣更有利於搜索引擎檢索出圖片的描述信息。
統一網站的地址連接:
http://www.domain.com
http://domain.com
http://www.domain.com/index.html
http://domain. com/index.html
以上四個地址均可以表示跳轉到同一個站點的首頁,雖然不會對用戶訪問形成什麼麻煩,但對於搜索引擎來講是四條網址而且內容相同。這種狀況有可能會被搜索引擎誤認爲是做弊手段,另外當搜索引擎要規範化網址時,須要從這些選擇中挑一個做爲表明,可是挑的這個不必定是最好的,所以咱們最好統一搜索引擎訪問頁面的地址,不然可能影響網站入口搜索結果的權重。
若是URL發生改變,必定要使舊的地址301指向新的頁面,不然搜索引擎會把原有的這個URL看成死鏈處理,以前完成的頁面內容收錄權重的工做就都失效了。
當該頁面有不一樣參數傳遞的時候,標籤屬性也能夠起到標識頁面惟一性的做用,例如如下
三個地址。
domain.com/index.html
domain.com/index.html?from=123
domain.com/index.html?from=456
在搜索引擎中,以上三個地址分別表示三個頁面,但其實後面兩個通常表示頁面跳轉的來源,因此爲了確保這三個地址爲同一個頁面,每每在<head>上加上canonical聲明,告訴搜索引擎在收錄頁面時能夠按照這個href提供的頁面地址去處理,而不是將每一個地址都獨立處理。
<link rel="cononical" href="//:domain. com/index.html" />複製代碼
robots.txt是網站站點用來配置搜索引擎抓取站點內容路徑的一種控制方式,放置於站點根目錄下。搜索引擎爬蟲訪問網站時會訪問robots.txt文件,robots.txt能夠指導搜索引擎爬蟲禁止抓取網站某些內容或只容許抓取哪些內容,這就保證了搜索引擎不抓取站點中臨時或不重要的內容,保證網站的主要內容被搜索引擎收錄。
sitemap格式通常分爲HTML和XML兩種,命名能夠爲sitemap.html或sitemap.xml,做用是列出網站全部的URL地址,方便搜索引擎去逐個抓取網站的頁面,增長網站頁面在搜索引擎中的的曝光量。
關於SEO的內容有不少,這裏只是簡單提了一些實際開發中可能涉及的部分。關於內外鏈、權重、內容結構、內容建設等和編碼基本沒啥關係的,就沒有說了。
前端技術涉及UI界面、數據展現、用戶交互等實現,所以不可避免地要和團隊其餘成員進行協做溝通,如產品經理、UI設計師、交互設計師、後臺工程師、運維工程師等。前端主要協做的內容有:
和產品經理:主要關注需求是否明確,技術方案是否可行,需求性價比是否高,是否有簡單的可接受的替代方案,需求變動的影響等。
和後端工程師:主要關注數據接口定義,線上問題定位,接口調試等。
和UI設計師:主要關注設計圖是否容易實現,使用什麼樣的組件,操做過程當中的交互和動畫效果等。
和運維工程師:主要關注如何上線,環境配置等。
注意在協做過程當中不要毆打同事。