目录

介绍

单页应用程序的演变(SPA)

三大框架概述

Angular开发者的Vue.js

学习Vue.js

Vue.js页面的剖析

构建示例应用程序

TypeScript的案例

入门——Vue.js CLI 3.0

Vuetify和Material Design

开始和运行

主Vue.js TypeScript页面

装饰器和基于类的组件

原始Vanilla JavaScript的主Vue.js页面

单独的HTML模板文件

Vuex

Vuex存储

用户信息视图模型

管理存储大小和代码库

共享的Vue.js组件

Vue.js路由器

Vue.js路由器导航保护

HTTP RESTful Web API支持

实现HTTP单例服务

Axios HTTP拦截器

登录组件

Vuetify表单验证

Vue.js过滤器

TypeScript兼容性和类型定义文件

定制全局过滤器

Vue.js Mixins

生产部署

Vue.js扩展包

Vue DevTools浏览器扩展

总结

先决条件和运行示例应用程序


下载Web API  下载JavaScript版本下载    TypeScript版本

介绍

三巨头时代。如果你是一名职业篮球迷,你可以把“三巨头”这个词当作三个篮球运动员——勒布朗·詹姆斯,德维恩·韦德和克里斯·波什——他们从2010年至2014年为迈阿密热火队的全国篮球协会(NBA)效力在轰动一时的2010年NBA自由球员经纪公司(nba free agency)中,热火引发了一场重大的权力转移,开启了“三巨头”时代,开始了一个历史上伟大的混乱时期。我们现在可以肯定地说,这支球队是NBA历史上最有趣的球队。在生活的其他领域,“三巨头”通常引用任何给定分组或主题中的三个最突出的实体。以男子职业网球运动的现状为例。目前网球仍然在网球“三巨头”的大拇指下——罗杰·费德勒,拉斐尔·纳达尔和诺瓦克·德约科维奇。自斯坦瓦林卡赢得2016年美国公开赛以来,网球的“三巨头”现在赢得了所有八个大满贯。

在Web开发领域,当涉及到单页应用程序(SPAs)时,我们现在也有了“三巨头”。 单页应用程序正变得越来越流行。Facebook,YouTube,Twitter,GitHub,Google和许多其他服务都是使用SPA技术构建的。在这十年中,我们看到了三个主要SPA框架的兴趣增长:Angular,React和Vue.js,与其他框架如Ember.JS,Aurelia,Meteor.Js等的兴趣索然。

单页应用程序的演变(SPA)

在基于Web的应用程序开发的早期阶段,JavaScript和jQuery为前端的Web开发带来了重大进步。它提供了简单有效的开箱即用功能,例如客户端验证,模态窗口,警报消息,动画,甚至基于Ajax的部分页面更新。进入单页应用程序框架和库的世界。单页面应用程序是基于Web的应用程序,它可以加载单个HTML页面,并在用户与应用程序交互时动态更新页面。SPA使用AJAX和HTML5创建流畅且响应迅速的Web应用程序前端,而无需不断页面重新加载。但是,这意味着大部分工作都发生在客户端,在JavaScript中。单页应用这一术语的起源尚不清楚,尽管该概念至少早在2003年就被讨论过,但通常被描述为在Web应用程序中使用JavaScript显示用户界面(UI)、运行应用程序逻辑以及与Web服务器通信的独立网站。

三大框架概述

欢迎来到现在的JavaScript世界。像Angular,React.js或Vue.js这样的框架变得流行,因为它们解决了jQuery未解决的基本问题:跟踪我们页面的状态,DOM并与之交互,而无需自己控制所有内容或执行完整的操作页面刷新。以下是“三巨头”SPA框架和库的摘要:

  • 由Google开发的Angular于2010年首次发布,使其成为最古老的。尽管AngularJS(版本1)仍然获得更新,但Angular 2的发布(以及原始名称中的“JS”的删除——AngularJS)发生了重大转变,因为Angular框架在TypeScript中被完全重写。Angular 2+简称为Angular。
  • 由Facebook开发的React最初于2013年发布。Facebook在其产品(Facebook,Instagram和WhatsApp)中广泛使用React。
  • Vue,也称为Vue.js,是该组中最年轻的成员。它是由前谷歌员工Evan You在2014年开发的。在过去几年中,尽管Vue.js没有大公司的支持,但它的受欢迎程度已大幅下降。对于Vue.Js的第3版,Vue的贡献者和支持者正在使用TypeScript重写框架。

Angular开发者的Vue.js

如果你像我一样,并且自它被早期采用以来一直在开发单页应用程序,那么你可能已经使用Angular 1编写了一个或两个或更多的SPA,并在2016年发布时使用TypeScript转移到Angular 2。作为软件开发人员和架构师,我们需要精通几项技术,但由于时间有限,我们必须仔细挑选我们如何花时间。当然,您不想花费五年时间学习和使用Microsoft的Silverlight技术,只是为了看到Web应用程序开发社区完全放弃它或从不接受它。为此,我决定换档并深入Vue.js。这篇文章来自于我花了很多时间在Angular Spa世界的工作,它将介绍我学习Vue.js的经验,最初使用的是普通的老版本的Vanilla JavaScript,但最终转向使用typescript开发Vue.js

学习Vue.js

如果你是一个刚刚开始使用Vue.js的Angular背景的开发人员,那么跳进去可能既令人兴奋又令人不知所措。虽然每个人的学习过程都非常不同,但我发现以下路径对我的帮助最大:

  • 访问Vue.js网站以获取Vue的概述
  • 下载并安装Vue.js及其随附的CLI工具
  • 使用Vue.js CLI在Scaffold上创建默认的Vue.js Hello World应用程序
  • 在冒险进入TypeScript世界之前,先使用vanilla JavaScript学习Vue.js
  • 通过plurasight.com或Udemy.com支付在线课程费用--Maximillian Schwarzmuller关于Udemy的完整Vue.js课程非常好,非常值得它的低价标签
  • 了解Vue.js的主要组件和方面,包括Vue Router和Vuex
  • 学习Vue.js UI框架,例如Vuetify for Material Design
  • 熟悉一些Vue.js开发工具
  • 使用Vue.js开发一个小应用程序

Vue.js页面的剖析

每个Vue.js单页应用程序的核心都是Vue文件。Vue文件以“.vue”扩展名结尾,并在其中写入三个标记:一个用于template标记内的HTML模板,一个用于控制script标记内部页面的JavaScript,最后有一个style标记将CSS应用于页面。也可以使用Style标记上的Scoped属性将CSS样式的范围限定为仅应用于当前页。Vue页面还包括数据属性,方法,计算属性,观察者,生命周期事件和其他属性。Computed properties和watchers是Vue.js中最基本的两个概念。对于Angular开发人员,Vue的计算属性和观察者很像Angular的可观察对象,并提供了一种观察和响应Vue实例上的数据更改的方法。默认情况下,Vue页面包含单个文件中包含的页面的所有HTML,JavaScript和CSS。与Angular一样,Vue.js使用基于HTML的模板语法,允许您以声明方式将呈现的DOM绑定到基础Vue实例的数据。

//
// orders.vue
//
<template><v-layout><!---xs12 sm6 offset-sm3--><v-flex><v-card><v-card-title primary-title><div><h3 class="headline mb-0">Orders</h3></div></v-card-title><v-data-table class="elevation-1" :headers="headers" :hide-actions="true" :loading="loading" :items="orderInquiryViewModel.orders"><v-progress-linear v-slot:progress color="blue" indeterminate></v-progress-linear><template v-slot:items="props"><td style="min-width: 50px; max-width: 50px; width:50px;">{{ props.item.orderNumber }}</td><td style="min-width:50px; max-width: 50px; width:50px;">{{ props.item.orderDate | moment("MM/DD/YYYY") }}</td></template><template v-slot:no-data><v-alert :value="displayNoDataMessage" color="error" icon="warning">There are no orders</v-alert></template></v-data-table></v-card></v-flex></v-layout>
</template><style scoped>
a {color: white;
}
</style><script>
import { OrderInquiryViewModel } from "../viewmodels/order-inquiry.viewmodel";
import { HttpService } from "../services/http-service";export default {components: {HttpService},data() {return {httpService: null,alertMessage: null,orderInquiryViewModel: null,headers: [{text: "Order Number",align: "left",sortable: false,value: "OrderNumber"},{ text: "Order Date", value: "OrderDate", sortable: false },{ text: "Customer Name", value: "CustomerName", sortable: false },{ text: "Product #", value: "ProductNumber", sortable: false },{ text: "Description", value: "Description", sortable: false },{text: "Order Quantity",align: "right",value: "OrderQuantity",sortable: false},{text: "Unit Price",align: "right",value: "UnitPrice",sortable: false}],loading: true,displayNoDataMessage: false};},methods: {initializeSearch() {this.orderInquiryViewModel.orders = [];},displayOrders(response) {this.orderInquiryViewModel.orders = response.entity;this.loading = false;if (this.orderInquiryViewModel.orders.length == 0) {this.displayNoDataMessage = true;}},displayServerError: function(response) {this.loading = false;store.dispatch("alert/error", response.returnMessage[0]);},executeSearch() {this.httpService.getOrders().then(function(response) {if (response.returnStatus == true) {this.displayOrders(response);} else {this.displayServerError(response);}});}},created() {this.orderInquiryViewModel = new OrderInquiryViewModel();this.httpService = new HttpService(this.$http);this.displayNoDataMessage = false;},mounted() {this.initializeSearch();this.executeSearch();}
};
</script>

