Если вы в первую очередь были разработчиком .Net (не слишком знакомы с технологиями Javascript, за исключением, возможно, JQuery) и сейчас находитесь в процессе принятия ASP.Net Core, весьма вероятно, что вас очень быстро подтолкнет в этом направлении. популярных интерфейсных фреймворков, таких как React или Angular. Очевидная причина этого заключается в том, что, хотя ядро ​​ASP.Net является привлекательной серверной платформой, оно не имеет большого набора элементов управления пользовательского интерфейса, которые можно программировать как с сервера, так и с внешнего интерфейса. Неизбежно вам придется делать выбор для вашего внешнего кода; я просто использую JQuery с некоторой библиотекой шаблонов JS? мне купить дорогую базовую библиотеку «элементов управления» ASP.Net (обычно компоненты в этих библиотеках генерируют код JS / HTML)? или я смотрю на интеграцию таких технологий, как React или Angular?

В этой статье предполагается, что вы сделали выбор в пользу интеграции React в основное приложение ASP.Net. И обычно именно здесь начинается самое интересное (?). Вы начинаете с перехода на веб-сайт React и сталкиваетесь с такими терминами, как JSX, ES6, npm, yarn, Webpack, Babel и т. Д. Все это может быстро стать ошеломляющим. В качестве первой попытки вы создаете новый проект из Visual Studio 2017, используя шаблон React. Если повезет, решение работает «из коробки»; но вполне вероятно, что у вас могут возникнуть проблемы с созданием другого проекта только с тем, что вам нужно. Решение шаблона Visual Studio использует ряд сложных концепций, которые могут выходить за рамки понимания начинающих или даже промежуточных разработчиков Javascript.

В этом (длинном) посте я начну с базового шаблона Visual Studio 2017 и проведу вас по шагам до момента, когда вы можете начать добавлять код React в свое решение (например, Pro, ну, почти). Прежде чем приступить к нашей конечной цели, важно иметь четкое представление о нескольких концепциях. Я объясню концепции по ходу дела.

Серверный Javascript; Понимание и установка узла

К настоящему времени большинство разработчиков (кроме наиболее изолированных) знают (или слышали) о Javascript на сервере. Node существует уже некоторое время и предоставляет среду выполнения Javascript. Node работает во всех самых популярных операционных системах и позволяет нам запускать полные (автономные) приложения Javascript.

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

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

Когда вы устанавливаете Node, он также устанавливает утилиту под названием «npm» (менеджер пакетов узлов). npm используется для установки пакетов (библиотек), которые вы можете использовать в своих собственных (или вместе с другими разработчиками) приложениях Javascript. Просто набрав «npm» в командной строке, вы получите подсказки по использованию, а также подтвердите, что вы его действительно установили.

Настройка пустого веб-решения ASP.Net для Visual Studio 2017

Я предполагаю, что вы уже установили Visual Studio 2017 (сообщества или другие версии), и я не буду здесь рассказывать об этом.

Мы будем использовать MVC, но начнем с «пустого» шаблона основного веб-проекта ASP.Net и поддержки MVC вручную, чтобы в нашем проекте не было много файлов проекта MVC по умолчанию. Я добавил новый проект с именем «ASPNetCoreReact», выбрав «Файл» → «Создать» → «Проект», а затем выбрав «Веб-приложение ASP.Net Core» и выбрав «Пусто» на следующем шаге (без поддержки Docker, без поддержки аутентификации).

Если вы запустите приложение в это время, оно просто напечатает «Hello World!» сообщение в новом окне браузера.

Примечание: я также добавил этот проект в репозиторий Github. На этом этапе фиксация называется Первоначальная фиксация - пустой шаблон решения ASP.Net Core.

Включение MVC в приложение

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

Добавьте службу MVC в свой класс запуска (метод ConfigureServices)

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
}

Обновите метод настройки класса запуска

public void Configure(IApplicationBuilder app, 
                      IHostingEnvironment env)
{
     if (env.IsDevelopment())
     {
       app.UseDeveloperExceptionPage();
     }
     app.UseStaticFiles();
     app.UseMvc(routes =>
         {
           routes.MapRoute(
                     name: "default",
                     template: "                  
                     {controller=Home}/{action=Index}/{id?}");
         }                                         
       );
 }

Обратите внимание, что «Hello World!» запись в консоли удалена. Я также добавил вызов «UseStaticFiles», так как он потребуется позже (для обслуживания статического контента, такого как CSS, JS и т. Д.).

Добавьте новую папку с именем «Контроллеры» и добавьте контроллер «Домашний»

(На этом этапе Visual Studio может также добавить пакет «Microsoft.VisualStudio.Web.CodeGeneration.Design» в ваше решение, если вы используете опцию формирования шаблонов для добавления домашнего контроллера.)

Код Home Controller (по умолчанию / scaffolded) должен выглядеть следующим образом:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace ASPNetCoreReact.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}

Добавьте представление, соответствующее действию Home / Index

После добавления представления (с параметром «Без макета» - если вы используете скаффолдинг) добавьте один тег «h1» внутри тела с текстом: «Это это начальная страница ». Код просмотра должен выглядеть так:

