Когда приложение "SPA" встречает раздутый проект

Vue.js

Добавить Автора

задний план

По мере роста проекта одна страницаspaПостепенно включает множество направлений бизнеса

  • Система торговых центров
  • Система послепродажного обслуживания
  • Система членства
  • ...

Когда страница проекта превышает определенное количество (150+), возникает ряд проблем.

  • Масштабируемость

Время компиляции проекта (запуск сервера, изменение кода) становится все больше и больше, и каждая отладка может быть сосредоточена только на 1 или 2 страницах.

  • противоречивые потребности

Все требования находятся в текущем git, а тестовая среда часто ставится в очередь из-за слишком большого количества требований.

Исходя из вышеперечисленных проблем, существуют технические требования к разделению git. детали следующим образом

Цель

  • ещеspa

Поскольку улучшение — это среда разработки, мы, конечно, не хотим дробить проект, чтобы повлиять на пользовательский опыт. Если бизнес-направление полностью разделено на две отдельные страницы, пользователи больше не смогут плавно переключаться между бизнес-направлениями, потому что все фреймворки и статические ресурсы будут перезагружаться при переключении страниц. Поэтому при переходе на бизнес-линию необходимо оставаться в спа-центре.不刷新页面, поделиться одной и той же записью страницы;

  • Бизнес-страницы больше не загружают ресурсы повторно

Поскольку большинству направлений бизнеса необходимо использовать структуру (vue, vuex...), общедоступные компоненты (dialog,toast) уже загружены на входе spa, и я не хочу, чтобы бизнес-линия загружала эти ресурсы повторно. Бизнес-проекты должны содержать только собственные уникальные ресурсы и иметь возможность использовать общедоступные ресурсы;

  • Полное распределение ресурсов между бизнес-направлениями

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

Требования такие же, как и выше, а метод реализации описан ниже.

техническая база

  • vue: 2.4.2
  • vue-router: 2.7.0
  • vuex: 2.5.0
  • webpack: 4.7.0

выполнить

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

  • Основной проект: Содержит основные страницы системы + различные необходимые фреймворки (vue, vuex...)
  • hello project: содержит собственный внутренний бизнес-код hello

Перейти к процессу страницы приветствия

  1. Пользователь посещает бизнес-страницу Маршрутизация#/hello/index;
  2. Роутер основного проекта не подобран, перейти в паблик*иметь дело с;
  3. Публичный маршрутизатор определяет, что текущий маршрут является маршрутом приветствия бизнес-линии, и запрашивает запись приветствия.bundle js;
  4. Во время выполнения приветственной записи js зарегистрируйте собственный маршрутизатор и сохраните его в основном проекте;
  5. После завершения регистрации отметьте текущую бизнес-линию hello как зарегистрированную;
  6. Затем маршрут вызывает следующий. Он будет автоматически продолжать запрашивать страницу, соответствующую #/hello/index.chunk(js, css) успешный переход на страницу;
  7. На данный момент hello интегрирован с основным проектом, hello может свободно использовать все магазины и использовать маршрутизатор для свободного перехода на любую страницу. Готово

Это функции, которые вам нужны.Давайте рассмотрим конкретную реализацию шаг за шагом.

Запрос бизнес-маршрутизации (шаг 1)

первый запрос#/hello/indexВ настоящее время все маршруты в маршрутизаторе не могут быть сопоставлены и станут общедоступными.*иметь дело с

/** 主项目 **/
const router = new VueRouter({
  routes: [
    ...
    // 不同路由默认跳转链接不同
    {
      path: '*',
      async beforeEnter(to, from, next) {
        // 业务线拦截
        let isService = await service.handle(to, from, next);

        // 非业务线页面,走默认处理
        if(!isService) {
          next('/error');
        }

      }
    }
  ]
});

Инициализация направления бизнеса (шаг 2, шаг 3)

Во-первых, требуется глобальная конфигурация бизнес-линии для хранения входных js-файлов каждой бизнес-линии.

const config = {
    "hello": {
        "src": [
          "http://local.aaa.com:7000/dist/dev/js/hellobundle.js"
        ]
    },
    "其他业务线": {...}
}

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

/** 主项目 **/
// 业务线接入处理
export const handle = async (to, from, next) => {
  let path = to.path || "";
  let paths = path.split('/');
  let serviceName = paths[1];

  let cfg = config[serviceName];

  // 非业务线路由
  if(!cfg) {
    return false;
  }

  // 该业务线已经加载
  if(cfg.loaded) {
    next();
    return true;
  }

  for(var i=0; i<cfg.src.length; i++) {
    await loadScript(cfg.src[i]);
  }
  cfg.loaded = true;
  next(to);  // 继续请求页面
  return true;
}

Несколько замечаний

  • Общая конфигурация бизнес-линии хранится в бэкэнде, который указан здесь для иллюстрации.
  • Направление бизнеса загружается только один раз,loadedдля условия суда. Если загружено, переходите непосредственно к следующему
  • Когда первая линия бизнеса успешно загружена, основной проект уже содержит#/hello/indexмаршрут, то следующий может прыгать нормально. См. следующий раздел по причинам

