构建后的js代码只包含被引用并被执行的模块,而不被引用或不被执行的模块会被删除,以起到减包的做用。javascript
webpack 在 mode为production下默认执行tree shaking,不设置mode默认为productioncss
注意项:tree shaking只会检测 ES2015 模块语法(即 import 和 export)。更多细节html
webpack的tree shaking至今实现的功能并不完美,凡有反作用的模块,tree-shaking并不起做用前端
例子🌰:vue
// index.js
import { func2, func3 } from './module-a'
func2()
复制代码
// module-a.js
import lodash from 'lodash-es'
export * from './module-b'
export const func1 = function(value) {
return lodash.isArray(value)
}
export const func2 = function() {
console.log('这是func2')
return 123123
}
复制代码
// module-b.js
export const func3 = () => {
console.log('B模块的func3方法')
}
复制代码
这个插件主要用于填充webpack自身Tree-shaking的不足,经过做用域分析来消除无用的代码。原理请看java
const WebpackDeepScopeAnalysisPlugin = require('webpack-deep-scope-plugin')
.default
...
plugins: [
new WebpackDeepScopeAnalysisPlugin(),
],
复制代码
对于css的tree shaking 则使用 purgecss-webpack-plugin 和 mini-css-extract-plugin 来实现react
在css中添加如下代码:webpack
.red {
color: red
}
.blue {
color: blue
}
复制代码
而后在js文件中引用:nginx
import './index.css'
var div = createElement('div')
div.styles.class = 'red'
console.log(div);
复制代码
理想状况下,咱们但愿打包后的文件中只含**.red而不含未使用的.blue**,配置以下:git
const TerserJSPlugin = require("terser-webpack-plugin")
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const PurgecssPlugin = require('purgecss-webpack-plugin')
optimization: {
// 压缩js、css
minimizer: [
new TerserJSPlugin({}),
new OptimizeCSSAssetsPlugin({})
],
},
module: {
...
use: [
MiniCssExtractPlugin.loader,
'css-loader',
]
}
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
}),
// css tree shaking
new PurgecssPlugin({
paths: glob.sync(`${path.join(__dirname, '/src/**/*')}`, { nodir: true }),
}),
]
复制代码
mini-css-extract-plugin会将css打包为一个单独的文件,这里咱们使用插件purgecss-webpack-plugin帮助咱们分析过滤未引用的css,最终结果以下(压缩有关的插件和配置参考第6小节):
Scope Hoisting 它可让webpack打包出来的代码文件更小,运行更快,它能够被称做为 "做用域提高"。
启用 Scope Hoisting的优势以下:
代码体积会变小,由于函数声明语句会产生大量代码。
代码在运行时由于建立的函数做用域减小了,因此内存开销就变小了。
plugins: [
// 开启 Scope Hoisting 功能
new webpack.optimize.ModuleConcatenationPlugin()
]
复制代码
在 mode production 状况下默认开启
咱们在开发环境进行验证,这里有两个文件:
// index.js
import a from './module-a'
console.log(a);
// module-a.js
export default 'module-a'
复制代码
当未启动Scope Hoisting时,按照文件模块打包,生成多个函数做用域,结果以下:
启用时则会将两个模块预编译到同一个模块中:
e.g.
// index.js
import _ from 'lodash'
console.log(_.cloneDeep({a: 'index'}))
// module.js
import _ from 'lodash'
console.log(_.cloneDeep({a: 'module'}))
复制代码
这里有两个文件中都引用了lodash这个第三方包,直接打包的话两个文件中都会包含lodash的代码:
可使用optimization.splitChunks来清除重复项:
optimization: {
splitChunks: {
chunks: 'all'
}
}
复制代码
清除重复后会将lodash生成到一个单独的chunk中:
当涉及到动态代码拆分时,可使用**import()**动态加载,或者webpack特有的require.ensure
// index.js
function getLodash() {
return import(/* webpackChunkName: "lodash" */'lodash').then(({ default: _ }) => {
return _.cloneDeep({a: 'index'})
}).catch(e => 'error occurred')
}
getLodash().then(res => {
console.log(res);
})
复制代码
这里咱们import()动态加载lodash,而且利用注释设置了chunkname,能够看到将动态引入的lodash单独拆分为一个文件:
更多详细介绍能够参考Code Splitting和SplitChunksPlugin
将框架包 vue、react、ui框架、sdk等框架包抽离放入cdn
externals: {
vue: "Vue",
}
复制代码
html 压缩 HtmlWebpackPlugin
js 压缩 UglifyJsPluginTerserWebpackPlugin
css 压缩 css-loader 内置了压缩,配合ExtractTextPluginMiniCssExtractPlugin
给css加上hash
e.g.
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new HtmlWebpackPlugin({
minify: {
collapseWhitespace: true,
removeComments: true,
},
}),
]
复制代码
能够看到html文件实现了压缩:
在 HtmlWebpackPlugin 3.x 中,minify为true时默认为{},此时须要手动配置,在4.x中则为
{
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
}
复制代码
详细参见:github.com/jantimon/ht… 关于具体配置项能够参考html-minifier
webpack 4.x 中默认使用TerserWebpackPlugin进行js压缩,原有的UglifyjsWebpackPlugin不推荐使用
关于TerserWebpackPlugin和UglifyJsPlugin,你须要知道如下几点:
在3.x中开发环境下配置以下:
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
plugins: [
new UglifyJsPlugin(),
]
复制代码
4.x开发环境中开启,production下minimize默认为true:
optimization: {
minimize: true,
}
复制代码
压缩前:
压缩后:
还能够在minimizer中新增TerserWebpackPlugin实例覆盖默认压缩:
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
test: /\.js(\?.*)?$/i,
}),
],
},
复制代码
将来在webpack5中将会引入内置的css minimizer,在4.x中须要手动配置 optimize-css-assets-webpack-plugin
手动配置 minimizer 会覆盖掉默认配置,所以须要补上TerserJSPlugin压缩js
MiniCssExtractPlugin则会提取css到单独的文件,配置以下:
const TerserPlugin = require('terser-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
optimization: {
minimizer: [
// 压缩js
new TerserJSPlugin({}),
// 压缩css
new OptimizeCSSAssetsPlugin({})
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"
]
}
]
}
复制代码
在import动态加载中配置preload和prefetch
预加载:
是一种 resource hint,用来指定页面加载后很快会被用到的资源,因此在页面加载的过程当中,咱们但愿在浏览器开始主体渲染以前尽早 preload。import(/* webpackPreload: true */ 'Modal');
复制代码
预取:
是一种 resource hint,用来告诉浏览器在页面加载完成后,利用空闲时间提早获取用户将来可能会访问的内容。import(/* webpackPrefetch: true */ 'Modal');
复制代码
两者的区别:
preload:
prefetch:
将hash替换为chunkhash,这样当chunk不变时,缓存依然有效
使用Name而不是id
每一个 module.id 会基于默认的解析顺序(resolve order)进行增量。也就是说,当解析顺序发生变化,ID 也会随之改变
Brotli 压缩算法具备多个特色,最典型的是如下 2 个:
brotil的支持状况 可查看caniuse
动态压缩即时发生。用户发出请求,压缩内容(当用户等待时)而且提供压缩内容。
静态压缩在用户请求以前在磁盘上压缩资产的时间。当用户请求资产时,不会发生压缩。预压缩资产只是从磁盘提供。
webpack提供的压缩方式compression-webpack-plugin ,brotli-webpack-plugin
gzip:
const CompressionWebpackPlugin = require('compression-webpack-plugin')
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8
})
复制代码
brotli:
const BrotliPlugin = require('brotli-webpack-plugin')
new BrotliPlugin({
asset: '[path].br[query]',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8
})
复制代码
1.5 nginx.conf
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_buffers 128 32k;
gzip_comp_level 9;
gzip_http_version 1.1;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml application/xml application/json text/javascript application/javascript application/x-javascript;
brotli on;
brotli_types text/plain text/css text/xml application/xml application/json text/javascript application/javascript application/x-javascript;
brotli_static off;
brotli_comp_level 11;
brotli_buffers 16 8k;
brotli_window 512k;
brotli_min_length 20;
复制代码
压缩工具:
也能够经过webpack压缩 imagemin-webpack-plugin
tinypng
imagemin
依赖:url-loader、file-loader、imagemin-webpack-plugin
const path = require('path')
const ImageminPlugin = require('imagemin-webpack-plugin').default
module.exports = {
...
module: {
rules: [
{
test: /\.(jpe?g|png|gif|svg)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: '[name].[hash:8].[ext]',
outputPath: 'imgs/',
},
},
],
},
],
},
plugins: [
new ImageminPlugin({ test: /\.(jpe?g|png|gif|svg)$/i }),
],
}
复制代码
不推荐在打包的时候经过webpack压缩,由于这样会大量增长打包的时间,而且imagemin这个插件的压缩能力远不如tinypng
将过大的背景图切分红多张小图,经过html的方式拼到一块儿。
利用浏览器的并发加载(h2)
网络请求 | 缓存 | 灵活性 | 其余 | |
---|---|---|---|---|
雪碧图 | 少 | 好 | 差 | 1.放大失真 2.同一图标,不一样颜色,会有重复部分 3.只用几张也须要加载整张图 |
iconfont | 少 | 好 | 好 | 1.矢量放大不失真 2.跟普通字体无异,能够设置size,color,opacity, 等 3.文件小 |
base64 | 无 | 差 | 好 | 1.增长额外的css、js大小 2.增长图片资源的大小 |
响应式图片不只仅指图片的排版和布局,还包括根据设备大小加载不一样的图片。
image:
经过设置image标签上的srcset告知浏览器在不一样屏幕宽度下加载不一样的图片
经过设置image标签上的sizes来设置图片的尺寸临界点,明肯定义了图片在不一样的media conditions下应该显示的尺寸。
picture:
浏览器会遍历 中的中
favicon.ico通常存放在网站根目录下,不管是否在页面中设置,浏览器都会尝试请求这个文件。
因此确保这个图标:
在不一样地域的用户请求资源(访问网站)的响应速度具备很大的差别,为了提升用户体验,咱们在用户和服务器中间加了一层,就是CDN。CDN(Content Delivery Network),它的思想就是将源站的内容分发到最接近用户的网络边缘节点,让用户可以就近取得所需的内容,提升用户访问的响应速度。
当用户发起HTTP请求时,经过CDN向边缘节点服务器发起请求,边缘节点会检测当前节点是否具备你想请求的数据,若是没有就去源站,若是有请求数据就会进一步判断,这个数据是否在有效期,根据是否过时来决定。
DNS解析(T1) -> 创建TCP链接(T2) -> 发送请求(T3) -> 等待服务器返回首字节(TTFB)(T4) ->> 接收数据(T5)
![]()
以chrome为例,http1.1的并发请求数量最大是6(同一域名下),超出的并发请求必须等待
**对于大资源:**是否合并对于加载时间没有明显影响,但拆分资源能够更好的利用浏览器缓存,不会由于某个资源的更新致使全部资源缓存失效,而资源合并后,任一资源的更新都会致使总体资源的缓存失效。另外还能够利用域名分片技术,将资源拆分部署到不一样域名下,既能够分散服务器的压力,又能够下降网络抖动带来的影响。
**对于小资源:**合并资源每每具备更快的加载速度,但在网络带宽情况良好的状况下,由于提高的时间单位以ms计量,收益能够忽略。若是网络延迟很大,服务器响应速度又慢,则能够带来必定收益,但在高延迟的网络场景下,又要注意合并资源后可能带来网络往返次数的增长,进而影响到加载时间。
浏览器对网站第一次的域名DNS解析查找流程依次为:浏览器缓存 -> 系统缓存 -> 本地hosts文件 -> 路由器缓存 -> ISP DNS缓存 -> 递归搜索
如今通常服务器都具有DNS缓存
在浏览器支持 DNS 预解析的特性时即便不使用该标签浏览器依然会进行预解析。
<meta http-equiv="x-dns-prefetch-control" content="on"> // 关闭 off
复制代码
<link rel="dns-prefetch" href="//domain.com">
复制代码
淘宝的预解析
注:dns-prefetch需慎用,多页面重复DNS预解析会增长重复DNS查询次数。
须要注意的是,虽然使用 DNS Prefetch 可以加快页面的解析速度,可是也不能滥用,由于有开发者指出 禁用DNS 预读取能节省每个月100亿的DNS查询 。
每一次的http请求都会默认携带上cookie,cookie过大的话会致使传输变慢
对于一些静态资源(图片)的获取,是不须要用到cookie,因此尽可能图片资源放入无cookie服务器,减小与主域名之间的cookie混用
http2采用二进制的传输协议,而http1.1采用的是文本传输,二进制格式的传输效率要比文本快的多。
同一个TCP链接中能够同时发送多个请求而不会阻塞
对消息头采用Hpack进行压缩传输,可以节省消息头占用的网络流量,http1.1每次请求,都会携带大量冗余的头信息,浪费了不少宽带资源。
它容许 Web 服务器在收到浏览器的请求以前提早发送一些资源给客户端
// html
<img src="" />
// js
var img = new Image();
img.src = "";
复制代码
虽然src属性为空字符串,但浏览器仍然会向服务器发起一个HTTP请求:
空src产生请求的后果不容小觑:
空的href属性也存在相似问题。用户点击空连接时,浏览器也会向服务器发送HTTP请求,能够经过JavaScript阻止空连接的默认的行为。
每一次的重定向都是须要从新再发起一次http请求
HTTP请求很昂贵,返回无效的响应(如404未找到)彻底不必,下降用户体验并且毫无益处。 一些网站设计很酷炫、有提示信息的404页面,有助于提升用户体验,但仍是浪费服务器资源。尤为糟糕的是外部脚本返回404,不只阻塞其余资源下载,浏览器还会尝试把404页面内容看成JavaScript解析,消耗更多资源。
由于IPv4即将用完以及主要的移动网络正在迅速采用IPv6(美国已经达到50% 的 IPv6 使用阈值),将你的 DNS 更新到 IPv6 以应对将来是一个好的想法。只要确保在网络上提供双栈支持,就可让 IPv6 和 IPv4 同时运行。毕竟,IPv6 不是向后兼容的。研究显示,也是正由于 IPv6 自带 NDP 以及路由优化,因此才可以让网站的载入速度提高10%到15%。
当客户端向服务器请求资源时,会先抵达浏览器缓存,若是浏览器有“要请求资源”的副本而且没有失效,就能够直接从浏览器缓存中提取而不是从原始服务器中提取这个资源。
常见的http缓存只能缓存get请求响应的资源,对于其余类型的响应则无能为力,因此后续说的请求缓存都是指GET请求。
http缓存都是从第二次请求开始的。第一次请求资源时,服务器返回资源,并在respone header头中回传资源的缓存参数;第二次请求时,浏览器判断这些请求参数,命中强缓存就直接200,不然就把请求参数加到request header头中传给服务器,看是否命中协商缓存,命中则返回304,不然服务器会返回新的资源。
强制缓存在缓存数据未失效的状况下(即Cache-Control的max-age没有过时或者Expires的缓存时间没有过时),那么就会直接使用浏览器的缓存数据,不会再向服务器发送任何请求。强制缓存生效时,http状态码为200。这种方式页面的加载速度是最快的,性能也是很好的,可是在这期间,若是服务器端的资源修改了,页面上是拿不到的,由于它不会再向服务器发请求了。这种状况就是咱们在开发种常常遇到的,好比你修改了页面上的某个样式,在页面上刷新了但没有生效,由于走的是强缓存,因此Ctrl + F5一顿操做以后就行了。 跟强制缓存相关的header头属性有(Pragma(http1.0)/Cache-Control(http1.1)/Expires)
当第一次请求时服务器返回的响应头中没有Cache-Control和Expires或者Cache-Control和Expires过时还或者它的属性设置为no-cache时(即不走强缓存),那么浏览器第二次请求时就会与服务器进行协商,与服务器端对比判断资源是否进行了修改更新。若是服务器端的资源没有修改,那么就会返回304状态码,告诉浏览器可使用缓存中的数据,这样就减小了服务器的数据传输压力。若是数据有更新就会返回200状态码,服务器就会返回更新后的资源而且将缓存信息一块儿返回。跟协商缓存相关的header头属性有(ETag/If-Not-Match 、Last-Modified/If-Modified-Since)请求头和响应头须要成对出现
协商缓存的执行流程是这样的:当浏览器第一次向服务器发送请求时,会在响应头中返回协商缓存的头属性:ETag和Last-Modified,其中ETag返回的是一个hash值,Last-Modified返回的是GMT格式的最后修改时间。而后浏览器在第二次发送请求的时候,会在请求头中带上与ETag对应的If-Not-Match,其值就是响应头中返回的ETag的值,Last-Modified对应的If-Modified-Since。服务器在接收到这两个参数后会作比较,若是返回的是304状态码,则说明请求的资源没有修改,浏览器能够直接在缓存中取数据,不然,服务器会直接返回数据。
实际开发中不可必变产生重绘和回流,咱们只能作到尽量的减小这种行为的发生
next,nuxt,等
首页骨架屏
css选择器的效率排序:
1.id选择器(#myid) 2.类选择器(.myclassname) 3.标签选择器(div,h1,p) 4.相邻选择器(h1+p) 5.子选择器(ul < li) 6.后代选择器(li a) 7.通配符选择器(*) 8.属性选择器(a[rel="external"]) 9.伪类选择器(a:hover, li:nth-child)
Front-End Performance Checklist 2019 [PDF, Apple Pages, MS Word]