@{
  Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" /><title>Index</title>
</head>
<body>
 <h1>This is the entry page.</h1>
</body>
</html>

Папка вашего решения на этом этапе должна выглядеть примерно так:

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

Обратите внимание, что тег «h1» имеет стиль по умолчанию, применяемый браузером (в приведенном выше случае это Chrome).

Решение на этом этапе доступно в этом репозитории Github с меткой фиксации: MVC Enabled.

Знакомство с Javascript и концепцией зависимостей

Давайте теперь добавим в наше приложение базовый Javascript.

Создайте новую папку с именем «Source» в папке «wwwroot» и два файла Javascript в исходной папке: app.js и lib.js.

В app.js введите код, как показано ниже:

document.getElementById("fillthis").innerHTML = getText();

В lib.js введите код, как показано ниже:

getText = function () {
  return "Data from getText function in dep.js";
}

Измените раздел «body» кода индексного представления, как показано ниже:

<body>
<h1>This is the entry page.</h1>
<div id="fillthis"></div>
<script src="~/Source/lib.js"></script>
<script src="~/Source/app.js"></script>
</body>

Теперь мы добавили новый элемент «div» с идентификатором «fillthis» и включили ссылки «Script» на два добавленных файла Javascript.

Если вы запустите приложение сейчас, вы должны увидеть результат, как показано ниже (с «div», отображающим данные из нового кода Javascript:

Обратите внимание, что мы сначала добавили ссылку на файл «lib.js», а затем ссылку на «app.js» в коде индексного представления.

Если бы вместо этого мы должны были изменить порядок ссылок в коде индексного представления, таким образом:

...
<script src="~/Source/app.js"></script>
<script src="~/Source/lib.js"></script>
...

браузер будет жаловаться на то, что функция «getText» не определена (и, конечно, элемент «div» не будет заполнен):

Считается, что «app.js» неявно зависит от «lib.js», и разработчик должен включать эти файлы Javascript в правильном порядке, в котором они упоминаются.

Папка решения теперь выглядит так:

Решение на этом этапе (с правильным порядком ссылок на файлы JS) доступно в этом репозитории Github с меткой фиксации: Представлен базовый Javascript

Представляем модули / пакеты / Webpack Javascript

В предыдущем разделе мы увидели, как разработчик должен надлежащим образом обрабатывать неявные зависимости с помощью функций из различных файлов Javascript. Что ж, есть способ получше. «Модули» Javascript позволяют программистам писать независимые блоки кода (конечно же, называемые модулями), которые другие программисты могут явно включать (или отмечать как «обязательные») в свой код Javascript.

Например, используя концепции модулей, теперь мы можем изменить файлы app.js следующим образом:

require('./lib');
document.getElementById("fillthis").innerHTML = getText();

app.js теперь явно помечает файл lib как зависимость.

Модули Javascript сами по себе - обширная тема, но для наших целей достаточно понять, что модули JS - это способ совместного использования кода с использованием явных зависимостей. Для подробного объяснения модулей Javascript вы можете прочитать Сообщение Preethi.

Представьте себе, что вы можете связать все свои модули Javascript в один файл, чтобы вы могли добавить ссылку на этот файл в свой HTML-код! Вот тут-то и пригодится инструмент Webpack.

Теперь установим Webpack. Откройте новое окно командной строки и перейдите в папку, в которой находится файл проекта csharp (файл .csproj). Прежде чем мы фактически установим Webpack, давайте сначала создадим файл package.json. Файл package.json содержит информацию о метаданных и используется для предоставления npm информации о зависимостях проекта (модуля). Чтобы создать файл package.json по умолчанию, введите команду npm init:

npm init -y

Параметр «-y» просто использует параметры по умолчанию.

Если команда была успешной, она должна была создать файл package.json с содержимым, подобным этому:

{
"name": "ASPNetCoreReact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
 "test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

Чтобы установить Webpack (который также является «модулем»), введите следующую команду:

npm install webpack --save-dev

Если команда выполнена успешно, она должна была создать новую папку с именем node_modules в папке вашего проекта и загрузить несколько модулей в эту папку (в различные папки), конечно, включая пакет Webpack. (Примечание: между модулями и пакетами есть некоторые различия - вы можете прочитать об этом здесь.)

Параметр «- save-dev» добавляет к файлу package.json «зависимость dev» вместо зависимости времени выполнения. Это указывает на то, что пакет Webpack полезен только во время разработки. Это имело бы смысл, поскольку после того, как мы создали необходимые нам «пакеты», мы могли бы использовать их непосредственно в производстве. Файл package.json должен выглядеть примерно так:

{
"name": "ASPNetCoreReact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
   "webpack": "^3.8.1"
  }
}

Обратите внимание на раздел devDepencies выше. Он указывает на зависимость от веб-пакета, а также на информацию о версии. Другие разработчики могут использовать этот файл package.json и установить модули, указанные в разделе devDepencies, с помощью npm install. (npm также может создать файл package-lock.json, который содержит информацию о деревьях зависимостей.)

Теперь, когда мы установили Webpack, позвольте нам использовать его для создания нашего первого пакета! Обратите внимание, что мы установили Webpack «локально» (то есть в папке проекта), а не глобально (как мы сделали node). Итак, теперь мы могли бы просто ввести «webpack» в командной строке.

Есть несколько способов запустить webpack из локальной установки, но мне показалось, что проще всего запустить его с помощью узла после добавления записи в разделе Scripts в файле package.json. Измените файл package.json следующим образом:

{
"name": "ASPNetCoreReact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"wbp" : "webpack wwwroot/source/app.js wwwroot/dist/bundle.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
   "webpack": "^3.8.1"
  }
}

Мы добавили «скрипт» с именем «wpb» в качестве псевдонима для команды: «webpack wwwroot / source / app.js wwwroot / dist / bundle.js» - теперь мы можем использовать этот скрипт для запуска нашей реальной команды с помощью npm. . Команда передает «app.js» в качестве «входного» файла в webpack и инструктирует его создать новый пакетный файл с именем bundle.js (после прохождения всех явных зависимостей во входном файле). Webpack обрабатывает (преобразует) каждый файл Javascript как модуль.

Перед запуском команды Webpack убедитесь, что ваш app.js обновлен для использования «require» для библиотеки следующим образом:

require('./lib');
document.getElementById("fillthis").innerHTML = getText();

Чтобы запустить webpack, введите в командной строке следующее:

npm run wbp

Это запустит команду, которая соответствует имени «скрипта» wbp. Если команда выполнена успешно, она должна была создать файл с именем bundle.js в папке wwwroot / dist. Это ваш первый файл пакета! Найдите время, чтобы проверить содержимое bundle.js. Вы заметите, что он включил код в app.js, а также lib.js в bundle.js (кроме логики для обработки модулей).

Чтобы протестировать наш новый пакет, измените код представления индекса, как показано ниже (здесь показан только элемент body):

...
<body>
<h1>This is the entry page.</h1>
<div id="fillthis"></div>
<script src="~/dist/bundle.js"></script>
</body>
...

Обратите внимание, что ссылки на app.js и lib.js удалены. Единственный файл, на который мы здесь ссылаемся, - это недавно созданный файл bundle.js.

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

Теперь, когда мы выполнили нашу первую команду Webpack, давайте немного рискнем и создадим «конфигурационный» файл для команды Webpack. Webpack имеет различные возможности, и чтобы использовать все его возможности, мы можем настроить файл «webpack.config.js». «Webpack.config.js» - это имя файла по умолчанию, которое webpack использует для чтения «инструкций» по обработке файлов.

В нашем случае мы указали один «входной» файл и один «пакетный» (выходной) файл. Давайте теперь создадим файл webpack.config.js. Используйте Visual Studio для создания нового файла Javascript в основной папке проекта и назовите его webpack.config.js. Введите в этот файл следующий код:

const path = require('path');
module.exports = {
  entry: './wwwroot/source/app.js',
  output: {
            path: path.resolve(__dirname, 'wwwroot/dist'),
            filename: 'bundle.js'
          }
};

Нам нужно экспортировать объект конфигурации из файла конфигурации (который, кстати, является обычным файлом Javascript, в котором вы можете использовать код из других модулей). В нашем объекте конфигурации указаны запись и выходные данные. Как и ожидалось, входным файлом является app.js, а в качестве пути вывода указывается папка wwwroot / dist, а имя файла вывода (пакета) указывается как bundle.js. Встроенный модуль узла« путь » используется для разрешения путей к папкам.

Теперь, когда мы создали файл конфигурации для webpack, мы можем удалить аргументы из команды webpack в разделе Scripts файла package.json, например:

{
"name": "ASPNetCoreReact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"wbp" : "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
   "webpack": "^3.8.1"
  }
}

