前端开发人员一直面临的挑战是我们应用程序的性能。 我们如何在不强迫用户等待页面永久加载的情况下向我们的用户交付功能强大的功能齐全的应用程序? 用于提高网站速度的技术种类繁多,以至于在优化性能和速度时,往往难以确定将精力集中在哪里。
值得庆幸的是,该解决方案并不像看起来的那样复杂。 在本文中,我将介绍大型Web应用程序用来加速其用户体验的最有效的技术之一。 我将仔细研究一个软件包以简化此过程,并确保我们可以更快地将应用交付给用户,而无需他们注意任何更改。
网站快速意味着什么?
网络性能的问题是广泛的。 为了这篇文章的缘故,我将尝试用最简单的术语来定义性能: 尽可能快地发送。 当然,这可能是问题的过分简化,但是实际上,我们可以通过简单地发送较少的数据供用户下载并快速发送该数据来实现速度的显着提高。
出于本文的目的,我将专注于此定义的第一部分-向用户的浏览器发送尽可能少的信息。
放慢应用程序运行速度的最大罪魁祸首总是图像和JavaScript。 在这篇文章中,我将向您展示如何处理大型应用程序捆绑包的问题并在此过程中加快我们的网站的速度。
反应加载
React Loadable是一个包,它允许我们仅在应用程序需要时才延迟加载JavaScript。 当然,并非所有网站都使用React,但是为了简洁起见,我将重点介绍在使用Webpack构建的服务器端渲染应用程序中实现React Loadable。 最终结果将是在需要该代码时自动将多个JavaScript文件传送到用户的浏览器。 如果您想尝试完整的代码,可以从GitHub存储库中克隆示例源代码 。
使用之前的定义,这仅意味着我们可以预先发送较少的信息给用户,以便可以更快地下载数据,并使用户体验到性能更高的网站。
1.将Loadable
React添加到您的组件中
我将以React组件MyComponent
为例。 我假设该组件由两个文件组成, MyComponent/MyComponent.jsx
和MyComponent/index.js
。
在这两个文件中,我完全像在MyComponent.jsx
一样定义React组件。 在index.js
,我导入了React组件,然后将其重新导出-这次包装在Loadable
函数中。 使用ECMAScript import
功能,我可以向Webpack指示我希望此文件可以动态加载。 这种模式使我可以轻松地延迟加载我已经编写的任何组件。 它还使我可以将延迟加载和渲染之间的逻辑分开。 这听起来可能很复杂,但是实际上这是这样的:
// MyComponent/MyComponent.jsx
export default () => (
This component will be lazy-loaded!
)
// MyComponent/index.js
import Loadable from 'react-loadable'
export default Loadable({
// The import below tells webpack to
// separate this code into another bundle
loader: import('./MyComponent')
})
然后,我可以像往常一样完全导入我的组件:
// anotherComponent/index.js
import MyComponent from './MyComponent'
export default () =>
我现在已经将React Loadable引入MyComponent
。 如果愿意,我以后可以向该组件添加更多逻辑-这可能包括向该组件引入加载状态或错误处理程序。 感谢Webpack,当我们运行构建时,现在将为我提供两个单独JavaScript捆绑包: app.min.js
是我们的常规应用程序捆绑包,而myComponent.min.js
包含我们刚刚编写的代码。 稍后,我将讨论如何将这些捆绑包交付给浏览器。
2.使用Babel简化设置
通常,将对象传递给Loadable
函数时,我必须包括两个额外的选项, modules
和webpack
。 这些帮助Webpack确定我们应该包括哪些模块。 幸运的是,通过使用react-loadable/babel
插件,我们可以避免在每个组件中都包含这两个选项。 这将自动为我们包括以下选项:
// input file
import Loadable from 'react-loadable'
export default Loadable({
loader: () => import('./MyComponent')
})
// output file
import Loadable from 'react-loadable'
import path from 'path'
export default Loadable({
loader: () => import('./MyComponent'),
webpack: () => [require.resolveWeak('./MyComponent')],
modules: [path.join(__dirname, './MyComponent')]
})
我可以通过将其添加到.babelrc文件中的插件列表中来包括此插件,如下所示:
{
"plugins": ["react-loadable/babel"]
}
现在,我比延迟加载组件快了一步。 但是,就我而言,我正在处理服务器端渲染。 目前,服务器将无法呈现我们的延迟加载组件。
3.在服务器上渲染组件
在服务器应用程序中,我有一个标准配置,看起来像这样:
// server/index.js
app.get('/', (req, res) => {
const markup = ReactDOMServer.renderToString(
)
res.send(`
${markup}
`)
})
app.listen(8080, () => {
console.log('Running...')
})
第一步将是指示React Loadable我希望所有模块都被预加载。 这使我可以决定应立即将哪些加载到客户端上。 我这样做是通过修改我的server/index.js
文件来实现的:
// server/index.js
Loadable.preloadAll().then(() => {
app.listen(8080, () => {
console.log('Running...')
})
})
下一步将把我要渲染的所有组件推到一个数组中,以便我们以后可以确定哪些组件需要立即加载。 这样一来,HTML可以通过脚本标签(包含更多正确JavaScript捆绑包)返回(稍后会有更多介绍)。 现在,我将像这样修改服务器文件:
// server/index.js
import Loadable from 'react-loadable'
app.get('/', (req, res) => {
const modules = []
const markup = ReactDOMServer.renderToString(
modules.push(moduleName)}>
)
res.send(`
${markup}
`)
})
Loadable.preloadAll().then(() => {
app.listen(8080, () => {
console.log('Running...')
})
})
每次使用需要React Loadable
的组件时,都会将其添加到modules
数组中。 这是由React Loadable
完成的自动过程,因此这是我们该过程所需的全部。
现在,我们有了一个模块列表,我们知道这些模块需要立即呈现。 我们现在面临的问题是将这些模块映射到Webpack为我们自动生成的捆绑软件中。
4.将Webpack捆绑包映射到模块
因此,现在我指示Webpack创建myComponent.min.js ,并且我知道MyComponent
已被立即使用,因此我需要将此包加载到我们交付给用户的初始HTML有效负载中。 幸运的是,React Loadable也为我们提供了一种实现此目标的方法。 在我的客户端Webpack配置文件中,我需要包含一个新插件:
// webpack.client.config.js
import { ReactLoadablePlugin } from 'react-loadable/webpack'
plugins: [
new ReactLoadablePlugin({
filename: './build/loadable-manifest.json'
})
]
loadable-manifest.json文件将为我提供模块和捆绑软件之间的映射,以便我可以使用之前设置的modules
数组来加载我需要的捆绑软件。 就我而言,该文件可能看起来像这样:
// build/loadable-manifest.json
{
"MyComponent": "/build/myComponent.min.js"
}
这也将需要一个通用的Webpack清单文件来包括模块和文件之间的映射,以用于内部Webpack。 我可以通过包含另一个Webpack插件来做到这一点:
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
})
]
5.在HTML中包含捆绑软件
将动态捆绑包加载到服务器上的最后一步是将这些捆绑包包含在我们交付给用户HTML中。 对于此步骤,我将合并步骤3和4的输出。我可以首先修改上面创建的服务器文件:
// server/index.js
import Loadable from 'react-loadable'
import { getBundles } from 'react-loadable/webpack'
import manifest from './build/loadable-manifest.json'
app.get('/', (req, res) => {
const modules = []
const markup = ReactDOMServer.renderToString(
modules.push(moduleName)}>
)
const bundles = getBundles(manifest, modules)
// My rendering logic below ...
})
Loadable.preloadAll().then(() => {
app.listen(8080, () => {
console.log('Running...')
})
})
在此,我导入了清单,并要求React Loadable创建具有模块/束映射的数组。 我唯一要做的就是将这些捆绑包呈现为HTML字符串:
// server/index.js
app.get('/', (req, res) => {
// My App & modules logic
res.send(`
${markup}
${bundles.map(({ file }) =>
``
}).join('\n')}
`)
})
Loadable.preloadAll().then(() => {
app.listen(8080, () => {
console.log('Running...')
})
})
6.在客户端上加载服务器渲染的捆绑包
使用我们在服务器上加载的捆绑包的最后一步是在客户端上使用它们。 这样做很简单-我可以指示React Loadable
预Loadable
它立即可用的所有模块:
// client/index.js
import React from 'react'
import { hydrate } from 'react-dom'
import Loadable from 'react-loadable'
import MyApplication from './MyApplication'
Loadable.preloadReady().then(() => {
hydrate(
,
document.getElementById('root')
);
});
结论
完成此过程后,我可以根据需要将应用程序捆绑包分成多个较小的捆绑包。 通过这种方式,我的应用程序仅在用户需要时才向用户发送较少的内容。 我减少了需要发送的代码量,以便可以更快地发送。 对于较大的应用程序,这可以显着提高性能。 如果需要,它还可以设置较小的应用程序以实现快速增长。
翻译自: https://code.tutsplus.com/tutorials/dramatically-speed-up-your-frontend-application-using-react-loadable--cms-32107