Angular 4 LocationStrategy

在介紹 LocationStrategy 策略之前释液,我們先來了解以下相關(guān)知識:

  • History 對象

  • Hash 模式和 HTML 5 模式

History 對象

屬性

length

只讀的全释,其值為一個整數(shù),標志包括當前頁面在內(nèi)的會話歷史中的記錄數(shù)量误债,比如我們通常打開一個空白窗口浸船,length 為 0,再訪問一個頁面找前,其 length 變?yōu)?1糟袁。

scrollRestoration

允許 Web 應(yīng)用在會話歷史導航時顯式地設(shè)置默認滾動復(fù)原,其值為 auto 或 manual躺盛。

state

只讀项戴,返回代表會話歷史堆棧頂部記錄的任意可序列化類型數(shù)據(jù)值,我們可以以此來區(qū)別不同會話歷史紀錄槽惫。

方法

back()

返回會話歷史記錄中的上一個頁面周叮,等價于 window.history.go(-1) 和點擊瀏覽器的后退按鈕。

forward()

進入會話歷史記錄中的下一個頁面界斜,等價于 window.history.go(1) 和點擊瀏覽器的前進按鈕仿耽。

go()

加載會話歷史記錄中的某一個頁面,通過該頁面與當前頁面在會話歷史中的相對位置定位各薇,如项贺,-1 代表當前頁面的上一個記錄君躺,1 代表當前頁面的下一個頁面。若不傳參數(shù)或傳入0开缎,則會重新加載當前頁面棕叫;若參數(shù)超出當前會話歷史紀錄數(shù),則不進行操作奕删。

pushState()

在會話歷史堆棧頂部插入一條記錄俺泣,該方法接收三個參數(shù),一個 state 對象完残,一個頁面標題伏钠,一個 URL:

  • 狀態(tài)對象
    • 存儲新添會話歷史記錄的狀態(tài)信息對象,每次訪問該條會話時谨设,都會觸發(fā) popstate 事件熟掂,并且事件回調(diào)函數(shù)會接收一個參數(shù),值為該事件對象的復(fù)制副本扎拣。
    • 狀態(tài)對象可以是任何可序列化的數(shù)據(jù)打掘,瀏覽器將狀態(tài)對象存儲在用戶的磁盤以便用戶再次重啟瀏覽器時能恢復(fù)數(shù)據(jù)
    • 一個狀態(tài)對象序列化后的最大長度是 640K,如果傳遞數(shù)據(jù)過大鹏秋,則會拋出異常
  • 頁面標題
    • 目前該參數(shù)值會被忽略,暫不被使用亡笑,可以傳入空字符串
  • 頁面 URL
    • 此參數(shù)聲明新添會話記錄的入口 URL
    • 在調(diào)用 pushState() 方法后侣夷,瀏覽器不會加載 URL 指向的頁面,我們可以在 popstate 事件回調(diào)中處理頁面是否加載
    • 此 URL 必須與當前頁面 URL 同源,仑乌,否則會拋異常百拓;其值可以是絕對地址,也可以是相對地址晰甚,相對地址會被基于當前頁面 URL 解析得到絕對地址衙传;若其值為空,則默認是當前頁面 URL

replaceState()

更新會話歷史堆棧頂部記錄信息厕九,支持的參數(shù)信息與 pushState() 一致蓖捶。

pushState() 與 replaceState() 的區(qū)別:pushState()是在 history 棧中添加一個新的條目,replaceState() 是替換當前的記錄值扁远。此外這兩個方法改變的只是瀏覽器關(guān)于當前頁面的標題和 URL 的記錄情況俊鱼,并不會刷新或改變頁面展示。

onpopstate 事件

window.onpopstate 是 popstate 事件在 window 對象上的事件句柄畅买。每當處于激活狀態(tài)的歷史記錄條目發(fā)生變化時并闲,popstate 事件就會在對應(yīng) window 對象上觸發(fā)。如果當前處于激活狀態(tài)的歷史記錄條目是由 history.pushState() 方法創(chuàng)建谷羞,或者由 history.replaceState() 方法修改過的帝火,則 popstate 事件對象的 state 屬性包含了這個歷史記錄條目的 state 對象的一個拷貝。

