2020-4-20 seo達(dá)人
性能優(yōu)化(網(wǎng)絡(luò)方向)
web應(yīng)用無(wú)非是兩臺(tái)主機(jī)之間互相傳輸數(shù)據(jù)包的一個(gè)過(guò)程; 如何減少傳輸過(guò)程的耗時(shí)就是網(wǎng)絡(luò)方向優(yōu)化的重點(diǎn), 優(yōu)化出發(fā)點(diǎn)從第一篇文章中說(shuō)起
DNS解析過(guò)程的優(yōu)化
當(dāng)瀏覽器從第三方服務(wù)跨域請(qǐng)求資源的時(shí)候,在瀏覽器發(fā)起請(qǐng)求之前,這個(gè)第三方的跨域域名需要被解析為一個(gè)IP地址,這個(gè)過(guò)程就是DNS解析;
DNS緩存可以用來(lái)減少這個(gè)過(guò)程的耗時(shí),DNS解析可能會(huì)增加請(qǐng)求的延遲,對(duì)于那些需要請(qǐng)求許多第三方的資源的網(wǎng)站而言,DNS解析的耗時(shí)延遲可能會(huì)大大降低網(wǎng)頁(yè)加載性能。
dns-prefetch
當(dāng)站點(diǎn)引用跨域域上的資源時(shí),都應(yīng)在<head>元素中放置dns-prefetch提示,但是要記住一些注意事項(xiàng)。首先,dns-prefetch僅對(duì)跨域域上的DNS查找有效,因此請(qǐng)避免將其用于您當(dāng)前訪問(wèn)的站點(diǎn)
<link rel="dns-prefetch" >
preconnect
由于dns-prefetch僅執(zhí)行DNS查找,但preconnect會(huì)建立與服務(wù)器的連接。如果站點(diǎn)是通過(guò)HTTPS服務(wù)的,則此過(guò)程包括DNS解析,建立TCP連接以及執(zhí)行TLS握手。將兩者結(jié)合起來(lái)可提供機(jī)會(huì),進(jìn)一步減少跨源請(qǐng)求的感知延遲
<!-- 注意順序, precontent和dns-prefetch的兼容性 -->
<link rel="preconnect" crossorigin>
<link rel="dns-prefetch" >
TCP傳輸階段優(yōu)化
這個(gè)前端方面好像能做的有限, 我們都知道 http協(xié)議 是基于 tcp的;
升級(jí)http協(xié)議版本可以考慮下, 比如把 http/1.0 -> http/1.1 -> http/2;
這個(gè)需要我們?cè)趹?yīng)用服務(wù)器上配置(nginx, Apache等), 不做概述了, 另外還需要客戶端和服務(wù)器都支持哦, 目前還沒(méi)開(kāi)發(fā)出穩(wěn)定版本,好多只支持https,不過(guò)也不遠(yuǎn)了...
http2 的優(yōu)勢(shì)
# 1.多路復(fù)用: 同一個(gè)tcp連接傳輸多個(gè)資源
這樣可以突破統(tǒng)一域名下只允許有限個(gè)tcp同時(shí)連接,
這樣http1.1所做的減少請(qǐng)求數(shù)優(yōu)化就沒(méi)有太大必要了
如多張小圖合成一張大圖(雪碧圖),合并js和css文件
# 2.報(bào)文頭壓縮和二進(jìn)制編碼: 減少傳輸體積
http1 中第一次請(qǐng)求有完整的http報(bào)文頭部,第二次請(qǐng)求的也是;
http2 中第一次請(qǐng)求有完整的http報(bào)文頭部,第二次請(qǐng)求只會(huì)攜帶 path 字段;
這樣就大大減少了發(fā)送的量。這個(gè)的實(shí)現(xiàn)要求客戶端和服務(wù)同時(shí)維護(hù)一個(gè)報(bào)文頭表。
# 3.Server Push
http2可以讓服務(wù)先把其它很可能客戶端會(huì)請(qǐng)求的資源(比如圖片)先push發(fā)給你,
不用等到請(qǐng)求的時(shí)候再發(fā)送,這樣可以提高頁(yè)面整體的加載速度
但目前支持性不太好...emm...
總的來(lái)說(shuō), 在 c 端業(yè)務(wù)下不會(huì)太普及, 畢竟需要軟件支持才行...
http 請(qǐng)求響應(yīng)階段優(yōu)化
為了讓數(shù)據(jù)包傳輸?shù)母? 我們可以從兩個(gè)方面入手: 請(qǐng)求的數(shù)據(jù)包大小(服務(wù)器), 請(qǐng)求數(shù)據(jù)包的頻率(客戶端)
減少請(qǐng)求文件的大小
請(qǐng)求文件對(duì)應(yīng)的是我們項(xiàng)目完成后,打包所指的靜態(tài)資源文件(會(huì)被部署到服務(wù)器), 文件越小, 傳輸?shù)臄?shù)據(jù)包也會(huì)相對(duì)較小, 講道理也會(huì)更快到達(dá)客戶端
how to reduce a package size?
目前我們都會(huì)使用打包工具了(比如webpack, rollup, glup 等), 如何使用工具來(lái)減小包的體積呢? 這邊建議您去官網(wǎng)文檔呢...當(dāng)然這里列舉一下常用的手段(webpack 的), 但是注意要插件版本更新哦
JS文件壓縮
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
plugins: [
new UglifyJsPlugin({
// 允許并發(fā)
parallel: true,
// 開(kāi)啟緩存
cache: true,
compress: {
// 刪除所有的console語(yǔ)句
drop_console: true,
// 把使用多次的靜態(tài)值自動(dòng)定義為變量
reduce_vars: true,
},
output: {
// 不保留注釋
comment: false,
// 使輸出的代碼盡可能緊湊
beautify: false
}
})
]
}
CSS 文件壓縮
// optimize-css-assets-webpack-plugin
plugins: [
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'),
}),
];
html 文件壓縮
// html-webpack-plugin
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.html'),
filename: 'index.html',
chunks: ['index'],
inject: true,
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false,
},
}),
];
source map 文件關(guān)閉
tree shaking
1.代碼不會(huì)被執(zhí)行,不可到達(dá),比如 if(false){// 這里邊的代碼}
2.代碼執(zhí)行的結(jié)果不會(huì)被用到
3.代碼只會(huì)影響死變量(只寫(xiě)不讀)
4.方法不能有副作用
// 原理相關(guān): 以后在研究
利用 ES6 模塊的特點(diǎn):
只能作為模塊頂層的語(yǔ)句出現(xiàn)
import 的模塊名只能是字符串常量
import binding 是 immutable 的
代碼擦除: uglify 階段刪除無(wú)用代碼
scope hoisting(作用域提升)
分析出模塊之間的依賴關(guān)系,盡可能的把打散的模塊合并到一個(gè)函數(shù)中去,但前提是不能造成代碼冗余
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
module.exports = {
resolve: {
// 針對(duì) Npm 中的第三方模塊優(yōu)先采用 jsnext:main 中指向的 ES6 模塊化語(yǔ)法的文件
mainFields: ['jsnext:main', 'browser', 'main']
},
plugins: [
// 開(kāi)啟 Scope Hoisting
new ModuleConcatenationPlugin(),
],
};
項(xiàng)目中使用按需加載,懶加載(路由,組件級(jí))
const router = new VueRouter({
routes: [
{ path: '/foo', component: () => import(/* webpackChunkName: "foo" */ './Foo.vue') }
{ path: '/bar', component: () => import(/* webpackChunkName: "bar" */ './Bar.vue') }
]
})
開(kāi)啟 gizp 壓縮
有時(shí)候啟用也會(huì)消耗服務(wù)器性能, 看情況使用吧
暫時(shí)先提這么些吧...后續(xù)想到了再加
減少請(qǐng)求頻率
因?yàn)橥挥蛎?tcp 連接數(shù)的限制導(dǎo)致過(guò)多的請(qǐng)求會(huì)排隊(duì)阻塞, 所以我們需要盡量控制請(qǐng)求的數(shù)量和頻率
常見(jiàn)措施
將靜態(tài)資源的內(nèi)聯(lián)到HTML中
這樣這些資源無(wú)需從服務(wù)器獲取, 但可能影響到渲染進(jìn)程...
<!-- 1.小圖片內(nèi)聯(lián) base64 (url-loader) -->
<!-- 2.css內(nèi)聯(lián) -->
<!-- 3.js內(nèi)聯(lián) -->
<script>
${require('raw-loader!babel-loader!./node_modules/lib-flexible/flexible.js')}
</script>
利用各級(jí)緩存(下一篇存儲(chǔ)方面介紹)
通常都是在服務(wù)端做相關(guān)配置, 但你要知道
我們可以利用http緩存(瀏覽器端)來(lái)減少和攔截二次請(qǐng)求, 當(dāng)然一般都是在服務(wù)端設(shè)置的;
服務(wù)器端也可以設(shè)置緩存(redis等), 減少數(shù)據(jù)查詢的時(shí)間同樣可以縮短整個(gè)請(qǐng)求時(shí)間
利用本地存儲(chǔ)
我們可以將常用不變的信息存在本地(cookie,storage API 等);
判斷存在就不去請(qǐng)求相關(guān)的接口, 或者定期去請(qǐng)求也是可以的
花錢買 CDN 加速
CDN 又叫內(nèi)容分發(fā)網(wǎng)絡(luò),通過(guò)把資源部署到世界各地,用戶在訪問(wèn)時(shí)按照就近原則從離用戶最近的服務(wù)器獲取資源,從而加速資源的獲取速度。 CDN 其實(shí)是通過(guò)優(yōu)化物理鏈路層傳輸過(guò)程中的網(wǎng)速有限、丟包等問(wèn)題來(lái)提升網(wǎng)速的...
購(gòu)買 cdn 服務(wù)器;
然后把網(wǎng)頁(yè)的靜態(tài)資源上傳到 CDN 服務(wù)上去,
在請(qǐng)求這些靜態(tài)資源的時(shí)候需要通過(guò) CDN 服務(wù)提供的 URL 地址去訪問(wèn);
# 注意, cdn 緩存導(dǎo)致的新版本發(fā)布后不生效的問(wèn)題
所以打包的時(shí)候常在文件后面加上 hash 值
然后在 HTML 文件中的資源引入地址也需要換成 CDN 服務(wù)提供的地址
/alicdn/xx12dsa311.js
# 利用不同域名的 cdn 去存放資源, (tcp連接限制)
webpack 構(gòu)建時(shí)添加 cdn
// 靜態(tài)資源的導(dǎo)入 URL 需要變成指向 CDN 服務(wù)的絕對(duì)路徑的 URL 而不是相對(duì)于 HTML 文件的 URL。
// 靜態(tài)資源的文件名稱需要帶上有文件內(nèi)容算出來(lái)的 Hash 值,以防止被緩存。
// 不同類型的資源放到不同域名的 CDN 服務(wù)上去,以防止資源的并行加載被阻塞。
module.exports = {
// 省略 entry 配置...
output: {
// 給輸出的 JavaScript 文件名稱加上 Hash 值
filename: '[name]_[chunkhash:8].js',
path: path.resolve(__dirname, './dist'),
// 指定存放 JavaScript 文件的 CDN 目錄 URL
publicPath: '//js.cdn.com/id/',
},
module: {
rules: [
{
// 增加對(duì) CSS 文件的支持
test: /\.css$/,
// 提取出 Chunk 中的 CSS 代碼到單獨(dú)的文件中
use: ExtractTextPlugin.extract({
// 壓縮 CSS 代碼
use: ['css-loader?minimize'],
// 指定存放 CSS 中導(dǎo)入的資源(例如圖片)的 CDN 目錄 URL
publicPath: '//img.cdn.com/id/'
}),
},
{
// 增加對(duì) PNG 文件的支持
test: /\.png$/,
// 給輸出的 PNG 文件名稱加上 Hash 值
use: ['file-loader?name=[name]_[hash:8].[ext]'],
},
// 省略其它 Loader 配置...
]
},
plugins: [
// 使用 WebPlugin 自動(dòng)生成 HTML
new WebPlugin({
// HTML 模版文件所在的文件路徑
template: './template.html',
// 輸出的 HTML 的文件名稱
filename: 'index.html',
// 指定存放 CSS 文件的 CDN 目錄 URL
stylePublicPath: '//css.cdn.com/id/',
}),
new ExtractTextPlugin({
// 給輸出的 CSS 文件名稱加上 Hash 值
filename: `[name]_[contenthash:8].css`,
}),
// 省略代碼壓縮插件配置...
],
};
/*
以上代碼中最核心的部分是通過(guò) publicPath 參數(shù)設(shè)置存放靜態(tài)資源的 CDN 目錄 URL,
為了讓不同類型的資源輸出到不同的 CDN,需要分別在:
output.publicPath 中設(shè)置 JavaScript 的地址。
css-loader.publicPath 中設(shè)置被 CSS 導(dǎo)入的資源的的地址。
WebPlugin.stylePublicPath 中設(shè)置 CSS 文件的地址。
設(shè)置好 publicPath 后,WebPlugin 在生成 HTML 文件和 css-loader 轉(zhuǎn)換 CSS 代碼時(shí),會(huì)考慮到配置中的 publicPath,用對(duì)應(yīng)的線上地址替換原來(lái)的相對(duì)地址。
*/
參考
DNS MDN]
webpack 文檔
深入淺出 Webpack
Scope Hoisting
藍(lán)藍(lán)設(shè)計(jì)的小編 http://m.ssll180.com