Эта статья从零开始配置 react + typescript
В третьей части серии вам предстоит завершить настройку веб-пакета проекта шаблона. Конфигурацией всего проекта я стремлюсь достичь следующих целей:
гибкий:Когда я настраиваю eslint, я выбираю использовать формат js вместо json, просто для гибкости, использование файлов js позволяет импортировать другие модули, динамически настраивать в соответствии со средой разработки и в полной мере использовать возможности языка js.
Модный:Я считаю, что это прекрасное качество – всегда быть в поиске чего-то нового и пытаться это использовать. Конечно, легко наткнуться на ямы при погоне за новыми, но это не беда, я наступил на нее для вас, и не буду писать, если не смогу наступить на нее. от моего эслинтаparserOptions.ecmaVersion
Установите его на 2020 год и возвращайтесь почащеyarn upgrade --latest
может проявиться.
строгий:Точно так же, как я обычно сужу о равенстве, в большинстве случаев я использую strict и т. д.===
, вместо нестрогих и т. д.==
, я думаю, чем она строже, тем четче будет анализ, и тем быстрее можно будет найти проблемы. Например, позже мы будем использовать некоторые плагины веб-пакетов, чтобы строго проверять регистр модулей и проверять наличие циклических зависимостей.
комфортный:Проект попытается интегрировать инструменты, которые практичны в текущей экосистеме внешнего интерфейса и могут повысить удовольствие от разработки (другими словами, навороты).
производство готово: при настройке он оптимизируется для различных сред упаковки и обеспечивает возможность его использования в производственных средах.
Эта статья будет разделена на три части:
- dev server
- Оптимизация среды разработки
- Оптимизация производственной среды
Если читатель видит эту статью впервые, рекомендуется прочитать первые две статьи:
- Настроить реакцию + машинописный текст с нуля (один): dotfiles
- Настраиваем react + typescript с нуля (два): линтеры и форматтер
адрес проекта:react-typescript-boilerplate
dev server
Я думаю, когда я только начал изучать фронтенд-фреймворк, меня замучил вебпак, и я начал писать фронтенд только после того, как научился сначала ноду, очень удобно писать нодэйс, и он идет со своим модульное решение.commonjs
, Для написания фронтенд-проекта нужно настроить инструмент упаковки. В то время самым популярным инструментом для упаковки уже был webpack, за ним последовалиgulp
. Настройка веб-пакета всегда забывает, какие поля находятся в конфигурации веб-пакета, но также включает в себя множество связанных инструментов, таких как компилятор ES6.babel
, препроцессор CSSsass
/less
, CSS Post Processorpostcss
, а также различные загрузчики и плагины веб-пакетов. Затем я какое-то время использовал официальные леса, если это было проблематично, и просто использовал реакциюcra
, то есть,create-react-app
, просто используйте vuevue-cli
. На самом деле это довольно полезно, но, честно говоря, лично я думаю,cra
Нетvue-cli
Дизайн хорошая, оба простота использования и масштабируемости являются полными, CRA неудобно для пользователей модифицировать конфигурацию WebPack, Vue-CLI не только легко изменить конфигурацию WebPack, но также позволяет пользователям сохранять шаблоны и приносить их Собственный плагин. Я чувствую, что реадгициал также знают об этом, поэтому официальные утверждения о том, что он будет сосредоточен на оптимизации связанных с подходящими инструменталками в ближайшем будущем. Теперь, если я создаю новый интерфейсный проект, я выберу свою собственную конфигурацию вместо того, чтобы использовать официальный CLI, потому что я думаю, что я вполне знаком с различными инструментами в интерфейсах, и я закончу свой выпускной и работу на охоту Первая половина года. Я должен извлечь некоторые общие конфигурации в пакет NPM. Теперь это слишком утомительно для копирования и изменения каждый раз, когда я пишу проект. Конфигурация сборки одного проекта оптимизирована, а другие проекты должны быть вручную синхронизирован, что слишком неэффективно.
Технический отбор
Как статически типизированный язык, TypeScript, несомненно, имеет огромное улучшение в подсказках типов по сравнению с js. С помощью подсказок типа IDE и автодополнения нам нужно знать, какие поля в объекте конфигурации webpack, поэтому нам не нужно сверяться с официальной документацией, и мы не можем ошибиться, это очень удобно, поэтому выбираем язык разработкиTypeScript.
В официальной документации есть специальный разделConfiguration LanguagesПредставьте, как инструмент командной строки webpack использует файл конфигурации формата ts, я думаюwebpack-dev-server
Инструменты командной строки должны быть одинаковыми.
Но я не планирую использовать метод, представленный официальной документацией, я не планирую использовать инструмент командной строки вообще, просто используйте API узла.самый гибкийспособ конфигурации. настроитьwebpack devServer
Подводя итог, можно выделить следующие способы:
-
webpack-dev-server
, это самый негибкий способ, конечно, очень удобный, когда сценарий использования простой -
webpack-dev-server
API узла, вызываемый в сценарии узлаweb-dev-server
API узла, предоставляемый пакетом для запуска devServer. -
express
+webpack devServer 相关中间件
, по фактуwebpack-dev-server
это использоватьexpress
И разработано некоторое промежуточное ПО, связанное с devServer. Таким образом, различное промежуточное ПО становится доступным напрямую, и мы можем гибко настраивать параметры каждого промежуточного ПО. -
koa
+webpack devServer 相关中间件
, я действительно нашел промежуточное ПО webpack, связанное с webpack devServer, на github. По сути, webpack devServer — это всего лишь узел-сервер, и неважно, какая технология фреймворка используется для его достижения, главное, чтобы он мог выполнять нужные нам функции.
Я закончил тем, что использовалexpress
+ webpack devServer 相关中间件
Кстати, почему бы не использоватьkoa
? Потому что я думаю, что официальное использованиеexpress
,использоватьexpress
определенно лучше, чемkoa
Более зрелый и стабильный, с меньшим количеством ям.
Реализовать самые основные функции упаковки
От простого к сложному, давайте сначала реализуем самые основные функции упаковки, чтобы их можно было упаковать.tsx
На этой основе файл шаг за шагом обогащается, и наша конфигурация оптимизируется.
Файл записи конфигурации
Сначала установите TypeScript:
# 本地安装开发依赖 typescript
yarn add typescript -D
Каждый проект TypeScript должен иметь одинtsconfig.json
config, используйте следующую команду вsrc
новый каталогtsconfig.json
документ:
cd src && npx tsc --init && cd ..
Мы временно приспосабливаемся к этому:
{
"compilerOptions": {
/* Basic Options */
"jsx": "react",
"isolatedModules": true,
/* Strict Type-Checking Options */
"strict": true,
/* Additional Checks */
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
/* Module Resolution Options */
"moduleResolution": "node",
"esModuleInterop": true,
"resolveJsonModule": true,
"baseUrl": "./",
"paths": {
// 配置模块路径映射
"@/*": ["./*"],
},
/* Experimental Options */
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
/* Advanced Options */
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
// 下面这些选项对 babel 编译 TypeScript 没有作用但是可以让 VSCode 等编辑器正确提示错误
"target": "ES2019",
"module": "ESNext"
}
}
Мы будем использовать Babel для компиляции TypeScript. Когда Babel компилирует код TypeScript, он напрямую удаляет тип TypeScript, а затем использует различные плагины для его компиляции как обычный код javascript. tsc не вмешивается в процесс компиляции, поэтомуtsconfig.json
множество вариантов, таких какtarget
а такжеmodule
бесполезно.
включитьisolatedModules
опция обеспечит некоторые дополнительные проверки, когда Babel компилирует код,esModuleInterop
Эта опция используется, чтобы разрешить модулям без атрибута по умолчанию использовать импорт по умолчанию.Например, если эта опция не включена, вы можете только импортировать модули fs следующим образом:
import * as fs from 'fs';
После его включения вы можете напрямую использовать импорт по умолчанию:
import fs from 'fs';
По сути, импорт ESM по умолчанию является атрибутом импортируемого модуля по умолчанию:
import fs from 'fs';
// 等同于
import * as __module__ from 'fs';
let fs = __module__.default;
Но встроенный модуль Node FS не имеет атрибута по умолчанию, открытьisolatedModules
Параметры автоматически конвертируются без атрибута по умолчанию:
import fs, { resolve } from 'fs';
// 转换成
import * as fs from 'fs';
let { resolve } = fs;
Добавляем входной файлsrc/index.tsx
, содержание очень простое:
import plus from './plus';
console.log(plus(404, 404, 404, 404, 404)); // => 2020
src/plus.ts
Содержание:
export default function plus(...nums: number[]) {
return nums.reduce((pre, current) => pre + current, 0);
}
Скомпилировать TypeScript
Мы знаем, что модульная система веб-пакета по умолчанию поддерживает только файлы js.Для других типов файлов, таких как шрифты jsx, ts, tsx, vue и image, нам необходимо установить соответствующий загрузчик. Для файлов ts в настоящее время существует три популярных схемы:
Потрясающе-нагрузчик-погрузчик забывает об этом, автор отказался от обслуживания. Прежде всего, мы должны использовать Babel, потому что есть много практических плагинов в экосистеме Вавила. Хотя Babel можно использовать с TS-Loader, TS-Loader официально дал примерreact-babel-karma-gulp, но я думаю, что раз Babel уже может компилировать TypeScript, нам не нужно добавлять ts-загрузчик, поэтому я выбираю первый вариант. Следует отметить, что babel не проверяет тип TypeScript по умолчанию, и мы настроим его позже в части плагина webpack.fork-ts-checker-webpack-plugin
Для решения этой проблемы.
Добавить конфигурацию веб-пакета
Мы поместим все нод-скрипты в корень проектаscripts
папка, потому чтоsrc
Папки — это интерфейсные проекты, аscripts
Папка - это проект узла, мы должны настроить его отдельноtsconfig.json
, в котором начальнаяtsconfig.json
документ:
cd ./scripts && npx tsc --init && cd ..
Подгоняем под соус:
// scripts/tsconfig.json
{
"compilerOptions": {
/* Basic Options */
"target": "ES2019",
"module": "commonjs",
/* Strict Type-Checking Options */
"strict": true,
/* Additional Checks */
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
/* Module Resolution Options */
"moduleResolution": "node",
"esModuleInterop": true,
"resolveJsonModule": true,
/* Experimental Options */
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
/* Advanced Options */
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
}
}
Несколько замечаний:
-
"target": "ES2019"
, на самом деле, для вас не проблема настроить очень низкий уровень компиляции.Вы можете использовать расширенный синтаксис tsc для перекодирования.Недостаток в том, что размер кода, как правило, становится больше после перекодирования, а также снижается эффективность выполнения .Нативный синтаксис в целом оптимизирован. Мне нравится немного подкручивать его, обычно это нормально, если вы не используете синтаксис, который не поддерживается на платформе, на которой работает ваш код. Поскольку TypeScript 3.7 поддерживает необязательную цепочку, я начал пытаться использовать ее в TypeScript, но проблема в том, что я всегда устанавливал самый высокий уровень компиляции до этого, то естьESNext
, так как необязательная цепочка находится вES2020
Это уже стандарт, поэтому tsc не перекодирует необязательные цепочки. Затем узел 12 не поддерживает необязательную цепочку, он сообщит о синтаксической ошибке, поэтому я опускаю его доES2019
. -
Strict Type-Checking Options
, эта часть полностью открыта, так как она находится на корабле TypeScript, используйте самую строгую проверку типов и откажитесь от AnyScript
Затем мы создаем новыйscripts/configs
Папка, которая используется для хранения файлов конфигурации, включая webpack. Создайте в нем три новых файла конфигурации webpackwebpack.common.ts
,webpack.dev.ts
а такжеwebapck.prod.ts
.webpack.common.ts
Сохраните некоторые общедоступные файлы конфигурации,webpack.dev.ts
Он используется в среде разработки и будет прочитан devServer.webapck.prod.ts
Это то, что мы используем при создании производственного пакета.
Затем мы устанавливаем webpack и webpack-merge и их файлы объявления типов:
yarn add webpack webpack-merge @types/webpack @types/webpack-merge -D
webpack-mergeэто инструмент слияния, предназначенный для слияния конфигураций веб-пакетов, предоставляющий некоторые расширенные методы слияния. Однако в настоящее время я не использовал эти расширенные методы слияния, а использовал их как обычные инструменты слияния. Я могу изучить оптимизацию в этой области позже.
Чтобы скомпилировать tsx, нам нужно установитьbabel-loader
и сопутствующие плагины:
yarn add babel-loader @babel/core @babel/preset-typescript -D
Создайте новый файл конфигурации babelbabel.config.js
, теперь мы просто добавляем предустановку TypeScript:
// babel.config.js
module.exports = function (api) {
api.cache(true);
const presets = ['@babel/preset-typescript'];
const plugins = [];
return {
presets,
plugins,
};
};
Добавьте загрузчик babel вwebpack.common.ts
:
// webpack.common.ts`
import { Configuration } from 'webpack';
import { projectName, projectRoot, resolvePath } from '../env';
const commonConfig: Configuration = {
context: projectRoot,
entry: resolvePath(projectRoot, './src/index.tsx'),
output: {
publicPath: '/',
path: resolvePath(projectRoot, './dist'),
filename: 'js/[name]-[hash].bundle.js',
// 加盐 hash
hashSalt: projectName || 'react typescript boilerplate',
},
resolve: {
// 我们导入ts 等模块一般不写后缀名,webpack 会尝试使用这个数组提供的后缀名去导入
extensions: ['.ts', '.tsx', '.js', '.json'],
},
module: {
rules: [
{
// 导入 jsx 的人少喝点
test: /\.(tsx?|js)$/,
loader: 'babel-loader',
// 开启缓存
options: { cacheDirectory: true },
exclude: /node_modules/,
},
],
},
};
Я не думаю, что в этом проекте react + ts должен быть файл jsx.Если файл jsx импортирован, webpack сообщит об ошибке, что соответствующий загрузчик не может быть найден, чтобы мы могли вовремя разобраться с этим проблемным файлом.
Разработать devServer с экспресс
Давайте сначала установимexpress
И немного промежуточного ПО, связанного с webpack devServer:
yarn add express webpack-dev-middleware webpack-hot-middleware @types/express @t
ypes/webpack-dev-middleware @types/webpack-hot-middleware -D
webpack-dev-middlewareэтоexpress
Основная роль промежуточного ПО:
- В качестве статического файлового сервера используйте файловую систему в памяти для размещения пакетов, скомпилированных webpack.
- Если файл был изменен, запрос сервера будет отложен до завершения компиляции.
- Сотрудничатьwebpack-hot-middlewareРеализовать функцию горячего обновления
webpack-hot-middlewareЭто промежуточное ПО Express зарегистрирует себя как подключаемый модуль веб-пакета и будет прослушивать события компиляции веб-пакета. Какая запись вам нужна для достижения горячего обновления, вам нужно импортировать плагин, указанный в этой записиwebpack-hot-middleware/client.js
Патч клиента. Этот интерфейсный код получит devServerServer Sent EventsConnect, когда происходит событие компиляции, devServer отправит уведомление клиенту. После того, как клиент получит уведомление, он определит, является ли локальный код последним, сравнив значение хеш-функции, если нет, он вытащит исправление обновления с devServer с помощью некоторых других инструментов, таких какreact-hot-loaderРеализуйте горячее обновление.
Вот два запроса, отправленных клиентским патчем после изменения строки кода в другом электронном проекте, который я все еще разрабатываю:
Значение h, возвращенное первым запросом, можно угадать, переместив пальцы ног, чтобы оно было хеш-значением.После того, как вы обнаружите, что оно не соответствует локальному хеш-значению, снова запросите исправление обновления.
Мы создаем новый файлscripts/start.ts
Чтобы запустить наш devServer:
import chalk from 'chalk';
import getPort from 'get-port';
import logSymbols from 'log-symbols';
import open from 'open';
import { argv } from 'yargs';
import express, { Express } from 'express';
import webpack, { Compiler, Stats } from 'webpack';
import historyFallback from 'connect-history-api-fallback';
import cors from 'cors';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import proxy from './proxy';
import devConfig from './configs/webpack.dev';
import { hmrPath } from './env';
function openBrowser(compiler: Compiler, address: string) {
if (argv.open) {
let hadOpened = false;
// 编译完成时执行
compiler.hooks.done.tap('open-browser-plugin', async (stats: Stats) => {
// 没有打开过浏览器并且没有编译错误就打开浏览器
if (!hadOpened && !stats.hasErrors()) {
await open(address);
hadOpened = true;
}
});
}
}
function setupMiddlewares(compiler: Compiler, server: Express) {
const publicPath = devConfig.output!.publicPath!;
// 设置代理
proxy(server);
// 使用 browserRouter 需要重定向所有 html 页面到首页
server.use(historyFallback());
// 开发 chrome 扩展的时候可能需要开启跨域,参考:https://juejin.cn/post/6844904049276354567
server.use(cors());
const devMiddlewareOptions: webpackDevMiddleware.Options = {
// 保持和 webpack 中配置一致
publicPath,
// 只在发生错误或有新的编译时输出
stats: 'minimal',
// 需要输出文件到磁盘可以开启
// writeToDisk: true
};
server.use(webpackDevMiddleware(compiler, devMiddlewareOptions));
const hotMiddlewareOptions: webpackHotMiddleware.Options = {
// sse 路由
path: hmrPath,
// 编译出错会在网页中显示出错信息遮罩
overlay: true,
// webpack 卡住自动刷新页面
reload: true,
};
server.use(webpackHotMiddleware(compiler, hotMiddlewareOptions));
}
async function start() {
const HOST = '127.0.0.1';
// 4个备选端口,都被占用会使用随机端口
const PORT = await getPort({ port: [3000, 4000, 8080, 8888] });
const address = `http://${HOST}:${PORT}`;
// 加载 webpack 配置
const compiler = webpack(devConfig);
openBrowser(compiler, address);
const devServer = express();
setupMiddlewares(compiler, devServer);
const httpServer = devServer.listen(PORT, HOST, err => {
if (err) {
console.error(err);
return;
}
// logSymbols.success 在 windows 平台渲染为 √ ,支持的平台会显示 ✔
console.log(
`DevServer is running at ${chalk.magenta.underline(address)} ${logSymbols.success}`,
);
});
// 我们监听了 node 信号,所以使用 cross-env-shell 而不是 cross-env
// 参考:https://github.com/kentcdodds/cross-env#cross-env-vs-cross-env-shell
['SIGINT', 'SIGTERM'].forEach((signal: any) => {
process.on(signal, () => {
// 先关闭 devServer
httpServer.close();
// 在 ctrl + c 的时候随机输出 'See you again' 和 'Goodbye'
console.log(
chalk.greenBright.bold(`\n${Math.random() > 0.5 ? 'See you again' : 'Goodbye'}!`),
);
// 退出 node 进程
process.exit();
});
});
}
// 写过 python 的人应该不会陌生这种写法
// require.main === module 判断这个模块是不是被直接运行的
if (require.main === module) {
start();
}
webpackHotMiddleware
изoverlay
Опция - включить маску ошибки или нет:
Я написал много подробностей в комментариях и установил некоторые используемые в нем библиотеки инструментов:
yarn add get-port log-symbols open yarg -D
Первые триsindresorhusМагистерская работа,get-port
для получения доступных портов,log-symbols
Предусмотрены следующие четыре символа журнала:open
Открыт для системных приложенийuri
(uri
включая файлы и URL-адреса, которые должен знать каждый),yargs
Используется для разбора аргументов командной строки.
webpack-dev-middleware
не поддерживаетсяwebpack-dev-server
серединаhistoryFallback
а такжеproxy
функция, это не имеет большого значения, мы можем пройтиDIYНаш экспресс-сервер для достижения мы можем даже использоватьexpress
интегрироватьmock
Функция. Установите два соответствующих промежуточных ПО:
yarn add connect-history-api-fallback http-proxy-middleware @types/connect-history-api-fallback @types/http-proxy-middleware -D
connect-history-api-fallback
может быть непосредственно использован какexpress
Промежуточное ПО интегрировано в экспресс-сервер и инкапсулированоhttp-proxy-middleware
, допустимыйproxyTable
Добавьте свою собственную конфигурацию прокси в:
import { createProxyMiddleware } from 'http-proxy-middleware';
import chalk from 'chalk';
import { Express } from 'express';
import { Options } from 'http-proxy-middleware/dist/types';
interface ProxyTable {
[path: string]: Options;
}
const proxyTable: ProxyTable = {
// 示例配置
'/path_to_be_proxy': { target: 'http://target.domain.com', changeOrigin: true },
};
// 修饰链接的辅助函数, 修改颜色并添加下划线
function renderLink(str: string) {
return chalk.magenta.underline(str);
}
function proxy(server: Express) {
Object.entries(proxyTable).forEach(([path, options]) => {
const from = path;
const to = options.target as string;
console.log(`proxy ${renderLink(from)} ${chalk.green('->')} ${renderLink(to)}`);
// eslint-disable-next-line no-param-reassign
if (!options.logLevel) options.logLevel = 'warn';
server.use(path, createProxyMiddleware(options));
// 如果需要更灵活的定义方式,请在下面直接使用 server.use(path, proxyMiddleware(options)) 定义
});
process.stdout.write('\n');
}
export default proxy;
Чтобы запустить devServer, нам также необходимо установить два инструмента командной строки:
yarn add ts-node cross-env -D
ts-nodeпозволяет нам напрямую запускать код TypeScript,cross-envэто инструмент для установки переменных среды в операционных системах, добавления команд запуска в скрипт npm:
// package.json
{
"scripts": {
"start": "cross-env-shell NODE_ENV=development ts-node --files -P ./scripts/tsconfig.json ./scripts/start.ts --open",
}
}
cross-envВ официальной документации упоминается, что если вы хотите обрабатывать сигналы узлов на платформе Windows, напримерSIGINT
, то есть мыctrl + c
Сигнал срабатывал, когдаcross-env-shell
Команда вместоcross-env
.
ts-node Для повышения скорости выполнения он не будет читаться по умолчанию.tsconfig.json
серединаfiles
, include
а такжеexclude
поля, но считываются на основе зависимостей модуля. Это приводит к некоторым глобальным.d.ts
Файл не будет прочитан, для этого нам нужно указать--files
параметры, подробности можно посмотретьhelp-my-types-are-missing. У нас не так много кода узла, и мы не часто перезапускаем проект, пусть ts-node сканирует весьscripts
Папки не имеют большого значения.
Запустите наш сервер разработки и выйдите с помощью Ctrl + C:
npm start
Оптимизация среды разработки
plugins
Каждый плагин webpack — это класс, который содержит метод применения, когда мы вызываемcompiler.run
илиcompiler.watch
вызывается, передавая его компилятору в качестве аргумента. Объект компилятора предоставляет хуки для каждого периода, и мы можем использовать эти хуки для монтирования функций обратного вызова для реализации различных функций, таких как сжатие, статистика оптимизации и уведомление об успешной компиляции после компиляции.
Показать процесс упаковки
webpack-dev-server
использовать при упаковке--progress
Параметр будет выводить процент в консоль в режиме реального времени для индикации текущего прогресса упаковки, но из рисунка выше видно, что выводятся только некоторые статистические данные (stats). Я знаю три способа отображения процесса упаковки в режиме реального времени:
-
встроенный веб-пакетwebpack.ProgressPluginплагин
ВстроенныйProgressPlugig
Очень примитивно, можно получить текущий прогресс в callback-функции, а потом распечатать в удобном для вас формате:
const handler = (percentage, message, ...args) => {
// e.g. Output each progress message directly to the console:
console.info(percentage, message, ...args);
};
new webpack.ProgressPlugin(handler);
progress-bar-webpack-plugin
Вместо отображения процентов этот плагин отображает индикатор выполнения, нарисованный символами:
Этот плагин на самом деле довольно простой и практичный, но есть ошибка: если при печати индикатора выполнения выводятся другие операторы, индикатор выполнения будет не на своем месте, и наш devServer выдаст адрес после запуска:
console.log(`DevServer is running at ${chalk.magenta.underline(address)} ${logSymbols.success}`);
Использование этого плагина индикатора выполнения вызовет проблемы со следующими проблемами, поэтому я сдался.
webpackbar
Это библиотека в рамках проекта nuxt, поддерживаемаяnuxt, качество абсолютно гарантировано. я использовалprogress-bar-webpack-plugin
, потому что я искал на официальном сайте npmwebpack progress
, в целом надежнее,webpackbar
Не найден. прочитай этоwebpackbar
изpackage.json
,В самом делеkeywords
все пустые.webpackBar
я все еще изучаюant design
Конфиг вебпака увидел, что он использует этот плагин и нашел это сокровище:
Установитьwebpackbar
:
yarn add webpackbar @types/webpackbar -D
Добавить конфигурацию вwebpack.common.ts
Массив плагинов, цвет, который мы используем, реагирует на синий:
import { Configuration } from 'webpack';
const commonConfig: Configuration = {
plugins: [
new WebpackBar({
name: 'react-typescript-boilerplate',
// react 蓝
color: '#61dafb',
}),
],
};
Оптимизировать вывод консоли
Мы используемfriendly-errors-webpack-pluginПлагин делает вывод консоли более дружественным.Следующее использует эффект при успешной компиляции:
yarn add friendly-errors-webpack-plugin @types/friendly-errors-webpack-plugin -D
// webpack.common.ts
import FriendlyErrorsPlugin from 'friendly-errors-webpack-plugin';
const commonConfig: Configuration = {
plugins: [new FriendlyErrorsPlugin()],
};
уведомление о сборке
До стажировки на последнем курсе я не писал полностью проект vue.Во время стажировки в прошлой компании я в основном писал vue.В то время меня очень раздражали частые уведомления об ошибках vue-cli.Я сказал своим коллеги, что я хотел избавиться от этого уведомления, но я никогда не думал, что мои коллеги предпочтут это уведомление.Раз оно кому-то нужно, мы также можем сопоставить его с этим проектом.
Мы используемwebpack-build-notifierДля поддержки уведомлений об ошибках этот плагин написан на TypeScript и не требует установки типов:
yarn add webpack-build-notifier -D
// webpack.common.ts
import WebpackBuildNotifierPlugin from 'webpack-build-notifier';
const commonConfig: Configuration = {
plugins: [
// suppressSuccess: true 设置只在第一次编译成功时输出成功的通知, rebuild 成功的时候不通知
new WebpackBuildNotifierPlugin({ suppressSuccess: true }),
],
};
Так как я не люблю всплывающие уведомления, я закомментировал этот плагин в проекте шаблона, и при необходимости могу открыть его сам.
Строго проверяйте регистр пути
Следующий тест показывает, что webpack по умолчанию нечувствителен к регистру в путях:
Мы используемcase-sensitive-paths-webpack-pluginСтрогая проверка регистра путей:
yarn add case-sensitive-paths-webpack-plugin @types/case-sensitive-paths-webpack-plugin -D
// webpack.common.ts
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
const commonConfig: Configuration = {
plugins: [new CaseSensitivePathsPlugin()],
};
В ходе фактического тестирования упаковки было обнаружено, что этот подключаемый модуль требует очень много времени, а посколькуeslint-import-plugin
По умолчанию ошибка будет сообщаться для путей модулей, которые отличаются только регистром, поэтому наш проект не будет интегрирован.
Циклическая проверка зависимостей
По умолчанию webpack не будет сообщать об ошибке циклических зависимостей черезcircular-dependency-plugin Этот плагин веб-пакета может помочь нам обнаружить циклические зависимости во времени:
yarn add circular-dependency-plugin @types/circular-dependency-plugin -D
// webpack.common.ts
import CircularDependencyPlugin from 'circular-dependency-plugin';
import { projectRoot, resolvePath } from '../env';
const commonConfig: Configuration = {
plugins: [
new CircularDependencyPlugin({
exclude: /node_modules/,
failOnError: true,
allowAsyncCycles: false,
cwd: projectRoot,
}),
],
};
Кстати вотcwd
То есть проблема рабочего пути, официальный документ используется напрямуюprocess.cwd()
, это плохая практика, путь проекта и рабочий путь - это два разных понятия. В узле означает, что путь к проекту никогда не должен использоватьсяpreocess.cwd()
, Поскольку всегда будет несколько пользователей Sand Sculpture, которые не переходят в корневой каталог проекта.process.cwd()
То есть рабочий путь возвращает путь, на котором запущен узел, при условии, что проект находится в/code/projectRoot
, некоторые пользователи открывают терминал прямо в корневом каталоге системы, приходят к выводуnode ./code/projectRoot/index.js
, тогдаindex.js
серединаprocess.cwd()
Возвращает корневой путь системы/
, а не как некоторые думают или/code/projectRoot
.
Получение пути к проекту должно использоватьpath.resolve
:
Эта проблема eslint-import-plugin также сообщит об ошибке, а иногда в проектах TypeScript необходимо импортировать файлы по кругу, поэтому он не интегрирован.
Очистить последний связанный пакет
Некоторые модные плагины были представлены ранее, и некоторые плагины, которые поддерживают наш проект в рабочем состоянии.Теперь мы начинаем вводить некоторые плагины для упаковки.
clean-webpack-pluginон будет удален при первой компиляцииdist
Все файлы в каталоге, но останутсяdist
папка и каждый разrebuild
удалит все файлы, которые больше не используются.
Этот проект тоже написан на TypeScript, и мне всегда кажется, что проект, написанный на TypeScript, имеет необъяснимое ощущение солидности:
yarn add clean-webpack-plugin -D
// webpack.common.ts
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
const commonConfig: Configuration = {
plugins: [new CleanWebpackPlugin()],
};
Автоматически генерировать index.html
Как мы все знаем, передовые интервью Tencent любят тестировать компьютерные сети, Меня много раз спрашивали, как обновить сильный кеш. Чтобы решить проблему немедленного обновления сильного кеша, мы обычно вставляем хеш-значение содержимого файла в имя файла, и тогда домашняя страница не использует сильный кеш. Таким образом, пока вы обновляете сильно кэшированный файл ресурсов, значение хеш-функции обновленного содержимого будет меняться, а также изменится сгенерированное имя файла.Таким образом, когда вы запрашиваете домашнюю страницу, потому что вы при доступе к новому пути к ресурсу он запросит последний ресурс с сервера. Информацию о HTTP-кешировании браузера см. в другой моей статье:Изучите механизм HTTP-кэширования браузера с помощью практики -koa2-server..
Когда мы впоследствии оптимизируем производственную среду, мы разделим CSS в отдельные файлы, если Index.html вставлен во внешний стильlink
помеченhref
Мы задаем его вручную, тогда каждый раз, когда мы изменяем файл стиля, будет генерироваться новое значение хеш-функции.Приходится вручную изменять этот путь, что слишком хлопотно, не говоря уже о том, что файл сохраняется в файловой системе памяти в среда разработки.Вы даже не можете увидеть имя файла.
использоватьhtml-webpack-pluginФайл index.html может быть сгенерирован автоматически, а также могут быть вставлены пути к ресурсам, такие как указанный пакет и разделенный CSS.
Ссылаться наcreat-react-app
шаблон, мы создаем новыйpublic
папку и добавить в нееindex.html
,favico.ico
,manifest.json
и другие документы.public
Папки используются для хранения некоторых файлов, которые будут упакованы вdist
Папки публикуются вместе с файлами.
Установить и настроитьhtml-webpack-plugin:
yarn add html-webpack-plugin @types/html-webpack-plugin -D
import HtmlWebpackPlugin from 'html-webpack-plugin';
import { __DEV__, projectName, resolvePath, projectRoot, hmrPath } from '../env';
const htmlMinifyOptions: HtmlMinifierOptions = {
collapseWhitespace: true,
collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
minifyCSS: true,
minifyJS: true,
minifyURLs: true,
useShortDoctype: true,
};
const commonConfig: Configuration = {
output: {
publicPath: '/',
},
plugins: [
new HtmlWebpackPlugin({
// HtmlWebpackPlugin 会调用 HtmlMinifier 对 HTMl 文件进行压缩
// 只在生产环境压缩
minify: __DEV__ ? false : htmlMinifyOptions,
// 指定 html 模板路径
template: resolvePath(projectRoot, './public/index.html'),
// 类型不好定义,any 一时爽...
// 定义一些可以在模板中访问的模板参数
templateParameters: (...args: any[]) => {
const [compilation, assets, assetTags, options] = args;
const rawPublicPath = commonConfig.output!.publicPath!;
return {
compilation,
webpackConfig: compilation.options,
htmlWebpackPlugin: {
tags: assetTags,
files: assets,
options,
},
// 除掉 publicPath 的反斜杠,让用户在模板中拼接路径更自然
PUBLIC_PATH: rawPublicPath.endsWith('/')
? rawPublicPath.slice(0, -1)
: rawPublicPath,
};
},
}),
],
};
Чтобы пользователи моглиcreate-react-app
Как вindex.html
пройти внутрьPUBLIC_PATH
Чтобы получить доступ к пути выпуска, требуется конфигурацияtemplateParameters
добавлена опцияPUBLIC_PATH
переменная в параметр шаблона,html-webpack-pluginПо умолчанию поддерживается некоторый синтаксис ejs, и мы можем динамически установить его следующими способами.favicon.ico
, mainfest.json
Равный путь к ресурсам:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" href="<%= `${PUBLIC_PATH}/favicon.ico` %>" />
<link rel="apple-touch-icon" href="<%= `${PUBLIC_PATH}/logo192.png` %>" />
<link rel="manifest" href="<%= `${PUBLIC_PATH}/manifest.json` %>" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
скопировать файлы в dist
public
В папке есть несколько файлов, напримерfavico.icon
а такжеmainfest.json
необходимо скопировать вdist
папка, мы можем использоватьcopy-webpack-pluginСкопируйте файл в файловую систему в памяти при использовании devServer и на диск при сборке рабочей среды:
yarn add copy-webpack-plugin @types/copy-webpack-plugin -D
// webpack.common.ts
import CopyPlugin from 'copy-webpack-plugin';
const commonConfig: Configuration = {
plugins: [
new CopyPlugin(
[
{
// 所有一级文件
from: '*',
to: resolvePath(projectRoot, './dist'),
// 目标类型是文件夹
toType: 'dir',
// index.html 会通过 html-webpack-plugin 自动生成,所以需要被忽略掉
ignore: ['index.html'],
},
],
{ context: resolvePath(projectRoot, './public') }
),
],
};
Проверьте типы TypeScript
Чтобы повысить скорость компиляции, babel поддерживает только компиляцию синтаксиса TypeScript, но не проверку типов.Чтобы поддерживать проверку типов ts при упаковке webpack, мы будем использовать плагин webpackfork-ts-checker-webpack-plugin, этот плагин вебпака будет выполнять проверку типов TypeScript параллельно в отдельном процессе, этот проект тоже написан на TypeScript, нам не нужно устанавливать типы.
yarn add fork-ts-checker-webpack-plugin -D
добавить вwebpack.dev.ts
, используемая память ограничена 1G:
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
const devConfig = merge(commonConfig, {
mode: 'development',
plugins: [
new ForkTsCheckerWebpackPlugin({
memoryLimit: 1024,
// babel 转换的是我们前端代码,所以是指向前端代码的 tsconfig.json
tsconfig: resolvePath(projectRoot, './src/tsconfig.json'),
}),
],
});
Изменить заодноwebpack.prod.ts
, потому что наша конструкция производственной среды долго не занимает память, поэтому мы можем настроить ее на больший размер.Мы ограничиваем память, используемую конструкцией производственной среды, до 2G по умолчанию:
// webpack.prod.ts
const prodConfig = merge(commonConfig, {
mode: 'production',
plugins: [
new ForkTsCheckerWebpackPlugin({
memoryLimit: 1024 * 2,
tsconfig: resolvePath(projectRoot, './src/tsconfig.json'),
}),
],
});
артефакт кеша
hard-source-webpack-pluginэто подарокmodules
Предоставьте плагин webpack для промежуточного шага кеша, чтобы увидеть эффект, нам может понадобиться запустить дважды, первый раз - нормальная скорость компиляции, второй раз может быть много раз, возьмите одну из моих разработокПлагин VSCodeДавайте проверим это:
я первый поставилnode_modules/.cache/hard-source
Удалите папку кеша и посмотрите скорость компиляции при отсутствии кеша:
На перекомпиляцию ушло 3,075 секунды:
Вау 🚀, это более чем в 3,6 раза быстрее...
Фактическое измерение показало, что этот плагин займет много времени для упаковки в первый раз, и грядущий webpack 5 будет иметь встроенную функцию.Подробности см. В этом выпуске:[spec: webpack 5] - A module disk cache between build processes . Поэтому наш проект не будет интегрировать этот плагин.
Что ж, плагиновая часть представлена, приступим к настройке загрузчиков!
loaders
По умолчанию webpack поддерживает только импорт js и не может обрабатывать другие файлы, необходимо настроить соответствующий загрузчик, напримерexcel-loader
Вы можете анализировать excel как объект,file-loader
Изображение png может быть проанализировано как окончательный путь выпуска. Загрузчики воздействуют на класс файлов, а плагины воздействуют на различные этапы компиляции веб-пакета.
Раньше мы только настраивалиbabel-loader
, чтобы веб-пакет мог обрабатывать файлы TypeScript.В реальной разработке нам также необходимо поддерживать импорт файлов стилей, файлов изображений, файлов шрифтов и т. д.
Работа с файлами стилей.
Конечной целью, которую мы хотим достичь, является поддержка трех синтаксисов css/less/sass, а также черезpostcss
а такжеautoprefixer
Плагин реализует такие функции, как автоматическое завершение заголовков браузера.
CSS
Для обработки файлов css нам нужно установитьstyle-loaderа такжеcss-loader:
yarn add css-loader style-loader -D
css-loader
Роль заключается в обработке файла CSS@import
а такжеurl()
возвращает объединенную строку CSS, аstyle-loader
Отвечает за преобразование возвращенной строки CSS с помощьюstyle
Теги вставляются в DOM, а также реализуют интерфейс горячего обновления веб-пакета.
style-loader
Официальная примерная конфигурация такова:
module.exports = {
module: {
rules: [
{
// i 后缀忽略大小写
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
};
Вы можете видеть, что используется соответствующий регулярныйi
Суффикс, думаю, это нехорошо, не должен улучшать какую-то бессмысленную отказоустойчивость, использовать.CSS
Выполнение суффикса не должно позволять компилировать webpack. Мы знаем, что порядок загрузки загрузчиков веб-пакета справа налево, поэтому его нужно выполнить первым.css-loader
должно быть выполнено послеstyle-loader
Позади:
// webpack.common.ts
const commonConfig: Configuration = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
// CSS modules 比较耗性能,默认就是禁用的
modules: false,
// 开启 sourcemap
sourceMap: true,
// 指定在 CSS loader 处理前使用的 laoder 数量
importLoaders: 0,
},
},
],
},
],
},
};
less
less-loader
полагатьсяless
:
yarn add less less-loader -D
// webpack.common.ts
const commonConfig: Configuration = {
module: {
rules: [
{
test: /\.less$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: false,
sourceMap: true,
// 需要先被 less-loader 处理,所以这里设置为 1
importLoaders: 1,
},
},
{
// 先让 less-loader 将 less 文件转换成 css 文件
// 再交给 css-loader 处理
loader: 'less-loader',
options: {
sourceMap: true,
},
},
],
},
],
},
};
sass
На самом деле, я никогда не используюless
а такжеstylus
, я использовалsass
.sass
Существует два синтаксических формата, отличающихся суффиксным именем..sass
суффикс похожyml
отступ,.scss
Он похож на фигурные скобки CSS, но поддерживает такие функции, как вложенность и переменные. Учитывая, что я практически не видел проектов, использующихyml
Слишком мало людей используют формат написания, а наш шаблон поддерживает толькоscss
суффикс.sass-loader
также зависит отnode-sass
,node-sass
Это реально сука, я не могу установить его без агента, поэтому я в первой части серии.npmrc
только что настроенnode-sass
Зеркало:
yarn add node-sass sass-loader -D
// webpack.common.ts
const commonConfig: Configuration = {
module: {
rules: [
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: false,
sourceMap: true,
importLoaders: 1,
},
},
{
loader: 'sass-loader',
options: {
// 中间每个 loader 都要开启 sourcemap,才能生成正确的 soucemap
sourceMap: true,
},
},
],
},
],
},
};
postcss
Я помню, когда я изучал CSS3 на уроке веб-дизайна на первом курсе, мне приходилось добавлять в заголовок браузера множество свойств для обеспечения совместимости.В то время мой интерес к CSS сильно снизился, и это было слишком хлопотно. С момента появления Node фронтенд-инжиниринг начал стремительно развиваться: раньше фронтенд назывался Chetuzi, теперь фронтенд-инженеры могут использовать Node и для псевдо-full-stack разработки.
postcssявляется инструментом постпроцессора CSS, потому что сначала появился CSS,postcss
Чтобы обработать его позже, так называемый постпроцессор.
less/sassназываются препроцессорами CSS, потому что они должны бытьless
илиnode-sass
Вы предварительно компилируете код в PS.
Ссылаться наКонфигурация postcss приложения create-реагировать, установите следующие плагины:
yarn add postcss-loader postcss-flexbugs-fixes postcss-preset-env autoprefixer postcss-normalize -D
Добавить кpostcss.config.js
для конфигурацииpostcss
:
module.exports = {
plugins: [
// 修复一些和 flex 布局相关的 bug
require('postcss-flexbugs-fixes'),
// 参考 browserslist 的浏览器兼容表自动对那些还不支持的现代 CSS 特性做转换
require('postcss-preset-env')({
// 自动添加浏览器头
autoprefixer: {
// will add prefixes only for final and IE versions of specification
flexbox: 'no-2009',
},
stage: 3,
}),
// 根据 browserslist 自动导入需要的 normalize.css 内容
require('postcss-normalize'),
],
};
Нам также нужно добавитьbrowserslist
настроить наpackage.json
// package.json
{
"browserslist": [
"last 2 versions",
// ESR(Extended Support Release) 长期支持版本
"Firefox ESR",
"> 1%",
"ie >= 11"
],
}
Оглядываясь назад на конфигурацию CSS, less и sass, мы видим, что здесь много повторений, мы провели рефакторинг и модифицировали.importLoaders
Опции:
function getCssLoaders(importLoaders: number) {
return [
'style-loader',
{
loader: 'css-loader',
options: {
modules: false,
sourceMap: true,
importLoaders,
},
},
{
loader: 'postcss-loader',
options: { sourceMap: true },
},
];
}
const commonConfig: Configuration = {
module: {
rules: [
{
test: /\.css$/,
use: getCssLoaders(1),
},
{
test: /\.less$/,
use: [
// postcss-loader + less-loader 两个 loader,所以 importLoaders 应该设置为 2
...getCssLoaders(2),
{
loader: 'less-loader',
options: {
sourceMap: true,
},
},
],
},
{
test: /\.scss$/,
use: [
...getCssLoaders(2),
{
loader: 'sass-loader',
options: { sourceMap: true },
},
],
},
],
},
};
Работа с изображениями и шрифтами
Вообще говоря, наш проект будет использовать некоторые изображения для проверки эффекта во время разработки, а затем заменит их на CDN вместо использования локальных изображений, упакованных веб-пакетом. Есть два широко используемых загрузчика для обработки файлов:file-loader
а такжеurl-loader
,file-loader
Он используется для анализа импортированного файла как URL-адреса во время публикации и вывода файла в указанное место, в то время как последний является инкапсуляцией первого, что обеспечивает преобразование изображений ниже порогового объема (установленного на 8192 байт ниже) в base64. Я вдруг вспомнил, что интервьюер в Tencent раньше задавал такой вопрос: Есть ли вред от использования base64? На самом деле я считаю преимуществом base64 то, что нет необходимости делать повторный запрос, а недостатком то, что объем изображения, преобразованного в base64, станет больше, и он станет на четыре трети исходного размера.
yarn add url-loader -D
const commonConfig: Configuration = {
module: {
rules: [
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
use: [
{
loader: 'url-loader',
options: {
// 图片低于 10k 会被转换成 base64 格式的 dataUrl
limit: 10 * 1024,
// [hash] 占位符和 [contenthash] 是相同的含义
// 都是表示文件内容的 hash 值,默认是使用 md5 hash 算法
name: '[name].[contenthash].[ext]',
// 保存到 images 文件夹下面
outputPath: 'images',
},
},
],
},
{
test: /\.(ttf|woff|woff2|eot|otf)$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name]-[contenthash].[ext]',
outputPath: 'fonts',
},
},
],
},
],
},
};
Обратите внимание, что здесь я вставил хеш-значение содержимого файла в имя файла, чтобы его можно было решить.Сильный кеш необходимо немедленно обновитьЭта проблема.
sourcemap
devtool | скорость наращивания | восстановить скорость | Производственная среда | качественный |
---|---|---|---|---|
(none) | +++ | +++ | yes | упакованный код |
eval | +++ | +++ | no | сгенерированный код |
cheap-eval-source-map | + | ++ | no | Переведенный код (только строки) |
cheap-module-eval-source-map | o | ++ | no | Исходный код исходного кода (только линейка) |
eval-source-map | -- | + | no | Оригинальный исходный код |
cheap-source-map | + | o | yes | Переведенный код (только строки) |
cheap-module-source-map | o | - | yes | Оригинальный исходный код (только строка) |
inline-cheap-source-map | + | o | no | Переведенный код (только строки) |
inline-cheap-module-source-map | o | - | no | Оригинальный исходный код (только строка) |
source-map | -- | -- | yes | оригинальный исходный код |
inline-source-map | -- | -- | no | оригинальный исходный код |
hidden-source-map | -- | -- | yes | оригинальный исходный код |
nosources-source-map | -- | -- | yes | Нет содержимого исходного кода |
+++
очень быстро,++
быстро,+
Быстрее,o
Средняя,-
помедленнее,--
медленный
Sourcemap является незаменимой функцией для многих инструментов во внешнем мире.Webpack, TypeScript, babel, Powder-assert и другие инструменты преобразования кода должны обеспечивать функцию исходной карты.Исходный код сжат, запутан, полифиллен и без исходной карты. нет способа отладить это проблема позиционирования.
Учитывая скорость компиляции и удобство отладки, я выбираюeval-source-map
, если пользователь чувствует себя медленно при упаковке и не может терпеть отсутствие номера столбца, рассмотрите возможность изменить его наcheap-eval-source-map
.
мы модифицируемwebpack.dev.ts
Инструмент для разработчиковeval-source-map
:
// webpack.dev.ts
import commonConfig from './webpack.common';
const devConfig = merge(commonConfig, {
devtool: 'eval-source-map',
});
Кстати, здесь упоминается плагин webpackerror-overlay-webpack-plugin, который обеспечивает ту же маскировку ошибок, что и create-react-app:
Но у него есть ограничение, заключающееся в том, что он не может использовать какие-либоeval
Исходная карта, заинтересованные читатели могут попробовать следующее.
Горячее обновление
Мы добавили devServer ранееwebpack-hot-middlewareMiddleware, ссылаясь на его документацию, нам нужно сначала добавить плагин webapckwebpack.HotModuleReplacementPlugin
:
// webpack.dev.ts
import { HotModuleReplacementPlugin, NamedModulesPlugin } from 'webpack';
const devConfig = merge(commonConfig, {
plugins: [new HotModuleReplacementPlugin()],
});
также добавить'webpack-hot-middleware/client'
Для горячего обновления патча в наш бандл добавьте его в массив записей:
// webpack.common.ts
import { __DEV__, hmrPath } from '../env';
const commonConfig: Configuration = {
entry: [resolvePath(projectRoot, './src/index.tsx')],
};
if (__DEV__) {
(commonConfig.entry as string[]).unshift(`webpack-hot-middleware/client?path=${hmrPath}`);
}
Добавляя после записиqueryString
Способ позволяет настроить некоторые параметры, как это реализовано? Проверить'webpack-hot-middleware/client'
Исходный код можно посмотреть, вебпак будетqueryString
Вставьте этот файл как глобальную переменную:
На самом деле, мы также поддерживаем горячее обновление CSS (style-loader реализует интерфейс горячего обновления), если мы хотим поддерживать горячее обновление компонентов реакции, нам также необходимо настроитьreact-hot-loader, давайте оптимизируем нашу конфигурацию babel перед ее настройкой.
оптимизация конфигурации бабеля
Ранее мы настроили только один@babel/preset-typescript
Плагины используются для компиляции TypeScript, и на самом деле есть много моментов, которые можно оптимизировать.
@babel/preset-env
В вавилоне,пресет представляет собой набор плагинов,@babel/preset-env
Мы можем заставить Babel добавить только синтаксис и многолифики, которые необходимо преобразовать в соответствии с нашим настроенным браузером.
Установить@babel/preset-env
:
yarn add @babel/preset-env -D
@babel/plugin-transform-runtime
Мы знаем, что по умолчанию babel будет вставлять некоторые вспомогательные функции при необходимости при компиляции каждого модуля, например_extend
, каждый требуемый модуль будет генерировать эту вспомогательную функцию, что приведет к ненужному раздуванию кода,@babel/plugin-transform-runtime
Этот плагин преобразует все вспомогательные функции из@babel/runtime
Импортируйте, чтобы уменьшить размер кода.
yarn add @babel/plugin-transform-runtime -D
@babel/preset-react
несмотря на то что@babel/preset-typescript
может конвертировать код tsx в js, но@babel/preset-react
Он также интегрирует некоторые полезные плагины для реактивных проектов.
@babel/preset-react
По умолчанию включены следующие плагины:
- @babel/plugin-syntax-jsx
- @babel/plugin-transform-react-jsx
- @babel/plugin-transform-react-display-name
если установленоdevelopment: true
также включится:
Установить зависимости@babel/preset-react
:
yarn add @babel/preset-react -D
react-hot-loader
Чтобы добиться частичного обновления компонентов, нам нужно установитьreact-hot-loader
Плагин Бабель.
yarn add react-hot-loader
Этот плагин не нужно устанавливатьdevDependencies
, который не будет выполняться в производственной среде и будет занимать минимум места. На самом деле, чиновник разрабатывает следующее поколение плагина быстрого обновления реакции.React Fast Refresh, но в настоящее время не поддерживает webpack.
Для того, чтобы увидеть эффект теста, мы устанавливаем ведро семейства React и настраиваем его.src
Содержимое по умолчанию в папке:
yarn add react react-dom react-router-dom
yarn add @types/react @types/react-dom @types/react-router-dom -D
react
это основной интерфейс фреймворка,react-dom
Отвечает за монтирование наших реагирующих компонентов в настоящий DOM,react-dom-router
реализуетсяreact-router
Интерфейс к библиотеке маршрутизации для веб-платформы.
Позволятьreact-hot-loader
Возьмите на себя наш корневой компонент реакции, на самом деле, эта горячая функция являетсяhocХорошо:
// App.ts
import React from 'react';
import { hot } from 'react-hot-loader/root';
import './App.scss';
const App = () => {
return (
<div className="app">
<h2 className="title">react typescript boilerplate</h2>
</div>
);
};
export default hot(App);
Добавьте исправление в запись веб-пакета:
const commonConfig: Configuration = {
entry: ['react-hot-loader/patch', resolvePath(projectRoot, './src/index.tsx')],
};
В официальной документации упоминается, что если требуется поддержкаreact hooks
Горячее обновление, нам тоже нужно установить@hot-loader/react-dom
, используйте его для замены значения по умолчаниюreact-dom
чтобы добавить некоторые дополнительные функции горячего обновления, чтобы заменитьreact-dom
Нам нужно настроить псевдоним webpack:
// webpack.common.ts
module.exports = {
resolve: {
alias: {
'react-dom': '@hot-loader/react-dom',
},
},
};
В сочетании с вышеупомянутым плагином babel окончательная модификацияbabel.config.js
стали:
const envPreset = [
'@babel/preset-env',
{
// 只导入需要的 polyfill
useBuiltIns: 'usage',
// 指定 corjs 版本
corejs: 3,
// 禁用模块化方案转换
modules: false,
},
];
module.exports = function (api) {
api.cache(true);
return {
presets: ['@babel/preset-typescript', envPreset],
plugins: ['@babel/plugin-transform-runtime'],
env: {
// 开发环境配置
development: {
presets: [['@babel/preset-react', { development: true }]],
plugins: ['react-hot-loader/babel'],
},
// 生产环境配置
production: {
presets: ['@babel/preset-react'],
plugins: ['@babel/plugin-transform-react-constant-elements', '@babel/plugin-transform-react-inline-elements'],
},
},
};
};
Обратите внимание, что в нашей производственной среде также установлены два подключаемых модуля для оптимизации производственной среды:
yarn add @babel/plugin-transform-react-constant-elements @babel/plugin-transform-react-inline-elements -D
@babel/plugin-transform-react-constant-elements
Эффект состоит в том, чтобы поднять переменные в функциональном компоненте из функции следующим образом, чтобы избежать повторных объявлений и ненужной сборки мусора каждый раз, когда функциональный компонент вызывается снова:
const Hr = () => {
return <hr className="hr" />;
};
// 转换成
const _ref = <hr className="hr" />;
const Hr = () => {
return _ref;
};
@babel/plugin-transform-react-inline-elements
Читатели могут обратиться к этому вопросу реакции:Optimizing Compiler: Inline ReactElements.
Оптимизация производственной среды
Добавить уведомление об авторских правах
Это построено непосредственно с помощью веб-пакетаBannerPlugin
Только что:
import { BannerPlugin } from 'webpack';
const mergedConfig = merge(commonConfig, {
mode: 'production',
plugins: [
new BannerPlugin({
raw: true,
banner: `/** @preserve Powered by react-typescript-boilerplate (https://github.com/tjx666/react-typescript-boilerplate) */`,
}),
],
});
Следует отметить, что мы добавили в комментарии уведомление об авторских правах@preserve
отметка, которую мы будем использовать позжеterser
При сжатии кода при сборке производственной среды все комментарии удаляются при сжатии кода, за исключением некоторых комментариев, содержащих специальные теги, например те, которые мы добавили@preserve
.
Разделение CSS
Если CSS включен в пакет JS, который мы упаковали, это приведет к большому конечному объему, а в тяжелых случаях доступ к домашней странице вызовет короткий белый экран. Разделить CSS, который мы используем напрямуюmini-css-extract-plugin:
yarn add mini-css-extract-plugin -D
Измените конфигурацию производственной среды:
// webpack.prod.ts
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
const prodConfig = merge(commonConfig, {
mode: 'production',
plugins: [
new MiniCssExtractPlugin({
// 文件名中插入文件内容的 hash 值
filename: 'css/[name].[contenthash].css',
chunkFilename: 'css/[id].[contenthash].css',
ignoreOrder: false,
}),
],
});
mini-css-extract-pluginтакже обеспечиваетmini-css-extract-plugin.loader
, нельзя сочетать сstyle-loader
сосуществовать, поэтому мы модифицируемwebpack.common.ts
Конфигурация позволяет среде разработки использоватьstyle-loader
Использование в производственной средеmini-css-extract-plugin.loader
:
import { loader as MiniCssExtractLoader } from 'mini-css-extract-plugin';
import { __DEV__ } from '../env';
function getCssLoaders(importLoaders: number) {
return [
__DEV__ ? 'style-loader' : MiniCssExtractLoader,
{
loader: 'css-loader',
options: {
modules: false,
sourceMap: true,
importLoaders,
},
},
{
loader: 'postcss-loader',
options: { sourceMap: true },
},
];
}
сжатие кода
Сжатие JavaScript
Многие учебники в Интернете используют webpack для сжатия кода.uglifyjs-webpack-plugin, на самом деле, этот репозиторий уже давно отказался от поддержки, и он не поддерживает синтаксис ES6, основной разработчик веб-пакетаevilebottnawiобратиться к обслуживаниюterser-webpack-plugin. Мы используемterser-webpack-pluginКод сжимается в производственной среде, и мы можем воспользоваться преимуществами нового webpack4.tree-shakingУдалите мертвый код в коде, чтобы еще больше уменьшить размер пакета:
yarn add terser-webpack-plugin @types/terser-webpack-plugin -D
Treeshake должен быть вpackage.json
Средняя конфигурацияsideEffects
поле, вы можете прочитать официальную документацию для деталей:Tree Shaking.
CSS-сжатие
Сжать CSS с помощьюoptimize-css-assets-webpack-plugin:
yarn add optimize-css-assets-webpack-plugin @types/optimize-css-assets-webpack-plugin -D
Исправлятьwebpack.prod.ts
:
import TerserPlugin from 'terser-webpack-plugin';
import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';
const prodConfig = merge(commonConfig, {
mode: 'production',
optimization: {
// 使用 minimizer 而不是默认的 uglifyJS
minimize: true,
// 两个 minimizer:TerserPlugin 和 OptimizeCSSAssetsPlugin
minimizer: [new TerserPlugin({ extractComments: false }), new OptimizeCSSAssetsPlugin()],
},
});
Анализ сборки
Добавляем несколько плагинов webpack для анализа сборки
статистика времени
Мы используемspeed-measure-webpack-pluginСтатистика по времени упаковки:
yarn add speed-measure-webpack-plugin -D
На этом этапе проекта мы наконец столкнулись с первой библиотекой без файла объявления типа TypeScript.scripts/typings/index.d.ts
файл, так как типов для записи мало,index.d.ts
Так же, как файл глобального объявления, добавьтеspeed-measure-webpack-plugin
Объявление внешнего модуля:
// scripts/typings/index.d.ts
declare module 'speed-measure-webpack-plugin' {
import { Configuration, Plugin } from 'webpack';
// 查看官方文档,需要哪些选项就声明哪些选项就行
// 可以看出 TypeScript 是非常灵活的
interface SpeedMeasurePluginOptions {
disable: boolean;
outputFormat: 'json' | 'human' | 'humanVerbose' | ((outputObj: object) => void);
outputTarget: string | ((outputObj: string) => void);
pluginNames: object;
granularLoaderData: boolean;
}
// 继承 Plugin 类, Plugin 类都有 apply 方法
class SpeedMeasurePlugin extends Plugin {
constructor(options?: Partial<SpeedMeasurePluginOptions>);
wrap(webpackConfig: Configuration): Configuration;
}
export = SpeedMeasurePlugin;
}
Исправлятьwebpack.prod.ts
:
import SpeedMeasurePlugin from 'speed-measure-webpack-plugin';
const mergedConfig = merge(commonConfig, {
// ...
});
const smp = new SpeedMeasurePlugin();
const prodConfig = smp.wrap(mergedConfig);
пакетный анализ
yarn add BundleAnalyzerPlugin @types/BundleAnalyzerPlugin -D
Добавляем npm-скрипт для сборки с анализом пакетов, потому что иногда нам не хочется открывать браузер для анализа размера и доли каждого модуля:
"scripts": {
"build": "cross-env-shell NODE_ENV=production ts-node --files -P scripts/tsconfig.json scripts/build",
"build-analyze": "cross-env-shell NODE_ENV=production ts-node --files -P scripts/tsconfig.json scripts/build --analyze",
},
Исправлятьwebpack.prod.ts
:
// 添加
import { isAnalyze } from '../env';
if (isAnalyze) {
mergedConfig.plugins!.push(new BundleAnalyzerPlugin());
}
Таким образом, мы можем запускать, когда хотим увидеть размер и пропорции каждого модуля в комплекте.npm run build-analyze
, автоматически откроет страницу, показанную на изображении выше, в вашем браузере.
Подготовьте версию, сжатую gzip.
Мы используем официально поддерживаемыеcompression-webpack-pluginЧтобы подготовить сжатые gzip версии каждого упакованного файла:
yarn add compression-webpack-plugin @types/compression-webpack-plugin -D
Отслеживание размера сжатого ресурса
size-pluginЭто продукт, созданный Google, который показывает размер каждого фрагмента веб-пакета после сжатия gzip и изменение размера по сравнению с прошлым разом. Войти.
yarn add size-plugin -D
Есть ли официальный файл типов для этой библиотеки, мы добавляемsize-plugin
Объявление внешнего модуля:
// scripts/typings/index.d.ts
declare module 'size-plugin' {
import { Plugin } from 'webpack';
interface SizePluginOptions {
pattern: string;
exclude: string;
filename: string;
publish: boolean;
writeFile: boolean;
stripHash: Function;
}
class SizePlugin extends Plugin {
constructor(options?: Partial<SizePluginOptions>);
}
export = SizePlugin;
}
// webpack.prod.ts
const mergedConfig = merge(commonConfig, {
plugins: [
// 不输出文件大小到磁盘
new SizePlugin({ writeFile: false }),
],
});
Суммировать
только что выучил словоTL; DR
, что на самом деле:
Too long; didn't read.
На самом деле, я часто бываю таким, ха-ха. Здесь уже более 10 000 слов, по моим оценкам, мало кто это увидит. Я думаю, что это по-прежнему очень естественно, от среды разработки до производственной среды, от базовой конфигурации до консоли оптимизации, подготовить шаги версии Gzip для сжатия этих лагерей. На написание этой статьи на самом деле уходит основное время, каждый плагин я стараюсь описать их роль, если есть платная, то тоже выйду в коде аннотацию или текстовое описание. Я знаю что эта статья относительно плоха для каких-то базовых границ или как настроить WebPack.Вероятно не видно.Это нормально.Раньше я был таким,но думаю можно зубы кусать.Хотя,хотя много где не понятно, всегда что-нибудь полезное для себя узнаешь, а можно и как словарь собрать. Эта статья не сильно связана с React + TypeScript, вы не можете разработать Vue-Loader для разработки VUE?Что еще более важно, я надеюсь, что некоторые читатели смогут почерпнуть из нее дух исследования. Страх не означает невозможность. Только практикуя и исследуя, мы можем постичь истинное знание.
Наконец, мы добавляем наш скрипт сборкиbuild.ts
:
// scripts/build.ts
import webpack from 'webpack';
import prodConfig from './configs/webpack.prod';
import { isAnalyze } from './env';
const compiler = webpack(prodConfig);
compiler.run((error, stats) => {
if (error) {
console.error(error);
return;
}
const prodStatsOpts = {
preset: 'normal',
modules: isAnalyze,
colors: true,
};
console.log(stats.toString(prodStatsOpts));
});
Недавно я был занят выпускным и искал работу, и следующая может быть через месяц или около того. Если читателю ничего не понятно в статье, рекомендуется сначала прочитать ееисходный код, если у вас остались вопросы, вы можетеgithub issuesИли задавайте мне вопросы в области комментариев издательской платформы.Если вы считаете, что эта статья полезна для вас, вы можете поставить звезду 😁.
В следующей статье должно быть описано, как интегрироватьant design
,lodash
Дождитесь появления популярных библиотек и оптимизируйте их упаковку...
Эта статья представляет собой оригинальный контент, впервые опубликованный вличный блог, Пожалуйста, укажите источник.