Начало работы с Webpack Classic

Webpack

оригинал:Webpack your bags

October 16th, 2015 by Maxime Fabre
(Китайский перевод обновлен 22.10.2017. Так как вебпак обновился до 3.8.1, перевод изменился. В конце перевода указаны адреса каждого сегментированного пакета кода. При необходимости, пожалуйста, скачайте это ты сам)


Webpack your bags

Возможно, вы слышали об этом классном инструменте под названием webpack раньше, если вы не читали его внимательно, вы можете быть немного сбиты с толку, потому что некоторые люди говорят, что это похоже на инструмент сборки, такой как Gulp, а некоторые люди говорят, что это как инструмент управления модулями Browserify Class. , если вы посмотрите повнимательнее, вы все еще можете не понять, что происходит, потому что на официальном сайте указано, что webpack — это два.

Честно говоря, в начале я очень расплывчато представлял "что такое, черт возьми, webpack" и чувствовал себя очень расстроенным, а в итоге просто закрыл страницу, ведь до этого у меня уже был инструмент сборки системы, и я им пользовался слишком Очень весело, если вы следили за развитием javascript так же внимательно, как и я, вы, вероятно, были в пыли, часто прыгая по всевозможным популярным вещам. К счастью, теперь, когда у меня есть некоторый опыт, я думаю, что могу написать статью, чтобы более четко объяснить, что такое веб-пакет, и, что более важно, почему он настолько хорош, что он того стоит. Мы вложили в него так много энергии.

1. Что такое Webpack?

Является ли Webpack системой сборки или менеджером модулей? Хорошо, теперь я отвечу сразу --- ответ будет и тем, и другим, конечно, я не говорю, что он делает и то, и другое, я имею в виду, что он органично соединяет два, веб-пакет не создает вам сначала ресурсы, а затем управляет вашими модулями. , но относитесь к своим ресурсам как к модулям.

Точнее, это не компиляция всех ваших scss-файлов, оптимизация всех ваших изображений, их импорт в одном месте, управление всеми вашими модулями и, наконец, их импорт на вашу страницу в другом месте. Предположим, вы такой:

import stylesheet from 'styles/my-styles.scss';

import logo from 'img/my-logo.svg';

import someTemplate from 'html/some-template.html';

console.log(stylesheet); // "body{font-size:12px}"

console.log(logo);//"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5[...]"

console.log(someTemplate) // "Hello"

Как показано выше, все ваши ресурсы (будь то scss, html или другие) можно рассматривать как модули, и эти модули можно импортировать (вводить), изменять (модифицировать), манипулировать (управлять) и, наконец, упаковывать в ваш окончательный пакет ( пучок)

Для этого вам необходимо зарегистрировать загрузчики в файле конфигурации веб-пакета. Загрузчик — это подключаемый модуль, который обрабатывает определенный файл, когда вы с ним сталкиваетесь. Ниже приведены некоторые из загрузчиков. Например:

{
  // 当你导入(import)一个a.ts文件时,系统将会用Typescript加载器去解析它
  test: /\.ts/,
  loader: 'typescript',
},
{
  // 当你的项目里面有图片时,系统将会用image-webpack加载器把它们压缩
  // 并且它们会被关联到data64 URLs
  test: /\.(png|jpg|svg)/,
  loaders: ['url', 'image-webpack'],
},
{
  // 当你的项目里面有scss文件时,系统将会通过node-sass加载器去解析,并且自动补充前缀
  // 最后返回css(可以直接被系统解析)
  test: /\.scss/,
  loaders: ['css', 'autoprefixer', 'sass'],
}

В конце концов, все загрузчики возвращают строки, так что webpack наконец-то может обернуть ресурсы в javascript-модули. Как и в этом примере, файл scss после преобразования загрузчиками выглядит так:

export default 'body{font-size:12px}';


2. Мир такой большой, зачем ты его используешь?

Как только вы поймете, что такое webpack, скорее всего, на ум придет второй вопрос: каковы преимущества его использования? «Поместите изображение и CSS в мой JS? Какого черта?» Ну, мне давно говорили помещать все в один файл, чтобы не тратить наши HTTP-запросы.

Но это приведет к большому минусу, то есть большинство людей сейчас упаковывают все ресурсы в один файл app.js, а потом импортируют этот файл в каждую страницу. Это означает, что при рендеринге каждой страницы большая часть времени тратится на загрузку кучи ресурсов, которые вообще не используются. Но если вы этого не сделаете, то вы, вероятно, вручную введете эти ресурсы на указанную страницу, что приведет к беспорядку деревьев зависимостей для поддержки и отслеживания таких проблем, как: Какие страницы должны зависеть от этого файла? На какие страницы повлияет изменение файлов a.css и b.css?

Так что оба подхода неверны, но не совсем неверны. Если рассматривать вебпак как посредника - это не просто система сборки или сборщик, а злобная умная система сборщиков, которая при правильной настройке даже лучше вас знает вашу систему, а также лучше, чем Вы умеете оптимизировать ваша система.

3. Давайте сделаем небольшое приложение

Чтобы вам было проще понять преимущества веб-пакета, мы делаем небольшое приложение, а затем используем веб-пакет для упаковки ресурсов нашего приложения.Перед этим я рекомендую использовать Node 4 и выше и NPM 3 и выше. , потому что хорошие зависимости позволят избежать многих головных болей при использовании веб-пакета, если ваша версия npm недостаточно нова, вы можете пройтиnpm install npm -gобновить.

$ node --version
v6.11.2
$ npm --version
5.4.2

В то же время я также рекомендую добавить node_modules/.bin в переменную окружения PATH, чтобы каждый раз вручную не вводить node_modules/.bin/webpack.Все следующие примеры не будут отображать node_modules/ командной строки, которую я хочу execute..bin (пропустите этот шаг, если веб-пакет установлен глобально).

Примечание. Установите глобальную командную строку webpack: черезnpm install webpack -gобновить.

основное руководство

Теперь создайте папку с именем webpack-your-bags, установите webpack в папку и добавьте jquery, чтобы доказать что-то позже.

$ npm init -y
$ npm install jquery --save
$ npm install webpack --save-dev

Теперь давайте создадим запись приложения, используя чистый ES5.

дорожка:webpack-your-bags/src/index.js

var $ = require('jquery');
$('body').html('Hello');

Создать файл конфигурации веб-пакетаwebpack.config.js, webpack.config.js — это javascript и ему нужно экспортировать (экспортировать) объект (объект)
дорожка:webpack-your-bags/webpack.config.js

var path = require("path");//用于处理目录的对象,提高开发效率(__dirname需要引入path后才可以使用)
var ROOT_PATH = path.resolve(__dirname);//获取当前整个模块文件所在目录的完整绝对路径
var BUILDS_PATH = path.resolve(ROOT_PATH, "builds");//获取我们的builds目录完整绝对路径
module.exports = {
    entry: './src',
    output: {
        path: BUILDS_PATH,
        filename: 'bundle.js',
    },
};

Здесь запись сообщает веб-пакету, какой файл является точкой входа вашего приложения. Это ваши основные файлы, они находятся в верхней части дерева зависимостей, а затем мы говорим ему скомпилировать наши ресурсы в файл bundle.js в каталоге сборок (путь: webpack-your-bags/builds), я сейчас создайте соответствующий index.html
дорожка:webpack-your-bags/index.html

