webpack5 + webpack-chain создает большую серию приложений 2 (с vscode и более красивой конфигурацией)

Webpack

Вслед за предыдущей статьейШаг за шагом, чтобы создать большой проект с помощью веб-пакета с нуляВторой после этого. В этой статье используетсяwebpack5Проект был рефакторинг и используется во всемwebpack-chainнастроитьwebpack, каждая функция также является отдельным файлом и может использоваться независимо. Так что конфигурацию этого проекта можно использовать в любом проекте. Этот проект может быть использован в реальном бою или в качествеwebpackРуководство для изучения. Моя цель в разработке этого проекта заключается в том, что вы можете извлечь выгоду из этого, независимо от того, являются ли вы новичком или опытным боссом. Этот проект для желания учитьсяwebpackСтуденты предоставили очень хорошую платформу для реального боя, каждый插件КаждыйloaderТам будет подробное объяснение и история использования.

Чтобы сэкономить время каждого и повысить эффективность обучения, я хочу поставить всеwebpackСвязанные серии интегрированы здесь.Каждая оптимизация здесь основана на моих неоднократных размышлениях и практике, и я также буду использовать некоторые отличные библиотеки с открытым исходным кодом, чтобы улучшить его.Этот проект будет поддерживаться в течение длительного времени, и все искренне приглашаются к участию в этом проекте, среди которых станьте со-строителями этого проекта вместе!

адрес проекта:GitHub.com/falling snow-Вик Т…

Поскольку в этой статье используетсяwebpack5Рефакторинг, поэтому сначала поставлю 14-й урок

Содержание этой статьи

TODO планирую сделать дальшеGitHub.com/falling snow-Вик Т…

Урок 14: Обновление webpack5

Эта глава в основном обновляет проект до webpack5, первый шаг в яме. Поделитесь опытом хождения по яме со всеми.

Webpack5 больше похож на черный ящик: многие задачи, которые раньше приходилось решать с помощью плагинов, теперь интегрированы и используются «из коробки». Основная цель webpack5 — оптимизировать скорость компиляции, добавить больше конфигураций по умолчанию (встроено больше конфигураций), улучшить генерацию кода и проложить путь для дальнейшего развития webpack в будущем.

Краткое содержание этой главы

Что делает вебпак5?

  • Используйте долгосрочный кэш для повышения скорости компиляции
  • Улучшите долгосрочное кэширование с помощью лучших алгоритмов и значений по умолчанию.
  • Увеличенный размер пакета с улучшенным встряхиванием дерева и генерацией кода
  • Рефакторинг внутренних компонентов для реализации функциональности версии 4 без внесения каких-либо критических изменений.
  • Подготовьтесь к будущим функциям, внеся критические изменения, чтобы мы могли использовать v5 как можно дольше.

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

В этом учебном пособии можно выполнить обновление/откат одним щелчком мыши с помощью команд создания шаблонов.

webpack-box upgrade 5/4

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

package.json

{
  "html-webpack-plugin": "^4.0.0-beta.11",
  "webpack": "^5.0.0-beta.9"
}

Сравнение скорости компиляции

webpack-box build index

Version: webpack 4.41.2

Используется следующееcache-loader

  • первый раз4216ms
  • второй раз2781ms
  • в третий раз2827ms
  • четвертый раз2797ms

Version: webpack 5.0.0-beta.9

использовал持久缓存

  • первый раз3567ms
  • второй раз2602ms
  • в третий раз2609ms
  • четвертый раз2582ms

можно увидетьwebpack5чем использование постоянного кешаwebpack4Компиляция с cache-loader быстрее100ms ~ 200ms, поэтому в будущем нет необходимости использовать cache-loader, webpack5 предоставляет лучшие алгоритмы и лучшие решения для кэширования

Изменения с webpack4 на webpack5

1. кеш-загрузчик больше не нужен

При использовании постоянного кеша вам больше не нужен загрузчик кеша. То же, что и в Babel cacheDirectory.