构建示例应用程序

本文的示例应用程序是一个小型购物车应用程序,其中包含最新版本的Angular 2和TypeScript用于前端。对于本文,我将介绍前端应用程序的开发,但这次使用最新版本的Vue.js,同时将TypeScript合并到一起。本文示例应用程序的后端将使用MongoDB数据库和.NET Core 2后端项目。

TypeScript的案例

我开始使用vanilla JavaScript学习Vue.js,最初使用带有vanilla JavaScript的Vue.js为本文开发了示例应用程序的整个前端。我觉得这是学习Vue.js框架的最佳方式,因为Vue.js上的大多数在线课程和文章都基于vanilla JavaScript。但作为一名Angular 2开发人员,我开始怀念TypeScript。TypeScript是Microsoft开发和维护的一种开源编程语言。它是一个严格的语法超类JavaScript集,并为该语言添加静态类型。我喜欢TypeScript,但对于大多数开发者来说,这是一种后天的品味。与所有技术一样,JavaScript开发也深深植根于大多数JavaScript开发人员喜欢的标准和传统。毕竟,TypeScript代码只是简单地编译成最新版本的JavaScript。

我喜欢使用TypeScript开发的一些内容包括:

  • 变量声明——在TypeScript中,必须先声明变量才能使用它。这是我最喜欢的TypeScript功能。Vanilla JavaScript开发可能会导致错误,因为变量拼写错误并且在测试过程中从未捕获过。如果我们尝试将值分配给不存在或由于未被声明或拼写错误而未找到的变量,则TypeScript编译器将生成错误。
  • 语法糖——TypeScript附带了一种语法,可以改善JavaScript的外观和感觉,包括类,接口和箭头函数。这似乎是JavaScript语言的一种非常自然的扩展,也是ECMA规范中采用许多TypeScript功能的原因之一。
  • 类型/静态类型——静态类型语言可以最大限度地减少您所犯错误的数量并改进代码分析,这样您的所有工具(如IDE)都可以为您提供提示,帮助和适当的重构。如果我们尝试将值分配给不同类型的变量,TypeScript编译器也会生成错误。
  • 未来就是现在——如前所述,许多TypeScript功能已成为ECMA规范的一部分。并且由于编译(转换),您不需要等待浏览器采用。此外,Angular和下一版本的Vue.js(版本3)都是使用TypeScript在内部完全开发的,可以肯定地说TypeScript在可预见的未来仍然存在,甚至可能成为JavaScript开发标准。

在一天结束时,开发没有错误的软件很难。我对我们现在拥有的所有新工具感到兴奋,包括CLI工具,连接器,当然还有TypeScript,可以帮助我们生产更少的错误软件。

入门——Vue.js CLI 3.0

与Angular一样,Vue.js提供了一个用于快速Vue.js开发的完整系统。Vue CLI 3.0版安装最新版本的Vue.js框架,并提供完整的项目创建和脚手架。Vue CLI旨在成为整个Vue.js生态系统的标准工具基准,包括安装插件,与webpack集成,以便在开发和生产部署功能期间更换热模块。

安装Vue CLI后,您可以按如下方式创建新的Vue项目:

vue create hello-world

系统将提示您选择预设。您可以选择默认预设,也可以选择“手动选择功能”以选择所需的功能。对于示例应用程序,我选择了以下功能:Babel,TypeScript,Router,Vuex,Linting和Formatting。此外,您还可以使用图形界面创建和管理项目, vue ui命令将打开CLI UI,使项目创建和设置更加直观。

创建后,您可以使用以下CLI命令编译,构建和启动webpack开发服务器并访问localhost:8080上的应用程序:

npm run serve

Vuetify和Material Design

在开发应用程序之前,确定如何实现用户界面非常重要。就像选择任何其他库或框架一样,在选择UI框架或库时应该仔细研究。这些UI框架和库通常带有专有语法和/或符号。一旦您的应用程序变大,您将与该框架结合——比您选择的加下划线JavaScript框架更多。Material Design是最新趋势之一。Material是一个适应性指南,组件和工具系统,支持用户界面设计的最佳实践。在开源代码的支持下,Material简化了设计人员和开发人员之间的协作,并帮助团队快速构建精美的产品。对于示例应用程序,我为应用程序Material Design UI框架选择了Vuetify。Vuetify符合Material设计规范。这意味着Vue.js和Material的核心功能将默认可用,并且可以由两个社区进行改进。Vuetify还通过其vue-cli-3插件支持Vue.js工具的未来。

Vuetify也可以使用Vue CLI UI安装,或者从命令行安装,如下所示:

Vue add vuetify

开始和运行

作为Angular开发人员,您会注意到的一件事是,使用Vue.js启动和运行起来要容易得多。对于示例应用程序,它都以下面的main.ts TypeScript文件开头。您只需使用Vue.js注册Vuetify并创建Vue实例并将其附加/挂载到HTML元素。您不必配置任何模块或任何其他复杂配置,如Angular要求。

// main.tsimport Vue from "vue";
import AppComponent from "./app.component.vue";
import router from "./router";
import "./plugins/vuetify";
import "./registerServiceWorker";
import Vuetify from "vuetify";
import colors from "vuetify/es5/util/colors";Vue.use(Vuetify, {theme: {primary: colors.blue.darken1, // #E53935secondary: colors.red.lighten4, // #FFCDD2accent: colors.indigo.base // #3F51B5}
});Vue.config.productionTip = false;new Vue({router,store,render: h => h(AppComponent)
}).$mount("#app");

主Vue.js TypeScript页面

下面的示例应用程序的主Vue页面是用TypeScript编写的。此页面是主页面,作为示例应用程序的主体,顶部有一个菜单栏,还有一个内容部分,其中将呈现所有其他Vue页面。主Vue页面还将包含用于显示用户状态信息和警报的功能。

// app.component.tstemplate src="./app.component.html"></template><style>
#app {font-family: "Avenir", Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;/*text-align: center;*/color: #2c3e50;/*margin-top: 60px;*/
}
</style><script lang="ts">import { Component, Vue } from "vue-property-decorator";
import { UserInformationViewModel } from "./viewmodels/user-information.viewmodel";
import AlertComponent from "./alert.component.vue";
import store from "./store";@Component({components: { AlertComponent }
})export default class AppComponent extends Vue {private title: string;constructor() {super();this.title = "VueJs With Typescript";}public logout() {store.dispatch("logoutUser");}get userInformation() {return store.state.userInformation;}get alert() {return store.state.alert;}get displayProcessing() {return store.state.processing;}}</script>

主应用程序HTML页面有一个router-view标记,其中所有内容页面将在路由更改时注入页面。主应用程序HTML页面还包含一个进度条,当进行HTTP请求时,该进度条将显示在内容页面的顶部。

<!-- app.component.html --><v-app style="height: 100vh;"><v-toolbar app style="background-color: green; color: white"><v-toolbar-title class="headline text-uppercase"><span>Code Project</span><span class="font-weight-light">&nbsp;{{title}}</span></v-toolbar-title><v-spacer></v-spacer><div v-if="userInformation.isAuthenicated==true">{{userInformation.firstName}}&nbsp;{{userInformation.lastName}}
</div>
<v-spacer></v-spacer><div id="nav"><router-link to="/">Home</router-link>&nbsp;|<router-link to="/about">About</router-link>&nbsp;|&nbsp;
</div><div id="nav" v-if="userInformation.isAuthenicated==true"><router-link to="/productsearch">Product Search</router-link>&nbsp;|<router-link to="/orders">Orders</router-link>&nbsp;|<router-link to="/shoppingcart">Shopping Cart</router-link>
</div><div id="nav" style="margin-left:15px;" v-if="userInformation.isAuthenicated==false"><router-link to="/register">Register</router-link>&nbsp;|<router-link to="/login">Login</router-link>&nbsp;
</div><div id="nav" style="margin-left:15px;" v-if="userInformation.isAuthenicated==true">
<router-link v-on:click.native="logout" to="/login">Logout</router-link>&nbsp;
</div></v-toolbar><v-content><v-dialog content-class="progressbar" v-model="displayProcessing" persistent transition="false"><v-container fluid fill-height fullscreen transition="false"><v-layout justify-center align-center><v-progress-linear height="10" indeterminate color="primary"></v-progress-linear></v-layout></v-container>
</v-dialog><router-view /><v-layout style="position:absolute; top:0; width:100%"><AlertComponent :messageId="alert.messageId" :messageType="alert.type" :displayMessage="alert.message"></AlertComponent></v-layout></v-content></v-app>

装饰器和基于类的组件

如果您有Angular(2+)背景,您可能熟悉将组件作为类来编写的模式,使用属性和修饰符来描述组件的复杂部分。与标准Vue.js组件相比,基于类的组件最大的优势在于,它们允许使用更简洁和标准的代码。在示例应用程序的TypeScript代码中,我安装并导入vue-class-component和vue-property-decorator npm包。

类组件只是一个扩展Vue对象的TypeScript类。在下面的AlertComponent中,Vue对象使用@Component装饰器进行扩展,就像我们现在在Vue.js中有一个基于类的组件一样。AlertComponent还使用@Prop装饰器来装饰组件的输入属性。