<!DOCTYPE html>
<html>
<body>
    <h1>My title</h1>
    <a>Click me</a>
    <script src="builds/bundle.js"></script>
</body>
</html>

Выполняем webpack, если все хорошо, мы увидим сообщение о том, что bundle.js был скомпилирован корректно.

$ webpack
Hash: 65d56cd1e7dddf04958b
Version: webpack 3.8.1
Time: 250ms
    Asset    Size  Chunks                    Chunk Names
bundle.js  271 kB       0  [emitted]  [big]  main
   [0] ./src/index.js 51 bytes {0} [built]
    + 1 hidden module

Здесь webpack сообщит вам, что bundle.js содержит нашу точку входа (index.js) и скрытый модуль. Этот скрытый модуль — jquery. По умолчанию webpack скрывает сторонние модули. Если вы хотите увидеть webpack Чтобы скомпилировать все модули , мы можем добавить параметр --display-modules

$ webpack --display-modules
Hash: 65d56cd1e7dddf04958b
Version: webpack 3.8.1
Time: 263ms
    Asset    Size  Chunks                    Chunk Names
bundle.js  271 kB       0  [emitted]  [big]  main
   [0] ./src/index.js 51 bytes {0} [built]
   [1] ./node_modules/jquery/dist/jquery.js 268 kB {0} [built]

Если вы чувствуете, что вам нужно выполнять заново каждый раз, когда вы вносите измененияwebpackСлишком много проблем, вы также можете запуститьwebpack --watchДля автоматического отслеживания изменений в файле, если файл изменился, он будет автоматически перекомпилирован.

Сборка нашего первого загрузчика

Помните, как мы обсуждали, что webpack может импортировать CSS и HTML и все виды файлов? Когда это пригодится? Хорошо, если за последние несколько лет вы сделали большой скачок в сторону веб-компонентов (Angular 4, Vue, React, Polymer, X-Tag и т. д.). Итак, я думаю, вы слышали идею о том, что если ваше приложение построено с набором повторно используемых автономных компонентов пользовательского интерфейса, его будет легче поддерживать, чем единый связанный пользовательский интерфейс — этот повторно используемый компонент представляет собой веб-компоненты (я говоря о простых вещах здесь, пока вы можете их понять), теперь, чтобы сделать компоненты действительно самодостаточными, вам нужно инкапсулировать все, что нужно компонентам, в их собственное внутреннее пространство, например, давайте рассмотрим кнопку, это в нем должен быть немного HTML, и немного JS для обеспечения его интерактивности, и, может быть, немного CSS.Если эти вещи будут загружаться вместе, когда это необходимо, это будет идеально, то есть только когда мы импортируем кнопку Компоненты, мы будем загружать этот ресурс файлы.

Теперь давайте напишем кнопку, сначала предположим, что вы уже знакомы с ES2015 (т.е. ES6, новым стандартом javascript), потому что некоторые браузеры еще не поддерживают ES6, поэтому нам нужен babel для преобразования ES6 в ES5, поддерживаемый браузерами для нас, Давайте сначала добавим загрузчик Babel. Чтобы установить загрузчик в веб-пакете, нужно сделать два шага: 1. npm install {whatever}-loader 2. Добавить его в раздел module.loaders вашего файла конфигурации веб-пакета (webpack.config.js), хорошо, теперь мы должны добавить babel, поэтому сначала установите его:

$ npm install babel-loader --save-dev

Нам также нужно установить сам babel, потому что теперь наш загрузчик не устанавливает их автоматически, поэтому нам нужно установитьbabel-coreЭтот пак и его пресетыes2015(то есть версия преобразователя, которая выполняется до выполнения кода):

$ npm install babel-core babel-preset-es2015 --save-dev

--save-dev: пакеты, на которые нужно полагаться во время разработки проекта, пакеты, на которые не нужно полагаться после выпуска, такие как нашbabel, процесс разработки требуетbabelПреобразуйте для нас написанный es6 в es5.После релиза, так как весь написанный нами код es6 был преобразован в es5, нет необходимости продолжать полагаться на него;
--save: Пакеты, которые все еще нуждаются в зависимостях после выпуска проекта, такие как нашjquery;

Теперь мы создаем файл .babelrc, который сообщает Babel, какой пресет использовать, это файл json, который сообщает Babel, какой преобразователь выполнять в вашем коде, теперь мы говорим ему использовать es2015:
дорожка:webpack-your-bags/.babelrc

{
"presets": ["es2015"] 
}

Теперь, когда babel настроен, мы можем обновить конфигурацию webpack (он же файл webpack.config.js): чего мы хотим? Мы хотим, чтобы babel работал со всеми нашими файлами, оканчивающимися на .js, но, поскольку webpack будет проходить через все зависимости, но мы хотим избежать запуска babel со сторонним кодом, таким как jquery, поэтому мы можем фильтровать его немного больше, загрузчики могут либо Может быть включено или исключено, это может быть строка (string), регулярное выражение (regex) или обратный вызов (callback), в зависимости от того, что вы используете. Поскольку мы хотим, чтобы babel работал только с нашими файлами, нам нужно включить его только в наш собственный исходный каталог, которым является папка src:

var path = require("path");//用于处理目录的对象,提高开发效率(__dirname需要引入path后才可以使用)
var ROOT_PATH = path.resolve(__dirname);//获取当前整个模块文件所在目录的完整绝对路径
var BUILDS_PATH = path.resolve(ROOT_PATH, "builds");//获取我们的builds目录完整绝对路径
var SRC_PATH = path.resolve(ROOT_PATH, "src");//获取到我们的资源目录src的完整路径
module.exports = {
    entry: './src',
    output: {
        path: BUILDS_PATH,
        filename: 'bundle.js',
    },
    module: {
        loaders: [{
            test: /\.js/,
            loader: 'babel-loader',
            include: SRC_PATH,
        }],
    }
};

Теперь мы можем переписать наш index.js на ES6, так как мы представили babel, все примеры, которые следуют отсюда, используют ES6.

import $ from 'jquery';
$('body').html('Hello');

написать виджет

Теперь давайте напишем небольшой компонент Button, он должен иметь некоторые стили SCSS (SCSS — это новый синтаксис, представленный в SASS 3, SASS — это своего рода препроцессор CSS), шаблон HTML и некоторое взаимодействие с JS, затем давайте сначала установим его. что-то нам нужно. Во-первых, мы используем очень легкий пакет шаблонов Mustache (древний фреймворк с разделенными фронтом и бэкендом, просто поймите это), нам также нужно настроить загрузчики для SCSS и HTML, потому что результаты будут передаваться от одного загрузчика к другому через пайп(пайп), Тут немного запутанно.Как бы это сказать?Похоже на очиститель воды,написанный нами SCSS очищается первым загрузчиком и потом становится CSS.STYLE модули которые можно импортировать и тд, Итак, нам нужен sass-загрузчик, чтобы «очистить» SCSS, когда у нас есть CSS, есть несколько способов справиться с ним, здесь мы используем загрузчик стилей. Загрузчик, который берет фрагмент CSS и динамически вставляет его. на страницу.

$ npm install mustache --save
$ npm install css-loader style-loader html-loader sass-loader node-sass --save-dev

