В этой статье в основном рассказывается о некоторых оптимизациях React и Webpack, оригинальный проект находится вздесь, будет продолжать обновляться, когда у вас будет время, добро пожаловать, чтобы подписаться и начать, и есть еще один, который не может использовать HtmlWebpackPlugin для вставки фрагментов.issuesПожалуйста, помогите мне решить эту проблему, спасибо~
Оптимизация сборки
loaders
- Минимизируйте использование различных загрузчиков/плагинов
- использовать
include
Поле указывает каталог для конвертации, использованияexclude
Исключая каталоги:
module.exports = {
...
module: {
rules: [
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
exclude: path.resolve(__dirname, 'node_modules'),
loader: 'babel-loader'
}
]
}
};
resolve
-
попытаться вычесть
resolve.modules
,resolve.extensions
,resolve.mainFiles
,resolve.descriptionFiles
количество значений -
resolve.modules
:использовать
resolve.modules
Указываем путь к каталогу модуля:module.exports = { ... resolve: { modules: [path.resolve(__dirname, 'node_modules')] } };
-
resolve.alias
:resolve.alias
Заставьте Webpack напрямую использовать сжатую версию библиотеки, больше не анализировать библиотеку и использовать псевдонимы для упрощения ссылок на файлы:module.exports = { ... resolve: { alias: { Components: path.resolve(__dirname, 'src/components/'), Utils: path.resolve(__dirname, 'src/utils/'), react: patch.resolve(__dirname, './node_modules/react/dist/react.min.js') } } };
Например, чтобы вы могли непосредственно использовать сжатую версию реагирования, каждый раз, когда снова разбирают здание. Вы также можете ссылаться на файл псевдонима без необходимости прибегать к длинным эталонным путям:
import ReactComponent from 'Components/ReactComponent';
Но недостатком этого является то, что Tree-Shaking нельзя использовать, поэтому, как правило, лучше использовать относительно сильную библиотеку, такую как React, а библиотеки инструментов, такие как lodash, по-прежнему используют Tree-Shaking для удаления избыточного кода.
-
resolve.extensions
:Установите суффикс файла для анализа, значение по умолчанию:
module.exports = { ... resolve: { extensions: ['.wasm', '.mjs', '.js', '.json'] } };
Вы можете установить тип файла, который вы хотите проанализировать, чтобы ускорить поиск:
module.exports = { ... resolve: { extensions: ['.js', '.json', 'jsx'] } };
externals
использоватьexternals
Это может предотвратить упаковку некоторых библиотек и ссылаться на библиотеку другими способами (например, CDN).Преимущество этого заключается в том, что при обновлении кода кэш кода библиотеки не будет затронут, и пользователю нужно будет только скачать новый код. Конечно, мы также можем использовать chunk для упаковки редко обновляемых библиотек в другой файл, о чем мы поговорим ниже.
Например, импорт реагирует с CDN:
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js" defer></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" defer></script>
<script src="./dist/index.js" defer></script>
module.exports = {
...
externals: {
react: 'React',
'react-dom': 'ReactDOM'
},
}
devtool
использоватьdevtool
Это очень требовательно к производительности. Если вам это не нужно, не устанавливайте. Если вам это нужно и качество хорошее, вы можете установить.source-map
, но это занимает очень много времени, если приемлемо низкое качество, используйтеcheap-source-map
Официальная рекомендация является тем, что с лучшей производительностью и беднее качество.cheap-module-eval-source-map
.
splitChunks
После Webpack 4 общий инструмент извлечения кода будет удален изCommonChunksPluginзаменить на лучшееSplitChunksPlugin.下面这个例子不使用externals
, вместо этого извлекает React и ReactDOM в общий код модуля.
module.exports = {
...
// externals: {
// react: 'React',
// 'react-dom': 'ReactDOM'
// },
optimization: {
...
splitChunks: {
chunks: 'all',
name: true,
automaticNameDelimiter: '-', // 模块间的连接符,默认为"~"
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10 // 优先级,越小优先级越高
},
default: { // 默认设置,可被重写
minChunks: 2,
priority: -20,
reuseExistingChunk: true // 如果本来已经把代码提取出来,则重用存在的而不是重新产生
}
}
}
},
}
mode
mode
Возможные значения:
-
production
: режим сборки, который автоматически активирует некоторые связанные со сборкой подключаемые модули, такие как сжатый код.
module.exports = {
+ mode: 'production',
- plugins: [
- new UglifyJsPlugin(/* ... */),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
- new webpack.optimize.ModuleConcatenationPlugin(),
- new webpack.NoEmitOnErrorsPlugin()
- ]
}
-
development
: Режим разработки, в котором запускаются некоторые подключаемые модули оптимизации, связанные с разработкой.
module.exports = {
+ mode: 'development'
- devtool: 'eval',
- plugins: [
- new webpack.NamedModulesPlugin(),
- new webpack.NamedChunksPlugin(),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
- ]
}
node
вавилон, встряхивающий деревья
Здесь используется версия Babel 7. Так как большинство браузеров поддерживают синтаксис ES6, если весь код перевести на ES5, может быть много лишнего кода, поэтому конвертируется только часть кода, и что мне делать с низкой версией браузера? поговорим о некоторых решениях, давайте сначала посмотрим на конфигурацию Babel:
{
"presets": [
[
"@babel/react",
{
"modules": false // 关闭babel的模块转换,才能使用Webpack的Tree-Shaking功能
}
]
],
"plugins": [
"@babel/plugin-proposal-class-properties", // class,这个要放在前面,否则可能会报错
"@babel/plugin-transform-classes", // class
"@babel/plugin-transform-arrow-functions", // 箭头函数
"@babel/plugin-transform-template-literals" // 字符串模板
]
}
Когда некоторые библиотекиpackage.json
изsideEffects
Tree-Shaking хорошо поддерживается при установке, например lodash:
{
"name": "lodash",
"sideEffects": false
}
happypack
Используйте happypack, чтобы включить многопоточность для ускорения обработки загрузчика:
var HappyPack = require('happypack');
module.exports = {
...
rules: [
{
test: /\.(js|jsx)$/,
include: path.resolve(__dirname, 'src'),
exclude: path.resolve(__dirname, 'node_modules'),
use: 'happypack/loader?id=babel'
},
],
plugins: [
new HappyPack({
id: 'babel',
loaders:['babel-loader?cacheDirectory']
}),
],
}
разное
Создайте код для ES6+
Как упоминалось выше, преобразование кода в ES5 займет много времени и может быть много избыточного кода, потому что сейчас большинство браузеров уже поддерживают синтаксис ES6, теперь давайте посмотрим, как быть совместимым с более низкими версиями браузеров.
-
module
,nomodule
:
можно использовать<script type="module" src="index.js"></script>
для загрузки кода ES6+, потому что браузеры, поддерживающие это свойство, должны его поддерживатьasync/await
,Promise
,class
Браузеры, которые не поддерживают эти свойства, будут игнорировать их и не загружать.
Поэтому вам также нужен скрипт ES5 для совместимости с более ранними версиями браузеров, используйте<script nomodule src="index.es5.js"></script>
для загрузки кода ES5, который может распознаватьnomodule
Браузеры, которые его не распознают, будут его игнорировать, а старые браузеры, которые его не распознают, загрузят его. Это делает его совместимым с более ранними версиями браузеров, в то время как более новые браузеры используют гораздо меньше кода ES6+.
Но есть у этого метода и недостатки: при использованииsplitChunks
Когда код разбит на большее количество модулей, необходимо сгенерировать большое количество двух версий кода.
- Динамический полифилл
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
Он автоматически загрузит полифиллы, необходимые браузеру, проанализировав UserAgent в заголовке запроса. Если вы используете более новую версию для доступа к приведенной выше ссылке, вы обнаружите, что кода не так много, а с IE он будет производить много. Таким образом, мы можем использовать код ES6+ и динамический полифилл для совместимости с браузерами более ранних версий, но динамический полифилл не поддерживаетclass
и стрелочные функции и т. д., поэтому вам нужно настроить babel, как указано выше, чтобы преобразовать их в ES5. Если вы хотите узнать больше о динамических полифиллах, вы можете нажатьздесь.
оптимизация разработки
Избегайте использования инструментов, которые используются только во время сборки
Есть некоторые инструменты, которые не нужно использовать при разработке, их использование может сильно замедлить скорость генерации кода, напримерUglifyJsPlugin
, код не нужно сжимать во время разработки, а также во время разработки избегают использования следующих инструментов:
UglifyJsPlugin
ExtractTextPlugin
[hash]/[chunkhash]
AggressiveSplittingPlugin
AggressiveMergingPlugin
ModuleConcatenationPlugin
Не выводить информацию о пути
module.exports = {
// ...
output: {
pathinfo: false
}
};
Отключить некоторые оптимизации сборки
module.exports = {
...
optimization: {
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false,
}
};
Реагировать на оптимизацию
Поскольку все HTML-элементы React написаны в файлах JS, результирующие файлы JS, как правило, очень большие, и во время длительного процесса загрузки и выполнения JS браузер пользователя всегда отображает состояние белого экрана, первый экран. Время рендеринга становится очень долго.Если вы не используете рендеринг на стороне сервера, вы можете внести некоторые улучшения следующим образом.
Добавить загрузку вверху страницы
Вы можете добавить загрузку в html-файлы с помощью плагина HtmlWebpackPlugin без белого экрана.
var loading = {
ejs: fs.readFileSync(path.resolve(__dirname, 'template/loading.ejs')),
css: fs.readFileSync(path.resolve(__dirname, 'template/loading.css')),
};
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'template/index.ejs'),
hash: true,
loading: loading, // 在React渲染完前添加loading
}),
new ScriptExtHtmlWebpackPlugin({ // 给script标签加上defer
defaultAttribute: 'defer'
}),
]
}
См. конкретный код шаблоназдесь
prerender-spa-plugin
Плагин prerender-spa может генерировать первый экран одностраничного приложения в HTML. Принцип заключается в том, чтобы получить доступ к соответствующему пути через puppeteer для захвата соответствующего контента. Поскольку я никогда не мог установить puppeteer, я не буду говорить об этом подробно.
module.exports = {
...
new PrerenderSpaPlugin(
// Absolute path to compiled SPA
path.resolve(__dirname, '../dist'),
// List of routes to prerender
['/']
)
}
React Loadable
Вы можете использовать его для динамического импорта компонентов React, вы можете разделить некоторые менее важные компоненты на куски, а затем динамически импортировать их, что может улучшить скорость рендеринга первого экрана:
import Loading from './src/components/Loading';
import ReactDOM from 'react-dom';
import Loadable from 'react-loadable';
const LoadableApp = Loadable({
loader: () => import('./src/App'),
loading: Loading,
});
ReactDOM.render(LoadableApp, document.querySelector('#root'));
Временно напишите так оптимизация, где я могу смотреть постоянно обновляется, добро пожаловать, чтобы обсудить любые проблемы ~