Загрузчик Webpack и написание плагина

внешний интерфейс переводчик JavaScript Webpack

1 базовый обзор

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

1.1 Общая конфигурация веб-пакета

// 入口文件
  entry: {
    app: './src/js/index.js',
  },
  // 输出文件
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/'     //确保文件资源能够在 http://localhost:3000 下正确访问
  },
  // 开发者工具 source-map
  devtool: 'inline-source-map',
  // 创建开发者服务器
  devServer: {
    contentBase: './dist',
    hot: true                // 热更新
  },
  plugins: [
    // 删除dist目录
    new CleanWebpackPlugin(['dist']),
    // 重新穿件html文件
    new HtmlWebpackPlugin({
      title: 'Output Management'
    }),
    // 以便更容易查看要修补(patch)的依赖
    new webpack.NamedModulesPlugin(),
    // 热更新模块
    new webpack.HotModuleReplacementPlugin()
  ],
  // 环境
  mode: "development",
  // loader配置
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: [
          'file-loader'
        ]
      }
    ]
  }

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

1.2 Принцип упаковки

  • Определите входной файл
  • Определите зависимости модуля слой за слоем. (Импорт Commonjs, amd или es6, webpack проанализирует его. Для получения зависимостей кода)
  • Все, что делает webpack, — анализирует код. Преобразование кода, компиляция кода, вывод кода
  • Наконец, упакованный код формируется

Это некоторые основные знания WebPack, которые полезны для понимания рабочего механизма WebPack.

2 loader

Хорошо, первый главный герой дебютирует сегодня

2.1 Что такое загрузчик?

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

  • Для обработки файла можно использовать несколько загрузчиков, причем порядок выполнения загрузчиков противоположен самому себе, то есть последний загрузчик выполняется первым, а первый загрузчик выполняется последним.
  • Первый запущенный загрузчик получает в качестве параметра содержимое исходного файла, а остальные загрузчики получают в качестве параметра возвращаемое значение предыдущего выполненного загрузчика. Последний выполненный загрузчик вернет исходный код JavaScript этого модуля.

2.2 рукописный ввод загрузчика

нужно:

  1. Обрабатывать файлы .txt
  2. перевернуть строку
  3. заглавные буквы

Например: abcdefg преобразовано в Gfedcba

Хорошо, давайте начнем

1) Сначала создайте два загрузчика (здесь в качестве примера взят локальный загрузчик)

Зачем создавать два лаода? Причина будет объявлена ​​позже

image

reverse-loader.js

module.exports = function (src) {
  if (src) {
    console.log('--- reverse-loader input:', src)
    src = src.split('').reverse().join('')
    console.log('--- reverse-loader output:', src)
  }
  return src;
}

uppercase-loader.js

module.exports = function (src) {
  if (src) {
    console.log('--- uppercase-loader input:', src)
    src = src.charAt(0).toUpperCase() + src.slice(1)
    console.log('--- uppercase-loader output:', src)
  }
  // 这里为什么要这么写?因为直接返回转换后的字符串会报语法错误,
  // 这么写import后转换成可以使用的字符串
  return `module.exports = '${src}'`
}

Смотрите, это структура погрузчика очень проста, это нормально, чтобы получить параметр и вернуть контент.

Затем создайте текстовый файл

image

2) mytest.txt

abcdefg

3) Теперь приступайте к настройке веб-пакета.

module.exports = {
  entry: {
    index: './src/js/index.js'
  },
  plugins: [...],
  optimization: {...},
  output: {...},
  module: {
    rules: [
      ...,
      {
        test: /\.txt$/,
        use: [
          './loader/uppercase-loader.js',
          './loader/reverse-loader.js'
        ]
      }
    ]
  }
}

На этом настройка завершена

4) Импортируем этот скрипт в файл входа

Зачем нам импортировать сюда, разве мы не настраиваем webapck для обработки всех файлов .txt?

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

import _ from 'lodash';
import txt from '../txt/mytest.txt'
import '../css/style.css'
function component() {
  var element = document.createElement('div');
  var button = document.createElement('button');
  var br = document.createElement('br');

  button.innerHTML = 'Click me and look at the console!';
  element.innerHTML = _.join('【' + txt + '】');
  element.className = 'hello'
  element.appendChild(br);
  element.appendChild(button);

  // Note that because a network request is involved, some indication
  // of loading would need to be shown in a production-level site/app.
  button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
    var print = module.default;

    print();
  });

  return element;
}
document.body.appendChild(component());

Конфигурация package.json

{
  ...,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config webpack.prod.js",
    "start": "webpack-dev-server --open --config webpack.dev.js",
    "server": "node server.js"
  },
  ...
}

затем выполните команду

npm run build

image

На этом наш загрузчик закончен.

Теперь ответьте, зачем писать два загрузчика?

Смотрите порядок выполнения, наша конфигурация такая

use: [
  './loader/uppercase-loader.js',
  './loader/reverse-loader.js'
]

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

Мы также можем написать свой собственный загрузчик для разбора пользовательских шаблонов.Например, vue-loader очень сложен.Он будет писать много парсинга файлов .vue внутри, а затем генерировать соответствующие html, js и css.

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

3 plugin

3.1 Что такое плагин?

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

В чем разница между плагином и загрузчиком?

Для загрузчика это преобразователь, который компилирует файл A для формирования файла B. Операция здесь представляет собой файл, например преобразование A.scss или A.less в B.css, простой процесс преобразования файла.

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

3.2 Минимальный плагин

/plugins/MyPlugin.js (локальный плагин)