Для того, чтобы веб-пакет передал данные от одного загрузчика к другому, мы просто передаем несколько направлений загрузчика справа налево, используя ! отдельно, или можно передать через свойство loaders массив, а не сами загрузчики:

{
test: /\.js/,
loader: 'babel-loader',
include: SRC_PATH,
}, {
test: /\.scss/,
loader: 'style-loader!css-loader!sass-loader',
// Or
//loaders: ['style-loader', 'css-loader', 'sass-loader'],
}, {
test: /\.html/,
loader: 'html-loader',
}

Теперь, когда наши загрузчики готовы, давайте напишем кнопку:
дорожка:webpack-your-bags/src/Components/Button.scss

.button {background: tomato; color: white; }

дорожка:webpack-your-bags/src/Components/Button.html

<a class="button" href="{{link}}">{{text}}</a>

дорожка:webpack-your-bags/src/Components/Button.js

import $ from 'jquery';
import template from './Button.html';
import Mustache from 'mustache';
import './Button.scss';

export default class Button {
    constructor(link) {
        this.link = link;
    }

    onClick(event) {
        event.preventDefault();
        alert(this.link);
    }

    render(node) {
        const text = $(node).text();

        // 渲染我们的按钮
        $(node).html(
            Mustache.render(template, {text})
        );

        // 增加监听事件
        $('.button').click(this.onClick.bind(this));
    }
}

Ваша кнопка теперь готова на 100%, независимо от того, когда вы ее импортируете, независимо от того, где она работает, у нее есть все, что ей нужно, и она правильно отображается, теперь нам просто нужно передать нашу кнопку через index.js Render на нашу веб-страницу:

// import $ from 'jquery';
// $('body').html('Hello');

import Button from './Components/Button';
const button = new Button('google.com');
button.render('a');

Давайте запустим webpack, затем обновим страницу index.html, и вы сможете увидеть свою уродливую кнопку.


Теперь, когда вы узнали, как настраивать загрузчики и определять зависимости для каждой части вашего приложения, сейчас это может показаться не важным, так как мы собираемся немного улучшить этот пример.

разделение кода

Приведенный выше пример неплох, потому что в нем есть все, но иногда нам может не понадобиться наша Button, потому что если у некоторых интерфейсов нет метки, то нам не нужно делать ненужный рендеринг метки a в операцию Button. То есть нам не нужно импортировать стиль, шаблон, усы и все, что связано с кнопкой, которую мы настроили выше, верно? Здесь нам нужно разделение кода.
Разделение кода — это решение веб-пакета для громоздких операций, связанных с интерфейсом, в который нам нужно вручную импортировать кнопки, то есть с веб-пакетом вам не нужно искать, какие страницы импортировать, а какие страницы импортировать не нужно. Суть разделения кода заключается в определении точек разделения в вашем коде: ваш код можно легко разбить на отдельные файлы и загрузить по требованию Синтаксис очень прост:

import $ from 'jquery';

// 这是一个拆分点
require.ensure([], () => {
  // 这里所有的代码都是需要被导入的
  // 在一个单独的页面里
  const library = require('some-big-library');
  $('foo').click(() => library.doSomething());
});

в обратном вызове require.ensure (т.е.() => {}) будет разделен на куски — пакеты, которые будут загружаться отдельно через ajax, когда страница должна загрузиться, что означает, что наши пакеты в основном имеют следующее:

bundle.js
|- jquery.js
|- index.js // 我们所有文件都要导入的代码
chunk1.js
|- some-big-libray.js
|- index-chunk.js // 在回调里面的代码

Вам не нужно импортировать chunk1.js, веб-пакет будет загружать его только тогда, когда он в этом нуждается, что означает, что вы можете разделить свой код на несколько фрагментов в соответствии с различной логикой, мы изменим наш index.js ниже, чтобы только кнопка загружается, когда на странице есть тег:

if (document.querySelectorAll('a').length) {
    require.ensure([], () => {
        const Button = require('./Components/Button').default;
        const button = new Button('google.com');
        button.render('a');
    });
}

Обратите внимание, что при использовании require, если вы хотите получить экспорт по умолчанию для объекта Button, вам нужно вручную получить его через .default, потому что require не будет обрабатывать и экспорт по умолчанию, и exports.obj, поэтому вы должны указать, что нужно вернуть, но import может обработать это, поэтому он уже знает (например: import foo from 'bar' получает материал экспорта объекта 'bar' по умолчанию, import {baz} from 'bar' получает материал exports.baz объекта 'bar'). Здесь все немного сложно, если вы не очень хорошо в этом разбираетесь и хотите понять, вы можете поглубже взглянуть на ES6 и nodeJS.

Теперь вывод webpack должен соответственно отличаться, воспользуемсяwebpack --display-modules --display-chunksЗапустите его, чтобы увидеть, какой модуль находится в каком фрагменте

$ webpack --display-modules --display-chunks
Hash: c419d385603afdd301ab
Version: webpack 3.8.1
Time: 1489ms
      Asset     Size  Chunks                    Chunk Names
0.bundle.js   307 kB       0  [emitted]  [big]
  bundle.js  6.48 kB       1  [emitted]         main
chunk    {0} 0.bundle.js 305 kB {1} [rendered]
    [1] ./src/Components/Button.js 1.92 kB {0} [built]
    [2] ./node_modules/jquery/dist/jquery.js 268 kB {0} [built]
    [3] ./src/Components/Button.html 70 bytes {0} [built]
    [4] ./node_modules/mustache/mustache.js 19.4 kB {0} [built]
    [5] ./src/Components/Button.scss 1.16 kB {0} [built]
    [6] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Button.scss 219 bytes {0} [built]
    [7] ./node_modules/css-loader/lib/css-base.js 2.26 kB {0} [built]
    [8] ./node_modules/style-loader/lib/addStyles.js 9.41 kB {0} [built]
    [9] ./node_modules/style-loader/lib/urls.js 3.01 kB {0} [built]
chunk    {1} bundle.js (main) 615 bytes [entry] [rendered]
    [0] ./src/index.js 615 bytes {1} [built]
   [0] ./src/index.js 615 bytes {1} [built]
   [1] ./src/Components/Button.js 1.92 kB {0} [built]
   [2] ./node_modules/jquery/dist/jquery.js 268 kB {0} [built]
   [3] ./src/Components/Button.html 70 bytes {0} [built]
   [4] ./node_modules/mustache/mustache.js 19.4 kB {0} [built]
   [5] ./src/Components/Button.scss 1.16 kB {0} [built]
   [6] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Button.scss 219 bytes {0} [built]
   [7] ./node_modules/css-loader/lib/css-base.js 2.26 kB {0} [built]
   [8] ./node_modules/style-loader/lib/addStyles.js 9.41 kB {0} [built]
   [9] ./node_modules/style-loader/lib/urls.js 3.01 kB {0} [built]

Вы можете видеть, что наша запись ( bundle.js ) теперь имеет только некоторую логику веб-пакета, все остальное (jQuery, Mustache, Button) находится в 0.bundle.js , 0.bundle будет загружен только тогда, когда есть тег на page .js, чтобы вебпак знал, где искать чанки при загрузке с помощью ajax, мы должны добавить в наш конфиг строчку:

output: {
    path: BUILDS_PATH,
    filename: 'bundle.js',
    publicPath: 'builds/',
},

