Webpack5 создает реагирующие леса

Webpack

Webpack 5Прошло много времени с момента выпуска, и леса, используемые в компании, по-прежнему являются версией v4.Я слышал, что основное внимание в обновлении версии v5 уделяется оптимизации производительности конфигурации. Звучит вкусно, хочу использоватьWebpack 5Соберите стенд и посмотрите, как он себя чувствует в целом, потому что я в основном используюReactразвитие такReactНапример, для тех, кто не знаком с Webpack 5, можно прочитать эту статьюОценка начала работы с Webpack5, на самом деле содержимое конфигурации не сильно изменилось по сравнению с Webpack 4. Главное — это улучшение производительности, так что без лишних слов приступим.

Webpack 5Требование к версии для Node.js — не ниже 10.13.0 (LTS).

Инициализация проекта

mkdir webpack5-demo
cd webpack5-demo
npm init -y

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

Пакет, который нужно установить прямо сейчас, я вставлю его напрямую, вы можете скопировать его в файл package.json для выполненияnpm i 或 yarn

package.json

"devDependencies": {
    "@babel/core": "^7.13.8",
    "@babel/preset-env": "^7.13.8",
    "@babel/preset-react": "^7.12.13",
    "babel-loader": "^8.2.2",
    "chalk": "^4.1.0",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^5.1.0",
    "html-webpack-plugin": "^5.2.0",
    "ip": "^1.1.5",
    "style-loader": "^2.0.0",
    "progress-bar-webpack-plugin": "^2.1.0",
    "speed-measure-webpack-plugin": "^1.4.2",
    "webpack": "^5.24.2",
    "webpack-cli": "^4.5.0",
    "webpack-dev-server": "^3.11.2"
  },
  "dependencies": {
    "react": "^17.0.1",
    "react-dom": "^17.0.1"
  }

Ситуация с файловым каталогом в настоящее время

webpack5-demo
├─ node_modules
├─ package-lock.json
└─ package.json

настроить веб-пакет

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

# 这里我们新建一个 config 目录用来专门存放 webpack 配置文件
mkdir config
cd config
touch webpack.common.js # 开发环境 和 生产环境 公共配置 存放在这个文件里面
touch webpack.dev.js # 需要针对开发环境特殊处理的配置存放在这里
touch webpack.prod.js # 需要针对生产环境特殊处理的配置存放在这里

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

touch paths.js

paths.js

const path = require("path");
const fs = require("fs");
// 获取当前工作目录
const appDirectory = fs.realpathSync(process.cwd());
// 从相对路径中解析绝对路径
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
// 默认的模块扩展名
const moduleFileExtensions = ["js", "jsx", "ts", "tsx", "json"];
// 解析模块路径
const resolveModule = (resolveFn, filePath) => {
    // 查看文件存不存在
    const extension = moduleFileExtensions.find((extension) =>
        fs.existsSync(resolveFn(`${filePath}.${extension}`))
    );
    if (extension) {
        return resolveFn(`${filePath}.${extension}`);
    }
    return resolveFn(`${filePath}.js`); // 如果没有默认就是js
};

module.exports = {
    appBuild: resolveApp("build"), // 打包路径
    appPublic: resolveApp("public"), // 静态资源路径
    appHtml: resolveApp("public/index.html"), // html 模板路径
    appIndexJs: resolveModule(resolveApp, "src/index"), // 打包入口路径
    appNodeModules: resolveApp("node_modules"), // node_modules 路径
    appSrc: resolveApp("src"), // 主文件入口路径
    moduleFileExtensions, // 模块扩展名
};

Ситуация с файловым каталогом в настоящее время

demo
├─ config
├─ paths.js
│  ├─ webpack.common.js
│  ├─ webpack.dev.js
│  └─ webpack.prod.js
├─ node_modules
├─ package-lock.json
└─ package.json

webpack.common.js

const paths = require("./paths");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = function (options) {
    return {
        mode: options.mode,
        entry: paths.appSrc,
        output: {
            path: paths.appBuild,
            publicPath: "/",
        },
        cache: {
            // 使用持久化缓存
            type: "filesystem", //memory:使用内容缓存 filesystem:使用文件缓存
        },
        devtool: false,
        module: {
            rules: [
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    use: [
                        {
                            loader: "babel-loader",
                            options: {
                                presets: [
                                    "@babel/preset-env",
                                    "@babel/preset-react",
                                ],
                            },
                        },
                    ],
                },
            ],
        },
        devServer: {},
        plugins: [
            new HtmlWebpackPlugin({
                template: "./public/index.html",
            }),
            ...options.plugins,
        ],
        stats: options.stats, // 打包日志发生错误和新的编译时输出
    };
};

запись - это запись, путь поиска по умолчанию - это то, что мы написали, здесь нам нужно предоставить файл записи

# 回到webpack5-demo 根目录
# 创建一个src 目录开发文件

mkdir src
touch index.js # 创建一个入口文件
import React from "react";
import ReactDOM from "react-dom";

const App = () => {
    return <div> App入口 </div>;
};

ReactDOM.render(<App />, document.querySelector("#root"));

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