調(diào)用 history.pushState() 或者 history.replaceState() 不會觸發(fā) popstate 事件。popstate 事件只會在瀏覽器某些行為下觸發(fā)犀填,比如點擊后退蠢壹、前進按鈕 (或者在 JavaScript 中調(diào)用 history.back()、history.forward()宏浩、history.go() 方法)知残。

當網(wǎng)頁加載時,各瀏覽器對 popstate 事件是否觸發(fā)有不同的表現(xiàn)比庄,Chrome 和 Safari 會觸發(fā) popstate 事件求妹,而 Firefox 不會。

Hash 模式和 HTML 5 模式

Hash 模式

Hash 模式是基于錨點定位的內(nèi)部鏈接機制佳窑,在 URL 加上 # 制恍,然后在 # 后面加上 hash 標簽,根據(jù)不同的標簽做定位神凑。示例如下:

https://segmentfault.com/u/angular4#user

開啟 Hash 模式

導入 HashLocationStrategy 及 HashLocationStrategy

import { LocationStrategy, HashLocationStrategy } from '@angular/common';

配置 NgModule - providers

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot(routes)
  ],
  ...,
  providers: [
    { provide: LocationStrategy, useClass: HashLocationStrategy }
  ]
})

友情提示:URL 中包含的 hash 信息是不會提交到服務(wù)端净神,所以若要使用 SSR (Server-Side Rendered) ,就不能使用 Hash 模式即不能使用 HashLocationStrategy 策略溉委。

HTML 5 模式

HTML 5 模式則直接使用跟"真實"的 URL 一樣鹃唯,如上面的路徑,在 HTML 5 模式地址如下:

https://segmentfault.com/u/angular4/user

HTML 5 模式下 URL 有兩種訪問方式:

  • 在瀏覽器地址欄直接輸入 URL瓣喊,這會向服務(wù)器請求加載頁面坡慌。
  • 在 Angular 應(yīng)用程序中,訪問 HTML 5 模式下的 URL 地址藻三,這不需要重新加載頁面洪橘,可以直接切換到對應(yīng)的視圖。

在 HTML 5 模式下棵帽,Angular 使用了 HTML 5 的 pushState() API 來動態(tài)改變?yōu)g覽器的 URL 而不用重新刷新頁面熄求。

開啟 HTML 5 模式

導入 APP_BASE_HREF、LocationStrategy逗概、PathLocationStrategy

import { APP_BASE_HREF, LocationStrategy, PathLocationStrategy } from '@angular/common';

配置 NgModule - providers

@NgModule({
  imports: [
    BrowserModule,
    RouterModule.forRoot(routes)
  ],
  ..,
  providers: [
    { provide: LocationStrategy, useClass: PathLocationStrategy },
    { provide: APP_BASE_HREF, useValue: '/' }
  ]
})

示例代碼中的 APP_BASE_HREF弟晚,用于設(shè)置資源 (圖片、腳本逾苫、樣式) 加載的基礎(chǔ)路徑指巡。除了在 NgModule 中配置 provider 外,我們也可以在入口文件隶垮,如 index.html 文件 <base> 標簽中設(shè)置基礎(chǔ)路徑藻雪。

<base> 標簽為頁面上的所有鏈接規(guī)定默認地址或默認目標。通常情況下狸吞,瀏覽器會從當前文檔的 URL 中提取相應(yīng)的路徑來補全相對 URL 中缺失的部分勉耀。使用 <base> 標簽可以改變這一點指煎。瀏覽器隨后將不再使用當前文檔的 URL,而使用指定的基本 URL 來解析所有的相對 URL便斥。這其中包括<a>至壤、<img><link>枢纠、<form> 標簽中的 URL像街。具體使用示例如下:

<base href="/">

LocationStrategy

LocationStrategy 用于從瀏覽器 URL 中讀取路由狀態(tài)。Angular 中提供兩種 LocationStrategy 策略:

  • HashLocationStrategy
  • PathLocationStrategy

