前端技術演進(六):前端專案與技術實踐

姜小抖發表於2019-02-16
這個來自之前做的培訓,刪減了一些業務相關的,參考了很多資料(參考資料列表),謝謝前輩們,麼麼噠 ?

任何五花八門的技術,最終還是要在實踐中落地。現代的軟體開發,大部分講求的不是高難度高精尖,而是效率和質量。

這裡主要來說說現代前端技術在專案中的實踐。

開發規範

開發規範是開發工程師之間交流的另一種語言,它在一定程度上決定了程式碼是否具有一致性和易維護性,統一的開發規範常常可以降低程式碼的出錯概率和團隊開發的協作成本。

就拿命名規範來說,如果沒有規範,你會經常看到這樣的程式碼:

var a1,a2,temp1,temp2,woshimt;複製程式碼

image.png | center | 640x356

開發規範制定的重要性不言而喻,使用怎樣的規範又成為了另一個問題,因為程式設計規範並不唯一。通俗地講,規範的差別很多時候只是程式碼寫法的區別,不同的規範都有各自的特點,大部分沒有優劣之分。一般在選擇時沒必要糾結於使用哪一種規範, 只要團隊成員都認可並達成一致就行。

實際上,我們平時所說的開發規範更多時候指的是狹義上的編碼規範,廣義上的開發規範包括實際專案開發中可能涉及的所有規範,如專案技術選型規範、元件規範、介面規範、模組化規範等。由於每個團隊使用的專案技術實現不一樣,規範也可能千差萬別,但無論是哪一種規範, 在一個團隊中儘可能保持統一。

這裡是一個規範的例子:guide.aotu.io/docs/index.…

如果使用框架,各個框架會有自己的最佳實踐,一般來說參考官方的最佳實踐,結合自己團隊的習慣即可。

比如Vue:cn.vuejs.org/v2/style-gu…

自動化構建

在現代軟體開發中,自動化構建已經成為一個不可缺少的部分。

對於編譯型語言來說,一般都會通過命令列或者IDE先進行編譯,然後在不同平臺上安裝執行。而前端程式碼不需要軟體編譯,Javascript算是解釋型語言,瀏覽器變解析邊執行,所以前端的自動化構建和傳統語言略有不同。

前端自動化構建目的

前端構建工具的作用主要是對專案原始檔或資源進行檔案級處理,將檔案或資源處理成需要的最佳輸出結構和形式。

在處理過程中,我們可以對檔案進行模組化引入、依賴分析、資源合併、壓縮優化、檔案嵌入、路徑替換、生成資源包等多種操作,這樣就能完成很多原本需要手動完成的事情,極大地提高開發效率。

前端自動化構建工具

在沒有自動化構建工具之前,前端在上線前的處理一般是這樣的:

  1. HTML程式碼語法檢查
  2. HTML去掉註釋
  3. CSS程式碼去掉註釋,新增版權資訊
  4. CSS程式碼語法檢查
  5. CSS檔案新增相容性屬性
  6. CSS檔案壓縮合並
  7. JS檔案語法檢查
  8. JS檔案去掉註釋,新增版權資訊
  9. JS檔案壓縮
  10. 圖片壓縮、合併
  11. 各個檔名稱新增唯一hash
  12. 修改HTML檔案引用路徑
  13. 區分線上和開發環境

整個過程每個步驟會用到相應的工具,比如:CSSLint、JSLint、Uglyfy、HTMLMin、CssMinify、imagemin等,繁瑣且浪費時間。

而且還有一些附加的構建要求,比如程式碼一旦修改就要自動校驗,自動測試,重新整理瀏覽器等,這種在幾年前基本上無法實現。

漸漸地,出現了一些自動化構建的工具。

Grunt

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 用程式碼方式來寫打包指令碼,並且程式碼採用流式的寫法,只抽象出了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 要快很多。

Webpack

image.png | center | 827x444

Grunt 和 Gulp 可以算是第一代的自動化構建工具。現在前端主要使用的是 Webpack。

其實對比 Gulp 來說,Webpack 並不是一個完全的替代平,Gulp 是任務執行工具,它只是一個自動執行可重複活動的應用程式,它的用途更加的廣泛,因為自動任務的範圍更廣。

