Note:Vue3

Vue3常见API:

  • reactive:
    reactive返回proxy对象,如果发现被访问的属性是引用类型,还会用reactive递归处理,属性是可以被修改的。
  • shallowReactive:
    shallowReactive返回proxy对象,和reactive的区别是只建立第一层为响应式,如果发现被访问的属性是引用类型也不会进行递归代理,属性是可以被修改的。
  • readonly:
    readonly返回proxy对象,如果发现被访问的属性是引用类型会进行递归处理,但是属性是只读的,不能修改。可以做props传递给子组件使用。
  • shallowReadonly:
    shallowReadonly返回proxy对象,shallowReadonly 仅对对象的第一次层属性进行只读处理,对于嵌套的对象或数组,它不会进行只读保护。

Vue3 新特性有哪些?

1、优化响应式系统:

  • 使用ES6的 Proxy配合Reflect 代替vue2的 Object.defineProperty ,不仅可以更高效地追踪依赖关系,还解决了 Vue 2 中响应式无法追踪数组索引和对象属性添加的问题。

2、更好的支持 typeScript:
Vue3在源码使用了 TypeScript,可以为开发者 提供更好的类型检查和类型推断。

3、新增 Composition API:

  • Composition API 提供了一种新的代码组织和复用方式,它允许开发者将逻辑拆分成独立的函数(称为“组合函数”),并在setupm中按需使用。这使得代码更加模块化,易于测试和维护,特别是在大型项目中。

4、新增组件:

  • Fragment: template 模板可以有多个根元素。
  • Teleport 传送门,允许Teleport中的内容传送到任意的 DOM 中。
  • Suspense 用于处理异步组件的加载状态,提供了更好的用户体验。

5、支持多个根节点

6、Tree-shaking:支持摇树优化:
Vue3.x中的核心API都支持tree-shaking,这些API都是通过包引入的方式而不是直接挂载在vue原型上,只会对使用到的功能或特性进行打包(按需打包),这意味着更多的功能和更小的体积。

7、diff算法的优化:

  1. vue2中虚拟dom进行全量对比
  2. Vue 3 的虚拟 DOM Diff 算法通过引入静态标记(PatchFlag)和静态提升等优化手段,显著提高了渲染性能。PatchFlag 允许 Vue 在比较新旧节点时跳过未发生变化的静态部分,只关注动态变化的部分。静态提升则通过复用未发生变化的节点,减少了不必要的节点创建和销毁操作。

8、移除了一些功能:

  • 移除v-on的键盘修饰符,鼓励开发者使用键盘事件(如 keyCode 或 key)来替代;
  • 移除过滤器filter。

Vue3性能提升主要是通过哪几方面体现的?

1、编译阶段优化

  • diff算法优化
    vue3在diff算法中相比vue2增加了静态标记Patchflag,在编译template模板时,给vnode添加的一个PatchFlag标识信息,这个标识信息可以反映哪些vnode具有动态绑定的属性或子节点,在与上次的节点进行对比时候,Vue会利用patchFlag来高效地识别出哪些是需要更新的节点,并仅对这些节点进行对比和更新操作。这样可以避免不必要的DOM操作,从而提升渲染性能。
  • 静态提升
    Vue3会对不参与更新的元素做静态提升,只会在初始化的时候创建一次,重新渲染时直接复用。在初始化的时候会对该静态元素打上特殊静态标记,值为-1,表示静态提升,(特殊标志是负整数表示永远不会用于 Diff),做了静态提升后,未参与更新的元素,被放置在render 函数外,每次渲染的时候直接复用即可。
    没做静态提升之前,未参与更新的元素也在render函数内部,会重复创建阶段。

详细:

  • Vue 2在对比较新旧节点时,会检查每个节点是否是静态的,如果是静态的会跳过这个节点及其子节点的比较。虽然会跳过静态节点的比较,但是Vue2并没有做静态提升,静态根节点还是在render函数内,所以这些静态根节点每次都会被重新创建渲染。
  • vue3在diff算法中相比vue2增加了静态标记Patchflag,在编译template模板时,给vnode添加的一个PatchFlag标识信息,这个标识信息可以反映哪些vnode具有动态绑定的属性或子节点,在与上次的节点进行对比时候,Vue会利用patchFlag来高效地识别出哪些是需要更新的节点,并仅对这些节点进行对比和更新操作。对不参与更新的元素做静态提升,只会在初始化的时候创建一次,重新渲染时直接复用。在初始化的时候会对该静态元素打上特殊静态标记,值为-1,表示静态提升,这样可以避免不必要的DOM操作,从而提升渲染性能。

