webpack от начального до продвинутого

Webpack

предисловие

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

Предварительные знания статьи

Прежде чем изучать webpack, давайте вместе изучим необходимые вспомогательные понятия

знание пути

Перед этим необходимо понять знание путей NodeJS, которые будут использоваться в webpack:Знание пути NodeJS

Основная концепция вступления

Запись используется для указания записи упаковки веб-пакета.

Использование записи

Одиночная запись: запись представляет собой строку

module.exports = {
    entry: './src/index.js'
};

Несколько записей: запись является объектом

module.exports = {
    entry: {
        index: './src/index.js',
        manager: './src/manager.js'
    }
};

Основная концепция вывода

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

Использование вывода: конфигурация с одним входом

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js’,
        path: __dirname + '/dist'
    }
};

Использование вывода: конфигурация с множественной записью

module.exports = {
    entry: {
        app: './src/app.js',
        search: './src/search.js'
    },
    output: {
        filename: '[name].js',
        path: __dirname + '/dist'
    }
};

Обеспечьте уникальные имена файлов с заполнителем [имя]

Основная концепция Loader

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

Каковы общие загрузчики?

babel-loader, 将es6+语法转换为es3/es5语法
css-loader,
style-loader,
scss-loader, 
less-loader,
postcss-loader,
file-loader, 进行图片字体等资源的打包
ts-loader, 将ts转换为js
raw-loader 将文件以字符串形式导入
...

Использование загрузчика

const path = require('path');
module.exports = {
    output: {
        filename: 'bundle.js'
    },
    module: {
        rules: [
            { test: /\.js$/, use: 'babel-loader' }
        ]
    }
};

test указывает правило сопоставления

use указывает имя используемого загрузчика

Основная концепция плагинов

Плагины используются для оптимизации пакета, управления ресурсами и внедрения переменных среды. Действует на весь процесс сборки

Общие плагины

CleanWebpackPlugin 清理构建目录
MiniCssExtractPlugin 将css从bundle文件里提取为一个独立的css文件
htmlWebpackPlugin 创建html去承载输出的bundle文件
UglifyWebpackPlgin 压缩js 去除console等指定语句
...

Использование плагинов

const path = require('path');
module.exports = {
    output: {
        filename: 'bundle.js'
    },
    plugins: [
        new HtmlWebpackPlugin({template:'./src/index.html'})
    ]
};

Все плагины должны быть помещены в массив plugins

Настоящий бой

Перед этим мы должны установить следующие зависимости:

npm install webpack webpack-cli -D

Преобразование синтаксиса ES6 в ES5

Установите необходимые зависимостиnpm i @babel/core @babel/preset-env babel-loader -D

webpack.config.js настраивается следующим образом:

module: {
    rules: [
        {
            test: /\.js$/,
            use: 'babel-loader'
        },
    ]
};

Добавьте предустановленную конфигурацию Babel для ES6.

Конфигурационный файл babel: .babelrc [новый в корневом каталоге проекта]

{
    "presets": [
        "@babel/preset-env" //可以根据配置的目标浏览器或者运行环境来自动将ES2015+的代码转换为es5。
    ]
}

Разобрать React JSX

Установите необходимые зависимости

npm i @babel/preset-react -D

Конфигурация выглядит следующим образом:

Конфигурационный файл babel: .babelrc [новый в корневом каталоге проекта]

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ],
    "plugins": []
}

Разбор ресурсов: разбор CSS

npm install style-loader css-loader less-loader -D
module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /\.less$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'less-loader'
                ]
            },
        ]
},

css-loader используется для загрузки файлов .css и преобразования их в объекты commonjs.

style-loader передает стили через<style>теги вставляются в голову

конфигурация загрузчика стилей:

options: {
    insertAt: 'top', // 样式插入到 <head>
    singleton: true, //将所有的style标签合并成一个
}

less-loader преобразует меньше синтаксиса в синтаксис css

Примечание. Порядок выполнения загрузчиков снизу вверх, справа налево.

Разбор ресурсов: разбор изображений и файлов шрифтов

npm install url-loader file-loader -D
module: {
        rules: [
            {
                test: /\.(png|jpg|gif|jpeg)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 10240
                        }
                    }
                ]
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: 'file-loader'
            }
        ]
},