# 回到webpack5-demo 根目录
# 创建一个public 目录专门存放 静态资源

mkdir public
touch index.html # 创建一个html 模板

шаблон index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Webpack-React-Cli</title>
    </head>
    <body>
        <div id="root"></div>
    </body>
</html>

Конфигурация разработки webpack.dev.js

module.exports = require("./webpack.common")({
    mode: "development",
    plugins: [],
    stats: "errors-only", //只在发生错误或有新的编译时输出
});

рабочая конфигурация webpack.prod.js

const { CleanWebpackPlugin } = require("clean-webpack-plugin"); //打包前清空build目录文件
const ProgressBarPlugin = require("progress-bar-webpack-plugin"); // 打包进度条美化
const chalk = require("chalk");

module.exports = require("./webpack.common")({
    mode: "production",
    devtool: "source-map",
    plugins: [
        new CleanWebpackPlugin(),
        new ProgressBarPlugin({
            format:
                `${chalk.green.bold("build[:bar]")} ` +
                chalk.green.bold(":percent") +
                " (:elapsed seconds)",
            clear: false,
            width: 60,
        }),
    ],
    stats: "normal", //标准输出
});

Ситуация с файловым каталогом в настоящее время

webpack5-demo
├─ config
│  ├─ paths.js
│  ├─ webpack.common.js
│  ├─ webpack.dev.js
│  └─ webpack.prod.js
├─ node_modules
├─ public
│  └─ index.html
├─ src
│  └─ index.js
├─ package-lock.json
└─ package.json

запустить службу

Теперь нашWebpackБазовая конфигурация в порядке, она вот-вот начнется

Настройте скрипт pageage.json

{
  "name": "webpack5-demo",
  "version": "1.0.0",
  "description": "基于webpack5 的React架手架",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config config/webpack.prod.js",
    "start": "webpack serve --config config/webpack.dev.js", // v5
    "dev": "webpack-dev-server --config config/webpack.dev.js" //v4
  },
  "keywords": [],
  "author": "Jason",
  "license": "ISC",
  ...
}

Небольшое изменение здесь:

существуетWebpack 4пройти внутрьwebpack-dev-serverоказание услуг существуетWebpack 5Внутри запустите службу черезwebpack serve

Сейчас он был успешно запущен, но мне не очень нравится этот журнал запуска, мне нужно его преобразовать (чтобы реализовать собственное содержимое компиляции вывода), и мне нужно перенастроить сервер разработки (webpack-dev-server)

Настроить сервер разработки

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

# 回到webpack5-demo 根目录
# 创建一个server目录

mkdir server
touch index.js # 服务入口
touch appConfig.js # 基础服务配置 自定义服务端口,ip, 代理地址
touch logger.js # 控制台输出的日志

appConfig.js

module.exports = {
    deployUrl: "127.0.0.0:8080", // 本地代码推推送到指定服务器
    proxyUrlMap: {
        "/api": "localtion:3000", // 代理的接口
        "/api2": "localtion:4000", // 代理的接口
    },
    port: 9000, //端口号,
    host: "localhost", //主机号
};

logger.js

const ip = require("ip");

const divider = chalk.gray("\n-----------------------------------");

