Как я могу предварительно отрендерить приложение react в gulp / node?

#javascript #node.js #reactjs #gulp

#javascript #node.js #reactjs #gulp

Вопрос:

Как я могу программно отобразить приложение react в gulp и node 12?

Я перенял и обновил старое приложение react (0.12.0) до последней версии. Это также включало обновление до ES6. Сам код react выполнен, но нам также необходимо выполнить предварительный рендеринг приложения (приложение представляет собой интерактивную документацию и должно просматриваться поисковыми системами).

Ранее процесс сборки gulp запускал browserify для кода, а затем запускал его с помощью vm.runInContext:

 // source code for the bundle
const component = path.resolve(SRC_DIR   subDir, relComponent);

vm.runInNewContext(
  fs.readFileSync(BUILD_DIR   'bundle.js')   // ugly
    'nrequire("react").renderToString('  
    'require("react").createElement(require(component)))',
  {
    global: {
      React: React,
      Immutable: Immutable,
    },
    window: {},
    component: component,
    console: console,
  }
);
  

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

Я упаковал специальный серверный скрипт ниже с помощью browserify amp; babel, а затем запустил его с помощью runInNewContext. Он не завершается сбоем, но также не выводит никакого кода, он просто регистрирует пустой объект

 import React from 'react';
import { renderToString } from 'react-dom/server';
import App from './index';

const content = renderToString(<App />);
  

Я нашел множество статей о «рендеринге на стороне сервера», но все они, похоже, касаются рендеринга с помощью express и используют те же строки, что и сценарий выше. Я не могу запустить этот код непосредственно в gulp, так как он плохо работает с импортом ES6, который доступен только после узла 14 (и является экспериментальным).

Комментарии:

1. Тогда переключите импорт на requires?

2. Спасибо за комментарий, вы заставили меня понять, что я забыл показать задачу рендеринга gulp, которая заключалась не в рендеринге серверного скрипта, а компонента приложения! Итак, я пытался запустить приложение

Ответ №1:

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

Использование vm.runInNewContext позволяет нам определять синтетический контекст браузера, который require этого не делает. Это важно, если вы получаете доступ window к любому месту в приложении.

src/server.js:

 import React from 'react';
import { renderToString } from 'react-dom/server';
import App from './index';

const content = renderToString(<App />);
global.output = content;
  

приведенный выше скрипт служит точкой входа в browserify. Задача Gulp для компиляции:

 function gulpJS() {
  const sourcePath = path.join(SRC_DIR, 'src/server.js');
  return browserify(sourcePath, { debug:true })
    .transform('babelify', {
      presets: [
        ["@babel/preset-env", { targets: "> 0.25%, not dead" }],
        "@babel/preset-react",
      ],
    })
    .bundle()
    .pipe(source('server_output.js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(sourcemaps.write('.'))
    .pipe(dest(BUILD_DIR));
}
  

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

 const componentContent = fs.readFileSync(path.join(BUILD_DIR, 'server.js'));
const context = {
  global: {
    React: React,
    Immutable: Immutable,
    data: {
      Immutable
    },
  },
  window: {
    addEventListener() { /* fake */ },
    removeEventListener() { /* fake */ },
  },
  console,
};

vm.runInNewContext(componentContent, context);
const result = context.global.output;