файл-загрузчик для обработки файлов шрифтов

url-loader можно использовать для обработки изображений и шрифтов Вы можете установить ограничение на автоматические меньшие ресурсы base64

загрузчик файлов используется в загрузчике URL

Мониторинг файлов в webpack

Мониторинг файлов заключается в автоматическом восстановлении нового выходного файла, когда обнаруживается, что исходный код изменился.

Есть два способа включить webpack для режима мониторинга:

· При запуске команды webpack укажите параметр --watch [рекомендуется]

Установите watch: true в конфигурации webpack.config.js

Использование мониторинга файлов в веб-пакете

Настроить в package.json

{
    "name": "hello-webpack",
    "main": "index.js",
    "scripts": {
        "watch": "webpack --watch",
        "build": "webpack --config webpack.prod.js",
    },
}

Горячее обновление: webpack-dev-сервер

npm install webpack-dev-server -D

Настраивается под объектом скриптов в package.json:

{
    "scripts": {
        "dev": "webpack-dev-server --open --mode development"
    },
}

--open означает автоматически открывать браузер

webpack-dev-server не обновляет браузер не выводит файл, а кладет в память

Используйте плагин HotModuleReplacementPlugin (встроенный плагин webpack) для автоматического обновления браузера.

Конфигурация в webpack.config.js:

const webpack = require('webpack');
plugins: [
    new webpack.HotModuleReplacementPlugin(),
],
devServer:{
    hot: true,
    contentBase: [path.join(__dirname, "public"), path.join(__dirname, "assets")], //contentBase 用于配置提供额外静态文件内容的目录,之前提到的 publicPath 是配置构建好的结果以什么样的路径去访问
    proxy: {
        '/api': {
            target: "http://localhost:3000", // 将 URL 中带有 /api 的请求代理到本地的 3000 端口的服务上
            pathRewrite: { '^/api': '' }, // 把 URL 中 path 部分的 `api` 移除掉
        },
    },
    before(app) {
            app.get('/some/path', function(req, res) { // 当访问/some/path 路径时,返回自定义的 json 数据
                res.json({ custom: 'response' })
            })
    }
}

Перед обработкой промежуточного программного обеспечения статических ресурсов webpack-dev-server его можно использовать для перехвата некоторых запросов на возврат определенного контента или для реализации простого имитации данных.

after После обработки промежуточного программного обеспечения статического ресурса webpack-dev-server оно редко используется и может использоваться для печати журналов или выполнения некоторой дополнительной обработки.

Принципиальный анализ горячего обновления

Компиляция Webpack: скомпилируйте JS в пакет

Сервер HMR: экспортировать оперативно обновляемые файлы в HMR Rumtime.

Сервер Bundle: обеспечивает доступ к файлам в браузере (например, localhost:8080/Bundle.js).

HMR Rumtime: на этапе упаковки на этапе разработки он будет внедрен в файл bundle.js на стороне браузера, а файл bundle.js на стороне браузера установит соединение с браузером, обычно через веб-сокет, чтобы изменения в файл может быть обновлен.Файл автоматически обновляется при получении каких-либо изменений в файле

bundle.js: построить выходной файл

хэш файла

Хэш-значение файла — это суффикс имени выходного файла после упаковки.

Как генерируются хэши файлов

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

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

Contenthash: определите хеш в соответствии с содержимым файла.Если содержимое файла не изменилось, хеш содержимого останется неизменным.

Настройки хэша файла JS

Чтобы установить имя выходного файла, используйте [chunkhash]

output: {
    filename: '[name][chunkhash:8].js',
    path: __dirname + '/dist'
}

Примечание: чанкхэш нельзя использовать с горячим обновлением.

Настройки хэша файла CSS

Установите имя файла MiniCssExtractPlugin,

использовать [контентхэш]

npm install mini-css-extract-plugin -D
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
plugins: [
     new MiniCssExtractPlugin({
         filename: `[name][contenthash:8].css`
     });
]

Если вы хотите извлечь css, то style-loader использовать нельзя, потому что они взаимоисключающие, поэтому мы можем написать так:

