Angular 2.x 从0到1 (五)史上最简单的Angular2教程

第一节:Angular 2.0 从0到1 (一)
第二节:Angular 2.0 从0到1 (二)
第三节:Angular 2.0 从0到1 (三)
第四节:Angular 2.0 从0到1 (四)
第五节:Angular 2.0 从0到1 (五)

第五节:多用户版本的待办事项应用

第四节我们完成的Todo的基本功能看起来还不错,但是有个大问题,就是每个用户看到的都是一样的待办事项,我们希望的是每个用户拥有自己的待办事项列表。我们来分析一下怎么做,如果每个todo对象带一个UserId属性是不是可以解决呢?好像可以,逻辑大概是这样:用户登录后转到/todo,TodoComponent得到当前用户的UserId,然后调用TodoService中的方法,传入当前用户的UserId,TodoService中按UserId去筛选当前用户的Todos。
但可惜我们目前的LoginComponent还是个实验品,很多功能的缺失,我们是先去做Login呢,还是利用现有的Todo对象先试验一下呢?我个人的习惯是先进行试验。

数据驱动开发

按之前我们分析的,给todo加一个userId属性,我们手动给我们目前的数据加上userId属性吧。更改todo\todo-data.json为下面的样子:

{"todos": [{"id": "bf75769b-4810-64e9-d154-418ff2dbf55e","desc": "getting up","completed": false,"userId": 1},{"id": "5894a12f-dae1-5ab0-5761-1371ba4f703e","desc": "have breakfast","completed": true,"userId": 2},{"id": "0d2596c4-216b-df3d-1608-633899c5a549","desc": "go to school","completed": true,"userId": 1},{"id": "0b1f6614-1def-3346-f070-d6d39c02d6b7","desc": "test","completed": false,"userId": 2},{"id": "c1e02a43-6364-5515-1652-a772f0fab7b3","desc": "This is a te","completed": false,"userId": 1}]
}

如果你还没有启动json-server的话让我们启动它: json-server ./src/app/todo/todo-data.json,然后打开浏览器在地址栏输入http://localhost:3000/todos/?userId=2你会看到只有userId=2的json被输出了

[{"id": "5894a12f-dae1-5ab0-5761-1371ba4f703e","desc": "have breakfast","completed": true,"userId": 2},{"id": "0b1f6614-1def-3346-f070-d6d39c02d6b7","desc": "test","completed": false,"userId": 2}
]

有兴趣的话可以再试试http://localhost:3000/todos/?userId=2&completed=false或其他组合查询。现在todo有了userId字段,但我们还没有User对象,User的json表现形式看起来应该是这样:

    {"id": 1,"username": "wang","password": "1234"}

当然这个表现形式有很多问题,比如密码是明文的,这些问题我们先不管,但大概样子是类似的。那么现在如果要建立User数据库的话,我们应该新建一个user-data.json

{"users": [{"id": 1,"username": "wang","password": "1234"},{"id": 2,"username": "peng","password": "5678"}]
}

但这样做的话感觉单独为其建一个文件有点不值得,我们干脆把user和todo数据都放在一个文件吧,现在删除./src/app/todo/todo-data.json删除,在src\app下面新建一个data.json

//src\app\data.json
{"todos": [{"id": "bf75769b-4810-64e9-d154-418ff2dbf55e","desc": "getting up","completed": false,"userId": 1},{"id": "5894a12f-dae1-5ab0-5761-1371ba4f703e","desc": "have breakfast","completed": true,"userId": 2},{"id": "0d2596c4-216b-df3d-1608-633899c5a549","desc": "go to school","completed": true,"userId": 1},{"id": "0b1f6614-1def-3346-f070-d6d39c02d6b7","desc": "test","completed": false,"userId": 2},{"id": "c1e02a43-6364-5515-1652-a772f0fab7b3","desc": "This is a te","completed": false,"userId": 1}],"users": [{"id": 1,"username": "wang","password": "1234"},{"id": 2,"username": "peng","password": "5678"}]
}

当然有了数据,我们就得有对应的对象,基于同样的理由,我们把所有的entity对象都放在一个文件:删除src\app\todo\todo.model.ts,在src\app下新建一个目录domain,然后在domain下新建一个entities.ts,请别忘了更新所有的引用。

export class Todo {id: string;desc: string;completed: boolean;userId: number;
}
export class User {id: number;username: string;password: string;
}

验证用户账户的流程

