Заполнение пробелов
пройти черезКак использовать webpack для повышения эффективности разработки интерфейса (1)обучения, мы смогли пройтиwebpack
изloader
а такжеpiugin
механизм для обработки различных файловых ресурсов. Осторожные друзья обнаружили, что файлы шрифтов иHTML
середина<img>
Обработка ресурсов метки, давайте сначала решим эту проблему.
Продолжая предыдущую статью, наша структура каталогов выглядит так:
Первый — это обработка файлов шрифтов, модификацияwebpack.config.js
// webpack.config.js
// 新增对字体的loader
{
test: /\.(eot|woff|woff2|ttf)$/,
use: [{
loader: 'url-loader',
options: {
name: '[name].[hash:7].[ext]',
limit: 8192,
outputPath: 'font', // 打包到 dist/font 目录下
}
}]
},
Как мы случайно скачали шрифт из интернета и поместили его вsrc
папку и изменитьsrc/index.html
<!-- src/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>my-webpack</title>
</head>
<body>
<h1>webpack大法好!!前端大法好!!</h1>
</body>
</html>
существуетindex.scss
импортировать шрифты
/* src/index.scss */
/* 添加以下样式 */
@font-face {
font-family: 'myFont';
src: url('./font/ZaoZiGongFangQiaoPinTi-2.ttf');
}
h1 {
font-family: 'myFont';
}
Перед этим удалите каждый репакdist
папка, это действительно хлопотно, теперь мы можем использоватьclean-webpack-plugin
, он может удалять указанную папку каждый раз при упаковке, мы выполняем его в командной строкеnpm i clean-webpack-plugin -D
Исправлятьwebpack.config.js
// webpack.config.js
// 新增以下引入
const CleanWebpackPlugin = require('clean-webpack-plugin');
// 新增以下插件
plugins: [
new CleanWebpackPlugin(['dist']) // 在最新的v2版本中,如果默认删除dist文件夹,只需new CleanWebpackPlugin()
],
Затем выполните в командной строкеnpm run build
,нашdist
Папка будет автоматически удалена, и будут выведены следующие результаты.Вы можете видеть, что хотя мы успешно упаковали файл шрифта, файл шрифта слишком большой и дажеwebpack
предупредил[big]
.
- Открыть для файлов шрифтов
CDN
ускорить - По дизайну дает хороший стиль произвел диаграмму
- использовать
font-spider
Сжать шрифты
Давайте попрактикуемся в третьем решении, которое я также рекомендую. Выполнить последовательно в командной строке
npm i font-spider -D
font-spider ./dist/index.html
можно увидеть вблизи4MBРазмер файла шрифта моментально сжимается до недостаточного6KB! ! ! Эффект страницы точно такой же, как и раньше.
И дляHTML
в документе<img>标签的引入问题
, нам нужно использоватьhtml-loader
, Так и будетHTML
в документеimg.src
анализируется вrequire
, чтобы добиться введения изображений, нечего сказать, давайте посмотрим на эффект напрямую. выполнить в командной строкеnpm i html-loader -D
Измените следующие файлы
// webpack.config.js
// 新增对html的loader
{
test: /\.html$/,
use: {
loader: 'html-loader',
options: {
attrs: ['img:src'] // img代表解析标签,src代表要解析的值,以key:value形式存在于attrs数组中
}
}
}
<!-- src/index.html -->
<body>
+ <img src="./leaf.png" alt="">
</body>
выполнить в командной строкеnpm run build
,Проверитьdist/index.html
, вроде получилось
динамическая нагрузка
Представьте, что если наш входной файл очень большой (включая весь код бизнес-логики), это приведет к замедлению загрузки первого экрана и ухудшению взаимодействия с пользователем. Здесь мы решаем это с двух сторон:
- Разделение модулей, разделение входных файлов и отделение базовых модулей (UI, классов инструментов) от бизнес-модулей, что не только облегчает обслуживание и расширение кода, но и уменьшает объем входных файлов.
- При динамической загрузке пользователи не могут использовать все функции с самого начала, в это время мы можем динамически вводить вторичные модули, которые должны запускаться событиями в последующем процессе взаимодействия.
существует
src
Добавить в каталогdynamic.js
// dynamic.js
export default () => {
console.log('Im dynamically loaded.');
}
Измените следующие файлы
<!-- src/index.html -->
<body>
+ <button id="btn">点击我,动态加载dynamic.js</button>
</body>
// src/index.js
// 新增以下内容
const btn = document.getElementById('btn');
// 点击按钮,动态加载dynamic.js
btn.onclick = () => {
import(/* webpackChunkName: "dynamic" */ './dynamic.js').then(function (module) {
const fn = module.default;
fn();
})
}
воплощать в жизньnpm run build
, можно увидеть
/* webpackChunkName: "dynamic" */
, тогдаМожно сделать вывод, что: установкаChunkName
для"dynamic"
Это необходимо, иначе упаковка будет автоматически выделяться и читабельность будет плохой.id
названныйJS
документ. и нетChunk Names
логотип.
теперь мы открываемdist/index.html
,В настоящее время
dynamic.js
Пока мы успешно реализовали динамическую загрузку.
Разделение среды разработки и производства
оглянуться назад на нашуwebpack.config.js
, я написал так много кода, не зная об этом. Поскольку мы разрабатываем реальный проект, у нас есть два рабочих режима разработки и производства, каждый из которых выполняет свои обязанности. Мы могли бы также сделать перерыв и разделить конфигурацию.
выполнение командной строкиnpm i webpack-merge cross-env -D
webpack-merge
Вы можете объединить элементы конфигурации веб-пакета,cross-env
Переменные среды могут быть установлены и использованы.
новыйwebpack.base.js
, обеспечивая основныеwebpack loader plugin
настроить
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const pathResolve = (targetPath) => path.resolve(__dirname, targetPath);
const devMode = process.env.NODE_ENV !== 'production';
// 在node中,有全局变量process表示的是当前的node进程。
// process.env包含着关于系统环境的信息。
// NODE_ENV是用户一个自定义的变量,在webpack中它的用途是来判断当前是生产环境或开发环境。
// 我们可以通过 cross-env 将 NODE_ENV=development 写入 npm run dev的指令中,从而注入NODE_ENV变量。
module.exports = {
entry: {
index: pathResolve('js/index.js')
},
output: {
path: pathResolve('dist'),
},
module: {
rules: [
{
test: /\.html$/,
use: {
loader: 'html-loader',
options: {
attrs: ['img:src']
},
},
},
{
test: /\.(eot|woff|woff2|ttf)$/,
use: [{
loader: 'url-loader',
options: {
name: '[name].[hash:7].[ext]',
limit: 8192,
outputPath: 'font',
},
}],
},
{
test: /\.(sa|sc|c)ss$/,
use: [
devMode ? 'style-loader' : { // 如果处于开发模式,则无需再外链CSS,直接插入到<style>标签中
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../'
}
},
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /\.(png|jpg|jpeg|svg|gif)$/,
use: [{
loader: 'url-loader',
options: {
limit: 8192,
name: '[name].[hash:7].[ext]',
outputPath: 'img',
},
}],
},
],
},
plugins: [
new htmlWebpackPlugin({
minify: {
collapseWhitespace: true, // 移除空格
removeAttributeQuotes: true, // 移除引号
removeComments: true // 移除注释
},
filename: pathResolve('dist/index.html'),
template: pathResolve('src/index.html'),
})
]
};
новыйwebpack.dev.js
, работающий в режиме разработки
const path = require('path');
const webpack = require('webpack');
const base = require('./webpack.base.js');
const { smart } = require('webpack-merge');
const pathResolve = (targetPath) => path.resolve(__dirname, targetPath);
module.exports = smart(base, {
mode: 'development',
output: {
filename: 'js/[name].[hash:7].js'
},
devServer: {
contentBase: pathResolve('dist'),
port: '8080',
inline: true,
historyApiFallback: true,
hot: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin()
]
})
новыйwebpack.prod.js
, работающий в производственном режиме
const path = require('path');
const base = require('./webpack.base.js');
const { smart } = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const pathResolve = (targetPath) => path.resolve(__dirname, targetPath);
module.exports = smart(base, {
mode: 'production',
devtool: 'source-map', // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度,适用于打包后的代码查错
output: {
filename: 'js/[name].[chunkhash:7].js',
chunkFilename: 'js/[name].[chunkhash:7].js',
},
plugins: [
new CleanWebpackPlugin(['dist']),
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:7].css',
}),
],
});
соответствующий,package.json
тоже надо доработать
// 新增以下两条命令
// cross-env 决定运行环境 --config 决定运行哪个配置文件
"dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.dev.js",
"build": "cross-env NODE_ENV=production webpack --config webpack.prod.js "
Тайная война
В состоянии кэширования во фронтэнде сомнений нет, правильное использование кэширования может значительно повысить скорость загрузки и производительность приложений.webpack
воспользовалсяhash
Значение используется как часть имени файла для эффективного использования кэша. Когда файл будет изменен и переупакован, хеш-значение изменится, что приведет к аннулированию кеша и повторному извлечению ресурса HTTP-запросом.
а такжеwebpack
Существует три стратегии обработки хэша, а именно:
hash
Он относится к инженерному уровню проекта, то есть каждый раз, когда какой-либо файл модифицируется, все имена файловhash
значение изменится. Таким образом, как только любой файл будет изменен, файловый кеш всего проекта станет недействительным. как и весь проектfilename
Стратегия именования дляname.[hash:7]
(:7 означает перехватывать первые семь цифр из полного хеш-значения), мы видим, что хеш-значение упакованного файла такое же, поэтому для модуля, который не изменился, хэш также обновляется, что приводит к кешу недействительность .
chunkhash
chunkhash
По разным входным файлам (Entry
), чтобы выполнить синтаксический анализ файла зависимостей и построить соответствующийchunk
, сгенерируйте соответствующее хеш-значение. Если весь проектfilename
Стратегия именования дляname.[chunkhash:7]
,Мы видим, чтоChunk Names
для"index"
Хэш-значения файлов одинаковые, но разныеchunk
изhash
значение другое. Это также позволяет избежать ситуации, когда изменение файла изменит значение хеш-функции всего проекта.
contenthash
Но пришла проблема,index.scss
импортируется как модуль вindex.js
в, егоchunkhash
Значения согласованы до тех пор, пока одно из них изменяется, связанный с ним файлchunkhash
Значение также изменится. Пришло время использоватьcontenthash
, он рассчитывается в соответствии с содержимым файла, содержимое файла изменяется, значение хэша содержимого будет меняться. Мы изменили стратегию именования файла css наname.[contenthash:7]
и изменитьsrc/index.js
, не меняя другие файлы, снова запакуйте его и найдите:
Оптимизация конфигурации производственной среды
tree-shaking
Буквальный смысл — стряхнуть листья с дерева, чтобы вес числа уменьшился, а программа-аналог — это как бы удалить бесполезный код из нашего приложения, тем самым уменьшив размер. заимствовано изES6
Импорт модуля анализируется статически, поэтомуwebpack
Можно правильно определить, какой код загружается во время компиляции, то есть модули, на которые нет ссылок, не будут упакованы, что уменьшит размер нашего пакета, сократит время загрузки приложения и предоставит пользователям лучший опыт. Итак, как его использовать?
новыйsrc/utils.js
// src/utils.js
const square = (num) => num ** 2;
const cube = num => num * num * num;
// 导出了两个方法
export {
square,
cube
}
новыйsrc/shake.js
// src/shake.js
import { cube } from './utils.js';
// 只使用了cube方法
console.log('cube(3) is' + cube(3));
существуетwebpack.base.js
новый входной файл вshake.js
entry: {
+ shake: pathResolve('src/shake.js')
},
выполнение командной строкиnpm run build
После просмотра пакетаshake.js
, не нашелsquare
Метод не упакован, заявивtree-shaking
работал.
И все этоwebpack
существуетproduction
Окружение автоматически внедряется за нас.
splitChunks
Буквально разделить блок кода, по умолчанию это повлияет только на блок кода, загружаемый по запросу, потому что изменение исходного блока кода повлияетHTML
Включено в текущий проектscript
Этикетка. помнишь, мы былиsrc/index.js
Динамически внедряется вsrc/dynamic.js
, в конце концовdynamic.js
упаковывается самостоятельно, благодаряsplitChunks
.
В реальном производстве мы часто внедряем сторонние библиотеки (JQuery
,Lodash
), часто эти сторонние библиотеки имеют объем до десятков килобайт вперемешку с бизнес-кодом, и не будут обновляться так часто, как бизнес-код.В это время нам необходимо их разделить, что может не только поддерживать постоянный кеш сторонней библиотеки, но также может уменьшить размер бизнес-кода.
Исправлятьwebpack.prod.js
// 在module.exports中新增如下内容
optimization: {
runtimeChunk: {
name: 'manifest', // 被注入了webpackJsonp的定义及异步加载相关的定义,单独打包模块信息清单,利于缓存
},
splitChunks: {
cacheGroups: { // 缓存组,默认将所有来源于node_modules的模块分配到叫做'venders'的缓存组,所有引用超过两次的模块分配到'default'缓存组.
vendor: {
chunks: "all", // all, async, initial 三选一, 插件作用的chunks范围,推荐all
test: /[\\/]node_modules[\\/]/, // 缓存组所选择的的模块范围
name: "vendor", // Chunk Names及打包出来的文件名
minChunks: 1, // 引用次数>=1
maxInitialRequests: 5, // 页面初始化时加载代码块的请求数量应该<=5
minSize: 0, // 代码块的最小尺寸
priority: 100, // 缓存优先级权重
},
}
}
},
выполнение командной строкиnpm i lodash -S
Исправлятьsrc/index.js
// 新增以下内容
import _ from 'lodash';
воплощать в жизньnpm run build
, вы можете видеть, что до оптимизацииlodash
упакован вindex.js
,Оптимизированныйlodash
упакован вvendor.js
.
Сжать код, чтобы удалить избыточность
Часто в CSS-коде есть много стилей, которые мы не используем, они избыточны, нам нужно их удалить и сжать оставшиеся стили CSS, чтобы уменьшить размер файлов CSS.
выполнить в командной строкеnpm i glob optimize-css-assets-webpack-plugin purifycss-webpack purify-css -D
Исправлятьwebpack.prod.js
// 新增以下引入
const glob = require('glob'); // 匹配所需文件
const PurifyCssWebpack = require('purifycss-webpack'); // 去除冗余CSS
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); // 压缩CSS
// 新增以下插件
new PurifyCssWebpack({
paths: glob.sync(pathResolve('src/*.html')) // 同步扫描所有html文件中所引用的css,并去除冗余样式
})
// 新增以下优化
optimization: {
minimizer: [
new OptimizeCSSAssetsPlugin({}) // 压缩CSS
]
}
воплощать в жизньnpm run build
CSS
, и сжаты в одну строку. Далее нам нужно сжатьJS
код.
Поскольку мы используемuglifyjs-webpack-plugin
, для этого требуется поддержка ES6, поэтому давайте сначала заставим проект поддерживать синтаксис ES6. Babel — это компилятор JavaScript. Он переводит синтаксис JavaScript следующего поколения в ES5 для различных сред выполнения.
@babel/core
Предоставляет API перевода Babel, напримерbabel.transform
и т.д., для переноса кода. картинаwebpack
изbabel-loader
Это вызов этих API для завершения процесса перевода.
@babel/preset-env
Он может быть автоматически настроен в соответствии с настроенным целевым браузером или операционной средой.ES2015+
Код преобразуется вES5
.
Сначала выполнить в командной строкеnpm i @babel/core @babel/preset-env babel-loader @babel/plugin-syntax-dynamic-import -D
новый.babelrc
документ
{
"presets": [ // 配置预设环境
["@babel/preset-env", {
"modules": false
}]
],
"plugins": [
"@babel/plugin-syntax-dynamic-import" // 处理src/index.js中动态加载
]
}
Исправлятьwebpack.base.js
// 新增js的解析规则
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
exclude: /node_modules/
},
Затем выполняется командная строкаnpm i uglifyjs-webpack-plugin -D
Исправлятьwebpack.prod.js
// 新增以下引入
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
// 新增以下优化
optimization: {
minimizer: [
+ new UglifyJsPlugin({ // 压缩JS
cache: true,
parallel: true,
sourceMap: true
})
]
}
воплощать в жизньnpm run build
, вы увидите, что размер упакованного файла значительно уменьшился, и все готово.JS
также сжимается.
кindex.html
Например, мы можем открыть инструменты разработчика Chrome, выбрать «Дополнительные инструменты» и щелкнуть панель «Покрытие», чтобы просмотреть использование JS, CSS и других файлов и оптимизировать их с помощью нашей индивидуальной конфигурации веб-пакета.
несколько страниц
Иногда нам нужно создать несколько страниц одновременно с помощьюhtml-webpack-plugin
, как раз вplugins
Добавьте элементы конфигурации для новых страниц в .
новыйsrc/main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>main page</title>
</head>
<body>
<h1>I am Main Page</h1>
</body>
</html>
Исправлятьwebpack.base.js
// 修改以下内容
plugins: [
new htmlWebpackPlugin({ // 配置index.html
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true
},
filename: pathResolve('dist/index.html'),
template: pathResolve('src/index.html'),
chunks: ['manifest', 'vendor', 'index', ] // 配置index.html需要用的chunk块,即加载哪些JS文件,manifest模块管理的核心,必须第一个进行加载,不然会报错
}),
new htmlWebpackPlugin({ // 配置main.html
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true
},
filename: pathResolve('dist/main.html'),
template: pathResolve('src/main.html'),
chunks: ['manifest', 'shake'] // 配置index.html需要用的chunk块,加载manifest.js,shake.js
}),
],
воплощать в жизньnpm run build
, успешно построенindex.html
,main.html
.
Эпилог
До сих пор мы избавлялись от оков сторонних строительных лесов и постепенно создавали собственные интерфейсные инструменты процесса, которые легко изменять и использовать, с полными функциями, быстрыми и удобными, а также с возможностью повторного использования. Я надеюсь, что маленькие друзья могут сделать это сами, не нужно всегда говорить об этом на бумаге.webpack
, понять его принципы построения и оптимизации, интегрировать в собственные инженерные проекты, отказаться от использования прежнего громоздкого и нестандартного процесса разработки, не быть «инженером по резюме», создать свою систему знаний, рабочий процесс, улучшить Фронт - конечная эффективность разработки.
Наконец, исходный код этого проекта был развернут вGithub
, со многими дополнительными оптимизациями (less
служба поддержки,ESLint
Обнаружение, сжатие форматов изображений...), так что каждый может загрузить опыт напрямую и помочь в развитии проекта, и будет продолжать поддерживать в будущем. Я надеюсь, что друзья могут учиться друг у друга и вносить предложения .
Проект «Звезда» — это ваша самая большая поддержка для меня! !
На пути вперед не забывайте о первоначальном намерении, я желаю вам всем как можно скорее разбогатеть! !