module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    - 'style-loader',
                    + MiniCssExtractPlugin.loader
                    'css-loader'
                ]
            },
            {
                test: /\.less$/,
                use: [
                    - 'style-loader',
                    + MiniCssExtractPlugin.loader
                    'css-loader',
                    'less-loader'
                ]
            },
        ]
},

Настройки хэша файла изображения и шрифта

module: {
rules: [
    {
        test: /\.(png|svg|jpg|gif)$/,
        use: [{
              loader: 'file-loader’,
              options: {
                 name: 'img/[name][hash:8].[ext] '
              }
        }]
    }
]

Знакомство с заполнителями

[ext] суффикс имени ресурса

[имя] имя файла

[путь] относительный путь к файлу

[папка] Папка, в которой находится файл

[contenthash] Хэш содержимого файла генерируется по умолчанию md5

[хэш] Хэш содержимого файла генерируется по умолчанию md5

сжатие кода

HTML-сжатие

CSS-сжатие

JS-сжатие

Сжатие JS-файлов

npm install uglifyjs-webpack-plugin -D

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

plugins: [
     new UglifyJsPlugin()
]

Сжатие файлов CSS

npm install optimize-css-assets-webpack-plugin cssnano -D

Используйте плагин optim-css-assets-webpack-plugin, Также используйте cssnano [препроцессор]

const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
plugins: [
     new OptimizeCSSAssetsPlugin({
         assetNameRegExp: /\.css$/g,
         cssProcessor: require('cssnano’)
    })
]

Сжатие html файлов

npm install html-webpack-plugin -D

Используйте html-webpack-plugin для установки параметров сжатия

в webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
     new HtmlWebpackPlugin({
         template: path.join(__dirname, '/dist/index.html’),
         filename: 'index.html’,
         chunks: ['index’],
         inject: true,
         minify: {
             html5: true,
             collapseWhitespace: true, //去除空格
             preserveLineBreaks: false, //去除换行
             minifyCSS: true,
             minifyJS: true,
             removeComments: false //去除注释
        }
    })
]

Автоматически очищать каталоги сборки

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

использовать чистый-webpack-плагин

По умолчанию выходной каталог, указанный в output, будет удален.

npm install clean-webpack-plugin -D

const CleanWebpackPlugin = require('clean-webpack-plugin');
plugins: [
    new CleanWebpackPlugin(),
]

Автопрефикс плагина PostCSS дополняет префиксы css3

IE: Trident(-ms) Firefox: Geko(-moz) Google: Webkit(-webkit) Oupeng: Presto(-o)

npm install postcss-loader autoprefixer -D

 module: {
        rules: [
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'less-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
                            plugins: () => [
                                require('autoprefixer')({
                                    browsers: ['last 2 version', '>1%', 'ios 7']
                                })
                            ]
                        }
                    },
                ]
            },
        ]
    },

Мобильный терминал CSS px автоматически конвертируется в rem

что такое рем?

Определение rem W3C: размер шрифта корневого элемента

Сравнение rem и px:

бэр - относительная единица

px - абсолютная единица

Используйте px2rem-загрузчик

Чтобы вычислить значение размера шрифта корневого элемента при отображении страницы, вы можете использовать библиотеку lib-flexible Taotao.

npm install px2rem-loader -D
npm i lib-flexible raw-loader@0.5.1 -S

module: {
        rules: [
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'less-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
                            plugins: () => [
                                require('autoprefixer')({
                                    browsers: ['last 2 version', '>1%', 'ios 7']
                                })
                            ]
                        }
                    },
                    {
                        loader: 'px2rem-loader',
                        options: {
                            remUnit: 75,
                            remPrecision: 8 //转换好rem的小数点位数
                        }
                    }
                ]
            }
        ]
}

Используйте необработанный загрузчик, чтобы встроить lib-flexible в index.html

<script>
    ${require('raw-loader!babel-loader!../node_modules/lib-flexible')}
</script>

встроенный html в исходном загрузчике

<script>
    ${require(' raw-loader!babel-loader!. /meta.html')}
</script>

Концепция многостраничного приложения (MPA)

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

Распространенные решения для многостраничной упаковки: Динамически получить запись и установить html-webpack-plugin

использоватьglobбиблиотека

Напишите следующий код в webpack.config.js:

npm install glob -D

