- PVSM.RU - https://www.pvsm.ru -
Данная стать является переводом вот этой статьи [1]. В ней я расскажу вам как удобно использовать Vue.js фреймворк при разработке приложения на ASP.NET MVC
Последние несколько месяцев я присматривал разные JavaScript фреймворки для интеграции их в свои MVC проекты. Как только вы выбираете что-то в духе React, Angular или Ember для работы с .NET вы должны устанавливать модули-адаптеры, переписывать всю логику маршрутизации для всех контроллеров. В конце концов это влияет на все веб-приложение, когда ты хочешь работать бок о бок с уже готовым рабочим стеком. Если вы начинаете новый проект, целесообразно использовать Web API для бэкенда, который обеспечит REST интерфейс для JS фреймворка на ваш выбор, но для существующих проектов на MVC это не выход.
После небольших исследований я наткнулся на Vue.JS и после некоторых экспериментов мне удалось заставить его работать в связке с MVC. Vue.JS – относительно легковесный фреймворк, так что я могу добавить его в представление, где мне нужны дополнительные возможности JS, тем самым оставляя остальную часть веб-приложения нетронутой. Теперь, когда я использую Vue в продакшене я рад поделится с вами некоторыми примерами, для всех кто разделяет мои проблемы. Мой рабочий процесс заимствует концепции Migel’s Castro [тык [2]] где он использовал ее для интеграции AngularJS с MVC.
{
"version": "1.0.0",
"Name": "ASP.NET",
"private": true,
"devDependencies": {
},
"dependencies": {
"vue": "^1.0.26"
}
}
Теперь давайте использовать Vue в нашем представлений
@{
ViewBag.Title = "Home Page";
}
@Scripts.Render("~/node_modules/vue/dist/vue.min.js")
@{
ViewBag.Title = "Home Page";
}
<div id="app">
</div>
@Scripts.Render("~/node_modules/vue/dist/vue.min.js")
<script>
const v = new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
})
</script>
Очевидно, что поместив все ваши скрипты в наше представления — это гарантированный путь к головные боли, поэтому сейчас давайте организуем наш код.
Когда у нас есть большое приложение построенное на VueJS, я разделяю проект на части. Для каждого контроллера у меня есть соответствующее приложение Vue, которое состоит из нескольких Vue компонентов. Отношение один-к-одному между контроллером и приложением делает наш код более читаемым и удобным в поддержке. Каждый модуль приложения будет содержать только JavaScript библиотеки, которые нужны, а не один большой пакет.
JavaScript упаковщики улучшились за эти годы. Browserify позволяет вам использовать стиль node.js модулей для работы в браузере. Мы определяем зависимости и потом Browserify собирает их в один маленький и чистенький JavaScript файл. Вы подключаете ваши JavaScript файлы используя require("./ваш_файл.js"); выражение. Это позволяет нам использовать только те библиотеки, которые нужны. Так что теперь, если мы предположим, каждый контроллер представляет собой некоторый контейнер, каждый из которых содержит один или больше js файлов. Эти файлы затем будут помещены в пакет, который будет размещен в папке нашего проекта, который затем загружает и использует браузер.
Я обычно следую следующую структуру. Весь мой Vue код помещен в папку ViewModel. Для каждого контроллера, который использует Vue я создаю под папку в папке ViewModel, и затем я вызываю контейнеры в файле main.js. После этого я использую Gulp и Browserify для упаковки всех файлов в пакет, который хранится в папке проекта. Представления ссылаются на наши пакеты и когда браузер запрашивает страницу пакет скачивается и запускается.
"devDependencies": {
"browserify": "^13.0.0",
"watchify": "^3.7.0",
"gulp": "^3.9.1",
"gulp-util": "^3.0.7",
"gulp-babel": "^6.1.2",
"gulp-uglify": "^2.0.0",
"gulp-sourcemaps": "^1.6.0",
"fs-path": "^0.0.22",
"vinyl-source-stream": "^1.1.0",
"vinyl-buffer": "^1.0.0",
"babel-preset-es2015": "^6.13.2"
}
const gulp = require('gulp');
const gutil = require('gulp-util');
var babel = require('gulp-babel');
var minify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
const fs = require('fs');
const path = require('path');
const browserify = require('browserify');
const watchify = require('watchify');
const fsPath = require('fs-path');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var es2015 = require('babel-preset-es2015');
function getFolders(dir) {
return fs.readdirSync(dir)
.filter(function (file) {
return fs.statSync(path.join(dir, file)).isDirectory();
});
}
const paths = [
process.env.INIT_CWD + '\ViewModels\home',
process.env.INIT_CWD + '\ViewModels\home\components',
process.env.INIT_CWD + '\ViewModels\common\components'
];
function watchFolder(input, output) {
var b = browserify({
entries: [input],
cache: {},
packageCache: {},
plugin: [watchify],
basedir: process.env.INIT_CWD,
paths: paths
});
function bundle() {
b.bundle()
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
//.pipe(babel({ compact: false, presets: ['es2015'] }))
// Add transformation tasks to the pipeline here.
//.pipe(minify())
// .on('error', gutil.log)
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(output));
gutil.log("Bundle rebuilt!");
}
b.on('update', bundle);
bundle();
}
function compileJS(input, output) {
// set up the browserify instance on a task basis
var b = browserify({
debug: true,
entries: [input],
basedir: process.env.INIT_CWD,
paths: paths
});
return b.bundle()
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(babel({ compact: false, presets: ['es2015'] }))
// Add transformation tasks to the pipeline here.
.pipe(minify())
.on('error', gutil.log)
//.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(output));
}
const scriptsPath = 'ViewModels';
gulp.task('build', function () {
var folders = getFolders(scriptsPath);
gutil.log(folders);
folders.map(function (folder) {
compileJS(scriptsPath + "//" + folder + "//main.js", "Scripts//app//" + folder);
});
});
gulp.task('default', function () {
var folders = getFolders(scriptsPath);
gutil.log(folders);
folders.map(function (folder) {
watchFolder(scriptsPath + "//" + folder + "//main.js", "Scripts//app//" + folder);
});
});
Теперь вернемся к коду
const Vue = require('vue');
const v = new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
});
@{
ViewBag.Title = "Home Page";
}
<div id="app">
{ { message } }
</div>
@Scripts.Render("~/Scripts/app/home/bundle.js")
@{
var controllerName = HttpContext.Current.Request.RequestContext.RouteData.Values["Controller"].ToString();
}
@Scripts.Render("~/Scripts/app/" + controllerName + "/bundle.js")
С помощью Vue мы можем извлекать и отображать данные которые приходят с сервера.
public JsonResult GetData()
{
return Json(new
{
Name = "Marco",
Surname = "Muscat",
Description = "Vue data loaded from razor!"
},JsonRequestBehavior.AllowGet);
}
const Vue = require("vue");
const $ = require("jquery");
const v = new Vue({
el: '#app',
ready: function () {
this.loadData();
},
data: {
message: 'Hello Vue.js!',
serverData: null
},
methods: {
loadData: function (viewerUserId, posterUserId) {
const that = this;
$.ajax({
contentType: "application/json",
dataType: "json",
url: window.ServerUrl + "/Home/GetData",
method: "GET",
success: function (response) {
console.log(response);
that.$data.serverData = response;
},
error: function () {
console.log("Oops");
}
});
}
}
})
@{
var controllerName = HttpContext.Current.Request.RequestContext.RouteData.Values["Controller"].ToString();
var serverUrl = string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, Url.Content("~"));
var controllerUrl = Url.Content("~") + controllerName;
}
<script>
window.ServerUrl = '@serverUrl';
window.VueRouterUrl = '@controllerUrl';
</script>
@Scripts.Render("~/Scripts/app/" + controllerName + "/bundle.js")
<div id="app">
{ { message } }
<br/>
<span>coming straight from mvc! { {serverData.Name} } { {serverData.Surname} }. { {serverData.Description} }</span>
</div>
На данный момент этого достаточно для того чтобы использовать vue.js и MVC, но, как и любые другие фронтэнд фреймворки, vue страдает от задержек при загрузке. При запросе страницы, JavaScript должен быть загружен и запущен. Затем фреймворк делает еще несколько запросов к серверу для получения данных. Для предотвращения этого мы должны прибегнуть к анимации загрузки и к другим хакам, но так как мы также используем MVC мы можем сделать лучше и ускорить процесс загрузки, за счет использования Razor и загрузки данных для представления вместе с остальной частью страницы.
Многие JS фреймворки сейчас работают над различными реализациями предварительного рендеринга, но с .NET стеком мы можем придумать альтернативное решение.
Предупреждение! Этот метод подойдет в том случае если в начальной загрузке участвует небольшое количество данных. В других случаях лучше использовать пагинацию или AJAX запросы для достижения более плавной загрузки страницы и уменьшить задержки.
Давайте приступим к созданию. Для этого будем использовать проект, который мы сделали раньше.
public ActionResult Index()
{
var serverModel = JsonConvert.SerializeObject(new
{
Name = "Marco",
Surname = "Muscat",
Description = "Vue data loaded from razor!"
});
return View(new SampleModel()
{
Data = serverModel
});
}
public class SampleModel
{
public string Name { get; set; }
public string Surname { get; set; }
public string Description { get; set; }
public string Data { get; set; }
}
<script> window.preLoadeddata = JSON.parse('@Html.Raw(Model.Data)')</script>
const Vue = require("vue");
const $ = require("jquery");
const v = new Vue({
el: '#app',
ready: function () {
},
data: {
message: 'Hello Vue.js!',
serverData: window.preLoadeddata
},
methods: {
}
})
Наш проект не может считаться законченным пока в нем отсутствует маршрутизация. Более сложные приложения потребуется несколько представлений, так как там слишком много информации на одной странице. Использование Vue вместе с MVC это круто, так как мы можем загружать все наши представления оставаясь на одной странице без полной перезагрузки.
const Vue = require("vue");
const VueRouter = require("vue-router");
Vue.use(VueRouter);
var Foo = Vue.extend({
template: '<p>This is foo!</p>'
});
var Bar = Vue.extend({
template: '<p>This is bar!</p>'
});
var App = Vue.extend({});
var router = new VueRouter({
history: true,
root: "/vue-example/vuerouting"
});
router.map({
'/foo': {
component: Foo
},
'/bar': {
component: Bar
}
});
router.start(App, '#app');
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- use v-link directive for navigation. -->
<a v-link="{ path: '/foo' }">Go to Foo</a>
<a v-link="{ path: '/bar' }">Go to Bar</a>
</p>
<!-- route outlet -->
<router-view></router-view>
</div>
Результат может показаться очень хорошим, но это еще не все. В нас осталась проблема. Если мы скопируем наш адрес с Vue компонентом (http://localhost/vue-example/vuerouting/bar) и вставите ее в новую вкладку у нас вылезет 404 ошибка потому что сервер не в состоянии найти данный маршрут. Нам нужно настроить маршрутизацию на сервере так чтобы он игнорировал наши Vue маршруты.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}/",
defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional},
constraints:new { controller = "Home"}
);
routes.MapRoute(
name: "Silo Controller",
url: "{controller}/{*.}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { controller = "examplevuerouter|ExampleSeedingRazor" }
);
var router = new VueRouter({
history: true,
root: "/vue-example/vuerouting"
});
var router = new VueRouter({
history: true,
root: window.VueRouterUrl
});
Ссылка на оригинал: тык [1].
Я надеюсь, что информация, которую я предоставил в данной статье поможет вам при создании ваших проектов с использованием VueJS и MVC. Если у вас есть замечания, вопросы и предложения прошу оставить их в комментариях.
Спасибо всем за прочтение.
Автор: bogdanstefanjuk
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-2/249863
Ссылки в тексте:
[1] статьи: http://www.lambdatwist.com/dot-net-vuejs/
[2] тык: http://www.codemag.com/article/1605081
[3] localhost/vue-example/vuerouting: http://localhost/vue-example/vuerouting
[4] localhost/vue-example/vuerouting/bar: http://localhost/vue-example/vuerouting/bar
[5] Источник: https://habrahabr.ru/post/324176/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox
Нажмите здесь для печати.