2. Проблема с html-webpack-плагином

некоторые ошибки и исправленияerror

  1. Cannot add property htmlWebpackPluginAlterChunks, object is not extensible

Установка версии 4.x исправляет это

npm i html-webpack-plugin@4.0.0-beta.11
  1. Cannot read property 'get' of undefined

未修复Первая компиляция вступает в силу, и после сохранения будет выдано сообщение об ошибке.webpack5Горячее обновление было переписано, в результате чегоhtml-webpack-pluginне совместимы,Причина может быть найдена

3. Динамически загружаемый файл, наконец, имеет имя, но уже не идентификатор, а конкатенацию пути проекта.

Может быть изменен с помощью optimization.chunkIds

Нажмите, чтобы увидеть документацию

module.exports = {
  //...
  optimization: {
    chunkIds: "named"
  }
};

// 链式修改
config.optimization.set("chunkIds", "natural");

4. Вложенное встряхивание дерева

Таким образом, в webpack4 a и b будут упакованы в бандл, а webpack5 также удалит вложенный бесполезный код, то есть b не будет упакован в бандл, потому что b не используется.

// inner.js
export const a = 1;
export const b = 2;

// module.js
import * as inner from "./inner";
export { inner };

// user.js
import * as module from "./module";
console.log(module.inner.a);

5. Внутреннее встряхивание модулей

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

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

Чтобы использовать его, вам нужноpackage.jsonСредняя конфигурация"sideEffects": false, и установитеoptimization.usedExportsправда

// package.json
{
  "sideEffects": false
}

// config/optimization.js
config.optimization.usedExports(true);

代码演示

import { something } from "./something";

function usingSomething() {
  return something;
}

export function test() {
  return usingSomething();
}

Если внешний модуль не использует тестовый метод, то usingSomething что-то тоже будет удалено в бандле

6. Улучшить генерацию кода

Сообщите веб-пакету максимальную версию EcmaScript, чтобы веб-пакет мог генерировать код.

webpack4Поддерживает только ES5,webpack5Поддержка ES5 и ES6

ecmaVersionДиапазон значений 5 ~ 11 или 2009 ~ 2020, для webpack5 по умолчанию установлено минимальное значение 5.

config.output.set("ecmaVersion", 6);

7. SplitChunks and Module Sizes

webpack5 может установить размер упаковки splitChunks в соответствии с различными типами файлов.По умолчанию разделяется и упаковывается только javascript

config.optimization.splitChunks({
  minSize: {
    javascript: 30000,
    style: 50000
  }
});

8. Постоянный кеш

webpack5 предоставляет два метода кэширования: один — постоянный кеш для кэширования файлов в файловой системе, другой — кэширование в памяти.

// type {filesystem | memory}
config.cache({
  type: "filesystem"
});

По умолчанию он будет кэшироваться вnode_modules/.cache/webpack, вы можете пройтиcacheDirectoryВозможность изменить каталог кеша

9. Другие

Конкретное содержимое настройки webpack5 нажмите здесь

Тема 10: Добавьте eslint и включите автоисправление

Краткое содержание этой главы

Извлечено из слоя cwd, одна функция

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

  • PluginAPI обрабатывает логику плагина скаффолдинга
  • CommandAPI обрабатывает логику командной строки скаффолдинга
  • cwd извлекает командный слой
└── api
   │── CommandAPI.js
   └── PluginAPI.js
└── cwd
   │── build:ssr.js
   │── build.js
   │── dev.js
   │── dll.js
   │── lint.js
   └── ssr:server.js

Настроить eslint-загрузчик

Настройте eslint-loader, правила eslint будут обнаружены при разработке webpack-box, и если будет ошибка, то она будет отображаться в консоли