Работа, проделанная записью hello entry.js (шаг 4)

В целях экономии ресурсов линия hello business больше не упаковывается повторно.vue,vuexи т.д. В основном проекте загружен фреймворк.

Затем, чтобы hello работал правильно, основной проект должен передать указанный выше фреймворк в hello, напрямую повесив соответствующие переменные вwindow:

/** 主项目 **/
import Vue from 'vue';
import { default as globalRouter } from 'app/router.js'; 2个需要动态赋值
import { default as globalStore } from 'app/vuex/index.js';
import Vuex from 'vuex'

// 挂载业务线数据
function registerApp(appName, {
  store,
  router
}) {
  if(router) {
    globalRouter.addRoutes(router);
  }
  if(store) {
    globalStore.registerModule(appName, Object.assign(store, {
      namespaced: true
    }));
  }
}

window.bapp = Object.assign(window.bapp || {}, {
  Vue,
  Vuex,
  router: globalRouter,
  store: globalStore,
  util: {
    registerApp
  }
});

УведомлениеregisterAppЭтот метод — это метод монтирования, который интегрирует hello с основным проектом и вызывается бизнес-линией.

На предыдущем шаге entry.js из hello запускался нормально, поэтому давайте посмотрим, что делает hello в entry:

/** hello **/
import App from 'app/pages/Hello.vue'; // 路由器根实例
import {APP_NAME} from 'app/utils/global';
import store from 'app/vuex/index';

let router = [{
  path: `/${APP_NAME}`,
  name: 'hello',
  meta: {
    title: '页面测试',
    needLogin: true
  },
  component: App,
  children: [
    {
      path: 'index',
      name: 'hello-index',
      meta: {
        title: '商品列表'
      },
      component: resolve => require.ensure([], () => resolve(require('app/pages/goods/Goods.vue').default), 'hello-goods')
    },
    {
      path: 'newreq',
      name: 'hello-newreq',
      meta: {
        title: '新品页面'
      },
      component: resolve => require.ensure([], () => resolve(require('app/pages/newreq/List.vue').default), 'hello-newreq')
    },
  ]
}]

window.bapp && bapp.util.registerApp(APP_NAME, {router, store});

Несколько замечаний

  • APP_NAMEЭто уникальный идентификатор бизнес-линии, то есть привет
  • Направления бизнеса имеют свои внутренниеrouterа такжеstore
  • Активная линия деловых звонковregisterApp, интегрируйте собственный роутер и магазин с основным проектом
  • Когда магазин интегрирован, его нужно добавитьnamespace: true, потому что в это время весь магазин линии hello business стал модулем globalStore
  • addRoutesа такжеregisterModuleЭто метод динамической регистрации маршрутизатора и хранилища.
  • разгромленnameдолжен быть уникальным для основного проекта

Обновление конфигурации бизнес-направления

Конфигурация бизнес-линии должна быть обновлена ​​после завершения каждого компиляции Hello. Обновление разделено на本地调试更新а также线上更新.

  • 本地调试更新Просто нужно обновить локальный файл конфигурацииservice-line-config.json, а затем файл читается основным проектом и возвращается в js при запросе бизнес-конфигурации.
  • 线上更新Это проще, после каждого релиза и компиляции обновлять полный url текущей записи js+md5 на бэкэнд

выше, см. использованиеwebpack-pluginБолее подходящая для текущей сцены реализация выглядит следующим образом

class ServiceUpdatePlugin {
  constructor(options) {
    this.options = options;
    this.runCount = 0;
  }

  // 更新本地配置文件
  updateLocalConfig({srcs}) {
    ....
  }

  // 更新线上配置文件
  uploadOnlineConfig({files}) {
    ....
  }

  apply(compiler) {
    // 调试环境:编译完毕,修改本地文件
    if(process.env.NODE_ENV === 'dev') {
      // 本地调试没有md5值,不需要每次刷新
      compiler.hooks.done.tap('ServiceUpdatePlugin', (stats) => {
        if(this.runCount > 0) {
          return;
        }
        let assets = stats.compilation.assets;
        let publicPath = stats.compilation.options.output.publicPath;
        let js = Object.keys(assets).filter(item => {
          // 过滤入口文件
          return item.startsWith('js/');
        }).map(path => `${publicPath}${path}`);

        this.updateLocalConfig({srcs: js});
        this.runCount++;
      });
    }
    // 发布环境:上传完毕,请求后端修改
    else {
      compiler.hooks.uploaded.tap('ServiceUpdatePlugin', (upFiles) => {
        let entries = upFiles.filter(file => {
          return file &&
            file.endsWith('js') &&
            file.includes('js/');
        });

        this.uploadOnlineConfig({files: entries});
        return;
      })

    }
  }
}