相對Gulp來說, Webpack是一個靜態模組打包器(static module bundler),主要目的是幫助程式模組及其依賴構建靜態資源。但是因為前端自動化構建的主要任務其實就是靜態資源的構建,所以Webpack基本都可以完成。因此 Gulp 現在的使用比較少了。

image.png | center | 827x414

其實 Webpack 之所以流行,是因為之前的工具對模組化的支援不足,以前的工具大部分是以檔案為單位的,而現代JS開發,都是基於模組的,模組依賴的識別是需要語法語義分析的,像 Gulp 之類的工具,只是一個自動執行的工具,沒法很好的識別所有的模組依賴,所以繼續使用會限制書寫的方式和專案結構,配置起來也更加繁瑣。

Webpack 把所有的程式碼或圖片都當做資源,它會從一個或多個入口檔案開始找起,找到所有的資源依賴,然後做語法分析,去除掉不用的或重複的,最終按照配置要求生成處理過的檔案。

image.png | center | 576x566

一個典型的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
      }
    })
  ]
};複製程式碼

打包後生成:

image.png | center | 720x186

Rollup

image.png | center | 827x414

最近,React,Vue、Ember、Preact、D3、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

Performance Timing API是一個支援Internet Explorer9以上版本及WebKit核心瀏覽器中用於記錄頁面載入和解析過程中關鍵時間點的機制,它可以詳細記錄每個頁面資源從開始載入到解析完成這一過程中具體操作發生的時間點,這樣根據開始和結束時間戳就可以計算出這個過程所花的時間了。

之前我們介紹 Chrome 網路皮膚的時候說過一個請求的生命週期:

image.png | center | 827x494

可以通過 Performance Timing API 捕獲到各個階段的時間,通過計算各個屬性的差值來評測效能,比如:

var timinhObj = performance.timing;複製程式碼

image.png | left | 386x371

DNS查詢耗時 :domainLookupEnd - domainLookupStart
TCP連結耗時 :connectEnd - connectStart
request請求耗時 :responseEnd - responseStart
解析dom樹耗時 : domComplete - domInteractive
白屏時間 :responseStart - navigationStart
domready時間 :domContentLoadedEventEnd - navigationStart
onload時間 :loadEventEnd - navigationStart複製程式碼

Profile 工具

之前有說過,使用 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 之類的工具檢視時序圖,來分析頁面阻塞:

image.png | center | 585x366

前端優化策略

前端優化的策略非常多,主要的策略大概可以歸為幾大類:

網路載入類

減少HTTP資源請求次數

在前端頁面中,通常建議儘可能合併靜態資源圖片、JavaScript或CSS程式碼,減少頁面請求數和資源請求消耗,這樣可以縮短頁面首次訪問的使用者等待時間。

減小HTTP請求大小

應儘量減小每個HTTP請求的大小。如減少沒必要的圖片、JavaScript、 CSS及HTML程式碼,對檔案進行壓縮優化,或者使用gzip壓縮傳輸內容等都可以用來減小檔案大小,縮短網路傳輸等待時延。

將CSS或JavaScript放到外部檔案中,避免使用 script 標籤直接引入

在HTML檔案中引用外部資源可以有效利用瀏覽器的靜態資源快取。

避免使用空的href和src

當 link 標籤的 href 屬性為空,或script、 img、iframe標籤的src屬性為空時,瀏覽器在渲染的過程中仍會將href屬性或src屬性中的空內容進行載入,直至載入失敗,這樣就阻塞了頁面中其他資源的下載程式,而且最終載入到的內容是無效的,因此要儘量避免。

為HTML指定Cache-Control或Expires

為HTML內容設定Cache-Control或Expires可以將HTML內容快取起來,避免頻繁向伺服器端傳送請求。前面講到,在頁面Cache-Control或Expires頭部有效時,瀏覽器將直接從快取中讀取內容,不向伺服器端傳送請求。比如:

<meta http-equiv="Cache -Control" content="max-age=7200" />複製程式碼