config.module
  .rule("eslint")
  .pre()
  .exclude.add(/node_modules/)
  .end()
  .test(/\.(vue|(j)sx?)$/)
  .use("eslint-loader")
  .loader(require.resolve("eslint-loader"))
  .options({
    extensions,
    cache: true,
    cacheIdentifier,
    emitWarning: allWarnings,
    emitError: allErrors,
    eslintPath: path.dirname(
      resolveModule("eslint/package.json", cwd) ||
        resolveModule("eslint/package.json", __dirname)
    ),
    formatter: loadModule("eslint/lib/formatters/codeframe", cwd, true)
  });

функция авторемонта eslint

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

Часть кода написана здесь, подробнее можно посмотреть в проекте

packages/eslint/lint.js

const { CLIEngine } = loadModule("eslint", cwd, true) || require("eslint");
const config = Object.assign({
  extensions,
  fix: true,
  cwd
});
const engine = new CLIEngine(config);
const defaultFilesToLint = ["src", "tests", "*.js", ".*.js"].filter(pattern =>
  globby
    .sync(pattern, { cwd, absolute: true })
    .some(p => !engine.isPathIgnored(p))
);

const files = args._ && args._.length ? args._ : defaultFilesToLint;
const report = engine.executeOnFiles(files);
if (config.fix) {
  CLIEngine.outputFixes(report);
}

Добавить команду строительных лесов

Надеемся исправить в виде командной строки,webpack-box lint eslint, так и должно бытьcwdКомандная строка добавления слоя

cwd/lint.js

module.exports = function(injectCommand, api) {
  injectCommand(function({ program, cleanArgs, boxConfig }) {
    program
      .command("lint [type]")
      .description("修复lint")
      .action(async (name, cmd) => {
        const options = cleanArgs(cmd);
        const args = Object.assign(options, { name }, boxConfig);
        require("../build/lint")(args, api);
      });
  });
};

чтобы мы могли использоватьwebpack-box lint eslintИсправь большинство ошибок, попробуй~

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

Конечно выполняемwebpack-box lint eslintМы можем исправить некоторые ошибки во время выполнения команды, но когда мы пишем код, мы надеемся, что компилятор поможет нам его модифицировать автоматически, вместо того, чтобы ждать написания кода перед проверкой, что принесет нам второстепенные неприятности, и даже будут проблемы, которые не могут быть отремонтированы.вопрос.

Поэтому мы используемvscodeизeslintПлагины, которые помогут нам достичь этого

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

После того, как вы установили плагин eslint, вам необходимо установить его в настройках"eslint.autoFixOnSave": true, так что это может быть исправлено автоматически при сохраненииeslintОшибка

Конечно, вы, вероятно, использовали его только в этом проекте.eslint, тогда как в других проектах не требуется исправлять при сохранении

можно добавить в корневой каталог

└── .vscode
   └── settings.json

Поместите копию моей собственной конфигурации для справки

{
  /*
   * @description 编译器配置
   * @param tabSize 默认tab为两个空格
   * @param formatOnSave 保存时自动修复
   */
  "editor.tabSize": 2,
  "editor.formatOnSave": true,
  /*
   * @description eslint 配置
   * @param alwaysShowStatus 配置
   * @param autoFixOnSave 保存时自动修复
   * @param validate 在vue中添加错误提示
   */
  "eslint.alwaysShowStatus": true,
  "eslint.autoFixOnSave": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    {
      "language": "vue",
      "autoFix": true
    }
  ],
  /*
   * @description tslint 配置
   * @param autoFixOnSave 保存时自动修复
   * @param alwaysShowRuleFailuresAsWarnings 所有特征都是用 Warnings
   */
  "tslint.autoFixOnSave": true,
  "tslint.alwaysShowRuleFailuresAsWarnings": true,
  /*
   * @description stylelint 配置
   * @param autoFixOnSave 保存时自动修复
   */
  "stylelint.autoFixOnSave": true,
  /*
   * @description vetur 配置
   */
  "vetur.format.defaultFormatter.html": "prettier",
  "vetur.format.defaultFormatterOptions": {
    "prettier": {
      "semi": false,
      "singleQuote": true
    }
  },
  /*
   * @description 配置编辑器设置以覆盖某种语言
   */
  "[typescript]": {
    // "editor.defaultFormatter": "esbenp.prettier-vscode"
    "editor.defaultFormatter": "eg2.tslint"
  },
  "[html]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[jsonc]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[vue]": {
    "editor.defaultFormatter": "eg2.tslint"
  },
  "[javascript]": {
    "editor.defaultFormatter": "dbaeumer.vscode-eslint"
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

Проверка фиксации кода (поэтапная проверка)

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

Добавьте lint-staged в package.json, lint будет выполняться первым при отправке кода, а отправка будет успешной после передачи lint

package.json

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{js,jsx}": ["webpack-box lint eslint", "git add"]
  }
}