Параметры «вход» и «выход» удалены из команды webpack. Теперь он будет использовать новый файл конфигурации, который мы создали.

Чтобы запустить команду, введите в командной строке следующее (как и раньше):

npm run wbp

Если все в порядке, это будет иметь тот же эффект, что и выполнение команды Webpack с предоставленными аргументами.

Решение на этом этапе (с правильным порядком ссылок на JS-файлы) доступно в этом репозитории Github с меткой фиксации: Добавлены Webpack и webpack.config.js.

Папка решения должна выглядеть примерно так:

Введение в решение JQuery

Давайте теперь познакомим вас с JQuery.

Измените представление индекса, чтобы включить JQuery из CDN, добавьте новый div для хранения содержимого, заполняемого JQuery, и (временно) вернитесь к использованию app.js и lib.js как независимых файлов, вот так:

...
<head>
<meta name="viewport" content="width=device-width" /><title>Index</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" ></script>
</head>
<body>
<h1>This is the entry page.</h1>
<div id="fillthis"></div>
<div id="fillthiswithjquery"></div>
<script src="~/Source/lib.js"></script>
<script src="~/Source/app.js"></script>
</body>
...

Измените app.js, чтобы выполнить вызов JQuery для заполнения нового div с идентификатором «fillthiswithjquery», а также закомментировать строку с помощью вызова «require», например:

//require('./lib');
document.getElementById("fillthis").innerHTML = getText();
$('#fillthiswithjquery').html('Filled by Jquery!');

Если вы запустите приложение сейчас, вы должны увидеть следующий результат:

Как видите, новый div заполнен текстом: «Заполнено JQuery!» как и ожидалось.

Давайте теперь не комментируем строку «require» в app.js, например:

require('./lib');
document.getElementById("fillthis").innerHTML = getText();
$('#fillthiswithjquery').html('Filled by Jquery!');

и воссоздайте bundle.js, выполнив ту же команду, что и раньше:

npm run wbp

Измените код представления индекса, чтобы включить созданный пакет, а также поместите строку ссылки сценария JQuery под строкой ссылки bundle.js.

...
<head>
<meta name="viewport" content="width=device-width" /><title>Index</title>
</head>
<body>
<h1>This is the entry page.</h1>
<div id="fillthis"></div>
<div id="fillthiswithjquery"></div>
<script src="~/dist/bundle.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" ></script>
</body>
...

Если вы запустите приложение сейчас, вы получите следующую ошибку:

Браузер сообщает, что «$» не определено. «Неявная» зависимость JQuery не была включена в правильном порядке.

Давайте теперь удалим неявную зависимость от JQuery и сделаем ее явной зависимостью в app.js. Но прежде чем мы это сделаем, давайте установим пакет JQuery с помощью npm, например (запустите команду из папки Project):

npm install jquery --save-dev

Это должно установить пакет JQuery в node_modules и изменить файл package.json, например:

{
"name": "ASPNetCoreReact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"wbp" : "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
   "jquery": "^3.2.1",
   "webpack": "^3.8.1"
  }
}

Измените файл app.js так, чтобы он «требовал» JQuery, и сопоставьте его с «$», например:

$ = require('jquery');
require('./lib');
document.getElementById("fillthis").innerHTML = getText();
$('#fillthiswithjquery').html('Filled by Jquery!');

Примечание: вы также можете использовать «import $ from‘ jquery ’;» (синтаксис модуля ES6 вместо инструкции require.)

Удалите ссылку на JQuery CDN из кода представления индекса. вот так:

...
<head>
<meta name="viewport" content="width=device-width" /><title>Index</title>
</head>
<body>
<h1>This is the entry page.</h1>
<div id="fillthis"></div>
<div id="fillthiswithjquery"></div>
<script src="~/dist/bundle.js"></script>
</body>
...

Снова соберите пакет, используя следующую команду, как и раньше:

npm run wbp

Если вы запустите приложение, вы должны увидеть ожидаемый результат, как показано ниже:

Однако на этот раз JQuery включен в bundle.js! Вы заметите, что размер bundle.js увеличился.

Решение на этом этапе доступно в этом репозитории Github с меткой фиксации: JQuery добавлен как пакет.

Представляем загрузчики и плагины Webpack, ES6 (ES2015)

Webpack использует так называемые загрузчики и плагины.

