Что такое веб-пакет и почему вы должны использовать веб-пакет
что такое вебпак
Понятие, данное на официальном сайте, таково:
По сути, webpack — это сборщик статических модулей для современных приложений JavaScript. Когда webpack обрабатывает приложение, он рекурсивно строит граф зависимостей, содержащий каждый модуль, необходимый приложению, а затем объединяет все эти модули в один или несколько пакетов. По самой интуитивно понятной и известной картинке на официальном сайте мы можем знать, что webpack можетпакет/скрипт/изображение/стиль/таблицаИз рисунка видно, что модули с зависимостями слева (MODULES WITH DEPENDENCIES) упакованы в различные статические ресурсы (STATIC ASSETS) через webpack
Зачем использовать вебпак
Благодаря приведенным выше концепциям вы уже знаете, что делает веб-пакет, поэтому вопрос в том, зачем использовать веб-пакет? Это долгая история, потом долгая история, эммммм
Зачем использовать вебпак?Это должно быть связано с развитием фронтенда.Я думаю, что вебпак это неизбежный продукт развития фронтенда до определенного этапа (похоже на нонсенс). Из-за быстрого развития компьютерных сетей веб-интерфейс также быстро развивается. Первоначальное практическое решение уже не может удовлетворить наши потребности, поэтому рождаются все новые и новые технологии, новые идеи и новые фреймворки, такие как:
Передняя часть модульная
С ростом сложности интерфейсных проектов, все большим количеством зависимостей друг перед другом и для лучшего повторного использования кода интерфейсу также необходимо использовать модульные идеи для организации кода.
Прежде всего, нам нужно понять, какие болевые точки модульность решает во внешнем интерфейсе:
- конфликт имен
- Зависимости файлов (порядок загрузки js)
- повторное использование кода
Мы говорим о модулях здесь и Javapackage
Концепция похожа. Логически связанный код помещается в пакет, и каждый пакет независим друг от друга, поэтому не нужно беспокоиться о конфликтах имен.Если другие люди хотят использовать эту часть функции, они могут напрямуюimport
Просто импортируйте пакет
Поэтому реализация модуляризации внешнего кода поможет нам решить проблемы конфликтов имен и повторного использования кода, так как же быть с файловыми зависимостями? Это использует наш веб-пакет, который будет рассмотрен позже.
Так вот с модулями мы легко можем переиспользовать чужой код, тут наступает проблема, правил и кругов нет, мы должны следовать определенной спецификации при использовании чужого кода, поэтому решения CommonJS, AMD и Ultimate Modular - модули ES6, эти являются нормой для модульности внешнего интерфейса.
Давайте кратко рассмотрим:
CommonJS
node.js использует спецификацию CommonJS, используяrequire
МетодыСинхронизироватьЗагрузка зависимостей, файл представляет собой модуль, формат импорта и экспорта следующий:
// 导入
const moduleA = require('./moduleA');
// 导出
module.exports = moduleA.someFunc;
Недостаток в том, что загружаемые модули синхронны, и последующие операции можно выполнять только после их загрузки. Поскольку файлы модулей node.js обычно находятся на локальном жестком диске, эта проблема обычно не возникает, но эта спецификация не применима в среде браузера.
AMD
Причина та же, что и выше, потому что CommonJS не подходит для браузерной среды, поэтому появилась спецификация AMD. спецификацияасинхронныйПри загрузке зависимостей можно указать зависимости, которые необходимо загрузить при их повторном объявлении и передать в качестве параметров.Для зависимых модулей они выполняются заранее, а зависимости являются предзависимыми.
Это написано следующим образом:
define("module", ["dep1", "dep2"], function(d1, d2) {
return someExportedValue;
});
require(["module", "../file"], function(module, file) { /* ... */ });
Модульный ES6
ES6 реализует модульность непосредственно на уровне языка. Сейчас мы больше всего используем модульную практику ES6.
Это написано следующим образом:
// 导入
import { readFile } from 'fs';
import React from 'react';
// 导出
export function hello() {};
export default {
// ...
};
Модульный стиль
Сейчас все больше и больше людей начинают писать в стиле модульного мышления. Например, большинство прекомпиляторов CSS теперь поддерживают@import
письма. Поместите несколько общих стилей в один файл и импортируйте их в другие файлы.
Появление трех основных фреймворков избавляет нас от необходимости оперировать DOM, как традиционный JQ, и сосредоточиться на обработке данных.
И растущая популярность ES6/7/8 и TS, несомненно, значительно повысила эффективность нашей разработки.
Но проблема в том, что эти новые технологии применимы не ко всем браузерам, и всем нужно преобразовать исходный код в код, который можно запустить прямо в браузере.
Итак, webpack решает эту проблему.
Сравнение Gulp/Grunt, Rollup и webpack
Gulp/Grunt
На самом деле, GUNP / GUNT и WEBPACK не должны быть сопоставимыми, но все они могут быть названы фронтальными автоматическими инструментами сборки (давайте перестанем делать механически повторяющиеся вещи и освободить наши руки)
Но Gulp/Gunt и webpack на самом деле не делают одно и то же.
Gulp — это, по сути, средство запуска задач, а Webpack — сборщик модулей.
Я думаю о Gulp, как он его определяет:Инструмент автоматизированной сборки на основе потока, определите каждую задачу, а затем автоматически выполните задачи одну за другой.
А webpack модульно организован, модульно зависим, а затем модульно упакован. Условно говоря, сцена ограничена модульной упаковкой внешнего интерфейса.
Рекомендованный Gangster Chi-inch почти ответил:Каковы функции gulp, которые не может заменить веб-пакет?
Rollup
Rollup — это сборщик модулей, похожий на Webpack, но ориентированный на ES6. Изюминкой Rollup является то, что он может выполнять Tree Shaking в исходном коде ES6 для удаления кода, который был определен, но не используется, и Scope Hoisting для уменьшения размера выходного файла и повышения производительности во время выполнения. Однако позже эти особенности Rollup были скопированы и реализованы в Webpack. Поскольку использование Rollup аналогично использованию Webpack, я не буду здесь подробно описывать, как его использовать, а подробно объясню их различия:
Rollup — это альтернатива, появившаяся после того, как Webpack стал популярным; Экологическая цепочка Rollup не идеальна, и опыт не так хорош, как Webpack; Rollup не такой полный, как Webpack, но его настройка и использование проще; Rollup не поддерживает Code Spliting, но преимущество в том, что в упакованном коде нет кода для загрузки, выполнения и кэширования модулей Webpack. Rollup имеет преимущество перед Webpack, когда речь идет об объединении библиотек JavaScript, поскольку связанный код меньше и быстрее. Однако функции не идеальны, и многие сценарии не могут найти готовых решений.
установка и использование
Создайтеpackage.json
документ
Может быть создан вручную или автоматически с помощью команд
npm init
Установить
webpack можно установить напрямую с помощью npm, его можно установить глобально или установить в проекте
//全局安装
npm install -g webpack
//安装到你的项目目录
npm install --save-dev webpack
использовать
Рекомендую всем прочитать эту статью:Для начала работы с Webpack этого достаточно
Я только что следил за этой статьей
создать новый файл
Сначала создайте новую папку, затем откройте папку в терминале и выполните для инициализации вашего проекта.Во время начального процесса будут некоторые проблемы, которые помогут вам создатьpackage.json
документ.
npm init
После инициализации мы должны создать несколько файлов для хранения файлов нашего проекта.
Создаватьapp
Папка для хранения исходных файлов перед упаковкой
Создаватьpublic
папка для хранения входного файлаindex.html
И браузер может запускаться сразу после упаковки через webpackjs
документ
Например, мыapp
создать папкуGreeter.js
файл, в котором есть метод для отображения текстовой информации на страницеHi there and greetings!
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings!";
return greet;
};
Затем мы создаемmain.js
представлятьGreeter.js
этот файл,
запись в браузереindex.html
Содержимое файла следующее (гдеbundle.js
представляет собой упакованный файл):
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webpack Sample Project</title>
</head>
<body>
<div id='root'>
</div>
<script src="bundle.js"></script>
</body>
</html>
Путь к файлу на данный момент следующий
.
├── app
│ ├── Greeter.js
│ └── main.js
├── package.json
├── public
│ ├── bundle.js
│ └── index.html
Установить веб-пакет
После инициализации проекта установите webpack
npm install --save-dev webpack
После установки веб-пакета вы можетеpackage.json
Файл видит добавление зависимостей webpack.
настроить веб-пакет
После установки webpack нам нужно настроить webpack, для начала создадим конфигурационный файлwebpack.config.js
Содержимое файла следующее:
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
}
}
Затем набираем в терминале проекта
webpack
Вы можете увидеть следующую информацию:
Hash: 4e6a6b5eb88a83b29e02
Version: webpack 4.12.0
Time: 551ms
Built at: 2018-06-24 14:53:39
Asset Size Chunks Chunk Names
bundle.js 6.82 KiB 0 [emitted] main
[3] ./node_modules/css-loader!./app/main.css 190 bytes {0} [built]
[4] ./app/main.css 1.04 KiB {0} [built]
[5] ./app/Greeter.js 143 bytes {0} [built]
[6] ./app/main.js 119 bytes {0} [built]
+ 3 hidden modules
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
Увидев такую информацию, поздравляю, ваш первый проект webpack завершен!
Открытьpublic
под папкойindex.html
, вы можете увидеть следующий эффект в браузере.
Loader
Загрузчик используется для преобразования исходного кода модуля. Загрузчики позволяют предварительно обрабатывать файлы при импорте или «загрузке» модулей. Таким образом, загрузчики похожи на «задачи» в других инструментах сборки и предоставляют мощный способ обработки этапов сборки внешнего интерфейса. Загрузчики могут преобразовывать файлы с разных языков (например, TypeScript) в JavaScript или преобразовывать встроенные изображения в URL-адреса данных. Загрузчик даже позволяет импортировать файлы CSS прямо в модули JavaScript!
Поскольку сам webpack может обрабатывать только JavaScript, если вы хотите обрабатывать другие типы файлов, вам нужно использоватьloader
для преобразования,loader
Сама она представляет собой функцию, которая принимает в качестве параметра исходный файл и возвращает результат преобразования.
Например 🌰 ——css-loader
Например, если мы хотим добавить стили на страницу прямо сейчас, чтобы текст располагался по центру, то яapp
Создайте новый в папкеmain.css
документ. В вебпаке все файлы являются модулями, так что используйте этоcss
файл, его необходимо сначала импортировать.
представлятьcss
документ
так что яapp
под папкойmain.js
Вставьте файл css в
const greeter = require('./Greeter.js');
require ('./main.css')
document.querySelector("#root").appendChild(greeter());
переупаковывать
Потом еще раз перепаковал, и после синевы обнаружил, что сообщается об ошибке!
Hash: 179c18498fac6de89a96
Version: webpack 4.12.0
Time: 533ms
Built at: 2018-06-24 15:00:24
1 asset
[0] ./app/Greeter.js 143 bytes {0} [built]
[1] ./app/main.js 119 bytes {0} [built]
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
ERROR in ./app/main.js
Module not found: Error: Can't resolve 'style-loader' in '/Users/cherry/Workspace/webpack-demo'
@ ./app/main.js 2:0-22
Судя по сообщению об ошибке, очевидно, что это напоминание о том, что наш проект отсутствует.style-loader
, это связано с тем, что веб-пакет изначально поддерживает только синтаксический анализ файлов js.Чтобы поддерживать файлы не-js-типа, вам нужно использоватьloader
установить загрузчик
Итак, мы собираемся установитьstyle-loader
а такжеcss-loader
npm i -D style-loader css-loader
Изменить файл конфигурации
Затем измените файл конфигурации веб-пакетаwebpack.config.js
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
}
добавлен файл конфигурацииmodule.rules
Массив правил конфигурации, которые сообщают веб-пакету о соответствииtest
Файл нужно использоватьuse
Назадloader
иметь дело с. Так что правило для всех.css
файл, заканчивающийся наstyle-loader
,css-loader
для обработки.
функция загрузчика
Давайте взглянемloader
Каковы особенности:
- Загрузчики поддерживают цепочку. Возможность использовать конвейеры для ресурсов. Цепочка загрузчиков будет выполняться в обратном порядке. Первый загрузчик в цепочке загрузчиков возвращает значение следующему загрузчику. В последнем загрузчике верните JavaScript, ожидаемый webpack.
- Загрузчики могут быть синхронными или асинхронными.
- Загрузчики работают в Node.js и способны делать все возможное.
- Загрузчик принимает параметры запроса. Используется для передачи конфигурации загрузчику.
- Загрузчики также можно настроить с помощью объекта параметров.
- Помимо использования общего основного свойства package.json, вы также можете экспортировать обычные модули npm в качестве загрузчика, определив поле загрузчика в package.json.
- Плагины могут расширить возможности загрузчика.
- Загрузчик может генерировать дополнительные произвольные файлы.
Три позы использования webpack
Есть три положения для использования загрузчика в веб-пакете
через интерфейс командной строки
запустить из командной строки
webpack --module-bind jade --module-bind 'css=style!css'
Вы можете опустить -loader после jade, style и css, они используют jade-loader для .jade и style-loader и css-loader для .css соответственно.
по требованию
Вы можете указать, какой загрузчик использовать для обработки файлов, прямо в исходном коде.
require('style-loader!css-loader?minimize!./main.css')
Вот так./main.css
файл сначалаcss-loader
повторное использованиеstyle-loader
для преобразования
Использовать файлы конфигурацииwebpack.config.js
Самый распространенный способ — использовать конфигурационный файл, использованный в этой статье.
обычные погрузчики
loader name | loader des |
---|---|
babel-loader |
Загрузите код ES2015+ и транспилируйте его в ES5 с помощью Babel. |
buble-loader |
Загрузите код ES2015+ с помощью Bublé и перенесите код в ES5. |
cache-loader |
Добавьте этот загрузчик перед некоторыми дорогостоящими загрузчиками, чтобы кэшировать результаты на диск. |
coffee-loader |
БудуCoffeeScript Преобразовать в JS |
css-loader |
css-loader буду@import ,url() Импортированный css преобразуется вimport/require() способ, а затем их разбор |
exports-loader |
добавлениемexports[...] = ... Оператор экспортирует переменные в файл. |
expose-loader |
expose-loader Добавьте модуль в глобальный объект |
file-loader |
file-loader Он может анализировать введение URL-адреса в проекте (не ограничиваясь css), в соответствии с нашей конфигурацией, |
Скопируйте образ по соответствующему пути, а затем в соответствии с нашей конфигурацией измените путь ссылки на упакованный файл, чтобы он указывал на правильный файл.gzip-loader
| Может загружать сжатые gzip-ресурсыhtml-loader
| Вывод html в виде строки, а также может быть сжат в соответствии с конфигурациейimports-loader
| imports-loader
Позволяет использовать модули, которые зависят от определенных глобальных переменных, что полезно для таких зависимостей, как$
Такие сторонние модули для таких глобальных переменных очень полезны.jshint-loader
| Использовать jshint для загруженных модулейjson-loader
| Поскольку webpack >= v2.0.0 по умолчанию поддерживает импорт файлов JSON. Если вы используете пользовательское расширение файла, вам все равно может понадобиться использовать этот загрузчик.json5-loader
| Разобрать файл json5 в объект jsless-loader
| Меньше конвертировать в cssnull-loader
|возвращает пустой модульpostcss-loader
| Конвертировать postcss в cssraw-loader
|Загрузить оригинальное содержимое файла (формат utf-8)sass-loader
| Конвертировать SASS/SCSS в csssource-map-loader
| Извлечение исходных карт из существующих исходных файлов (URL исходного кода) для упрощения отладкиstyle-loader
| Поместите CSS в<style>
теги внедряются в DOMscript-loader
| Выполнить файл JavaScript один раз в глобальном контексте (например, в теге скрипта) без разбораsvg-inline-loader
| Встроить SVG как модульurl-loader
| Загрузите файл как URL-адрес в кодировке Base64.
row 2 col 1 | row 2 col 2
Plugin
Плагины являются основой веб-пакета. Сам веб-пакет построен поверх той же системы плагинов, которую вы используете в своей конфигурации веб-пакета! Цель плагинов - решить другие вещи, которые не может сделать загрузчик.
Плагин используется для расширения функциональности Webpack, добавляя хуки в процесс сборки, что обеспечивает большую гибкость Webpack.
С помощью плагина (плагина) веб-пакет может реализовать сложные функции, которые не может выполнить загрузчик.Используя богатый настраиваемый API и события жизненного цикла плагина, вы можете контролировать каждую ссылку процесса упаковки веб-пакета и реализовывать расширение пользовательских функций вебпак.
Например 🌰 - ExtractTextPlugin
webpack4 больше не поддерживает использование extract-text-webpack-plugin для оптимизации css, его необходимо изменить на optim-css-assets-webpack-plugin и mini-css-extract-plugin.
В только что приведенном примере мы смотрим на упакованныйindex.html
В файле видно, что код css, который мы только что написали, размещенhead
серединаstyle
этикетка, этоstyle-loader
справился с этим для нас
Однако, если вы хотите, чтобы css после упаковки был в отдельном файле, то вам нужноExtractTextPlugin
Этот плагин вышел.
УстановитьExtractTextPlugin
npm i -D ExtractTextPlugin
Изменить файл конфигурации
Нам нужно изменить файл конфигурации:
const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js",//打包后输出文件的文件名
},
module: {
rules: [
{
test: /\.css$/,
// 转换 .css 需要的 loader
loaders: ExtractTextPlugin.extract({
use: ['css-loader'],
})
}
]
},
plugins: [
new ExtractTextPlugin({
filename: '[name]-[contenthash:8].css'
})
]
}
Затем мы переупаковываем, мы можем найти вpublic
В папке есть еще одинmain-493a2c3c.css
файл, далее мы собираемсяindex.html
Этот файл css автоматически импортируется в
html-webpack-plugin
html-webpack-plugin
В соответствии с установленным шаблоном после каждого запуска может быть создан соответствующий файл шаблона, а также будут импортированы зависимые CSS/JS.Если CSS/JS содержит хеш-значение, тоhtml-webpack-plugin
Сгенерированный файл шаблона также содержит правильную версию файлов CSS/JS.
Установитьhtml-webpack-plugin
Мы уже знакомы со способом установки
npm i -D html-webpack-plugin
Изменить файл конфигурации
const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js",//打包后输出文件的文件名
},
module: {
rules: [
{
test: /\.css$/,
loaders: ExtractTextPlugin.extract({
use: ['css-loader'],
})
}
]
},
plugins: [
new ExtractTextPlugin({
filename: '[name]-[contenthash:8].css'
}),
new HtmlWebpackPlugin(),
]
}
После синего мы удаляем, прежде чем мы вpunlic
под папкойindex.html
Содержимое пакета после его упаковки создастhtml
шаблон
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
<link href="main-493a2c3c.css" rel="stylesheet"></head>
<body>
<script type="text/javascript" src="bundle.js"></script></body>
</html>
существуетnew HtmlWebpackPlugin
, мы можем сделать серию конфигурации
new HtmlWebpackPlugin({
// 生成的HTML模板的title,如果模板中有设置title的名字,则会忽略这里的设置
title: "This is the webpack config",
// 生成的模板文件的名字
filename: "/index.html",
// 模板来源文件
template: "index.html",
// 引入模块的注入位置;取值有true/false/body/head
// true 默认值,script标签位于html文件的 body 底部
// body script标签位于html文件的 body 底部
// head script标签位于html文件的 head中
// false 不插入生成的js文件,这个几乎不会用到的
inject: "body",
// 指定页面图标;
favicon: "",
// 是html-webpack-plugin中集成的 html-minifier ,生成模板文件压缩配置
minify: {
caseSensitive: false, //是否大小写敏感
collapseBooleanAttributes: true, // 省略布尔属性的值
collapseWhitespace: true //删除空格
},
// 是否生成hash添加在引入文件地址的末尾,类似于我们常用的时间戳,避免缓存带来的麻烦
hash: true,
// 是否需要缓存,如果填写true,则文件只有在改变时才会重新生成
cache: true,
// 是否将错误信息写在页面里,默认true,出现错误信息则会包裹在一个pre标签内添加到页面上
showErrors: true,
// 引入的模块,这里指定的是entry中设置多个js时,在这里指定引入的js,如果不设置则默认全部引入
chunks: "",
// 引入模块的排序方式
// 默认四个选项: none auto dependency {function}
// 'dependency' 文件的依赖关系
// 'auto' 默认值,插件的内置的排序方式
// 'none' 无序
// {function} 自定义
chunksSortMode: "auto",
// 排除的模块
excludeChunks: "",
// 生成的模板文档中标签是否自动关闭,针对xhtml的语法,会要求标签都关闭,默认false
xhtml: false
}),
Общие плагины
Взято с: http://www.css88.com/doc/webpack/plugins/
Name | Description |
---|---|
AggressiveSplittingPlugin | Разделите исходный кусок на более мелкие куски |
BabiliWebpackPlugin | Инструмент для обрезки на основе Babel: Babili |
BannerPlugin | Добавить баннер в верхней части каждого сгенерированного куска |
CommonsChunkPlugin | Извлечение общих модулей, используемых между фрагментами |
ComponentWebpackPlugin | Использование компонентов с веб-пакетом |
CompressionWebpackPlugin | Предварительно подготовленные сжатые версии ресурсов, использующие Content-Encoding для предоставления услуг доступа |
ContextReplacementPlugin | Переопределить предполагаемый контекст требуемых выражений |
DefinePlugin | Глобальные константы, позволяющие настраивать во время компиляции. |
DllPlugin | Чтобы значительно сократить время сборки, отдельная упаковка |
EnvironmentPlugin | Сокращение для ключа process.env в DefinePlugin. |
ExtractTextWebpackPlugin | Извлечь текст (CSS) из пакетов в отдельные файлы |
HotModuleReplacementPlugin | Включить горячую замену модуля (HMR) |
HtmlWebpackPlugin | Простое создание файлов HTML для доступа к серверу |
I18nWebpackPlugin | Добавить поддержку интернационализации в пакеты |
IgnorePlugin | Исключить определенные модули из пакета |
LimitChunkCountPlugin | Установите минимальные/максимальные ограничения для чанков для точной настройки и управления чанками |
LoaderOptionsPlugin | Для перехода с веб-пакета 1 на веб-пакет 2 |
MinChunkSizePlugin | Убедитесь, что размер чанка превышает указанный предел |
NoEmitOnErrorsPlugin | На этапе вывода ошибки компиляции пропускаются. |
NormalModuleReplacementPlugin | Замените ресурсы, соответствующие регулярному выражению |
NpmInstallWebpackPlugin | Автоматически устанавливать отсутствующие зависимости во время разработки |
ProvidePlugin | Вам не нужно импортировать/требовать использовать модули |
SourceMapDevToolPlugin | Более детальный контроль над исходными картами |
UglifyjsWebpackPlugin | Может контролировать версию UglifyJS в проекте |
ZopfliWebpackPlugin | Предварительно сжатая версия ресурса через node-zopfli |
Справочная статья: