Как использовать npm с ASP.NET Core

Я использую npm для управления jQuery, Bootstrap, Font Awesome и аналогичными клиентскими библиотеками, которые нужны для моего приложения ASP.NET Core.

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

{
    "version": "1.0.0",
    "name": "myapp",
    "private": true,
    "devDependencies": {
  },
  "dependencies": {
    "bootstrap": "^3.3.6",
    "font-awesome": "^4.6.1",
    "jquery": "^2.2.3"
  }
}

npm восстанавливает эти пакеты в папке node_modules, которая находится на том же уровне, что и wwwroot в каталоге проекта:

Поскольку ASP.NET Core обслуживает статические файлы из папки wwwroot, а node_modules там нет, мне пришлось внести пару изменений, чтобы это сработало, первое: добавить app.UseFileServer прямо перед app.UseStaticFiles в моем автозагрузке. CS файл:

app.UseFileServer(new FileServerOptions()
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(Directory.GetCurrentDirectory(), @"node_modules")), 
    RequestPath = new PathString("/node_modules"),
    EnableDirectoryBrowsing = true
});

app.UseStaticFiles();

и второй, включая node_modules в моих publishOptions в файле project.json:

"publishOptions": {
  "include": [
    "web.config",
    "wwwroot",
    "Views",
    "node_modules"
  ]
},

Это работает в моей среде разработки и также работает, когда я развертываю его в своем экземпляре службы приложений Azure, jquery, bootstrap и font-awesome статические файлы хорошо обслуживаются, но я не уверен насчет этой реализации.

Каков правильный подход для этого?

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

Любые советы будут с благодарностью.

8 ответов

Решение

Опубликовав весь node_modules папка, в которой вы развертываете гораздо больше файлов, чем вам на самом деле нужно в рабочей среде.

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

Затем вы также можете полностью удалить FileServer конфигурация и полагаться на UseStaticFiles вместо.

В настоящее время Gulp является выбранным для выполнения задач VS. Добавить gulpfile.js в корне вашего проекта, и настройте его для обработки статических файлов при публикации.

Например, вы можете добавить следующее scripts раздел к вашему project.json:

 "scripts": {
    "prepublish": [ "npm install", "bower install", "gulp clean", "gulp min" ]
  },

Который будет работать со следующим gulpfile (по умолчанию, когда леса с yo):

/// <binding Clean='clean'/>
"use strict";

var gulp = require("gulp"),
    rimraf = require("rimraf"),
    concat = require("gulp-concat"),
    cssmin = require("gulp-cssmin"),
    uglify = require("gulp-uglify");

var webroot = "./wwwroot/";

var paths = {
    js: webroot + "js/**/*.js",
    minJs: webroot + "js/**/*.min.js",
    css: webroot + "css/**/*.css",
    minCss: webroot + "css/**/*.min.css",
    concatJsDest: webroot + "js/site.min.js",
    concatCssDest: webroot + "css/site.min.css"
};

gulp.task("clean:js", function (cb) {
    rimraf(paths.concatJsDest, cb);
});

gulp.task("clean:css", function (cb) {
    rimraf(paths.concatCssDest, cb);
});

gulp.task("clean", ["clean:js", "clean:css"]);

gulp.task("min:js", function () {
    return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
        .pipe(concat(paths.concatJsDest))
        .pipe(uglify())
        .pipe(gulp.dest("."));
});

gulp.task("min:css", function () {
    return gulp.src([paths.css, "!" + paths.minCss])
        .pipe(concat(paths.concatCssDest))
        .pipe(cssmin())
        .pipe(gulp.dest("."));
});

gulp.task("min", ["min:js", "min:css"]);

  • С помощью npm для управления библиотеками на стороне клиента это хороший выбор (в отличие от Bower или NuGet), вы думаете в правильном направлении:)
  • Разделите проекты на стороне сервера (ASP.NET Core) и на стороне клиента (например, Angular 2, Ember, React) на отдельные папки (в противном случае ваш проект ASP.NET может иметь много шума - модульные тесты для кода на стороне клиента, node_modules папка, сборка артефактов и т. д.). Разработчики front-end, работающие в одной команде с вами, будут вам благодарны:)
  • Восстановите модули npm на уровне решения (аналогично тому, как вы восстанавливаете пакеты через NuGet, а не в папку проекта), таким образом, вы можете иметь модульные и интеграционные тесты в отдельной папке (в отличие от клиентских JavaScript-тестов внутри вашего Проект ASP.NET Core).
  • Использование может не понадобиться FileServer имея StaticFiles должно быть достаточно для обслуживания статических файлов (.js, изображений и т. д.)
  • Используйте Webpack для объединения вашего клиентского кода в один или несколько кусков (связок)
  • Вам может не понадобиться Gulp/Grunt, если вы используете пакетный модуль, такой как Webpack
  • Написание сценариев автоматизации сборки на ES2015+ JavaScript (в отличие от Bash или PowerShell), они будут работать кроссплатформенно и станут более доступными для различных веб-разработчиков (сегодня все говорят на JavaScript)
  • переименовывать wwwroot в public в противном случае структура папок в веб-приложениях Azure может привести к путанице (D:\Home\site\wwwroot\wwwroot против D:\Home\site\wwwroot\public)
  • Публиковать только скомпилированные выходные данные в веб-приложениях Azure (никогда не нажимайте node_modules на сервер веб-хостинга). Увидеть tools/deploy.js В качестве примера.

Посетите ASP.NET Core Starter Kit на GitHub (отказ от ответственности: я автор)

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

NPM (продвинутый уровень)

Сначала добавьте package.json в корневой каталог вашего проекта. Добавьте следующий контент:

{
  "version": "1.0.0",
  "name": "asp.net",
  "private": true,
  "devDependencies": {
    "gulp": "3.9.1",
    "del": "3.0.0"
  },
  "dependencies": {
    "jquery": "3.3.1",
    "jquery-validation": "1.17.0",
    "jquery-validation-unobtrusive": "3.2.10",
    "bootstrap": "3.3.7"
  }
}

Это заставит NPM загрузить Bootstrap, JQuery и другие библиотеки, которые используются в новом основном проекте asp.net, в папку с именем node_modules. Следующим шагом является копирование файлов в соответствующее место. Для этого мы будем использовать gulp, который также был загружен NPM. Затем добавьте новый файл в корневой каталог вашего проекта с именем gulpfile.js. Добавьте следующий контент:

/// <binding AfterBuild='default' Clean='clean' />
/*
This file is the main entry point for defining Gulp tasks and using Gulp plugins.
Click here to learn more. http://go.microsoft.com/fwlink/?LinkId=518007
*/

var gulp = require('gulp');
var del = require('del');

var nodeRoot = './node_modules/';
var targetPath = './wwwroot/lib/';

gulp.task('clean', function () {
    return del([targetPath + '/**/*']);
});

gulp.task('default', function () {
    gulp.src(nodeRoot + "bootstrap/dist/js/*").pipe(gulp.dest(targetPath + "/bootstrap/dist/js"));
    gulp.src(nodeRoot + "bootstrap/dist/css/*").pipe(gulp.dest(targetPath + "/bootstrap/dist/css"));
    gulp.src(nodeRoot + "bootstrap/dist/fonts/*").pipe(gulp.dest(targetPath + "/bootstrap/dist/fonts"));

    gulp.src(nodeRoot + "jquery/dist/jquery.js").pipe(gulp.dest(targetPath + "/jquery/dist"));
    gulp.src(nodeRoot + "jquery/dist/jquery.min.js").pipe(gulp.dest(targetPath + "/jquery/dist"));
    gulp.src(nodeRoot + "jquery/dist/jquery.min.map").pipe(gulp.dest(targetPath + "/jquery/dist"));

    gulp.src(nodeRoot + "jquery-validation/dist/*.js").pipe(gulp.dest(targetPath + "/jquery-validation/dist"));

    gulp.src(nodeRoot + "jquery-validation-unobtrusive/dist/*.js").pipe(gulp.dest(targetPath + "/jquery-validation-unobtrusive"));
});

Этот файл содержит код JavaScript, который выполняется при сборке и очистке проекта. Он скопирует все необходимые файлы в lib2 (не в lib - вы можете легко это изменить). Я использовал ту же структуру, что и в новом проекте, но файлы легко изменить в другое место. Если вы перемещаете файлы, убедитесь, что вы также обновили _Layout.cshtml. Обратите внимание, что все файлы в каталоге lib2 будут удалены при очистке проекта.

Если вы щелкнете правой кнопкой мыши по gulpfile.js, вы можете выбрать Task Runner Explorer. Отсюда вы можете запустить gulp вручную для копирования или очистки файлов.

Gulp также может быть полезен для других задач, таких как минимизация JavaScript и CSS-файлов:

https://docs.microsoft.com/en-us/aspnet/core/client-side/using-gulp?view=aspnetcore-2.1

Диспетчер библиотек (простой)

Щелкните правой кнопкой мыши по вашему проекту и выберите Управление клиентскими библиотеками. Файл libman.json теперь открыт. В этом файле вы указываете, какую библиотеку и файлы использовать и где они должны храниться локально. Действительно просто! Следующий файл копирует библиотеки по умолчанию, которые используются при создании нового проекта ASP.NET Core 2.1:

{
  "version": "1.0",
  "defaultProvider": "cdnjs",
  "libraries": [
    {
      "library": "jquery@3.3.1",
      "files": [ "jquery.js", "jquery.min.map", "jquery.min.js" ],
      "destination": "wwwroot/lib/jquery/dist/"
    },
    {
      "library": "jquery-validate@1.17.0",
      "files": [ "additional-methods.js", "additional-methods.min.js", "jquery.validate.js", "jquery.validate.min.js" ],
      "destination": "wwwroot/lib/jquery-validation/dist/"
    },
    {
      "library": "jquery-validation-unobtrusive@3.2.10",
      "files": [ "jquery.validate.unobtrusive.js", "jquery.validate.unobtrusive.min.js" ],
      "destination": "wwwroot/lib/jquery-validation-unobtrusive/"
    },
    {
      "library": "twitter-bootstrap@3.3.7",
      "files": [
        "css/bootstrap.css",
        "css/bootstrap.css.map",
        "css/bootstrap.min.css",
        "css/bootstrap.min.css.map",
        "css/bootstrap-theme.css",
        "css/bootstrap-theme.css.map",
        "css/bootstrap-theme.min.css",
        "css/bootstrap-theme.min.css.map",
        "fonts/glyphicons-halflings-regular.eot",
        "fonts/glyphicons-halflings-regular.svg",
        "fonts/glyphicons-halflings-regular.ttf",
        "fonts/glyphicons-halflings-regular.woff",
        "fonts/glyphicons-halflings-regular.woff2",
        "js/bootstrap.js",
        "js/bootstrap.min.js",
        "js/npm.js"
      ],
      "destination": "wwwroot/lib/bootstrap/dist"
    },
    {
      "library": "list.js@1.5.0",
      "files": [ "list.js", "list.min.js" ],
      "destination": "wwwroot/lib/listjs"
    }
  ]
}

Если вы перемещаете файлы, убедитесь, что вы также обновили _Layout.cshtml.

Установите Bundler и Minifier в расширениях Visual Studio

Затем вы создаете bundleconfig.json и введите следующее, как:

// Configure bundling and minification for the project.
// More info at https://go.microsoft.com/fwlink/?LinkId=808241
[
 {
    "outputFileName": "wwwroot/js/jquery.min.js",
    "inputFiles": [
      "node_modules/jquery/dist/jquery.js"
    ],
    // Optionally specify minification options
    "minify": {
      "enabled": true,
      "renameLocals": false
    },
    // Optionally generate .map file
    "sourceMap": false
  }
]

Таким образом, пакет и минификатор (на основе gulp) имеет доступ к исходным файлам (которые должны быть исключены из Visual Studio, а также исключены из GIT) и помещает их в wwwroot, как указано

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

Каков правильный подход для этого?

Есть много "правильных" подходов, вы просто должны решить, какой из них лучше всего соответствует вашим потребностям. Похоже, вы не понимаете, как использовать node_modules...

Если вы знакомы с NuGet, вы должны думать о npm как о его клиентской части. Где node_modules каталог похож на bin каталог для NuGet. Идея заключается в том, что этот каталог является обычным местом для хранения пакетов, на мой взгляд, лучше взять dependency на пакеты, которые вам нужны, как вы сделали в package.json, Тогда используйте бегуна задач, как Gulp например, чтобы скопировать нужные файлы в нужный wwwroot место нахождения.

Я написал сообщение в блоге об этом еще в январе, в котором подробно рассказывается о npm, Gulp и многих других деталях, которые актуальны и сегодня. Кроме того, кто-то привлек внимание к моему такому вопросу, который я задал, и в конечном итоге сам ответил на него, что, вероятно, полезно.

Я создал Gist это показывает gulpfile.js В качестве примера.

В вашем Startup.cs все еще важно использовать статические файлы:

app.UseStaticFiles();

Это обеспечит доступ вашего приложения к тому, что ему нужно.

Вместо того, чтобы пытаться обслуживать папку узловых модулей, вы также можете использовать Gulp для копирования того, что вам нужно для wwwroot.

https://docs.asp.net/en/latest/client-side/using-gulp.html

Это тоже может помочь

Visual Studio 2015 ASP.NET 5, задача Gulp не копирует файлы из узловых модулей

Намного более простой подход - использовать пакет Nuget OdeToCode.UseNodeModules. Я только что протестировал его с.Net Core 3.0. Все, что вам нужно сделать, это добавить пакет в решение и указать его в методе Configure класса Startup:

app.UseNodeModules();

Я узнал об этом из отличного курса " Создание веб-приложения с ASP.NET Core, MVC, Entity Framework Core, Bootstrap и Angular Pluralsight" Шона Вильдермута.

У Шона Вильдермута есть хороший гид здесь: https://wildermuth.com/2017/11/19/ASP-NET-Core-2-0-and-the-End-of-Bower

Статья ссылается на gulpfile на GitHub, где он реализовал стратегию в статье. Вы можете просто скопировать и вставить большую часть содержимого gulpfile в свой файл, но обязательно добавьте соответствующие пакеты в package.json в devDependencies: gulp gulp-uglify gulp-concat rimraf merge-stream

Я нашел лучший способ управления пакетами JS в моем проекте с помощью обработчиков задач NPM Gulp/Grunt. Мне не нравится идея иметь NPM с другим слоем библиотеки javascript для обработки "автоматизации", и мое требование номер один - просто запустить обновление npm, не беспокоясь о том, нужно ли мне запускать gulp вещи, если он успешно все скопировал и наоборот.

NPM способ:

  • Минификатор JS уже включен в ядро ​​ASP.net, поищите bundleconfig.json, так что это не проблема для меня (не компилируя что-то нестандартное)
  • Преимущество NPM в том, что у него хорошая файловая структура, поэтому я всегда могу найти предварительно скомпилированные / минимизированные версии зависимостей в файле node_modules/module/dist
  • Я использую сценарий NPM node_modules/.hooks/{eventname}, который обрабатывает копирование / обновление / удаление файлов Project / wwwroot / lib / module / dist / .js, вы можете найти документацию здесь https://docs.npmjs.com/misc/scriptsобновлю скрипт, который я использую для git, как только он будет более отшлифован). Мне не нужны дополнительные исполнители задач ( инструменты.js, которые мне не нравятся) что делает мой проект чистым и простым.

Путь питона:

https://pypi.python.org/pyp... но в этом случае вам нужно поддерживать источники вручную

Пожалуйста, извините за длину этого поста.

Это рабочий пример использования ASP.NET Core версии 2.5.

Следует отметить, что project.json устарел ( см. Здесь) в пользу .csproj. Проблема с .csproj. Файл - это большое количество функций и тот факт, что для его документации нет центрального расположения ( см. здесь).

Еще одна вещь, этот пример - запуск ядра ASP.NET в контейнере Docker Linux (alpine 3.9); поэтому пути будут отражать это. Он также использует gulp ^4.0. Однако с некоторыми изменениями он должен работать с более старыми версиями ASP.NET Core, Gulp, NodeJS, а также без Docker.

Но вот ответ:

gulpfile.js увидеть реальный рабочий пример здесь

// ROOT and OUT_DIR are defined in the file above. The OUT_DIR value comes from .NET Core when ASP.net us built.
const paths = {
    styles: {
        src: `${ROOT}/scss/**/*.scss`,
        dest: `${OUT_DIR}/css`
    },
    bootstrap: {
        src: [
            `${ROOT}/node_modules/bootstrap/dist/css/bootstrap.min.css`,
            `${ROOT}/node_modules/startbootstrap-creative/css/creative.min.css`
        ],
        dest: `${OUT_DIR}/css`
    },
    fonts: {// enter correct paths for font-awsome here.
        src: [
            `${ROOT}/node_modules/fontawesome/...`,
        ],
        dest: `${OUT_DIR}/fonts`
    },
    js: {
        src: `${ROOT}/js/**/*.js`,
        dest: `${OUT_DIR}/js`
    },
    vendorJs: {
        src: [
            `${ROOT}/node_modules/jquery/dist/jquery.min.js`
            `${ROOT}/node_modules/bootstrap/dist/js/bootstrap.min.js`
        ],
        dest: `${OUT_DIR}/js`
    }
};

// Copy files from node_modules folder to the OUT_DIR.
let fonts = () => {
    return gulp
        .src(paths.styles.src)
        .pipe(gulp.dest(paths.styles.dest));
};

// This compiles all the vendor JS files into one, jsut remove the concat to keep them seperate.
let vendorJs = () => {
    return gulp
        .src(paths.vendorJs.src)
        .pipe(concat('vendor.js'))
        .pipe(gulp.dest(paths.vendorJs.dest));
}

// Build vendorJs before my other files, then build all other files in parallel to save time.
let build = gulp.series(vendorJs, gulp.parallel(js, styles, bootstrap));

module.exports = {// Only add what we intend to use externally.
    default: build,
    watch
};

Добавьте цель в файл .csproj. Обратите внимание, что мы также добавили часы для просмотра и исключения, если мы используем в своих интересах dotnet run watch команда.

app.csprod

  <ItemGroup>
    <Watch Include="gulpfile.js;js/**/*.js;scss/**/*.scss" Exclude="node_modules/**/*;bin/**/*;obj/**/*" />
  </ItemGroup>

  <Target Name="BuildFrontend" BeforeTargets="Build">
    <Exec Command="yarn install" />
    <Exec Command="yarn run build -o $(OutputPath)" />
  </Target>

Теперь когда dotnet run build Он также установит и соберет модули узлов.

Другие вопросы по тегам