Загрузчики - это преобразования, которые применяются к исходному коду модуля. Плагины - это основа webpack. Сам веб-пакет построен на той же системе плагинов, которую вы используете в конфигурации веб-пакета! Они также служат для выполнения всего остального, чего не может сделать загрузчик ».

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

Давайте воспользуемся этим плагином, чтобы автоматически загрузить модуль JQuery и сделать его доступным повсюду.

Измените webpack.config.js следующим образом:

const path = require('path');
const webpack = require('webpack');
module.exports = {
  entry: './wwwroot/source/app.js',
  output: {
            path: path.resolve(__dirname, 'wwwroot/dist'),
            filename: 'bundle.js'
          },
plugins: [
   new webpack.ProvidePlugin({
              $: 'jquery',
         jQuery: 'jquery',
'window.jQuery': 'jquery'
                    })
         ]
};

Мы добавляем новый элемент «plugins» как массив плагинов, которые webpack будет использовать для обработки исходных файлов. В этом случае мы проинструктировали сделать «$», «Jquery», «window.jquery» доступными повсюду без необходимости явно импортировать их в наши файлы. Обратите внимание, что мы «требуем» сам модуль webpack и используем его для создания подключаемого модуля.

В результате использования плагина мы теперь можем закомментировать инструкцию «require» из app.js, например:

//$ = require('jquery');
require('./lib');
document.getElementById("fillthis").innerHTML = getText();
$('#fillthiswithjquery').html('Filled by Jquery!');

Если вы сейчас регенерируете bundle.js с помощью приведенной ниже команды, результаты останутся неизменными и ожидаемыми.

npm run wbp

ES6 (ES2015)

ECMAScript 6 (теперь известный преимущественно как ES2015) - это спецификация языка Javascript. ES6 представляет довольно много новых функций, реализованных не во всех браузерах (особенно в старых браузерах, таких как IE 11 / IE 10, которые все еще могут широко использоваться). Фактически, уже представлены новые функции, соответствующие ES2016 / ES2017. Одна из новых функций ES6 - классы.

Давайте представим функцию ES6 в новом файле Javascript и включим ее как зависимость в app.js. Создайте новый файл с именем «es6codelib.js» в папке «Исходный код» и введите в этот файл следующий код:

export default class ES6Lib {
constructor() {
  this.text = "Data from ES6 class";
 }
getData() {
     return this.text;
   }
}

Это не делает ничего, кроме инициализации «текстового» свойства, которое возвращается из «метода» getData (). Он отмечает класс ES6Lib как «экспортный» по умолчанию.

Измените код представления индекса, чтобы включить новый «div» с идентификатором: fillthiswithes6lib, например:

...
<h1>This is the entry page.</h1>
<div id="fillthis"></div>
<div id="fillthiswithjquery"></div>
<div id="fillthiswithes6lib"></div>
...

Измените app.js, чтобы использовать новый код ES6, например:

//$ = require('jquery');
require('./lib');
import ES6Lib from './es6codelib';
document.getElementById("fillthis").innerHTML = getText();
$('#fillthiswithjquery').html('Filled by Jquery!');
let myES6Object = new ES6Lib();
$('#fillthiswithes6lib').html(myES6Object.getData());

Теперь, если мы воссоздадим пакет и запустим приложение (убедитесь, что ваши браузеры не загружают файлы из кеша), современные браузеры, такие как Chrome, вероятно, запустят код без каких-либо проблем. Однако, если вы запустите код в старом браузере, таком как IE 11, вы, скорее всего, столкнетесь с проблемой, описанной ниже:

IE 11 не распознает синтаксис «класс».

Для решения этих проблем используйте Transpiler, например Babel. Транспиллер преобразует код ES6, чтобы использовать синтаксис ES5, понятный старым браузерам. Хотя Babel сам по себе очень многофункциональный и может использоваться независимо, для наших целей мы можем использовать соответствующие загрузчики babel для выполнения этой работы.

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

npm install [email protected] @babel/core@next @babel/preset-env@next --save-dev

Если команда выполнена успешно, эти загрузчики будут установлены в папке node_modules и package.json будет изменен, например:

{
"name": "ASPNetCoreReact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"wbp" : "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
   "@babel/core": "^7.0.0-beta.31",
   "@babel/preset-env": "^7.0.0-beta.31",
   "babel-loader": "^8.0.0-beta.0",
   "jquery": "^3.2.1",
   "webpack": "^3.8.1"
  }
}

Измените webpack.config.js следующим образом:

const path = require('path');
const webpack = require('webpack');
module.exports = {
  entry: './wwwroot/source/app.js',
  output: {
            path: path.resolve(__dirname, 'wwwroot/dist'),
            filename: 'bundle.js'
          },
plugins: [
   new webpack.ProvidePlugin({
              $: 'jquery',
         jQuery: 'jquery',
'window.jQuery': 'jquery'
                    })
],
module: {
  rules: [ { test: /\.js?$/, 
          use: { loader: 'babel-loader', options: { presets: 
                       ['@babel/preset-env'] } } },
           ]
       }
};

Теперь мы добавили свойство модуль к объекту конфигурации, который, в свою очередь, имеет массив правил. Правила предписывают webpack использовать загрузчик babel-loader (для файлов .js, как указано в тесте Regex), передавая ему параметры preset-env. Эта предустановка указывает загрузчику распознавать все последние функции ES (2015/2016/2017) и преобразовывать код с использованием этих функций в код, понятный для старых браузеров.

Теперь воссоздайте пакет, как всегда, с помощью:

npm run wbp

Запустите приложение, и теперь оно должно корректно отображаться в IE 11:

Возможно, стоит изучить код в bundle.js, чтобы увидеть, как работают преобразования!

Решение на этом этапе доступно в этом репозитории Github с меткой фиксации: ProvidePlugin и добавлен babel-loader.

Папка Solution на этом этапе выглядит так:

Введение в решение Bootstrap и пользовательского CSS

Поскольку Bootstrap так широко используется, я решил включить его в это решение.

Это может показаться невероятным, но на самом деле файлы CSS могут быть включены в качестве «модулей» с помощью webpack, который получает помощь от загрузчика для выполнения своей работы. Загрузчик, как вы уже догадались, называется css-loader.

