Важной особенностью WebPack5 является модуль-федерацией. Эта статья начинается из того, почему эта функция появляется, как ее использовать, то, что она есть, а некоторые сценарии приложений. Наконец, демонстрация, чтобы кратко представить эту функцию.
Why
Федерация модулей, дословно переводится как федерация модулей, — это захватывающий подключаемый модуль, предоставляемый webpack5, который может изменить способ упаковки внешнего интерфейса в ближайшие несколько лет.Какова функция и цель этого подключаемого модуля?
- Функция: Дети, читавшие результаты построения webpack3 или webpack4, должны знать, что webpack предоставляет только глобальный массив webpackJsonp (а не метод). угнали (модифицировали) на внутренний
webpack_modules
На этом объекте внутренние переменные могут получить доступ к объекту, но внешние не могут быть получены, что является полностью «операцией черного ящика», что также приводит к невозможности «федерации» модулей с внешней средой, из-за чего объединение модулей было введен в механизм webpack5. Благодаря этому механизму построенная кодовая база может бытьДинамический,время выполнениязапустить в другой кодовой базе. - Цель: Усовершенствовав функциональные модули, повторное использование компонентов, обмен сторонними библиотеками и онлайн-загрузку пакетов npm в зависимостях времени выполнения, он может лучше обслуживать режимы разработки, такие как многостраничные приложения и микроинтерфейсы.
How
Кратко опишите, как использовать этот плагин.
В обычных условиях вам необходимо настроить соответствующие элементы конфигурации реферера и рефери, в нестандартных случаях реферер может использовать их напрямую без настройки. Конечно, на сам модуль можно ссылаться как на объявление ресурса, или его можно использовать в качестве реферера для объявления других ресурсов, на которые необходимо ссылаться. Оба настраиваются последовательно на основе подключаемого модуля ModuleFederationPlugin. Как подключаемый модуль, он должен быть удобно использовать.
Конфигурация направляемой стороны
- имя: обязательное и уникальное, используется в качестве имени ключа для сторонней ссылки, эквивалентно псевдониму, методу ссылки
${name}/${expose}
- библиотека: объявить имя переменной, смонтированной в глобале, где имя — это имя umd
- Имя файла: имя имени чанка
- Экспонирует: как наиболее важный элемент конфигурации упомянутой стороны, он используется для предоставления модулей, предоставляемых извне.
- общие: объявить общие сторонние ресурсы
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin")
new ModuleFederationPlugin({
name: "zLib",
library: { type: "var", name: "zLib" },
filename: "zLib.js",
exposes: {
utils: "./src/utils.js"
},
shared: ['lodash']
})
конфигурация реферера
- remotes: как наиболее важный элемент конфигурации реферера, он используется для объявления имени и имени модуля пакета удаленных ресурсов, на который необходимо ссылаться.
- Другие элементы конфигурации такие же, как и выше.Вы также можете объявить поле exposes здесь, чтобы предоставить ресурсы вашего модуля для внешнего использования.
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin")
new ModuleFederationPlugin({
name: "zLocal",
library: { type: "var", name: "zLocal" },
remotes: {
zLib: "zLib"
},
shared: ["lodash"]
})
При использовании его в проекте, который ссылается на удаленные ресурсы, вам необходимо сначала импортировать файл записи удаленного ресурса, который можно загрузить асинхронно или импортировать с помощью тега script. Эта функция предназначена для монтирования переменной zComp в глобальную и предоставления метода get для получения модуля.
<script src="/zLib/dist/zLib.js"></script>
Когда вам нужно сослаться на модуль ресурсов, передайтеimport('远程资源包名称/模块名')
способ импортировать напрямую.
import('zLib/utils').then(({ timeDelayFn }) => {
timeDelayFn(function () {
console.log('from remote utils fn')
}, 1000)
})
What
Глядя на код после сборки, вы можете видеть, что приведенные выше функции основаны на переписыванииwebpack_require.e
Этот метод реализован, до webpack5 этот метод использовался только для загрузки асинхронных чанков через jsonp и ссылки на загруженные модули в тогдашней микрозадаче. Но в webpack5 этот метод был модернизирован, и он будет проходиться и выполняться каждый раз при вызове.webpack_require.f
Три метода объекта:
- webpack_require.f.overridables для объединения общих модулей в чанке A с webpck_require.O в чанке B
- webpack_require.f.remotes, для загрузки общих модулей в чанке B в webpack_modules в чанке A
- webpack_require.f.jsonp используется для загрузки асинхронных чанков и внедрения webpack_modules в указанный чанк записи
Логика выполнения трех методов следующая:
- Выполните переопределения, чтобы проверить, объявлен ли фрагмент, который необходимо загрузить, как общий ресурс в элементе конфигурации.
__webpack_require__.O
Если соответствующие ресурсы можно найти в Интернете, используйте их напрямую, не запрашивая ресурсы. - Выполните удаленные операции, чтобы проверить, объявлен ли загружаемый в данный момент блок в качестве удаленного удаленного ресурса в элементе конфигурации.Если соответствующие модули могут быть найдены в другом приложении с помощью метода get, удаленный ресурс асинхронно загружается и кэшируется в текущий
__webpack_modules__
на объекте - Выполнить jsonp для асинхронной загрузки ресурсов непосредственно на указанный адрес
// __webpack_require__.f.overridables
__webpack_require__.f.overridables = (chunkId, promises) => {
if (__webpack_require__.o(chunkMapping, chunkId)) {
chunkMapping[chunkId].forEach((id) => {
if (__webpack_modules__[id]) return
promises.push(Promise.resolve((__webpack_require__.O[idToNameMapping[id]] || fallbackMapping[id])()).then((factory) => {
__webpack_modules__[id] = (module) => { // 模拟注入modules操作
module.exports = factory()
}
}))
})
}
}
// __webpack_require__.f.remotes
__webpack_require__.f.remotes = (chunkId, promises) => {
if (__webpack_require__.o(chunkMapping, chunkId)) {
chunkMapping[chunkId].forEach((id) => {
if (__webpack_modules__[id]) return
var data = idToExternalAndNameMapping[id]
promises.push(Promise.resolve(__webpack_require__(data[0]).get(data[1])).then((factory) => {
__webpack_modules__[id] = (module) => {
module.exports = factory()
}
}))
})
}
}
// jsonp部分代码太长,基本原理就是创建script标签插入页面是实现资源加载,下面有模拟实现
__webpack_require__.f.j = () => {}
Where
Что толку так много говорить? Тогда возможные сценарии применения, вероятно, таковы:
- Интерфейс Micro обеспечивает загрузку общедоступных зависимых ресурсов через общий и удаленный доступ, уменьшает объем онлайн и упрощает обслуживание.
- Ускоряется скорость компиляции.Ресурсы node_modules могут быть упакованы заранее и на них может ссылаться метод времени выполнения, а при компиляции создаются только исходные файлы проекта.
- Многостраничное повторное использование ресурсов приложения, включая введение зависимостей во время выполнения, повторное использование компонентов и даже совместное использование всей страницы.
Demo
Просьба о помощи не так хороша, как огромная сумма денег, тысяча слов не так хороша, как пример, независимо от того, понимаете ли вы вышеизложенное, вы поймете это, посмотрев демо!
В качестве примера возьмем локальный проект Vue. Предположим, что текущему проекту нужен компонент ввода и компонент кнопки. Вы можете представить его как компонент, который ссылается на elementUI, но мы не хотим напрямую упаковывать эти два компонента пользовательского интерфейса в локальные ресурсы. ., очевидно, что лениво загружаемый компонент vue будет использоваться в прошлом, как показано ниже:
<template>
<section class="module-federation">
<myButton />
<myInput />
</section>
</template>
<script>
const myButton = () => import('./myButton.vue')
const myInput = () => import('./myInput.vue')
export default {
components: { myButton, myInput }
}
</script>
Таким образом, компоненты могут быть упакованы в независимый фрагмент для асинхронной загрузки, но на файл фрагмента может ссылаться только текущий проект, и он не может использоваться другими проектами, поскольку фрагмент нельзя использовать после внедрения в объект модулей. проекта внешний доступ. Из-за этого ограничения вводится наша федерация модулей.Здесь нам не нужно вносить изменения в исходный проект, а нужно только настроить элементы конфигурации сборки общих ресурсов:
new ModuleFederationPlugin({
name: "zComp",
library: { type: "var", name: "zComp" },
filename: "zComp.js",
exposes: {
myButton: "./src/myButton.vue",
myInput: './src/myInput.vue'
},
shared: ['vue']
})
Настройте имя пакета ресурсов как zComp, выставьте 2 компонента пользовательского интерфейса myButton и myInput, если проект, настроенный через удаленный доступ, можно использовать напрямую.zComp/myButton
метод, но здесь мы не меняем исходный проект, поэтому используем другой методzComp.get('myButton')
Косвенная ссылка.
Основная идея заключается в асинхронной загрузке файлов пакетов общих ресурсов.zComp.js
, файл смонтирует в окне глобальную переменную zComp. Для этой переменной существует два метода: get и override. Как упоминалось выше, get используется для получения общих ресурсов пакета общих ресурсов, а override используется для совместного использования текущий проект ресурсы ниже. Здесь основное внимание уделяется методу get, который после вызова возвращает фабричную функцию типа обещания:
// 调用get方法之后会先检查需要加载组件的依赖项
// 返回值为() => __webpack_require__("./src/myButton.vue")
Promise.all([
__webpack_require__.e("vue"),
__webpack_require__.e("src_myButton_vue")
]).then(() => () => __webpack_require__("./src/myButton.vue"))
Продолжайте вызывать фабричную функцию, чтобы ссылаться на соответствующий ресурс модуля, который будет кэшироваться в webpack_modules самого общего пакета ресурсов через webpackJsonp. Когда вы получаете ресурсы модуля, вы получаете компоненты, а затем наступает время свободного падения и свободной игры.
Исходный код:
<template>
<section class="module-federation">
<h1>webpack5 module-federation</h1>
<component :is="remoteButton" />
<component :is="remoteInput" />
</section>
</template>
<script>
import { asyncJsonp } from '@/utils'
export default {
data () {
return {
remoteButton: null,
remoteInput: null
}
},
mounted () {
this.getRemoteComp()
this.getRemoteLib()
},
methods: {
async getRemoteComp () {
await asyncJsonp('/static/common-shared/zComp.js')
console.log('zComp chunk loaded')
// 引用button组件
const buttonFactory = await zComp.get('myButton')
this.remoteButton = buttonFactory().default
// 引用input组件
const inputFactory = await zComp.get('myInput')
this.remoteInput = inputFactory().default
},
async getRemoteLib () {
await asyncJsonp('/static/common-shared/zLib.js')
console.log('zLib chunk loaded')
// 引用另一个共享资源包中的utils工具库
const factory = await zLib.get('utils')
const utils = factory()
utils.timeDelayFn(() => {
console.log('from remote utils fn log')
}, 2000)
}
}
}
вasyncJsonp
Метод опирается на реализацию webpack Jsonp и просто инкапсулирует его в обещание:
export const asyncJsonp = (() => {
const cacheMap = {}
return (path, delay = 120) => {
if (!path || cacheMap[path]) return
return new Promise((resolve, reject) => {
const script = document.createElement('script')
script.charset = 'utf-8'
script.timeout = delay
script.src = path
const onScriptComplete = (event) => {
script.onerror = script.onload = null
clearTimeout(timeout)
if (event.type === 'load') {
cacheMap[path] = true
return resolve()
}
const error = new Error()
error.name = 'Loading chunk failed.'
error.type = event.type
error.url = path
reject(error)
}
const timeout = setTimeout(() => {
onScriptComplete({ type: 'timeout', target: script })
}, delay * 1000)
script.onerror = script.onload = onScriptComplete
document.head.appendChild(script)
})
}
})()
Summary
- путем переписывания
webpack_require.e
Представлены три метода переопределения, удаленного доступа и jsonp для реализации совместного использования зависимостей, повторного использования компонентов и асинхронной загрузки между различными приложениями. Однако необходимо обратить внимание на порядок введения различных ресурсов ввода приложений. - Совместное использование модулей между различными приложениями по существу использует глобальное окно в качестве моста и соединяет различные приложения с помощью методов получения и переопределения.
- Текущая версия webpack5-beta14 еще не добавила плагин ModuleFederationPlugin, его нужно установить на практике
git://github.com/webpack/webpack.git#dev-1
Зависимость от версии для разработчиков, с нетерпением жду появления нормальной версии.