it-roy-ru.com

Используя React Router и Webpack 2, как требовать внешние библиотеки только на определенных маршрутах?

В данный момент работает

С помощью Webpack 2 и React Router v4 я смог настроить разделение рабочего кода. Существует промежуточный <AsyncComponent>, который разрешает обещание и возвращает компонент (шаблон, обнаруженный при проблемах с github).

Пример набора маршрутов ниже:

<Switch>
    <Route 
        path="me" 
        render={(props) => 
            <AsyncComponent promise={ require.ensure([], (require) => require('./modules/Profile'), 'profile') } props={props} />
        } 
    />
    <Route 
        path="credit-card" 
        render={(props) => 
            <AsyncComponent promise={ require.ensure([], (require) => require('./modules/CreditCard'), 'credit-card') } props={props} />
        } 
    />
</Switch>

Задача

Я хотел бы расширить это, и для только определенных маршрутов , загружать в дополнительные библиотеки. В приведенном выше примере я хотел бы получить библиотеку StripeJS ( https://js.stripe.com/v2/ ) только на маршрут кредитной карты.

Хочу подчеркнуть, что я могу загрузить Stripe прямо в нижний колонтитул и все работает нормально. Существует несколько библиотек, я использую Stripe в качестве простого примера.

Попытка

Следующие попытки были предприняты с небольшим успехом:

  • Маркировка lib external в конфигурации webpack. Это (правильно) помечает библиотеку как внешнюю и не будет пытаться связать ее во время последовательности разрешения требования. Однако ожидается, что библиотека будет введена вручную.
  • Я поиграл с идеей использования загрузчика псевдосценариев (при переходе по этому маршруту вручную создайте <script> с атрибутом src, дождитесь его загрузки, затем дайте компоненту сделать свое дело. Это работает нормально, но на самом деле ужасно с возможность сопровождения (если нужны две или более библиотек, мне нужно повторить неуклюжую ручную script load) точку зрения, и, похоже, она работает против "пути" веб-пакета.

Соответствующие части конфига

const core = [
    'lodash',
    'react',
    'react-dom',
    'axios',
    'react-router-dom',
];

const config = {
    context: path.resolve(__dirname, './ts_build'),
    node: {
        fs: "empty"
    },
    entry: {
        app: './app.js',
        core: core,
    },
    output: {
        filename: '[name].js',
        chunkFilename: '[name].[id].chunk.js',
        path: path.resolve(__dirname, './../../public'),
        publicPath: 'http://example.org/',
    },
    resolve: {
        modules: [
            path.resolve('./src'),
        ],
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            names: ['core'], 
            minChunks: Infinity,
        }),
        new webpack.NamedModulesPlugin(),
    ],
};
16
Chris

https://github.com/hinok/webpack2-react-router-code-splitting

Репозиторий содержит webpack2 + react-router v4 + реализованный code splitting с использованием динамического import () и загрузку внешних библиотек по требованию только один раз с помощью loadjs . Как пример, он загружает Stripe.js для определенного маршрута.

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

Он основан на официальной документации Webpack и Gist Эндрю Кларка

При подготовке этого хранилища я обнаружил, что @Chris хотел бы загружать Stripe.js только для определенных маршрутов, которые размещены на внешнем CDN. С тех пор я думал о том, как лучше использовать модули AMD и избежать утечек глобальных переменных, но это немного сложно, потому что каждый модуль AMD может иметь разные оболочки, говоря обертку, я имею в виду что-то вроде:

(function (root, factory) {
    if (typeof define === 'function' && define.AMD) {
        define(['b'], function (b) {
            return (root.amdWebGlobal = factory(b));
        });
    } else {
        root.amdWebGlobal = factory(root.b);
    }
}(this, function (b) {
    return {};
}));

Я мог бы сказать, что UMD обертка является своего рода стандартом, но иногда люди предпочитают менее обдуманные обертки, или они просто не заботятся о других средах. Я упоминаю об этом, потому что в истории git вы можете найти commit под названием Amdify , который стал еще одним доказательством концепции, как AMD env может быть смоделирована. Наконец, я решил удалить его, потому что он далек от идеала и не охватывает все случаи использования и крайние случаи.

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

@ mc-zone вы сможете загружать модули AMD с помощью script.js. Но это не имеет ничего общего с веб-пакетом, оно просто работает, потому что AMD была разработана для этого. Таким образом, вы не сможете использовать функции веб-пакетов внутри этих модулей AMD. Веб-пакет требует статического анализа во время сборки.

@jhns смотрите на github