const glob = require('glob');
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
const setMPA = () => {
    const entry = {};
    const htmlWebpackPlugins = [];
    const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js'));

    Object.keys(entryFiles)
        .map((index) => {
            const entryFile = entryFiles[index];

            const match = entryFile.match(/src\/(.*)\/index\.js/);
            const pageName = match && match[1];

            entry[pageName] = entryFile;
            htmlWebpackPlugins.push(
                new HtmlWebpackPlugin({
                    inlineSource: '.css$',
                    template: path.join(__dirname, `src/${pageName}/index.html`),
                    filename: `${pageName}.html`,
                    chunks: ['vendors', pageName],
                    inject: true,
                    minify: {
                        html5: true,
                        collapseWhitespace: true,
                        preserveLineBreaks: false,
                        minifyCSS: true,
                        minifyJS: true,
                        removeComments: false
                    }
                })
            );
        });

    return {
        entry,
        htmlWebpackPlugins
    }
}
const { entry, htmlWebpackPlugins } = setMPA();

module.exports = {
    entry: entry,
    plugins: [].concat(htmlWebpackPlugins)
}

DefinePlugin

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      PRODUCTION: JSON.stringify(true), // const PRODUCTION = true
      VERSION: JSON.stringify('5fa3b9'), // const VERSION = '5fa3b9'
      BROWSER_SUPPORTS_HTML5: true, // const BROWSER_SUPPORTS_HTML5 = 'true'
      TWO: '1+1', // const TWO = 1 + 1,
      CONSTANTS: {
        APP_VERSION: JSON.stringify('1.1.2') // const CONSTANTS = { APP_VERSION: '1.1.2' }
      }
    }),
  ],
}

С приведенной выше конфигурацией вы можете получить доступ к настроенным переменным в файле кода приложения, например:

console.log("Running App version " + VERSION);

if(!BROWSER_SUPPORTS_HTML5) require("html5shiv");

Если сконфигурированное значение представляет собой строку, вся строка будет выполнена как фрагмент кода, а результатом будет значение конечной переменной, например «1+1» выше, окончательный результат равен 2.

Если сконфигурированное значение не является строкой или литералом объекта, то значение будет преобразовано в строку, например true, и конечным результатом будет «true».

Если конфигурация является литералом объекта, то все ключи объекта будут определены одинаково

Итак, мы можем понять, почему используется JSON.stringify(), поскольку результатом JSON.stringify(true) является «истина», а результатом JSON.stringify("5fa3b9") является "5fa3b9".

copy-webpack-plugin

const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
  // ...
  plugins: [
    new CopyWebpackPlugin([
      { from: 'src/file.txt', to: 'build/file.txt', }, // 顾名思义,from 配置来源,to 配置目标路径
      { from: 'src/*.ico', to: 'build/*.ico' }, // 配置项可以使用 glob
      // 可以配置很多项复制规则
    ]),
  ],
}

ProvidePlugin

Можно понимать как более удобное введение, типа jquery, lodash

new webpack.ProvidePlugin({
     _: 'lodash',//import _ from lodash
     $: 'jquery'//import $ from jquery
})

IgnorePlugin

Вы можете использовать webpack.IgnorePlugin напрямую, чтобы получить его.

Этот плагин используется для игнорирования некоторых определенных модулей, чтобы веб-пакет не упаковывал эти указанные модули. Например, когда мы используем moment.js, после прямой ссылки в нем много кода i18n, что приводит к запакованному в итоге большому файлу, а реальной сцене эти коды i18n не нужны, тогда мы можем использовать IgnorePlugin для игнорирования файла этих кодов, настроенный следующим образом:

module.exports = {
  // ...
  plugins: [
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
  ]
}

Для конфигурации IgnorePlugin есть два параметра: первый — это регулярное выражение, соответствующее пути импортированного модуля, а второй — соответствующий контекст соответствующего модуля, то есть имя каталога, в котором он находится.

тип исходной карты

devtool:

source-map:

Разработка:cheap-module-eval-source-map

Производство:hidden-source-map

Разделение базовой библиотеки

Идея: внедрить базовые пакеты react, react-dom, axios, element-ui через CDN вместо бандла.

npm install html-webpack-externals-plugin -D