import { Component, Watch, Prop, Vue } from "vue-property-decorator";@Component
export default class AlertComponent extends Vue {public timeoutId: number;public originalMessageId: Date;public alertModel: any;public dismissed: Boolean;@Prop() displayMessage: any;@Prop() messageType: any;@Prop() messageId: any;constructor() {}}</script>

vue-property-decorator NPM包包括以下七个装饰:@Prop, @PropSync, @Provide, @Model, @Watch, @Inject, @Provide and @Emit。@Component装饰器由vue-class-component NPM包提供。您还可以使用vuex-class npm包中的装饰器扩展Vuex 存储。vuex-class包附带了以下Vuex 存储的装饰器:@State, @Getter, @Action, and @Mutation。

原始Vanilla JavaScript的主Vue.js页面

以下是主Vue页面的原始vanilla JavaScript。此页面具有使用计算对象块结构实现的三个计算属性:一个用于状态信息的计算属性,一个用于显示警报消息,另一个用于显示进度条。在TypeScript中重写示例应用程序意味着使用get语句更改计算属性块。在查看示例应用程序的最终产品之后,Vue页面的vanilla JavaScript版本似乎具有专有的外观和感觉。使用TypeScript似乎为Vue页面提供了一个更清洁,更标准和更自然的外观和感觉;包括使用类构造函数构建类和组件。使用TypeScript,一切看起来都像普通类。突然,Vue.js开始看起来很像Angular(2+)。

<template><v-app style="height: 100vh;"><v-toolbar app style="background-color: green; color: white"><v-toolbar-title class="headline text-uppercase"><span>Code Project</span><span class="font-weight-light">&nbsp;MATERIAL DESIGN</span></v-toolbar-title><v-spacer></v-spacer><div v-if="userInformation.isAuthenicated==true">{{userInformation.firstName}}&nbsp;{{userInformation.lastName}}</div><v-spacer></v-spacer><div id="nav"><router-link to="/">Home</router-link>&nbsp;|<router-link to="/about">About</router-link>&nbsp;|&nbsp;</div><div id="nav" v-if="userInformation.isAuthenicated==true"><router-link to="/productsearch">Product Search</router-link>&nbsp;|<router-link to="/orders">Orders</router-link>&nbsp;|<router-link to="/shoppingcart">Shopping Cart</router-link></div><div id="nav" style="margin-left:15px;" v-if="userInformation.isAuthenicated==false"><router-link to="/register">Register</router-link>&nbsp;|<router-link to="/login">Login</router-link>&nbsp;</div><div id="nav" style="margin-left:15px;" v-if="userInformation.isAuthenicated==true"><router-link v-on:click.native="logout" to="/login">Logout</router-link>&nbsp;</div></v-toolbar><v-content><v-dialog content-class="progressbar" v-model="displayProcessing" persistent transition="false"><v-container fluid fill-height fullscreen transition="false"><v-layout justify-center align-center><v-progress-linear height="10" indeterminate color="primary"></v-progress-linear></v-layout></v-container></v-dialog><router-view/><v-layout style="position:absolute; top:0;  width:100%"><AlertComponent :messageId="alert.messageId" :messageType="alert.type" :displayMessage="alert.message"></AlertComponent></v-layout></v-content></v-app></template><style>.progressbar {opacity: 0.7;position: absolute;top: 25px;height: 25px;overflow: hidden;
}</style><script>import { UserInformationViewModel } from "./viewmodels/user-information.viewmodel";
import AlertComponent from "./components/Alert.component";
import store from "./store";export default {name: "App",components: {AlertComponent},data() {return {};},methods: {logout() {store.dispatch("logoutUser");}},created() {},computed: {userInformation() {return store.state.userInformation;},alert() {return store.state.alert;},displayProcessing() {return store.state.processing;}}
};
</script>

单独的HTML模板文件

Vue.js的一些鼓吹是,所有的HTML模板都用JavaScript代码组合到同一个文件中。这遵循React框架实现。在开发示例应用程序的vanilla JavaScript版本时,我注意到我在HTML模板和JavaScript代码之间的“.vue”中上下滚动并丢失了我在文件中的位置。当然,我可以在Visual Studio Code中安装一些工具,这些工具可以提供一些跳转和锚点功能,以便在源文件中轻松来回移动。也许我刚刚习惯使用单独的HTML文件开发Angular应用程序,并使用Visual Studio Code中的联系人切换工具和命令在模板文件和代码文件之间快速切换。在开发示例应用程序的TypeScript版本时,我决定将HTML模板移动到每个Vue页面的单独HTML文件中。使用为此功能提供模板标记的src属性。作为Angular开发人员,我开发Vue页面的经验与Angular的熟悉程度相似。

<template src="./app.component.html"></template>

Vuex

由于多个组件分散在许多组件之间以及它们之间的交互,因此大型应用程序的复杂性通常会增加。为解决这个问题,Vue提供Vuex。Vuex的灵感来自Redux的React实现,它帮助React开发人员集中在应用程序的状态和逻辑,从而实现强大的状态持久性功能。Vuex是Vue.js应用程序的状态管理模式+库。它充当应用程序中所有组件的集中存储,其规则确保状态只能以可预测的方式进行变更。在Angular应用程序中,状态管理通常在全局单例类对象中进行管理,该对象可以通过依赖注入在所有组件之间共享。通过定义和分离状态管理中涉及的概念并实施维护视图和状态之间独立性的规则,Vuex为Vue代码提供了更多的结构和可维护性。可以通过Vue CLI UI或命令行安装Vuex npm软件包,如下所示:

vue install vuex --save

在下面的main.ts文件中,我添加了一个import语句来引用Vuex包,我只是用Vue注册它。

// main.tsimport Vue from ‘vue’;
import VueX from ‘vuex’;
Vue.use(Vuex);

Vuex存储

为了管理示例应用程序中的状态,我创建了一个store.ts文件并添加了代码来创建一个存储实例,以使用Vuex.Store函数跟踪应用程序状态。Vuex存储通过mutations和actions管理。实际更改Vuex存储中状态的唯一方法是提交突变。Vuex突变与事件非常相似:每个突变都有一个字符串类型和一个处理程序。处理函数是我们执行实际状态修改的地方。

//
//  store.ts
//import Vue from "vue";
import Vuex from "vuex";
import alert from "./alert.module";
import { UserInformationViewModel } from "./viewmodels/user-information.viewmodel";let userInformationModel: UserInformationViewModel = new UserInformationViewModel();Vue.use(Vuex);export default new Vuex.Store({modules: {alert},state: {userInformation: userInformationModel,processing: false},mutations: {updateUser(state: any, user: UserInformationViewModel): void {state.userInformation = user;},startProcessing(state: any): void {state.processing = true;},stopProcessing(state: any): void {setTimeout(function(): void {state.processing = false;}, 100);},logoutUser(state: any): void {state.userInformation = new UserInformationViewModel();localStorage.removeItem("VueToken");}},actions: {updateUser(context: any, user: UserInformationViewModel): void {context.commit("updateUser", user);},logoutUser(context: any): void {context.commit("logoutUser");},startProcessing(context: any): void {context.commit("startProcessing");},stopProcessing(context: any): void {context.commit("stopProcessing");}}
});

要调用变异处理程序,您需要调用存储的commit语句。例如,以下行更新存储中的用户信息。

context.commit(“updateUser”, user);

动作(Actions)类似于突变,不同之处在于动作(Actions)不是突变状态,而是提交突变。希望更新存储的Vue页面可以通过执行store.dispatch方法来触发存储操作。要从Vue页面更新用户的状态信息,将在示例应用程序的各个Vue页面中执行以下语句。

store.dispatch("updateUser", userInformation);

用户信息视图模型

为了增强单页面应用程序的开发过程,我喜欢使用具有强类型TypeScript属性的ES6类,并使用构造函数初始化类的每个属性的值。ES6类和TypeScript的组合使得单页应用程序的代码库更加紧凑,并且不易受到编码错误的影响,这些编码错误可能会在测试过程中无法实现。

下面的UserInformationViewModel将被用来存储在Vuex存储用户的登录信息。

export class UserInformationViewModel {constructor() {this.id = "";this.firstName = "";this.lastName = "";this.addressLine1 = "";this.addressLine2 = "";this.city = "";this.state = "";this.zipCode = "";this.emailAddress = "";this.phoneNumber = "";this.lastLogin = new Date();this.isAuthenicated = false;}public id: string;public firstName: string;public lastName: string;public addressLine1: string;public addressLine2: string;public city: string;public state: string;public zipCode: string;public emailAddress: string;public phoneNumber: string;public lastLogin : Date;public isAuthenicated: boolean;}

管理存储大小和代码库

随着您的应用程序变得越来越大,Vuex存储中的信息很可能会变得更大,管理存储的代码也会变得更大且难以管理。要缓解此问题,您可以创建可添加到存储代码库的单独命名空间模块。示例应用程序的一个附加功能是能够从一个集中的地方生成各种toaster和警报消息。Vuex存储似乎是此功能的合理位置。示例应用程序可以使用我们现在在大多数应用程序中看到的标准颜色(红色,蓝色,黄色和橙色)来引发几种不同类型的消息,信息性消息,错误消息,警告消息和成功消息。