合理設定Etag和Last-Modified

合理設定Etag和Last-Modified使用瀏覽器快取,對於未修改的檔案,靜態資源伺服器會向瀏覽器端返回304,讓瀏覽器從快取中讀取檔案,減少Web資源下載的頻寬消耗並降低伺服器負載。

減少頁面重定向

頁面每次重定向都會延長頁面內容返回的等待延時,一次重定向大約需要600毫秒的時間開銷,為了保證使用者儘快看到頁面內容,要儘量避免頁面重定向。

使用靜態資源分域存放來增加下載並行數

瀏覽器在同一時刻向同一個域名請求檔案的並行下載數是有限的,因此可以利用多個域名的主機來存放不同的靜態資源,增大頁面載入時資源的並行下載數,縮短頁面資源載入的時間。通常根據多個域名來分別儲存JavaScript、CSS和圖片檔案。比如京東:

image.png | center | 827x513

使用靜態資源CDN來儲存檔案

如果條件允許,可以利用CDN網路加快同一個地理區域內重複靜態資原始檔的響應下載速度,縮短資源請求時間。

使用CDN Combo下載傳輸內容

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快取能加快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
  })
}複製程式碼

使用GET來完成AJAX請求

使用XMLHttpRequest時,瀏覽器中的POST方法傳送請求首先傳送檔案頭,然後傳送HTTP正文資料。而使用GET時只傳送頭部,所以在拉取服務端資料時使用GET請求效率更高。

減少Cookie的大小並進行Cookie隔離

HTTP請求通常預設帶上瀏覽器端的Cookie一起傳送給伺服器,所以在非必要的情況下,要儘量減少Cookie來減小HTTP請求的大小。對於靜態資源,儘量使用不同的域名來存放,因為Cookie預設是不能跨域的,這樣就做到了不同域名下靜態資源請求的Cookie隔離。

縮小favicon.ico並快取

這樣有利於favicon.ico的重複載入,因為一般一個Web應用的favicon.ico是很少改變的。

推薦使用非同步JavaScript資源

非同步的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或JavaScript檔案,需要進行合理拆分或延後載入,保證關鍵路徑的資源能快速載入完成。

避免使用CSS import引用載入CSS

CSS中的@import可以從另一個樣式檔案中引入樣式,但應該避免這種用法,因為這樣會增加CSS資源載入的關鍵路徑長度,帶有@import的CSS樣式需要在CSS檔案序列解析到@import時才會載入另外的CSS檔案,大大延後CSS渲染完成的時間。

首屏資料請求提前,避免JavaScript 檔案載入後才請求資料

針對移動端,為了進一步提升頁面載入速度,可以考慮將頁面的資料請求儘可能提前,避免在JavaScript載入完成後才去請求資料。通常資料請求是頁面內容渲染中關鍵路徑最長的部分,而且不能並行,所以如果能將資料請求提前,可以極大程度.上縮短頁面內容的渲染完成時間。

首屏載入和按需載入,非首屏內容滾屏載入,保證首屏內容最小化

由於移動端網路速度相對較慢,網路資源有限,因此為了儘快完成頁面內容的載入,需要保證首屏載入資源最小化,非首屏內容使用滾動的方式非同步載入。一般推薦移動端頁面首屏資料展示延時最長不超過3秒。目前中國聯通3G的網路速度為338KB/s (2.71Mb/s), 不能保證客戶都是流暢的4G網路,所以推薦首屏所有資源大小不超過1014KB,即大約不超過1MB。

模組化資源並行下載

在移動端資源載入中,儘量保證JavaScript資源並行載入,主要指的是模組化JavaScript資源的非同步載入,使用並行的載入方式能夠縮短多個檔案資源的載入時間。

inline 首屏必備的CSS和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}
...複製程式碼

meta dns prefetch設定DNS預解析

設定檔案資源的DNS預解析,讓瀏覽器提前解析獲取靜態資源的主機IP,避免等到請求時才發起DNS解析請求。通常在移動端HTML中可以採用如下方式完成。

<!-- cdn域名預解析-->
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//cdn.domain.com">複製程式碼

資源預載入