以上兩種策略都是繼承于 LocationStrategy 抽象類晋渺,該類的具體定義如下:

LocationStrategy 抽象類

export abstract class LocationStrategy {
  // 獲取path路徑
  abstract path(includeHash?: boolean): string;
  // 生成完整的外部鏈接
  abstract prepareExternalUrl(internal: string): string;
  // 添加會話歷史狀態(tài)
  abstract pushState(state: any, title: string, url: string, 
    queryParams: string): void;
  // 修改會話歷史狀態(tài)
  abstract replaceState(state: any, title: string, url: string, 
    queryParams: string): void;
  // 進入會話歷史記錄中的下一個頁面
  abstract forward(): void;
  // 返回會話歷史記錄中的上一個頁面
  abstract back(): void;
  // 設(shè)置popstate監(jiān)聽
  abstract onPopState(fn: LocationChangeListener): void;
  // 獲取base地址信息
  abstract getBaseHref(): string;
}

了解完 LocationStrategy 抽象類镰绎,接下來我們先來介紹 HashLocationStrategy 策略。

HashLocationStrategy

HashLocationStrategy 類繼承于 LocationStrategy 抽象類木西,它的構(gòu)造函數(shù)如下:

export class HashLocationStrategy extends LocationStrategy {
  constructor(
      private _platformLocation: PlatformLocation,
      @Optional() @Inject(APP_BASE_HREF) _baseHref?: string) {
      super();
      if (_baseHref != null) {
        this._baseHref = _baseHref;
      }
  }
}

該構(gòu)造函數(shù)依賴 PlatformLocation 及 APP_BASE_HREF 關(guān)聯(lián)的對象畴栖。APP_BASE_HREF 的作用,我們上面已經(jīng)介紹過了八千,接下來我們來分析一下 PlatformLocation 對象吗讶。

PlatformLocation

// angular2/packages/platform-browser/src/browser.ts
export const INTERNAL_BROWSER_PLATFORM_PROVIDERS: Provider[] = [
  ...,
  {provide: PlatformLocation, useClass: BrowserPlatformLocation},
];

通過以上代碼,我們可以知道在瀏覽器環(huán)境中恋捆,HashLocationStrategy 構(gòu)造函數(shù)中注入的 PlatformLocation 對象是 BrowserPlatformLocation 類的實例照皆。我們也先來看一下 BrowserPlatformLocation 類的構(gòu)造函數(shù):

// angular2/packages/platform-browser/src/browser/location/browser_platform_location.ts
export class BrowserPlatformLocation extends PlatformLocation {
  private _location: Location;
  private _history: History;

  constructor(@Inject(DOCUMENT) private _doc: any) {
    super();
    this._init();
  }

  _init() {
    this._location = getDOM().getLocation(); // 獲取瀏覽器平臺下Location對象
    this._history = getDOM().getHistory(); // 獲取瀏覽器平臺下的History對象
  }
}

在 BrowserPlatformLocation 構(gòu)造函數(shù)中,我們調(diào)用 _init() 方法沸停,在方法體中纵寝,我們調(diào)用 getDOM() 方法返回對象中的 getLocation()getHistory() 方法,分別獲取 Location 對象和 History 對象星立。那 getDOM() 方法返回的是什么對象呢?其實該方法返回的是 DomAdapter 對象葬凳。

DomAdapter

let _DOM: DomAdapter = null !;

export function getDOM() {
  return _DOM;
}

export function setDOM(adapter: DomAdapter) {
  _DOM = adapter;
}

export function setRootDomAdapter(adapter: DomAdapter) {
  if (!_DOM) {
    _DOM = adapter;
  }
}

那什么時候會調(diào)用 setDOM()setRootDomAdapter() 方法呢绰垂?通過查看 Angular 源碼,我們發(fā)現(xiàn)在瀏覽器平臺初始化時火焰,會調(diào)用 setRootDomAdapter() 方法劲装。具體如下:

export const INTERNAL_BROWSER_PLATFORM_PROVIDERS: Provider[] = [
  {provide: PLATFORM_INITIALIZER, useValue: initDomAdapter, multi: true},
  ...
];