Уведомление,uploadedСобытие генерируется подключаемым модулем загрузки статических ресурсов нашей проектной группы, и оно передает полный путь ко всем загружаемым в данный момент файлам. Вам нужно дождаться загрузки файла в CDN, прежде чем обновлять бизнес-линию.

Затем используйте его в веб-пакете

/** hello **/
{
  ...
  plugins: [
    // 业务线js md5更新
    new McServiceUpdatePlugin({
      app_name,
      configFile: path.resolve(process.cwd(), '../mainProject/app/service-line-config.json')
    })
  ],
  ...
}

Обратите внимание, что при локальной отладке бизнес-конфигурация主项目будет использоваться только, поэтому непосредственно обновите файл конфигурации в главном каталоге проекта

Отладочный выпуск

На основе вышеуказанного плагина есть следующие эффекты

Процесс отладки выглядит следующим образом:
  1. Запустите основной сервер проекта (порт7777);
  2. Запустите сервер hello business line (порт7000), в это время успешный запуск одновременно обновит локальный файлservice-line-config.json;
  3. Посетите страницу приветствия, загрузите локальную конфигурацию, загрузите7000Статические ресурсы, предоставляемые портом (например, http://local.aaa.com:7000/dist/dev/js/hellobundle.js)
Процесс тестирования выпуска следующим образом:
  1. воплощать в жизньnpm run test
  2. В процессе выполнения файл будет загружен, а бизнес-конфигурация тестовой среды будет обновлена.
  3. На данный момент доступ к странице тестовой среды обновлен.

Видно, что hello-релиз более легковесный, чем основной проект, потому что бизнес-линия обновляет только интерфейс, а основному проекту нужно обновить html-веб-сервис для выпуска.

резюме

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

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

Возникшие проблемы и решения

Wepback упаковка линии hello business

  • Направления бизнеса требуют отдельных пространств имен пакетов
  • Для того, чтобы отличить его от основного проекта, он будет приветствовать бизнес-линиюbundleПереименовано, добавлен префикс названия компании
  • Чем меньше входных файлов, тем лучше, поэтому удалена некоторая конфигурация упаковки.
    • удаленvendor: основная сторонняя библиотека загружается основным проектом
    • удаленdll: ресурсы dll загружаются основным проектом
    • Удалена среда выполнения(manifest) конфигурация: каждое направление бизнеса будет обрабатывать загрузку зависимостей отдельно
/** hello **/
{
  ...
  entry: {
    [app_name + 'bundle']: path.resolve(SRC, `entry.js`)
  },
  output: {
    publicPath: `http://local.aaa.com:${PORT}${devDefine.publicPath}`,
    library: app_name // 业务线命名空间
  },
  ...
  optimization: {
    runtimeChunk: false, // 依赖处理与bundle合并
    splitChunks: {
      cacheGroups: false // 业务线不分包
    }
  },
  ...
}

УведомлениеlibraryНастройки изолируют отдельные направления бизнеса входной файл

image

полагаться

image
image

проблема разделения роутера

Первоначально используйте /:name для публичной обработки.

Однако обнаружено, что приоритет маршрутизатора находится в порядке вставки массива, тогда приоритет вставленного позднее маршрута hello всегда будет ниже, чем маршрут /:name.

использовать после*Выполнение публичной обработки всегда будет в сухом остатке, и проблема будет решена.

разделение магазина

Хранилище hello зарегистрировано как модуль globalStore и должно быть помеченоnamespaced: true, иначе данные будут недоступны;

В магазине используется один и тот же базовый и основной проект:

/** hello **/

let { Vuex } = bapp;
// 全局store获取
let { mapState: gmapState, mapActions: gmapActions, createNamespacedHelpers } = Vuex;
// 本业务线store获取
const { mapState, mapActions } = createNamespacedHelpers(`${APP_NAME}/feedback`)

export default {
  ...
  computed: {
    ...gmapState('userInfo', {
      userName: state => state.userName
    }),
    ...gmapState('hello/feedback', {
      helloName2: state => state.helloName
    }),
    ...mapState({
      helloName: state => state.helloName
    })
  },
}

разделение интерфейса

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

общественное использование

Вы можете использовать глобальный напрямую组件,mixins,directives, который можно использовать напрямуюfont. Локальный связанный контент должен быть скопирован в hello или открыт для hello, прежде чем его можно будет использовать. Изображение вообще нельзя использовать повторно

локальный серверный инструмент

Основной проект должен иметь относительно хорошую работу с запросом, поэтому он реализован нами.expressдля отладки локально.

Но единственная функция проекта hello — предоставлять локальные текущие js и css, поэтому используйте официальныйdevServerдостаточно.


выше, спасибо за чтение

Оригинальная ссылка:Фирменные блюда Овощи сливы Талант/деталь/75,Вы также можете найти апплет "Техническая группа по продуктам Meicai" в WeChat. Он полон галантерейных товаров и обновляется каждую неделю. Если вы хотите изучить технологии, не пропустите его.