對於移動端首屏載入後可能會被使用的資源,需要在首屏完成載入後儘快進行載入,保證在使用者需要瀏覽時已經載入完成,這時候如果再去非同步請求就顯得很慢。

合理利用MTU策略

通常情況下,我們認為TCP網路傳輸的最大傳輸單元(Maximum Transmission Unit, MTU)為1500B,即一個RTT ( Round-Trip Time,網路請求往返時間)內可以傳輸的資料量最大為1500位元組。因此,在前後端分離的開發模式中,儘量保證頁面的HTML內容在1KB以內,這樣整個HTML的內容請求就可以在一個RTT內請求完成,最大限度地提高HTML載入速度。

頁面渲染類

把CSS資源引用放到HTML檔案頂部

一般推薦將所有CSS資源儘早指定在HTML文件中, 這樣瀏覽器可以優先下載CSS並儘早完成頁面渲染。

JavaScript資源引用放到HTML檔案底部

JavaScript資源放到HTML文件底部可以防止JavaScript的載入和解析執行對頁面渲染造成阻塞。由於JavaScript資源預設是解析阻塞的,除非被標記為非同步或者通過其他的非同步方式載入,否則會阻塞HTML DOM解析和CSS渲染的過程。

不要在HTML中直接縮放圖片

在HTML中直接縮放圖片會導致頁面內容的重排重繪,此時可能會使頁面中的其他操作產生卡頓,因此要儘量減少在頁面中直接進行圖片縮放。

減少DOM元素數量和深度

HTML中標籤元素越多,標籤的層級越深,瀏覽器解析DOM並繪製到瀏覽器中所花的時間就越長,所以應儘可能保持DOM元素簡潔和層級較少。

儘量避免使用table、iframe等慢元素

table 內容的渲染是將table的DOM渲染樹全部生成完並一次性繪製到頁面上的,所以在長表格渲染時很耗效能,應該儘量避免使用它,可以考慮使用列表元素 ul 代替。儘量使用非同步的方式動態新增iframe,因為iframe內資源的下載程式會阻塞父頁面靜態資源的下載與CSS及HTML DOM的解析。

避免執行耗時的JavaScript

長時間執行的JavaScript會阻塞瀏覽器構建DOM樹、DOM渲染樹、渲染頁面。所以,任何與頁面初次渲染無關的邏輯功能都應該延遲載入執行,這和JavaScript資源的非同步載入思路是一致的。

避免使用CSS表示式或CSS濾鏡

CSS表示式或CSS濾鏡的解析渲染速度是比較慢的,在有其他解決方案的情況下應該儘量避免使用。

快取類

合理利用瀏覽器快取

除了上面說到的使用Cache-Control、Expires、 Etag 和Last-Modified來設定HTTP快取外,在移動端還可以使用localStorage 等來儲存AJAX返回的資料,或者使用localStorage儲存CSS或JavaScript靜態資源內容,實現移動端的離線應用,儘可能減少網路請求,保證靜態資源內容的快速載入。

靜態資源離線方案

對於移動端或Hybrid應用,可以設定離線檔案或離線包機制讓靜態資源請求從本地讀取,加快資源載入速度,並實現離線更新。

圖片類

圖片壓縮處理

在移動端,通常要保證頁面中一切用到的圖片都是經過壓縮優化處理的,而不是以原圖的形式直接使用的,因為那樣很消耗流量,而且載入時間更長。

使用較小的圖片,合理使用base64內嵌圖片

在頁面使用的背景圖片不多且較小的情況下,可以將圖片轉化成base64編碼嵌入到HTML頁面或CSS檔案中,這樣可以減少頁面的HTTP請求數。需要注意的是,要保證圖片較小,一般圖片大小超過2KB就不推薦使用base64嵌入顯示了。

使用更高壓縮比格式的圖片

使用具有較高壓縮比格式的圖片,如webp 等。在同等圖片畫質的情況下,高壓縮比格式的圖片體積更小,能夠更快完成檔案傳輸,節省網路流量。不過注意 webp 的相容性,除了Chrome其他瀏覽器支援不好。

圖片懶載入