要添加到存储并帮助保持代码库可管理,我创建了一个警报模块,如下所示:

//
// alert.module.ts
//import store from "./store";export const alert: any = {namespaced: true,state: {type: null,message: null,messageId: null},mutations: {error(state: any, message: any): void {state.type = "error";state.message = message;state.messageId = new Date();},success(state: any, message: any): void {state.type = "success";state.message = message;state.messageId = new Date();},warning(state: any, message: any): void {state.type = "warning";state.message = message;state.messageId = new Date();},info(state: any, message: any): void {state.type = "info";state.message = message;state.messageId = new Date();}},actions: {success(state: any, message: any): void {store.commit("alert/success", message);},error(state: any, message: any): void {store.commit("alert/error", message);},warning(state: any, message: any): void {store.commit("alert/warning", message);},info(state: any, message: any): void {store.commit("alert/info", message);}}
};export default alert;

要将警报模块添加到Vuex存储,只需将警报模块导入store.ts文件并作为模块引用。

//
// store.ts
//import Vue from "vue";
import Vuex from "vuex";
import alert from "./alert.module";Vue.use(Vuex);export default new Vuex.Store({modules: {alert}
});

由于警报模块是namespaced,因此通过调度带有警报命名空间前缀的操作以及要显示的消息类型来执行从任何Vue页面发出警报消息。

store.dispatch("alert/error", response.returnMessage[0]);

共享的Vue.js组件

与Angular应用程序一样,Vue.js也支持可在整个应用程序中使用的可重用共享组件。在示例应用程序中,警报消息将显示在页面顶部,并具有toaster功能。下面的代码片段引用了一个AlertComponent并添加到主Vue页面HTML模板中。

<v-layout style="position:absolute; top:0; width:100%"><AlertComponent:displayMessage="alert.message" :messageId="alert.messageId" :messageType="alert.type"></AlertComponent>
</v-layout>

AlertComponent HTML模板由以下HTML组成。Vuetify具有显示toaster消息和消息框的功能。

<div class="alert-style"> <v-alert v-if="messageType !== null"dismissible  :type="messageType" :value="displayAlertMessage" transition="scale-transition"@click="close" :value="displayAlertMessage" >{{displayMessage}}</v-alert>
</div>

使用各种UI库时有时必须克服的一件事是,它们并不总能提供您想要的确切功能。Vuetify警报没有任何功能,可以在没有用户按下解除按钮的情况下经过几秒钟后自动关闭消息框或toaster消息。为了提供这个功能,我必须扩展消息框以自动关闭,方法是实现一个超时功能,该功能在五秒钟后将名为displayAlertMessage的计算属性设置为false。

<template src="./alert.component.html"></template><style scoped>.alert-style {width: 100%;position: absolute;right: 0;z-index: 1000;
}</style><script lang="ts">import { Component, Watch, Prop, Vue } from "vue-property-decorator";@Component
export default class AlertComponent extends Vue {public timeoutId: number;public originalMessageId: Date;public alertModel: any;public dismissed: Boolean;@Prop() displayMessage: any;@Prop() messageType: any;@Prop() messageId: any;constructor() {super();this.timeoutId = -1;this.originalMessageId = new Date();this.alertModel = null;this.dismissed = false;}public close() {clearTimeout(this.timeoutId);this.dismissed = true;this.originalMessageId = new Date();}public created() {this.dismissed = false;}get displayAlertMessage() {if (this.messageId != this.originalMessageId && this.dismissed == false) {clearTimeout(this.timeoutId);this.timeoutId = setTimeout(() => {this.originalMessageId = this.messageId;}, 5000);this.dismissed = false;return true;} else {this.dismissed = false;return false;}}
}
</script>

AlertComponent有三个输入属性,包括要显示的消息类型和要显示的实际消息。使用TypeScript时,需要使用@Prop()注释对这些属性进行注释。此AlertComponent的原始vanilla JavaScript版本包含输入属性的props块。再次,您可以看到这个组件的TypeScript版本的外观和感觉不那么专有。

<script>export default {name: "AlertComponent",data() {return {timeoutId: -1,originalMessageId: null,alertModel: null,dismissed: false}},props: {displayMessage: String,messageType: String,messageId: Date},methods: {close() {clearTimeout(this.timeoutId);this.dismissed = true;this.originalMessageId = null;}},created() {this.dismissed = false;},computed: {displayAlertMessage: function() {if (this.messageId != this.originalMessageId && this.dismissed == false) {  clearTimeout(this.timeoutId);this.timeoutId = setTimeout(()=>{ this.originalMessageId = this.messageId;}, 5000);this.dismissed = false;return true;}else {this.dismissed = false;return false;}}}}</script

Vue.js路由器

Vue Router是Vue.js的官方路由器。它与Vue.js核心功能深度集成,使用Vue.js构建单页面应用程序变得轻而易举。它实际上与Angular中的路由非常相似。您只需将路径路径关联到Vue组件即可。在下面的router.ts文件中,vue-router导入并向Vue实例注册。一些额外的关键点包括以下内容:

  • 元字段——在Vue路由器中,您可以在定义路径时包含元字段。在Vue应用程序中进行路由时,路径信息中包含元字段。在下面的代码片段中,我创建了一个requiresAuthorization元字段。稍后将使用此字段来保护路由免受未经授权的访问,如Angular中的路由器防护实现。
  • Webpack块名称——使用捆绑器构建应用程序时,JavaScript捆绑包可能会变得非常大,从而影响页面加载时间。如果我们可以将每个路由的组件拆分成一个单独的块,并且只在访问路径时加载它们,那么效率会更高。使用webpack块名称,您可以将组件分组为块。在下面的代码片段中,将创建两个块(chuck)文件,一个用于产品,一个用于订单。
  • 历史模式——Vue路由器的默认模式是散列模式,并使用URL散列模拟完整URL,以便在URL更改时不会重新加载页面。为了摆脱URL中的哈希(井号),我们可以使用路由器的历史模式,该模式利用history.pushstate API。
//
// router.ts
//import Vue from "vue";
import Router from "vue-router";
import HomeComponent from "./views/home.component.vue";
import AboutComponent from "./views/about.component.vue";
import RegisterComponent from "./views/register.component.vue";
import LoginComponent from "./views/login.component.vue";Vue.use(Router);export default new Router({mode: "history",base: process.env.BASE_URL,routes: [{path: "/",name: "home",component: HomeComponent},{path: "/login",name: "login",component: LoginComponent},{path: "/register",name: "register",component: RegisterComponent},{path: "/checkout",name: "checkout",component: () =>import(/* webpackChunkName: "orders" */ "./views/checkout.component.vue"),meta: {requiresAuthorization: true}},{path: "/orders",name: "orders",component: () =>import(/* webpackChunkName: "orders" */ "./views/orders.component.vue"),meta: {requiresAuthorization: true}},{path: "/productdetail/:id",name: "productdetail",component: () =>import(/* webpackChunkName: "products" */ "./views/productdetail.component.vue"),meta: {requiresAuthorization: true}},{path: "/productsearch",name: "productsearch",component: () =>import(/* webpackChunkName: "products" */ "./views/productsearch.component.vue"),meta: {requiresAuthorization: true}},{path: "/shoppingcart",name: "shoppingcart",component: () =>import(/* webpackChunkName: "orders" */ "./views/shoppingcart.component.vue"),meta: {requiresAuthorization: true}},{path: "/about",name: "about",component: AboutComponent}]})

Vue.js路由器导航保护

顾名思义,Vue路由器提供的导航保护主要用于通过重定向路由请求或取消路由请求来保护导航。有几种方法可以挂钩路径导航过程:全局,按路径或组件内。保护Vue.js应用程序部分的另一种方法是使用带有元字段的全局导航保护。通过这种方法,我们可以像前面提到的那样在Vue路由器中注册保护。在下面的main.ts文件中,添加了一个路由器beforeEach钩子,它将在每个路由转换时执行。钩子检查路由器定义中定义的布尔元字段requiresAuthorizated,当requiredAuthorization字段设置为true时,路由器钩子检查应用程序存储以查看用户是否已经通过身份验证。如果身份验证检查失败,路由器会将应用程序重定向到登录页面,否则路由器会继续执行请求的路由。

//
//  router guard implementation - main.ts
//
router.beforeEach((to: any, from: any, next: any): void => {let requiresAuthorization: Boolean = to.matched.some((x: { meta: { requiresAuthorization: Boolean } }) =>x.meta.requiresAuthorization);if (requiresAuthorization) {if (store.state.userInformation.isAuthenicated === true) {next();} else {next("/login");}} else {next();}}
);

HTTP RESTful Web API支持

构建现代Web应用程序时,您可能需要使用某些远程资源中的数据,无论是您构建的还是其他人构建的数据。发送HTTP请求是将数据从面向客户端的应用程序发送到RESTful Web API后端的更常用方法之一。Vue.js没有提供开箱即用HTTP的方法,但是你可以使用一些库和插件,例如流行的Axios HTTP客户端和vue-resource插件或浏览器的内置fetch API。我最初为示例应用程序的vanilla JavaScript版本实现了vue-resource插件,但vue-resource插件被认为已弃用,并且Vue世界中的许多插件已从官方推荐状态退出。对于示例应用程序的TypeScript版本,我实现了Axios HTTP客户端。Axios目前是最受欢迎的HTTP客户端库之一,几乎涵盖了vue-resource提供具有非常相似API的所有内容。此外,它是通用的,支持取消,并具有TypeScript定义。Axios简单且非常轻巧,这使其成为任何Vue.js项目的理想解决方案。