使⽤ html-webpackexternals-plugin

const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
plugins:[
    new HtmlWebpackExternalsPlugin({
                externals: [
                      {
                        module: 'react',
                        entry: 'cdn.com/react.min.js',
                        global: 'React',
                      },
                      {
                        module: 'axios',
                        entry: 'cdn.com/axios.min.js',
                        global: 'Axios',
                      },
                ]
    }),
]

Обычное разделение скрипта с помощью SplitChunksPlugin

SplitChunksPlugin — это встроенный Webpack4, который заменяет плагин CommonsChunkPlugin для webpack3.

async Асинхронно введенные библиотеки разделены (по умолчанию)

Библиотеки, введенные начальной синхронизацией, разделены

all Все импортированные библиотеки разделены (рекомендуется)

optimization: {
     splitChunks: {
        chunks: 'all',
        minSize: 30000,//抽离的公共包最小的大小
        maxSize: 0, //抽离的公共包最大的大小
        minChunks: 1, //一段代码多处都使用的次数 如果大于这里的次数就抽离成公共的文件
        maxAsyncRequests: 5,
        maxInitialRequests: 3,//浏览器同时请求的异步js的数量
        name: true,
        cacheGroups: {
            vendors: {
                test: /(axios|react)/,
                priority: -10,
                minChunks: 1
            }
        }
    }
 }

treeShaking (оптимизация встряхивания дерева)

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

Использование: webpack4 поддерживается по умолчанию, просто установите modules: false в .babelrc Требование: должен быть синтаксис ES6, метод CommonJS не поддерживается.

Он включен по умолчанию в производственном режиме.

В случае treeShaking:

代码执⾏的结果不会被⽤到

代码不会被执⾏,不可到达

代码只会影响死变量(只写不读)

Принцип встряхивания дерева

Особенности модуля Lee Use ES6:

Появляется только как оператор на верхнем уровне модуля

Имя модуля импорта может быть только строковой константой.

привязка импорта неизменна

Стирание кода: на этапе исправления удаляется неиспользуемый код.

встряхивание деревьев не работает, если в коде есть побочные эффекты

Вы можете настроить sideEffect:[] в package.json, например, babel-polyfill

В построенном коде много замыкающего кода

Большое количество скоупов оборачивает код, что приводит к увеличению размера (чем больше модулей, тем очевиднее)

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

Анализ преобразования модуля

В заключение:

Модули, конвертированные webpack, будут обернуты слоем

импорт будет преобразован в __webpack_require

Дальнейший анализ механизма модуля webpack

анализировать: Пакет представляет собой IIFE (анонимное закрытие)

модули — это массив, каждый элемент — это функция инициализации модуля

__webpack_require используется для загрузки модулей и возвращает module.exports

Используйте подъем прицела, чтобы устранить многие феномены закрытия.

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

Должен быть синтаксис ES6, CJS не поддерживает

plugins: [
 new webpack.optimize.ModuleConcatenationPlugin()
]

Ленивая загрузка модуля

Одной из особенностей webpack является разделение вашей кодовой базы на чанки (chunks) и загрузка их при запуске кода до тех пор, пока они ему не потребуются.

CommonJS: require.ensure

ES6: динамический импорт (пока нет встроенной поддержки, требуется преобразование babel)

Как использовать динамический импорт?

Установите плагин Babel ES6: динамический импорт (пока нет встроенной поддержки, требуется преобразование babel)

Настройте в .babelrc:

npm install @babel/plugin-syntax-dynamic-import --save-dev

{
    "plugins": ["@babel/plugin-syntax-dynamic-import"],
}

Использовать в коде:

loadComponent() {
    import('./text.js').then((Text) => {
        this.setState({
            Text: Text.default
        });
    });
}

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

webpack связывает библиотеки и компоненты

Помимо упаковки приложений, webpack также можно использовать для упаковки js-библиотек.

Реализовать упаковку дополнительной библиотеки

Требуются упакованные сжатые и несжатые версии

Поддержка модуля AMD/CJS/ESM

//src下index.js
export default function add(a, b) {
    return a + b;
}

Как выставить библиотеку?

Только сжатие .min может быть сопоставлено с помощью плагина TerserPlugin.