為了保證頁面內容的最小化,加速頁面的渲染,儘可能節省移動端網路流量,頁面中的圖片資源推薦使用懶載入實現,在頁面滾動時動態載入圖片。比如京東首頁滾動。

使用Media Query或srcset 根據不同螢幕載入不同大小圖片

介紹響應式時說過,針對不同的移動端螢幕尺寸和解析度,輸出不同大小的圖片或背景圖能保證在使用者體驗不降低的前提下節省網路流量,加快部分機型的圖片載入速度,這在移動端非常值得推薦。

使用iconfont代替圖片圖示

在頁面中儘可能使用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以內。如果使用者,上傳的圖片過大,建議設定告警系統 。

指令碼類

指令碼類涉及到程式碼的優化,這裡只簡單列一些:

  • 儘量使用id選擇器
  • 合理快取DOM物件
  • 頁面元素儘量使用事件代理,避免直接事件繫結
  • 使用touchstart代替click
  • 避免touchmove、scroll 連續事件處理,設定事件節流
  • 推薦使用ECMAScript 6的字串模板連線字串
  • 儘量使用新特性

渲染類

使用Viewport固定螢幕渲染,可以加速頁面渲染內容

在移動端設定Viewport可以加速頁面的渲染,同時可以避免縮放導致頁面重排重繪。比如:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,
user-scalable=no">複製程式碼

避免各種形式重排重繪

頁面的重排重繪很耗效能,所以一定要儘可能減少頁面的重排重繪。

使用CSS3動畫,開啟GPU加速

使用CSS3動畫時可以設定 transform: translateZ(0) 來開啟移動裝置瀏覽器的GPU圖形處理,加速,讓動畫過程更加流暢。

合理使用Canvas和requestAnimationFrame

選擇Canvas或requestAnimationFrame等更高效的動畫實現方式,儘量避免使用setTimeout、setInterval 等方式來直接處理連續動畫。

SVG代替圖片

部分情況下可以考慮使用SVG代替圖片實現動畫,因為使用SVG格式內容更小,而且SVG DOM結構方便調整。

不濫用float

在DOM渲染樹生成後的佈局渲染階段,使用float的元素佈局計算比較耗效能,推薦使用固定佈局或flex-box彈性佈局的方式來實現頁面元素佈局。

架構協議類

嘗試使用SPDY和HTTP 2

在條件允許的情況下可以考慮使用SPDY協議來進行檔案資源傳輸,利用連線複用加快傳輸過程,縮短資源載入時間。HTTP2在未來也是可以考慮嘗試的。

使用後端資料渲染

使用後端資料渲染的方式可以加快頁面內容的渲染展示,避免空白頁面的出現,同時可以解決移動端頁面SEO的問題。如果條件允許,後端資料渲染是一個很不錯的實踐思路。

使用NativeView代替DOM的效能劣勢

可以嘗試使用Native View等來避免HTML DOM效能慢的問題,目前使用React Native、Weex等已經可以將頁面內容渲染體驗做到接近客戶端Native應用的體驗了。

這裡列舉了一部分優化的策略,世界上沒有十全十美的事情,在做到了極致優化的同時也會付出很大的代價,這也是前端優化的一個問題。理論上這些優化都是可以實現的,但是作為工程師,要懂得權衡。優化提升了使用者體驗,使資料載入更快,但是專案程式碼卻可能打亂,非同步內容要拆分出來,首屏的一個雪碧圖可能要分成兩個,頁面專案程式碼的數量和維護成本可能成倍增加,專案結構也可能變得不夠清晰。

任何一部分優化都可以做得很深入,但不一定都值得,在優化的同時也要儘量考慮價效比,這才是處理前端優化時應該具有的正確思維。

使用者資料分析

在現代網際網路產品的開發迭代中,對前端使用者資料的統計分析嚴重影響著最終產品的成敗。談到前端資料,涉及的方面就比較廣了。網站使用者資料統計分析通常可以反映出網站的使用者規模、使用者使用習慣、使用者的內容偏好等,瞭解了這些就能幫助我們調整產品策略、改進產品需求、提高產品質量,除此之外使用者資料的統計甚至也會直接和廣告收入相關聯。