2、源码体积优化

  • 相比Vue2,Vue3整体体积变小了,除了移出一些不常用的API,最重要的是优化了对 Tree shanking的支持。Vue3中的核心API都支持tree-shaking,这些API都需通过包引入的方式才能使用,只会对使用到的功能或特性进行打包(按需打包),这意味着更多的功能和更小的体积。

3、响应式系统优化

  • vue2主要采用 object.defineProperty来劫持对象属性,通过进行深度遍历所有属性,给每个属性添加getter和setter,实现响应式拦截。
  • vue3采用proxy重写了响应式系统,因为proxy是对整个对象进行监听的,所以无需递归遍历对象的属性。
  • 可以监听到属性的添加、删除操作。
  • 可以监听到数组的索引和数组length属性的操作。

4、事件优化

  • 在Vue2中,每次渲染时都会重新创建事件处理函数,即使是相同的事件处理逻辑。
  • 在Vue3中,引入了缓存事件处理函数的概念,它会将事件处理函数在编译阶段缓存起来,重新渲染的时候直接复用。
// 静态提升例子:
//  Vue2的静态节点
render(){
  return createVNode("div", null, "Hello World")
}

// Vue3的静态节点
const staticLifting = createVNode("div", null, "Hello World")
function render(){
  // 直接使用 staticLifting 即可
}
<button @click="i++">plus</button>
// vue2
render(ctx){
  return createVNode("button", {
    onClick: function($event){
      ctx.i++;
    }
  })
}

// vue3
render(ctx, _cache){
  return createVNode("button", {
    onClick: cache[0] || (cache[0] = ($event) => (ctx.i++))
  })
}

Vue3和Vue2的区别?

1. 响应式数据的监听方式不同:

  • Vue2 的响应式数据监听是采用ES5的Object.definePropert() 对数据进行劫持的;
  • Vue3 中使用ES6的Proxy()对数据进行代理劫持。
  • 相比Object.definePropert(),ES6的Proxy()有以下优点:
  • Proxy代理监听的是整个对象,不需要通过递归+遍历为对象属性设置getter和setter进行劫持,提高了性能;
  • Proxy可以监听到对象属性的动态添加和删除;
  • 可以监听到数组的索引和数组length属性的操作;
  • 如果对象的属性也是对象的话,只有在被访问的时候才会进行递归响应观测,提高了性能;

2. Vue3新增了组合式(Composition API):

  • Vue2是使用options API的,options API内部的逻辑点是碎片化的,一个功能逻辑会出现在不同的位置,通过定义methods,computed,watch,data等options API,共同处理页面逻辑。
  • Vue3新增了Composition API(组合API),同时仍然兼容Vue2中的Options API。组合式API,组件根据逻辑功能来组织代码的,一个功能所定义的所有 API 会放在一起,通过函数分割复用代码,将其封装成一个hooks,hooks需要在setup函数中使用。

3. 定义数据变量和方法不同:

  • vue2是在对应的选项中定义变量、方法、计算属性等。在data选项中定义响应式数据,在method中定义方法。
  • Vue3需要从vue中导出reactive、ref、computed等API,这些API需要在一个setup钩子函数中使用,通过使用reactive、ref来定义响应式数据。

4. template模板支持:

  • Vue3 template 支持多个根节点;
  • Vue2 template 只能支持单个根节点。

5. 生命周期函数:

  1. vue2的生命周期是选项式的,,主要生命周期函数是beforeCreate、created、beforeMount、Mounted、beforeUpdate、updated、beforeDestory、destroyed;
  2. vue3的生命周期是组合式的,需要通过包引入的方式才可以时使用;
  • vue3用setup钩子取代了beforeCreate和created钩子函数,setup函数在beforeCreate之前调用;
  • vue3将beforeDestory和destroyed这两个钩子函数的名称改为onBeforeUnmount和onUnmounted。

6. Vue实例的创建方式不同:

  • Vue2是通过new一个Vue实例来创建Vue实例对象的;
  • Vue3需要引入createApp方法,然后调用该方法创建Vue对象。
// Vue2
import Vue from 'vue';
import App from './App.vue'
new Vue({
  render: h => h(App)
}).$mount('#app');

// Vue3
import {createApp} from 'vue';
import App from './App.vue'
createApp(App).mount('#app')

