Статья впервые опубликована в моем блогеGitHub.com/MCU король/Но...
адрес проекта:
задний план
Для крупномасштабных интерфейсных проектов, таких как внутренняя система управления компании (как правило, включая OA, HR, CRM, резервирование встреч и другие системы), если все предприятия помещаются во внешний проект, поскольку бизнес-функции продолжают увеличиваться. , это приведет к следующим проблемам:
-
Большой размер кода приводит к длительному времени компиляции и снижению скорости разработки и упаковки.
-
Файлов проекта становится все больше и больше, что затрудняет поиск связанных файлов.
-
Небольшое изменение в бизнесе приводит к упаковке и развертыванию всего проекта
Технические решения
preload-routes и async-routes — это микро-интерфейсные решения, используемые в настоящее время командой автора, которые в конечном итоге разберут весь внешний проект на основной проект и несколько подпроектов, две функции которых следующие:
-
Основной проект: используется для управления переключением маршрутизации подпроектов, регистрации маршрутизации и уровня глобального хранилища подпроектов, предоставления глобальных библиотек и методов.
-
Компонент: подпрокат код службы развития бизнеса, подлина, соответствующая подслуги и содержит два конца (PC + Mobile) код и слой мультиплексирования кода (элемент без просмотра в уровне иерархии)
Как показано, вся передняя часть множества разделенных подпозиций в соответствии с сервисной строкой, независимая от кода каждого подсклада, содержит только одну служебную линию, которая может быть разработана и развернута независимо, чтобы уменьшить сложность затрат на обслуживание позиции.
Благодаря этому решению наш интерфейсный проект сохраняет не только горизонтальную масштабируемость (несколько подпроектов), но и возможность повторного использования по вертикали (один подпроект). Так как же работает этот план? Механизм реализации схемы подробно описан ниже.
Прежде чем объяснять, сначала ясно, что есть два способа реализовать эту схему: маршрутизация с предварительной загрузкой и маршрутизация с отложенной загрузкой.Далее мы представим механизмы реализации этих двух методов.
Предварительно загрузить маршруты
1. ПодпроектыУпаковка в соответствии с режимом библиотеки vue-cli 3для последующих основных проектных ссылок
Примечание. В режиме библиотеки Vue является внешним. Это означает, что в пакете не будет Vue, даже если вы импортируете Vue в свой код. Если библиотека будет использоваться через упаковщик, она попытается загрузить Vue как зависимость через упаковщик; в противном случае она вернется к глобальной переменной Vue.
2. При компиляции основного проектаВставить файл входа main.js подпроекта в html основного проекта в виде тега скрипта через плагин InsertScriptPlugin
Примечание: обязательно поместите тег сценария, соответствующий входному файлу main.js подпроекта, над тегом сценария входного файла app.js основного проекта, чтобы гарантировать, что входной файл подпроекта будет выполнен до код входного файла основного проекта. Следующие шаги покажут вам, почему.
Еще одно замечание: скомпилированный main.js файла входа проекта в локальной среде разработки хранится в памяти, поэтому на диске его не видно, но к нему можно получить доступ.
Основной код InsertScriptPlugin выглядит следующим образом:
compiler.hooks.compilation.tap('InsertScriptWebpackPlugin', (compilation) => {
compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing.tap(
'InsertScriptWebpackPlugin',
(htmlPluginData) => {
const {
assets: { js }
} = htmlPluginData;
// 将传入的 js 以 script 标签形式插入到 html 中
// 注意:需要将子项目的入口文件 main.js 放在主项目入口文件 app.js 之前,因为需要子项目提前将自己的 route list 注册到全局上
js.unshift(...self.files);
}
);
});
3. HTML-код основного проекта должен иметь доступ к скомпилированным js/css и другим ресурсам в подпроекте.переадресация через прокси
- Если вы разрабатываете локально, вы можете использовать прокси, предоставляемый webpack, например:
const PROXY = {
'/app-a/': {
target: 'http://localhost:10241/'
}
};
- Если он развернут в сети, его можно перенаправить через nginx или можно поместить упакованный основной проект и подпроекты в папку и ссылаться на них по относительным путям.
4. Когда браузер анализирует html, он анализирует и выполняет входной файл main.js подпроекта,Зарегистрируйте список маршрутов подпроекта в Vue.__share__.routes., чтобы последующие мастер-проекты могли включить его в общую маршрутизацию.
Код main.js подпроекта выглядит следующим образом: (Чтобы минимизировать ресурсы, загружаемые при первом рендеринге главной страницы проекта, файл входа подпроекта рекомендуется монтировать только маршрутно)
import Vue from 'vue';
import routes from './routes';
const share = (Vue.__share__ = Vue.__share__ || {});
const routesPool = (share.routes = share.routes || {});
// 将子项目的 route list 挂载到 Vue.__share__.routes 上,以便后续主项目将其合并到总的路由中
routesPool[process.env.VUE_APP_NAME] = routes;
5. Продолжайте парсить html вниз, парсить и выполнять в основной проект main.js,Получите список маршрутов всех подпроектов из Vue.__share__.routes и объедините их в общую таблицу маршрутизации., затем инициализируйте экземпляр vue-router и передайте его в новый Vue.
Соответствующие ключевые коды следующие
// 从 Vue.__share__.routes 获取所有子项目的 route list,合并到总的路由表中
const routes = Vue.__share__.routes;
export default new Router({
routes: Object.values(routes).reduce((acc, prev) => acc.concat(prev), [
{
path: '/',
redirect: '/app-a'
}
])
});
На данный момент одностраничное приложение разделено на несколько подпроектов в зависимости от бизнеса, Грубо говоря, входной файл main.js подпроекта является мостом, соединяющим основной проект и подпроект.
Кроме того, если вам нужно использовать vuex, порядок vue-router прямо противоположный (сначала основной проект, затем подпроект):
1. Сначала документ основного проекта на входе для инициализации экземпляра хранилища нового Vuex.Store, затем висит на Vue.__share__.store
2. Затем получен вызов, и Vue.__share__.store store.registerModule ('app-x', store) App.vue в подпрограмме, подпрограмма регистрируется как подмодуль для хранения хранилища.
ленивые маршруты загрузки
Маршрутизация отложенной загрузки, как следует из названия, означает, что когда пользователь щелкает, чтобы войти в модуль подпроекта, он определяет, какой это подпроект, анализируя маршрут для перехода, а затем асинхронно загружает входной файл main.js подпроекта (который можно получить черезsystemjsИли напишите метод, который динамически создает теги сценария и вставляет их в тело). После успешной загрузки маршрут подпроекта может быть динамически добавлен к общему маршруту основного проекта.
1. Основной проект router.js определен в файлеПерехватите маршрут в хуке beforeEach vue-router, проанализируйте, какой подпроект необходим в соответствии с маршрутом, который нужно перепрыгнуть, а затем асинхронно загрузите соответствующий файл записи подпроекта., следующий основной код:
const cachedModules = new Set();
router.beforeEach(async (to, from, next) => {
const [, module] = to.path.split('/');
if (Reflect.has(modules, module)) {
// 如果已经加载过对应子项目,则无需重复加载,直接跳转即可
if (!cachedModules.has(module)) {
const { default: application } = await window.System.import(modules[module])
if (application && application.routes) {
// 动态添加子项目的 route-list
router.addRoutes(application.routes);
}
cachedModules.add(module);
next(to.path);
} else {
next();
}
return;
}
});
2. Подпроект импортных документов нужно только main.jsПредоставление маршрутов подпроекта основному проектуВот и все, код такой:
import routes from './routes';
export default {
name: 'javascript',
routes,
beforeEach(from, to, next) {
console.log('javascript:', from.path, to.path);
next();
},
}
Примечание: в дополнение к предоставлению метода route также доступен метод beforeEach.Фактически, он предназначен для поддержки ограничений прав доступа к страницам для подпроектов с помощью защиты маршрутизации.Основной проект получает метод beforeEach этого подпроекта, который может быть выполняется в хуке beforeEach vue-router, см. код async-routes.
В дополнение к другому взаимодействию между основным проектом и подпроектами прокси-переадресация ресурсов подпроекта, регистрация хранилища vuex и т. д. точно такие же, как и маршруты предварительной загрузки выше.
Преимущества и недостатки
Вот плюсы и минусы такого подхода:
преимущество
-
Подпроекты могут быть упакованы и развернуты отдельно, что повышает скорость разработки и упаковки.
-
Разработка подпроектов не зависит друг от друга и не влияет друг на друга, а также может вестись на разных складах, уменьшая масштаб одного проекта
-
Сохраняйте опыт одностраничного приложения и переключайтесь между подпроектами без обновления
-
Низкая стоимость обновления, минимальное вмешательство в существующие проекты и низкая стоимость миграции бизнес-направлений
-
Убедитесь, что весь проект унифицирован с технологическим стеком
недостаток:
- Основной проект и подпроекты должны совместно использовать экземпляр Vue, поэтому невозможно использовать последнюю версию Vue (например, Vue3) или React только для подпроекта.
Ответы на некоторые вопросы
1. Если код подпроекта обновляется, помимо упаковки и развертывания подпроекта нужно ли упаковывать и развертывать основной проект?
Главный проект развертывания обновлять не нужно. Здесь есть хитрость, о которой я забыл упомянуть выше, то есть в запакованный файл входа подпроекта не добавляется chunkhash, это напрямую main.js (у остальных js подпроекта есть chunkhash). То есть основному проекту нужно только запомнить имя подпроекта, чтобы найти файл входа подпроекта через subapp-name/main.js, поэтому после того, как подпроект упакован и развернут, основному проекту не нужно обновить что-либо.
2. По второму вопросу, если файл входа подпроекта main.js не использует chunkhash, как предотвратить постоянное кеширование файла?
На стороне сервера статических ресурсов вы можете настроить принудительный кэш так, чтобы он не кэшировался для файла записи подпроекта.Ниже приведена соответствующая конфигурация, когда сервер nginx:
location / {
set $expires_time 7d;
...
if ($request_uri ~* \/(contract|meeting|crm)-app\/main.js(\?.*)?$) {
# 针对入口文件设置 expires_time -1,即expire是服务器时间的 -1s,始终过期
set $expires_time -1;
}
expires $expires_time;
...
}
конец
Если нет необходимости использовать несколько технологических стеков в масштабном front-end проекте, я все же рекомендую это решение, практикуемое в настоящее время автором. Кроме того, если это стек технологий React, подобное решение может быть реализовано по этой идее.