введение
Я недавно начал строить свой собственный блог-сайт, так как мой проект не торопится и не торопится, и мне не нужно делать ничего сверхъестественного, поэтому я не планирую использовать те готовые отличные инструменты для строительства на Интернет, чтобы начать проект, но из веб-пакета + React начал создавать простой псевдоинженерный интерфейсный проект, и, кстати, узнать больше о веб-пакете, а затем исследовать и улучшать проект понемногу в реальной разработке.
CHANGE LOG
2020.5.17
- Ввести эслинта
- Оптимизировать конфигурацию веб-пакета
- Изменена ошибка @babel/transform-runtime, на которую указал @Lu Feifei.
- Решена проблема с ошибкой псевдонима пути ts
2020.5.30
- Реализовать редактор markdonw
- Webpack упаковывает изображения размером менее 20 КБ в base64, чтобы уменьшить количество ресурсов.
webpack
Говоря о веб-пакете, мы должны говорить о фронтенд-инжиниринге. По мере того, как JavaScript продолжает становиться больше и сильнее, внешний интерфейс выполняет не только некоторые действия по отображению страницы и специальные эффекты страницы, но и все больше и больше взаимодействует с пользователем и оптимизирует взаимодействие с пользователем, а некоторые бизнес-сценарии также могут быть доступны из серверной части. наклоняться вперед. Фронтенд-инженеры уже имеют дело не с простыми веб-сайтами, а с более сложными веб-приложениями, а это значит, что построение фронтенд-проектов должно строиться в более системном направлении.
Помню, когда я начинал изучать фронтенд, я просто писал одну страницу и один файл + html/css/js все вместе, нет сомнения, что эффективность такой разработки очень низкая и она будет делать много лишнего и повторяющегося работы, поэтому необходимо ставить фронтенд Проект анализируется, организуется и строится как системный проект, и он более компонентный, модульный и стандартизованный на уровне разработки, что делает структуру проекта понятной, уменьшает избыточность нагрузки и повышает эффективность работы.
Фактически, фронтенд-инжиниринг также включает в себя разработку/развертывание/тестирование/автоматизацию/производительность/стабильность/доступность/обслуживаемость, а также некоторые концепции, такие как развертывание/выпуск/CI/CD/оттенки серого.
Основываясь на вышеизложенном, в интерфейсе появился ряд отличных модульных инструментов для создания и упаковки интерфейсов, таких как Grunt/Gulp/Webpack/Rollup и т. д. Каждый инструмент имеет свои собственные применимые сценарии и функции, которые не так хорош, как другие инструменты.Больше нет описания, в этой статье в основном описывается идея построения front-end проектов с помощью webpack и некоторые общие моменты оптимизации
Поднимите концепцию веб-пакета
По сути,webpackэто современное приложение JavaScriptсборщик статических модулей. Когда веб-пакет обрабатывает приложение, он рекурсивно создаетграфик зависимости, который содержит все модули, необходимые приложению, а затем упаковывает все эти модули в один или несколькоbundle.
Снова украсть изображение webpack
Вышеупомянутая концепция и достаточная передача идей и роль веб-пакета, вывод о слове: проектирование переднего плана, мы только что разработали его, все здание упадет в детей Божьих на лошадь веб-пакета.
В Интернете есть много отличных статей об обучении и создании проектов с нуля. Эта статья также опирается на статьи больших парней, чтобы научиться использовать веб-пакет ===>Webpack создает простой проект React, так что здесь я также просматриваю свое поверхностное понимание веб-пакета и непосредственно вхожу в процесс построения
Построить проект
Как говорится, без правил круг не образуется.Прежде чем делать проект, нужно подумать о первоначальном замысле, ожиданиях и возможных форс-мажорных факторах проекта (например, вы вдруг не захотите продолжать писать этот день...). Поэтому перед инициализацией проекта необходимо составить простой план проекта, чтобы облегчить последующие итерации разработки и обслуживания.
Структура проекта
├── dist 编译后项目文件
├── node_modules 依赖包
├── public 静态资源文件
├── script 构建文件
│ ├── analyzer.js 分析包大小
│ ├── build.js 打包
│ ├── dll.js 抽离公共的依赖
│ ├── start.js 开发环境启动
│ ├── webpack.base.config.js webpack基础配置
├── src 源码
│ ├── pages 页面组件
│ ├── ... 其它
│ ├── index.tsx 入口文件
├── .babelrc babel 配置
├── .eslintrc.js eslint 配置
├── .gitignore 忽略提交到git目录文件
├── .prettierrc 代码美化
├── package.json 依赖包及配置信息文件
├── tsconfig.json typescript 配置
├── README.md 描述文件
Поскольку основное внимание в этом абзаце уделяется теории, в нем содержится небольшое количество исходного кода. Итак, это краткое описание отдельных файлов в Script:
-
webpack.base.config.js
Это базовый файл веб-пакета, в котором настроены базовые конфигурации, включая модули ввода, вывода, синтаксического анализа, сжатие кода и дедупликацию, распаковку и извлечение, а также шаблоны html. Почему его нужно вынимать, в основном по двум причинам
- Извлеките общий код, сделав код более кратким и понятным.
- Убедитесь, что среды разработки и производства максимально согласованы, и избегайте некоторых бессмысленных проблем, вызванных различиями в средах.
-
start.js
Запись для запуска среды разработки проекта, в которой объединены базовая конфигурация и конфигурация разработки, такие как devServer/HMR и другие элементы конфигурации среды разработки.
plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin() ] -
build
Пакет проекта и запись о сборке, помимо базовой конфигурации, также включают некоторый код мониторинга производительности сборки и т. д. Например, используйте HappyPack для ускорения упаковки:
new HappyPack({ threads, id: 'jsx', loaders: ['babel-loader'] }), -
dll.js
Извлеките сторонние модули, чтобы уменьшить объем сборки
-
analyzer.js
После того, как пакет собран, необходимо провести детальный анализ размера разделения пакета, что способствует дальнейшей оптимизации распаковки и корректировке структуры кода.
1. Стек технологий
Выбор фронтенд-технологии в основном неотделим от трех основных фреймворков react/vue/angular.Каждый фреймворк имеет применимые и неприменимые сценарии, а также экосистему, созданную фреймворком, затраты на обучение, стабильность и возможность сотрудничать с базовой конструкцией. и т.д. И так далее, ряд других соображений.
Поскольку это личный проект, я напрямую рассматриваю возможность разработки семейства typescript+react, а конфигурация относительно проста.
// babel编译 .babelrc配置
{
test: /.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader'
},
// 使用typescript
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'ts-loader',
},
}
// .babelrc
{
"presets": [
"@babel/react",
"@babel/preset-env"
],
"plugins": [
"@babel/plugin-transform-runtime"
]
}
2. Совместимость
Вопрос, которого нельзя избежать в проектах с интерфейсом, — это проблема совместимости.Существуют две основные стратегии решения проблем с совместимостью с интерфейсом.
-
прогрессивное улучшение
Прогрессивное улучшение обеспечивает работу браузеров более ранних версий и немного улучшает работу новых браузеров, поддерживающих новые функции.
-
изящная деградация
Мягкая деградация, наоборот, обеспечивает наилучшие возможности для современных браузеров, в то время как старые браузеры отступают и гарантируют приблизительную функциональность.
Здесь выбрана элегантная стратегия даунгрейда (потому что на самом деле я никогда не задумывался о совместимости браузеров младших версий =.=)
3. Спецификации кодирования
Стандарт кодирования проекта также является общей темой.Хорошие привычки кодирования способствуют долгосрочному обслуживанию проекта, повышают эффективность разработки и качество кода, снижают нагрузку на обслуживание устаревшей системы и уменьшают технический долг, вызванный мусорным кодом. .
Что обычно используется и рекомендуется в Интернете, так это eslint для ограничения спецификаций кода, затем использование Prettier для украшения и, наконец, использование хаски для ограничения представлений.
Однако, поскольку я достаточно уверен в своем стиле кодирования, этот проект бесполезен.
В-четвертых, внешний мониторинг и оптимизация
Зрелый веб-сайт должен поддерживаться зрелой системой мониторинга. Хорошая система мониторинга может не только помочь мне проанализировать pv/uv/пользовательские привычки/поведение пользователей и т. д., но также иметь возможность своевременно обнаруживать отклонения системы/системы обработки, устранять ошибки и избегать серьезных производственных сбоев.
Хоть это и персональный сайт соленой рыбы, но у меня все же есть мечта.Учитывая, что в будущем я могу стать популярным, я буду использовать некоторые системы мониторинга и статистики в Интернете, чтобы предотвратить проблемы. Порыскав вокруг, либо приходится тратить деньги (пустые карманы, нет бюджета), либо вы не можете найти ожидаемых результатов, поэтому я написал несколько закопанных интерфейсов (Обратитесь к разделу реализации интерфейса в предыдущей статье.), лучше говорить, чем ничего, используется только для мониторинга исключений и статистического анализа, что удобно для непрерывной оптимизации последующей системы
4. Некоторые неважные характеристики в этом проекте
Есть еще много спецификаций, которые важны для командных проектов, но поскольку этот проект является личным проектом, я буду кратко упомянуть это здесь:
- Управление ветвями кода
- Спецификация процесса
- спецификация сотрудничества
- Разумное и четкое разделение труда
- Процесс сборки релиза
- Спецификации качества кода
- Рекомендации по стилю кода
- Уточнение/обзор кода
- Спецификация испытаний
- Текущая программа технического обслуживания
- спецификация документа
- ...
Пять, некоторые моменты оптимизации
Динамичное введение
Для некоторых распространенных модулей, таких как избыточность маршрутизации и т.п., можно ввести через веб-пакет динамического модуля require.context.
-
Динамически импортировать маршруты
let RouteMap = [] // 白名单 const WHITE_LIST = ['login'] // NOTE: 默认不考虑路由传参的情况[xxx/:id],这种场景还没想好用什么方式来解决 // 动态读取pages下的所有目录,把每个目录都当成一个路由 const files = require.context('./pages', true, /\/index\.jsx$/) files.keys().forEach(key => { // const pattern = /(?<=\/).+(?=\/)/ const pattern = /\.\/(.+)\/(\w+)\.jsx?$/ const route = key.match(pattern)[1] // 白名单路由跳过 if (!WHITE_LIST.includes(route)) { RouteMap.push({ path: route, component: files(key).default }) } }) const LayoutRoute = (props) => ( <Layout {...props}> <Switch> { RouteMap.map(({path, component}) => <Route key={path} exact path={`/app/${path}`} component={component} />) } </Switch> </Layout> )Таким образом, нет необходимости каждый раз импортировать файл маршрута и сопоставлять файл маршрута с соответствующим маршрутом.Пока существует много правил и хороших правил, даже шаг настройки файла маршрута может быть реализован в этом путь. .
-
Динамически регистрировать избыточные модули
// 自动注册reducer模块 let rootReducer = {} const reducersFiles = require.context('./reducers', false, /\.js$/) reducersFiles.keys().map(filename => { // const pattern = /(?<=\.\/).+(?=\.js)/ 新语法,部分浏览器还不支持 const pattern = /\/(\w+)\.jsx?$/ try { const matchRes = filename.match(pattern) const key = matchRes[1] rootReducer[key] = reducersFiles(filename).default } catch (e) { console.log('无法注册', e) } }) // 自动注册saga模块 let rootSagaList = [] const sagaFiles = require.context('./sagas', false, /\.js/) sagaFiles.keys().map(filename => { rootSagaList.push(fork(sagaFiles(filename).default)) })
Через require.context можно сэкономить много бесполезного и повторяющегося кода.По крайней мере, не нужно импортировать файлы модулей один за другим.Идею dva можно изучить в будущем, а потом выполнять слой упаковки.Конечно, это отдельная история.
вавилонская оптимизация
Из-за совместимости между браузерами и разницы во времени между браузерами, реализующими спецификацию w3c, чтобы избежать того, чтобы API, который мы используем, не был реализован в браузере, обычно интерфейсные проекты вводят Babel для компиляции. Представление инструмента компиляции здесь также должно иметь возможность выполнять некоторые простые оптимизации структуры проекта.
-
Исключить из компиляции node_modules/bower_components, исключить: /(node_modules|bower_components)/
// webpack.base.config.js { test: /.jsx?$/, exclude: /(node_modules|bower_components)/, loader: 'babel-loader' } -
Используйте @babel/plugin-transform-runtime для извлечения общих модулей и уменьшения размера кода.
// .babelrc "plugins": [ "@babel/plugin-transform-runtime" ] -
Используйте happypack, чтобы открыть несколько тем
Поскольку webpack является однопоточным, иногда мы не можем в полной мере воспользоваться многоядерными преимуществами современных процессоров, поэтому нам нужно найти способ использовать это преимущество для повышения эффективности упаковки и компиляции. можно использовать HappyPack для таких вещей, позволять разбивать задачи на несколько подпроцессов для одновременного выполнения
const HappyPack = require('happypack') // 手动创建进程池 const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }) module.exports = { module: { rules: [ ... { test: /\.ts$/, // 问号后面的查询参数指定了处理这类文件的HappyPack实例的名字 loader: 'happypack/loader?id=happyBabel', ... }, ], }, plugins: [ ... new HappyPack({ // 这个HappyPack的“名字”就叫做happyBabel,和楼上的查询参数遥相呼应 id: 'happyBabel', // 指定进程池 threadPool: happyThreadPool, loaders: ['babel-loader?cacheDirectory'] }) ], }
Мониторинг производительности упаковки
Если мы упакуем слишком большой пакет, это приведет к очень долгому времени загрузки страницы, что является очень, очень плохим опытом, поэтому нам нужен механизм подсказок.Когда наш упакованный файл слишком большой, мы можем своевременно Зная размер файла , вебпакТакже предусмотрена очень удобная функцияperformance
performance: {
hints: 'warning', // 提示类型
// 定一个创建后超过 200kb 的资源,将展示一条警告
maxAssetSize: 1024 * 200,
maxEntrypointSize: 1024 * 200,
}
Таким образом, мы можем своевременно обнаруживать пакеты большого объема, которые появляются в упаковке, а затем дополнительно оптимизировать упаковку и разработку.
Удалить сторонние зависимости
webpack предоставляет очень приятную функциюDllPlugin, он вполне может извлекать сторонние зависимости, а затем создавать файл сопоставленияmanifest.json
const OUTPUT_PATH = BUILD_OUTPUT_DIR ? `${BUILD_OUTPUT_DIR}/lib/` : path.resolve('dist/lib')
// ...
module.exports = {
// ...
plugins: [
// 抽离包的插件
new webpack.DllPlugin({
context: __dirname,
path: path.resolve(OUTPUT_PATH, 'manifest.json'),
name: '[name]-[hash]'
})
]
}
После извлечения пакетов вам необходимо использовать эти пакеты через этот файл сопоставления, который будет использоваться в это время.DllReferencePluginСопоставление со связанными зависимостями.В это время, когда проект запущен, эти зависимости можно использовать в обычном режиме.
new webpack.DllReferencePlugin({
context: __dirname,
sourceType: "commonjs2",
name: 'lib_dll',
manifest: dllMap // manifest.json地址
})
splitChunks
После webpack4.x предоставляет более удобную функцию распаковкиsplitChunks, который является оптимизированной версиейDllPluginБар
Советы: я не знаю, проблема ли это в конфигурации.Изначально некоторые пакеты будут упакованы дважды, когда они используются одновременно, но размер будет увеличиваться.
optimization: {
// 打包后再拆包
splitChunks: {
// 这表示将选择哪些块进行优化。当提供一个字符串,有效值为 all, async 和 initial. 提供 all 可以特别强大,因为这意味着即使在异步和非异步块之间也可以共享块。
chunks: 'all',
// 要生产的块最小大小(以字节为单位)
minSize: 10240,
maxSize: 0,
// 分割前必须共享模块的最小块数
minChunks: 1,
// 按需加载时的最大并行请求数
maxAsyncRequests: 5,
// 入口点处的最大并行请求数
maxInitialRequests: 3,
// 指定用于生成的名称的分割符 vendors~main.js
automaticNameDelimiter: '~',
// 拆分块的名称
name: true,
cacheGroups: {
// 抽出css
// styles: {
// name: 'static/css/styles',
// test: /\.(css|scss|sass)$/,
// chunks: 'all',
// enforce: true,
// },
// 抽出公共模块
commons: {
name: 'static/js/components',
test: path.join(__dirname, '..', 'src/components'),
minChunks: 3,
priority: 5,
reuseExistingChunk: true,
},
// 单独抽出react
react: {
test: /[\\/]node_modules[\\/](react)[\\/]/,
name: 'static/js/react',
priority: 20,
},
// 单独抽出react-dom
reactDom: {
test: /[\\/]node_modules[\\/](react-dom)[\\/]/,
name: 'static/js/react-dom',
priority: 20,
},
// 抽出第三方的包
vendors: {
name: 'static/js/vendors',
test: /[\\/]node_modules[\\/]/,
priority: 15,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
}
Приведенная выше конфигурация извлекает общий компонент и относительно большие зависимости Webpack извлечет зависимости react-dom/react/другие сторонние и сгенерирует соответственно три файла Конечно, если есть другие сравнения Если есть большая зависимость, вы можете также добавьте правила для его извлечения.
6. Последующие планы
- Постоянно оптимизируйте конфигурацию веб-пакета
- Ввести код спецификации eslint
- Знакомство с модульным тестированием реализации JEST
- Создавайте стратегии кэширования для оптимизации взаимодействия с пользователем
- Проект модернизации для рендеринга ssr/server
- Извлечение общих компонентов и создание библиотек компонентов
- Оптимизация внешнего модуля мониторинга
- ...
заключительные замечания
На бумаге я всегда чувствую себя мелким, и я абсолютно точно знаю, что это дело должно быть реализовано.Я видел, как другие строят проект небрежно.Я чувствовал себя лысым и холодным, когда подошла моя очередь.Иди за волосами.
👏 Если заинтересованные друзья найдут что-то не так/неадекватно/нужно доработать/оптимизировать, со временем обновлю, давайте становиться лучше вместе!
👏 Если статья была вам полезна, жмите 👍 и вперёд!