Прежде чем использовать Bootstrap в нашем решении, нам, конечно же, придется установить пакет Bootstrap. Сам Bootstrap (компоненты JS) зависит от пакета с именем Popper.js (кроме зависимости от JQuery), поэтому давайте сначала установим его, например:

npm install popper.js --save-dev

Мы также установим последнюю версию Bootstrap (в настоящее время V4.0 / Beta 2) с помощью команды:

npm install [email protected] --save-dev

Также установите CSS-загрузчик, вот так:

npm install css-loader --save-dev

После этих команд в папку node_modules должны быть установлены пакеты Bootstrap и Popper, а также css-loader, и файл package.json должен выглядеть так:

{
"name": "ASPNetCoreReact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"wbp" : "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
   "@babel/core": "^7.0.0-beta.31",
   "@babel/preset-env": "^7.0.0-beta.31",
   "babel-loader": "^8.0.0-beta.0",
   "bootstrap": "^4.0.0-beta.2",
   "css-loader": "^0.28.7",
   "jquery": "^3.2.1",
   "popper.js": "^1.12.6",
   "webpack": "^3.8.1"
  }
}

Конечно, мы можем включить исходные предварительно скомпилированные файлы Sass для начальной загрузки и использовать соответствующие загрузчики для преобразования файлов Sass, но вместо этого мы будем использовать файл CSS Bootstrap min для простоты.

Давайте также добавим собственный файл CSS site.css в новую папку с именем: CSS в папке wwwroot.

Измените содержимое файла site.css следующим образом:

h1 {
 color: red;
}

Измените файл app.js, включив в него минимальный CSS-файл начальной загрузки:

//$ = require('jquery');
require('./lib');
import 'bootstrap/dist/css/bootstrap.min.css';
import '../css/site.css';
import ES6Lib from './es6codelib';
document.getElementById("fillthis").innerHTML = getText();
$('#fillthiswithjquery').html('Filled by Jquery!');
let myES6Object = new ES6Lib();
$('#fillthiswithes6lib').html(myES6Object.getData());

Измените webpack.config.js следующим образом:

const path = require('path');
const webpack = require('webpack');
module.exports = {
  entry: './wwwroot/source/app.js',
  output: {
            path: path.resolve(__dirname, 'wwwroot/dist'),
            filename: 'bundle.js'
          },
plugins: [
   new webpack.ProvidePlugin({
              $: 'jquery',
         jQuery: 'jquery',
     'window.jQuery': 'jquery',
      Popper: ['popper.js', 'default']
                        })
      ],
module: {
  rules: [ 
         {test: /\.css$/, use: [{ loader: "css-loader" }]},
         { test: /\.js?$/, 
          use: { loader: 'babel-loader', options: { presets: 
                       ['@babel/preset-env'] } } },
         ]
      }
};

Мы добавили ссылку «Popper» на ProvidePlugin (сделав его доступным повсюду), а также добавили «тестовое» правило для обработки файлов css с помощью загрузчика css.

Теперь заново создайте пакет, используя ту же команду, что и раньше, и запустите приложение.

Вы обнаружите, что отображение не изменилось и стили Bootstrap не были применены. Почему? Потому что все, что сделал css-loader, - это преобразовал файл CSS в модуль Javascript и включил его в bundle.js. Фактически он не отрисовал элемент «стиль». Изучите код bundle.js, чтобы найти в нем ссылки на Bootstrap (Bootstrap v4.0.0-beta.2).

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

Во-первых, давайте установим загрузчик стилей, например:

npm install style-loader --save-dev

Это устанавливает загрузчик стилей в папку node_modules и изменяет файл package.json следующим образом:

{
"name": "ASPNetCoreReact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"wbp" : "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
   "@babel/core": "^7.0.0-beta.31",
   "@babel/preset-env": "^7.0.0-beta.31",
   "babel-loader": "^8.0.0-beta.0",
   "bootstrap": "^4.0.0-beta.2",
   "css-loader": "^0.28.7",
   "jquery": "^3.2.1",
   "popper.js": "^1.12.6",
   "style-loader": "^0.19.0",
   "webpack": "^3.8.1"
  }
}

Измените webpack.config.js следующим образом:

const path = require('path');
const webpack = require('webpack');
module.exports = {
  entry: './wwwroot/source/app.js',
  output: {
            path: path.resolve(__dirname, 'wwwroot/dist'),
            filename: 'bundle.js'
          },
plugins: [
   new webpack.ProvidePlugin({
              $: 'jquery',
         jQuery: 'jquery',
     'window.jQuery': 'jquery',
      Popper: ['popper.js', 'default']
                        })
      ],
module: {
  rules: [ 
         {test: /\.css$/, use: [{ loader: "style-loader" },
                                { loader: "css-loader" }]},
         { test: /\.js?$/, 
          use: { loader: 'babel-loader', options: { presets: 
                       ['@babel/preset-env'] } } },
]
}
};

Загрузчики применяются справа налево; в этом случае сначала применяется css-loader, а затем style-loader.

Повторно создайте bundle.js, используя ту же команду, что и раньше. Когда вы запустите приложение сейчас, вы увидите примененные стили Bootstrap, а также «красный» цвет, который мы применили к элементу «h1» с помощью нашего настраиваемого файла CSS:

Если вы проверите DOM на этой странице, вы увидите, что элемент Style был отрисован встроенным:

Вы должны найти строку кода: «addStylesToDom (стили, параметры);» внутри файла bundle.js. Если вы закомментируете эту строку и повторно загрузите страницу, вы увидите, что элемент стиля больше не доступен. Это код, добавленный загрузчиком стилей.

Решение на этом этапе доступно в этом репозитории Github с меткой фиксации: Добавлен Bootstrap и настраиваемый CSS.

Папка решения теперь выглядит так:

Извлечь-текст-веб-пакет-плагин и uglifyjs-webpack-plugin