要在项目中包含axios,请执行以下操作:

npm install axios --save

实现HTTP单例服务

本文的示例应用程序对.NET Core Web API后端进行RESTful Web API调用。为了帮助封装和集中Axios库的HTTP功能,以便可以在整个应用程序中重复使用它而不会使用大量重复的HTTP代码污染应用程序,我创建了一个实现单例模式的HTTP Service类。单例服务是跨组件共享的服务实例。实现单例模式意味着您只在应用程序中创建该类的单个实例。http.service.ts文件的最后一行创建了一个HttpService类的实例并将其导出,以便任何需要使用它的组件都可以简单地导入它。

//
// http.service.ts
//import { ResponseModel } from "./viewmodels/response.model";
import axios from "axios";
import store from "./store";export class HttpService {private urlRoot: string;constructor() {this.urlRoot = "https://localhost:44340/api/secureonlinestore";}login(requestData: any): any {return this.httpPost("/login", requestData);}register(requestData: any): any {return this.httpPost("/register", requestData);}getProducts(requestData: any): any {return this.httpPost("/productinquiry", requestData);}createOrder(requestData: any): any {return this.httpPost("/createOrder", requestData);}getOrders(): any {return this.httpGet("/GetOrders");}getProductDetail(productId: string): any {return this.httpGet("/getproductdetail/" + productId);}validateEmailAddress(emailAddress: string): any {return this.httpGet("/ValidateEmailAddress/" + emailAddress);}httpGet(urlPath: string): any {store.dispatch("startProcessing");let url: string = this.urlRoot + urlPath;return axios.get(url).then(response => {store.dispatch("stopProcessing");return response.data;}).catch((error: any) => {let response: ResponseModel = new ResponseModel();response.returnStatus = false;if (error.response) {if (error.response.status === "401") {response.returnMessage.push("Unauthorized");} else {response.returnMessage.push(error.response.data.returnMessage[0]);}} else {response.returnMessage.push(error);}store.dispatch("stopProcessing");return response;});}httpPost(urlPath: string, requestData: any): any {store.dispatch("startProcessing");let url: string = this.urlRoot + urlPath;return axios.post(url, requestData).then(response => {store.dispatch("stopProcessing");return response.data;}).catch((error: any) => {let response: ResponseModel = new ResponseModel();response.returnStatus = false;if (error.response) {if (error.response.status === "401") {response.returnMessage.push("Unauthorized");} else {response.returnMessage.push(error.response.data.returnMessage[0]);}} else {response.returnMessage.push(error);}store.dispatch("stopProcessing");return response;});}
}export default new HttpService();

Axios HTTP拦截器

Axios提供了一些受Angular的$ http库启发的特性。Axios允许我们添加调用的函数interceptors。这些拦截器功能可以在发出请求时或在收到响应时附加到请求或响应。对于示例应用程序,用于Web API请求的Axios拦截器将作为向服务器发送自定义头的集中方式。由于服务器Web API需要大多数终端的授权令牌,因此Axios请求拦截器将从客户端local storage中获取,并在将请求发送到服务器之前将存储的JSON Web Token (JWT)作为“Bearer”令牌添加到请求的头部。对于响应拦截器,来自服务器的任何响应,其状态代码为401的未授权将被截获,并将用户重定向到登录页面。

import axios from "axios";//
//  http interceptors - main.ts
//axios.interceptors.request.use(config => {const token: any = localStorage.getItem("VueToken");if (token != null && token !== undefined) {config.headers.Authorization = "Bearer " + token;}config.headers.Accept = "application/json";return config;},error => {return Promise.reject(error);}
);const UNAUTHORIZED: number = 401;axios.interceptors.response.use(response => response,error => {const status: any = error.response;if (status === UNAUTHORIZED) {router.push({ name: "login" });}return Promise.reject(error);}
);

登录组件

要查看所有操作,以下登录组件将执行以下功能:

  • 导入Http Service单例服务
  • 导入Vuex存储
  • 导入并初始化用户和用户信息视图模型
  • 为Vuetify表单设置必需的验证规则
  • 当用户按下登录按钮时,将验证表单
  • 执行Http服务以将用户的凭证发布到服务器
  • 成功登录后,服务器生成的授权令牌(JWT)将存储在用户的本地存储中
  • 调度Vuex存储以更新Vuex存储中的用户状态信息
  • 成功登录后,Vue路由器会将用户重定向到产品搜索页面
  • 如果用户的凭据无法进行身份验证,则会显示toaster 错误消息
//
// login.component.ts
//<template src="./login.component.html"></template<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import { UserViewModel } from "../viewmodels/user.viewmodel";
import { UserInformationViewModel } from "../viewmodels/user-information.viewmodel";
import httpService from "../http.service";
import store from "../store";@Component
export default class LoginComponent extends Vue {public emailAddressErrors: Array<string>;public validForm: Boolean;public userViewModel: UserViewModel;public requiredRules: Array<any>;constructor() {super();this.emailAddressErrors = new Array<string>();this.validForm = false;this.userViewModel = new UserViewModel();this.userViewModel.emailAddress = "";this.userViewModel.password = "";this.requiredRules = [(v: any) => !!v || "Field is required"];}public login() {let thisComponent: any = this;let form: any = this.$refs.form;if (form.validate == false) {return false;}httpService.login(this.userViewModel).then(function(response: any) {if (response.returnStatus === true) {let token = response.entity.token;localStorage.setItem("VueToken", token);let userInformation = new UserInformationViewModel();userInformation.firstName = response.entity.firstName;userInformation.lastName = response.entity.lastName;userInformation.emailAddress = response.entity.emailAddress;userInformation.id = response.entity.id;userInformation.phoneNumber = response.entity.phoneNumber;userInformation.addressLine1 = response.entity.addressLine1;userInformation.addressLine2 = response.entity.addressLine2;userInformation.city = response.entity.city;userInformation.state = response.entity.state;userInformation.zipCode = response.entity.zipCode;userInformation.isAuthenicated = true;store.dispatch("updateUser", userInformation);thisComponent.loginSuccessful();} else {store.dispatch("alert/error", response.returnMessage[0]);}});}public loginSuccessful() {this.$router.push({ name: "productsearch" });}}
</script>

Vuetify表单验证

在表单验证方面,Vuetify拥有众多集成和功能。你也可以选择使用第三方认证的插件,如Vee-validate或Vuelidate。对于示例应用程序,我使用了baked验证功能。

下面是注册页面的HTML模板,其中包含Vuetify表单以及必需字段的验证、电子邮件地址模式、密码匹配确认和后端数据库中重复电子邮件地址的验证。

<v-layout>
<v-flex><v-card><v-card-title primary-title><div><h3 class="headline mb-0">Register</h3></div></v-card-title><v-form ref="form" v-model="validForm"><v-container fluid><v-layout v-bind="binding"><v-flex xs12 lg3 sm6 md6><v-text-field @blur="validateEmailAddress" v-model="userViewModel.emailAddress" required :rules="emailRules" :error-messages="emailAddressErrors" label="Email Address"></v-text-field></v-flex></v-layout><v-layout v-bind="binding"><v-flex xs12 lg3 sm6 md6><v-text-field v-model="userViewModel.password" required :rules="requiredRules"label="Password"></v-text-field></v-flex><v-flex xs12 lg3 sm6 md6><v-text-field v-model="userViewModel.passwordConfirmation" required :error-messages="passwordConfirmationErrors" label="Password Confirmation" :rules="passwordConfirmationRules"></v-text-field></v-flex></v-layout><v-layout v-bind="binding"><v-flex xs12 lg3 sm6 md6><v-text-field v-model="userViewModel.firstName" required :rules="requiredRules"label="First Name"></v-text-field></v-flex><v-flex xs12 lg3 sm6 md6><v-text-field v-model="userViewModel.lastName" required :rules="requiredRules"label="Last Name"></v-text-field></v-flex></v-layout><v-layout v-bind="binding"><v-flex xs12 lg3 sm6 md6><v-text-field v-model="userViewModel.addressLine1" required :rules="requiredRules"label="Address Line 1"></v-text-field></v-flex><v-flex xs12 lg3 sm6 md6><v-text-field v-model="userViewModel.addressLine2" label="Address Line 2"></v-text-field></v-flex></v-layout><v-layout v-bind="binding"><v-flex xs12 lg3 sm6 md6><v-text-field v-model="userViewModel.city" required :rules="requiredRules" label="City"></v-text-field></v-flex><v-flex xs12 lg3 sm6 md6><v-text-field v-model="userViewModel.state" required :rules="requiredRules" label="State"></v-text-field></v-flex></v-layout><v-layout v-bind="binding"><v-flex xs12 lg3 sm6 md6><v-text-field v-model="userViewModel.zipCode" required :rules="requiredRules"label="Zip Code"></v-text-field></v-flex></v-layout><v-layout v-bind="binding"><v-flex xs12 lg3 sm6 md6><v-text-field v-model="userViewModel.phoneNumber" required :rules="requiredRules"label="Phone Number"></v-text-field></v-flex></v-layout></v-container><v-card-actions><v-btn flat color="primary" :disabled="!validForm" @click="register">Register</v-btn></v-card-actions></v-form></v-card>
</v-flex>
</v-layout>