initDomAdapter() 方法

export function initDomAdapter() {
  BrowserDomAdapter.makeCurrent();
  BrowserGetTestability.init();
}

從上面代碼中,可以看出在 initDomAdapter() 方法中昌简,我們又調(diào)用了 BrowserDomAdapter 類提供的靜態(tài)方法 makeCurrent() 占业,該方法的實現(xiàn)如下:

export class BrowserDomAdapter extends GenericBrowserDomAdapter {
    static makeCurrent() { setRootDomAdapter(new BrowserDomAdapter()); }
}

現(xiàn)在我們已經(jīng)知道調(diào)用 getDom() 方法后,我們獲得的是 BrowserDomAdapter 對象纯赎。該對象為我們提供 getLocation()getHistory() 方法谦疾,用于獲取 Location 和 History 對象。以上兩個方法的具體實現(xiàn)如下:

getHistory(): History { return window.history; }
getLocation(): Location { return window.location; }

此外該對象中還包含一個 getBaseHref() 方法犬金,用于獲取基礎(chǔ)路徑:

getBaseHref(doc: Document): string|null {
    const href = getBaseElementHref();
    return href == null ? null : relativePath(href);
}

// 獲取入口文件中base元素的href屬性值
function getBaseElementHref(): string|null {
  if (!baseElement) {
    baseElement = document.querySelector('base') !;
    if (!baseElement) {
      return null;
    }
  }
  return baseElement.getAttribute('href');
}

分析完 BrowserPlatformLocation 類的構(gòu)造函數(shù)念恍,我們再來分析該類中幾個重要的方法:

getBaseHrefFromDOM()

// 用于獲取base元素的href屬性
getBaseHrefFromDOM(): string { return getDOM().getBaseHref(this._doc) !; }

onPopState()

// 設(shè)置popstate事件的監(jiān)聽函數(shù)
onPopState(fn: LocationChangeListener): void {
    getDOM().getGlobalEventTarget(this._doc, 'window')
      .addEventListener('popstate', fn, false);
}

interface LocationChangeListener { (e: LocationChangeEvent): any; }
interface LocationChangeEvent { type: string; }

onHashChange()

// 設(shè)置hashchange事件的監(jiān)聽函數(shù)
onHashChange(fn: LocationChangeListener): void {
    getDOM().getGlobalEventTarget(this._doc, 'window')
      .addEventListener('hashchange', fn, false);
}

pushState()

// 添加會話歷史狀態(tài)
pushState(state: any, title: string, url: string): void {
    if (supportsState()) {
      this._history.pushState(state, title, url);
    } else {
      this._location.hash = url;
    }
}

// 判斷是否支持state相關(guān)API
export function supportsState(): boolean {
  return !!window.history.pushState;
}

replaceState()

// 修改會話歷史狀態(tài)
replaceState(state: any, title: string, url: string): void {
    if (supportsState()) {
      this._history.replaceState(state, title, url);
    } else {
      this._location.hash = url;
    }
}

forward()

// 進入會話歷史記錄中的下一個頁面
forward(): void { this._history.forward(); }

back()

// 進入會話歷史記錄中的上一個頁面
back(): void { this._history.back(); }

現(xiàn)在終于介紹完 PlatformLocation 對象六剥,讓我們回過頭來繼續(xù)分析我們的主角 - HashLocationStrategy 類。前面我們已經(jīng)分析了該類的構(gòu)造函數(shù)峰伙,我們再來看一下該類其它的方法:

// angular2/packages/common/src/location/hash_location_strategy.ts
export class HashLocationStrategy extends LocationStrategy {
  private _baseHref: string = ''; // 用于保存base URL地址

  onPopState(fn: LocationChangeListener): void {
    this._platformLocation.onPopState(fn);
    this._platformLocation.onHashChange(fn);
  }

  // 獲取基礎(chǔ)路徑
  getBaseHref(): string { return this._baseHref; }
  
