Источник изображения:zhuanlan.zhihu.com/p/78362028
Автор этой статьи: Ши Чжипэн
предисловие
Фоновый проект прямой трансляции LOOK — это «приложение для валунов», которое повторялось более 2 лет, в развитии бизнеса участвовало более 10+ разработчиков и содержалось более 250 страниц. Огромный объем кода приводит к неэффективному построению и развертыванию, кроме того, проект опирается на внутренний наборRegularjsСтек технологий также выполнил свою историческую миссию, и соответствующую библиотеку компонентов пользовательского интерфейса и инженерные леса также было рекомендовано прекратить использовать, переходя к стадии меньшего обслуживания или отсутствия обслуживания. Поэтому в график работы был включен фон операции прямой трансляции LOOK на основе нового строительства React и разделения проекта. Цель состоит в том, чтобы описать цель одним предложением: новая страница будет разработана в новом проекте на основе React, проект React может быть развернут независимо, и ожидается, что адрес доступа, выдаваемый фоном операции прямой трансляции LOOK, останется неизменным. .
Эта статья основана на практике посадки микро-интерфейса на фоне операции прямой трансляции LOOK. В этой статье в основном представлен процесс посадки микроинтерфейса приложений CMS с использованием микроинтерфейса фреймворка qiankun в сценарии, в котором сосуществуют существующее «приложение Boulder», стеки технологий Regularjs и React.
Для ознакомления с qiankun перейдите по ссылкеофициальныйПроверьте это, эта статья не будет посвящена представлению концепций микрофронтендов.
1. Предпосылки
1.1 Статус
- Как упоминалось выше, есть приложение CMS, как показано на рисунке ниже, проект этого приложения называется liveadmin, а адрес доступа:
https://example.com/liveadmin
, доступ, как показано ниже.
- Мы надеемся, что больше не будем добавлять новые бизнес-страницы в старый проект liveadmin, поэтому мы создали новый проект под названием «Увеличение» на основе внутренних шаблонов React. Этот проект рекомендуется для новых бизнес-страниц. Это приложение можно развернуть независимо и получить к нему доступ независимо адрес доступа:
https://example.com/lookadmin
, доступ, как показано ниже:
1.2 Цели
Мы надеемся использовать метод микроинтерфейса для интеграции всех меню двух приложений, чтобы пользователь не воспринимал это изменение и по-прежнему следовал исходному методу доступа.https://example.com/liveadmin
, вы можете получить доступ ко всем страницам liveadmin и увеличить проекты.
Для такой цели нам необходимо решить следующие две основные проблемы:
- Комбинированное отображение меню двух систем;
- Используйте исходный адрес доступа для доступа к страницам двух приложений.
По второму вопросу я считаю, что ученики, знающие цянькунь, могут прийти к такому же консенсусу, как и мы, а что касается первого вопроса, то мы решили его через какие-то внутренние решения в процессе практики. Процесс реализации будет описан ниже. Здесь мы сначала приводим рендеры всего лендинга проекта:
Видно, что меню первого уровня нового проекта увеличения добавлено к меню первого уровня проекта liveadmin, и исходный адрес может получить доступ ко всем меню двух проектов.
1.3 Управление правами
Говоря о CMS, необходимо также рассказать о реализации системы управления правами, именуемой в дальнейшем PMS.
- Разрешения: в настоящее время в нашей PMS определены два типа разрешений: разрешения страницы (определяющие, может ли пользователь просматривать определенную страницу) и разрешения функции (определяющие, может ли пользователь получить доступ к API функции). Внешний интерфейс отвечает за реализацию разрешений для страниц, а разрешения функций управляются и контролируются сервером.
- Управление разрешениями: в этой статье описывается только управление разрешениями страниц. Во-первых, каждое внешнее приложение связано с приложением разрешений PMS, например, liveadmin связано с приложением разрешений appCode = live_backend. После успешного развертывания проекта внешнего интерфейса отправьте страницу внешнего проекта и данные кода разрешения, связанные со страницей, в PMS через черный ход. Операция управления рисками находит соответствующее приложение разрешений в системе PMS, назначает разрешения страниц в соответствии с гранулярностью роли, и пользователи с ролью могут получить доступ к страницам, назначенным ролью.
- Управление разрешениями: при доступе к внешнему приложению самый внешний модуль отвечает за запрос списка кодов разрешений страницы текущего пользователя, а затем фильтрует действительные меню, к которым можно получить доступ в соответствии со списком кодов разрешений, регистрируя маршруты действительные меню и, наконец, создание текущей страницы.Легитимное приложение меню с разрешениями пользователя.
2. Реализация
2.1 основное приложение lookcms
- Прежде всего, создайте новый базовый проект CMS, определите его как основные внешности приложения, и имеют основные функции запроса разрешений, данных меню и меню рендеринга.
Файл входа выполняет следующие функции запроса разрешений и данных меню, а также рендеринга меню.
// 使用 Redux Store 处理数据
const store = createAppStore();
// 检查登录状态
store.dispatch(checkLogin());
// 监听异步登录状态数据
const unlistener = store.subscribe(() => {
unlistener();
const { auth: { account: { login, name: userName } } } = store.getState();
if (login) { // 如果已登录,根据当前用户信息请求当前用户的权限和菜单数据
store.dispatch(getAllMenusAndPrivileges({ userName }));
subScribeMenusAndPrivileges();
} else {
injectView(); // 未登录则渲染登录页面
}
});
// 监听异步权限和菜单数据
const subScribeMenusAndPrivileges = () => {
const unlistener = store.subscribe(() => {
unlistener();
const { auth: { privileges, menus, allMenus, account } } = store.getState();
store.dispatch(setMenus(menus)); // 设置主应用的菜单,据此渲染主应用 lookcms 的菜单
injectView(); // 挂载登录态的视图
// 启动qiankun,并将菜单、权限、用户信息等传递,用于后续传递给子应用,拦截子应用的请求
startQiankun(allMenus, privileges, account, store);
});
};
// 根据登录状态渲染页面
const injectView = () => {
const { auth: { account: { login } } } = store.getState();
if (login) {
new App().$inject('#j-main');
} else {
new Auth().$inject('#j-main');
window.history.pushState({}, '', `${$config.rootPath}/auth?redirect=${window.location.pathname}`);
}
};
- Введите qiankun и зарегистрируйте два дополнительных приложения liveadmin и увеличьте.
Определите подприложение и определите поля name, entry, container и activeRule в соответствии с официальными документами qiankun.Конфигурация входа обращает внимание на различение среды и получает меню, привилегии и другие данные предыдущего шага. основной код выглядит следующим образом:
// 定义子应用集合
const subApps = [{ // liveadmin 旧工程
name: 'music-live-admin', // 取子应用的 package.json 的 name 字段
entrys: { // entry 区分环境
dev: '//localhost:3001',
// liveadmin这里定义 rootPath为 liveadminlegacy,便于将原有的 liveadmin 释放给主应用使用,以达到使用原始访问地址访问页面的目的。
test: `//${window.location.host}/liveadminlegacy/`,
online: `//${window.location.host}/liveadminlegacy/`,
},
pmsAppCode: 'live_legacy_backend', // 权限处理相关
pmsCodePrefix: 'module_livelegacyadmin', // 权限处理相关
defaultMenus: ['welcome', 'activity']
}, { // increase 新工程
name: 'music-live-admin-react',
entrys: {
dev: '//localhost:4444',
test: `//${window.location.host}/lookadmin/`,
online: `//${window.location.host}/lookadmin/`,
},
pmsAppCode: 'look_backend',
pmsCodePrefix: 'module_lookadmin',
defaultMenus: []
}];
// 注册子应用
registerMicroApps(subApps.map(app => ({
name: app.name,
entry: app.entrys[$config.env], // 子应用的访问入口
container: '#j-subapp', // 子应用在主应用的挂载点
activeRule: ({ pathname }) => { // 定义加载当前子应用的路由匹配策略,此处是根据 pathname 和当前子应用的菜单 key 比较来做的判断
const curAppMenus = allMenus.find(m => m.appCode === app.pmsAppCode).subMenus.map(({ name }) => name);
const isInCurApp = !!app.defaultMenus.concat(curAppMenus).find(headKey => pathname.indexOf(`${$config.rootPath}/${headKey}`) > -1);
return isInCurApp;
},
// 传递给子应用的数据:菜单、权限、账户,可以使得子应用不再请求相关数据,当然子应用需要做好判断
props: { menus: allMenus.find(m => m.appCode === app.pmsAppCode).subMenus, privileges, account }
})));
// ...
start({ prefetch: false });
- Логика главного меню приложения
На основе существующих данных меню мы используем внутренние компоненты пользовательского интерфейса для завершения рендеринга меню, привязки события щелчка к каждому меню и изменения пути окна через pushState после нажатия. Например, щелкните меню a-b, соответствующий маршрутhttp://example.com/liveadmin/a/b
, qiankun отреагирует на изменение маршрута, сопоставит соответствующее субприложение в соответствии с определенным activeRule, а затем субприложение возьмет на себя маршрут и загрузит ресурсы страницы, соответствующие субприложению. Подробный процесс реализации см. в исходном коде qiankun.Основная идея состоит в том, чтобы очистить HTML-код, возвращаемый записью субприложения.<script>
tag , получить ресурсы Javascript модуля, а затем выполнить соответствующий Javascript через eval.
2.2 субприложение liveadmin
- В соответствии с практикой официальной документации qiankun экспортируйте соответствующую функцию ловушки жизненного цикла в файл входа подприложения.
if (window.__POWERED_BY_QIANKUN__) { // 注入 Webpack publicPath, 使得主应用正确加载子应用的资源
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
if (!window.__POWERED_BY_QIANKUN__) { // 独立访问启动逻辑
bootstrapApp({});
}
export const bootstrap = async () => { // 启动前钩子
await Promise.resolve(1);
};
export const mount = async (props) => { // 集成访问启动逻辑,接手主应用传递的数据
bootstrapApp(props);
};
export const unmount = async (props) => { // 卸载子应用的钩子
props.container.querySelector('#j-look').remove();
};
- Измените конфигурацию упаковки Webpack.
output: {
path: DIST_PATH,
publicPath: ROOTPATH,
filename: '[name].js',
chunkFilename: '[name].js',
library: `${packageName}-[name]`,
libraryTarget: 'umd', // 指定打包的 Javascript UMD 格式
jsonpFunction: `webpackJsonp_${packageName}`,
},
- Скрывайте элементы заголовка и боковой панели вложенного приложения при работе с интегрированным доступом.
const App = Regular.extend({
template: window.__POWERED_BY_QIANKUN__
? `
<div class="g-wrapper" r-view></div>
`
: `
<div class="g-bd">
<div class="g-hd mui-row">
<AppHead menus={headMenus}
moreMenus={moreMenus}
selected={selectedHeadMenuKey}
open={showSideMenu}
on-select={actions.selectHeadMenu($event)}
on-toggle={actions.toggleSideMenu()}
on-logout={actions.logoutAuth}></AppHead>
</div>
<div class="g-main mui-row">
<div class="g-sd mui-col-4" r-hide={!showSideMenu}>
<AppSide menus={sideMenus}
selected={selectedSideMenuKey}
show={showSideMenu}
on-select={actions.selectSideMenu($event)}></AppSide>
</div>
<div class="g-cnt" r-class={cntClass}>
<div class="g-wrapper" r-view></div>
</div>
</div>
</div>
`,
name: 'App',
// ...
})
- При обработке интегрированного доступа блокируйте запросы данных разрешений и информации для входа и вместо этого получайте разрешения и данные меню из основного приложения, чтобы избежать избыточных HTTP-запросов и настроек данных.
if (props.container) { // 集成访问时,直接设置权限和菜单
store.dispatch(setMenus(props.menus))
store.dispatch({
type: 'GET_PRIVILEGES_SUCCESS',
payload: {
privileges: props.privileges,
menus: props.menus
}
});
} else { // 独立访问时,请求用户权限,菜单直接读取本地的配置
MixInMenus(props.container);
store.dispatch(getPrivileges({ userName: name }));
}
if (props.container) { // 集成访问时,设置用户登录账户
store.dispatch({
type: 'LOGIN_STATUS_SUCCESS',
payload: {
user: props.account,
loginType: 'OPENID'
}
});
} else { // 独立访问时,请求和设置用户登录信息
store.dispatch(loginStatus());
}
- Изменения базы маршрутов при обработке доступа к интеграции
Поскольку rootPath должен быть унифицирован как liveadmin во время интегрированного доступа, маршрут, зарегистрированный во время интегрированного доступа, должен быть изменен на rootPath основного приложения и новую точку монтирования.
const start = (container) => {
router.start({
root: config.base,
html5: true,
view: container ? container.querySelector('#j-look') : Regular.dom.find('#j-look')
});
};
2.3 увеличить подприложение
Аналогично тому, что делает субприложение liveadmin.
- Экспортируйте соответствующие хуки жизненного цикла.
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
const CONTAINER = document.getElementById('container');
if (!window.__POWERED_BY_QIANKUN__) {
const history = createBrowserHistory({ basename: Config.base });
ReactDOM.render(
<Provider store={store()}>
<Symbol />
<Router path="/" history={history}>
{routeChildren()}
</Router>
</Provider>,
CONTAINER
);
}
export const bootstrap = async () => {
await Promise.resolve(1);
};
export const mount = async (props) => {
const history = createBrowserHistory({ basename: Config.qiankun.base });
ReactDOM.render(
<Provider store={store()}>
<Symbol />
<Router path='/' history={history}>
{routeChildren(props)}
</Router>
</Provider>,
props.container.querySelector('#container') || CONTAINER
);
};
export const unmount = async (props) => {
ReactDOM.unmountComponentAtNode(props.container.querySelector('#container') || CONTAINER);
};
- Конфигурация упаковки Webpack.
output: {
path: DIST_PATH,
publicPath: ROOTPATH,
filename: '[name].js',
chunkFilename: '[name].js',
library: `${packageName}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${packageName}`,
},
- При интеграции доступа удалите заголовок и боковую панель.
if (window.__POWERED_BY_QIANKUN__) { // eslint-disable-line
return (
<BaseLayout location={location} history={history} pms={pms}>
<Fragment>
{
curMenuItem && curMenuItem.block
? blockPage
: children
}
</Fragment>
</BaseLayout>
);
}
- При интеграции доступа блокируйте разрешения и запросы на вход, а также получайте разрешения и данные меню, передаваемые основным приложением.
useEffect(() => {
if (login.status === 1) {
history.push(redirectUrl);
} else if (pms.account) { // 集成访问,直接设置数据
dispatch('Login/success', pms.account);
dispatch('Login/setPrivileges', pms.privileges);
} else { // 独立访问,请求数据
loginAction.getLoginStatus().subscribe({
next: () => {
history.push(redirectUrl);
},
error: (res) => {
if (res.code === 301) {
history.push('/login', {
redirectUrl,
host
});
}
}
});
}
});
- При интеграции доступа измените базу реактивного маршрутизатора.
export const mount = async (props) => {
const history = createBrowserHistory({ basename: Config.qiankun.base });
ReactDOM.render(
<Provider store={store()}>
<Symbol />
<Router path='/' history={history}>
{routeChildren(props)}
</Router>
</Provider>,
props.container.querySelector('#container') || CONTAINER
);
};
2.4 Интеграция разрешений (необязательный шаг)
- Как упоминалось выше, внешнее приложение связано с приложением разрешений PMS, тогда, если каждое внешнее приложение объединено через микро-интерфейс, и если каждое внешнее подприложение по-прежнему соответствует разрешению собственной PMS приложение разрешений, а затем стоящее в разрешении. С точки зрения администраторов необходимо обратить внимание на несколько приложений разрешений PMS, назначать разрешения и управлять ролями, что очень сложно для работы, например, различие страниц двух подприложений, и управление ролями двух подприложений с одним и тем же разрешением. Поэтому необходимо рассмотреть возможность унификации приложений разрешений PMS, соответствующих подприложениям.Только наш метод обработки описан здесь только для справки.
- Чтобы максимально сохранить исходный метод управления разрешениями (диспетчер разрешений отправляет код разрешения страницы в PMS через бэкдор внешнего интерфейса, а затем назначает разрешение страницы PMS), в сценарии микро-интерфейса, что необходимо сделать для интеграции разрешений можно описать следующим образом:
- Каждое подприложение сначала отправляет данные меню и кода разрешения этого проекта в соответствующие приложения разрешений PMS.
- Основное приложение загружает данные меню и кода разрешения каждого подприложения, изменяет данные каждого меню и код разрешения на данные приложения разрешения PMS, соответствующие основному приложению, а затем отправляет их в приложение разрешения PMS, соответствующее основному приложению. Разрешение PMS, соответствующее приложению, используется для унифицированного распределения и управления разрешениями в приложении.
- В нашей практике, чтобы диспетчеры разрешений по-прежнему не знали об изменениях, внесенных этим разделенным приложением, приложение разрешений appCode = live_backend PMS, соответствующее исходному приложению liveadmin, по-прежнему используется для назначения разрешений.Нам необходимо применить разрешение PMS, соответствующее к приложению liveadmin.Измените приложение разрешений PMS, соответствующее основному приложению lookcms, и создайте новое приложение разрешений PMS с appCode = live_legacy_backend для подприложения liveadmin, и новое подприложение повышения будет по-прежнему соответствовать PMS применение разрешения appCode = look_backend. Данные меню и кодов разрешений двух вышеуказанных подприложений соответственно передаются соответствующему приложению разрешений PMS в соответствии с пунктом 2, описанным на предыдущем шаге. Наконец, основное приложение lookcms одновременно получает меню внешнего подприложения и данные кода разрешения двух приложений разрешений PMS, appCode = live_legacy_backend и appCode = look_backend, и изменяет их на данные приложения разрешения PMS appCode. = live_backend, и отправляет его в PMS. Общий процесс показан на рисунке ниже. Слева — исходный дизайн системы, справа — измененный дизайн системы.
2.5 Развертывание
- Liveadmin и увеличение каждого используют внешнюю статическую систему развертывания Cloud Music для независимого развертывания, а основные приложения lookcms также развертываются независимо.
- Обработайте междоменную проблему основного приложения, обращающегося к ресурсам подприложения. В нашей практике, поскольку все они развернуты в одном домене, упаковка ресурсов подчиняется одному и тому же правилу домена.
2.6 Резюме
С тех пор мы завершили реализацию микроинтерфейса на основе qiankun LOOK в режиме прямой трансляции, в основном путем создания нового основного проекта, разделения обязанностей основного приложения и изменения подпроектов таким образом, чтобы подпроекты приложения могут быть интегрированы в основное приложение и доступны. , вы также можете сохранить исходную независимую функцию доступа. Общий процесс можно описать следующим образом:
3. Совместное использование зависимостей
Официальный qiankun не рекомендует конкретное решение для совместного использования зависимостей. Мы также провели некоторые исследования по этому вопросу. Вывод можно резюмировать следующим образом: для зависимостей общедоступных библиотек Javascript, таких как Regularjs и React, функции жизненного цикла подприложений могут быть загружены через внешние компоненты Webpack и qiankun.И плагин import-html-entry для решения, а для компонентов и других сценариев, требующих совместного использования кода, вы можете использовать плагин федерации модулей Webapck 5 для решения. Конкретные планы таковы:
3.1 Разобранные нами публичные зависимости делятся на две категории
3.1.1 Одной из них является базовая библиотека, такая как Regularjs, Regular-state, MUI, React, React Router и другие ресурсы, которые, как ожидается, не будут загружаться повторно в течение цикла доступа.
3.1.2 Другой тип — общедоступные компоненты.Например, компоненты React должны быть общими для подприложений, и нет необходимости копировать код между проектами.
3.2. Для вышеупомянутых двух типов зависимостей мы выполнили некоторую локальную практику, поскольку нет срочного спроса со стороны бизнеса, а Webpack 5 временно выпущен как стабильная версия (на момент написания этой статьи Webpack 5 выпустил релизную версию , и мы увидим конкретный бизнес в будущем.Будет ли требование онлайн в этой части функции), поэтому оно не было проверено в производственной среде, но вы можете поделиться методом обработки и результатами здесь.
3.2.1 Для первого типа публичных зависимостей мы реализуем общее ожидание: при интегрированном доступе основное приложение может динамически загружать библиотеку, от которой сильно зависит подприложение, а само подприложение больше не загружается. При независимом доступе само подприложение также может загружать нужные ему зависимости самостоятельно. Здесь необходимо решить два вопроса: а) как основное приложение собирает и динамически загружает зависимости подприложений б) как подприложение достигает различных представлений загрузки ресурсов во время интеграции и независимого доступа.
3.2.1.1. Первая проблема, нам нужно сохранить определение общих зависимостей, то есть определить общие ресурсы, от которых зависит каждое подприложение в основном приложении, и вставить их в глобальный хук жизненного цикла микроприложения qiankun перед загрузкой.<script>
Метод тега загрузки ресурсов Javascript, требуемых текущим подприложением, справочный код выглядит следующим образом.
// 定义子应用的公共依赖
const dependencies = {
live_backend: ['regular', 'restate'],
look_backend: ['react', 'react-dom']
};
// 返回依赖名称
const getDependencies = appName => dependencies[appName];
// 构建script标签
const loadScript = (url) => {
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.setAttribute('ignore', 'true'); // 避免重复加载
script.onerror = () => {
Message.error(`加载失败${url},请刷新重试`);
};
document.head.appendChild(script);
};
// 加载某个子应用前加载当前子应用的所需资源
beforeLoad: [
(app) => {
console.log('[LifeCycle] before load %c%s', 'color: green;', app.name);
getDependencies(app.name).forEach((dependency) => {
loadScript(`${window.location.origin}/${$config.rootPath}${dependency}.js`);
});
}
],
Здесь мы также должны обратить внимание на создание соответствующих зависимых ресурсов через Webpack.Мы используем плагин copy-webpack-plugin для преобразования ресурсов выпуска в node_modules в пакеты, к которым можно получить доступ через независимые URL-адреса.
// 开发
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('development')
}
}),
new webpack.NoEmitOnErrorsPlugin(),
new CopyWebpackPlugin({
patterns: [
{ from: path.join(__dirname, '../node_modules/regularjs/dist/regular.js'), to: '../s/regular.js' },
{ from: path.join(__dirname, '../node_modules/regular-state/restate.pack.js'), to: '../s/restate.js' },
{ from: path.join(__dirname, '../node_modules/react/umd/react.development.js'), to: '../s/react.js' },
{ from: path.join(__dirname, '../node_modules/react-dom/umd/react-dom.development.js'), to: '../s/react-dom.js' }
]
})
],
// 生产
new CopyWebpackPlugin({
patterns: [
{ from: path.join(__dirname, '../node_modules/regularjs/dist/regular.min.js'), to: '../s/regular.js' },
{ from: path.join(__dirname, '../node_modules/regular-state/restate.pack.js'), to: '../s/restate.js' },
{ from: path.join(__dirname, '../node_modules/react/umd/react.production.js'), to: '../s/react.js' },
{ from: path.join(__dirname, '../node_modules/react-dom/umd/react-dom.production.js'), to: '../s/react-dom.js' }
]
})
3.2.1.2 Что касается вторичной загрузки общедоступных зависимостей, когда подприложения интегрированы и доступны независимо, мы используем метод, который заключается в том, чтобы сначала скопировать общедоступные зависимости, определенные основным приложением, с помощью copy-webpack-plugin и html-webpack-externals- плагин Эти два плагина независимы внешним методом и не упакованы в бандл Webpack.При этом, через настройку плагина,<script>
Добавьте к тегу атрибут ignore, а затем используйте его, когда qiankun загружает это подприложение.Плагин import-html-entry, от которого зависит qiankun, анализирует его.<script>
теги, загруженные с атрибутом ignore, будут игнорироваться<script>
тег, и само подприложение может нормально загружать этот ресурс Javascript при независимом доступе.
plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: path.join(__dirname, '../node_modules/regularjs/dist/regular.js'), to: '../s/regular.js' },
{ from: path.join(__dirname, '../node_modules/regular-state/restate.pack.js'), to: '../s/restate.js' },
]
}),
new HtmlWebpackExternalsPlugin({
externals: [{
module: 'remoteEntry',
entry: 'http://localhost:3000/remoteEntry.js'
}, {
module: 'regularjs',
entry: {
path: 'http://localhost:3001/regular.js',
attributes: { ignore: 'true' }
},
global: 'Regular'
}, {
module: 'regular-state',
entry: {
path: 'http://localhost:3001/restate.js',
attributes: { ignore: 'true' }
},
global: 'restate'
}],
})
],
3.2.2. Для второго типа сценария совместного использования кода мы исследовали подключаемый модуль объединения модулей Webpack 5 и собрали общую информацию о ресурсах, ссылаясь на импортированные и экспортированные Webpack друг друга между приложениями, чтобы асинхронно загрузить общий код для достижения совместного использования кода.
3.2.2.1 Прежде всего, сценарий, определенный нашей практикой, таков: основное приложение lookcms предоставляет как компонент RButton на основе Regularjs, так и компонент TButton на основе React для совместного использования с субприложением liveadmin и субприложением увеличения соответственно.
3.2.2.2 Для основного приложения lookcms мы определяем подключаемый модуль объединения модулей Webpack5 следующим образом:
plugins: [
// new BundleAnalyzerPlugin(),
new ModuleFederationPlugin({
name: 'lookcms',
library: { type: 'var', name: 'lookcms' },
filename: 'remoteEntry.js',
exposes: {
TButton: path.join(__dirname, '../client/exports/rgbtn.js'),
RButton: path.join(__dirname, '../client/exports/rcbtn.js'),
},
shared: ['react', 'regularjs']
}),
],
Определенные компоненты общего кода показаны на следующем рисунке:
3.2.2.3 Для субприложения liveadmin мы определяем подключаемый модуль объединения модулей Webpack5 следующим образом:
plugins: [
new BundleAnalyzerPlugin(),
new ModuleFederationPlugin({
name: 'liveadmin_remote',
library: { type: 'var', name: 'liveadmin_remote' },
remotes: {
lookcms: 'lookcms',
},
shared: ['regularjs']
}),
],
С точки зрения использования подприложение должно сначала вставить исходный код в html какhttp://localhost:3000/remoteEntry.js
Запись общих ресурсов основного приложения может быть вставлена через html-webpack-externals-plugin, см. публичную зависимость внешней обработки подприложений выше.
Для загрузки внешних общих ресурсов подприложения загружаются асинхронно с помощью метода импорта Webpack, а затем вставляются в виртуальный DOM.Мы ожидаем реализовать Regularjs со ссылкой на решение React, предоставленное Webapck.К сожалению, Regularjs не имеет соответствующие основные функции помогают нам реализовать Lazy и Suspense.
После некоторых исследований мы выбрали условный рендеринг асинхронно загружаемых компонентов на основе API r-component, предоставляемого Regularjs.
Основная идея состоит в том, чтобы определить компонент Regularjs, который получает имя асинхронного компонента для загрузки из реквизита на этапе инициализации, загружает имя компонента, совместно используемое lookcms, с помощью метода импорта Webpack на этапе построения и добавляет его в RSuspense в соответствии с имя, определенное в свойствах. В компоненте логика отображения компонента RSusspense r-component также изменена для отображения компонента, привязанного по имени.
Из-за ограниченного написания грамматики Regularjs нам неудобно абстрагироваться от логики вышеперечисленных компонентов RSuspense, поэтому мы используем метод преобразования Babel: разработчик определяет оператор режима загрузки компонента и использует Babel AST для его преобразования. в компонент RSuspense. Наконец, используйте это в шаблоне RegularjsRSuspense
компоненты.
// 支持定义一个 fallback
const Loading = Regular.extend({
template: '<div>Loading...{content}</div>',
name: 'Loading'
});
// 写成一个 lazy 加载的模式语句
const TButton = Regular.lazy(() => import('lookcms/TButton'), Loading);
// 模版中使用 Babel AST 转换好的 RSuspense 组件
`<RSuspense origin='lookcms/TButton' fallback='Loading' />`
Преобразование синтаксиса с помощью Babel AST показано на следующем рисунке:
Фактический эффект работы показан на следующем рисунке:
3.2.2.4 Для вспомогательного приложения увеличения мы определяем подключаемый модуль федерации модулей Webpack 5 следующим образом:
plugins: [
new ModuleFederationPlugin({
name: 'lookadmin_remote',
library: { type: 'var', name: 'lookadmin_remote' },
remotes: {
lookcms: 'lookcms',
},
shared: ['react']
}),
],
С точки зрения использования, пожалуйста, обратитесь к официальной документации Webpack 5. Код выглядит следующим образом:
const RemoteButton = React.lazy(() => import('lookcms/RButton'));
const Home = () => (
<div className="m-home">
欢迎
<React.Suspense fallback="Loading Button">
<RemoteButton />
</React.Suspense>
</div>
);
Фактический эффект работы показан на следующем рисунке:
- Суммировать
4. Вопросы, требующие внимания
- Ресурсы разных источников Если ваше приложение реализует междоменную загрузку ресурсов другими способами, обратите внимание, что qiankun загружает все ресурсы подприложения через выборку, поэтому доступ к междоменным ресурсам необходимо осуществлять через CORS.
- HTML-теги для подприложений Возможно, некоторые атрибуты или некоторые функции установлены в теге html одного из ваших подприложений.Следует отметить, что qiankun удаляет теги html подприложений в фактической обработке.Поэтому, если требуется установить rem, пожалуйста использовать другие методы адаптации.
5. Будущее
- автоматизация Доступ к субприложению осуществляется через платформу, конечно, для этого требуется стандартный маршрут, по которому следует субприложение.
- совместное использование зависимостей Webpack 5 выпустил официальную версию, так что использование плагина федерации модулей можно поставить в график работы.
6. Резюме
Основываясь на реальных бизнес-сценариях, фон операции прямой трансляции LOOK использует qiankun для выполнения микро-интерфейсных инженерных разделений.Он бесперебойно работает в производственной среде в течение почти 4 месяцев.В процессе практики это правда, что установлены требования и реализована реализация доступа к qiankun.И есть некоторые трудности, возникающие на нескольких этапах развертывания приложения, такие как установление первоначальных требований, мы считали, что функция главного меню должна быть реализована, и часто встречающиеся ошибки в процессе доступа к qiankun, а также столкнулись с внутренними проблемами в процессе развертывания.Выбор и препятствия развертывания системы, к счастью, коллеги усердно работают, и проект может быть запущен и работать без сбоев.
использованная литература
Эта статья была опубликована сКоманда внешнего интерфейса NetEase Cloud Music, Любое несанкционированное воспроизведение статьи запрещено. Мы набираем front-end, iOS и Android круглый год.Если вы готовы сменить работу и любите облачную музыку, присоединяйтесь к нам на grp.music-fe(at)corp.netease.com!