我们来梳理一下用户验证的流程

  1. 存储要访问的URL

  2. 根据本地的已登录标识判断是否此用户已经登录,如果已登录就直接放行

  3. 如果未登录导航到登录页面 用户填写用户名和密码进行登录

  4. 系统根据用户名查找用户表中是否存在此用户,如果不存在此用户,返回错误

  5. 如果存在对比填写的密码和存储的密码是否一致,如果不一致,返回错误

  6. 如果一致,存储此用户的已登录标识到本地

  7. 导航到原本要访问的URL即第一步中存储的URL,删掉本地存储的URL

看上去我们需要实现

  • UserService:用于通过用户名查找用户并返回用户

  • AuthService:用于认证用户,其中需要利用UserService的方法

  • AuthGuard:路由拦截器,用于拦截到路由后通过AuthService来知道此用户是否有权限访问该路由,根据结果导航到不同路径。
    看到这里,你可能有些疑问,为什么我们不把UserService和AuthService合并呢?这是因为UserService是用于对用户的操作的,不光认证流程需要用到它,我们未来要实现的一系列功能都要用到它,比如注册用户,后台用户管理,以及主页要显示用户名称等。

核心模块

根据这个逻辑流程,我们来组织一下代码。开始之前我们想把认证相关的代码组织在一个新的模块下,我们暂时叫它core吧。在src\app下新建一个core目录,然后在core下面新建一个core.module.ts

import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({imports: [CommonModule]
})
export class CoreModule {constructor (@Optional() @SkipSelf() parentModule: CoreModule) {if (parentModule) {throw new Error('CoreModule is already loaded. Import it in the AppModule only');}}

注意到这个模块和其他模块不太一样,原因是我们希望只在应用启动时导入它一次,而不会在其它地方导入它。在模块的构造函数中我们会要求Angular把CoreModule注入自身,这看起来像一个危险的循环注入。不过,@SkipSelf装饰器意味着在当前注入器的所有祖先注入器中寻找CoreModule。如果该构造函数在我们所期望的AppModule中运行,就没有任何祖先注入器能够提供CoreModule的实例,于是注入器会放弃查找。默认情况下,当注入器找不到想找的提供商时,会抛出一个错误。 但@Optional装饰器表示找不到该服务也无所谓。 于是注入器会返回null,parentModule参数也就被赋成了空值,而构造函数没有任何异常。
那么我们在什么时候会需要这样一个模块?比如在这个模块中我们可能会要提供用户服务(UserService),这样的服务系统各个地方都需要,但我们不希望它被创建多次,希望它是一个单例。再比如某些只应用于AppComponent模板的一次性组件,没有必要共享它们,然而如果把它们留在根目录,还是显得太乱了。我们可以通过这种形式隐藏它们的实现细节。然后通过根模块AppModule导入CoreModule来获取其能力。

路由守卫

首先我们来看看Angular内建的路由守卫机制,在实际工作中我们常常会碰到下列需求:

  • 该用户可能无权导航到目标组件。 导航前需要用户先登录(认证)。

  • 在显示目标组件前,我们可能得先获取某些数据。

  • 在离开组件前,我们可能要先保存修改。

  • 我们可能要询问用户:你是否要放弃本次更改,而不用保存它们?

我们可以往路由配置中添加守卫,来处理这些场景。守卫返回true,导航过程会继续;返回false,导航过程会终止,且用户会留在原地(守卫还可以告诉路由器导航到别处,这样也取消当前的导航)。

路由器支持多种守卫:

  • 用CanActivate来处理导航到某路由的情况。

  • 用CanActivateChild处理导航到子路由的情况。

  • 用CanDeactivate来处理从当前路由离开的情况。

  • 用Resolve在路由激活之前获取路由数据。