Параметр output.publicPath указывает веб-пакету, где найти созданные ресурсы относительно текущего файла, теперь посетите нашу страницу, и мы увидим, что все работает, но, что более важно, мы можем видеть это из-за тега, поэтому веб-пакет загружается точно фрагмент 0.bundle.js, который мы выделили:


Если на нашей странице нет тега, будет загружен только bundle.js. Это позволяет разумно разделить большие части логики в вашем приложении, чтобы каждая страница загружала только то, что ей действительно нужно. Мы также можем назвать фрагмент Имена пакетов кода мы разделяем, можно использовать семантические имена, можно указать, передав третий параметр в require.ensure:

require.ensure([], () => {
const Button = require('./Components/Button').default;
const button = new Button('google.com');
button.render('a');
}, 'button');

Это приведет к тому, что имена фрагментов пакета кода будут кнопками, а не пустыми:

$ webpack
Hash: 50ed6a7993b581f0bf0a
Version: webpack 3.8.1
Time: 1524ms
      Asset     Size  Chunks                    Chunk Names
0.bundle.js   307 kB       0  [emitted]  [big]  button
  bundle.js  6.49 kB       1  [emitted]         main
   [0] ./src/index.js 625 bytes {1} [built]
   [1] ./src/Components/Button.js 1.92 kB {0} [built]
   [3] ./src/Components/Button.html 70 bytes {0} [built]
   [5] ./src/Components/Button.scss 1.16 kB {0} [built]
   [6] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Button.scss 219 bytes {0} [built]
    + 5 hidden modules

добавить второй компонент

Довольно круто иметь все сейчас, но давайте добавим еще один компонент, чтобы посмотреть, работает ли он:
дорожка:webpack-your-bags/src/Components/Header.scss

.header {
  font-size: 3rem;
}

дорожка:webpack-your-bags/src/Components/Header.html

<header class="header">{{text}}</header>

дорожка:webpack-your-bags/src/Components/Header.js

import $ from 'jquery';

import Mustache from 'mustache';

import template from './Header.html';

import './Header.scss';

export default class Header {

    render(node) {

        const text = $(node).text();

        $(node).html(

            Mustache.render(template, { text })

        );

    }

}

Давайте отрендерим это в нашем index.js:

// If we have an anchor, render the Button component on it
if (document.querySelectorAll('a').length) {
    require.ensure([], () => {
        const Button = require('./Components/Button').default;
        const button = new Button('google.com');

        button.render('a');
    });
}

// If we have a title, render the Header component on it
if (document.querySelectorAll('h1').length) {
    require.ensure([], () => {
        const Header = require('./Components/Header').default;

        new Header().render('h1');
    });
}

Теперь используйте --display-chunks --display-modules, чтобы увидеть вывод веб-пакета:

$ webpack --display-modules --display-chunks
Hash: 66f9e900ac553f5d66eb
Version: webpack 3.8.1
Time: 1646ms
      Asset     Size  Chunks                    Chunk Names
0.bundle.js   306 kB       0  [emitted]  [big]
1.bundle.js   307 kB       1  [emitted]  [big]
  bundle.js  6.56 kB       2  [emitted]         main
chunk    {0} 0.bundle.js 305 kB {2} [rendered]
    [2] ./src/Components/Header.js 1.7 kB {0} [built]
    [3] ./node_modules/jquery/dist/jquery.js 268 kB {0} {1} [built]
    [4] ./node_modules/mustache/mustache.js 19.4 kB {0} {1} [built]
    [5] ./node_modules/css-loader/lib/css-base.js 2.26 kB {0} {1} [built]
    [6] ./node_modules/style-loader/lib/addStyles.js 9.41 kB {0} {1} [built]
    [7] ./node_modules/style-loader/lib/urls.js 3.01 kB {0} {1} [built]
   [11] ./src/Components/Header.html 62 bytes {0} [built]
   [12] ./src/Components/Header.scss 1.16 kB {0} [built]
   [13] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Header.scss 199 bytes {0} [built]
chunk    {1} 1.bundle.js 305 kB {2} [rendered]
    [1] ./src/Components/Button.js 1.92 kB {1} [built]
    [3] ./node_modules/jquery/dist/jquery.js 268 kB {0} {1} [built]
    [4] ./node_modules/mustache/mustache.js 19.4 kB {0} {1} [built]
    [5] ./node_modules/css-loader/lib/css-base.js 2.26 kB {0} {1} [built]
    [6] ./node_modules/style-loader/lib/addStyles.js 9.41 kB {0} {1} [built]
    [7] ./node_modules/style-loader/lib/urls.js 3.01 kB {0} {1} [built]
    [8] ./src/Components/Button.html 70 bytes {1} [built]
    [9] ./src/Components/Button.scss 1.16 kB {1} [built]
   [10] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Button.scss 219 bytes {1} [built]
chunk    {2} bundle.js (main) 601 bytes [entry] [rendered]
    [0] ./src/index.js 601 bytes {2} [built]
   [0] ./src/index.js 601 bytes {2} [built]
   [1] ./src/Components/Button.js 1.92 kB {1} [built]
   [2] ./src/Components/Header.js 1.7 kB {0} [built]
   [3] ./node_modules/jquery/dist/jquery.js 268 kB {0} {1} [built]
   [4] ./node_modules/mustache/mustache.js 19.4 kB {0} {1} [built]
   [5] ./node_modules/css-loader/lib/css-base.js 2.26 kB {0} {1} [built]
   [6] ./node_modules/style-loader/lib/addStyles.js 9.41 kB {0} {1} [built]
   [7] ./node_modules/style-loader/lib/urls.js 3.01 kB {0} {1} [built]
   [8] ./src/Components/Button.html 70 bytes {1} [built]
   [9] ./src/Components/Button.scss 1.16 kB {1} [built]
  [10] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Button.scss 219 bytes {1} [built]
  [11] ./src/Components/Header.html 62 bytes {0} [built]
  [12] ./src/Components/Header.scss 1.16 kB {0} [built]
  [13] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Header.scss 199 bytes {0} [built]

Вы можете видеть, что здесь есть проблема: оба наших компонента будут использовать jQuery и Mustache, а это означает, что эти две зависимости дублируются по частям.Хотя веб-пакет по умолчанию сделает небольшую оптимизацию, он также может использоватьplugin(плагин) для предоставления более мощной функциональности webpack.

Плагин отличается от загрузчика тем, что он не выполняет некоторые операции над указанными файлами, а обрабатывает все файлы и выполняет некоторые более сложные операции, но его не обязательно конвертировать как загрузчик.В вебпаке есть встроенный плагин, который может делать различные оптимизации, на данный момент нас больше интересует CommonChunksPlugin: он анализирует рекурсивные зависимости ваших фрагментов, а затем извлекает их и помещает в другое место, которое может быть совершенно отдельным файлом или вашим основным файлом.

В нашем текущем примере нам нужно переместить общие зависимости в наш входной файл. Если все файлы требуют jQuery и Mustache, мы также можем переместить их вверх. Давайте обновим наш файл конфигурации webpack .config.js:

......
var webpack = require('webpack');
module.exports = {
    entry:   './src',
    output:  {
      // ...
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name:      'main', // 将依赖移到我们的主文件
            children:  true, // 在所有被拆分的代码块中寻找共同的依赖关系
            minChunks: 2, // 在被提取之前,一个依赖要出现多少次(也就是一个依赖会在遍历所有拆分的代码块时被重复发现多少次)
        }),
    ],
    module:  {
      // ...
    }
};

Если мы повторимwebpack --display-modules --display-chunks, мы видим, что теперь он намного лучше, чем раньше, потому что общие части 0.bundle.js и 1.bundle.js были перемещены в bundle.js.

$ webpack --display-modules --display-chunks
Hash: 22fbf6bdd63c4bbbb096
Version: webpack 3.8.1
Time: 1554ms
      Asset     Size  Chunks                    Chunk Names
0.bundle.js  3.36 kB       0  [emitted]
1.bundle.js  3.58 kB       1  [emitted]
  bundle.js   310 kB       2  [emitted]  [big]  main
chunk    {0} 0.bundle.js 3.12 kB {2} [rendered]
    [7] ./src/Components/Header.js 1.7 kB {0} [built]
   [11] ./src/Components/Header.html 62 bytes {0} [built]
   [12] ./src/Components/Header.scss 1.16 kB {0} [built]
   [13] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Header.scss 199 bytes {0} [built]
chunk    {1} 1.bundle.js 3.37 kB {2} [rendered]
    [6] ./src/Components/Button.js 1.92 kB {1} [built]
    [8] ./src/Components/Button.html 70 bytes {1} [built]
    [9] ./src/Components/Button.scss 1.16 kB {1} [built]
   [10] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Button.scss 219 bytes {1} [built]
chunk    {2} bundle.js (main) 303 kB [entry] [rendered]
    [0] ./src/index.js 601 bytes {2} [built]
    [1] ./node_modules/style-loader/lib/urls.js 3.01 kB {2} [built]
    [2] ./node_modules/jquery/dist/jquery.js 268 kB {2} [built]
    [3] ./node_modules/mustache/mustache.js 19.4 kB {2} [built]
    [4] ./node_modules/css-loader/lib/css-base.js 2.26 kB {2} [built]
    [5] ./node_modules/style-loader/lib/addStyles.js 9.41 kB {2} [built]
   [0] ./src/index.js 601 bytes {2} [built]
   [1] ./node_modules/style-loader/lib/urls.js 3.01 kB {2} [built]
   [2] ./node_modules/jquery/dist/jquery.js 268 kB {2} [built]
   [3] ./node_modules/mustache/mustache.js 19.4 kB {2} [built]
   [4] ./node_modules/css-loader/lib/css-base.js 2.26 kB {2} [built]
   [5] ./node_modules/style-loader/lib/addStyles.js 9.41 kB {2} [built]
   [6] ./src/Components/Button.js 1.92 kB {1} [built]
   [7] ./src/Components/Header.js 1.7 kB {0} [built]
   [8] ./src/Components/Button.html 70 bytes {1} [built]
   [9] ./src/Components/Button.scss 1.16 kB {1} [built]
  [10] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Button.scss 219 bytes {1} [built]
  [11] ./src/Components/Header.html 62 bytes {0} [built]
  [12] ./src/Components/Header.scss 1.16 kB {0} [built]
  [13] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Header.scss 199 bytes {0} [built]

Вы также можете указать это, не указав общее имя чанка или указавasync: trueЧтобы обеспечить асинхронную загрузку общих зависимостей, в webpack есть много таких мощных интеллектуальных оптимизаций. Я не могу перечислить их все, но в качестве упражнения давайте попробуем создать рабочую версию нашего приложения.

производство и оптимизация

Ну, во-первых, мы добавляем несколько плагинов в файл конфигурации, но мы хотим толькоNODE_ENVравныйproductionКогда эти плагины загружены, давайте добавим некоторую логику в файл конфигурации, потому что это всего лишь файл js, поэтому он относительно прост:

......
var webpack    = require('webpack');
var production = process.env.NODE_ENV === 'production';
var plugins = [
    new webpack.optimize.CommonsChunkPlugin({
            name:      'main', // 将依赖移到我们的主文件
            children:  true, // 在所有被拆分的代码块中寻找共同的依赖关系
            minChunks: 2, // 在被提取之前,一个依赖要出现多少次(也就是一个依赖会在遍历所有拆分的代码块时被重复发现多少次)
    }),
];

if (production) {
    plugins = plugins.concat([
       // 生产环境的插件放在这里
    ]);
}

module.exports = {
    entry:   './src',
    output:  {
        path:       BUILDS_PATH,
        filename:   'bundle.js',
        publicPath: 'builds/',
    },
    plugins: plugins,
    ......
};

В качестве второго шага WebPack имеет некоторые настройки, которые мы также можем выключить производство:

......
if (production) {
    plugins = plugins.concat([

        new webpack.LoaderOptionsPlugin({
            debug: true
        }),
        
        ......
    ]);
}

module.exports = {
	//debug:   !production,
    //loaders的调试模式已经在webpack3及以上版本完全移除,为了保持与旧的loaders的兼容性,loaders可以通过LoaderOptionsPlugin插件切换到调试模式
    devtool:production ? false : 'eval',
    ......
    }

Первый установлен в режим без отладки, что означает, что система не будет обрабатывать слишком много кода, что упрощает отладку локальных ресурсов, а второй — контролироватьsourcemapСгенерированный, webpack имеет несколько способов представления исходной карты. Встроенный eval — лучший. Нам не очень важна исходная карта в производственной среде, поэтому мы ее отключаем. Давайте добавим наши производственные плагины (плагины):

if (production) {
    plugins = plugins.concat([

        // 这个插件会寻找类似的块和文件然后进行合并
        //new webpack.optimize.DedupePlugin(),
        //webpack3版本之后这个插件已经被移除

        // 这个插件会根据模块在你的程序中使用的次数来优化模块
        //new webpack.optimize.OccurenceOrderPlugin(),
        //webpack2版本之后这个插件的功能已经是默认开启,无需调用

        // 这个插件可以防止webpack创建太小以至于不值得单独加载的块
        new webpack.optimize.MinChunkSizePlugin({
            minChunkSize: 51200, // ~50kb
        }),

        //这个插件会最小化所有最终资源的javascript代码
        new webpack.optimize.UglifyJsPlugin({
            mangle:   true,
            compress: {
                warnings: false, //  阻止难看的警告
            },
        }),
        //loaders的最小化模式也会在webpack3或者以后的版本中移除

        // 这个插件允许我们可以在生产中设置各种为错误的变量,以避免它们被编译在我们的最后打包的代码中
        new webpack.DefinePlugin({
            __SERVER__:      !production,
            __DEVELOPMENT__: !production,
            __DEVTOOLS__:    !production,
            'process.env':   {
                BABEL_ENV: JSON.stringify(process.env.NODE_ENV),
            },
        }),

    ]);
}

Выше приведены те, которые я использую чаще всего, но webpack предоставляет множество плагинов для оптимизации ваших модулей и чанков.Есть также плагины с различными функциями, написанные другими людьми на npm, вы можете выбрать в соответствии с вашими потребностями.

