前后端分离了,然后呢?(什么前后端分离)
815
2022-07-27
前言
我们将通过一个完整的实例, 一步步的优化加载, 渲染等各方面的体验.
开始
首先我们先看一下项目的文件构成
这之中包含了一个基本网页的元素, js(React App), css, 还有图片.
我们先来看一下来serve整个网页的部分.
server.js
'use strict'; const fs = require('fs'); const path = require('path'); const koa = require('koa'); const app = koa();
app.use(function* (next) { const file = this.path.slice(1) || 'index.html'; try { const content = yield cb => fs.readFile(path.resolve('./dist', file), cb); this.body = content; this.type = path.extname(file).slice(1); this.status = 200;
} catch (e) { this.status = 404;
} yield next;
});
app.listen(process.env.PORT || 3000);
这段代码只是简单的将 dist 目录下的文件给转发一下.
打开网页便可以看到相关加载情况.
我们可以看到, 整个 app.js 共277kb, 在模拟3G网络的情况下(蓝色框框),每次加载需要花费999ms, 其中-花费了911ms(红色框框).
接下来我们将逐步优化, 然后每次将结果进行比较.
优化(一) --- 304
网页加载优化中最常见的就是 304 Not Modified 了, 具体机制是浏览器发起请求, headers中包含 If-Modified-Since ,(如无缓存, 则无此头字段), 服务器对比硬盘上(或内存中)文件最后修改的时间, 如果小于或等于请求的时间, 则返回304. 否则, 则返回200, 并加上 Last-Modified 字段, 告诉客户端下次请求可以尝试请求是否有缓存.
具体代码如下:
app.use(function* () { const file = path.resolve(__dirname, path.resolve('dist', this.path.slice(1) || 'index.html')); const headers = this.headers; let ifLastModified = this.headers['if-modified-since']; if (ifLastModified) {
ifLastModified = new Date(ifLastModified);
} try { const stat = yield cb => fs.stat(file, cb); const now = Date.now(); if (ifLastModified &&
file !== path.resolve(__dirname, path.resolve('dist/index.html'))) { if (ifLastModified >= stat.mtime) { this.status = 304; return;
}
} console.log(file) const content = yield cb => fs.readFile(file, cb); this.body = content; this.type = path.extname(file).slice(1); this.status = 200; this.set('Last-Modified', stat.mtime);
} catch (e) { this.status = 404;
}
});
(模拟实际情况中, 首页会动态生成, 加入一些广告,追踪或个性化数据, index.html 并未缓存)
最终效果:
我们可以看见, -时间为2ms, 可以几乎忽略掉(只有HTTP Headers), 总共的加载时间也只有了120ms, 相比之前, 整整少了 869ms.
但是, 我们满足了吗?
优化(二) --- 分别打包
我们可以注意到, 我们打包出来最终只有一个js文件, 当依赖变多后(此例中只有react和react-dom, 每次修改都导致整个js文件被重新请求.所以我们想要把不同的library(甚至是项目内部公用的代码模块)提取出来.
我们首先要创建一个 webpack.vendors.config.js 来构建这些library, 或者vendor.
const path = require('path'); const WebpackCleanupPlugin = require('webpack-cleanup-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); const ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = {
plugins: [ new webpack.DefinePlugin({ 'process.env': {
NODE_ENV: '"production"',
},
}), new webpack.optimize.OccurenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
screw_ie8: true,
drop_console: true,
drop_debugger: true,
},
}), new webpack.DllPlugin({
path: path.resolve(__dirname, 'dist/vendor/[name]-manifest.json'),
name: '[name]',
context: '.',
}),
],
devtool: 'hidden-source-map',
entry: { 'react': ['react', 'react-dom'],
},
output: {
path: path.resolve(__dirname, 'dist/vendor'),
filename: '[name].js',
library: '[name]',
},
};
注意到
entry: { 'react': ['react', 'react-dom'],
},
意味着我们可以将同一类型的包打包成一个js文件.
当然, 我们也要对 webpack.production.js 做一些修改.
const dlls = fs.readdirSync(path.resolve(__dirname, 'dist/vendor/'))
.filter(file => path.extname(file) === '.js')
.map(file => path.basename(file, '.js')) const dllReferencePlugins = dlls
.map(dll => new webpack.DllReferencePlugin({
context: '.',
manifest: require(`./dist/vendor/${dll}-manifest.json`),
})
); module.exports = {
plugins: dllReferencePlugins.concat([
...
]),
...
}
在这, 我们将自动扫描 vendor 目录下面的文件, 自动将所有的vendor加载进来.
这样我们就实现了分包加载(还有一些细节的修改, 包括 index.html , 请参见github上, step-2 分支)
效果还是不错的, app.js 单独加载只需要400多ms, 比起所有依赖一起加载要快了至少一半以上.
对于一般类型网站, 优化到这已经可以取得非常不错的效果了, 但是对于大型网站来说, 我们可以做的还有很多.
优化(三) --- 强制缓存
我们可以注意到优化一种, 一个304的请求仍然花掉了100多毫秒, 对于大型网站, 资源特别多的情况, 这仍然是一个不小的开支. 那我们可以把这个省掉吗? 答案是可以的.
浏览器缓存当中, 还有一个特别的字段. Expires , 它可以指定文件的过期时间, 直到那一刻位置, 浏览器都不会再重新发起请求, 而是直接从本地缓存中读取.
但是, 这仍旧需要每隔一段时间去请求. 我们该如何做呢? 答案就是, 设置超长的缓存时间, 例如10年. 但是这样我们便无法更新任何内容了. 我们该如何用到这样的特性, 而又很方便的更新呢.
我们可以给文件名加上 hash特征值 , 这样只有当文件内容有改动时, 才会重新加载, 而且这样适合于分布式CDN的, 非覆盖式的发布, 可以使其在引用页面(首页)已经改变的情况下(当前服务器已经发布), 才会用到新资源, 而访问到未发布的服务器时, 还是会引用老的资源, 使得发布再也不需要熬夜
具体细节改动见git branch step-3.
实现效果:
可以从蓝色方框出看见, 缓存已经生效, 而整体的读取时间才只有20毫秒不到.
从原始的1000毫秒, 到现在的20毫秒, 简简单单的三个步骤便可以让你的网页加载提速50倍
扩展阅读
1.在实际生产中, 我们通常看到的是加载的CDN域名, 这是为何呢?
这是因为, 一个大型的网站, 请求当中会带上很多Cookie, 有的甚至于接近1KB, 而100个图片的加载, 就是整整100KB. 通过第三方域名(不同于当前域名), 我们可以节省掉许多不必要的请求头, Cookie头. 同样达到提速的目的
2.还有一种情况是, 资源分布在不同的服务器上
这是因为浏览器对于同一域名下资源的并行-数量有限制.
使用不同的资源服务器可以避开这种限制, 加大-并发数. 但是, 这样同样带来的缓存命中率的问题, 所以还需要存储用户缓存相关的数据. 合理的利用下, 对于页面整体的加载速度还是很有好处的.
3.其他的方法
在技术飞速发展的当下, 还有很多技术都是可以对终端用户的体验带来提升的.
BigPipe + Server-Side Rendering, 加速首页加载速度
Goole AMP
HTTP/2
来自:http://tech.dianwoda.com/2016/11/01/web-load-optimization-step-by-step/
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~