基于element-plus button 源碼分析造輪子

前言

實(shí)現(xiàn)組件 button 新增功能和自定義UI換膚衷畦,使用 SCSS 變量和 CSS 自定義屬性弊予,參考 element-plus 源碼造輪子

button 組件

element-plus 的 button 文件
/packages/components/button/src/button.vue 和 element-ui 實(shí)現(xiàn)邏輯是相似的亿汞,不同地方在于生成 bem 規(guī)范實(shí)現(xiàn)方式不一樣胯杭,前者通過(guò)函數(shù)創(chuàng)建命名空間對(duì)象,然后調(diào)用 b()凝赛、e()袍冷、m()栈戳、is()等函數(shù)返回符合 bem 規(guī)范的類,后者通過(guò)字符串拼接生成

腳本函數(shù)創(chuàng)建命名空間對(duì)象

  • 優(yōu)點(diǎn):可讀性強(qiáng)难裆,減少模版編寫,方便維護(hù)管理镊掖,可以動(dòng)態(tài)的更改命名空間前綴
  • 缺點(diǎn):每個(gè)組件創(chuàng)建命名空間對(duì)象乃戈,占用額外內(nèi)存
// 參考element-plus button 實(shí)現(xiàn)
<template>
  <button
    :class="[
      ns.b(), // el-button
      ns.m(type), // 傳入 type 生成 el-button--primary/success/info 等
      ns.m(buttonSize), // 傳入 size 得到 el-button--large/small
      ns.is('disabled', buttonDisabled), // is-disabled
      ns.is('loading', loading), // is-loading
      ns.is('plain', plain), // is-plain
      ns.is('ghost', ghost), // is-ghost
      ns.is('round', round), // is-round
      ns.is('circle', circle), // is-circle
      ns.is('text', text), // is-text
      ns.is('link', link), // is-link
    ]"
    @click="handleClick"
    :disabled="buttonDisabled || loading" // 禁用
    :autofocus="autofocus"
    :type="nativeType"
  >
    <template v-if="loading"> // 加載圖標(biāo)
      <slot v-if="$slots.loading" name="loading"></slot>
      <i v-else class="el-icon-loading"></i>
    </template>
    // 自定義圖標(biāo)
    <template v-else-if="icon || $slots.icon">
      <span v-if="$slots.icon"><slot name="icon"></slot></span>
      <i v-else :class="icon"></i>
    </template>
    <span v-if="$slots.default"><slot></slot></span>
  </button>
</template>

<script>

const { useNamespace } from '@element-plus/hooks'
// 創(chuàng)建 button 命名空間
const ns = useNamespace('button')
</script>

useNamespace 從全局獲取命名空間,我這里沒有用亩进,直接使用默認(rèn)的命名空間症虑,例如 el,然后調(diào)用不同的函數(shù)归薛,根據(jù)傳入?yún)?shù)判斷拼接字符串返回

export const defaultNamespace = 'el'
const statePrefix = 'is-'

/**
 * 生成 bem
 * @param {} namespace 命名空間
 * @param {*} block 塊
 * @param {*} blockSuffix 塊多個(gè)單詞
 * @param {*} element 元素
 * @param {*} modifier 修飾符
 * @returns
 */
const _bem = (namespace, block, blockSuffix, element, modifier) => {
  let cls = `${namespace}-${block}` // el-button
  if (blockSuffix) {
    cls += `-${blockSuffix}`
  }
  if (element) {
    cls += `__${element}`
  }
  if (modifier) {
    cls += `--${modifier}`
  }
  return cls
}

export const useNamespace = (block) => {
  // 默認(rèn)命名空間
  const namespace = defaultNamespace
  // b() => el-button
  const b = (blockSuffix = '') => _bem(namespace, block, blockSuffix, '', '')
  // e(primary) => el-button__primary
  const e = (element) => element ? _bem(namespace, block, '', element, '') : ''
  // m(primary) => el-button--primary
  const m = (modifier) => modifier ? _bem(namespace, block, '', '', modifier) : ''

  const be = (blockSuffix, element) => blockSuffix && element
    ? _bem(namespace, block, blockSuffix, element, '')
    : ''

  const em = (element, modifier) => element && modifier
    ? _bem(namespace, block, '', element, modifier)
    : ''

  const bm = (blockSuffix, modifier) => blockSuffix && modifier
    ? _bem(namespace, block, blockSuffix, '', modifier)
    : ''

  const bem = (blockSuffix, element, modifier) => blockSuffix && element && modifier
    ? _bem(namespace, block, blockSuffix, element, modifier)
    : ''
  // is(disabled) => is-disabled
  const is = (name, ...args) => {
    const state = args.length >= 1 ? args[0] : true
    return name && state ? `${statePrefix}${name}` : ''
  }
  
  return {
    b,
    e,
    m,
    be,
    em,
    bm,
    bem,
    is,
  }
}