В предыдущем разделе мы видели, как весь CSS-код был встроен в страницу. Мы можем этого не хотеть. Мы можем захотеть извлечь / объединить / минимизировать весь CSS в файл, а затем добавить на него ссылку. Здесь на помощь приходит плагин Extract-text-webpack-plugin. Мы можем использовать этот плагин для извлечения кода стилей, сгенерированного css-loader, и извлечения его в физический файл CSS.

Uglifyjs-webpack-plugin используется для минимизации кода JavaScript.

Установите оба плагина, как показано ниже:

npm install extract-text-webpack-plugin --save-dev
npm install uglifyjs-webpack-plugin --save-dev

Теперь файл package.json выглядит так:

{
"name": "ASPNetCoreReact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"wbp" : "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
   "@babel/core": "^7.0.0-beta.31",
   "@babel/preset-env": "^7.0.0-beta.31",
   "babel-loader": "^8.0.0-beta.0",
   "bootstrap": "^4.0.0-beta.2",
   "css-loader": "^0.28.7",
   "extract-text-webpack-plugin": "^3.0.2",
   "jquery": "^3.2.1",
   "popper.js": "^1.12.6",
   "style-loader": "^0.19.0",
   "uglifyjs-webpack-plugin": "^1.0.1",
   "webpack": "^3.8.1"
  }
}

К настоящему времени вы должны быть знакомы с добавлением плагинов; Измените webpack.config.js, чтобы добавить эти плагины, например:

const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractCSS = new ExtractTextPlugin('allstyles.css');
module.exports = {
 entry: './wwwroot/source/app.js',
 output: {
   path: path.resolve(__dirname, 'wwwroot/dist'),
  filename: 'bundle.js'
     },
 plugins: [ 
   extractCSS,
   new webpack.ProvidePlugin({
            $: 'jquery',
       jQuery: 'jquery',
  'window.jQuery': 'jquery',
         Popper: ['popper.js', 'default']
                 }),
   new webpack.optimize.UglifyJsPlugin()
     ],
module: {
  rules: [
   { test: /\.css$/, use: extractCSS.extract(['css-loader?
    minimize']) },
  { test: /\.js?$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } },
      ]
    }
};

Обратите внимание, что плагин extract Text CSS использует вывод непосредственно из загрузчика css (загрузчик стиля не требуется).

Повторно запустите Webpack с этой конфигурацией; теперь вы должны увидеть файл «allstyles.css» в папке «dist». Вновь созданный файл bundle.js должен быть значительно меньше из-за использования uglifyjs-webpack-plugin.

Теперь вы должны включить ссылку на только что созданный allstyles.css в код представления индекса, например:

<html>
<head>
<meta name="viewport" content="width=device-width" /><title>Index</title>
<link rel="stylesheet" href="~/dist/allstyles.css" />
</head>
<body>
<h1>This is the entry page.</h1>
<div id="fillthis"></div>
<div id="fillthiswithjquery"></div>
<div id="fillthiswithes6lib"></div>
<script src="~/dist/bundle.js"></script>
</body>
</html>

Теперь при запуске приложения будут те же результаты, но с меньшим размером файла bundle.js и только что сгенерированным файлом CSS.

Решение на этом этапе доступно в этом репозитории Github с меткой фиксации: Добавлены плагины для извлечения текста и Uglify.

Папка решения теперь выглядит так:

Замена Hotmodule

Последняя функциональность, которую мы включим перед добавлением React в проект, - это замена Hotmodule.

При разработке приложения Javascript, для которого требуется этап сборки, прежде чем мы увидим изменения в браузере, становится довольно неудобно выполнять эти шаги каждый раз, когда мы вносим изменения в файл Javascript. Здесь нам на помощь приходит пара пакетов (позволяя автоматически распространять обновления файлов Javascript в браузер без ручного вмешательства).

Добавьте пакеты webpack-hot-middleware и aspnet-webpack, например:

npm install webpack-hot-middleware --save-dev
npm install aspnet-webpack --save-dev

После установки этих пакетов файл package.json будет выглядеть так:

{
"name": "ASPNetCoreReact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"wbp" : "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
   "@babel/core": "^7.0.0-beta.31",
   "@babel/preset-env": "^7.0.0-beta.31",
   "aspnet-webpack": "^2.0.1",
   "babel-loader": "^8.0.0-beta.0",
   "bootstrap": "^4.0.0-beta.2",
   "css-loader": "^0.28.7",
   "extract-text-webpack-plugin": "^3.0.2",
   "jquery": "^3.2.1",
   "popper.js": "^1.12.6",
   "style-loader": "^0.19.0",
   "uglifyjs-webpack-plugin": "^1.0.1",
   "webpack": "^3.8.1",
   "webpack-hot-middleware": "^2.20.0"
  }
}

Измените класс запуска, метод Configure для использования промежуточного программного обеспечения Webpack dev, например (вам также необходимо добавить оператор using для Microsoft.AspNetCore.SpaServices.Webpack в начале файла):

...
using Microsoft.AspNetCore.SpaServices.Webpack;
...
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
if (env.IsDevelopment())
{
  app.UseDeveloperExceptionPage();
  app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
  {
    HotModuleReplacement = true
  });
}
...

Измените webpack.config.js следующим образом:

const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractCSS = new ExtractTextPlugin('allstyles.css');
module.exports = {
 entry: { 'main': './wwwroot/source/app.js' },
output: {
  path: path.resolve(__dirname, 'wwwroot/dist'),
  filename: 'bundle.js',
  publicPath: 'dist/'
  },
plugins: [ 
 extractCSS, 
 new webpack.ProvidePlugin({
                    $: 'jquery',
               jQuery: 'jquery',
          'window.jQuery': 'jquery',
         Popper: ['popper.js', 'default']
                          }),
 new webpack.optimize.UglifyJsPlugin()
 ],
module: {
 rules: [
 { test: /\.css$/, use: extractCSS.extract(['css-loader?minimize']) },
{ test: /\.js?$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } },
 ]
}
};

Обратите внимание на незначительное изменение в элементе «entry» - для замены Hotmodule требуется запись с явно названной «основной» точкой входа. Также требуется «publicPath», указанный в элементе «output».

Нам также необходимо сообщить HMR, что мы готовы «принимать» обновления Hotmodule из нашего модуля.