npm install terser-webpack-plugin -D
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
    entry: {
        'large-number': './src/index.js',
        'large-number.min': './src/index.js'
    },
    output: {
        filename: '[name].js',
        library: 'largeNumber',
        libraryTarget: 'umd',
        libraryExport: 'default'
    },
    mode: 'none',
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin({
                include: /\.min\.js$/,
            })
        ]
    }
}

//index.js中
if (process.env.NODE_ENV === 'production') {
    module.exports = require('./dist/large-number.min.js');
} else {
    module.exports = require('./dist/large-number.js');
}

установить входной файл

Основное поле package.json — index.js.

Затем можно запаковать webpack

SSR

Идеи:

СерверИспользуйте метод renderToString react-dom/server для Компонент React отображается как строка, а маршрутизация на стороне сервера возвращает соответствующий шаблон.

клиент

Упакуйте компоненты для сервера

<!--search.html-->
<body>
    <div id="root"><!--HTML_PLACEHOLDER--></div>
</body>

Используйте аннотации для заполнителей здесь

const fs = require('fs');
const path = require('path');
const express = require('express');
const { renderToString } = require('react-dom/server');
const SSR = require('../dist/search-server');
const template = fs.readFileSync(path.join(__dirname, 'search.html'), 'utf-8');

const server = (port) => {
    const app = express();

    app.use(express.static('dist'));
    app.get('/search', (req, res) => {
        const html = renderMarkup(renderToString(SSR));
        res.status(200).send(html);
    });

    app.listen(port, () => {
        console.log('Server is running on port:' + port);
    });
};

server(process.env.PORT || 3000);

const renderMarkup = (str) => {
    return template.replace('<!--HTML_PLACEHOLDER-->', str)
}

оптимизация SSR

Сократите объем данных, необходимых для первого экрана, и устраните избыточные данные и запросы;

разумно контролировать кеш и кешировать данные/страницы;

Запрос на страницу доставляется в виде потока;

Как оптимизировать журналы сборки для командной строки

Используйте дружественный-ошибки-webpack-плагин

const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
plugins: [
    new FriendlyErrorsWebpackPlugin()
],
stats: 'errors-only'

отчет об ошибках сборки

plugins:[
    new FriendlyErrorsWebpackPlugin(),
    function() {
            this.hooks.done.tap('done', (stats) => {
                if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf('--watch') == -1)
                {
                    console.log('错误上报');
                    process.exit(1);
                }
            })
        }  
]

Компилятор срабатывает после каждой сборки крючки

В webpack4 это this.hooks.done.tap, в webpack3 это this.plugin

Спецификация process.exit в Node.js

0 означает успешное завершение, в функции обратного вызова, ERR NULL NULL

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

Опубликовать в нпм

升级补丁版本号:npm version patch  //一般是修改了bug

升级小版本号:npm version minor //一般是发布了feture

升级大版本号:npm version major //一般是重大更新

оптимизация веб-пакета

Объем анализа webpack-bundle-analyzer

npm install webpack-bundle-analyzer -D
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
plugins:[
    new BundleAnalyzerPlugin(),
]

После завершения сборки размер будет отображаться на порту 8888.

Обычно рекомендуется использовать более высокие версии узлов и веб-пакетов, поскольку они внутренне оптимизированы.

Причины использовать webpack4

V8 обеспечивает оптимизацию (для альтернативных forEach, Map и Set альтернативных объектов, включает альтернативный indexOf)

По умолчанию использовать более быстрый алгоритм хэширования md4.

Webpack AST может быть передан напрямую от загрузчика в AST, что сокращает время анализа

Используйте строковые методы вместо регулярных выражений

Создание нескольких процессов/многоэкземпляров: варианты параллельного разбора ресурсов

npm install cache-loader thread-loader -D

Используйте загрузчик потоков для разрешения ресурсов

Принцип: каждый раз, когда веб-пакет анализирует модуль, загрузчик потоков назначает его и его зависимости рабочим потокам.


module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                     {
                         loader: 'thread-loader',
                         options: {
                             workers: 3
                         }
                     },
                     'cache-loader',//使用cacheDirectory,可以缓存编译结果,避免多次重复编译;
                     'babel-loader',
                ]
        },            
    ]
 }

