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.pswp.cn/news/282642.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282642.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282642.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

《大道至簡》讀后感

所謂的大道至簡就是說大道理&#xff08;基本原理&#xff0c;方法和規律&#xff09;是極其簡單的&#xff0c;簡單到一兩句話就能說明白。所謂“真傳一句話&#xff0c;假傳萬卷書”。這也許也是這本書只有一百多頁的原因吧。 說實話&#xff0c;《大道至簡》這部作品對現在有…

ajax 分頁 評論刷新,評論:js無刷新分頁(原創)

繁華落盡02020/4/28 0:26:00大佬&#xff0c;教一下怎么用&#xff0c;以前我是直接在按鈕上綁個路徑。首頁上一頁${i}${i}下一頁尾頁漫走32020/4/28 20:43:32后臺的方法需要的參數&#xff1a;當前頁、每頁顯示條數&#xff0c;插件都給你控制好了&#xff0c;你直接用就行。e…

MariaDB基礎(二)

MariaDB基礎(二)介紹關于MariaDB的如下知識點&#xff1a;1. 查詢緩存2. 索引3. EXPLAIN1.查詢緩存&#xff1a;1&#xff09;什么是緩存&#xff1f;緩存就是數據交換的緩沖區&#xff0c;即Cache&#xff0c;存放在內存中&#xff1b;2&#xff09;查詢緩存的數據以何種形式存…

設計模式——享元模式具體解釋

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…

PHP-高并發和大流量的解決方案

一 高并發的概念 在互聯網時代&#xff0c;并發&#xff0c;高并發通常是指并發訪問。也就是在某個時間點&#xff0c;有多少個訪問同時到來。 二 高并發架構相關概念 1、QPS (每秒查詢率) : 每秒鐘請求或者查詢的數量&#xff0c;在互聯網領域&#xff0c;指每秒響應請求數…

原型

2019獨角獸企業重金招聘Python工程師標準>>> 什么是原型&#xff1a; 對象與對象之間的關系 轉載于:https://my.oschina.net/u/2285087/blog/854377

JavaScript中數組slice和splice的對比小結

前言 今天重溫了一下Javascript&#xff0c;看到了數組的方法&#xff0c;其中有兩個比較相似的方法——splice和splice&#xff0c;看著很像&#xff0c;就是多了一個p&#xff0c;但是用法卻相當不一樣。 在使用中&#xff0c;可以通過選擇一個具有強語義表達性的 API 來減少…

存儲服務器的操作系統,存儲服務器是什么操作系統

存儲服務器是什么操作系統 內容精選換一換鏡像服務提供了私有鏡像的全生命周期管理能力&#xff0c;主要包括創建私有鏡像&#xff0c;復制、共享或導出私有鏡像等操作&#xff0c;您可以根據實際場景選擇合適的方法&#xff0c;并結合彈性云服務器、對象存儲等周邊服務完成業務…

優化--減少HTTP請求

一、 圖片地圖 (將幾張圖片合為一張,根據用戶點擊的位置發送不同請求,減少了圖片的請求數量) 案例所在位置:http://stevesouders.com/hpws/imagemap.php 二、css精靈(和圖片地圖功能相似,都是將幾張圖片合并在一起,根據位置發送不同請求) 這里不做具體使用介紹,百度有此方面內…

軟件負載均衡

一、軟件負載均衡概述 硬件負載均衡性能優越&#xff0c;功能全面&#xff0c;但是價格昂貴&#xff0c;一般適合初期或者土豪級公司長期使用。因此軟件負載均衡在互聯網領域大量使用。常用的軟件負載均衡軟件有Nginx&#xff0c;Lvs&#xff0c;HaProxy等。本文參考大量文檔&a…

JAVA多線程之先行發生原則

一、引子   如果java內存模型中所有的有序性都僅僅依靠volatile和synchronized來完成&#xff0c;那么有一些操作會變得很繁瑣&#xff0c;但我們在編寫java并發代碼時并未感覺到這一點&#xff0c;這是因為java語言中有個先行發生原則&#xff08;happens-before&#xff09…

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請求。 服務器端圖片…

匯新杯┃拼多多黃崢:普通的創業者,不普通的朋友圈_創成匯

本月26日晚&#xff0c;拼多多在美國納斯達克上市&#xff0c;開盤后便持續走高&#xff0c;收漲高達40.53%&#xff0c;這家從成立到上市不過短短2年10個月的企業&#xff0c;是近四年來最大中概股IPO。拼多多創始人黃崢身家一夜暴漲到138.5億美元。在拼多多之前&#xff0c;黃…

NCC CAP 6.2 版本正式發布

原文&#xff1a;https://www.cnblogs.com/savorboard/p/cap-6-2.html作者&#xff1a;楊曉東前言今天&#xff0c;我們很高興宣布 CAP 發布 6.2 版本正式版&#xff0c;在這個版本中我們主要做了一些功能優化&#xff0c;以及針對目前已經發現的幾個 BUG 進行了修復了。那么&a…

pdksh 包

安裝oracle gi&#xff0c;rac 時經常會碰到 pdksh 的檢測失敗&#xff0c;可以從這個網站下載需要的包 http://rpm.pbone.net/ rpm -q ksh-* rpm -e ksh-* rpm -ivh ./pdksh* 或者參考 Requirements for Installing Oracle 11gR2 RDBMS on RHEL6 or OL6 64-bit (x86-64) (文…

sysctl.conf工作原理

2019獨角獸企業重金招聘Python工程師標準>>> sysctl.conf工作原理 sysctl命令被用于在內核運行時動態地修改內核的運行參數&#xff0c;可用的內核參數在目錄/proc/sys中。它包含一些TCP/IP堆棧和虛擬內存系統的高級選項&#xff0c; 這可以讓有經驗的管理員提高引人…