Внешние элементы Webpack как в узле, так и в браузере

У меня есть изоморфное приложение React, которое работает как в браузере, так и на сервере. Я создаю один и тот же код для обоих, запуская две отдельные сборки Webpack через две разные точки входа и с разными конфигурациями.

Проблема в том, что внешний файл, существующий в окне браузера через внешний тег скрипта (в данном случае Карты Google), очевидно, не будет существовать при запуске в узле на сервере. Код идентичен, за исключением файла точки входа.

индекс.html:

// index.html
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=XXX"></script>

Упрощенная конфигурация:

// Server Webpack config
{
    entry: 'server.js',
    target: 'node',
    externals: {
         google: google
    }
}

// Client Webpack config
{
    entry: 'client.js',
    target: 'browser',
    externals: {
         google: google
    }
}

Компонент:

// The view which builds and runs fine in 
// the client but doesn't run on the server.
var React = require('react'),
    css = require('./style.css'),
    google = require('google'); // Nope, not on the server obviously!

var Component = React.createClass({

    render: function () {
        return (
            <div>
                // Do maps stuff
            </div>
        );
    }

});
module.exports = Component;

Мой вопрос в том, как мне справиться с этим?

Error: Cannot find module 'google'

В настоящее время у меня есть решение, которое мне совсем не нравится.

// Server Webpack config
{
    entry: 'server.js',
    target: 'node',
    externals: {
         google: google
    },
    plugins: [
        new webpack.DefinePlugin({ 'ENV.browser': false }),
    ]
}

// Client Webpack config
{
    entry: 'client.js',
    target: 'browser',
    externals: {
         google: google
    },
    plugins: [
        new webpack.DefinePlugin({ 'ENV.browser': true }),
    ]
}

// The component
var React = require('react'),
    css = require('./style.css');

if (ENV.browser) {
    var google = require('google');
}

var Component = React.createClass({

    render: function () {
        return (
            <div>
                if (ENV.browser) {
                    // Do maps stuff
                }
            </div>
        );
    }

});
module.exports = Component;

person Matt Derrick    schedule 14.05.2015    source источник


Ответы (1)


Вы можете использовать NormalModuleReplacementPlugin, чтобы заменить модуль на noop, согласно идее Dustan Kasten:

{
  plugins: [
    new webpack.NormalModuleReplacementPlugin(/^google$/, 'node-noop'),
  ],
}
person Alexandre Kirszenberg    schedule 14.05.2015
comment
Спасибо, это выглядит удобно! А как насчет того, когда я на самом деле начинаю получать доступ к свойствам внешнего объекта? Вы бы просто проверили его существование, прежде чем работать с ним? - person Matt Derrick; 14.05.2015
comment
Это сработает. NormalModuleReplacementPlugin позволяет перенаправить ваш запрос на любой модуль, так что вы также можете создать простой макет модуля и использовать его на сервере. Если вы ссылаетесь только на переменную google в методе жизненного цикла componentDidMount, это не имеет значения, так как она никогда не будет запущена при использовании React.renderToString. - person Alexandre Kirszenberg; 14.05.2015
comment
Я думал о заглушке, но это отличный момент насчет componentDidMount. Это то утешение, которое я искал. Благодарю вас! - person Matt Derrick; 15.05.2015