All posts by 龙生
Selenium
Selenium 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。这个工具的主要功能包括:测试与浏览器的兼容性——测试你的应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创建回归测试检验软件功能和用户需求。支持自动录制动作和自动生成 .Net、Java、Perl等不同语言的测试脚本。 from:https://baike.baidu.com/item/selenium/18266
View Detailsspring cloud 入门系列二:使用Eureka 进行服务治理
服务治理可以说是微服务架构中最为核心和基础的模块,它主要用来实现各个微服务实例的自动化注册和发现。 Spring Cloud Eureka是Spring Cloud Netflix 微服务套件的一部分,主要负责完成微服务架构中的服务治理功能。 本文通过简单的小例子来分享下如何通过Eureka进行服务治理: 搭建服务注册中心 注册服务提供者 服务发现和消费 ==========我是华丽的分割线======================== 一、搭建服务注册中心 先列出完整目录结构: 搭建过程如下: 创建maven工程:eureka(具体实现略) 修改pom文件,引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sam</groupId> <artifactId>eureka</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> </parent> <properties> <javaVersion>1.8</javaVersion> </properties> <!-- 使用dependencyManagement进行版本管理 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR6</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- 引入eureka server依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> </dependencies> </project> |
创建启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * * @EnableEurekaServer * 用来指定该项目为Eureka的服务注册中心 */ @EnableEurekaServer @SpringBootApplication public class EurekaApp { public static void main(String[] args) { SpringApplication.run(EurekaApp.class, args); } } |
配置application.properties文件
1 2 3 4 5 6 7 8 9 10 11 12 |
#设置tomcat服务端口号 server.port=1111 #设置服务名称 spring.application.name=eureka-service eureka.instance.hostname=localhost #注册中心不需要注册自己 eureka.client.register-with-eureka=false #注册中心不需要去发现服务 eureka.client.fetch-registry=false #设置服务注册中心的URL eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka |
启动服务并访问,我们会看到这样的画面: 二、注册服务提供者 先列出完整目录结构: 搭建过程如下: 创建maven工程:hello-service(具体实现略) 修改pom文件,引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sam</groupId> <artifactId>hello-service</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> </parent> <properties> <javaVersion>1.8</javaVersion> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR6</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- 引入eureka 客户端依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> </dependencies> </project> |
创建启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/*** * * @EnableDiscoveryClient * 让服务使用eureka服务器 * 实现服务注册和发现 * */ @EnableDiscoveryClient @SpringBootApplication public class HelloApp { public static void main(String[] args) { SpringApplication.run(HelloApp.class, args); } } |
创建controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@RestController public class HelloController { Logger logger = LoggerFactory.getLogger(HelloController.class); @Autowired DiscoveryClient discoveryClient; @RequestMapping("/hello") public String hello() { ServiceInstance instance = discoveryClient.getLocalServiceInstance(); //打印服务的服务id logger.info("*********" + instance.getServiceId()); return "hello,this is hello-service"; } } |
配置application.properties文件
1 2 3 4 5 |
server.port=9090 #设置服务名 spring.application.name=hello-service #设置服务注册中心的URL,本服务要向该服务注册中心注册自己 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka |
启动并测试 )启动后再hello-service的控制台会有这种字样(xxx代表你的PC名)
1 |
Registered instance HELLO-SERVICE/xxx:hello-service:9090 with status UP (replication=false) |
eureka的控制台会打印出如下字样(xxx代表你的PC名)
1 |
Registered instance HELLO-SERVICE/xxx:hello-service:9090 with status UP (replication=false) |
)再次访问localhost:1111,会发现有服务注册到注册中心了 三、服务发现和消费 完整目录结构如下: 搭建过程: 创建maven工程(具体实现略) 修改pom文件,引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sam</groupId> <artifactId>hello-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> </parent> <properties> <javaVersion>1.8</javaVersion> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR6</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- 引入eureka 客户端依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- 引入ribbon 依赖 ,用来实现负载均衡,我们这里只是使用,先不作其他介绍--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> </dependencies> </project> 这里比hello-service服务提供者,多了ribbon的依赖 |
创建启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@EnableDiscoveryClient @SpringBootApplication public class ConsumerApp { //@Bean 应用在方法上,用来将方法返回值设为为bean @Bean @LoadBalanced //@LoadBalanced实现负载均衡 public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerApp.class, args); } } |
1 |
这里也要用到@EnableDiscoveryClient, 让服务使用eureka服务器, 实现服务注册和发现 |
创建controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@RestController public class ConsumerController { //这里注入的restTemplate就是在com.sam.ConsumerApp中通过@Bean配置的实例 @Autowired RestTemplate restTemplate; @RequestMapping("/hello-consumer") public String helloConsumer() { //调用hello-service服务,注意这里用的是服务名,而不是具体的ip+port restTemplate.getForObject("http://hello-service/hello", String.class); return "hello consumer finish !!!"; } } |
配置application.properties文件
1 2 3 4 5 6 7 |
server.port=9999 spring.application.name=hello-consumer eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka #这里的配置项目和服务提供者hello-service一样 |
启动,测试 )启动eureka。为了展示负责均衡的效果,我们的hello-service启动两个服务,启动两个服务的具体步骤如下 以上是hello-service1的启动步骤,端口号为9090;同样方法设置hello-service2,端口号为9091(具体实现略)。 )启动hello-consumer )再次访问http://localhost:1111/,会发现有2个hello-service服务(端口号一个是9090,一个是9091),1个hello-consume服务 ) 多次访问http://localhost:9999/hello-consumer,会发现hello-service1和hello-service2会轮流被调用(已经实现了负责均衡),可以通过两者的控制台打印内容确认(还记得我们在hello-service的controller中有个loggerlogger.info("*********" + instance.getServiceId());吗?对,就是这个打印) 四、总结 以上实例实现了基本的服务治理: 通过spring-cloud-starter-eureka-server和@EnableEurekaServer实现服务注册中心 通过spring-cloud-starter-eureka和@EnableDiscoveryClient使用并注册到服务注册中心 通过spring-cloud-starter-eureka和@EnableDiscoveryClient使用注册中心并发现服务,通过spring-cloud-starter-ribbon来实现负载均衡消费服务 PS:这里说明下,我用的IDE是Spring Tool Suite,是spring定制版的eclipse,方便我们使用spring进行开发,有兴趣的朋友可以自行百度了解下。 from:https://www.cnblogs.com/sam-uncle/p/8954401.html
View DetailsEureka
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。 Eureka包含两个组件:Eureka Server和Eureka Client。 Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。 Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。 在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。 Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。 from:https://baike.baidu.com/item/Eureka/22402835?fr=aladdin
View Details一次删除多个需要git rm的文件
For Git 1.x
1 |
$ git add -u |
This tells git to automatically stage tracked files — including deleting the previously tracked files. For Git 2.0 To stage your whole working tree:
1 |
$ git add -u :/ |
To stage just the current path:
1 |
$ git add -u . |
from:https://blog.csdn.net/dahailantian1/article/details/79475644
View Detailsgit<撤销本地修改与回退版本>
1. 使用 git checkout 撤销本地修改 即放弃对本地已修改但尚未提交的文件的修改,还原其到未修改前的状态。 注意: 已 add/ commit 的文件不适用个方法,应该用本文提到的第二种方法。 命令如下:
1 2 |
git checkout . # 撤销对所有已修改但未提交的文件的修改,但不包括新增的文件 git checkout [filename] # 撤销对指定文件的修改,[filename]为文件名 |
2. 使用 git reset 回退项目版本 可以回退到任意已经提交过的版本。已 add / commit 但未 push 的文件也适用。 命令如下: 1git reset --hard [commit-hashcode] 2# [commit-hashcode]是某个 commit 的哈希值,可以用 git log 查看 因此一般用法是先用 Git log 查看具体commit的哈希值,然后 reset 到那个版本。 说明: 这两个命令都不会对新增文件起作用。因为新增的文件是还未加到 git 的记录里面的,即属于未被 tracked 的状态,所以撤销修改和回退均对其不影响。我们直接手动删除文件就行了。 转自:http://blog.csdn.net/dandelion_drq/article/details/51259831 from:https://www.cnblogs.com/runningdonkey/p/6932437.html
View Detailsreact-navigation示例
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import React, { Component } from 'react' import { Platform, StyleSheet, Text, View } from 'react-native' import { createStackNavigator, createAppContainer } from 'react-navigation' import HomeScreen from './pages/HomeScreen' import ProfileScreen from './pages/ProfileScreen.js' const navigator = createStackNavigator({ Home: { screen: HomeScreen }, Profile: { screen: ProfileScreen } }) const App = createAppContainer(navigator) export default App const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF' }, welcome: { fontSize: 20, textAlign: 'center', margin: 10 }, instructions: { textAlign: 'center', color: '#333333', marginBottom: 5 } }) |
pages/ProfileScreen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import React, { Component } from 'react' import { Text, StyleSheet, View } from 'react-native' export default class ProfileScreen extends Component { static navigationOptions = { title: 'ProfileScreen' } render() { return ( <View> <Text> 2 </Text> </View> ) } } const styles = StyleSheet.create({}) |
pages/HomeScreen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import React, { Component } from 'react' import { Text, StyleSheet, View, Button } from 'react-native' import { createStackNavigator, createAppContainer } from 'react-navigation'; export default class HomeScreen extends Component { static navigationOptions = { title: 'HomeScreen' } render() { return ( <View> <Text> one </Text> <Button title="go to two" onPress={() => this.props.navigation.navigate('Profile')} /> </View> ) } } const styles = StyleSheet.create({}) |
然后报错: undefined is not an object (evaluating ‘RNGestureHandlerModule.State’ 解决办法: npm install npm install --save react-navigation npm install --save react-native-gesture-handler react-native link 参考: https://www.cnblogs.com/wuvkcyan/p/10011012.html https://blog.csdn.net/quhongqiang/article/details/86597694
View Details处理文本输入
TextInput是一个允许用户输入文本的基础组件。它有一个名为onChangeText的属性,此属性接受一个函数,而此函数会在文本变化时被调用。另外还有一个名为onSubmitEditing的属性,会在文本被提交后(用户按下软键盘上的提交键)调用。 假如我们要实现当用户输入时,实时将其以单词为单位翻译为另一种文字。我们假设这另一种文字来自某个吃货星球,只有一个单词: 🍕。所以"Hello there Bob"将会被翻译为"🍕🍕🍕"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import React, { Component } from 'react'; import { AppRegistry, Text, TextInput, View } from 'react-native'; export default class PizzaTranslator extends Component { constructor(props) { super(props); this.state = {text: ''}; } render() { return ( <View style={{padding: 10}}> <TextInput style={{height: 40}} placeholder="Type here to translate!" onChangeText={(text) => this.setState({text})} /> <Text style={{padding: 10, fontSize: 42}}> {this.state.text.split(' ').map((word) => word && '🍕').join(' ')} </Text> </View> ); } } |
在上面的例子里,我们把text保存到 state 中,因为它会随着时间变化。 文本输入方面还有很多其他的东西要处理。比如你可能想要在用户输入的时候进行验证,在React 中的受限组件一节中有一些详细的示例(注意 react 中的 onChange 对应的是 rn 中的 onChangeText)。此外你还需要看看TextInput 的文档。 TextInput 可能是天然具有“动态状态”的最简单的组件了。下面我们来看看另一类输入组件,先从如何处理触摸开始学习。 from:https://reactnative.cn/docs/handling-text-input/
View Details使用Flexbox布局
我们在 React Native 中使用 flexbox 规则来指定某个组件的子元素的布局。Flexbox 可以在不同屏幕尺寸上提供一致的布局结构。
一般来说,使用flexDirection、alignItems和 justifyContent三个样式属性就已经能满足大多数布局需求。
样式
本文档贡献者:sunnylqm(100.00%) 在 React Native 中,你并不需要学习什么特殊的语法来定义样式。我们仍然是使用 JavaScript 来写样式。所有的核心组件都接受名为style的属性。这些样式名基本上是遵循了 web 上的 CSS 的命名,只是按照 JS 的语法要求使用了驼峰命名法,例如将background-color改为backgroundColor。 style属性可以是一个普通的 JavaScript 对象。这是最简单的用法,因而在示例代码中很常见。你还可以传入一个数组——在数组中位置居后的样式对象比居前的优先级更高,这样你可以间接实现样式的继承。 实际开发中组件的样式会越来越复杂,我们建议使用StyleSheet.create来集中定义组件的样式。比如像下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, View } from 'react-native'; export default class LotsOfStyles extends Component { render() { return ( <View> <Text style={styles.red}>just red</Text> <Text style={styles.bigBlue}>just bigBlue</Text> <Text style={[styles.bigBlue, styles.red]}>bigBlue, then red</Text> <Text style={[styles.red, styles.bigBlue]}>red, then bigBlue</Text> </View> ); } } const styles = StyleSheet.create({ bigBlue: { color: 'blue', fontWeight: 'bold', fontSize: 30, }, red: { color: 'red', }, }); |
常见的做法是按顺序声明和使用style属性,以借鉴 CSS 中的“层叠”做法(即后声明的属性会覆盖先声明的同名属性)。 文本的样式定义请参阅Text 组件的文档。 现在你已经了解如何调整文本样式了,下面我们要学习的是如何控制组件的尺寸。 from:https://reactnative.cn/docs/style/
View Details(转)基于DDD的现代ASP.NET开发框架--ABP分层架构
介绍DDD概念 Eric Evans的“Domain-Driven Design领域驱动设计”简称 DDD,它是一套综合软件系统分析和设计的面向对象建模方法,或者可称为MDD模型驱动方法的一种,区别于MDA模型驱动架构。它是一种分析设计建模方法,它倡导统一语言,提出了实体和值对象以及聚合根等概念,借助DDD我们能够在结构理清需求中领域模型。 过去系统分析和系统设计都是分离的,正如我们国家“系统分析师” 和“系统设计师” 两种职称考试一样,这样割裂的结果导致,需求分析的结果无法直接进行设计编程,而能够进行编程运行的代码却扭曲需求,导致客户运行软件后才发现很多功能不是自己想要的,而且软件不能快速跟随需求变化。 DDD则打破了这种隔阂,提出了领域模型概念,统一了分析和设计编程,使得软件能够更灵活快速跟随需求变化。 DDD是解决复杂大型软件的一套行之有效方式,在国外已经成为主流。DDD认为很多原因造成软件的复杂性,我们不可能避免这些复杂性, 能做的是对复杂的问题进行控制。而一个好的领域模型是控制复杂问题的关键。领域模型的价值在于提供一种通用的语言,使得领域专家和软件技术人员联系在一起,沟通无歧义。 前言 为了减少复杂性和提高代码的可重用性,采用分层架构是一种被广泛接受的技术。 为了实现分层的体系结构,ABP遵循DDD(领域驱动设计)的原则,将分为四个层次: 展现层(Presentation):提供一个用户界面,实现用户交互操作。 应用层(Application):进行展现层与领域层之间的协调,协调业务对象来执行特定的应用程序的任务。它不包含业务逻辑。 领域层(Domain):包括业务对象和业务规则,这是应用程序的核心层。 基础设施层(Infrastructure):提供通用技术来支持更高的层。例如基础设施层的仓储(Repository)可通过ORM来实现数据库交互。 根据实际需要,可能会有额外添加的层。例如: 分布式服务层(Distributed Service):用于公开应用程序接口供远程客户端调用。比如通过ASP.NET Web API和WCF来实现。 这些都是常见的以领域为中心的分层体系结构。不同的项目在实现上可能会有细微的差别。 ABP的体系结构 一个简单的解决方案,大致包含5个项目: 每一层可以用一个或多个程序集来实现。 领域层(Domain) 领域层就是业务层,是一个项目的核心,所有业务规则都应该在领域层实现。 实体(Entity) 实体代表业务领域的数据和操作,在实践中,通过用来映射成数据库表。 仓储(Repository) 仓储用来操作数据库进行数据存取。仓储接口在领域层定义,而仓储的实现类应该写在基础设施层。 领域服务(Domain service) 当处理的业务规则跨越两个(及以上)实体时,应该写在领域服务方法里面。 领域事件(Domain Event) 在领域层某些特定情况发生时可以触发领域事件,并且在相应地方捕获并处理它们。 工作单元(Unit of Work) 工作单元是一种设计模式,用于维护一个由已经被修改(如增加、删除和更新等)的业务对象组成的列表。它负责协调这些业务对象的持久化工作及并发问题。 应用层(Application) 应用层提供一些应用服务(Application Services)方法供展现层调用。一个应用服务方法接收一个DTO(数据传输对象)作为输入参数,使用这个输入参数执行特定的领域层操作,并根据需要可返回另一个DTO。在展现层到领域层之间,不应该接收或返回实体(Entity)对象,应该进行DTO映射。一个应用服务方法通常被认为是一个工作单元(Unit of Work)。用户输入参数的验证工作也应该在应用层实现。ABP提供了一个基础架构让我们很容易地实现输入参数有效性验证。建议使用一种像AutoMapper这样的工具来进行实体与DTO之间的映射。 基础设施层(Infrastructure) 当在领域层中为定义了仓储接口,应该在基础设施层中实现这些接口。可以使用ORM工具,例如EntityFramework或NHibernate。ABP的基类已经提供了对这两种ORM工具的支持。数据库迁移也被用于这一层。 WEB与展现层(Web & Presentation) Web层使用ASP.NET MVC和Web API来实现。可分别用于多页面应用程序(MPA)和单页面应用程序(SPA)。 在SPA中,所有资源被一次加载到客户端浏览器中(或者先只加载核心资源,其他资源懒加载),然后通过AJAX调用服务端WebApi接口获取数据,再根据数据生成HTML代码。不会整个页面刷新。现在已经有很多SPA的JS框架,例如: AngularJs、 DurandalJs、BackboneJs、EmberJs。 ABP可以使用任何类似的前端框架,但是ABP提供了一些帮助类,让我们更方便地使用AngularJs和DurandalJs。 在经典的多页面应用(MPA)中,客户端向服务器端发出请求,服务器端代码(ASP.NET MVC控制器)从数据库获得数据,并且使用Razor视图生成HTML。这些被生成后的HTML页面被发送回客户端显示。每显示一个新的页面都会整页刷新。 SPA和MPA涉及到完全不同的体系结构,也有不同的应用场景。一个管理后台适合用SPA,博客就更适合用MPA,因为它更利于被搜索引擎抓取。 SignalR是一种从服务器到客户端发送推送通知的完美工具。它能给用户提供丰富的实时的体验。 已经有很多客户端的Javascript框架或库,JQuery是其中最流行的,并且它有成千上万免费的插件。使用Bootstrap可以让我们更轻松地完成写Html和CSS的工作。 ABP也实现了根据Web API接口自动创建 Javascript的代码函数,来简化JS对Web Api的调用。还有把服务器端的菜单、语言、设置等生成到JS端。(但是在我自己的项目中,我是把这些自动生成功能关闭的,因为必要性不是很大,而这些又会比较影响性能)。 ABP会自动处理服务器端返回的异常,并以友好的界面提示用户。 其它 ABP使用Castle Windsor为整个程序框架提供依赖注入的功能。使用Log4Net日志记录组件,提供给其他各层调用以进行日志记录。 from:https://www.cnblogs.com/anny-1980/p/4535589.html
View Details