Тема 11. Добавление stylelint и включение автоматического исправления

Краткое содержание этой главы

Настройка стандартного плагина stylelint

Используйте стандартный плагин stylelint-config

.stylelintrc.js

module.exports = {
  root: true,
  extends: "stylelint-config-standard"
};

Настройте плагин stylelint

module.exports = ({
  config,
  options: { stylelint: { lintOnSave = false, extensions } = {} },
  api
}) => {
  const StyleLintPlugin = require("stylelint-webpack-plugin");
  const CodeframeFormatter = require("stylelint-codeframe-formatter");
  const stylelint = [];
  return () => {
    config.plugin("stylelint").use(StyleLintPlugin, [
      Object.assign(
        {
          failOnError: lintOnSave === "error",
          files: ["src/**/*.{vue,htm,html,css,sss,less,scss}"],
          formatter: CodeframeFormatter
        },
        stylelint
      )
    ]);
  };
};

авто ремонт

module.exports = async function lint({ api, args = {}, pluginOptions = {} }) {
  const cwd = api.resolve(".");

  const files =
    args._ && args._.length
      ? args._
      : [cwd + "/src/**/*.{vue,htm,html,css,sss,less,scss}"];
  if (args["no-fix"]) {
    args.fix = false;
    delete args["no-fix"];
  }

  const { formatter } = args;
  if (
    formatter &&
    typeof formatter === "string" &&
    !["json", "string", "verbose"].includes(formatter)
  ) {
    try {
      args.formatter = require(formatter);
    } catch (e) {
      delete args.formatter;
      if (typeof pluginOptions.formatter !== "function") {
        console.log(
          format(
            chalk`{bgYellow.black  WARN }`,
            chalk`${e.toString()}\n{yellow Invalid formatter}`
          )
        );
      }
    }
  }

  const options = Object.assign(
    {},
    {
      configBasedir: cwd,
      fix: true,
      files,
      formatter: CodeframeFormatter
    },
    pluginOptions,
    normalizeConfig(args)
  );

  try {
    const { errored, results, output: formattedOutput } = await stylelint.lint(
      options
    );
    if (!errored) {
      if (!args.silent) {
        const hasWarnings = results.some(result => {
          if (result.ignored) {
            return null;
          }
          return result.warnings.some(
            warning => warning.severity === "warning"
          );
        });
        if (hasWarnings) {
          console.log(formattedOutput);
        } else {
          console.log(
            format(
              chalk`{bgGreen.black  DONE }`,
              `stylelint 没有发现错误!${
                options.fix ? chalk` {blue (已经自动修复)}` : ""
              }`
            )
          );
        }
      }
    } else {
      console.log(formattedOutput);
      process.exit(1);
    }
  } catch (err) {
    console.log(
      format(chalk`{bgRed.black  ERROR }`, err.stack.slice(" Error:".length))
    );
    process.exit(1);
  }
};

проверка фиксации кода

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{vue,htm,html,css,sss,less,scss}": [
      "webpack-box lint stylelint",
      "git add"
    ],
    "*.{js,jsx}": ["webpack-box lint eslint", "git add"]
  }
}

Тема 12. Добавление tslint и включение автоматического исправления

Краткое содержание этой главы

Настроить плагин

config/tslintPlugin.js