class MyPlugin {
  // 构造方法
  constructor (options) {
    console.log('MyPlugin constructor:', options)
  }
  // 应用函数
  apply (compiler) {
    // 绑定钩子事件
    compiler.plugin('compilation', compilation => {
      console.log('MyPlugin')
    ))
  }
}

module.exports = MyPlugin

конфигурация веб-пакета

const MyPlugin = require('./plugins/MyPlugin')
module.exports = {
  entry: {
    index: './src/js/index.js'
  },
  plugins: [
    ...,
    new MyPlugin({param: 'xxx'})
  ],
  ...
};

Это самый простой плагин (хотя мы ничего не делали)

  • После запуска веб-пакета в процессе чтения конфигурации он сначала выполнит новый MyPlugin(options), чтобы инициализировать MyPlugin для получения его экземпляра.
  • После инициализации объекта компилятора вызовите myPlugin.apply(compiler), чтобы передать объект компилятора экземпляру плагина.
  • После того, как экземпляр плагина получит объект компилятора, он может прослушивать события, транслируемые Webpack, через компилятор.plugin(имя события, функция обратного вызова).
  • И вы можете управлять веб-пакетом через объект компилятора.

Увидев это, вы можете спросить, что такое компилятор и что такое компиляция?

  • Объект Compiler содержит всю информацию о конфигурации среды Webpack., включая параметры, загрузчики, плагины, этот объект создается при запуске Webpack, он глобально уникален, и его можно просто понимать как экземпляр Webpack;

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

Разница между компилятором и компиляцией заключается в следующем:

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

3.3 Поток событий

  • Webpack использует Tapable для организации этой сложной производственной линии.
  • Механизм потока событий webpack обеспечивает упорядоченность плагинов, делая всю систему очень масштабируемой.
  • Механизм потоковой передачи событий webpack применяет шаблон наблюдателя, который очень похож на EventEmitter в Node.js.

привязать событие

compiler.plugin('event-name', params => {
  ...	  
});

триггерное событие

compiler.apply('event-name',params)

3.4 На что следует обратить внимание

  • Пока вы можете получить компилятор или объект компиляции, вы можете транслировать новые события, поэтому вы также можете транслировать события в недавно разработанных плагинах для других плагинов для мониторинга и использования.
  • Объекты Compiler и Compilation, передаваемые каждому подключаемому модулю, являются одной и той же ссылкой. Другими словами, изменение свойств компилятора или объекта компиляции в одном плагине повлияет на последующие плагины.
  • Некоторые события являются асинхронными. Эти асинхронные события будут иметь два параметра. Второй параметр — это функция обратного вызова. Когда плагин завершает обработку задачи, ему необходимо вызвать функцию обратного вызова, чтобы уведомить веб-пакет перед переходом к следующему процессу обработки.. Например:
compiler.plugin('emit',function(compilation, callback) {
  ...
    
  // 处理完毕后执行 callback 以通知 Webpack 
  // 如果不执行 callback,运行流程将会一直卡在这不往下执行 
  callback();
});

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

"документация к компилятору"

"Документация по компиляции"

3.5 Написать плагин вручную

Сцены:

Проект апплета mpvue компилируется webpack для создания подпакетов (которые мы вводим в основную программу как подпакеты), а затем допускаются к основному пакету. После того, как подпакет сгенерирован, адрес ссылки wxss публичного статического ресурса в нем необходимо добавить с префиксом подпакета: /subPages/enjoy_given.

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

image

Итак, требование:

Измените путь к общедоступным ресурсам, представленным файлами стилей (файлами wxss) всех страниц в каталоге dist/static/css/pages.

Поскольку все стили страниц ссылаются на общий стиль vendor.wxss

那么就需要把@import "/static/css/vendor.wxss"; 改为:@import "/subPages/enjoy_given/static/css/vendor.wxss";

Хорошо, чтобы начать!

1) Создайте файл плагина CssPathTransfor.js

image

CssPathTransfor.js

class CssPathTransfor {
  apply (compiler) {
    compiler.plugin('emit', (compilation, callback) => {
      console.log('--CssPathTransfor emit')
      // 遍历所有资源文件
      for (var filePathName in compilation.assets) {
        // 查看对应的文件是否符合指定目录下的文件
        if (/static\/css\/pages/i.test(filePathName)) {
          // 引入路径正则
          const reg = /\/static\/css\/vendor\.wxss/i
          // 需要替换的最终字符串
          const finalStr = '/subPages/enjoy_given/static/css/vendor.wxss'
          // 获取文件内容
          let content = compilation.assets[filePathName].source() || ''
          
          content = content.replace(reg, finalStr)
          // 重写指定输出模块内容
          compilation.assets[filePathName] = {
            source () {
              return content;
            },
            size () {
              return content.length;
            }
          }
        }
      }
      callback()
    })
  }
}
module.exports = CssPathTransfor

Выглядит много, но на самом деле проходит через модульcompile.assets. Регулярная замена соответствующих файлов.

2) Изменить конфигурацию веб-пакета

var baseWebpackConfig = require('./webpack.base.conf')
var CssPathTransfor = require('../plugins/CssPathTransfor.js')

var webpackConfig = merge(baseWebpackConfig, {
  module: {...},
  devtool: config.build.productionSourceMap ? '#source-map' : false,
  output: {...},
  plugins: [
    ...,
    // 配置插件
    new CssPathTransfor(),
  ]
})

После того, как плагин будет написан, выполните команду компиляции

image

получить это~

Если у вас есть дополнительные потребности, см."Как написать плагин"