  // 獲取hash路徑
  path(includeHash: boolean = false): string {
    // the hash value is always prefixed with a `#`
    // and if it is empty then it will stay empty
    let path = this._platformLocation.hash;
    if (path == null) path = '#';

    return path.length > 0 ? path.substring(1) : path;
  }

  // 基于_baseHref及internal值疗疟,生成完整的URL地址
  prepareExternalUrl(internal: string): string {
    // joinWithSlash():該方法會判斷_baseHref和internal是否含有'/'
    // 字符,然后自動幫我們拼接成合法的URL地址
    const url = Location.joinWithSlash(this._baseHref, internal);
    return url.length > 0 ? ('#' + url) : url;
  }

  // 添加會話歷史狀態(tài)
  pushState(state: any, title: string, path: string, queryParams: string) {
    // normalizeQueryParams():該方法會判斷queryParams是否包含'?'
    // 字符瞳氓,若不包含策彤,則自動添加'?'字符。
    let url: string|null = this.prepareExternalUrl(path +
          Location.normalizeQueryParams(queryParams));
    if (url.length == 0) {
      url = this._platformLocation.pathname;
    }
    this._platformLocation.pushState(state, title, url);
  }

  // 更新會話歷史狀態(tài)
  replaceState(state: any, title: string, path: string, queryParams: string) {
    let url = this.prepareExternalUrl(path + 
          Location.normalizeQueryParams(queryParams));
    if (url.length == 0) {
      url = this._platformLocation.pathname;
    }
    this._platformLocation.replaceState(state, title, url);
  }

  // 進入會話歷史記錄中的下一個頁面
  forward(): void { this._platformLocation.forward(); }

  // 進入會話歷史記錄中的上一個頁面
  back(): void { this._platformLocation.back(); }  
}

到現(xiàn)在為止匣摘,我們已經(jīng)完整分析了 HashLocationStrategy 策略店诗。最后我們來分析 PathLocationStrategy 策略。

PathLocationStrategy

PathLocationStrategy 類也是繼承于 LocationStrategy 抽象類恋沃,如果使用該策略必搞,我們必須設(shè)置 APP_BASE_HREF 或在入口文件如 (index.html) 文件中設(shè)置 <base> 元素的 href 屬性。我們也先來分析該類的構(gòu)造函數(shù):

// angular2/packages/common/src/location/path_location_strategy.ts
export class PathLocationStrategy extends LocationStrategy {
  private _baseHref: string;

  constructor(
      private _platformLocation: PlatformLocation,
      @Optional() @Inject(APP_BASE_HREF) href?: string) {
        super(); 
        if (href == null) {
          // 若未設(shè)置APP_BASE_HREF的值囊咏,則從base元素中
          href = this._platformLocation.getBaseHrefFromDOM();
        }
         
        // 若發(fā)現(xiàn)未設(shè)置基礎(chǔ)路徑恕洲,則會拋出異常∶犯睿可能有一些初學者霜第,會遇到這個問題
        if (href == null) {
          throw new Error(
              `No base href set. Please provide a value for the APP_BASE_HREF 
                 token or add a base element to the document.`);
        }
        this._baseHref = href;
  }
}

PathLocationStrategy 類其它的方法:

export class PathLocationStrategy extends LocationStrategy {
  // ...
  onPopState(fn: LocationChangeListener): void {
    this._platformLocation.onPopState(fn);
    this._platformLocation.onHashChange(fn);
  }

  // 獲取基礎(chǔ)路徑
  getBaseHref(): string { return this._baseHref; }

  // 基于_baseHref及internal值,生成完整的URL地址
  prepareExternalUrl(internal: string): string {
    return Location.joinWithSlash(this._baseHref, internal);
  }

  // 根據(jù)傳遞的參數(shù)值户辞,返回path(包含或不包含hash值)的路徑
  path(includeHash: boolean = false): string {
    const pathname = this._platformLocation.pathname +
        Location.normalizeQueryParams(this._platformLocation.search);
    const hash = this._platformLocation.hash;
    return hash && includeHash ? `${pathname}${hash}` : pathname;
  }