使用者訪問統計

通常頁面上使用者訪問統計主要包括PV(Page View)、UV(Unique Visitor)、VV (Visit View)、IP(訪問站點的不同IP數)等。

PV

PV一般指在一天時間之內頁面被所有使用者訪問的總次數,即每一次頁面重新整理都會增加一次PV。PV作為單個頁面的統計量引數,通常用來統計獲取關鍵入口頁面或臨時推廣性頁面的訪問量或推廣效果,由於PV的統計一般是不做任何條件限制的,可以人為地重新整理來提升統計量,所以單純靠PV是無法反應頁面被使用者訪問的具體情況的。

UV

UV是指在一天時間之訪問內頁的不同使用者個數,和PV不同的是,如果一個頁面在同一天內被某個相同使用者多次訪問,只計
算一次UV。

UV可以認為是前端頁面統計中一個最有價值的統計指標,因為其直接反應頁面的訪問使用者數。目前有較多站點的UV是按照一天之內訪問目標頁面的IP數來計算的,因此也可以根據UV來統計站點的周活躍使用者量和月活躍使用者量。

嚴格來講,根據一天時間內訪問目標頁面的IP數來計算UV是不嚴謹的,因為在辦公區或校園區域網的情況下,多個使用者訪問互聯
網網站的IP可能是同一個,但實際上的訪問使用者卻有很多。所以為了得到更加準確的結果,除了根據IP,還需要結合其他的輔助資訊來識別統計不同使用者的UV,比如有兩種常用的方式:

  • 根據瀏覽器Cookie 和IP統計:在目標頁面每次開啟時向瀏覽器中寫入唯一的某個Cookie資訊,再結合IP一起上報統計,就可以精確統計出一天時間內訪問頁面的使用者數。存在的問題是如果使用者手動清除了Cookie再進入訪問,頁面被重新訪問時就只能算第二次。
  • 結合使用者瀏覽器標識userAgent和IP統計:由於使用Cookie統計存在可能被手動清除的問題,所以推薦結合瀏覽器標識userAgent 來統計。這樣可以在一定程度上區分同IP下的不同使用者,但也不完全準確,IP和瀏覽器標識userAgent相同的情況也很常見,但仍卻只能計算一次。

由此可見,雖然UV是網站統計的一個很重要的統計量,但一般情況下是無法用於精確統計的,所以通常需要結合PV、UV來一起分析網站被使用者訪問的情況。此外,我們還可以對站點一天的新訪客數、新訪客比率等進行統計,計算第一次訪問網站的新使用者數和比例,這對判斷網站使用者增長也是很有意義的。

VV

PV和UV更多是針對單頁面進行的統計,而VV則是使用者訪問整個網站的統計指標。例如使用者開啟站點,並在內部做了多次跳轉操作,最後關閉該網站所有的頁面,即為一次VV。

IP

IP是一天時間內訪問網頁或網站的獨立IP數,一般伺服器端可以直接獲取使用者訪問網站時的獨立IP,統計也比較容易處理。需要注意IP統計與UV統計的區別和聯絡。

使用者行為分析

對於較小的專案團隊來說,或許得到頁面或網站的PV、UV、VV、IP這些基本的統計資料就可以了。其實相對於訪問量的統計,使用者行為分析才是更加直接反映網頁內容是否受使用者喜歡或滿足使用者需求的一個重要標準,使用者在頁面上操作的行為有很多種,每種操作都可能對應頁面上不同的展示內容。如果我們能知道使用者瀏覽目標頁面時所有的行為操作,一定程度上就可以知道使用者對頁面的哪些內容感興趣,對哪些內容不感興趣,這對產品內容的調整和改進是很有意義的。一般用於分析使用者行為的引數指標主要包括:頁面點選量、使用者點選流、使用者訪問路徑、使用者點選熱力圖、使用者轉換率、使用者訪問時長分析和使用者訪問內容分析等。

頁面點選量