  • 用CanLoad来处理异步导航到某特性模块的情况。

在分层路由的每个级别上,我们都可以设置多个守卫。路由器会先按照从最深的子路由由下往上检查的顺序来检查CanDeactivate守护条件。然后它会按照从上到下的顺序检查CanActivate守卫。如果任何守卫返回false,其它尚未完成的守卫会被取消,这样整个导航就被取消了。

本例中我们希望用户未登录前不能访问todo,那么需要使用CanActivate

import { AuthGuardService } from '../core/auth-guard.service';
const routes: Routes = [{path: 'todo/:filter',canActivate: [AuthGuardService],component: TodoComponent}
];

当然光这么写是没有用的,下面我们来建立一个AuthGuardService,命令行中键入ng g s core/auth-guard(angular-cli对于Camel写法的文件名是采用-来分隔每个大写的词)。

import { Injectable, Inject } from '@angular/core';
import {CanActivate,Router,ActivatedRouteSnapshot,RouterStateSnapshot }    from '@angular/router';@Injectable()
export class AuthGuardService implements CanActivate {constructor(private router: Router) { }canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {//取得用户访问的URLlet url: string = state.url;return this.checkLogin(url);}checkLogin(url: string): boolean {//如果用户已经登录就放行if (localStorage.getItem('userId') !== null) { return true; }//否则,存储要访问的URl到本地localStorage.setItem('redirectUrl', url);//然后导航到登陆页面this.router.navigate(['/login']);//返回false,取消导航return false;}
}

观察上面代码,我们发现本地存储的userId的存在与否决定了用户是否已登录的状态,这当然是一个漏洞百出的实现,但我们暂且不去管它。现在我们要在登录时把这个状态值写进去。我们新建一个登录鉴权的AuthServiceng g s core/auth

import { Injectable, Inject } from '@angular/core';
import { Http, Headers, Response } from '@angular/http';import 'rxjs/add/operator/toPromise';
import { Auth } from '../domain/entities';@Injectable()
export class AuthService {constructor(private http: Http, @Inject('user') private userService) { }loginWithCredentials(username: string, password: string): Promise<Auth> {return this.userService.findUser(username).then(user => {let auth = new Auth();localStorage.removeItem('userId');let redirectUrl = (localStorage.getItem('redirectUrl') === null)?'/': localStorage.getItem('redirectUrl');auth.redirectUrl = redirectUrl;if (null === user){auth.hasError = true;auth.errMsg = 'user not found';} else if (password === user.password) {auth.user = Object.assign({}, user);auth.hasError = false;localStorage.setItem('userId',user.id);} else {auth.hasError = true;auth.errMsg = 'password not match';}return auth;}).catch(this.handleError);}private handleError(error: any): Promise<any> {console.error('An error occurred', error); // for demo purposes onlyreturn Promise.reject(error.message || error);}
}

注意到我们返回了一个Auth对象,这是因为我们要知道几件事:

  • 用户最初要导航的页面URL

  • 用户对象

  • 如果发生错误的话,是什么错误,我们需要反馈给用户

这个Auth对象同样在src\app\domain\entities.ts中声明

export class Auth {user: User;hasError: boolean;errMsg: string;redirectUrl: string;
}

当然我们还得实现UserService:ng g s user

import { Injectable } from '@angular/core';import { Http, Headers, Response } from '@angular/http';import 'rxjs/add/operator/toPromise';
import { User } from '../domain/entities';@Injectable()
export class UserService {private api_url = 'http://localhost:3000/users';constructor(private http: Http) { }findUser(username: string): Promise<User> {const url = `${this.api_url}/?username=${username}`;return this.http.get(url).toPromise().then(res => {let users = res.json() as User[];return (users.length>0)?users[0]:null;}).catch(this.handleError);}private handleError(error: any): Promise<any> {console.error('An error occurred', error); // for demo purposes onlyreturn Promise.reject(error.message || error);}
}

这段代码比较简单,就不细讲了。下面我们改造一下src\app\login\login.component.html,在原来用户名的验证信息下加入,用于显示用户不存在或者密码不对的情况

