Содержание этой статьи
- Как разрабатывать плагины для веб-пакетов и процесс разработки плагинов для упаковки апплетов
- Проблемы, возникающие при разработке, и пути их решения
- Что умеет мини-программа-webpack-loader
Разработка плагинов для веб-пакетов
Я полагаю, что студенты, которые разрабатывали плагины, видели этоWriting a PluginИли подобные статьи, потому что webpack 4 был выпущен, когда был разработан инструмент mini-program-webpack-loader, поэтому я прочитал эту статью и, кстати, прочитал следующие документы.
Если вы читали документацию, я думаю, вы должны знать:
- Каждый плагин должен иметь метод применения, который используется движком веб-пакета для выполнения кода, который вы хотите выполнить.
- Два важных объекта Compiler и Compilation, к ним можно привязать хуки событий (вызываются, когда webpack выполняет этот шаг), какие хуки событий можно прочитатьCompiler hooks.
- Отношения между модулем и чанком, мы можем понять, что каждый файл будет иметь модуль, а чанк состоит из нескольких модулей.
- Какие события есть во всем процессе упаковки webpack
- Как написать простой загрузчик
Если вы чувствуете, что не можете начать, вы можете продолжить наблюдать за тем, как я шаг за шагом разрабатывал и улучшал mini-program-webpack-loader для упаковки небольших программ.
Апплет имеет фиксированную процедуру: во-первых, должен быть файл app.json для определения всех путей к страницам, а затем каждая страница состоит из четырех файлов: .js, .json, .wxml и .wxss. Поэтому я использую app.json в качестве записи веб-пакета.Когда веб-пакет выполняет применение плагина, он получает запись, чтобы узнать, какие страницы есть у апплета. Вероятно, процесс как на следующей картинке, небольшой плагин для упаковки программы почти завершен.
Здесь используются два плагина MultiEntryPlugin, SingleEntryPlugin. почему ты хочешь сделать это? Поскольку webpack будет определять количество сгенерированных файлов в соответствии с вашей конфигурацией записи (запись здесь — это не просто запись в конфигурации webpack, import(), require.ensure() сгенерирует запись), чтобы определить количество сгенерированных файлов, мы не хотим помещать js всех страниц. Чтобы упаковать в файл, вам нужно использовать SingleEntryPlugin для создания нового модуля ввода; и для этих статических ресурсов мы можем использовать плагин MultiEntryPlugin для обработки этих файлов как зависимость модуля ввода и настроить файловый загрузчик в загрузчике для преобразования статических ресурсов. Псевдокод выглядит следующим образом:
const MultiEntryPlugin = require('webpack/lib/MultiEntryPlugin');
const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');
class MiniPlugin {
apply (compiler) {
let options = compiler.options
let context = compiler.rootContext
let entry = options.entry
let files = loadFiles(entry)
let scripts = files.filter(file => /\.js$/.test(file))
let assets = files.filter(file => !/\.js$/.test(file))
new MultiEntryPlugin(context, assets, '__assets__').apply(compiler)
scripts.forEach((file => {
let fileName = relative(context, file).replace(extname(file), '');
new SingleEntryPlugin(context, file, fileName).apply(compiler);
})
}
}
Конечно, если вы сделаете, как указано выше, вы обнаружите, что будет лишний main.js, xxx.js (имя, заполняемое при использовании MultiEntryPlugin), main.js соответствует файлу, сгенерированному настроенной записью, и xxx. .js генерируется MultiEntryPlugin. Эти файлы нам не нужны, поэтому их нужно удалить. Если вы знакомы с документацией веб-пакета, есть много мест, где мы можем изменить окончательные упакованные файлы, например, событие emit компилятора, а также могут быть реализованы события, связанные с optimizeChunks компиляции. Он существенно изменяет объектcompile.assets.
Событие emit используется в mini-program-webpack-loader для обработки такого контента, который не нужно выводить. Процесс, вероятно, выглядит следующим образом:
Конечно, упаковка небольших программ не так проста, и она должна поддерживать ссылки на wxml, wxss, wxs и пользовательские компоненты, поэтому в настоящее время для завершения необходим загрузчик, а загрузчик должен делать следующее. также очень просто - синтаксический анализ зависимых файлов, таких как .wxml, должен анализировать src компонента импорта, src wxs, .wxss должен анализировать @import, требование wxs и, наконец, использовать метод loadModule, чтобы добавить его в погрузчик. Пользовательский компонент получается непосредственно на этапе добавления записи в начале, поэтому для его завершения не требуется загрузчик. Картинка в это время:
Проблем с этим нет, но опыт разработки относительно невелик.Например, если вы добавляете кастомный компонент или страницу, вебпак незаметен, поэтому нужно проверять добавляется ли кастом при изменении .json на странице , Компоненты или новые страницы добавляются. На данный момент есть проблема: js кастомного компонента не может быть добавлен addModule, потому что js кастомного компонента нужно использовать как отдельный файл записи. Это невозможно сделать в загрузчике, поэтому попробуйте передать файл плагину (поскольку плагин выполняется до загрузчика, можно установить связь между загрузчиком и плагином). Простой и грубый способ:
// loader.js
class MiniLoader {}
module.exports = function (content) {
new MiniLoader(this, content)
}
module.exports.$applyPluginInstance = function (plugin) {
MiniLoader.prototype.$plugin = plugin
}
// plugin.js
const loader = require('./loader')
class MiniPlugin {
apply (compiler) {
loader.$applyPluginInstance(this);
}
}
но.... Файл передается плагину, но когда вы снова используете SingleEntryPlugin, вы обнаружите, что он не действует. Поскольку веб-пакет не может воспринимать новый модуль, добавленный после сборки компилятора, он бесполезен.В настоящее время вам нужно догадаться по документу, как заставить веб-пакет воспринимать новый модуль, и выполнить запрос по ключевым словам в соответствии с событиями в документ, можно обнаружить, что обработчик события needAdditionalPass компиляции вызывается, когда компиляция завершена:
this.emitAssets(compilation, err => {
if (err) return finalCallback(err);
if (compilation.hooks.needAdditionalPass.call()) {
compilation.needAdditionalPass = true;
const stats = new Stats(compilation);
stats.startTime = startTime;
stats.endTime = Date.now();
this.hooks.done.callAsync(stats, err => {
if (err) return finalCallback(err);
this.hooks.additionalPass.callAsync(err => {
if (err) return finalCallback(err);
this.compile(onCompiled);
});
});
return;
}
this.emitRecords(err => {
if (err) return finalCallback(err);
const stats = new Stats(compilation);
stats.startTime = startTime;
stats.endTime = Date.now();
this.hooks.done.callAsync(stats, err => {
if (err) return finalCallback(err);
return finalCallback(null, stats);
});
});
});
Если этот обработчик событий возвращает истинное значение, вы можете заставить веб-пакет вызывать обработчик событий AdditionalPass компилятора, попробуйте добавить сюда файлы, и это сработает. В это время картина становится такой:
Конечно, есть некоторые различия в упаковке небольших программ, такие как субподряд и правильное использование splitchunk.Когда вы начнете, вы обнаружите, что есть много способов добиться желаемого эффекта.
Разработка подключаемых модулей почти завершена. В общем, webpack — это обратный вызов по-разному. Когда вы знаете, что должен делать каждый обратный вызов, веб-пакет прост в использовании. Очевидно, я не знаю, потому что я столкнулся с некоторыми проблемами во время разработки.
возникшие проблемы
1. Как поддерживать разрешающий псевдоним, node_modules в коде апплета?
Поскольку это инструмент, то, конечно, нужно делать больше вещей. Апплеты такие сложные. Если вы поддерживаете псевдоним разрешения, node_modules может облегчить поддержку проекта. Может быть, вы скажете, что это не самая основная функция webpack, нет, мы Конечно, есть надежда, что псевдонимы можно использовать в любом файле, а node_modules поддерживает не только js. Конечно, это означает, что все усложнится.Во-первых, нужно получить путь к файлу, который должен быть асинхронным, потому что разрешение в веб-пакете 4 больше не поддерживает синхронизацию. Во-вторых, имя каталога апплета не может быть node_modules, в этом случае требуется правило вычисления относительного пути, либо вывод относительно пакета, а не относительно текущего каталога проекта.
2. Слияние нескольких проектов апплетов
YouzanЧто касается небольших программ, есть версия для микроторговых центров, розничная версия и общедоступная версия.Большинство основных функций и бизнеса одинаковы.Конечно, каждую маленькую программу нельзя разработать один раз, поэтому этот инструмент имеет возможность объединить несколько небольших программ.Программа, конечно, обязательна. Такое слияние немного сложнее, чем выборка файлов из node_modules, потому что необходимо убедиться, что объединенные страницы нескольких апплетов корректны, а путь остается неизменным.
Окончательное решение этих двух проблем состоит в том, чтобы использовать каталог src корневого контекста веб-пакета в качестве базового каталога, вычислить абсолютный путь упакованного файла на основе пути каталога, а затем вычислить окончательный выходной путь в соответствии с путем каталог, в котором находится app.json файла записи.
exports.getDistPath = (compilerContext, entryContexts) => {
/**
* webpack 以 config 所在目录的 src 为打包入口
* 所以可以根据该目录追溯源文件地址
*/
return (path) => {
let fullPath = compilerContext
let npmReg = /node_modules/g
let pDirReg = /^[_|\.\.]\//g
if (isAbsolute(path)) {
fullPath = path
} else {
// 相对路径:webpack 最后生成的路径,打包入口外的文件都以 '_' 表示上级目录
while (pDirReg.test(path)) {
path = path.substr(pDirReg.lastIndex)
fullPath = join(fullPath, '../')
}
if (fullPath !== compilerContext) {
fullPath = join(fullPath, path)
}
}
// 根据 entry 中定义的 json 文件目录获取打包后所在目录,如果不能获取就返回原路径
let contextReg = new RegExp(entryContexts.join('|'), 'g')
if (fullPath !== compilerContext && contextReg.exec(fullPath)) {
path = fullPath.substr(contextReg.lastIndex + 1)
console.assert(!npmReg.test(path), `文件${path}路径错误:不应该还包含 node_modules`)
}
/**
* 如果有 node_modules 字符串,则去模块名称
* 如果 app.json 在 node_modules 中,那 path 不应该包含 node_modules
*/
if (npmReg.test(path)) {
path = path.substr(npmReg.lastIndex + 1)
}
return path
}
}
3. Как упаковать содержимое, от которого зависит подпакет, отдельно в подпакет
Способ решения этой проблемы состоит в том, чтобы добавить файл записи этого чанка в зависимый модуль каждого чанка через событие optimChunks, а затем проверить количество зависимых модулей в тестовой конфигурации splitChunk. Если есть только один, и он зависит от подпакета, он упаковывается в подпакет.
4. Webpack не поддерживает один файл
Это открытая проблема, которая не кажется такой удобной при попытке использовать веб-пакет для поддержки отдельных файлов:
- После того, как один файл разбит на четыре файла, вы можете использовать emitFile и addDependency для создания файлов, но созданные файлы не будут выполняться загрузчиком.
- Использование loadModule сообщит об ошибке, поскольку файл не существует в файловой системе.
напиши в конце
Наконец, конечно же, нужно представить, что умеет мини-программа-вебпак-загрузчик.
Инструмент в основном решает следующие задачи:
- Апплеты не поддерживают npm
- Каталоги вложены слишком глубоко, а пути неуправляемы
- Старые проекты слишком большие, а использовать новые инструменты слишком дорого
- Предоставлять общие предложения по оптимизации в крупномасштабных проектах апплетов
повторить
- Вы можете напрямую использовать npm для загрузкиzanui-weappохватывать
- Вы можете попрощаться с "../../../../../xxx"
- Вы можете использовать mpVue, таро для написания новых функций, не беспокойтесь о несовместимости
Наконец, оставьте адрес документа в конце:mini-program-webpack-loader