Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

性能优化(网络方向) #44

Open
adodo0829 opened this issue Apr 29, 2020 · 0 comments
Open

性能优化(网络方向) #44

adodo0829 opened this issue Apr 29, 2020 · 0 comments

Comments

@adodo0829
Copy link
Owner

性能优化(网络方向)

web应用无非是两台主机之间互相传输数据包的一个过程; 如何减少传输过程的耗时就是网络方向优化的重点, 优化出发点从第一篇文章中说起

DNS解析过程的优化

当浏览器从第三方服务跨域请求资源的时候,在浏览器发起请求之前,这个第三方的跨域域名需要被解析为一个IP地址,这个过程就是DNS解析;
DNS缓存可以用来减少这个过程的耗时,DNS解析可能会增加请求的延迟,对于那些需要请求许多第三方的资源的网站而言,DNS解析的耗时延迟可能会大大降低网页加载性能。

  • dns-prefetch
    当站点引用跨域域上的资源时,都应在元素中放置dns-prefetch提示,但是要记住一些注意事项。首先,dns-prefetch仅对跨域域上的DNS查找有效,因此请避免将其用于您当前访问的站点
<link rel="dns-prefetch" href="https://fonts.googleapis.com/">
  • preconnect
    由于dns-prefetch仅执行DNS查找,但preconnect会建立与服务器的连接。如果站点是通过HTTPS服务的,则此过程包括DNS解析,建立TCP连接以及执行TLS握手。将两者结合起来可提供机会,进一步减少跨源请求的感知延迟
<!-- 注意顺序, precontent和dns-prefetch的兼容性 -->
<link rel="preconnect" href="https://fonts.googleapis.com/" crossorigin>
<link rel="dns-prefetch" href="https://fonts.googleapis.com/">

TCP传输阶段优化

这个前端方面好像能做的有限, 我们都知道 http协议 是基于 tcp的;
升级http协议版本可以考虑下, 比如把 http/1.0 -> http/1.1 -> http/2;
这个需要我们在应用服务器上配置(nginx, Apache等), 不做概述了, 另外还需要客户端和服务器都支持哦, 目前还没开发出稳定版本,好多只支持https,不过也不远了...

  • http2 的优势
#  1.多路复用: 同一个tcp连接传输多个资源
这样可以突破统一域名下只允许有限个tcp同时连接, 
这样http1.1所做的减少请求数优化就没有太大必要了
如多张小图合成一张大图(雪碧图),合并js和css文件

# 2.报文头压缩和二进制编码: 减少传输体积
http1 中第一次请求有完整的http报文头部,第二次请求的也是;
http2 中第一次请求有完整的http报文头部,第二次请求只会携带 path 字段;
这样就大大减少了发送的量。这个的实现要求客户端和服务同时维护一个报文头表。

# 3.Server Push
http2可以让服务先把其它很可能客户端会请求的资源(比如图片)先push发给你,
不用等到请求的时候再发送,这样可以提高页面整体的加载速度
但目前支持性不太好...emm...

总的来说, 在 c 端业务下不会太普及, 毕竟需要软件支持才行...

http 请求响应阶段优化

为了让数据包传输的更快, 我们可以从两个方面入手: 请求的数据包大小(服务器), 请求数据包的频率(客户端)

减少请求文件的大小

请求文件对应的是我们项目完成后,打包所指的静态资源文件(会被部署到服务器), 文件越小, 传输的数据包也会相对较小, 讲道理也会更快到达客户端

how to reduce a package size?