頁面點選量用來統計使用者對於頁面某個可點選或可操作區域的點選或操作次數。以點選的情況為例,統計頁面上某個按鈕被點選的次數就可以通過該方法來計算,這樣通過統計的結果可以分析出頁面上哪些按鈕對應的內容是使用者可能感興趣的。

使用者點選流分析

點選流用來統計使用者在頁面中發生點選或操作動作的順序,可以反映使用者在頁面上的操作行為。所以統計上報時需要在瀏覽器上先儲存記錄使用者的操作順序,例如在關鍵的按鈕中埋點,點選時向localStorage中記錄點選或操作行為的唯一id,在使用者一次VV結束或在下一次VV開始時進行點選流上報,然後通過後臺歸併統計分析。

使用者訪問路徑分析

使用者訪問路徑和使用者點選流有點類似,不過使用者訪問路徑不針對使用者的可點選或操作區域埋點,而是針對每個頁面埋點記錄使用者訪問不同頁面的路徑。上報資訊的方法和使用者點選流上報相同,常常也是在一次VV結束或下一次VV開始時,上報使用者的訪問路徑。

image.png | center | 827x601

使用者點選熱力圖

使用者點選熱力圖是為了統計使用者的點選或操作發生在整個頁面哪些區域位置的一種分析方法,一般是統計使用者操作習慣和頁面某些區域內容是否受使用者關注的一種方式。

image.png | center | 395x435

這種統計方法獲取上報點的方式主要是捕獲滑鼠事件在螢幕中的座標位置進行上報,然後在服務端進行計算歸類分析並繪圖。

使用者轉化率與導流轉化率

對使用者轉化率的分析一 般在一些臨時推廣頁面或拉取新使用者宜傳頁面上比較常用,這裡統計也很簡單,例如要統計某個新產品推廣頁面的使用者轉化率,通過計算經過該頁面註冊的使用者數相對於頁面的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 的優化

title. keywords、 description 是可以在HTML的<meta>標籤內定義的,有助於搜尋引擎抓取到網頁的內容。要注意的是,一般title的權重是最高的,也是最重要的。keywords 相對權重較低,可以作為頁面的輔助關鍵詞搜尋。description的描述一般會直接顯示在搜尋結果的介紹中,可以使使用者快速瞭解頁面內容的描述文字,所以要儘量讓這段文字能夠描述整個頁面的內容,增加使用者進入頁面的概率。

title的優化

一般title的設定要儘量能夠概括頁面的內容,可以使用多個title關鍵字組合的形式,並用分隔符連線起來。分隔符一般有 “_”、“-”、“ ”、“,”等,其中“_”分隔符比較容易被百度搜尋引擎檢索到,“-”分隔符則容易被谷歌搜尋引擎檢索到,“,” 則在英文站點中使用比較多,可以使用空格。

title 的長度在桌面瀏覽器端一般建議控制在 30個字以內,在移動端控制在20個字以內,若長度超出時瀏覽器會預設截斷並顯示省略號。

關於title格式的優化設定可以遵循以下規則:

  • 每個網頁都應該有獨一無二的標題,切忌所有的頁面都使用同樣的預設標題。
  • 標題主題明確,應該包含網頁中最重要的資訊。
  • 簡明精練,不應該羅列與網頁內容不相關的資訊。
  • 使用者瀏覽通常從左到右的,建議將重要的內容放到title靠前的位置。
  • 使用使用者所熟知的語言描述,如果有中、英文兩種網站名稱,儘量使用使用者熟知的語言作為標題描述。

對於網站不同頁面title的定義可以設定如下:

  • 首頁:網站名稱提供服務介紹或產品介紹
  • 列表頁:列表名稱_網站名稱
  • 文章頁:文章標題_文章分類_網站名稱
  • 如果文章標題不是很長,還可以增加部分關鍵詞來提高網頁的檢索量,如文章title_ 關鍵詞_網站名稱

例如某個部落格的名稱為極限前端,那麼其首頁的title就可以如下編寫:

<!-- 不好的title設定-->
<title>極限前端</title>
<title>極限前端_ front end</title>

<!-- 良好的title設定-->
<title>極限前端_首頁_ 前端技術知識_某某某的部落格</title>複製程式碼

keywords的優化