在电子邮件地址表单字段中,将向该字段添加@blur事件处理程序,以验证电子邮件地址是否唯一且当前未在用户数据库中使用。电子邮件验证规则将分配给:rules属性,并且将通过绑定到:error-messages表单字段属性来显示任何错误。

<v-text-field @blur="validateEmailAddress"   v-model="userViewModel.emailAddress"   required :rules="emailRules" :error-messages="emailAddressErrors" label="Email Address">
</v-text-field>

为Vuetify设置验证规则就像设置验证数组一样简单。以下数组的电子邮件验证规则包括一个用于必填字段,而另一个用于确保电子邮件地址遵循电子邮件地址的有效模式。

this.emailRules = [
(v: any) => !!v || "E-mail address is required",
(v: any) => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) || "E-mail address is invalid"
];

在on blur事件将触发validateEmailAddress方法,其将重置电子邮件地址验证数组,并将向服务器发出Web API请求,该请求将验证电子邮件地址是否唯一且尚不存在。

public validateEmailAddress(event: any) {let vm = this;let emailAddress = event.target.value;if (emailAddress == null || emailAddress == undefined || emailAddress.length == 0) {return false;}vm.emailAddressErrors = [];httpService.validateEmailAddress(emailAddress).then(function(response: any) {if (response.returnStatus == false) {vm.emailAddressErrors.push(response.returnMessage[0]);}});}

另一种类型的验证是密码确认匹配。由于密码验证匹配需要在表单中的两个字段上匹配,因此需要将密码匹配验证设置为computed property,以便它可以对表单中的值更改做出反应。使用TypeScript get property将passwordConfirmationRules属性设置为计算(computered)属性。此页面的原始vanilla JavaScript版本为此实现了计算块。

get passwordConfirmationRules() {let rules = [];let rule1 = (v: any) => !!v || "Password is required";rules.push(rule1);let rule2 = (v: any) => (!!v && v) === this.userViewModel.password ||  `Password confirmation must match`;rules.push(rule2);return rules;
}

当用户按下注册按钮时,执行注册方法。在将表单信息提交给服务器之前,您始终要做的第一件事是通过执行form.validate方法重新验证表单上的所有内容是否仍然有效。用户可能会重新编辑表单上可能无法触发表单上所有必需验证的字段。在register方法中,需要将对表单实例的引用重新分配给类型为any的另一个对象。这是必需的,因为Vue.js没有针对validate方法的TypeScript定义,并且在代码中引用它时生成了TypeScript编译错误。

public register() {let vm: any = this;let form: any = this.$refs.form;if (form.validate() === false) {return false;}httpService.register(this.userViewModel).then(function(response: any) {if (response.returnStatus == true) {vm.$router.push({ name: "login" });} else {store.dispatch("alert/error", response.returnMessage[0]);}});
}

Vue.js过滤器

以下是订单页面的Vuetify Material Design模板。您将在模板中看到的两个项目是使用过滤器格式化订单日期和单位价格。Vue.Js中的过滤器类似于Angular中的过滤器。过滤器是Vue组件提供的功能,允许您将格式和转换应用于模板数据的任何部分。过滤器不会更改组件数据或任何内容,但只会影响呈现的输出。过滤器可以采用两种方式:全局用于所有要使用的组件,或者本地作用域,以便仅在其定义的组件中使用。

事实证明,Vue.Js不提供开箱即用的任何过滤器,但是有一些有用的社区包可能对您的项目有帮助。对于示例应用程序的原始vanilla JavaScript版本的Vue.js项目,我下载并安装了以下两个流行的npm包:

  • vue2-filters——这个npm包附带了十多种类型的常见过滤器,如首字母大写,全部大写,全部小写,货币等。
  • vue-moment——基于流行的Moment.js过滤器包来格式化时间值。
<v-layout>
<!---xs12 sm6 offset-sm3-->
<v-flex>
<v-card>
<v-card-title primary-title>
<div>
<h3 class="headline mb-0">Orders</h3>
</div>
</v-card-title>
<v-data-table :headers="headers" :hide-actions="true" :items="orderInquiryViewModel.orders"class="elevation-1">
<v-progress-linear v-slot:progress color="blue" indeterminate></v-progress-linear><template v-slot:items="props">
<td style="min-width: 50px; max-width: 50px; width:50px;">{{ props.item.orderNumber }}
</td>
<td style="min-width:50px; max-width: 50px; width:50px;">{{ props.item.orderDate | moment("MM/DD/YYYY") }}
</td>
<td style="min-width:200px; max-width: 200px; width:200px;">{{ props.item.customerName }}
</td>
<td style="min-width:150px; max-width: 150px; width:150px;">{{ props.item.productNumber }}
</td>
<td style="min-width:200px; max-width: 200px; width:200px;">{{ props.item.description }}
</td>
<td style="min-width: 50px; max-width: 50px; width:50px;" class="text-xs-right">{{ props.item.orderQuantity }}
</td>
<td style="min-width: 50px; max-width: 50px; width:50px;" class="text-xs-right">{{ props.item.unitPrice | currency }}
</td>
</template><template v-slot:no-data>
<v-alert :value="displayNoDataMessage" color="error" icon="warning">There are no orders
</v-alert></template>
</v-data-table>
</v-card>
</v-flex>
</v-layout>

在为vue2-filters和vue-moment安装npm软件包之后,您只需要导入软件包并将它们与main.js启动代码中的Vue实例一起注册。

//
// maint.ts
//import Vue from 'vue';
import Vue2Filters from "vue2-filters";
import VueMoment from "vue-moment";Vue.use(Vue2Filters);
Vue.use(VueMoment);

TypeScript兼容性和类型定义文件

因此,在示例应用程序的vanilla JavaScript Vue.js版本中,使用vue过滤器和moment.js包和功能,一切都很顺利。然后我开始研究这个项目的TypeScript版本,发现并不是Vue.Js的所有内容都对TypeScript有很好的支持。TypeScript仍然是Vue.js生态系统的新手。要使TypeScript执行类型检查,需要在某处定义类型。这是type definition files发挥作用的地方。它们允许您提供JavaScript代码的类型信息,这些信息本质上不是静态类型的。这样一个文件的文件扩展名是“.d.ts”,其中d代表definition。类型定义文件使您可以享受类型检查,自动完成和成员文档带来的好处。虽然非常有用,但类型定义文件需要花费大量时间来创建。幸运的是,大多数npm包和库都带有TypeScript的类型定义。不幸的是,vue2-filters和vue-moment npm软件包都没有附带类型定义文件。幸运的是,您可以选择解决TypeScript定义文件未附带的包。一种方法是创建自己的定义文件或找到可能已经创建的其他人的。对于示例应用程序,我决定以不同方式解决TypeScript类型定义问题。要使用vue-moment包,我只需使用require语句注册包,而不是按如下方式导入它:

Vue.use(require("vue-moment"));

require语句将动态加载vue-moment模块,从而通过TypeScript类型检查器。

定制全局过滤器

Vue.js允许您定义可用于应用常见文本格式的过滤器。作为vue2-filters软件包的解决方案,我决定为示例应用程序所需的货币格式化功能创建一个自定义全局过滤器。查看vue2-filters的npm包,我能够提取提供货币格式的JavaScript函数,并将其作为自定义全局过滤器添加到示例应用程序中。以下filters.ts文件实现了全局自定义货币过滤器。

import Vue from "vue";//
//  currency filter - filters.ts
//Vue.filter("currency", (value: any, symbol: string, decimals: number, options: any) => {var thousandsSeparator: any,var symbolOnLeft: any,var spaceBetweenAmountAndSymbol: any;var digitsRE: any = /(\d{3})(?=\d)/g;options = options || {};value = parseFloat(value);if (!isFinite(value) || (!value && value !== 0)) {return "";}symbol = symbol != null ? symbol : "$";decimals = decimals != null ? decimals : 2;thousandsSeparator =options.thousandsSeparator != null ? options.thousandsSeparator : ",";symbolOnLeft = options.symbolOnLeft != null ? options.symbolOnLeft : true;spaceBetweenAmountAndSymbol =options.spaceBetweenAmountAndSymbol != null? options.spaceBetweenAmountAndSymbol: false;var stringified: any = Math.abs(value).toFixed(decimals);stringified = options.decimalSeparator? stringified.replace(".", options.decimalSeparator): stringified;var _int: any = decimals? stringified.slice(0, -1 - decimals): stringified;var i: any = _int.length % 3;var head: any =i > 0? _int.slice(0, i) + (_int.length > 3 ? thousandsSeparator : ""): "";var _float: any = decimals ? stringified.slice(-1 - decimals) : "";symbol = spaceBetweenAmountAndSymbol? symbolOnLeft? symbol + " ": " " + symbol: symbol;symbol = symbolOnLeft? symbol +head +_int.slice(i).replace(digitsRE, "$1" + thousandsSeparator) +_float: head +_int.slice(i).replace(digitsRE, "$1" + thousandsSeparator) +_float +symbol;var sign: any = value < 0 ? "-" : "";return sign + symbol;});