目前我们都会使用打包工具了(比如webpack, rollup, glup 等), 如何使用工具来减小包的体积呢? 这边建议您去官网文档呢...当然这里列举一下常用的手段(webpack 的), 但是注意要插件版本更新哦

  • JS文件压缩(
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
 plugins: [
   new UglifyJsPlugin({
     // 允许并发
     parallel: true,
     // 开启缓存
     cache: true,
     compress: {
       // 删除所有的console语句    
       drop_console: true,
       // 把使用多次的静态值自动定义为变量
       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 文件关闭
  • tree shaking
1.代码不会被执行,不可到达,比如 if(false){// 这里边的代码}
2.代码执行的结果不会被用到
3.代码只会影响死变量(只写不读)
4.方法不能有副作用

// 原理相关: 以后在研究
利用 ES6 模块的特点: 
  只能作为模块顶层的语句出现
  import 的模块名只能是字符串常量
  import binding 是 immutable 的
代码擦除: uglify 阶段删除无用代码
  • scope hoisting(作用域提升)
    分析出模块之间的依赖关系,尽可能的把打散的模块合并到一个函数中去,但前提是不能造成代码冗余
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
module.exports = {
  resolve: {
    // 针对 Npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件
    mainFields: ['jsnext:main', 'browser', 'main']
  },
  plugins: [
    // 开启 Scope Hoisting
    new ModuleConcatenationPlugin(),
  ],
};
  • 项目中使用按需加载,懒加载(路由,组件级)
const router = new VueRouter({
  routes: [
    { path: '/foo', component: () => import(/* webpackChunkName: "foo" */ './Foo.vue') }
    { path: '/bar', component: () => import(/* webpackChunkName: "bar" */ './Bar.vue') }
  ]
})
  • 开启 gizp 压缩
有时候启用也会消耗服务器性能, 看情况使用吧

暂时先提这么些吧...后续想到了再加

减少请求频率

因为同一域名下 tcp 连接数的限制导致过多的请求会排队阻塞, 所以我们需要尽量控制请求的数量和频率

常见措施
  • 将静态资源的内联到HTML中
    这样这些资源无需从服务器获取, 但可能影响到渲染进程...
<!-- 1.小图片内联 base64 (url-loader) -->
<!-- 2.css内联 -->
<!-- 3.js内联 -->
<script>
  ${require('raw-loader!babel-loader!./node_modules/lib-flexible/flexible.js')}
</script>
  • 利用各级缓存(下一篇存储方面介绍)
    通常都是在服务端做相关配置, 但你要知道
我们可以利用http缓存(浏览器端)来减少和拦截二次请求, 当然一般都是在服务端设置的;
服务器端也可以设置缓存(redis等), 减少数据查询的时间同样可以缩短整个请求时间
  • 利用本地存储
我们可以将常用不变的信息存在本地(cookie,storage API 等);
判断存在就不去请求相关的接口, 或者定期去请求也是可以的
  • 花钱买 CDN 加速
    CDN 又叫内容分发网络,通过把资源部署到世界各地,用户在访问时按照就近原则从离用户最近的服务器获取资源,从而加速资源的获取速度。 CDN 其实是通过优化物理链路层传输过程中的网速有限、丢包等问题来提升网速的...
购买 cdn 服务器;
然后把网页的静态资源上传到 CDN 服务上去,
在请求这些静态资源的时候需要通过 CDN 服务提供的 URL 地址去访问;

# 注意, cdn 缓存导致的新版本发布后不生效的问题
所以打包的时候常在文件后面加上 hash 值
然后在 HTML 文件中的资源引入地址也需要换成 CDN 服务提供的地址
/alicdn/xx12dsa311.js

# 利用不同域名的 cdn 去存放资源, (tcp连接限制)
  • webpack 构建时添加 cdn
// 静态资源的导入 URL 需要变成指向 CDN 服务的绝对路径的 URL 而不是相对于 HTML 文件的 URL。
// 静态资源的文件名称需要带上有文件内容算出来的 Hash 值,以防止被缓存。
// 不同类型的资源放到不同域名的 CDN 服务上去,以防止资源的并行加载被阻塞。
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: [
      {
        // 增加对 CSS 文件的支持
        test: /\.css$/,
        // 提取出 Chunk 中的 CSS 代码到单独的文件中
        use: ExtractTextPlugin.extract({
          // 压缩 CSS 代码
          use: ['css-loader?minimize'],
          // 指定存放 CSS 中导入的资源(例如图片)的 CDN 目录 URL
          publicPath: '//img.cdn.com/id/'
        }),
      },
      {
        // 增加对 PNG 文件的支持
        test: /\.png$/,
        // 给输出的 PNG 文件名称加上 Hash 值
        use: ['file-loader?name=[name]_[hash:8].[ext]'],
      },
      // 省略其它 Loader 配置...
    ]
  },
  plugins: [
    // 使用 WebPlugin 自动生成 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`,
    }),
    // 省略代码压缩插件配置...
  ],
};
/*
以上代码中最核心的部分是通过 publicPath 参数设置存放静态资源的 CDN 目录 URL,
为了让不同类型的资源输出到不同的 CDN,需要分别在:

output.publicPath 中设置 JavaScript 的地址。
css-loader.publicPath 中设置被 CSS 导入的资源的的地址。
WebPlugin.stylePublicPath 中设置 CSS 文件的地址。
设置好 publicPath 后,WebPlugin 在生成 HTML 文件和 css-loader 转换 CSS 代码时,会考虑到配置中的 publicPath,用对应的线上地址替换原来的相对地址。
*/

参考

DNS MDN]
webpack 文档
深入浅出 Webpack
Scope Hoisting

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant