扫描关注网站建设微信公众账号

微信二维码

当前位置:济南网站优化 > 技术洞察 > 建站 > 塞尼铁克高性能网站建设指南

塞尼铁克高性能网站建设指南

时间:2019-08-13•15:00:51文章来源:高性能网站建设文章作者:济南高性能网站建

  网站的划分一般分为两个部分:前端和后端。

  我们可以理解成后端是用来实现网站的功能的,比如:实现用户注册,用户能够为文章发表评论等等。

  而前端呢?其实应该是属于功能的表现。并且影响用户访问体验的绝大部分来自前端页面。

  而我们济南建设网站的目的是什么呢?不就是为了让目标人群来访问吗?所以我们可以理解成前端才是真正和用户接触的。除了后端需要在性能上做优化外,其实前端的页面更需要在性能优化上下功夫,只有这样才能给我们的用户带来更好的用户体验。

  那么我们应该如何对我们前端的页面进行性能优化呢?

  一般说来,前端包括浏览器加载、网站视图模型、图片服务、CDN 服务等,主要优化手段可以分为 7 类,包括减少请求数量、减小资源大小、优化网络连接、优化资源加载、减少重绘回流、使用性能更好的 API 和构建优化等方式。

  Yahoo Developer Network 提出了 14 条前端性能优化的规则,https://developer.yahoo.com/performance/rules.html?guccounter=1

  性能优化的 14 条规则

  规则 1:减少 http 请求数

  为什么要减少 http请 求数量呢?

  因为 http 建立连接是需要时间成本的,http 协议规定浏览器对于统一域名的资源请求数有限制。因此需要采取一些手段降低请求数量。

  高性能网站建设常见的减少 http 请求次数的方案又如下几个:

  1. 使用精灵技术

  css sprites 在国内很多人叫 css 精灵,是一种网页图片应用处理方式。它允许你将一个页面涉及到的所有零星图片都包含到一张大图中去,然后使用 css 中的background-position属性来进行定位即可。

  如果不了解 css 精灵技术,这里推荐一篇博文:https://www.cnblogs.com/shimily/articles/5539073.html

  2. 合并 js 以及 css 资源

  使用 webpack 模块打包工具,可以将繁琐却又互相依赖的 js、css 文件合并成一个。

  现在的很多前端框架的脚手架工具也是基于 webpack 来进行搭建的,当我们进行 build 构建时,会自动为我们将所有资源进行合并打包。

  3. 内联小图片

  当我们的页面中存在图片时,就会再次发送 http 请求来请求这张图片,例如,有如下的代码:

  ```html <!DOCTYPE html>

  ```