Vue.js Mixins

我想在示例应用程序中做的事情之一就是每当用户启动应用程序,刷新浏览器窗口(F5)或每次切换到不同的路径时,始终更新用户的当前状态。为此,我需要一个可以执行的全局代码来更新应用程序的Vuex存储。输入Vue.js Mixins。Mixins是为Vue组件分发可重用功能的灵活方式。mixin对象可以包含任何组件选项。当组件使用mixin时,mixin中的所有选项将“混合”到组件自己的选项中。当mixin和组件本身包含重叠选项时,它们将使用适当的策略“合并”。在下面的代码中,每当创建一个全局mixin时,created生命周期事件被触发。当触发创建的事件时,mixin将从本地存储检查用户的JSON Web令牌(JWT),并确定用户的令牌是否已过期。如果令牌已过期,则Vuex存储将通过更新dispatch语句和用户的状态将设置为未经身份验证的状态;本质上是将用户从应用程序中记录下来。由于所有内容都被视为Vue.js中的一个组件,包括共享组件和所有其他可视组件,因此每页会多次触发创建的事件。我只想在将vue组件页面路由到并创建时检查用户的令牌;所以我在mixin中检查是否在vue页面中触发了事件。这是如何创建全局混合的一个很好的例子。当然,我想要的功能也可以在路径变化的foreach事件钩子中完成。

//
//  mixin for user authenication on page routing
//Vue.mixin({created(): void {let vm: any = this;if (vm._self.$vnode.data.routerView !== undefined) {let user: UserInformationViewModel = new UserInformationViewModel();let token: any = localStorage.getItem("VueToken");if (token === null || token === undefined) {user.emailAddress = "";user.firstName = "";user.lastName = "";user.id = "";user.isAuthenicated = false;store.dispatch("updateUser", user);} else {let jwt: any = JSON.parse(atob(token.split(".")[1]));let currentTime: number = Date.now() / 1000;if (jwt.exp < currentTime) {user.emailAddress = "";user.firstName = "";user.lastName = "";user.id = "";user.isAuthenicated = false;store.dispatch("updateUser", user);}}}}
});

当用户刷新浏览器(F5)或在浏览器中重新打开应用程序时,可以解析用户的令牌,并可以在main.ts文件中更新应用程序存储,如下所示:

//
//  user authenication on application start-up/refresh - main.ts
//let user: UserInformationViewModel = new UserInformationViewModel();
user.emailAddress = "";
user.firstName = "";
user.lastName = "";
user.id = "";
user.isAuthenicated = false;let token: any = localStorage.getItem("VueToken");
if (token == null || token === undefined) {user.isAuthenicated = false;
} else {let jwt: any = JSON.parse(atob(token.split(".")[1]));user.emailAddress = jwt.emailAddress;user.firstName = jwt.given_name;user.lastName = jwt.nameid;user.id = jwt.primarysid;user.isAuthenicated = true;let currentTime: number = Date.now() / 1000;if (jwt.exp < currentTime) {user.isAuthenicated = false;}
}store.dispatch("updateUser", user);

即使JSON Web令牌是base64 encoded,它们仍然可以由任何人解码。重要的是不要在这些令牌中存储任何可用于危害应用程序安全性的关键或私人信息。

生产部署

单页面应用程序通常由数十个甚至数百个组件组成,这些组件可以分成几个JavaScript包文件。使用bundler构建应用程序时,JavaScript包可能会变得非常大,从而影响页面加载时间。如果我们可以将几个相关组件拆分成一个单独的块,并且只在访问与组件相关的路由时加载(lazy-load)它们会更有效。结合Vue的async component loading和webpack的code splitting feature,可以很容易地根据路径延迟加载组件。

在示例应用程序中,路由配置webpackChunckName为webpack捆绑器将用于创建单独的捆绑包。

// router.ts{path: "/shoppingcart",name: "shoppingcart",component: () =>import(/* webpackChunkName: "orders" */ "./views/shoppingcart.component.vue"),meta: { requiresAuthorization: true }
}

要通过缩小JS/CSS/HTML为生产部署创建捆绑的JavaScript文件,您只需执行Vue.js CLI构建命令即可编译应用程序并调用webpack bundler。

npm run build

默认情况下,webpack构建的输出将进入项目中的dist文件夹。以下列表是构建示例应用程序并为Vue.js路由器配置中定义的产品和订单创建预定义捆绑包的结果。

File                                      Size             Gzippeddist\js\chunk-vendors.132c74f3.js         829.55 KiB       207.68 KiBdist\js\orders~products.7efe385e.js       68.99 KiB        17.35 KiBdist\js\app.0eed3d02.js                   29.69 KiB        8.35 KiBdist\js\products.cf0343bb.js              16.21 KiB        4.37 KiBdist\js\orders.77b1fed7.js                14.09 KiB        3.46 KiBdist\precache-manifest.8c24b13f01c7e.js    1.16 KiB        0.40 KiBdist\service-worker.js                    0.95 KiB         0.54 KiBdist\css\chunk-vendors.587a5dd2.css       137.14 KiB       18.01 KiBdist\css\orders~products.c472f02d.css     29.67 KiB        4.74 KiBdist\css\products.0aa943ff.css            2.75 KiB         0.64 KiBdist\css\app.2db963e6.css                 0.45 KiB         0.28 KiBdist\css\orders.dd9178be.css              0.36 KiB         0.21 KiB

Vue.js扩展包

凭借其对TypeScript和Chrome调试器等开发工具的内置支持,Visual Studio Code已成为Microsoft社区开发人员基于JavaScript和TypeScript的项目的事实上的代码编辑器。为了使事情变得更好,Visual Studio Code有许多扩展,有助于使Vue.js开发成为一种很棒的体验。如果您正在使用Visual Studio Code开发Vue.js应用程序,则需要从Visual Studio Marketplace下载Vue VS Code Extension Pack。此扩展包附带了一组扩展,用于在Visual Studio Code中使用Vue应用程序。扩展包附带Vetur并完全支持.vue文件。Vetur包括语法高亮、代码片段、linting、错误检查、格式化、自动完成和调试。在扩展包中,您还可以获得一个代码格式化程序Prettier,它将自动格式化您的代码。扩展包中有大约20个扩展功能,可以增强您的Vue.js开发体验。

Vue DevTools浏览器扩展

在复杂的JavaScript代码中使用console.log语句仍然是在浏览器中调试JavaScript代码的一种流行且有用的方法。为了增强浏览器中的调试应用程序,Vue有一个专用插件,可以帮助您更有效地调试和开发应用程序。Vue DevTools是Chrome和Firefox的扩展,用于调试Vue.js应用程序。安装完成后,可以通过浏览器中的Developer Tools(F12)面板访问Vue DevTools,然后进入Vue选项卡。

Chrome和Firefox的Vue DevTools扩展的一些功能包括:

  • 实时编辑组件数据——Vue DevTools的一个非常方便的功能是实时编辑组件的数据。这使您可以快速测试组件的不同变体。
  • 使用Time Travel调试Vuex——Vue DevTools与Vuex状态管理库无缝集成,使您可以轻松浏览以前版本的Vuex状态对象。这允许你做所谓的time-travel debugging。时间旅行调试是通过应用程序数据回溯的过程,以了解执行期间发生的情况。
  • 跟踪应用程序的自定义事件——如果您在应用程序中使用事件,则可以在Vue DevTools的事件选项卡中跟踪它们。

总结

开发人员经常将Vue.js描述为Angular和React的初生子,并将两者的相似性烘焙到框架中。Vue.js和React分别使用类似的状态管理库,如Vuex和Redux。React和Vue.js都是单向的。这意味着数据仅在从父组件到子组件的一个方向上流动。与Angular相比,Vue.js是一种更灵活,更灵活的解决方案。这允许您以您希望的方式构建应用程序,而不是被迫以Angular方式执行所有操作。与Angular的模块配置和声明要求相比,使用Vue.js启动和运行是一个更简单的旅行之路。当然,如果您正在与大型开发团队一起开发大型任务关键型应用程序,一个固定的完整框架,如Angular,大公司支持大型生态系统,可能仍然是可行的方法。在TypeScript中重写示例Vue.js应用程序后,Vue.js代码库开始看起来很像Angular 2应用程序。一旦你有一个应用程序启动并运行,Vue.js和Angular之间的线条会开始模糊,当你使用TypeScript时更是如此。就像宗教一样,技术选择通常是基于你所提出的。最终,它最终取决于您自己的喜好。好消息;我没有看到Vue.js或Angular成为下一个Silverlight;一种从未真正拥抱过的废弃技术。

先决条件和运行示例应用程序

示例应用程序的后端是使用MongoDB的.NET Core Web API应用程序。在Vue.js,.NET Core和MongoDB之间,有许多移动部件需要安装和配置才能启动和运行示例应用程序。示例应用程序包含两个Visual Studio Professional(2017或2019)项目和两个Visual Studio代码项目——一个用于vanilla JavaScript Vue.js版本的Visual Studio Code项目和一个示例应用程序的TypeScript版本。为了尽可能轻松地在本地开发环境中启动和运行示例应用程序,我在下面概述了启动和运行所需的先决条件和安装步骤。