module.exports = ({
  config,
  options: { tslint: { lintOnSave = false, useThreads = false } = {} },
  api
}) => {
  const fs = require("fs");
  return () => {
    config.plugin("fork-ts-checker").tap(([options]) => {
      options.tslint =
        lintOnSave !== false && fs.existsSync(api.resolve("tslint.json"));
      options.formatter = "codeframe";
      options.checkSyntacticErrors = useThreads;
      return [options];
    });
  };
};

добавить правило

tslint.json

{
  "defaultSeverity": "warning",
  "extends": ["tslint:recommended"],
  "linterOptions": {
    "exclude": ["node_modules/**"]
  },
  "rules": {
    "max-classes-per-file": [true, 5, "exclude-class-expressions"],
    "quotemark": [true, "single"],
    "semicolon": [true, "never"],
    "indent": [true, "spaces", 2],
    "ordered-imports": false,
    "object-literal-sort-keys": false,
    "no-consecutive-blank-lines": false,
    "disable-next-line": false,
    "only-arrow-functions": false,
    "radix": false,
    "class-name": false,
    "eofline": false,
    "no-unused-expression": false,
    "no-console": false,
    "trailing-comma": false,
    "interface-name": false
  }
}

функция авторемонта

const { done } = require("@vue/cli-shared-utils");

module.exports = function lint({ args = {}, api, silent }) {
  const options = {
    fix: args.fix !== false,
    formatter: args.format || "codeFrame",
    formattersDirectory: args["formatters-dir"],
    rulesDirectory: args["rules-dir"]
  };

  const program = tslint.Linter.createProgram(api.resolve("tsconfig.json"));
  const linter = new tslint.Linter(options, program);

  const updateProgram = linter.updateProgram;
  linter.updateProgram = function(...args) {
    updateProgram.call(this, ...args);
    patchProgram(this.program);
  };

  const tslintConfigPath = tslint.Configuration.CONFIG_FILENAMES.map(filename =>
    api.resolve(filename)
  ).find(file => fs.existsSync(file));

  const config = tslint.Configuration.findConfiguration(tslintConfigPath)
    .results;

  const lint = file => {
    const filePath = api.resolve(file);
    const isVue = isVueFile(file);
    patchWriteFile();
    linter.lint(filePath, "", isVue ? vueConfig : config);
    restoreWriteFile();
  };

  const files =
    args._ && args._.length
      ? args._
      : [cwd + "/src/**/*.ts", cwd + "/src/**/*.vue", cwd + "/src/**/*.tsx"];

  return globby(files, { cwd }).then(files => {
    files.forEach(lint);
    if (silent) return;
    const result = linter.getResult();
    if (result.output.trim()) {
      process.stdout.write(result.output);
    } else if (result.fixes.length) {
      const f = new tslint.Formatters.ProseFormatter();
      process.stdout.write(f.format(result.failures, result.fixes));
    } else if (!result.failures.length) {
      done("tslint 没有发现错误.\n");
    }

    if (result.failures.length && !args.force) {
      process.exitCode = 1;
    }
  });
};

Отправить на проверку

{
  "lint-staged": {
    "*.{vue,htm,html,css,sss,less,scss}": [
      "webpack-box lint stylelint",
      "git add"
    ],
    "*.{ts,tsx}": ["webpack-box lint tslint", "git add"],
    "*.{js,jsx}": ["webpack-box lint eslint", "git add"]
  }
}

Тема 13: Настройка псевдонимов

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

Краткое содержание этой главы

Использовать псевдонимы в проектах

src/main.js

import { cube } from "./treeShaking";
import { cube } from "@/treeShaking";
import { cube } from "@src/treeShaking";

Настроить псевдонимы

alias: {
  '@': resolve('src'),
  '@src': resolve('src')
}

реализация веб-пакета