Многопроцессорность/многоэкземплярность: параллельное сжатие

Способ 1: используйте плагин parallel-uglify-plugin

plugins:[
 new ParallelUglifyPlugin({
    uglifyJS:{
        output:{
            beautify:false,
            comments:false
        },
        compress:{
            warning:false,
            drop_console:true,
            collapse_vars:true,
            reduce_vars:true
        }
    }
 })
]

Способ 2: uglifyjs-webpack-plugin включает параллельный параметр

plugins:[
 new UglifyJsPlugin({
    uglifyOptions:{
        warning:false
    },
    parallel:true
 })
]

Способ 3: terser-webpack-plugin открывает параллельный параметр

optimization:{
    minimizer:[
        new TerserPlugin({
            parallel:4
        })
    ]
}

Минимизировать цель сборки

Цель: построить как можно меньше модулей

Например, babel-loader не разрешает node_modules

rules: [
            {
                test: /\.js$/,
                exclude: 'node_modules',//忽略node_moudles,避免编译第三方库中已经被编译过的代码;
                use: [
                     'babel-loader',
                ]
            }
]

Уменьшить область поиска файлов

Оптимизация конфигурации resolve.extensions

Добросовестное использование псевдонимов

resolve: {
    alias: {
         'components': path.resolve(__dirname, './src/components'),
         'util': path.resolve(__dirname, './src/util'),
    },
    extensions: ['.js']
}

noParse

В веб-пакете загрузчик, который нам нужно использовать, настраивается в разделе module.rules, а модуль в конфигурации веб-пакета используется для управления тем, как обрабатывать различные типы модулей в проекте.

В дополнение к полю module.rules, используемому для настройки загрузчика, есть также поле module.noParse, которое можно использовать для настройки файлов модулей, которые не нужно анализировать. Для некоторых больших сторонних библиотек классов, которым не нужно разрешать зависимости (т. е. нет зависимостей), это поле можно настроить для повышения общей скорости сборки.

Механизмы импорта, такие как import, require, define, нельзя использовать в файлах модулей, которые игнорируются с помощью noParse.

module.exports = {
  // ...
  module: {
    noParse: /jquery|lodash/, // 正则表达式

    // 或者使用 function
    noParse(content) {
      return /jquery|lodash/.test(content)
    },
  }
}

Сжатие изображения

Использование: настроить загрузчик изображений-webpack