Измените файл app.js и добавьте вызов «accept», например:

//$ = require('jquery');
require('./lib');
import 'bootstrap/dist/css/bootstrap.min.css';
import '../css/site.css';
import ES6Lib from './es6codelib';
document.getElementById("fillthis").innerHTML = getText();
$('#fillthiswithjquery').html('Filled by Jquery??');
let myES6Object = new ES6Lib();
$('#fillthiswithes6lib').html(myES6Object.getData());
module.hot.accept();

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

Решение на этом этапе доступно в этом репозитории Github с меткой фиксации: Замена Hotmodule.

Теперь мы готовы добавить в решение React!

Добавление React в решение

Давайте сначала установим пакеты React (react и react-dom) в наше решение с помощью npm, например:

npm install react react-dom --save-dev

Нам также нужен «пресет» babel (React) для обработки кода React JSX. Установим соответствующий пресет, например:

npm install @babel/preset-react --save-dev

Теперь файл package.json будет выглядеть примерно так:

{
"name": "ASPNetCoreReact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"wbp" : "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
   "@babel/core": "^7.0.0-beta.31",
   "@babel/preset-env": "^7.0.0-beta.31",
   "@babel/preset-react": "^7.0.0-beta.31",
   "aspnet-webpack": "^2.0.1",
   "babel-loader": "^8.0.0-beta.0",
   "bootstrap": "^4.0.0-beta.2",
   "css-loader": "^0.28.7",
   "extract-text-webpack-plugin": "^3.0.2",
   "jquery": "^3.2.1",
   "popper.js": "^1.12.6",
   "react": "^16.0.0",
   "react-dom": "^16.0.0",
   "style-loader": "^0.19.0",
   "uglifyjs-webpack-plugin": "^1.0.1",
   "webpack": "^3.8.1",
   "webpack-hot-middleware": "^2.20.0"
  }
}

Добавьте @ babel / preset-response к загрузчику babel в webpack.config.js, например:

...
{ test: /\.js?$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react' ,'@babel/preset-env'] } } }
...

Удалите ненужное содержимое («div») из кода представления индекса, назначьте имя класса CSS Bootstrap «контейнер» элементу «body» и добавьте заполнители для двух компонентов React; базовый компонент React и компонент React, который обращается к контроллеру api для получения данных, например:

...
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<link rel="stylesheet" href="~/dist/allstyles.css" />
</head>
<body class="container">
<h1>This is the entry page.</h1>
<br/><br /><br />
<div id="basicreactcomponent"></div>
<br /><br /><br />
<div id="reactcomponentwithapidata"></div>

<script src="~/dist/bundle.js"></script>
</body>
</html>

Добавьте новый файл Javascript, reactcomponent.js, в папку Source и введите следующий код:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
export default class Counter extends React.Component {
  constructor() {
    super();
    this.state = { currentCount: 0 };
   }
  render() {
   return <div>
     <h1>Counter</h1>
      <p>This is a simple example of a React component.</p>    <p>Current count: <strong>{this.state.currentCount}</strong></p>
  <button onClick={() => { this.incrementCounter()    
                        }}>Increment
   </button>
   </div>;
  }
 incrementCounter() {
   this.setState({
     currentCount: this.state.currentCount + 1
         });
    }
}

Это создает базовый компонент «Счетчик»; это JSX-адаптация компонента Typescript в образце решения Visual Studio 2017 React.

Измените app.js, чтобы импортировать React и этот компонент и отрендерить его, например:

//$ = require('jquery');
require('./lib');
import 'bootstrap/dist/css/bootstrap.min.css';
import '../css/site.css';
import React from 'react';
import ReactDOM from 'react-dom';
import Counter from './reactcomponent';
import ES6Lib from './es6codelib';
ReactDOM.render(
   <Counter />,
   document.getElementById('basicreactcomponent')
  );
//document.getElementById("fillthis").innerHTML = getText();
//$('#fillthiswithjquery').html('Filled by Jquery??');
//let myES6Object = new ES6Lib();
//$('#fillthiswithes6lib').html(myES6Object.getData());
module.hot.accept();

Повторно сгенерируйте bundle.js и запустите приложение. Теперь вы должны увидеть компонент Counter, отображаемый на странице, например:

Нажатие кнопки увеличивает счетчик.

Давайте теперь добавим следующий компонент для отображения данных, полученных с контроллера api.

Добавьте новый контроллер с именем «SampleDataController» в папку «Контроллеры» и введите следующий код:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace ASPNetCoreReact.Controllers
{
[Route("api/[controller]")]
public class SampleDataController : Controller
{
  private static string[] Summaries = new[]
  {
   "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", 
   "Balmy", "Hot", "Sweltering", "Scorching"
  };
[HttpGet("[action]")]
public IEnumerable<WeatherForecast> WeatherForecasts()
{
 var rng = new Random();
  return Enumerable.Range(1, 5)
  .Select(index => new WeatherForecast   
  {
   DateFormatted = DateTime.Now.AddDays(index).ToString("d"),
   TemperatureC = rng.Next(-20, 55),
   Summary = Summaries[rng.Next(Summaries.Length)]
  });
}
public class WeatherForecast
{
 public string DateFormatted { get; set; }
 public int TemperatureC { get; set; }
 public string Summary { get; set; }
 public int TemperatureF
 {
 get
 {
  return 32 + (int)(TemperatureC / 0.5556);
 }
 }
}
}
}

Этот контроллер используется ASIS из образца шаблона решения React Visual Studio 2017.

Создайте новый файл Javascript с именем fetchdata.js в папке Source и введите следующий код:

import * as React from 'react';
import 'es6-promise';
import 'isomorphic-fetch';
export default class FetchData extends React.Component{
 constructor() {
    super();
     this.state = { forecasts: [], loading: true };
     fetch('api/SampleData/WeatherForecasts')
       .then(response => response.json())
       .then(data => {
          this.setState({ forecasts: data, loading: false });
        });
   }
render() {
   let contents = this.state.loading? <p><em>Loading...</em></p>
   : FetchData.renderForecastsTable(this.state.forecasts);
     
   return <div>
      <h1>Weather forecast</h1>
     <button onClick={() => { this.refreshData() }}>Refresh</button>
 <p>This component demonstrates fetching data from the server.</p>  
       {contents}
      </div>;
    }
refreshData() {
  fetch('api/SampleData/WeatherForecasts')
    .then(response => response.json())
    .then(data => {
      this.setState({ forecasts: data, loading: false });
     });
  }
static renderForecastsTable(forecasts) {
  return <table className='table'>
   <thead>
    <tr>
     <th>Date</th>
     <th>Temp. (C)</th>
     <th>Temp. (F)</th>
     <th>Summary</th>
    </tr>
   </thead>
 <tbody>
   {forecasts.map(forecast =>
        <tr key={forecast.dateFormatted}>
         <td>{forecast.dateFormatted}</td>
         <td>{forecast.temperatureC}</td>
         <td>{forecast.temperatureF}</td>
         <td>{forecast.summary}</td>
        </tr>
     )}
  </tbody>
</table>;
}
}

Это JSX-адаптация того же компонента Typescript в решении шаблона Visual Studio 2017 React. Добавлен метод refreshData () для ответа на кнопку «Обновить».

Измените файл app.js, чтобы импортировать этот компонент и соответствующим образом отобразить его, например:

...
import React from 'react';
import ReactDOM from 'react-dom';
import Counter from './reactcomponent';
import FetchData from './fetchdata';
import ES6Lib from './es6codelib';
ReactDOM.render(
  <Counter />,
  document.getElementById('basicreactcomponent')
  );
ReactDOM.render(
 <FetchData />,
 document.getElementById('reactcomponentwithapidata')
);
....

Компоненту FetchData требуется пакет isomorphic-fetch, поэтому давайте установим его, например:

npm install isomorphic-fetch --save-dev

Теперь файл package.json выглядит так:

{
"name": "ASPNetCoreReact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"wbp" : "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
   "@babel/core": "^7.0.0-beta.31",
   "@babel/preset-env": "^7.0.0-beta.31",
   "@babel/preset-react": "^7.0.0-beta.31",
   "aspnet-webpack": "^2.0.1",
   "babel-loader": "^8.0.0-beta.0",
   "bootstrap": "^4.0.0-beta.2",
   "css-loader": "^0.28.7",
   "extract-text-webpack-plugin": "^3.0.2",
   "isomorphic-fetch": "^2.2.1",
   "jquery": "^3.2.1",
   "popper.js": "^1.12.6",
   "react": "^16.0.0",
   "react-dom": "^16.0.0",
   "style-loader": "^0.19.0",
   "uglifyjs-webpack-plugin": "^1.0.1",
   "webpack": "^3.8.1",
   "webpack-hot-middleware": "^2.20.0"
  }
}