Options Api与Composition Api的区别?

代码组织:
1、Options Api:选项式API内部的逻辑点是碎片化的,一个功能逻辑会出现在不同的位置,通过定义methods,computed,watch,data等选项API,共同处理页面逻辑。当组件变得复杂时,会降低代码的可读性和可维护性。
2、Composition API(组合式API),使用 ref、reactive、computed、watch 等函数来替代 Options API 中的对应选项。组件根据逻辑功能来组织代码,通过函数分割复用代码,一个功能所定义的所有 API 会放在一起,这使得代码更加集中和模块化,易于阅读和维护。( 更加的 高内聚,低耦合 )。

逻辑复用:

  • 在vue2.0中,是通过mixin来实现逻辑复用的,当使用多个mixin会存在两个非常明显的问题:命名冲突、数据来源不清晰
  • Composition Api是通过函数复用,通过将相同逻辑功能的代码封装成一个函数,这些代码片段可以包含状态、计算属性、方法等,可以在多个组件中重复使用,而不会引起命名冲突或数据来源不明确的问题。
    总结:
  • 在代码组织和逻辑复用方面,Composition API是优于Options API
  • 因为Composition API几乎是函数,会有更好的类型推断;
  • Composition API 对 tree-shaking 更友好,从而生成更小的包;
  • Composition API中见不到this的使用,减少了this指向不明的情况;
  • 如果是小型组件,可以继续使用Options API,也是十分友好的。
  • 更好的测试,由于 Composition API 中的逻辑更加模块化,它们也更易于进行单元测试。

Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?

1、vue2是采用 Object.defineProperty来监听对象的操作的,通过遍历+递归的方式给每个属性添加getter和setter进行劫持。
存在以下的问题:

  • 检测不到对象属性的添加和删除,Vue提供了set添加和delete删除,但是这需要手动调用,有额外的负担;
  • 监听不到通过数组索引和数组length属性的操作;
  • 在初始化阶段,需要对对象属性进行遍历监听,如果是嵌套对象,即使没有被访问到,也会被递归监听,当对象过深的时候会造成性能问题。

2、Proxy监听的是整个对象的操作:

  • Proxy 创建了一个对象的代理,这个代理可以拦截针对对象的任何操作(如读取、赋值、函数调用等),而不仅仅是属性访问。因此,它可以更全面地追踪对象的变化。
  • Proxy不需要预先知道要监听哪些属性,它可以动态地处理对象的属性添加和删除。
  • 可以监听到数组的索引和数组length属性的操作;
  • 对于嵌套对象,只有在被访问到的时候才会对其进行递归响应观测,这有助于提高性能;
  • Proxy提供了多达13种拦截方法,这些方法可以覆盖对象的各种操作,提供了更大的灵活性和控制力。

Proxy 只会代理对象的第一层,那么 Vue3 又是怎样处理这个问题的呢?

当访问到深层嵌套对象的话,会触发getter,会判断当前 Reflect.get 的返回值是否是已经代理过的对象,如果是一个普通对象则再使用 reactive 方法做代理, 这样就实现了深度观测。

说说Vue 3.0中Treeshaking特性?举例说明一下?

1、什么是Tree shaking?
Tree shaking 是一种通过清除多余代码方式来优化项目打包体积的技术,专业术语叫 (Dead code elimination)简单来讲,就是在保持代码运行结果不变的前提下,去除无用的代码
2、Tree shaking原理?
Tree shaking是基于ES6模块化方案 (import与exports),模块之间的依赖关系是高度确定和静态的,与运行状态无关,可以进行可靠的静态分析。在打包过程中 静态分析 模块之间的导入导出,确定哪些导出的模块没有被引用并打上标记,最终将其删除。

  • 在Vue2中,很多API 都是挂载在Vue原型上的,程序无法检测到该对象的哪些属性在代码中被使用到。
  • 而Vue3优化了对tree shaking的支持,所有的核心API都支持Tree Shaking, 需要通过包引入的方式才可以使用。如果您不使用其某些功能,它们将不会包含在您的基础包中。

Tree shaking无非就是做了两件事:

  • 编译阶段利用ES6 Module判断哪些模块已经加载
  • 判断哪些模块和变量未被使用或者引用,进而删除对应代码
    3、Tree shaking作用(好处)?
  • 减少程序体积(更小);
  • 减少程序执行时间(更快);
  • 便于将来对程序架构进行优化(更友好)。

watch 和 watchEffect 的区别?