bem 規(guī)范腳本生成的方式靈活谍憔,單獨(dú)維護(hù)不嵌入代碼,如果要替換組件庫(kù)的前綴命名空間主籍,只需要在全局配置傳入替換就行

公共樣式 scss 變量

element-plus scss 文件結(jié)構(gòu)和 element-ui 差不多习贫,區(qū)別在于使用 Dart Sasssass:map...@use 重構(gòu)所有的 SCSS 變量,解決 @import 造成的重復(fù)輸出問(wèn)題千元,SASS 使用可以看下之前整理的這篇文章

scss 樣式變量定義在 packages/theme-chalk/src/common/var.scss 苫昌,例如主題顏色、字體顏色幸海、邊框顏色祟身、背景顏色、字體大小物独、組件樣式變量等

下面是部分代碼袜硫,$types 定義 6 種主要類型,是列表數(shù)組類型挡篓;$colors: () !default; 初始化 $colors 變量婉陷,map.deep-merge() 是調(diào)用 sass:map 函數(shù)深度合并,然后通過(guò) map.get 取值官研,獲取 map 多層嵌套值憨攒,傳入多個(gè)參數(shù),逗號(hào)隔開 map.get($colors, 'primary', 'base')

注意$color-primary 不以下劃線或橫桿開頭聲明 $-color-primary阀参,是因?yàn)闄M杠開頭聲明為私有變量肝集, @use 是沒辦法在外部引入使用

@use 'sass:map';

// types
$types: primary, success, warning, danger, error, info;

// Color
$colors: () !default;
$colors: map.deep-merge(
  (
    'white': #ffffff,
    'black': #000000,
    'primary': (
      'base': #409eff,
    ),
    'success': (
      'base': #67c23a,
    ),
    'warning': (
      'base': #e6a23c,
    ),
    'danger': (
      'base': #f56c6c,
    ),
    'error': (
      'base': #f56c6c,
    ),
    'info': (
      'base': #909399,
    ),
  ),
  $colors
);

$color-white: map.get($colors, 'white') !default;
$color-black: map.get($colors, 'black') !default;
$color-primary: map.get($colors, 'primary', 'base') !default;
$color-success: map.get($colors, 'success', 'base') !default;
$color-warning: map.get($colors, 'warning', 'base') !default;
$color-danger: map.get($colors, 'danger', 'base') !default;
$color-error: map.get($colors, 'error', 'base') !default;
$color-info: map.get($colors, 'info', 'base') !default;

@each 遍歷 $typs,調(diào)用 set-color-mix-level 函數(shù)蛛壳,使用 mix(color1, color2, percent) 進(jìn)行顏色混合杏瞻,它接收三個(gè)參數(shù)所刀,前面兩個(gè)參數(shù)是兩種混合的顏色, $mix-color 默認(rèn)是白色,map.get($colors, $type, 'base') 獲取 type 類型 base 顏色捞挥,第三個(gè)參數(shù)是兩個(gè)混合顏色的百分占比浮创,例如 0.1 表示第一個(gè)參數(shù)顏色占比 10%,第二個(gè)顏色 90%砌函;dark-2 值是混合黑色的顏色

// https://sass-lang.com/documentation/values/maps#immutability
// mix colors with white/black to generate light/dark level
@mixin set-color-mix-level(
  $type,
  $number,
  $mode: 'light',
  $mix-color: $color-white
) {
  $colors: map.deep-merge(
    (
      $type: (
        '#{$mode}-#{$number}':
          mix(
            $mix-color,
            map.get($colors, $type, 'base'),
            math.percentage(math.div($number, 10))
          ),
      ),
    ),
    $colors
  ) !global;
}

// $colors.primary.light-i
// --el-color-primary-light-i
// 10% 53a8ff
// 20% 66b1ff
// 30% 79bbff
// 40% 8cc5ff
// 50% a0cfff
// 60% b3d8ff
// 70% c6e2ff
// 80% d9ecff
// 90% ecf5ff
@each $type in $types {
  @for $i from 1 through 9 {
    @include set-color-mix-level($type, $i, 'light', $color-white);
  }
}