webpack не обрабатывает загрузку скриптов. Используйте для этого отдельную библиотеку. я. е. https://github.com/ded/script.js/

@sokra смотрите на github

Если это невозможно, вы не должны требовать (), а загружать его через загрузчик скриптов, такой как script.js.

@jhns смотрите на github

Связанные проблемы на Github:


Сегодня я нашла статью Джеймса Кейла на среде о своем проекте реагирующая загрузка которая делает почти то же самое, что и компонент LazilyLoad, но с обещанием, что

  • поддерживает рендеринг на стороне сервера через динамический require ()
  • он с готовностью загружает компоненты при необходимости

Я настоятельно рекомендую проверить это.

3
hinok

Вы можете посмотреть на:

https://github.com/faceyspacey/webpack-flush-chunks

https://github.com/faceyspacey/require-universal-module

Последний представляет собой пакет общего назначения, предназначенный для создания модулей, которым требуется ОБА синхронно и асинхронно, требующих других модулей. 

Этот пакет используется для очистки блоков Webpack, которые были определены в запросе:

https://github.com/faceyspacey/webpack-flush-chunks

Это не специфично для React Router, что, вероятно, означает, что он более прост и требует меньше обходных путей. 

0
faceyspacey.com

Если вы используете Webpack 2, вы используете import() в вашем конфигурационном файле React Router

export default {
 component: App,
 childRoutes: [
   {
     path: '/',
     getComponent(location, cb) {
       import('external-library-here')
       .then(function(){
         return System.import('pages/Home');
       })
      .then(loadRoute(cb)).catch(errorLoading);
     }
   },
   {
     path: 'blog',
     getComponent(location, cb) {
       import('pages/Blog').then(loadRoute(cb)).catch(errorLoading);
     }
   },
   {
     path: 'about',
     getComponent(location, cb) {
       import('pages/About').then(loadRoute(cb)).catch(errorLoading);
     }
   },
 ]
};

Вы также можете использовать реквизиты getComponent или getComponents в компоненте Router для передачи модулей, которые вы хотите специально для этого маршрута.

0
redconservatory

В этом вопросе есть несколько случаев, которые следует рассмотреть подробно.

Давайте начнем.

Наблюдения

  • Вам следует переключить использование react-router с компонентов на PlainRoute object, это даст вам больше гибкости, когда дело доходит до разделения кода, а также пропустит создание компонента <AsyncComponent>
  • Я уверен, что в вашем маршруте будет больше вложенных компонентов, так что, если вложенному компоненту в маршруте credit-card нужна библиотека?

Предложения

  • Используйте ваш компонент маршрута в качестве точки входа, чтобы он не стал огромной реализацией в одном компоненте
  • Учитывая приведенное выше предложение, вы можете добавить туда индекс с вашими библиотечными зависимостями и использовать их в этом маршруте (в ваших вложенных компонентах)
  • Вы не должны импортировать эти библиотеки в любую часть кода, в противном случае эта функциональность нарушается
  • Как только вы загрузите эти библиотеки, вставьте их в объект окна. и ваши вложенные компоненты будут иметь глобальный доступ к вашим библиотекам

Это предложение не было реализовано, но я пытаюсь направить вас в правильном направлении относительно вашего вопроса

В конце концов, это подход, который я предложил:

import App from 'components/App';

function errorLoading(err) {
  console.error('Dynamic page loading failed', err);
}

function loadRoute(cb) {
  return (module) => cb(null, module.default);
}

function injectLibraries(libraries) {
  Object.keys(libraries).forEach(key => {
    window[key] = libraries[key];
  });
}

export default {
  component: App,
  childRoutes: [
    {
      path: '/(index.html)',
      name: 'home',
      getComponent(location, cb) {
        const importModules = Promise.all([
          import('components/HomePage/'),
          import('components/HomePage/libraries'),
        ]);

        const renderRoute = loadRoute(cb);

        importModules.then(([component, libraries]) => {
          injectLibraries(libraries);
          renderRoute(component);
        });

        importModules.catch(errorLoading);
      },
    },
    {
      path: '/credit-card',
      name: 'credit-card',
      getComponent(nextState, cb) {
        const importModules = Promise.all([
          import('components/CreditCardPage/'),
          import('components/CreditCardPage/libraries'),
        ]);

        const renderRoute = loadRoute(cb);

        importModules.then(([component, libraries]) => {
          injectLibraries(libraries);
          renderRoute(component);
        });

        importModules.catch(errorLoading);
      },
    },
  ],
};

Ваш файл библиотеки должен выглядеть так:

import stripe from 'stripe';

export default {
  stripe 
};
0
Jose Paredes