watch 和 watchEffect 都是监听器,watchEffect 是一个副作用函数。
它们之间的区别有:

  • watch :既要指明监视的数据源,也要指明监视的回调。
  • watchEffect 可以自动监听数据源作为依赖。不用指明监视哪个数据,监视的回调中用到哪个数据,那就监视哪个数据。
  • watch 可以访问改变之前和之后的值,watchEffect 只能获取改变后的值。
  • watch 依赖的属性改变后才会执行,初始化的时候不会立即执行,但是可以通过 watch 的配置项 immediate 来改变;而 watchEffect 初始化的时候会立即执行。
    watchEffect有点像 computed :
  • computed 注重的计算出来的值(回调函数的返回值), 所以必须要写返回值。
  • watcheffect注重的是过程(回调函数的函数体),所以不用写返回值。

ref与reactive的区别?

  • ref与reactive 是 Vue3 新推出的主要 API 之一,主要用于创建响应式数据。
  • ref 函数创建的响应式数据,在模板中可以直接被使用,在 JS 中需要通过 .value 的形式才能使用。
  • ref 函数可以接收原始数据类型与引用数据类型。
  • reactive 函数只能接收引用数据类型。
  • ref 底层还是使用 reactive 来做,ref 是在 reactive 上在进行封装的,增强了其能力,使它支持了对原始数据类型的处理。
  • 在 Vue3 中 reactive 能做的,ref 也能做,reactive 不能做的,ref 也能做。
  • ref还能获取组件的实例。

setup() 函数特性:

  • setup 函数有两个参数:(props、context(包含attrs、slots、emit));
  • setup函数在 生命周期 beforeCreate 和 created 两个钩子函数之前执行;
  • setup函数中不能使用this,因为 setup 函数执行时,组件实例尚未被创建,Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了undefined;
  • 与模板一起使用:需要返回一个对象,将需要在模板中使用的变量和方法return 出去,不然无法在模板中使用
  • setup函数只能是同步的不能是异步的。
  • setup 函数中的 props 是响应式的,它是一个reactive。不能使用ES6的解构来结构props,因为它会消除 props 的响应性。如果需要解构 props,可以通过使用 setup 函数中的 toRefs 来完成解构操作:
import {toRefs} from 'vue'
setup(props) {
    const { name } = toRefs(props);
    console.log(name.value);
    onMounted(() => {
        console.log('name: ' + props.name);
    })

}

script setup 是干啥的?

scrtpt setup 是 vue3.2 的语法糖,简化了组合式 API 的写法,并且运行性能更好。
script setup 语法糖的特点:

  • 属性和方法无需返回,可以直接在template模板中使用。
  • 引入组件的时候,会自动注册,无需通过 components 手动注册。
  • 使用 defineProps 接收父组件传递的值、defineEmits 定义自定义事件。
  • 使用 useAttrs 获取属性,useSlots 获取插槽。
  • 默认不会对外暴露任何属性方法,如果有需要可使用 defineExpose 指定暴露的属性、方法。
  • defineOptions可以关闭属性透传和定义组件name值。
export const enum PatchFlags {
  // 表示vnode具有动态textContent的元素
  TEXT = 1,
  // 表示vnode具有动态的class
  CLASS = 1 << 1,
  // 表示具有动态的style
  STYLE = 1 << 2,
  // 表示具有动态的非class和style的props
  PROPS = 1 << 3,
  // 表示props具有动态的key,与CLASS、STYLE、PROPS冲突
  FULL_PROPS = 1 << 4,
  // 表示有监听事件(在同构期间需要添加)
  HYDRATE_EVENTS = 1 << 5,
  // 表示vnode是个children顺序不会改变的fragment
  STABLE_FRAGMENT = 1 << 6,
  // 表示children带有key的fragment
  KEYED_FRAGMENT = 1 << 7,
  // 表示children没有key的fragment
  UNKEYED_FRAGMENT = 1 << 8,
  // 表示vnode只需要非props的patch。例如只有标签中只有ref或指令
  NEED_PATCH = 1 << 9,
  // 表示vnode存在动态的插槽。例如动态的插槽名
  DYNAMIC_SLOTS = 1 << 10,
  // 表示用户在模板的根级别存在注释而创建的片段,这是一个仅用于开发的标志,因为注释在生产中被剥离
  DEV_ROOT_FRAGMENT = 1 << 11,
  
  // 以下都是一些特殊的flag,它们不能使用位运算进行匹配
  // 表示vnode经过静态提升
  HOISTED = -1,
  // diff算法应该退出优化模式
  BAIL = -2
}