        <div *ngIf="usernameRef.errors?.required">this is required</div><div *ngIf="usernameRef.errors?.minlength">should be at least 3 charactors</div><!--add the code below--><div *ngIf="auth?.hasError">{{auth.errMsg}}</div>

当然我们还得改造src\app\login\login.component.ts

import { Component, OnInit, Inject } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';import { Auth } from '../domain/entities';@Component({selector: 'app-login',templateUrl: './login.component.html',styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {username = '';password = '';auth: Auth;constructor(@Inject('auth') private service, private router: Router) { }ngOnInit() {}onSubmit(formValue){this.service.loginWithCredentials(formValue.login.username, formValue.login.password).then(auth => {let redirectUrl = (auth.redirectUrl === null)? '/': auth.redirectUrl;if(!auth.hasError){this.router.navigate([redirectUrl]);localStorage.removeItem('redirectUrl');} else {this.auth = Object.assign({}, auth);}});}
}

然后我们别忘了在core模块中声明我们的服务src\app\core\core.module.ts

import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AuthService } from './auth.service';
import { UserService } from './user.service';
import { AuthGuardService } from './auth-guard.service';
@NgModule({imports: [CommonModule],providers: [{ provide: 'auth', useClass: AuthService },{ provide: 'user', useClass: UserService },AuthGuardService]
})
export class CoreModule {constructor (@Optional() @SkipSelf() parentModule: CoreModule) {if (parentModule) {throw new Error('CoreModule is already loaded. Import it in the AppModule only');}}
}

最后我们得改写一下TodoService,因为我们访问的URL变了,要传递的数据也有些变化

//todo.service.ts代码片段// POST /todosaddTodo(desc:string): Promise<Todo> {//“+”是一个简易方法可以把string转成numberconst userId:number = +localStorage.getItem('userId');let todo = {id: UUID.UUID(),desc: desc,completed: false,userId};return this.http.post(this.api_url, JSON.stringify(todo), {headers: this.headers}).toPromise().then(res => res.json() as Todo).catch(this.handleError);}// GET /todosgetTodos(): Promise<Todo[]>{const userId = +localStorage.getItem('userId');const url = `${this.api_url}/?userId=${userId}`;return this.http.get(url).toPromise().then(res => res.json() as Todo[]).catch(this.handleError);}// GET /todos?completed=true/falsefilterTodos(filter: string): Promise<Todo[]> {const userId:number = +localStorage.getItem('userId');const url = `${this.api_url}/?userId=${userId}`;switch(filter){case 'ACTIVE': return this.http.get(`${url}&completed=false`).toPromise().then(res => res.json() as Todo[]).catch(this.handleError);case 'COMPLETED': return this.http.get(`${url}&completed=true`).toPromise().then(res => res.json() as Todo[]).catch(this.handleError);default:return this.getTodos();}}

现在应该已经ok了,我们来看看效果:
用户密码不匹配时,显示password not match
image_1b23h2m601puv1q9664c52c1jem9.png-7.2kB
用户不存在时,显示user not found
image_1b23h3l811dn4g9h16qu1jm11htbm.png-5.6kB
直接在浏览器地址栏输入http://localhost:4200/todo,你会发现被重新导航到了login。输入正确的用户名密码后,我们被导航到了todo,现在每个用户都可以创建属于自己的待办事项了。
image_1b23hdv51l621elh1uucsri32213.png-51.1kB

路由模块化

Angular团队推荐把路由模块化,这样便于使业务逻辑和路由松耦合。虽然目前在我们的应用中感觉用处不大,但按官方推荐的方式还是和大家一起改造一下吧。删掉原有的app.routes.tstodo.routes.ts。添加app-routing.module.ts:

import { NgModule }     from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from './login/login.component';const routes: Routes = [{path: '',redirectTo: 'login',pathMatch: 'full'},{path: 'login',component: LoginComponent},{path: 'todo',redirectTo: 'todo/ALL'}
];@NgModule({imports: [RouterModule.forRoot(routes)],exports: [RouterModule]
})
export class AppRoutingModule {}

以及src\app\todo\todo-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { TodoComponent } from './todo.component';import { AuthGuardService } from '../core/auth-guard.service';const routes: Routes = [{path: 'todo/:filter',canActivate: [AuthGuardService],component: TodoComponent}
];@NgModule({imports: [ RouterModule.forChild(routes) ],exports: [ RouterModule ]
})
export class TodoRoutingModule { }

并分别在AppModule和TodoModule中引入路由模块。

用VSCode进行调试

有读者问如何用vscode进行debug,这章我们来介绍一下。首先需要安装一个vscode插件,点击左侧最下面的图标或者“在查看菜单中选择命令面板,输入install,选择扩展:安装扩展”,然后输入“debugger for chrome”回车,点击安装即可。
image_1b23hjd3rble1nb11u7i19qgjqb1g.png-170.5kB
然后点击最左边的倒数第二个按钮
image_1b23htavu19i412obd751h8kusj1t.png-72.5kB
如果是第一次使用的话,齿轮图标上会有个红点,点击选择debugger for chrome,vscode会帮你创建一个配置文件,这个文件位于\.vscode\launch.json是debugger的配置文件,请改写成下面的样子。注意如果是MacOSX或者Linux,请把userDataDir替换成对应的临时目录,另外把"webpack:///C:*":"C:/*"替换成"webpack:///*": "/*",这句是因为angular-cli是采用webpack打包的,如果没有使用angular-cli不需要添加这句。

{"version": "0.2.0","configurations": [{"name": "Launch Chrome against localhost, with sourcemaps","type": "chrome","request": "launch","url": "http://localhost:4200","sourceMaps": true,"runtimeArgs": ["--disable-session-crashed-bubble","--disable-infobars"],"diagnosticLogging": true,"webRoot": "${workspaceRoot}/src",//windows setup"userDataDir": "C:\\temp\\chromeDummyDir","sourceMapPathOverrides": {"webpack:///C:*":"C:/*"//use "webpack:///*": "/*" on Linux/OSX}},{"name": "Attach to Chrome, with sourcemaps","type": "chrome","request": "attach","port": 9222,"sourceMaps": true,"diagnosticLogging": true,"webRoot": "${workspaceRoot}/src","sourceMapPathOverrides": {"webpack:///C:*":"C:/*"}}]
}

现在你可以试着在源码中设置一个断点,点击debug视图中的debug按钮,可以尝试右键点击变量把它放到监视中看看变量值或者逐步调试应用。
image_1b23igfkdhn71ug71cng3in94t2a.png-400.1kB

本章完整代码见: https://github.com/wpcfan/awe...

第一节:Angular 2.0 从0到1 (一)
第二节:Angular 2.0 从0到1 (二)
第三节:Angular 2.0 从0到1 (三)
第四节:Angular 2.0 从0到1 (四)
第五节:Angular 2.0 从0到1 (五)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/282642.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ajax 分页 评论刷新,评论:js无刷新分页(原创)

繁华落尽02020/4/28 0:26:00大佬&#xff0c;教一下怎么用&#xff0c;以前我是直接在按钮上绑个路径。首页上一页${i}${i}下一页尾页漫走32020/4/28 20:43:32后台的方法需要的参数&#xff1a;当前页、每页显示条数&#xff0c;插件都给你控制好了&#xff0c;你直接用就行。e…

设计模式——享元模式具体解释

0. 前言写在最前面&#xff0c;本人的设计模式类博文&#xff0c;建议先看博文前半部分的理论介绍。再看后半部分的实例分析。最后再返回来复习一遍理论介绍&#xff0c;这时候你就会发现我在重点处标红的用心&#xff0c;对于帮助你理解设计模式有奇效哦~本文原创。转载请注明…

OpenStack Nova计算服务管理(四)

作者&#xff1a;李晓辉联系方式: Xiaohui_lifoxmail.com环境介绍类型控制节点和计算节点等在一起&#xff0c;形成all-in-one内存8G硬盘200G网卡2块计算服务概览使用OpenStack计算服务来托管和管理云计算系统。OpenStack计算服务是基础设施即服务(IaaS)系统的主要部分&#xf…

miui替换官方文件解决无服务器,miui 关掉云服务器

miui 关掉云服务器 内容精选换一换本节操作介绍Linux云服务器切换密钥登录为密码登录的操作步骤。使用密钥登录Linux云服务器&#xff0c;设置root密码。sudo passwd root若密钥文件丢失或损坏&#xff0c;请参考Linux云服务器如何进入单用户模式重置root密码&#xff0c;重置r…

原型

2019独角兽企业重金招聘Python工程师标准>>> 什么是原型&#xff1a; 对象与对象之间的关系 转载于:https://my.oschina.net/u/2285087/blog/854377

存储服务器的操作系统,存储服务器是什么操作系统

存储服务器是什么操作系统 内容精选换一换镜像服务提供了私有镜像的全生命周期管理能力&#xff0c;主要包括创建私有镜像&#xff0c;复制、共享或导出私有镜像等操作&#xff0c;您可以根据实际场景选择合适的方法&#xff0c;并结合弹性云服务器、对象存储等周边服务完成业务…

优化--减少HTTP请求

一、 图片地图 (将几张图片合为一张,根据用户点击的位置发送不同请求,减少了图片的请求数量) 案例所在位置:http://stevesouders.com/hpws/imagemap.php 二、css精灵(和图片地图功能相似,都是将几张图片合并在一起,根据位置发送不同请求) 这里不做具体使用介绍,百度有此方面内…

软件负载均衡

一、软件负载均衡概述 硬件负载均衡性能优越&#xff0c;功能全面&#xff0c;但是价格昂贵&#xff0c;一般适合初期或者土豪级公司长期使用。因此软件负载均衡在互联网领域大量使用。常用的软件负载均衡软件有Nginx&#xff0c;Lvs&#xff0c;HaProxy等。本文参考大量文档&a…

git工具 将源码clone到本地指定目录的三种方式

git工具 将源码clone到本地指定目录的三种方式 CreationTime--2018年7月27日15点34分 Author:Marydon 1.情景展示 运行git-bash.exe&#xff0c;输入命令&#xff1a;git clone 下载源码地址-->回车&#xff0c;结果发现项目被下载到了&#xff0c;git工具的安装目录下 如何…

[摘]全文检索引擎Solr系列—–全文检索基本原理

原文链接--http://www.importnew.com/12707.html 全文检索引擎Solr系列—–全文检索基本原理 2014/08/18 | 分类&#xff1a; 基础技术, 教程 | 2 条评论 | 标签&#xff1a; solr 分享到&#xff1a; 64 本文作者&#xff1a; ImportNew - 刘志军 未经许可&#xff0c;禁止转载…

优化-浏览器缓存和压缩优化

一、减少HTTP请求 1.图片地图&#xff1a; 假设导航栏上有五幅图片&#xff0c;点击每张图片都会进入一个链接&#xff0c;这样五张导航的图片在加载时会产生5个HTTP请求。然而&#xff0c;使用一个图片地图可以提高效率&#xff0c;这样就只需要一个HTTP请求。 服务器端图片…

NCC CAP 6.2 版本正式发布

原文&#xff1a;https://www.cnblogs.com/savorboard/p/cap-6-2.html作者&#xff1a;杨晓东前言今天&#xff0c;我们很高兴宣布 CAP 发布 6.2 版本正式版&#xff0c;在这个版本中我们主要做了一些功能优化&#xff0c;以及针对目前已经发现的几个 BUG 进行了修复了。那么&a…

sysctl.conf工作原理

2019独角兽企业重金招聘Python工程师标准>>> sysctl.conf工作原理 sysctl命令被用于在内核运行时动态地修改内核的运行参数&#xff0c;可用的内核参数在目录/proc/sys中。它包含一些TCP/IP堆栈和虚拟内存系统的高级选项&#xff0c; 这可以让有经验的管理员提高引人…

CDN加速

一、CDN的概念 全称是Content Delivery Network&#xff0c;即内容分发网络。 其基本思路是: 尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节&#xff0c;使内容传输的更快、更稳定。 通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智…

修复删除/var/lib/dpkg目录后,无法使用apt-get命令问题

2019独角兽企业重金招聘Python工程师标准>>> Unfortunately Ive deleted dpkg directory while removing the lock. By mistake I typed rootsam:~$ rm -r /var/lib/dpkgNow when I am trying to install/uninstall packages it shows me following error. E: Could…

动态语言静态化

一、什么是动态语言静态化 将现有PHP等动态语言的逻辑代码生成为静态html文件,用户访问动态脚本重定向到静态html的过程 注 : 对实时性要求不高的页面才适合去做动态语言静态化 二、为什么要静态化 1. 动态脚本通常会做逻辑计算和数据查询,访问量越大,服务器压力越大 2. 访…

WPF-06 样式(Style)

在我们前面介绍资源的时候&#xff0c;我们提到了样式表&#xff0c;如果你之前是做Web开发的&#xff0c;你会发现Style有点类似于Web中的CSS。控件级别样式我们可以在控件级别定义自己的样式&#xff0c;控件级别的样式是优先级最高的<Window x:Class"Example_06.Sel…

构建Squid代理服务器-传统代理、透明代理、反向代理

Squid是Linux系统中最常用的一款开源代理服务软件&#xff0c;主要提供缓存加速和应用层过滤控制的功能&#xff0c;可以很好的实现HTTP、FTP、DNS查询以及SSL等应用的缓存代理。 正向代理&#xff1a;根据实现的方式不同&#xff0c;代理服务可分为传统代理和透明代理。 传统代…

数据库缓存层

一 常见的缓存形式 : 1.文件缓存 (为了避免I/O开销,尽量使用内存缓存) 2.内存缓存 二 为什么要使用缓存 缓存数据是为了让客户端很少甚至不访问数据库服务器进行的数据查询,高并发下,能最大程度降低对数据库服务器的访问压力 一般的数据请求: 用户请求->数据查询->…