module.exports = ({ config, options, resolve }) => {
  const fs = require("fs");
  const conf = options.alias;
  return () => {
    // 生成默认别名
    const dirs = fs.readdirSync(resolve("src"));
    let aliasConfig = config.resolve.extensions
      .merge([".mjs", ".js", ".jsx", ".vue", ".ts", ".json", ".wasm"])
      .end().alias;
    dirs.forEach(v => {
      const stat = fs.statSync(resolve(`src/${v}`));
      if (stat.isDirectory()) {
        aliasConfig = aliasConfig.set(`@${v}`, resolve(`src/${v}`));
      }
    });

    // 用户配置别名
    if (conf.alias) {
      const keys = Object.keys(conf.alias);
      keys.forEach(key => {
        aliasConfig = aliasConfig.set(key, conf.alias[key]);
      });
    }

    // 自定义设置别名
    aliasConfig.set("@", resolve("src")).set("@src", resolve("src"));
  };
};

Конфигурация перехода компилятора

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

tsconfig.json/jsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "rootDir": ".",
    "paths": {
      "@src/*": [
        "src/*"
      ],
      "@/*": [
        "src/*"
      ],
    }
  }
}

Урок 15: Определение общих переменных

Иногда нам нужен мост связи между скаффолдингом и бизнес-кодом.

как мыnpm run buildКогда мы запускаем производственную среду, я хочуmain.jsВыполните специальную логику в производственной среде. ноmain.jsВыполнение находится на стороне браузера, в то время какnpm run buildпри беге наnodeДва конца не могут общаться. Так что же нам делать?

webpackпредоставляет нам плагинEnvironmentPlugin, этот плагин позволяет намnodeсторонние переменные, значение компилируется в код во время компиляции, например

мы вmain.jsнаписал абзацnode, но это не распознается в браузере, потому что браузер неprocessObject, этот код без происшествий сообщит об ошибке

main.js

if (process.env.NODE_ENV === "production") {
  console.log("Welcome to production");
}

мы настраиваемwebpack.EnvironmentPluginплагин

const webpack = require("webpack");

module.exports = ({ config, resolve, options }) => {
  return () => {
    const resolveClientEnv = require("../util/resolveClientEnv");
    config
      .plugin("process-env")
      .use(webpack.EnvironmentPlugin, [resolveClientEnv(options)]);
  };
};

util/resolveClientEnv.js

module.exports = function resolveClientEnv(options, raw) {
  const env = {};
  if (process.env) {
    Object.keys(process.env).forEach(key => {
      if (key === "NODE_ENV") {
        env[key] = process.env[key];
      }
    });
  }
  if (options.env) {
    Object.assign(env, options.env);
  }
  return env;
};

мы выступаемnpm run build,посмотриdist/index.bundle.jsво что будет компилироваться

// "production" === "production"
if (true) {
  console.log("Welcome to production");
}

webpackБудуprocess.env.NODE_ENVЗначение скомпилировано вbundle, то что мы можемwebТерминал запущен и скомпилирован в рабочей среде.

Урок 16: Строгая чувствительность пути

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

└──── src
   │── Index.js
   └── main.js

Первая буква Index.js в указанном выше пути написана заглавной, но я ссылаюсь на нее строчными буквами в main.js.

main.js

import Index from "./index.js";

Это не сообщит об ошибке локально, но когда вы выйдете в Интернет с помощью Jenkins, вы сообщите об ошибке, что модуль ./index.js не может быть найден.

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

Мы используемcase-sensitive-paths-webpack-pluginплагин для его реализации

module.exports = ({ config, webpackVersion, resolve, options }) => {
  return () => {
    // webpack 5 不兼容
    if (parseInt(webpackVersion) >= 5) return;
    config
      .plugin("case-sensitive-paths")
      .use(require("case-sensitive-paths-webpack-plugin"));
  };
};

Урок 17: Загрузка ресурсов изображения, svg, мультимедиа, шрифты

Эта глава перейдет непосредственно к коду, который является дополнением к предыдущей основной главе.