// --el-color-primary-dark-2
@each $type in $types {
  @include set-color-mix-level($type, 2, 'dark', $color-black);
}

遍歷混合后斩披,打印 $colors 顏色值

(
info: ("dark-2": #73767a, "light-9": #f4f4f5, "light-8": #e9e9eb, "light-7": #dedfe0, "light-6": #d3d4d6, "light-5": #c8c9cc, "light-4": #bcbec2, "light-3": #b1b3b8, "light-2": #a6a9ad, "light-1": #9b9ea3, "base": #909399), 
error: ("dark-2": #cc3c2d, "light-9": #ffedeb, "light-8": #ffdbd7, "light-7": #ffc9c3, "light-6": #ffb7af, "light-5": #ffa59c, "light-4": #ff9388, "light-3": #ff8174, "light-2": #ff6f60, "light-1": #ff5d4c, "base": #FF4B38), 
danger: ("dark-2": #cc3c2d, "light-9": #ffedeb, "light-8": #ffdbd7, "light-7": #ffc9c3, "light-6": #ffb7af, "light-5": #ffa59c, "light-4": #ff9388, "light-3": #ff8174, "light-2": #ff6f60, "light-1": #ff5d4c, "base": #FF4B38), 
warning: ("dark-2": #cc7a00, "light-9": #fff5e6, "light-8": #ffebcc, "light-7": #ffe0b3, "light-6": #ffd699, "light-5": #ffcc80, "light-4": #ffc266, "light-3": #ffb84d, "light-2": #ffad33, "light-1": #ffa31a, "base": #FF9900), 
success: ("dark-2": #309e70, "light-9": #ecf9f4, "light-8": #d8f3e8, "light-7": #c5eedd, "light-6": #b1e8d1, "light-5": #9ee2c6, "light-4": #8adcba, "light-3": #77d6af, "light-2": #63d1a3, "light-1": #50cb98, "base": #3CC58C), 
primary: ("dark-2": #337ecc, "light-9": #ecf5ff, "light-8": #d9ecff, "light-7": #c6e2ff, "light-6": #b3d8ff, "light-5": #a0cfff, "light-4": #8cc5ff, "light-3": #79bbff, "light-2": #66b1ff, "light-1": #53a8ff, "base": #409eff), 
"white": #ffffff, 
"black": #000000)

除了定義常用的字體顏色、邊框顏色等變量外讹俊,所有的組件變量也定義在這個(gè)文件垦沉,例如 checkbox 復(fù)選框

// Components
// ---
// Checkbox
// css3 var in packages/theme-chalk/src/checkbox.scss
$checkbox: () !default;
$checkbox: map.merge(
  (
    'font-size': 14px,
    'font-weight': getCssVar('font-weight-primary'),
    'text-color': getCssVar('text-color-regular'),
    'input-height': 14px,
    'input-width': 14px,
    'border-radius': getCssVar('border-radius-small'),
    'bg-color': getCssVar('fill-color', 'blank'),
    'input-border': getCssVar('border'),
    'disabled-border-color': getCssVar('border-color'),
    'disabled-input-fill': getCssVar('fill-color', 'light'),
    'disabled-icon-color': getCssVar('text-color-placeholder'),
    'disabled-checked-input-fill': getCssVar('border-color-extra-light'),
    'disabled-checked-input-border-color': getCssVar('border-color'),
    'disabled-checked-icon-color': getCssVar('text-color-placeholder'),
    'checked-text-color': getCssVar('color-primary'),
    'checked-input-border-color': getCssVar('color-primary'),
    'checked-bg-color': getCssVar('color-primary'),
    'checked-icon-color': getCssVar('color', 'white'),
    'input-border-color-hover': getCssVar('color-primary'),
  ),
  $checkbox
);

上面定義變量值有使用 getCssVar() 函數(shù),它是應(yīng)用 css 自定義屬性仍劈,接下來(lái)介紹它

兩種 css 自定義變量

CSS 自定義屬性(變量) 設(shè)定標(biāo)記值(比如: --main-color: black;)厕倍,由 var() 函數(shù)來(lái)獲取值(比如: color: var(--main-color);

:root {
  --main-bg-color: brown;
}

局部變量時(shí)用 var() 函數(shù)包裹以表示一個(gè)合法的屬性值,var() 如果第一個(gè)參數(shù)不生效贩疙,可以接受第二個(gè)參數(shù)默認(rèn)值

注意:自定義屬性名是大小寫敏感的讹弯,--my-color--My-color 會(huì)被認(rèn)為是兩個(gè)不同的自定義屬性。

element {
  background-color: var(--main-bg-color);
}

通過(guò) JavaScript 操作 var 變量值

// 獲取一個(gè) Dom 節(jié)點(diǎn)上的 CSS 變量
element.style.getPropertyValue("--my-var");

// 獲取任意 Dom 節(jié)點(diǎn)上的 CSS 變量
getComputedStyle(element).getPropertyValue("--my-var");

// 修改一個(gè) Dom 節(jié)點(diǎn)上的 CSS 變量
element.style.setProperty("--my-var", 'red');

element-plus 有兩種 css 自定義屬性:全局 root 和局部組件

全局 css 變量

全局的 css 變量定義在 packages/theme-chalk/src/var.scss这溅,它被引入 theme-chalk/src/base.scs 文件组民,base.scss 分別引入到了 /theme-chalk/src/index.scsspackages/components/base/style/css.ts

如果全量注冊(cè)組件,引入 index.scss 打包編譯后的樣式悲靴;如果是按需注冊(cè)組件邪乍,從組件的 style 目錄下引入 css 文件,其中加入了 base/style/css.ts对竣,例如 button

import '@element-plus/components/base/style/css';
import '@element-plus/theme-chalk/el-button.css';

element-plus 全局css變量 定義兩個(gè) root庇楞, 通用和 light 主題

// common
:root {
  @include set-css-var-value('color-white', $color-white);
  @include set-css-var-value('color-black', $color-black);

  // get rgb
  @each $type in (primary, success, warning, danger, error, info) {
    @include set-css-color-rgb($type);
  }

  // Typography
  @include set-component-css-var('font-size', $font-size);
  ...
}

// for light
:root {
  color-scheme: light;

  @include set-css-var-value('color-white', $color-white);
  @include set-css-var-value('color-black', $color-black);

  // --el-color-#{$type}
  // --el-color-#{$type}-light-{$i}
  @each $type in (primary, success, warning, danger, error, info) {
    @include set-css-color-type($colors, $type);
  }
  ...
}

css 變量生成的函數(shù)定義在 packages/theme-chalk/src/mixins/_var.scss

例如 set-css-var-value('color-white', $color-white), 調(diào)用 joinVarName 得到 --el-color-white,最后結(jié)果是 --el-color-white: #fff;

@mixin set-css-var-value($name, $value) {
  #{joinVarName($name)}: #{$value};
}

theme-chalk/src/mixins/function.scss#L47-L55

@function joinVarName($list) {
  $name: '--' + config.$namespace;
  @each $item in $list {
    @if $item != '' {
      $name: $name + '-' + $item;
    }
  }
  @return $name;
}

全局 css 變量執(zhí)行結(jié)果如下


局部組件css變量

button.scss 會(huì)在前面執(zhí)行下面這段代碼生成 組件局部 css 自定義變量

@include b(button) {
  @include set-component-css-var('button', $button);
}

$button 組件變量是定義在 common/var.scss

// Button
// css3 var in packages/theme-chalk/src/button.scss
$button: () !default;
$button: map.merge(
  (
    'font-weight': getCssVar('font-weight-primary'),
    'border-color': getCssVar('border-color'),
    'bg-color': getCssVar('fill-color', 'blank'),
    'text-color': getCssVar('text-color', 'regular'),
    'disabled-text-color': getCssVar('disabled-text-color'),
    'disabled-bg-color': getCssVar('fill-color', 'blank'),
    'disabled-border-color': getCssVar('border-color-light'),
    'divide-border-color': rgba($color-white, 0.5),
    'hover-text-color': getCssVar('color-primary'),
    'hover-bg-color': getCssVar('color-primary', 'light-9'),
    'hover-border-color': getCssVar('color-primary-light-7'),
    'active-text-color': getCssVar('button-hover-text-color'),
    'active-border-color': getCssVar('color-primary'),
    'active-bg-color': getCssVar('button', 'hover-bg-color'),
    'outline-color': getCssVar('color-primary', 'light-5'),
    'hover-link-text-color': getCssVar('color-info'),
    'active-color': getCssVar('text-color', 'primary'),
  ),
  $button
);

set-component-css-var 遍歷 $button否纬,然后拼接 css 變量名和值

@mixin set-component-css-var($name, $variables) {
  @each $attribute, $value in $variables {
    @if $attribute == 'default' {
      #{getCssVarName($name)}: #{$value};
    } @else {
      #{getCssVarName($name, $attribute)}: #{$value};
    }
  }
}

生成 button 組件的css局部變量

設(shè)置相同的 name --name 可以覆蓋 root 變量值

button.scss 源碼分析

button.scss 樣式文件結(jié)構(gòu)和 element-ui 差別不大吕晌,可以閱讀 element-ui 組件庫(kù) button 源碼分析

分析一下差異點(diǎn)

  1. 使用 getCssVar() 設(shè)置 css 變量值,例如 getCssVar('button', 'bg-color'); 生成 var(--el-button-bg-color临燃,它使用的組件局部 css 變量睛驳,局部又是繼承全局的 --el-bg-color

這樣做的好處是如果要更改 button 的背景,只需要修改 --el-button-bg-color 值膜廊,這樣就不會(huì)影響到全局的背景顏色 --el-bg-color

  1. 之前生成 primary, success, warning, danger, info 6種類型的按鈕分別調(diào)用 button-variant乏沸,現(xiàn)在使用 Sass 重構(gòu)后直接 @each 遍歷就行
@each $type in (primary, success, warning, danger, info) {
    @include m($type) {
      @include button-variant($type);
    }
  }
  1. _button.scss 文件的 button-variant 懸浮、激活爪瓜、禁用等狀態(tài)不再直接編寫代碼蹬跃,而是定義好各個(gè)狀態(tài)的數(shù)據(jù)結(jié)構(gòu),然后遍歷修改 background铆铆、color蝶缀、border-color css變量值
@mixin button-variant($type) {
  $button-color-types: (
    '': (
      'text-color': (
        'color',
        'white',
      ),
      'bg-color': (
        'color',
        $type,
      ),
      'border-color': (
        'color',
        $type,
      ),
      'outline-color': (
        'color',
        $type,
        'light-5',
      ),
      'active-color': (
        'color',
        $type,
        'dark-2',
      ),
    ),
    'hover': (
      'text-color': (
        'color',
        'white',
      ),
      'link-text-color': (
        'color',
        $type,
        'light-5',
      ),
      'bg-color': (
        'color',
        $type,
        'light-3',
      ),
      'border-color': (
        'color',
        $type,
        'light-3',
      ),
    ),
    'active': (
      'bg-color': (
        'color',
        $type,
        'dark-2',
      ),
      'border-color': (
        'color',
        $type,
        'dark-2',
      ),
    ),
    'disabled': (
      'text-color': (
        'color',
        'white',
      ),
      'bg-color': (
        'color',
        $type,
        'light-5',
      ),
      'border-color': (
        'color',
        $type,
        'light-5',
      ),
    ),
  );

  @each $type, $typeMap in $button-color-types {
    @each $typeColor, $list in $typeMap {
      @include css-var-from-global(('button', $type, $typeColor), $list);
    }
  }

  &.is-plain,
  &.is-text,
  &.is-link {
    @include button-plain($type);
  }
}

以上是 element-plus button 源碼分析丹喻,造輪子后的演示地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市翁都,隨后出現(xiàn)的幾起案子碍论,更是在濱河造成了極大的恐慌,老刑警劉巖柄慰,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鳍悠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡坐搔,警方通過(guò)查閱死者的電腦和手機(jī)藏研,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)薯蝎,“玉大人,你說(shuō)我怎么就攤上這事谤绳≌季猓” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵缩筛,是天一觀的道長(zhǎng)消略。 經(jīng)常有香客問(wèn)我,道長(zhǎng)瞎抛,這世上最難降的妖魔是什么艺演? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮桐臊,結(jié)果婚禮上胎撤,老公的妹妹穿的比我還像新娘。我一直安慰自己断凶,他們只是感情好伤提,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著认烁,像睡著了一般肿男。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上却嗡,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天舶沛,我揣著相機(jī)與錄音,去河邊找鬼窗价。 笑死如庭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撼港。 我是一名探鬼主播柱彻,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼豪娜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了哟楷?” 一聲冷哼從身側(cè)響起瘤载,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎卖擅,沒想到半個(gè)月后鸣奔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惩阶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年挎狸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片断楷。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锨匆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冬筒,到底是詐尸還是另有隱情恐锣,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布舞痰,位于F島的核電站土榴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏响牛。R本人自食惡果不足惜玷禽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望呀打。 院中可真熱鬧矢赁,春花似錦、人聲如沸贬丛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瘫寝。三九已至蜒蕾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間焕阿,已是汗流浹背咪啡。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留暮屡,地道東北人撤摸。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親准夷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钥飞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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