用 Webpack 解决应用性能问题

1. 影响页面加载时长的 Top3 因素

  1. 页面初载时,加载大量 JavaScript 脚本;
  2. 页面初载时,加载大量 CSS 文件;
  3. 页面初载时,加载大量网络资源;

页面加载的越久,页面不可交互的时间就越长,用户的体验就越差。javascript


2. 页面优化的目标

  1. 页面初载时,未压缩的 JavaScript 脚本大小: <=200KB ;
  2. 页面初载时,未压缩的 CSS 资源大小: <=100KB ;
  3. HTTP 协议下,请求资源数: <=6 个 ;
  4. HTTP/2 协议下,请求资源数: <=20 个 ;
  5. 90% 的代码覆盖率(仅容许 10% 的未使用代码);

只有 Chrome 可以查看你的代码覆盖率。java

听从这个目标,应用将能在任何平台(PC,Mobile Phone...)都拥有良好的性能:react

  1. 将来的网络世界在移动端;
  2. 平均每一个移动端网站须要花费 14 秒时间达到可交互状态;
  3. 加载的代码越少,页面达到可交互的时间越短;

3. 查看代码覆盖率

  1. 打开 Chrome Dev Tool;
  2. 按下 Cmd + Shift + P or Ctrl + Shift + P ;
  3. 输入 Coverage ,选择第一个出现的选项:

Xnip2019-06-18_10-48-49.jpg

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

Xnip2019-06-18_10-50-46.jpg


4. 如何作到?

(1)代码分割(code splitting)

什么是 code splitting?
将部分代码在构建时转变为异步加载的过程。webpack

(1.1)代码分割原理

代码分割的核心是异步加载资源,而异步加载功能使用到 stage 3 规范:whatwg/loader。
import() 容许你在浏览器端运行时动态获取资源,虽然它存在如下一些问题:web

  1. 安全问题;
  2. 浏览器支持问题;

(1.2)代码分割的两种类型

  1. 静态的;
  2. “动态的”(在 webpack 中,并非真的动态):“动态”是指你可以在代码运行时决定应该引入什么 JavaScript 模块;

(1.3)静态代码分割

什么时候使用静态的代码分割?
  1. 你正在使用一个很是大的库或框架 :若是在页面初始化时你不须要使用它,就不要在页面初载时加载它;
  2. 任何临时的资源 :指不在页面初始化时被使用,被使用后又会当即被销毁的资源,例如模态框,对话框,tooltip等(任何一开始不显示在页面上的东西均可以有条件的加载);
  3. 路由 :既然用户不会一会儿看到全部页面,那么只把当前页面相关资源给用户就是个明智的作法;

代码示例
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)
  })
})

注意:json

  1. 在 Vue 中,能够直接使用 import() 关键字,在 React 中,使用 react-loadable 完成一样的事;
  2. 每当你使用动态代码分割时,函数将返回一个 Promise 对象;

(1.4)“动态”代码分割

什么时候使用“动态”代码分割?
  1. A/B Test :你不须要在代码中引入不须要的 UI 代码;
  2. 主题 :动态加载相应的主题;
  3. 为了方便 :本质上,你能够用静态代码分割代替“动态”代码分割,可是后者比前者拥有更少的代码量;

代码示例
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 的模块,因此本质上仍然是静态的。api

(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. 参考连接

相关文章
相关标签/搜索