{
        test: /\.(png|jpg|gif|jpeg)$/,
        use: [
            {
                loader: 'file-loader',
                options: {
                    name: '[name]_[hash:8].[ext]'
                }
            },
            {
                loader: 'image-webpack-loader',
                options: {
                  mozjpeg: {
                    progressive: true,
                    quality: 65
                  },
                  optipng: {
                    enabled: false,
                  },
                  pngquant: {
                    quality: '65-90',
                    speed: 4
                  },
                  gifsicle: {
                    interlaced: false,
                  },
                  webp: {
                    quality: 75
                  }
        }
}

Как удалить бесполезный CSS?

PurifyCSS: просмотрите код, чтобы определить классы CSS, которые были использованы. Это необходимо использовать в сочетании с плагином mini-css-extract-plugin.

То есть сначала извлеките его как файл css, а затем используйте PurifyCSS.

npm install purgecss-webpack-plugin

const PurgecssPlugin = require('purgecss-webpack-plugin');
plugins:[
    new MiniCssExtractPlugin({
            filename: '[name]_[contenthash:8].css'
    }),
    new PurgecssPlugin({
        paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`,  { nodir: true }),
    })
]

Оптимизация объема сборки

Мы можем использовать динамический Polyfill -> Polyfill Service

Принцип: идентифицируйте пользовательский агент и доставляйте различные полифиллы.

Как использовать динамический сервис Polyfill

Официальный сервис, предоставляемый polyfill.io: (введен в index.html)

<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>

Сводка по оптимизации объема

Scope Hoisting

Tree-shaking

Разделение государственных ресурсов

Сжатие изображения

Динамический полифилл

Принципы

разработка загрузчика

//raw-loader.js
module.exports = function(source) {
    const json = JSON.stringify(source)
        .replace('foo', '')
        .replace(/\u2028/g, '\\u2028')
        .replace(/\u2029/g, '\\u2029');
    return `export default ${json}`;
}

Наш загрузчик заменяет символы foo в указанном файле на пустые Используйте loader-runner, чтобы проверить, правильно ли работает загрузчик:

npm install loader-runner -D

Использование loader-runner можно посмотреть на github.

const { runLoaders } = require('loader-runner');
const fs = require('fs');
const path = require('path');

runLoaders({
    resource: path.join(__dirname, './src/demo.txt'),//资源路径
    loaders: [
        {
            loader: path.join(__dirname, './src/raw-loader.js'),//指定loader路径
        }
    ],
    context: {
        minimize:true
    },
    readResource: fs.readFile.bind(fs)
}, (err, result) => {
    err ? console.log(err) : console.log(result);
});

получение параметров загрузчика

npm install loader-utils -D

Получено с помощью метода getOptions программы loader-utils.

const loaderUtils = require("loader-utils");
module.exports = function(content) {
    const { name } = loaderUtils.getOptions(this);
};

обработка исключений загрузчика

Загрузчик бросает напрямую через throw, а ошибку передает через this.callback

this.callback(
    err: Error | null, content: string | Buffer, sourceMap?: SourceMap, meta?: any
);

Асинхронная обработка загрузчиков

Вернуть асинхронную функцию через this.async

Первый параметр — Ошибка, второй параметр — результат обработки

module.exports = function(input) {
    const callback = this.async();
    this.cacheable(false)
    // No callback -> return synchronous results
    // if (callback) { ... }
    callback(null, input + input);
};

Первый параметр this.async() также является объектом ошибки, а второй параметр — данными.

использовать кеш в загрузчике

Кэширование загрузчика включено по умолчанию в веб-пакете

Кэш можно отключить с помощью this.cacheable(false)

Условие кеша: результат загрузчика имеет определенный вывод при том же вводе

Зависимые загрузчики не могут использовать кеш

Как загрузчик выполняет вывод файла?

Запись файла через this.emitFile

const loaderUtils = require("loader-utils");
module.exports = function(content) {
    const url = loaderUtils.interpolateName(this, "[hash].[ext]", {
    content});
    this.emitFile(url, content);
    const path = `__webpack_public_path__ + ${JSON.stringify(url)};`;
    return `export default ${path}`;
};

Метод interpolateName используется для замены плейсхолдеров, __webpack_public_path__ — это глобальная переменная webpack

разработка плагина

Плагины не имеют отдельной среды выполнения, такой как загрузчик, Работает только внутри вебпака

Плагин построения операционной среды

const path = require('path');
const MyPlugin = require('./plugins/myPlugin');//插件开发阶段的路径

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'main.js'
    },
    mode: 'production',
    plugins: [
        new MyPlugin({
            name: 'myname'
        })
    ]
}

Разработайте самый простой плагин

module.exports = class MyPlugin { //MyPlugin是插件名
    constructor(options){
        this.options = options
    }
    apply(compiler){ //必须是apply
        console.log('我的插件执行了');
        console.log('我的插件配置项',this.options)
    }
}

Обработка ошибок для плагинов

Фаза проверки параметров может быть запущена напрямую, выполнив

throw new Error(“ Error Message”)

Если он попал в логику хуков, его можно получить через предупреждения и ошибки объекта компиляции.

compilation.warnings.push("warning");
compilation.errors.push("error");

Запись файла плагина

На этапе генерации файла webpack будет вызывать хуки emit, поэтому мы можем прослушивать этап emit для работы.

Для записи файла необходимо использовать [webpack-sources]((Уууу, эта лошадь plus.com/package/Web…)

const { RawSource } = require("webpack-sources");
module.exports = class DemoPlugin {
    constructor(options) {
    this.options = options;
    }
    apply(compiler) {
        const { name } = this.options;
        compiler.plugin("emit", (compilation, cb) => {
            compilation.assets[name] = new RawSource("demo");//demo为文件内容,name为文件的名称
            cb();
        });
    }
};

Суммировать

Я считаю, что в этой статье должны были быть освещены наиболее часто используемые моменты веб-пакета и способы его оптимизации.Заинтересованные партнеры могут общаться внизу статьи.