const logger = {
    error: (err) => {
        console.error(chalk.red(err));
    },
    appStarted: (port, host, tunnelStarted) => {
        console.log(`Server started ! ${chalk.green("✓")}`);

        if (tunnelStarted) {
            console.log(`Tunnel initialised ${chalk.green("✓")}`);
        }
        console.log(`
${chalk.bold("Access URLs:")}${divider}
Localhost: ${chalk.magenta(`http://${host}:${port}`)}
LAN: ${
            chalk.magenta(`http://${ip.address()}:${port}`) +
            (tunnelStarted
                ? `\n    Proxy: ${chalk.magenta(tunnelStarted)}`
                : "")
        }${divider}
${chalk.blue(`Press ${chalk.italic("CTRL-C")} to stop`)}
    `);
    },
};

module.exports = logger;

index.js

const Webpack = require("webpack");
const WebpackDevServer = require("webpack-dev-server");
// webpack开发 配置文件
const webpackConfig = require("../config/webpack.dev");
// 自定义日志输出
const logger = require("./logger");
// 服务配置
const appConfig = require("./appConfig");

const { port, host } = appConfig; // 监听的端口号
//编译器
const compiler = Webpack(webpackConfig);
//  devServer 参数
const devServerOptions = Object.assign({}, webpackConfig.devServer, {
    // open: true, // 自动打开浏览器
    compress: true, // gzip 压缩
    stats: "minimal",
});
const server = new WebpackDevServer(compiler, devServerOptions);

server.listen(port, host, async (err) => {
    if (err) {
        return logger.error(err.message);
    }
    logger.appStarted(port, "localhost");
});

существуетpackage.jsonНастроить команды запуска

"scripts": {
    "build": "webpack --config config/webpack.prod.js",
    "start": "node server"
  },

Ситуация с файловым каталогом в настоящее время

webpack5-demo
├─ config
│  ├─ paths.js
│  ├─ webpack.common.js
│  ├─ webpack.dev.js
│  └─ webpack.prod.js
├─ node_modules
├─ public
│  └─ index.html
├─ server
│  ├─ appConfig.js
│  ├─ index.js
│  └─ logger.js
├─ src
│  └─ index.js
├─ package-lock.json
└─ package.json

решить

resolveВы можете настроить следующее, а остальные можно использовать по умолчанию.

  • модули: используйте первую реакцию третьего модуля, чтобы перейти к node_modules в корневом каталоге, чтобы найти
  • расширения: вimportКогда расширение файла не добавлено, оно будет пройдено по очередиextensionsдобавить расширение для соответствия
  • псевдоним: создайте псевдоним, вimportилиrequireпсевдонимы для облегчения импорта модулей
 resolve: {
    modules: [paths.appNodeModules],
    extensions: ['.js', '.jsx', '.css'],
    alias: {
        moment$: 'moment/moment.js',
        '@src': paths.appSrc,
        '@public': paths.appPublic,
    },
},

Базовая конфигурация загрузчика

css и сасс

Установить

npm i -D style-loader css-loader
npm i -D node-sass sass-loader postcss postcss-loader postcss-preset-env

Конфигурация загрузчика css и sass очень проста Учитывая проблемы совместимости, postcss-loader также должен добавить заголовок логотипа производителя браузера.

Используемые плагины размещаются в корневом каталоге отдельноpostcss.config.jsв файле конфигурации

postcss.config.js

module.exports = {
    plugins: {
        "postcss-preset-env": {},
    },
};

Ресурсы модуля

Webpack 4Он используется при обработке изображений или текстовых файлов.file-loaderилиurl-loaderтеперь дляWebpack 5может быть использованAsset Modules(ресурсный модуль), нет необходимости настраивать загрузчик

  • актив/источник экспортировать исходный код актива (эквивалентно необработанному загрузчику)
  • Актив / ресурс отправляет один файл и экспортирует URL-адрес (эквивалентный файловой загрузчике)
  • актив/встроенный экспортирует URI данных актива (эквивалентно url-loader)
  • актив автоматически выбирает между экспортом URI данных и отправкой отдельного файла, что ранее достигалось с помощью url-loader и настройки ограничений размера актива.
// 设置 常量
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const imageInlineSizeLimit = 4 * 1024;

module.exports = function (options) {

    return {

        ...

        module: {
            rules: [
                {
                    oneOf: [

                        ...

                        {
                            test: cssRegex,
                            exclude: cssModuleRegex,
                            use: ['style-loader', {
                                loader: 'css-loader',
                                options: {
                                    importLoaders: 1 // 0 => 无 loader(默认); 1 => postcss-loader; 2 => postcss-loader, sass-loader
                                }
                            },'postcss-loader'],
                        },
                        {
                            test: sassRegex,
                            exclude: sassModuleRegex,
                            use: ['style-loader', {
                                loader: 'css-loader',
                                options: {
                                    importLoaders: 1 // 查询参数 importLoaders,用于配置「css-loader 作用于 @import 的资源之前」有多少个 loader
                                }
                            }, 'postcss-loader', 'sass-loader'],
                        },
                        {
                            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
                            type: 'asset',
                            parser: {
                                dataUrlCondition: {
                                    maxSize: imageInlineSizeLimit // 4kb
                                }
                            }
                        },
                        {
                            test: /\.(eot|svg|ttf|woff|woff2?)$/,
                            type: 'asset/resource'
                        },
                    ]
                }

            ]
        },
    }

Протестируйте css и изображения

# 在src 目录下新一个 style.scss 文件

touch style.scss

Сначала напишите что-нибудь

* {
    margin: 0;
    padding: 0;
}

div {
    color: red;
}

Импорт стилей и изображений

import React from "react";
import ReactDOM from "react-dom";

import npm from "@public/assets/imgs/npm.png";

import "./style.scss";

const App = () => {
    return (
        <div>
            App入口
            <img src={npm} />
        </div>
    );
};

ReactDOM.render(<App />, document.querySelector("#root"));

Проверьте упаковку

Второй раз быстрый из-за установленного постоянного кеша

Ситуация с файловым каталогом в настоящее время

webpack5-demo
├─ build
├─ config
│  ├─ paths.js
│  ├─ webpack.common.js
│  ├─ webpack.dev.js
│  └─ webpack.prod.js
├─ node_modules
├─ public
│  ├─ assets
│  │  └─ imgs
│  │     └─ npm.png
│  └─ index.html
├─ server
│  ├─ appConfig.js
│  ├─ index.js
│  └─ logger.js
├─ src
│  ├─ index.js
│  └─ style.css
├─ package-lock.json
└─ package.json

резюме

Контента еще много и я планирую написать его в двух частях, первая часть пока относительно простая. Вторая статья более оптимизирована, оптимизирована под новые возможности вебпака 5, настройку спецификации кода eslint, использование некоторых плагинов, а так же хочу попробовать написать статьи, посмотрите как первый черновик, больше 50 лайков пишите Один (Хахаха О(∩_∩)О)