keywords是目前用於頁面內容檢索的輔助關鍵字資訊,容易被搜尋引擎檢索到,所以恰當的設定頁面keywords內容對於頁面的SEO也是很重要的,而且keywords本身的使用也比較簡單。

description優化

在搜尋引擎檢索結果中,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標籤結構

如果頁面相容性條件允許,儘量使用HTML5語義化結構標籤。使用<header>、 <nav>、 <aside>、 <article>、 <footer>等標籤增加頁面的語義化內容,可以讓搜尋引擎更容易獲取頁面的結構內容。

image.png | center | 640x480

唯一的H1標題

建議每個頁面都有一個唯一的<h1>標題, 但一般<h1>內容並不是網站的標題。<h1>作為頁面最高層級的標題能夠更容易被搜尋引擎收錄,並賦予頁面相對較高權重的內容描述。一般設定首頁的<h1>標題為站點名稱,其他內頁的<h1>標題則可以為各個內頁的標題,如分類頁用分類的名字、詳情頁用詳情頁標題等。

因為SEO的需要,應該儘量保證搜尋引擎抓取到的頁面是有內容的,但是以AJAX技術實現的SPA應用在SEO上不具有優勢,因此要儘量避免這樣的頁面實現方式。

前端技術演進(六):前端專案與技術實踐新增alt屬性

一般要求<img>標籤必須設定 alt屬性,這樣更有利於搜尋引擎檢索出圖片的描述資訊。

URL規範化

統一網站的地址連結:

http://www.domain.com
http://domain.com
http://www.domain.com/index.html
http://domain. com/index.html

以上四個地址都可以表示跳轉到同一個站點的首頁,雖然不會對使用者訪問造成什麼麻煩,但對於搜尋引擎來說是四條網址並且內容相同。這種情況有可能會被搜尋引擎誤認為是作弊手段,另外當搜尋引擎要規範化網址時,需要從這些選擇中挑一個作為代表,但是挑的這個不一定是最好的,因此我們最好統一搜尋引擎訪問頁面的地址,否則可能影響網站入口搜尋結果的權重。

301跳轉

如果URL發生改變,一定要使舊的地址301指向新的頁面,否則搜尋引擎會把原有的這個URL當作死鏈處理,之前完成的頁面內容收錄權重的工作就都失效了。

canonical

當該頁面有不同引數傳遞的時候,標籤屬性也可以起到標識頁面唯一性的作用,例如以下
三個地址。

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

robots.txt是網站站點用來配置搜尋引擎抓取站點內容路徑的一種控制方式,放置於站點根目錄下。搜尋引擎爬蟲訪問網站時會訪問robots.txt檔案,robots.txt可以指導搜尋引擎爬蟲禁止抓取網站某些內容或只允許抓取哪些內容,這就保證了搜尋引擎不抓取站點中臨時或不重要的內容,保證網站的主要內容被搜尋引擎收錄。

sitemap

sitemap格式一般分為HTML和XML兩種,命名可以為sitemap.html或sitemap.xml,作用是列出網站所有的URL地址,方便搜尋引擎去逐個抓取網站的頁面,增加網站頁面在搜尋引擎中的的曝光量。

關於SEO的內容有很多,這裡只是簡單提了一些實際開發中可能涉及的部分。關於內外鏈、權重、內容結構、內容建設等和編碼基本沒啥關係的,就沒有說了。

前端協作

前端技術涉及UI介面、資料展示、使用者互動等實現,因此不可避免地要和團隊其他成員進行協作溝通,如產品經理、UI設計師、互動設計師、後臺工程師、運維工程師等。前端主要協作的內容有:

和產品經理:主要關注需求是否明確,技術方案是否可行,需求價效比是否高,是否有簡單的可接受的替代方案,需求變更的影響等。

和後端工程師:主要關注資料介面定義,線上問題定位,介面除錯等。

和UI設計師:主要關注設計圖是否容易實現,使用什麼樣的元件,操作過程中的互動和動畫效果等。

和運維工程師:主要關注如何上線,環境配置等。

注意在協作過程中不要毆打同事。

image.png | center | 496x269


相關文章