高性能网站建设指南

  在浏览器中打开该页面,到 Network 面板查看 http 请求,可以看到实际上发送了 2 次请求,并且可以看到每一次请求所花费的时间,如下图:

  使用 base 64 编码,可以将一张图片转换为一串字符串,这样会比请求一张图片的速度要快很多。一般来讲,我们只会将比较小的独立图标转换为 base 64 字符串。

  网上有很多在线图片转换和压缩的网站,随便选一个即可。我们将 heart.png 这张图片在线转换为了 base 64 字符串。

  (该工具站地址:https://www.sojson.com/image2base64.html)

  接下来在设置 background 时,url 中使用转换得到的字符串,如下图:

  重新请求页面,可以看到,这一次的请求时间为 0 ms,性能大大提升。

  规则 2:使用内容发布网络

  内容发布网络英文全称 Content Delivery Network,简称 CDN。

  使用 CDN 的原因在于用户距离我们的服务器可能十万八千里,为了提高资源的响应速度可以将资源放在距离用户较近的代理服务器上。

  最通俗的 CDN 解释:

  家住成都的小明在某宝上买了件衣服,卖家在广东,于是等呀等呀等快递,在下单后的第 4 天,他总算拿到了自己买的衣服。

  后来小明在某东上买了本书,由于某东在全国各地都有仓储和备货,于是货物直接从成都发出,小明当天就拿到书了。

  传统架构示意图如下:

  CDN 架构示意图如下:

  至于缺点,可能就是需要花点钱去购买 CDN 服务。

  规则 3:添加 Expires 头

  将静态资源缓存在用户本地,当用户二次访问时直接从本地获取资源,大大的降低了网络请求的成本。

  那么如何进行资源缓存呢?

  我们可以在 http 响应中添加 Expires 头部信息,对应的值包含日期和时间,即在此时间之后,响应过期。无效的日期,比如 0,代表着过去的日期,即该资源已经过期。

  如果在 http 响应中添加 Cache-Control 头部信息,对应的值为 max-age 或者 s-max-age,例如:

  js Cache-control: max-age=xxxxxxx

  那么 Cache-control 的优先级大于前者,Expires 头会被忽略。

  规则 4:压缩组件

  使用压缩能够减少资源的大小,进而减少网络响应的时间。目前 gzip 压缩使用的较为广泛。压缩脚本和样式表文件比较值得,不要压缩图片和 pdf 文件,它们本身已被压缩。

  下面是在 node.js 中进行文件压缩的示例代码:

  ```js const fs = require('fs'); // 引入文件模块 const zlib = require('zlib'); // 引入压缩模块

  const gzip = zlib.createGzip(); // 压缩文件创建的是 gzip 对象 // 创建一个可读流 , 读取当前目录下的 test.txt 文档 const inFile = fs.createReadStream('./test.txt'); // 创建一个可写流 , 将从流里面读取到的内容写入 test.txt.gz 文件 const out = fs.createWriteStream('./test.txt.gz'); // 通过可读流的 pipe() 方法 , 先传入 gzip 对象把可读流的文件压缩一下 , 然后在写入到可写流里面 // 其实就和之前的文件复制差不多 , 只不过使用 pipe() 写入之前先压缩一下 inFile.pipe(gzip).pipe(out); ```

  注:关于 node.js 中文件压缩的详细说明,可参阅 node.js 系列教程《资源压缩 zlib》

  规则 5:将样式表放在顶部

  该方案可以使的网页逐步呈现,用户体验会很好,感觉网站响应的比较快。不会出现闪烁。

  例如:

  ```html <!DOCTYPE html>

  ```

  规则 6:将脚本放在底部

  脚本的下载和解析会阻塞其他资源的并行下载。

  目前浏览器为 script 标签提供了两个新的属性 defer 和 async。两者都能实现资源的异步下载不会阻塞其他资源的下载。

  区别在于 defer 会等页面渲染完成后在执行,async 无法保证它执行的时间,下载完就执行。对比起来 defer 更具有实际使用价值。

  但是作为最佳方案,还是建议将脚本放在底部,如下:

  ```js <!DOCTYPE html>

  ```

  规则 7:避免使用 css 表达式

  什么是 css 表达式?

  IE 5 及其以后版本支持在 css 中使用 expression,用来把 css 属性和 Javascript 脚本关联起来,这里的 css 属性可以是元素固有的属性,也可以是自定义属性。就是说 css 属性后面可以是一段 Javascript 表达式,css 属性的值等于 Javascript 表达式计算的结果。

  那么为什么要避免使用 css 表达式呢?因为表达式里面所书写的实际上是 js 代码,同样会存在一个阻塞后面 css 渲染的问题,严重影响前端性能。

  规则 8:使用外部的 js 和 css 文件

  对于首次访问而言,内联形式的渲染速度会高于外链的形式。但是外部文件的形式是可以缓存的,所以当用户再次访问该页面时,就不用发送请求了,直接使用缓存即可。

  规则 9:减少 DNS 查询

  1. DNS 简单介绍

  DNS,英文全称 Domain Name System,负责将域名 URL 转化为服务器主机 IP。

  DNS 查找流程:首先查看浏览器缓存是否存在,不存在则访问本机 DNS 缓存,再不存在则访问本地 DNS 服务器。所以 DNS 查询也是一种开销,通常浏览器查找一个给定 URL 的 IP 地址要花费 20 - 120 ms,在 DNS 查找完成前,浏览器不能从 host 那里下载任何东西。

  TTL(Time To Live):表示查找返回的 DNS 记录包含的一个存活时间,过期则这个 DNS 记录将被抛弃。

  2. 影响 DNS 缓存的因素

  1. 服务器可以设置 TTL 值表示 DNS 记录的存活时间。本机 DNS 缓存将根据这个 TTL 值判断 DNS 记录什么时候被抛弃,这个 TTL 值一般都不会设置很大,主要是考虑到快速故障转移的问题。

  2. 浏览器 DNS 缓存也有自己的过期时间,这个时间是独立于本机 DNS 缓存的,相对也比较短,例如 chrome 只有 1 分钟左右。

  3. 浏览器 DNS 记录的数量也有限制,如果短时间内访问了大量不同域名的网站,则较早的 DNS 记录将被抛弃,必须重新查找。不过即使浏览器丢弃了 DNS 记录,操作系统的 DNS 缓存也有很大机率保留着该记录,这样可以避免通过网络查询而带来的延迟。

  3. 最佳实践

  当客户端的 DNS 缓存为空时,DNS 查找的数量与 Web 页面中唯一主机名的数量相等。所以减少唯一主机名的数量就可以减少 DNS 查找的数量。

  然而减少唯一主机名的数量会潜在地减少页面中并行下载的数量,避免 DNS 查找降低了响应时间,但减少并行下载可能会增加响应时间。当页面的组件量比较多的时候,可以考虑将组件分别放到至少 2 - 4 个主机名,以获得最大收益。

  规则 10:精简 js 代码

  删除注释、空格、将长变量名置换为短的变量名。也就是压缩和混淆了。css 代码也需要去除空格和注释。

  可以使用正则表达式来删除代码中的注释,一些前端构建工具也提供了删除注释、空格,精简代码的功能。

  规则 11:避免重定向

  1. 什么是重定向?

  重定向用于将用户从一个 URL 重新路由到另一个 URL。

  2. 常用重定向的类型

  301:永久重定向,主要用于当网站的域名发生变更之后,告诉搜索引擎域名已经变更了,应该把旧域名的的数据和链接数转移到新域名下,从而不会让网站的排名因域名变更而受到影响。

  302:临时重定向,主要实现 post 请求后告知浏览器转移到新的 URL。

  304:Not Modified,主要用于当浏览器在其缓存中保留了组件的一个副本,同时组件已经过期了,这是浏览器就会生成一个条件 GET 请求,如果服务器的组件并没有修改过,则会返回 304 状态码,同时不携带主体,告知浏览器可以重用这个副本,减少响应大小。

  3. 重定向如何损伤性能?

  浏览器做重定向这个操作是需要时间的。当页面发生了重定向,就会延迟整个 HTML 文档的传输。在 HTML 文档到达之前,页面中不会呈现任何东西,也没有任何文件会被下载。因此在做性能优化时,重定向也是一个必要的考虑因素。

  规则 12:删除重复的脚本

  随着项目规模的扩大及参与人员的增多,可能会出现重复引入资源的情况。这也是会降低前端性能的。但是在这个前端工程化的年代,打包工具会帮我们处理好该问题。

  这里推荐 Rollup 打包工具。Rollup 是一个 JavaScript ES 6 模块打包器。与 Browserify 和 Webpack 不同,Rollup 只包含在项目中用到的代码。如果有大模块,带有很多函数,但是你只是用到少数几个,Rollup 只会将需要的函数包含到打包文件中,从而显著减少打包文件大小。

  规则 13:配置 ETag

  首先需要介绍一下什么是 ETag。

  来自于百度百科的解释:

  HTTP 协议规格说明定义 ETag 为“被请求变量的实体值”。另一种说法是,ETag 是一个可以与Web资源关联的记号(token)。

  典型的 Web 资源可以一个 Web 页,但也可能是 JSON 或 XML 文档。服务器单独负责判断记号是什么及其含义,并在 HTTP 响应头中将其传送到客户端。

  例如服务器端返回的格式:ETag:"50b1c1d4f775c61:df3",客户端的查询更新格式是这样的:If-None-Match : W / "50b1c1d4f775c61:df3",如果 ETag 没改变,则返回状态 304 然后不返回资源。

  来看一个具体的示例,首先我们使用 Express 快速搭建一个服务器,代码如下:

  ```js // app.js

  const express = require('express'); const app = express();

  app.get('/', (req, res) => { res.send('hello http') })

  app.listen(3000, () => { console.log('The server is running at http://127.0.0.1:3000/') }) ```

  接下来在浏览器中进行访问,可以看到,我们正常的拿到了资源。但是仔细观察 http 响应头,里面有一个 ETag 属性,对应了一个值,这就是一个标记。

  如下图所示:

  当我们再次刷新页面的时候,http 请求中会存在 If-None-Match 这个字段,这里就会把上一次从服务器端带回来的 Etag 发送回服务器端,两个 ETag 值进行对比,发现没有发生改变,则直接返回 304 状态码。

  如下图所示:

  只要我们修改服务器端的文件内容,这个 Etag 就会发生改变,我们将 app.js 进行如下的修改:

  ```js // app.js

  const express = require('express'); const app = express();

  app.get('/', (req, res) => { // res.send('hello http') res.send('hello world') })

  app.listen(3000, () => { console.log('The server is running at http://127.0.0.1:3000/') }) ```

  接下来重新请求页面,可以看到,请求头仍然会把上一次的 ETag 值带过去,但是由于服务器端的内容已经发生了改变,所以服务器端又生成了新的 ETag 值。

  两者一对比,发现 ETag 值不同了,此时就会返回新的资源。

  如下图所示:

  总结一下ETag:

  浏览器会根据 HTTP 请求的 ETag 验证请求的资源是否发生了改变,如果它未发生变化,服务器将返回“304 Not Modified”响应,并且从浏览器缓存中读取资源,这样就不必再返回相同的资源了。

  ETag 和 Last-Modified 以及 Expire 的区别?

  先来看一下 ETag 和 Last-Modified 的区别。经常有人看到 Last-Modified 的请求头字段,它表示的是服务器上此文件最后一次修改的时间。

  浏览器会向服务器传送 If-Modified-Since 报头,询问该时间之后文件是否有被修改过,如果未修改过,那么直接就去取缓存,不用再次去访问服务器获得此资源。听起来是不是和上述的 Etag 差别不太大?

  没错,Etag 的出现就是要解决 Last-Modified 不能解决的一些问题,算是 Last-Modified 的升级版。

  来看看 Last-Modified 不能解决的下列问题:

  · 周期性更改的文件且内容并不改变(仅仅改变的修改时间)。

  · 有些文件修改极其频繁,也许 1 秒内修改了很多次,If-Modified-Since 能检查到的最小单位是秒级的,所以这种修改无法判断。

  · 不能精确得到文件的最后修改时间。

  · HTTP 1.1 之后就出现了升级版的 Etag 来解决上述 Last-Modified 不能解决的问题。

  接下来回顾一下上面所提到过的 Expire,它是 HttpHeader 中代表资源的过期时间,由服务器段设置。如果带有 Expire,则在 Expire 过期前不会发生 Http 请求,直接从缓存中读取。用户强制 F5 例外。

  通常来讲,Last-Modified,Etag,Expire 是一起混合使用的,特别是 Last-Modified 和 Expire 经常一起使用,因为 Expire 可以让浏览器完全不发起 http 请求,而当浏览器强制 F5 的时候又有 Last-Modified,这样就很好的达到了浏览器段缓存的效果。

  Etag 和 Expire 一起使用时,先判断 Expire,如果已经过期,再发起 http 请求,如果 Etag 也过期,则返回 200 响应。如果 Etag 没有过期则返回 304 响应。

  Last-Modified,Etag,Expires 三个同时使用时。先判断 Expire,然后发送 http 请求,服务器先判断 last-modified ,再判断 Etag,必须都没有过期,才会返回 304 响应。

  规则 14:使用 Ajax 缓存

  可以通过 http 的响应和请求头控制 ajax 是否需要缓存。

  什么情况下 ajax 请求会出现缓存?

  当请求的路径、参数名、参数值三者都没有发生变化时,浏览器将不会再发送此请求,只有这三者其中任意一个发生变化时(变化的一般只有参数值),浏览器才会再次向服务器发送请求!

  例如:

  js http://localhost:8070/demo/getJson.do?operation= 请求发送至服务器 http://localhost:8070/demo/getJson.do?operation= 没有发送请求,使用的缓存 http://localhost:8070/demo/getJson.do?operation=1 请求发送至服务器 http://localhost:8070/demo/getJson.do?operation=1 没有发送请求,使用的缓存

  缓存这个东西呢,用好了就是天使,用不好就是魔鬼。

  有几种方式可以禁用缓存:

  1. 设置 ajax 禁用缓存

  js $.ajaxSetup({"cache":false})

  注:这个方法调用需要放在发送 ajax 请求之前。

  也可以在发送 ajax 请求时,设置 cache 的值为false,例如:

  js $ajax({ ... cache : false, ... })

  2. 添加请求参数

  我们可以在请求的 url 后面添加一个随机的参数,这样每次都会重新发送请求,例如:

  js ×tamp=new Date().getTime();

  注:使用时间戳要优于使用随机数,因为随机数有可能出现数字一样的情况。

  总结

  性能优化,是提升用户体验的一大法宝。在软件开发中,无论是操作系统、游戏、还是我们的前端开发,性能优化都扮演着相当重要角色。

  本文主要介绍了 Yahoo Developer Network 所提出的 14 条前端性能优化的规则,在实际开发中,不见得每一条都必须做到,但是当你的网站呈现在用户面前显得很慢的时候,你就可以考虑从上面所介绍的规则中着手来进行优化。

  有关性能优化,这里再推荐一篇博文:https://www.cnblogs.com/xiaohuochai/p/9178390.html

上一篇:详解下网站建设要满足浏览体验的要求会更好?

下一篇:塞尼铁克建站案例之展示型网站制作细节控制

文章关键词
网站建设
性能优化
页面性能优化
网站建设指南