module.exports = ({ config, webpackVersion, resolve, options }) => {
  return () => {
    const getAssetPath = require("../util/getAssetPath");
    const inlineLimit = 4096;

    const genAssetSubPath = dir => {
      return getAssetPath(
        options,
        `${dir}/[name]${options.filenameHashing ? ".[hash:8]" : ""}.[ext]`
      );
    };

    const genUrlLoaderOptions = dir => {
      return {
        limit: inlineLimit,
        fallback: {
          loader: "file-loader",
          options: {
            name: genAssetSubPath(dir)
          }
        }
      };
    };

    config.module
      .rule("images")
      .test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
      .use("url-loader")
      .loader(require.resolve("url-loader"))
      .options(genUrlLoaderOptions("img"));

    config.module
      .rule("svg")
      .test(/\.(svg)(\?.*)?$/)
      .use("file-loader")
      .loader(require.resolve("file-loader"))
      .options({
        name: genAssetSubPath("img")
      });

    config.module
      .rule("media")
      .test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/)
      .use("url-loader")
      .loader(require.resolve("url-loader"))
      .options(genUrlLoaderOptions("media"));

    config.module
      .rule("fonts")
      .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/i)
      .use("url-loader")
      .loader(require.resolve("url-loader"))
      .options(genUrlLoaderOptions("fonts"));
  };
};

Урок 18: Настройка глобальных стилей

При написании css мы инкапсулируем часто используемые функции/переменные в файл global.less/scss, а затем импортируем его при использовании. Очевидно, что импорт вручную каждый раз становится очень хлопотным и подверженным ошибкам (особенно, когда в группу приходят новые люди), поэтому мы подумали, что было бы идеально, если бы мы автоматически вводили global в файл?

нам нужноstyle-resources-loaderчтобы помочь нам сделать это

Настроить загрузчик стилей-ресурсов

config/styleResourceLoader.js

module.exports = ({ config, options }) => {
  const resourcesOpt = options.resources;
  return () => {
    ["normal"].forEach(oneOf => {
      Object.keys(resourcesOpt).forEach(loader => {
        config.module
          .rule(loader)
          .oneOf(oneOf)
          .use("style-resources-loader")
          .loader("style-resources-loader")
          .options({
            patterns: resourcesOpt[loader].patterns
          });
      });
    });
  };
};

config/style.js

if (loader) {
  let resolvedLoader;
  try {
    resolvedLoader = require.resolve(loader);
  } catch (error) {
    resolvedLoader = loader;
  }
  rule
    .use(loader)
    .loader(resolvedLoader)
    // options 是对应 config 中的 css 参数,可以自行配置对应loader的参数
    .options(Object.assign({ sourceMap }, options));
}

Конфигурация проекта

box.config.js

{
  "css": {
    "sourceMap": true,  // 是否开启css source map
    "loaderOptions": { // 配置loader的options
      "css": {},
      "less": {
        "globalVars": { // less 设置全局变量
          "gray": "#ccc"
        }
      },
      "sass": {},
      "postcss": {},
      "stylus": {}
    },
    "isCssModule": false, // 是否对css进行模块化处理
    "needInlineMinification": false // 是否需要压缩css
  },
  "resources": {
    "less": {
      "patterns": [path.resolve(__dirname, "./src/global/*.less")]
    },
    "scss": {
      "patterns": [path.resolve(__dirname, "./src/global/*.scss")]
    }
  }
}

использовать

└──── src
   │── global
   │  │── index.less
   │  └── index.scss
   └── style
      └── index.less

установить глобальный стиль

global/index.less

.g-less-height () {
  height: 100%;
}

.g-less-test {
  width: 100%;
}

Используйте глобальные стили

style/index.less

.test {
  width: 300px;
  color: @gray;
  .g-less-height();
}

style/index.scss

.g-scss-test {
  width: 100%;
}

после компиляции

dist/css/index.css

.g-less-test {
  width: 100%;
}

.test {
  color: #ccc;
  width: 100%;
  height: 100%;
}

.g-scss-test {
  width: 100%;
}

Видимые глобальные стили упакованы вdist/css/index.cssВ, нам больше не нужно вручную импортировать каждый раз

конфигурация vscode

