用 Webpack 解决应用性能问题
1. 影响页面加载时长的 Top3 因素
- 页面初载时,加载大量 JavaScript 脚本;
- 页面初载时,加载大量 CSS 文件;
- 页面初载时,加载大量网络资源;
页面加载的越久,页面不可交互的时间就越长,用户的体验就越差。
2. 页面优化的目标
- 页面初载时,未压缩的 JavaScript 脚本大小: <=200KB ;
- 页面初载时,未压缩的 CSS 资源大小: <=100KB ;
- HTTP 协议下,请求资源数: <=6 个 ;
- HTTP/2 协议下,请求资源数: <=20 个 ;
- 90% 的代码覆盖率(仅允许 10% 的未使用代码);
只有 Chrome 能够查看你的代码覆盖率。
遵从这个目标,应用将能在任何平台(PC,Mobile Phone...)都拥有良好的性能:
- 未来的网络世界在移动端;
- 平均每个移动端网站需要花费 14 秒时间达到可交互状态;
- 加载的代码越少,页面达到可交互的时间越短;
3. 查看代码覆盖率
- 打开 Chrome Dev Tool;
- 按下
Cmd + Shift + PorCtrl + Shift + P; - 输入
Coverage,选择第一个出现的选项:

- 点击面板上的
reload按钮,查看整个应用 JavaScript 的代码覆盖率:

4. 如何做到?
(1)代码分割(code splitting)
什么是 code splitting?
将部分代码在构建时转变为异步加载的过程。
(1.1)代码分割原理
代码分割的核心是异步加载资源,而异步加载功能使用到 stage 3 规范:whatwg/loader。import() 允许你在浏览器端运行时动态获取资源,虽然它存在以下一些问题:
- 安全问题;
- 浏览器支持问题;
(1.2)代码分割的两种类型
- 静态的;
- “动态的”(在 webpack 中,并不是真的动态):“动态”是指你能够在代码运行时决定应该引入什么 JavaScript 模块;
(1.3)静态代码分割
何时使用静态的代码分割?
- **你正在使用一个非常大的库或框架 **:如果在页面初始化时你不需要使用它,就不要在页面初载时加载它;
- **任何临时的资源 **:指不在页面初始化时被使用,被使用后又会立即被销毁的资源,例如模态框,对话框,tooltip等(任何一开始不显示在页面上的东西都可以有条件的加载);
- 路由 :既然用户不会一下子看到所有页面,那么只把当前页面相关资源给用户就是个明智的做法;
代码示例
import Listener from './listeners.js'
const getModal = () => import('./src/modal.js')
Listener.on('didSomethingToWarrentModalBeingLoaded', () => {
// Async fetching modal code from a separate chunk
getModal().then((module) => {
const modalTarget = document.getElementById('Modal')
module.initModal(modalTarget)
})
})
注意:
- 在 Vue 中,可以直接使用
import()关键字,在 React 中,使用react-loadable完成同样的事; - 每当你使用动态代码分割时,函数将返回一个 Promise 对象;
(1.4)“动态”代码分割
何时使用“动态”代码分割?
- **A/B Test **:你不需要在代码中引入不需要的 UI 代码;
- **主题 **:动态加载相应的主题;
- 为了方便 :本质上,你可以用静态代码分割代替“动态”代码分割,但是后者比前者拥有更少的代码量;
代码示例
const getTheme = (themeName) => import(`./src/themes/${themeName}`)
// using `import()` 'dynamically'
if (window.feeling.stylish) {
getTheme('stylish').then((module) => {
module.applyTheme()
})
} else if (window.feeling.trendy) {
getTheme('trendy').then((module) => {
module.applyTheme()
})
}
这背后的原理是,webpack 将目录中所有可以分离的 JavaScript 文件都生成了被称为 contextModule 的模块,所以本质上仍然是静态的。
(2)魔术注释
魔术注释技术是为代码分割技术服务的,它表现为在 import 关键字前使用指定注释来控制 webpack 生成的分割后代码片段。
代码示例
import (
/* webpackChunkName: "my-chunk-name" */
'./footer'
)
同时,你需要在 webapck.config.js 中添加配置:
{
output: {
filename: "bundle.js",
chunkFilename: "[name].lazy-chunk.js"
}
}
(2.1)webpack Modes
webpack 提供了一些注释让我们能够选择异步加载的模块应该被怎样组织:
import (
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: lazy */
'./someModule'
)
webpackMode 的默认值为 lazy ,指所有异步模块都会被单独抽离成单一的 chunk,通过使用 lazy-once 值,可以将所有的异步加载模块放在同一个 chunk 中。
(2.2)Prefetch & Preload
通过开启 prefetch ,我们可以通过使用 <link rel="prefetch>" 的特性,让浏览器在空闲时帮我们预先加载我们的异步资源,这有助于提升应用性能。
import(
/* webpackPrefetch: true */
'./someModule'
)
注意 :当你确保你的异步代码在未来一定会用到时,再开启该功能。
5. 参考链接
- https://developers.google.com/web/fundamentals/performance/optimizing-javascript/code-splitting/?hl=zh-cn
- https://webpack.js.org/guides/code-splitting/
- https://webpack.js.org/api/module-methods/#magic-comments
- https://medium.com/webpack/link-rel-prefetch-preload-in-webpack-51a52358f84c
用 Webpack 解决应用性能问题的更多相关文章
- 第二十三节: EF性能篇(三)之基于开源组件 Z.EntityFrameWork.Plus.EF6解决EF性能问题
一. 开篇说明 EF的性能问题一直以来经常被人所吐槽,究其原因在于“复杂的操作在生成SQL阶段耗时长,且执行效率不高”,但并不是没有办法解决,从EF本身举几个简单的优化例子: ①:如果仅是查询数据,并 ...
- Webpack——解决疑惑,让你明白
Webpack——解决疑惑,让你明白 极客教程 作者 关注 2016.09.29 17:46* 字数 2868 阅读 22204评论 22喜欢 80 Webpack是目前基于React和Redux开发 ...
- 【译文】使用webpack提高网页性能优化
这篇文章原文来自https://developers.google.com/web/fundamentals/performance/webpack/. 说是译文其实更像是笔者做的笔记,如有错误之处请 ...
- 解决Linux性能问题的前60秒
为了解决性能问题,你登入了一台Linux服务器,在最开始的一分钟内需要查看什么? 在Netflix我们有一个庞大的EC2 Linux集群,还有非常多的性能分析工具来监控和调查它的性能.其中包括用于云监 ...
- webpack之前端性能优化(史上最全,不断更新中。。。)
最近在用webpack优化首屏加载性能,通过几种插件之后我们上线前后的速度快了一倍,在此就简单的分享下吧,先上个优化前后首屏渲染的对比图. 可以看到总下载时间从3800ms缩短到1600ms. 我们在 ...
- webpack 解决 semantic ui 中 google fonts 引用的问题
semantic ui css 的第一行引用了 google web font api,由于不可告人而又众所周知的原因,这条链接在国内无法访问: @import url('https://fonts. ...
- [转] webpack之前端性能优化(史上最全,不断更新中。。。)
最近在用webpack优化首屏加载性能,通过几种插件之后我们上线前后的速度快了一倍,在此就简单的分享下吧,先上个优化前后首屏渲染的对比图. 可以看到总下载时间从3800ms缩短到1600ms. 我们在 ...
- webpack 解决跨域问题
一.webpack 内置了 http-proxy-middleware 可以解决 请求的 URL 代理的问题 安装:npm install --save http-proxy-middleware 二 ...
- webpack解决单页面路由问题
index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...
随机推荐
- sql四种连接方式
1.内连接(inner join)只有两个表相匹配的行才能在结果集中显示出来2.左连接(left join)以左表为主,左表所有的数据都会在结果集中出现,右表根据左表对应的数据显示,与左表匹配的数 ...
- web开发(十) struts2之图片验证码
1.配置前端页面 <!-- 验证码--> <div class="form-group " style="padding-left: 9%;" ...
- Linux_基础指令
目录 目录 前言 cd和pwd ls cat du mkdir touch rm cp mv which whereis find ln head和tail wc tar vim useradd 添加 ...
- "fatal error LNK1169: 找到一个或多个多重定义的符号" 解决方案
本人在测试刚刚安装的vs2017时运行出了问题, 错误信息为 "fatal error LNK1169: 找到一个或多个多重定义的符号", 代码如下: //Myfile.h #in ...
- 【VS开发】【图像处理】Pleora推出iPORT CL-U3外置抓帧器
全球领先的高性能视频接口产品供应商Pleora科技公司近日宣布推出可将Camera Link®摄像头转化为USB3Vision™摄像头的首个产品iPORT CL-U3外置抓帧器,树立了另一个行业里程碑 ...
- 数据库SQL语句查询指定时间段内的数据
[摘要]有的时候,我们需要查询数据库某段时间之间的数据,比如2016年5月1号到到5月3号之间用户注册数量(特殊节假日期间)等.那么用SQL语句如何实现呢? 首先,数据表中的存时间的字段比如是addt ...
- python 并发编程 多线程 开启线程的两种方式
一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性 二 开启线程的两种方式 第一种 每造一个进程,默认有一个线程,就是 ...
- c++ release和debug语句分离
#ifdef _DEBUG a=1; #else a=2; #endif
- Hbase 0.92.1集群数据迁移到新集群
老集群 hbase(main):001:0> status 4 servers, 0 dead, 0.0000 average load hbase(main):002:0> list T ...
- HDU1846 Brave Game 题解
题面 本题是一道有向图博弈问题: 该题便是著名的巴什博弈: 我们可以发现,当n=0的时候后手必胜(设其为P态),n=1~m这几种状态由于先手可以一次全部取完导致先手必胜(设其为N态). 接着当n=m+ ...