Повторно сгенерируйте bundle.js и запустите приложение. Теперь он также должен отображать компонент таблицы динамических данных. При нажатии на кнопку «Обновить» должен отобразиться новый набор данных:

Окончательное решение доступно в этом репозитории Github с меткой фиксации: Интегрированные компоненты React.

На этом мы подошли к концу этого очень длинного поста! Мы рассмотрели множество тем, но все же возможны изменения, перечисленные ниже.

Дальнейшее развитие

Обработка на стороне сервера: это не было рассмотрено и оставлено в качестве упражнения для читателя.

Разделение файлов от поставщиков / другие полезные плагины: это решение объединяет решения от поставщиков (например, Bootstrap CSS) вместе с пользовательской реализацией (site.css). В идеале следует создавать отдельные «пакеты», чтобы файлы поставщика и пользовательские файлы были разделены. Решение Visual Studio 2017 React прекрасно демонстрирует это, имея отдельный файл webpack.config.vendor.js, который включает все файлы поставщиков, отдельно от файла webpack.config.js, который включает пользовательские реализации. Это решение дополнительно использует плагины DllReferencePlugin и DllPlugin для реализации разделения.

Интеграция сборки. Хотя мы использовали командную строку для всех наших установок и конфигураций, шаблонное решение Visual Studio 2017 имеет несколько этапов сборки, интегрированных в файл проекта. Щелкните правой кнопкой мыши файл проекта и выберите параметр «Редактировать файл проекта .csproj», чтобы просмотреть содержимое файла проекта. Вы увидите записи, похожие на следующие:

<Target Name="DebugRunWebpack" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('wwwroot\dist') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<!-- In development, the dist files won't exist on the first run or when cloning to a different machine, so rebuild them if not already present. -->
<Message Importance="high" Text="Performing first-run Webpack build..." />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js" />
<Exec Command="node node_modules/webpack/bin/webpack.js" />
</Target>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
<Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="wwwroot\dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath><CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>

Он определяет два случая: «Отладка RunWebpack» и «Опубликовать RunWebpack». В рабочем процессе отладки он проверяет наличие папки «wwwroot / dist» и, если нет, использует Webpack для работы с конфигурационными файлами «vendor» и «default» - по сути, собирая пакеты в соответствии со спецификацией в этих конфигурациях. файлы (после запуска npm install в файле package.json). Если он обнаруживает, что «узел» не установлен, он останавливает сборку, уведомляя пользователя об установке «узла», прежде чем продолжить.

В пути «Опубликовать» он включает файлы «распространения» в сборке (после запуска «новой» сборки с помощью npm). Обратите внимание, что в этом пути он передает аргумент «- env.prod» в webpack. В файлах конфигурации есть код, чтобы понять смысл этого параметра (файл конфигурации «vendor» использует плагин «DefinePlugin» для настройки того, должен ли webpack запускать сборку «для разработки» или «для производства»). Для сборки «dev» он также использует SourceMapDevToolPlugin для создания исходных карт.

Разработчики могут при желании изменить свои файлы сборки в соответствии со своими потребностями. Документы MSBuild могут быть полезны.

Спасибо за чтение

Спасибо за то, что терпеливо прочитали этот пост (раз уж вы зашли так далеко!). Это довольно длинный пост, и писать его было немного утомительно. Надеюсь, это окажется полезным для читателей!