Поместите его в корневой каталог и включите функцию автоматического восстановления eslint/tslint/stylelint.

.vscode/setting.json

{
  /*
   * @description 编译器配置
   * @param tabSize 默认tab为两个空格
   * @param formatOnSave 保存时自动修复
   */
  "editor.tabSize": 2,
  "editor.formatOnSave": true,
  /*
   * @description eslint 配置
   * @param alwaysShowStatus 配置
   * @param autoFixOnSave 保存时自动修复
   * @param validate 在vue中添加错误提示
   */
  "eslint.alwaysShowStatus": true,
  "eslint.autoFixOnSave": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    {
      "language": "vue",
      "autoFix": true
    }
  ],
  /*
   * @description tslint 配置
   * @param autoFixOnSave 保存时自动修复
   * @param alwaysShowRuleFailuresAsWarnings 所有特征都是用 Warnings
   */
  "tslint.autoFixOnSave":  true,
  "tslint.alwaysShowRuleFailuresAsWarnings": true,
  /*
   * @description stylelint 配置
   * @param autoFixOnSave 保存时自动修复
   */
  "stylelint.autoFixOnSave":  true,
  /*
   * @description vetur 配置
   */
  "vetur.format.defaultFormatter.html": "prettier",
  "vetur.format.defaultFormatterOptions": {
    "prettier": {
      "semi": false,
      "singleQuote": true
    }
  },
  /*
   * @description 配置编辑器设置以覆盖某种语言
   */
  "[typescript]": {
    // "editor.defaultFormatter": "esbenp.prettier-vscode"
    "editor.defaultFormatter": "eg2.tslint"
  },
  "[html]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[jsonc]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[vue]": {
    "editor.defaultFormatter": "eg2.tslint"
  },
  "[javascript]": {
    "editor.defaultFormatter": "dbaeumer.vscode-eslint"
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

более красивая конфигурация

Сделайте что-нибудь с lint, форматированием в стиле кода

prettier.config.js

/**
 * pretiier 标准配置
 */
module.exports = {
  // 在ES5中有效的结尾逗号(对象,数组等)
  trailingComma: "es5",
  // 不使用缩进符,而使用空格
  useTabs: false,
  // tab 用两个空格代替
  tabWidth: 2,
  // 仅在语法可能出现错误的时候才会添加分号
  semi: false,
  // 使用单引号
  singleQuote: true,
  // 在Vue文件中缩进脚本和样式标签。
  vueIndentScriptAndStyle: true,
  // 一行最多 100 字符
  printWidth: 100,
  // 对象的 key 仅在必要时用引号
  quoteProps: "as-needed",
  // jsx 不使用单引号,而使用双引号
  jsxSingleQuote: false,
  // 大括号内的首尾需要空格
  bracketSpacing: true,
  // jsx 标签的反尖括号需要换行
  jsxBracketSameLine: false,
  // 箭头函数,只有一个参数的时候,也需要括号
  arrowParens: "always",
  // 每个文件格式化的范围是文件的全部内容
  rangeStart: 0,
  rangeEnd: Infinity,
  // 不需要写文件开头的 @prettier
  requirePragma: false,
  // 不需要自动在文件开头插入 @prettier
  insertPragma: false,
  // 使用默认的折行标准
  proseWrap: "preserve",
  // 根据显示样式决定 html 要不要折行
  htmlWhitespaceSensitivity: "css",
  // 换行符使用 lf
  endOfLine: "lf"
};

Суммировать

На данный момент webpack series 2 закончился, а самое интересное еще впереди.Первые две статьи можно рассматривать только как основу для более поздних больших проектов.В дальнейшем буду использовать lerna для рефакторинга,использую plug - в управлении и построить подключаемую экологическую систему. Проекты Vue и React будут построены позже, и даже небольшие программы, кросс-энд и т. д. будут интегрированы.

Здесь вы можете разместить свои мыслиGitHub.com/falling snow-Вик Т…Я серьезно отнесусь к каждому вопросу