vue3 reactive响应式原理:

  • vue3 响应式主要是采用发布者订阅者模式 + es6的 Proxy 代理实现的。
  • 首先判断目标对象是否是一个对象,如果不是,直接警告提示。
  • 判断目标对象有没有被 Proxy 代理过,如果已经被代理过了直接返回目标对象。
  • 否则调用createReactiveObject方法对目标对象进行 Proxy 代理,设置getter和setter进行拦截。当 Proxy 对象中的属性被访问的时候,会触发getter,此时会去获取值,如果该值是对象且没有被Proxy代理过的话,会递归调用reactive去实现对其进行 Proxy 代理。在getter中会调用track收集effect;当 对象的属性被修改或者新增了属性的是,会调用trigger去通过订阅该属性的effect做出相应的更新;
  • track方法的作用就是收集effect,收集的effect会保存在全局WeakMap结构的targetMap对象中,其key值是需要代理的目标对象,value值是也是一个depsMap对象,它是一个 Map 结构的对象,是用来保存代理对象的key值所对应的所有effect,所有effect都是存储在Set结构的集合中的。
  • trigger方法主要是通知属性对应的所有effect去更新,会根据代理对象target去targetMap中查找代理对象所对应的depsMap,然后根据访问的代理对象的key值去depsMap查找该key的所对应的effect集合,然后遍历去执行每个effect里的更新方法,进而做出相应的更新操作。
  • effect是一个包装函数,接收一个副作用函数,它通常代表了一个需要响应数据变化的操作。当属性被访问的时候,会触发getter,此时会将当前激活的activeEffect收集到当前属性的effect收集器中,待收到数据发生变化的消息时,会调用自身对应配置下的更新方法重新继进行更新。

Vue3 ref原理:

ref利用了ES6的类的属性访问器原理,它有一个value属性,用于保存ref的值,在构造函数被创建的时候会在constructor获取初始值,如果值是对象的话,会使用reactive进行响应代理。value被设置了get和set进行监听,当value被访问的时候会触发get,在get里调用track方法进行依赖的收集。当对value进行赋值的时候会触发set,在set里会调用trigger方法通知所有的依赖该属性的方法也就是所有的effect进行更新。如果修改的值是一个对象的话,会调用reactive来对其进行响应式处理。

为什么 ref 类型数据,必须要通过 .value 访问值呢?

a. 因为 ref 既可以处理基本数据类型的数据,也可以处理对象类型的数据,但是基本数据类型的数据无法通过 proxy 进行代理。
b. 而 vue 结合ES6的类的属性访问器原理,通过 get value() 和 set value() 定义了两个属性函数,通过主动触发这两个函数的形式来进行依赖收集和依赖触发。
c. 所以我们必须通过 .value 来保证数据响应式。

computed的原理:

  1. 计算属性有函数式写法和对象写法,当用户传入的是一个函数的时候,默认使用的是getter只读操作;当用户传入的是一个对象时,用户可以设置getter,setter,此时计算属性具备可读可写的能力。
  2. 在vue3中实现计算属性中,第一步就是处理、收集用户设置的getter和setter;
  3. 然后实例化一个计算属性的类,在类的构造器中,会实例化一个effect,这个effect接收用户的设置的getter和一个调度函数,用于计算计算属性的值和更新计算属性的_dirty属性。计算属性的effect使用的是lazy配置,在初始化的时候是不会立即执行去获取值的,因为要考虑到计算属性是否有被使用。
  4. 计算属性类中有一个_dirty属性,默认为true,用于判断计算属性是否需要重新计算;还有一个_value属性,利用ES6类的属性访问器原理,被设置了get和set进行监听,当计算属性被修改时,会触发set,在set中只是简单的执行用户设置的setter;当计算属性value被访问的时候会触发get,在get里会调用effect的run方法也就是用户设置的getter去获取值,此时会访问依赖属性的值,依赖的属性会将当前活跃的计算属性的effect收集起来。在get中还会将_dirty设置为false,如果依赖属性没有发生变化是不需要重新计算值的。
  5. 当依赖的属性发生变化的时候,会去遍历该属性所有的effect,此时会执行计算属性effect的调度函数,将_dirty设置为true,当计算属性再次被访问的时候,此时的_dirty为true,会重新计算值。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356

推荐阅读更多精彩内容