软件安装先决条件:

  • MongoDB 4.0或更高版本
  • Visual Studio 2017或2019专业版或社区版
  • Visual Studio Code
  • .NET Core 2.2 - SDK版本2.2.106
  • NodeJS 10.13.0或更高版本
  • Vue.js CLI 3

要使示例应用程序运行,需要执行以下步骤:

  • MongoDB服务器安装为服务——从下载页面下载MongoDB社区服务器版本4.0。从4.0版开始,您可以在安装期间安装和配置MongoDB作为Windows服务,并在成功安装后启动MongoDB服务。使用安装bin目录中的配置文件配置MongoDB。
  • (可选)将MongoDB独立服务器转换为副本集——在购物车应用程序中下订单需要MongoDB事务支持。如果您希望看到此功能的实际应用;需要支持MongoDB事务。要支持事务,请将MongoDB安装转换为副本集。
  • 下载示例应用程序源代码——可以从本文顶部的下载源代码链接下载示例应用程序的源代码。有三个下载链接,一个用于使用TypeScript的Vue.js前端,另一个用于示例应用程序的vanilla JavaScript版本的链接。第三个链接将下载MongoDB和.NET Core后端Web API应用程序。
  • .NET Core 2.2——下载并安装Visual Studio 2017或2019(专业版或社区版)时,应自动安装.NET Core 2.2。如果您已经安装了Visual Studio,则可以通过转到Tools菜单并选择来Get Tools and Features验证安装,这将启动Visual Studio安装程序。从安装程序选项中,您可以验证是否已安装.NET Core 2.2。如果需要为Visual Studio下载.NET Core SDK;从.NET Core SDK下载页面为Windows 64位操作系统选择SDK版本2.2.106
  • Vue.js CLI 3——Vue.js前端应用程序是通过Vue.js CLI版本3构建和提供的。您可以通过运行Vue.js CLI命令验证Vue.js CLI安装: vue --version。要安装最新版本的Vue CLI,请执行:npm install -g @vue/cli。Vue CLI需要Node.js版本8.9或更高版本(推荐8.11.0+)。安装Vue CLI还将安装最新版本的Vue.js.
  • 构建并运行示例应用程序.NET Core Web API项目——要验证所有内容是否已正确安装,请编译CodeProject.Mongo.WebAPI 示例应用程序的Web API项目。在使用Visual Studio Professional或Community Edition打开和构建这些项目时,请务必等待一两分钟,因为在打开项目时,Visual Studio将需要还原编译这些项目所需的包。
  • SSL——Web API项目配置为使用SSL。要避免SSL问题,您需要通过选择IISExpress配置文件并选择运行按钮来尝试运行项目,ASP.NET Core将创建SSL证书。根据您的开发计算机,Visual Studio可能会询问您是否要信任ASP.NET Core生成的自签名证书。选择“是”以信任证书。因为Visual Studio是Visual Studio,所以可能必须第二次或第三次运行项目,或者退出并重新加载Visual Studio以确认项目的所有内容都正常工作。从Visual Studio运行项目时,浏览器应从值控制器启动并在浏览器中显示输出。
  • 构建和运行种子程序——使用示例应用程序构建的测试数据填充Products集合并运行.NET Core控制台应用程序CodeProject.Mongo.Import.此程序还将创建所需的索引以支持示例应用程序的功能。
  • 构建并提供Vue.js前端应用程序——两个版本的Vue.js前端应用程序都依赖于要安装在项目node_modules文件夹中的节点模块。创建所有节点模块可以通过从应用程序的根文件夹在DOS命令窗口中执行npm install来完成。安装软件包之后,您可以在DOS命令窗口中使用Vue.js CLI构建Vue.js应用程序项目,执行:npm run serve.一旦构建了Vue.js前端应用程序,webpack将在后台启动Node.js Express Web服务器。
  • 故障排除和修复Node.jsnpm问题——似乎Node.js和节点包管理器(npm)有时会遇到奇怪的问题。这些问题有时可以通过运行npm cache clean或使用-verbose选项运行npm install来解决,以查看更多详细信息。作为最后的手段,您可以卸载Node.js并删除%APPDATA%文件夹npmnpm-cache并重新安装最新的Node.js长期支持(LTS)版本。
  • 启动示例应用程序——要运行示例应用程序,请在浏览器中输入http://localhost:8080

为Angular(2+)开发人员提供带TypeScript的Vue.js相关推荐

  1. 基于Windows Mobile 2003 的 Pocket PC 为开发人员提供的新功能(转)

    对于 Pocket PC 用户和开发人员来说,基于 Microsoft® Windows Mobile® 2003 的 Pocket PC 是一个非常重要的里程碑.Pocket PC 2003 基于新 ...

  2. Windows Mobile 5.0 中为开发人员提供的新功能(3)

    Windows Mobile 5.0 中为开发人员提供的新功能(3) Pocket Outlook 增强功能 Pocket Outlook 向用户和应用程序开发人员提供了易于使用的 PIM.将 Poc ...

  3. Windows Mobile 6 中为开发人员提供的新功能(1)

    Windows Mobile 6 中为开发人员提供的新功能(1) 2007年06月10日 星期日 10:29 Jim Wilson,JW Hedgehog, Inc. 摘要 Windows Mobil ...

  4. 为什么Java开发人员都带眼镜 | 程序员搞笑段子合集

    Enjoy! 01 如何区分HTML和HTML5? 用IE打开,打不开吗?那就是HTML5没错了. 02 程序员为什么放弃斗争呢? 因为他没有兵(array)啊. 03 数据库SQL走进了一家NoSQ ...

  5. python实现微信自动加群_为Python开发人员提供实时代码片段,Kite获1700万美元A轮融资...

    [猎云网(微信号:)]1月29日报道(编译:孙家乐) Kite是一款为Python开发人员提供实时代码片段的工具,它在由Trinity Ventures领投的A轮融资中获得了1700万美元.最新版本的 ...

  6. Java自己文章只能自己修改_文章目录Java代码俯身指南,主要为Java开发人员提供代码复审参考,快捷有效提出修改意见。目的发现代码错误:一个人写的代码可能会有一些思想和设计盲点,多个人尽...

    文章目录 Java代码俯身指南,主要为Java开发人员提供代码复审参考,快捷有效提出修改意见. 目的发现代码错误:一个人写的代码可能会有一些思想和设计盲点,多个人尽早的发现BUG. 统一代码风格:统一 ...

  7. 腾讯内核java调用,taip: TAIP是调用腾讯AI的Java客户端,为调用腾讯AI功能的开发人员提供了一系列的交互方法。...

    OCR Java SDK目录结构 cn.xsshome.taip ├── base //基类 ├── http //Http通信相关类 ├── imageclassify │ └── TAipImag ...

  8. java相关段子_为什么Java开发人员都带眼镜 | 程序员搞笑段子合集

    劳工节小长假愉快! Enjoy! 01 如何区分HTML和HTML5? 用IE打开,打不开吗?那就是HTML5没错了. 02 程序员为什么放弃斗争呢? 因为他没有兵(array)啊. 03 数据库SQ ...

  9. 酸狗带你了解Vue.js

    带你了解Vue.js 今天酸狗就带着大家了解一个国内最火的前端框架Vue.js.希望对大家Vue的了解有帮助! 文章目录 带你了解Vue.js 前言 一.框架和库的区别 二.前端三大主流框架 三.什么 ...

最新文章

  1. 继清华之后,北邮成立人工智能研究院
  2. 死锁产生原因-竞争资源引起进程死锁
  3. 小巫新闻客户端底部菜单切换实现
  4. Atom JS 代码智能提示补全
  5. BZOJ 2456 mode
  6. swagger文档转换为WebApiClient声明式代码
  7. 每天一个实用小技巧!教你在Mac上快速输入长文本
  8. vscode安装live server
  9. 详细叙述ajax的详情,ajax的配置详情、ajax的调用解释、ajax的中文乱码和ajax的表单提交(内有实例)...
  10. Sublime Text 3 插件安装及Vim 模式设置
  11. rk3399_android7.1调试USB接口的TP记录
  12. 如何获取网络标准时间
  13. TASKCTL调度服务平台节点管理
  14. 基于金笛短信Web中间件实现Cacti短信报警
  15. ES6 里的symbol
  16. 服装行业如何用手持PDA盘点?
  17. BackTrack5 学习笔记三
  18. parseInt转换
  19. 开源的大文件上传组件NeatUpload™
  20. 搜索引擎优化(SEO)之 前端性能优化技巧

热门文章

  1. socketio mysql_socket.io 在java与微信小程序上的应用
  2. 计算机统考第五次作业操作题,计算机基础第5次作业 第五章 Powerpoint知识题
  3. laravel没有route.php,Laravel中的RouteCollection.php中的NotFoundHttpException
  4. 为什么要用shiro框架_社群裂变为什么要用微信群助手?怎么去策划社群的线上裂变?...
  5. 什么?你的电商网页不够时尚?看这里
  6. 极简潮流!最新海报欣赏给你设计灵感
  7. 感恩节活动促销海报模板,摆好借势感恩节的姿势
  8. 有效提高作品率的UI设计技巧,你知道多少?
  9. 推荐一个看ELF文件的软件 010Editor
  10. 一个O-RAN YANG语言文件:o-ran-interfaces.yang