我正在使用 HTML5、JavaScript 和 React 创建一个网站。我正在使用 Google Chrome Lighthouse 来检查网站首次加载的性能。
这是 Lighthouse 运行时环境:
- 用户代理:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
- 设备仿真 Nexus 5X:已启用
- 网络节流 562.5ms RTT, 1.4Mbps down, 0.7Mbps up: 启用
- CPU Throttling 4x 减速:启用
经过多次迭代和修复,这些是我目前在 Lighthouse 上的得分:
- 性能 57
- 渐进式网络应用 100
- 无障碍 100
- 最佳实践 88
- 搜索引擎优化 100
最佳实践不是 100,因为:
- 我拥有的主机不提供 HTTP/2(如果这真的是一个问题,我可能会改变)
- 我有带有 document.write() 的 Google Ads 脚本(对此无法更改)
我的大问题是性能指标。我有一个超过 3.3 秒的白色空白屏幕,并且在 8.3 秒后完全显示!第一次有意义的绘制为 5,750 毫秒,第一次交互为 6,720 毫秒(与一致交互相同)感知速度指数为 4,228(得分 64),估计输入延迟为 16 毫秒(得分 100)
这是我到目前为止所做/尝试过的:
- 使用 react-code-splitting 将起始页面中的所有组件移动到异步加载
- 优化图像以使用新的 J2P、JXR 和 WEBP 格式,并使它们的大小正确,因此浏览器不必调整它们的大小。
- 将 App.js 的内容移动到 index.js 中(在加载之前会并行获取 index.js 和 vendor.js,然后是一个包含所有 css 文件和 app.js 的 js 文件,之后它将并行加载app.js 中使用的组件
- 将一些 css 导入移动到与它们关联的组件中。
- 使用 webpack-bundle-analyzer 尝试查找常用库以及它们是如何拆分的。正如您在以下屏幕截图中看到的那样,我的包中有很多重复的模块,我找不到如何提取以提高效率。(lodash,react-overlays,...)
这是我的 webpack.config.js
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
const WebpackChunkHash = require('webpack-chunk-hash');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
//var SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const OfflinePlugin = require('offline-plugin');
// To handle the regeneratorRuntime exception
require('babel-polyfill');
require('file-loader');
require('css-loader');
require('style-loader');
require('html-webpack-template');
/* Shared Dev & Production */
const config = {
context: path.resolve(__dirname, 'src'),
entry: {
index: [
// To handle the regeneratorRuntime exception
'babel-polyfill',
'./index.js'
],
vendor: ['offline-plugin/runtime', 'react', 'react-dom', 'react-router', 'react-redux', 'history', 'react-router-dom', 'redux', 'react-router-redux', 'redux-form', 'lodash'],
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.(ico|jpg|png|gif|eot|otf|jp2|jxr|webp|svg|ttf|woff|woff2)(\?.*)?$/,
exclude: /\/favicon.ico$/,
use: [
{
loader: 'file-loader',
query: {
name: '[path][name][hash].[ext]',
publicPath: '/'
}
}
]
},
{
test: /\.css$/,
include: path.join(__dirname, 'src/style'),
use: [
'style-loader',
{
loader: 'css-loader',
options: {
minimize: true
}
}
]
},
{
test: /\.(ico)(\?.*)?$/,
exclude: /node_modules/,
use: {
loader: 'file-loader',
options: { name: '[name].[ext]' },
},
},
{
test: /\.xml/,
use: {
loader: 'file-loader',
options: { name: '[name].[ext]' },
},
},
],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js',
publicPath: '/',
},
resolve: {
extensions: ['.js'],
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
},
plugins: [
// New moment.js optimization
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: 2
}),
new webpack.optimize.ModuleConcatenationPlugin(),
new HtmlWebpackPlugin({
appMountId: 'app-root',
inlineManifestWebpackName: 'webpackManifest',
template: 'templateIndex.html',
title: 'Our Site',
}),
new OfflinePlugin({
AppCache: false,
ServiceWorker: { events: true },
}),
new BundleAnalyzerPlugin({analyzerMode: 'static'}),
],
devServer: {
historyApiFallback: true,
},
};
//if (process.env.NODE_ENV === 'production') {
config.output.filename = '[name].[chunkhash].js';
config.plugins = [
...config.plugins,
new webpack.HashedModuleIdsPlugin(),
new WebpackChunkHash(),
/*new ChunkManifestPlugin({
filename: 'chunk-manifest.json',
manifestVariable: 'webpackManifest',
inlineManifest: true,
}),*/
];
//}
module.exports = config;
我的.babelrc
{
"presets": [
[
"env",
{ "modules": false }
],
"react",
"stage-0",
"stage-2"
],
"plugins": [ "syntax-dynamic-import", "transform-react-jsx", "transform-decorators-legacy" ]
}
还有我的 index.js
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter } from "react-router-dom";
import {Redirect, Route, Switch, withRouter} from 'react-router';
import { Provider } from 'react-redux';
import Async from 'react-code-splitting';
const TopBar = () => (<Async load={import('./containers/TopBar/TopBarAsync')} />);
const NavBar = () => (<Async load={import('./containers/NavBar/NavBarAsync')} />);
const BottomBar = () => (<Async load={import('./containers/BottomBar/BottomBarAsync')} />);
const UserHelpers = (props) => <Async load={import('./containers/UserHelpers')} componentProps={props} />
const Main = () => (<Async load={import('./containers/Main')} />);
const Blog = () => (<Async load={import('./containers/Blog')} />);
const PublicPage = () => (<Async load={import('./containers/PublicPage')} />);
import configureStore from './store/store';
const store = configureStore();
import './styles/font-awesome.min.css';
import './styles/app.css';
import './styles/TeamBrowser.css';
let googleTracking = null;
if(process.env.NODE_ENV==='production') {
// https://web-design-weekly.com/2016/07/08/adding-google-analytics-react-application/
ReactGA.initialize([my key here]);
googleTracking = function fireTracking() {
let page=window.location.pathname+window.location.search;
ReactGA.set({ page: page });
ReactGA.pageview(page);
}
const runtime=require('offline-plugin/runtime');
runtime.install({
onUpdateReady() {
runtime.applyUpdate();
},
onUpdated() {
window.location.reload();
},
});
}
render((
<Provider
store={store}>
<BrowserRouter
onUpdate={googleTracking}>
<div className="body">
<header>
<TopBar/>
<NavBar />
</header>
<main>
<Switch>
<Route path="/" exact component={Main} />
<Route path="/main" component={Main} />
<Route path="/Feed" component={Blog} />
<Route path="/Blog" component={Blog} />
<Route path="/:id" component={PublicPage} />
<Redirect to="/" />
</Switch>
</main>
<footer
className="footer">
<BottomBar />
</footer>
<UserHelpers />
</div>
</BrowserRouter>
</Provider>),
document.getElementById('app'));
现在我不知道如何提高加载速度以获得更好的 Lighthouse 分数。任何人都可以发现我不知道的任何事情吗?
在我看来,chunk-manifest-webpack-plugin 似乎没有正常工作,因为它没有提取所有常见模块。其次,index.[hash].js 包包含它不应该包含的文件(例如:utils.js、“actions”文件夹下的文件……这些是我自己的文件,但在我的索引中没有引用它们.js 那么为什么要把它们放在那里呢?