  // 添加會話歷史狀態(tài)
  pushState(state: any, title: string, url: string, queryParams: string) {
    // normalizeQueryParams():該方法會判斷queryParams是否包含'?'
    // 字符泌类,若不包含,則自動添加'?'字符底燎。
    const externalUrl = this.prepareExternalUrl(url + 
      Location.normalizeQueryParams(queryParams));
    this._platformLocation.pushState(state, title, externalUrl);
  }

  // 更新會話歷史狀態(tài)
  replaceState(state: any, title: string, url: string, queryParams: string) {
    const externalUrl = this.prepareExternalUrl(url +
       Location.normalizeQueryParams(queryParams));
    this._platformLocation.replaceState(state, title, externalUrl);
  }

  // 進入會話歷史記錄中的下一個頁面
  forward(): void { this._platformLocation.forward(); }

  // 進入會話歷史記錄中的上一個頁面
  back(): void { this._platformLocation.back(); }
}

終于介紹完 HashLocationStrategy 和 PathLocationStrategy 策略刃榨,后續(xù)的文章,我們會基于該基礎(chǔ)双仍,深入分析 Angular 的路由模塊枢希。

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市朱沃,隨后出現(xiàn)的幾起案子苞轿,更是在濱河造成了極大的恐慌,老刑警劉巖逗物,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搬卒,死亡現(xiàn)場離奇詭異,居然都是意外死亡翎卓,警方通過查閱死者的電腦和手機契邀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來失暴,“玉大人蹂安,你說我怎么就攤上這事椭迎。” “怎么了田盈?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵畜号,是天一觀的道長。 經(jīng)常有香客問我允瞧,道長简软,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任述暂,我火速辦了婚禮痹升,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘畦韭。我一直安慰自己疼蛾,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布艺配。 她就那樣靜靜地躺著察郁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪转唉。 梳的紋絲不亂的頭發(fā)上皮钠,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音赠法,去河邊找鬼麦轰。 笑死,一個胖子當著我的面吹牛砖织,可吹牛的內(nèi)容都是我干的款侵。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼侧纯,長吁一口氣:“原來是場噩夢啊……” “哼新锈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起茂蚓,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎剃幌,沒想到半個月后聋涨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡负乡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年牍白,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抖棘。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡茂腥,死狀恐怖狸涌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情最岗,我是刑警寧澤帕胆,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站般渡,受9級特大地震影響懒豹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驯用,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一脸秽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蝴乔,春花似錦记餐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至铝穷,卻和暖如春钠怯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背曙聂。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工晦炊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宁脊。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓断国,卻偏偏與公主長得像,于是被迫代替她去往敵國和親榆苞。 傳聞我的和親對象是個殘疾皇子稳衬,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 點擊查看原文 Web SDK 開發(fā)手冊 SDK 概述 網(wǎng)易云信 SDK 為 Web 應(yīng)用提供一個完善的 IM 系統(tǒng)...
    layjoy閱讀 13,767評論 0 15
  • 新增的API 1.語義: 能夠讓你更恰當?shù)孛枋瞿愕膬?nèi)容是什么。 2.連通性: 能夠讓你和服務(wù)器之間通過創(chuàng)新的新技術(shù)...
    紅鯉魚不理綠鯉魚閱讀 6,897評論 0 5
  • 本文由尚妝前端開發(fā)工程師欲休撰寫本文發(fā)表于尚妝博客坐漏,歡迎訂閱薄疚! 移動端開發(fā)在某些場景中有著特殊需求,如為了提高用戶...
    尚妝產(chǎn)品技術(shù)刊讀閱讀 1,889評論 0 11
  • 雨像根根晶亮的銀線赊琳,從天穹撒向人間街夭。可是躏筏,對于走在路上沒有打傘的林夕來說可是根根锃亮的銀針板丽,不禁令她想到容嬤...
    曹文和閱讀 358評論 0 0
  • 2017年4月28日 相濡以沫 1、昨天背著小白出門趁尼,一無所獲埃碱,今天依然負重出門猖辫,希望不負重望!很想不通為啥它叫小...
    滋滋味味閱讀 266評論 2 2