После большого успеха использования DotNetCore MVC для создания приложений Vue я хотел попробовать Vue Router для создания приложения SPA. Оказывается, это была простая задача.
Осталось только как-то защитить страницы vue на основе учетных данных для входа. Пожалуйста, дайте мне несколько советов по этому поводу!
Ключевым моментом снова является использование @ Html.Partial (components.cshtml или pages.cshtml) для импорта ваших отдельных страниц или компонентов. Это не совсем настоящие одностраничные компоненты Vue, но они очень близки. Поскольку я использую Vuetify в качестве глобального пользовательского интерфейса, мне не нужны разделы стилей в моих компонентах.
Полный исходный код примера проекта можно скачать здесь: https://www.dropbox.com/s/yoce5z3uwo98l9x/vuespa.zip?dl=0
И снова я использую страницу _layout для загрузки всех необходимых файлов CSS и JS из CDN. Поскольку мое приложение в любом случае не будет работать без подключения к Интернету, мне нравится использовать CDN вместо того, чтобы загружать и управлять всеми зависимостями. Однако на этот раз я использую только страницу _layout для настройки html-страницы, и все панели инструментов, страницы, компоненты и т. Д. Встраиваются в страницу index.cshtml.
_layout.cshtml
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"> <title>@ViewData["Title"] SPA</title> <link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet"> <link href="https://unpkg.com/[email protected]/dist/vuetify.min.css" rel="stylesheet"> <link href="css/site.css" rel="stylesheet"> @RenderSection("css", required: false) </head> <body> <style> </style> <div class="container body-content"> @RenderBody() </div> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/moment.min.js"></script> <script src="https://unpkg.com/[email protected]/dist/vee-validate.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <script src="https://unpkg.com/[email protected]/dist/vuetify.min.js"></script> @RenderSection("js", required: false) </body> </html>
Теперь, используя страницу razor index.cshtml, мы создаем наше одностраничное приложение vuejs. На этой странице нам нужно встроить все страницы и компоненты vue с помощью Html.Partial (). Мы также добавляем маршруты для Vue Router.
index.cshtml
@page @model IndexModel @{ ViewData["Title"] = "Home page"; } <div id="app" v-cloak> <v-app> <spa-toolbar :title="msg"></spa-toolbar> <router-view></router-view> </v-app> </div> @section js { <script src="~/js/site.js"></script> @Html.Partial("~/Pages/VueComponents/Toolbar.cshtml") @Html.Partial("~/Pages/VuePages/Home.cshtml") @Html.Partial("~/Pages/VuePages/Airports.cshtml") @Html.Partial("~/Pages/VuePages/Components/AirportCard.cshtml") <script> Vue.use(VueRouter); const routes = [ { path: '/', component: spaHome }, { path: '/airports', component: spaAirports } ] const router = new VueRouter({ routes // short for `routes: routes` }) Vue.use(VeeValidate); var app = new Vue({ el: '#app', watch: { }, data: { msg: 'Welcome To Vue SPA' }, methods: { }, router }).$mount('#app') </script> }
Toolbar.cshtml
<template id="spa-toolbar"> <div> <v-toolbar dark color="primary" class="mb-2"> <v-layout align-center> <v-toolbar-side-icon></v-toolbar-side-icon> <v-toolbar-title class="white--text">{{title}}</v-toolbar-title> <v-spacer></v-spacer> <v-toolbar-items> <router-link to="/"><v-btn flat>Home</v-btn></router-link> <router-link to="/airports"><v-btn flat>Airports</v-btn></router-link> </v-toolbar-items> </v-layout> </v-toolbar> </div> </template> <script> Vue.component('spa-toolbar', { template: '#spa-toolbar', props: ['title'], $_veeValidate: { validator: 'new' }, }) </script>
Вот простая домашняя страница. На этом мы закончили. Страница аэропорта, включенная в проект, является примером страницы, которая вызывает API для получения данных JSON и использует компонент для визуализации данных.
Airports.cshtml
<template id="spa-airports"> <div> <h3>Airport Delays</h3> <v-card class="mb-2"> <v-card-media height="400" src="https://dsx.weather.com/util/image/map/airport_delays_1280x720.jpg?v=ap&w=1280&h=720&api=7db9fe61-7414-47b5-9871-e17d87b8b6a0"> </v-card-media> <v-card-text> <v-layout row> <v-flex xs12> <v-select :items="airportsUS" v-on:input="Lookup" v-model="airportCode" autocomplete item-text="longname" item-value="iata" prepend-icon="clear" :prepend-icon-cb="ClearList()" label="Airports"> </v-select> </v-flex> </v-layout> </v-card-text> </v-card> <card-airport :airport="airportData"></card-airport> </div> </template> <script> var spaAirports = Vue.component('Airports', { template: '#spa-airports', props: ['title'], $_veeValidate: { validator: 'new' }, computed: { airportsUS: function() { var items = this.airportList.filter(function (item) { item.longname = item.iata + ' - ' + item.name; return item.iso == 'US'; }); return items; } }, data: function () { return { airportCode: '', airportData: { name: null, status: { reason: '' } }, airportList: iata } }, methods: { Lookup: function () { let self = this; let url = 'http://services.faa.gov/airport/status/' + this.airportCode + '?format=application/json'; axios.get(url) .then(function (response) { self.airportData = response.data; //console.log(response); self.loading = false; }) .catch(function (error) { self.airportData = { name: null, status: { reason: '' } }; console.log('error', error); }); }, ClearList: function() { console.log(this.airportCode, this.airportData); this.airportCode = ''; } } }) </script>
AirportCard.cshtml
<template id="card-airport"> <div v-if="airport.name!=null"> <v-card dark :color="this.airport.delay==='true' ? 'error' : 'success' "> <v-card-text> <h4>{{airport.IATA}} {{airport.name}} {{airport.city}} {{airport.state}} {{airport.weather.meta.updated}}</h4> <v-spacer></v-spacer> <h5> {{airport.status.reason}} {{airport.status.minDelay}} {{airport.delay!='true' ? '' : ' to '}} {{airport.status.maxDelay}} {{airport.status.type}} {{airport.status.trend}} </h5> <h5> temp: {{airport.weather.temp}}, visibility: {{airport.weather.visibility}}, weather: {{airport.weather.weather}}, wind: {{airport.weather.wind}} </h5> </v-card-text> </v-card> </div> </template> <script> Vue.component('card-airport', { template: '#card-airport', props: ['airport'], $_veeValidate: { validator: 'new' }, methods: { } }) </script>
Так получилась верстка проекта.