Использование 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. Это (правильно) помещает библиотеку как внешнюю и не будет пытаться связывать ее во время последовательности запроса require. Однако ожидание заключается в том, что библиотека вводится вручную.
- Я играл с идеей использования загрузчика псевдо script (при ударе по этому маршруту вручную создайте
<script>
с атрибутом src
, дождитесь его загрузки, а затем пусть компонент выполнит свою задачу. Это работает нормально, но на самом деле ужасно из-за ремонтопригодности (если нужны две или более библиотеки, мне тогда нужно реплицировать неуклюжую ручную загрузку script
) и, похоже, работает против "способа" webpack.
Соответствующие части конфигурации
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(),
],
};
Ответы
Ответ 1
https://github.com/hinok/webpack2-react-router-code-splitting
Репозиторий содержит webpack2
+ react-router v4
+ реализованный code splitting
с помощью динамического import() и загрузки внешних библиотек по требованию только один раз используя loadjs. В качестве примера он загружает Stripe.js
для определенного маршрута.
В репозитории вы можете найти два способа сделать разбиение кода
Это основано на официальной документации и техническом документе Webpack от Andrew Clark
При подготовке этого репозитория я обнаружил, что @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 каким-то образом с webpack. Напротив, я обнаружил, что он просто не работает.
@mc-zone вы можете загрузить модули AMD с помощью script.js. Но это не имеет ничего общего с webpack, это просто работает, потому что AMD была разработана для этого. Таким образом, вы не сможете использовать функции webpack внутри этих модулей AMD. webpack требует статического анализа времени сборки.
by @jhns см. в github
webpack не обрабатывает загрузку script. Для этого используйте отдельную библиотеку. я. е. https://github.com/ded/script.js/
by @sokra посмотреть на github
Если это невозможно, вы не должны требовать(), но загружаете его через script -loader, например script.js.
by @jhns см. в github
Связанные с этим проблемы в Github:
Сегодня я нашел статью Джеймса Кале о среде о своем проекте react-loadable, которые выполняют почти то же самое, что и компонент LazilyLoad
, но с обещанием, что
- он поддерживает рендеринг на стороне сервера с помощью динамического require()
- он с готовностью предварительно загружает компоненты при необходимости
Я настоятельно рекомендую проверить его.
Ответ 2
Если вы используете 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
для передачи модулей, которые вы хотите специально для этого маршрута.
Ответ 3
В этом вопросе есть несколько случаев, которые следует принять подробно.
Пусть начнется.
Наблюдения
- Вы должны переключить использование
react-router
из компонентов в объект PlainRoute
, это даст вам больше гибкости, когда дело доходит до выполняет разделение кода, а также пропускает создание компонента <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
};
Ответ 4
Возможно, вы захотите изучить:
https://github.com/faceyspacey/webpack-flush-chunks
https://github.com/faceyspacey/require-universal-module
Последний представляет собой пакет общего назначения, предназначенный для создания модулей, которые нуждаются в BOTH синхронно и асинхронно, требуя других модулей.
Этот пакет - это то, что вы используете для очистки узлов Webpack, которые были определены как необходимые по запросу:
https://github.com/faceyspacey/webpack-flush-chunks
Это не относится к конкретному маршрутизатору, что, вероятно, означает, что он более прост и требует меньше обходных решений.