Другим аспектом ресурсов в производственной среде является то, что мы иногда хотим добавить версии к нашим упакованным ресурсам. Помните, что мы установили для output.filename значение bundle.js выше. Есть несколько переменных, которые могут быть названы в При использовании, одна из них [хеш], который представляет собой версию конечного файла ресурсов. В то же время используйте output.chunkFilename, чтобы добавить версию в чанк. Теперь давайте изменим наш файл конфигурации:

output: {
    path:          'builds',
    filename:      production ? '[name]-[hash].js' : 'bundle.js',
    chunkFilename: '[name]-[chunkhash].js',
    publicPath:    'builds/',
},

Поскольку нет особенно хорошего способа динамически получить имя бандла, скомпилированного этой упрощенной версией приложения (из-за добавления номера версии), мы просто добавляем версию в ресурс в рабочей среде, а окончательный пакет с версией в каталоге после нескольких компиляций. Их будет все больше и больше, поэтому в целях экономии места мы должны добавлять сторонний плагин, чтобы каждый раз очищать нашу предыдущую производственную версию (путь в выводе):

$ npm install clean-webpack-plugin --save-dev

и добавляем его в наш конфиг:

var webpack     = require('webpack');
var CleanPlugin = require('clean-webpack-plugin');

// ...

if (production) {
    plugins = plugins.concat([

        // 清理以前的版本/文件夹
        // 编译我们的最终资源
        new CleanPlugin('builds'),
        ......
        }
        ......

Хорошо, после нескольких небольших оптимизаций давайте сравним результаты:

$ webpack
Hash: 8b942bcf553d3d782e6c
Version: webpack 3.8.1
Time: 1542ms
                    Asset     Size  Chunks                    Chunk Names
0-294790a46bbc09afc580.js  4.34 kB       0  [emitted]
1-24fa8b23bd35ecac8a8f.js  4.57 kB       1  [emitted]
                bundle.js   348 kB       2  [emitted]  [big]  main
......
$ NODE_ENV=production webpack
clean-webpack-plugin: D:\other\webpack-your-bags\builds has been removed.
Hash: 22674bf33e7cdefb2776
Version: webpack 3.8.1
Time: 2973ms
                       Asset    Size  Chunks             Chunk Names
main-22674bf33e7cdefb2776.js  101 kB       0  [emitted]  main
......

Итак, что именно делает веб-пакет? Во-первых, поскольку наш пример очень легкий, два наших асинхронных фрагмента не являются HTTP-запросами, поэтому Webpack объединяет их с входным потоком. И все было сведено к минимуму, мы перешли от 3 HTTP-запросов на 356 КБ в сумме к 1 HTTP-запросу на 101 КБ.

Но Webpack действительно может удалить некоторые большие js-файлы без предупреждения?

Да, это происходит, но это происходит только потому, что наше приложение очень маленькое. Теперь учтите одну вещь: вы понятия не имеете, что объединяется, где и когда. Если блоки кода, которые вы разделяете, внезапно имеют больше зависимостей, эти разделенные блоки будут перемещены в асинхронный блок, а не объединены. Если эти разделенные фрагменты слишком похожи, чтобы их можно было загружать по отдельности, они будут объединены, вам просто нужно настроить это, и веб-пакет автоматически оптимизирует ваше приложение наилучшим образом. Вам не нужно делать это вручную, и вам не нужно думать о том, когда и где нужно полагаться на зависимости, все происходит полностью автоматически.


Вы могли заметить, что мы не используем никаких настроек для минимизации нашего HTML и CSS, потому что по умолчанию, если параметр отладки имеет значение false, как мы говорили ранее, css-loader и html-loader помогут нам сделать это. По этой же причине UglifyJsPlugin — это отдельный плагин (а не загрузчик): потому что в вебпаке нет js-загрузчика, потому что сам вебпак является загрузчиком для JS.

извлекать

Что ж, к настоящему времени вы, возможно, заметили, что с самого начала этого урока наши стили уже находятся внутри веб-страницы и делают действительно уродливую кнопку. Теперь, разве не было бы здорово, если бы мы могли создать окончательный файл CSS со всеми стилями через веб-пакет? Конечно можем, давайте сделаем это с помощью некоторых внешних плагинов:

$ npm install extract-text-webpack-plugin --save-dev

Позвольте мне рассказать вам, что делает этот плагин: он извлекает определенный тип контента из вашего конечного ресурса, который часто используется в CSS, поэтому давайте настроим webpack.config.js (здесь, чтобы соответствовать замене версии, мы заменим модуль .loaders с module.rules, т.к. в будущем module.loaders будут полностью заменены на module.rules):

var webpack    = require('webpack');
var CleanPlugin = require('clean-webpack-plugin');
var ExtractPlugin = require('extract-text-webpack-plugin');
var production = process.env.NODE_ENV === 'production';

var plugins = [
    new ExtractPlugin('bundle.css'), // <=== 内容将被打包到哪里
    new webpack.optimize.CommonsChunkPlugin({
            name:      'main', // 将依赖移到我们的主文件
            children:  true, // 在所有被拆分的代码块中寻找共同的依赖关系
            minChunks: 2, // 在被提取之前,一个依赖要出现多少次(也就是一个依赖会在遍历所有拆分的代码块时被重复发现多少次)
    }),
];

......

module.exports = {
   	......
    plugins: plugins,
    module:  {
		rules: [{
			test: /\.js/,
			loader: 'babel-loader',
			include: SRC_PATH,
		},{
			test: /\.html/,
			loader: 'html-loader',
		},{
			test: /\.scss$/,
			use: ExtractPlugin.extract({
				fallback: 'style-loader',
				use: ['css-loader', 'sass-loader']
			})
		}],
        // loaders: [{
		// 	test: /\.js/,
		// 	loader: 'babel-loader',
		// 	include: SRC_PATH,
		// },{
		// 	test: /\.html/,
		// 	loader: 'html-loader',
		// },
		// {
		// 	test: /\.scss/,
		// 	loader: 'style-loader!css-loader!sass-loader',
		// 	// Or
		// 	// loaders: ['style-loader', 'css-loader', 'sass-loader'],
		// }
		// ]
    }
};

Теперь метод извлечения правил имеет два параметра: первый — что делать с нераспакованным CSS, когда мы находимся в блоке кода («style-loader»), а второй — когда ресурс находится в основном файле («css -loader' !sass-loader') как конвертировать. Теперь, если мы находимся в блоке, мы не можем просто волшебным образом добавить CSS в этом блоке к сгенерированному CSS перед использованием загрузчика, но для тех стилей, которые находятся в основном файле, они должны быть объединены в сборку/связку. .css-файл. Теперь давайте проверим это, добавив небольшую основную таблицу стилей в наше приложение:
дорожка:webpack-your-bags/src/styles.scss

body {
  font-family: sans-serif;
  background: darken(white, 0.2);
}

Измените наш index.js:

import './styles.scss';
......

Давайте запустим webpack и, конечно, у нас теперь есть bundle.css, который мы теперь можем импортировать в наш HTML-файл:

$ webpack
Hash: 6890269852e3f4363514
Version: webpack 3.8.1
Time: 1607ms
                    Asset      Size  Chunks                    Chunk Names
0-4a6171ca8018a700cec4.js   4.26 kB       0  [emitted]
1-ffe316264a9c9fafbb4c.js   4.58 kB       1  [emitted]
                bundle.js    348 kB       2  [emitted]  [big]  main
               bundle.css  70 bytes       2  [emitted]         main
......


Обновление страницы в этот момент может пойти не так, потому что вам нужно объединить все файлы CSS в один файл, вы можете передать параметрыExtractTextPlugin('bundle.css', {allChunks: true}), вы также можете использовать переменные в имени файла здесь, если вы хотите версию стиля, которую вам просто нужно сделатьExtractTextPlugin('[name]-[hash].css').

загрузить изображение

Идеально подходит для всех наших файлов JavaScript сейчас, но одна тема, о которой мы не говорили, — это конкретные ресурсы: как изображения, шрифты и т. д. работают и оптимизируются в веб-пакете? Давайте скопируем изображение с веб-сайта и будем использовать его в качестве фона нашей страницы, потому что я видел, как люди размещали его на Geocities, и это выглядело круто:


Давайте сохраним это изображение в webpack-your-bags/img/puppy.jpg и соответствующим образом обновим наш styles.scss:

body{
	font-family:sans-serif;
	background:url('../img/puppy.jpg') no-repeat 0 0;
	background-size:50%;
}

Если вы сделаете это, веб-пакет уверенно скажет вам «что, черт возьми, он делает в формате png», потому что мы его не загружали. Здесь есть два встроенных загрузчика, которые мы можем использовать для обработки определенных ресурсов: загрузчик файлов и загрузчик URL: первый просто возвращает URL-адрес ресурса, который ничего не меняет, позволяя вам получить URL-адрес в файл версии процесса (это поведение по умолчанию); второй встраивает ресурс в адрес data:image/jpeg;base64.

На самом деле эти два плагина не являются противоположностями: если ваш фон представляет собой изображение размером 2 МБ, то не встраивайте его, а лучше загрузите отдельно. С другой стороны, если это маленькая иконка размером 2 КБ, лучше встроить ее, сохранив HTTP-запросы. Итак, мы установили два:

$ npm install url-loader file-loader --save-dev

{
    test:   /\.(png|gif|jpe?g|svg)$/i,
    loader: 'url-loader?limit=10000',
},

Здесь мы передаем параметр запроса limit загрузчику URL-адресов, сообщая ему о встроении, если ресурс больше 10 КБ или меньше, в противном случае возвращаемся к загрузчику файлов и ссылаемся на него. Этот синтаксис называется строкой запроса, и вы используете его для настройки загрузчика, или вы можете настроить загрузчик через объект:

{
    test:   /\.(png|gif|jpe?g|svg)$/i,
    loader: 'url-loader',
    query: {
      limit: 10000,
    }
}

Что ж, давайте посмотрим на результаты:

$ webpack
Hash: e672f7becf7b049e759d
Version: webpack 3.8.1
Time: 1653ms
                    Asset     Size  Chunks                    Chunk Names
0-4a6171ca8018a700cec4.js  4.26 kB       0  [emitted]
1-ffe316264a9c9fafbb4c.js  4.58 kB       1  [emitted]
                bundle.js   348 kB       2  [emitted]  [big]  main
               bundle.css  2.88 kB       2  [emitted]         main

Как мы видим: ни один из JPG не упакован, потому что наше изображение щенка (2 КБ) меньше настроенного размера (10 КБ), поэтому оно встроено. Это означает, что если мы посетим нашу веб-страницу, мы сможем насладиться славой нашего повелителя щенков, не загружая изображение.


Это очень мощно, потому что это означает, что веб-пакет теперь может интеллектуально оптимизировать определенные ресурсы на основе соотношения размера и HTTP-запроса. С хорошим загрузчиком можно завязать все еще дальше, самый распространенный из них — image-loader, который сжимает все изображения перед их упаковкой. у него даже есть?bypassOnDebugПараметры запроса позволяют делать это только в рабочей среде. Таких плагинов много, и я рекомендую вам взглянуть на них в конце этой статьи.

мы сделаем это живым сейчас

Конструкция, которую мы сделали выше, просто отражает мощь веб-пакета, теперь давайте больше сосредоточимся на отладке локального кода.
Возможно, когда вы упоминаете инструменты сборки, вы часто замечаете большой недостаток: загрузка в реальном времени: LiveReload, BrowserSync, не нужно ждать слишком долго. Но когда мы что-то изменяем, мы надеемся, что вся страница будет обновлена ​​автоматически, чтобы можно было достичь желаемого эффекта за один шаг — так называемая замена модуля или горячая перезагрузка. Идея состоит в том, что теперь, когда веб-пакет знает, где находится каждый модуль нашего дерева зависимостей, небольшое изменение можно отразить в простом патче с новым файлом. Проще говоря: вносимые вами изменения отображаются на странице в режиме реального времени, но вы не понимаете, что страница была обновлена.

Чтобы использовать HMR (горячая загрузка модулей), нам нужен сервер, который может обслуживать наши горячие ресурсы. В webpack есть встроенный dev-сервер, мы можем этим воспользоваться, поэтому установим его:

$ npm install webpack-dev-server --save-dev

Теперь, чтобы запустить наш сервер разработки, нет ничего проще, просто выполните следующую команду:

$ webpack-dev-server

Теперь давайте получим доступ к веб-серверуhttp://localhost:8080/webpack-dev-server/. Вы увидите свою обычную страницу, но теперь мы модифицируем файл SASS, как по волшебству:


Теперь, когда мы бежимwebpack-dev-serverОн уже находится в режиме HMR. Обратите внимание, что здесь мы используем webpack-dev-server для обслуживания наших горячих ресурсов, но вы также можете использовать несколько других опций, таких как Express server. Webpack предоставляет промежуточное программное обеспечение, и вы можете использовать это промежуточное программное обеспечение для переноса HMR с поддержкой plug-and-play на другие серверы.

очистить или засохнуть

Если вы следили за этим руководством, вы, вероятно, заметили нечто странное: почему загрузчики вложены в module.rules(module.loaders), а плагины — нет? Конечно, это потому, что вы можете добавлять в модули и другие вещи! В webpack есть не только загрузчики, но и фронтальные загрузчики, пост-загрузчики: наш основной загрузчик до или после выполнения кода. Давайте возьмем пример: я уверен, что код, который я пишу, ужасен, поэтому мы применяем ESLint к нашему коду, прежде чем преобразовывать его:

$ npm install eslint eslint-loader babel-eslint --save-dev

Теперь давайте создадим простой файл .eslintrc, который, как я знаю, позже не сработает:
дорожка:webpack-your-bags/.eslintrc

{
    "parser": "babel-eslint",
    "rules": {
        "quotes": 2
    }
}

Теперь добавим наш прелоадер, мы просто используем тот же синтаксис, что и раньше, только в module.preLoaders (module.preLoaders был удален после версии webpack2, где preLoader может быть изменен до конфигурации правил):

module:  {
        // preLoaders: [{
        //     test: /\.js/,
        //     loader: 'eslint-loader',
        // }],
        rules: [{
            test: /\.js$/,
            exclude: /node_modules/,
            enforce: "pre",
            loader: "eslint-loader"
        }],
}

Теперь, если мы запустим webpack, произойдет сбой:

$ webpack
Hash: 891f6c68504d0f787afa
Version: webpack 3.8.1
Time: 2335ms
                    Asset     Size  Chunks                    Chunk Names
0-4a6171ca8018a700cec4.js  4.26 kB       0  [emitted]
1-ffe316264a9c9fafbb4c.js  4.58 kB       1  [emitted]
                bundle.js   348 kB       2  [emitted]  [big]  main
               bundle.css  2.88 kB       2  [emitted]         main
   [0] ./src/index.js 627 bytes {2} [built] [1 error]
   [1] ./src/styles.scss 41 bytes {2} [built]
   [7] ./src/Components/Button.js 1.92 kB {1} [built] [1 error]
   [8] ./src/Components/Header.js 1.7 kB {0} [built] [1 error]
   [9] ./src/Components/Button.html 70 bytes {1} [built]
  [10] ./src/Components/Button.scss 1.16 kB {1} [built]
  [11] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Button.scss 219 bytes {1} [built]
  [12] ./src/Components/Header.html 62 bytes {0} [built]
  [13] ./src/Components/Header.scss 1.16 kB {0} [built]
  [14] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/Components/Header.scss 199 bytes {0} [built]
  [15] ./node_modules/css-loader!./node_modules/sass-loader/lib/loader.js!./src/styles.scss 297 bytes [built]
  [16] ./img/puppy.jpg 2.8 kB [built]
    + 5 hidden modules

ERROR in ./src/Components/Button.js

D:\other\webpack-your-bags\src\Components\Button.js
   1:15  error  Strings must use doublequote  quotes
   2:22  error  Strings must use doublequote  quotes
   3:22  error  Strings must use doublequote  quotes
   4:8   error  Strings must use doublequote  quotes
  25:11  error  Strings must use doublequote  quotes
......

Вышеупомянутую ошибку оставляем вам решать самостоятельно.Теплое напоминание: переведите содержание ошибки "Строки должны использовать двойные кавычки". Если это не может быть временно решено, закомментируйте содержимое, связанное с eslint, а затем перейдите к следующему содержимому.
Давайте запустим еще один пример фронтального загрузчика: для каждого компонента мы импортируем свою таблицу стилей с тем же именем и шаблон с тем же именем. Давайте используем предварительный загрузчик для автоматической загрузки любого файла с тем же именем, что и у модуля:

$ npm install baggage-loader --save-dev

{
	test: /\.js/,
	enforce: "pre",
    loader: 'baggage-loader?[file].html=template&[file].scss'
}

Это говорит веб-пакету импортировать как шаблон, если он встречает файл HTML с таким же именем, а также импортирует любые файлы SASS с тем же именем. Теперь мы можем изменить наш компонент следующим образом:

от

import $ from 'jquery';
import template from './Button.html';
import Mustache from 'mustache';
import './Button.scss';

стали:

import $ from 'jquery';
import Mustache from 'mustache';

Как видите, с таким мощным фронтальным погрузчиком не уступает и пост-погрузчик. Взгляните на список доступных загрузчиков в конце этой статьи, и вы обязательно найдете в нем много вариантов использования. Взгляните на список доступных загрузчиков в конце этого поста, и вы обязательно найдете больше вариантов использования.

дополнительный

Что касается webpack-dev-server, мы можем не только запустить командную строку напрямую,webpack-dev-serverЗапускаем наш веб-сервер и настраиваем ярлыки командных строк в скриптах файла package.json, например:

"scripts": {
  "start:dev": "webpack-dev-server"
}

Затем мы можем запустить командную строку напрямуюnpm run start:dev(эквивалентноwebpack-dev-server), чтобы запустить наш веб-сервер.

Вы хотите знать больше?

На данный момент наше приложение довольно маленькое, поэтому мы до сих пор не видим преимущества веб-пакета, но по мере того, как это приложение становится больше, веб-пакет станет полезным, и вы сможете лучше понять наши фактические зависимости. Что такое дерево отношений. . Мы можем делать это правильно или неправильно, когда наше приложение станет перегруженным и так далее. Теперь внутри приложения webpack знает все, но вы должны вежливо дать ему показать, что он знает. Вы можете запустить следующие команды, создав файл конфигурации:

webpack --profile --json > stats.json

Первый флаг (--profile) указывает веб-пакету сгенерировать файл конфигурации, второй (--json) создает свой JSON, последний (> stats.json) у нас все это выводится в файл JSON. В настоящее время существует множество веб-сайтов, анализирующих эти файлы конфигурации, но веб-пакет предоставляет официальный веб-сайт, который объясняет эту информацию один за другим. Итак, перейдите в webpack, чтобы проанализировать и импортировать файл JSON. Теперь перейдите на вкладку «Модули», и вы должны увидеть визуальное представление вашего дерева зависимостей:


Чем краснее точка, тем больше она повлияет на вашу последнюю сумку. В нашем приложении этой точкой является Jquery, потому что это самый важный из всех наших модулей. Просмотрите все вкладки, осмотритесь, в нашем маленьком приложении вы многому не научитесь, но этот инструмент очень важен, потому что он дает представление о вашем дереве зависимостей и, в конечном итоге, о ресурсах. Теперь, как я уже сказал, другие сервисы могут предоставить информацию о ваших файлах конфигурации, еще один, который мне нравится, — это Webpack Visualizer: он показывает круговую диаграмму того, сколько места занимают зависимости вашего приложения, и, конечно же, он также может представлять наш случай. :


это все, что у нас есть

Что я знаю сейчас, так это то, что webpack полностью заменил grunt и gulp: я использовал их по большей части, но теперь я использую webpack, а в остальном я просто использую npm-скрипты. Для каждого примера общая задача, которая у нас была в прошлом, — преобразовать нашу документацию API в HTML через Aglio, что легко сделать, например, мы модифицируем наш файл package.json:

{
  "scripts": {
    "build": "webpack",
    "build:api": "aglio -i docs/api/index.apib -o docs/api/index.html"
  }
}

Но если у вас есть более сложные задачи по связыванию и активам в gulp, webpack будет лучше работать на других сборках. Посмотрите, как этот пример интегрирует webpack в gulp:

var gulp = require('gulp');
var gutil = require('gutil');
var webpack = require('webpack');
var config = require('./webpack.config');

gulp.task('default', function(callback) {
  webpack(config, function(error, stats) {
    if (error) throw new gutil.PluginError('webpack', error);
    gutil.log('[webpack]', stats.toString());

    callback();
  });
});

И это все, потому что webpack также имеет API узла, поэтому его можно легко использовать в других системах сборки, и в любом случае вы найдете его идеи повсюду.

В любом случае, я думаю, что это достаточно хорошее представление о том, что может сделать для вас webpack. Вы можете подумать, что мы многое рассмотрели в этом посте, но мы думаем, что это всего лишь поверхностная информация: несколько точек входа, предварительная выборка, замена фона и многое другое. webpack — впечатляющий и мощный инструмент, конечно, потому что он более непрозрачный, чем традиционные инструменты сборки, я не буду этого отрицать. Однако, как только вы узнаете, как его приручить, он пошлет в ваши уши сладкий звук, который вы никогда не забудете. Я использовал его в нескольких проектах, и он обеспечивает оптимизацию и автоматизацию, и я не могу представить, сколько раз мне приходится биться головой туда-сюда, чтобы узнать, где и когда нужные ресурсы.

ресурс

Примечание

Каждый пакет сегментированного кода был загружен на GitHub, посетите адрес:

GitHub.com/Хуан Рихуа94/WebAfraid…