vue官网重温笔记

热重载:(hot-reloading)你修改页面之后,自动刷新,不用手动刷新

vue-cli的配置参考 (待仔细学习)

学习传送门:https://cli.vuejs.org/zh/config/#%E5%85%A8%E5%B1%80-cli-%E9%85%8D%E7%BD%AE

webpack配置(待仔细学习)

学习传送门:https://webpack.js.org/configuration/

过渡&动画 (要仔细学习,又是一个门)

学习传送门:https://cn.vuejs.org/v2/guide/transitions.html

通过插槽分发内容、动态组件、解析Dom模板时的注意事项

学习传送门:https://cn.vuejs.org/v2/guide/components.html#%E9%80%9A%E8%BF%87%E6%8F%92%E6%A7%BD%E5%88%86%E5%8F%91%E5%86%85%E5%AE%B9

用key管理可复用的元素

image.png

切换input的时候,input就是独立的了

v-if vs v-show

传送门:https://www.jianshu.com/p/2feb8cad6abf

对象变更检测注意事项

var vm=new Vue({
  data:{
    userProfile:{
     name:'anika'
   }
  }
})
//如果想要给userProfile添加一个属性age,操作如下
Vue.set(vm.userProfile,'age',27)
或者
vm.$set(vm.userProfile,'age',27)

如果想要给已知的对象赋值多个属性可以这样操作

Object.assign({},vm.userProfile,{
  age:27,
  favoriteColor:'vue Green'
})

计算属性的应用

<li v-for="n in  evenNumber"> {{n}}</li>

<script>
  new vue({
    data():{
      numbers:[1,2,3,4],
      computed:{
        evenNumber:function(){
              return this.numbers.filter(function(number){
                  return   number%2===0
              })
        }  
      }
    }
    
    })
</script>

或者

  <li v-for="n in even(numbers)">{{ n }}</li>

  data: {
      numbers: [ 1, 2, 3, 4, 5 ]
  },
  methods: {
    even: function (numbers) {
        return numbers.filter(function (number) {
            return number % 2 === 0
        })
    }
  }

v-for在组件中的应用

<ul>
   <li
     is="todo-item"
     v-for="(todo, index) in todos"
     v-bind:key="todo.id"
     v-bind:title="todo.title"
     v-on:remove="todos.splice(index, 1)"
   ></li>
 </ul>

因为li时ul内部的有效元素,所以通过is="todo-item"的方式在ul中去循环
这样循环,组件内部中需要通过prop去接收item传递的值:例如:

Vue.component('todo-item', {
  template: '\
    <li>\
      {{ title }}\
      <button v-on:click="$emit(\'remove\')">Remove</button>\
    </li>\
  ',
  props: ['title']
})

事件修饰符

传送门:https://cn.vuejs.org/v2/guide/events.html

.stop阻止单击事件继续传播 可以阻止冒泡行为
<div v-on:click="doThis1" class="btn">外部元素
             <div class="s-btn" v-on:click.stop="doThis">
                 内部元素
             </div>
         </div>
<script>
   export default{
       name:'test',
       methods:{
        doThis(){
           console.log('我是内部元素') 
        },
        doThis1(){
           console.log('我是外部元素') 
        }
       }
   }
</script>
.capture修饰符事件捕获模式

如果有此修饰符会先再此先处理,然后内部的监听事件才会进行处理
例如:

 <div v-on:click.capture="doThis2" class="btn3">
             1366777777777777777
             <div class="btn2" v-on:click="doThis3">
                 134
             </div>
         </div>
       doThis2(){
           console.log('我是1') 
        },
        doThis3(){
           console.log('我是2') 
        }

输出结果为:由外向内


image.png

反之:如果没有.capture修饰符输出结果为:由内向外


image.png
.self只有当点击的是当前本身的时候才会触发click事件

例如:
代码如下: other在self内部,当点击self的时候,输出结果为

  <div v-on:click.capture="doThis2" class="btn3">
             1366777777777777777
             <div class="btn2" v-on:click="doThis3">
                 134
                 <div v-on:click.self="doThat">
                     self
                    <div v-on:click="doThat1">
                        other
                    </div>
                 </div>
             </div>
         </div>
image.png

没有self

注意:使用顺序,

例如:v-on:click.prevent.self 会阻止所有的点击,
v-on:click.self.prevent会阻止对元素自身的点击

.passive修饰符尤其能够提供移动端的性能

<div class="click"  v-on:scroll.passive="onScroll" >
 </div>

不会等待scroll监听器执行完再去执行Onscroll,他会立即执行,因为.passive永远不会调用阻止默认行为的方法event.preventDefault()

表单输入绑定

注意

1.input中的v-mobel这样绑定,不会在输入法组合文字的过程中更新例子如下:
如果想要即时更新的,需要使用@input去实现

<input v-model="message" placeholder="edit me">
image.png

2.在textarea文本域中

 (<textarea>{{text}}</textarea>) //并不会生效
//应该使用v-mobel替换
<textarea v-model="message" placeholder="add multiple lines"></textarea>

3.select选择框


image.png
如果v-mobel表达式的初始值未能匹配任何选项,<select>元素将被渲染为"未选中"状态。
在ios中,这会使用户无法选择第一个选项。因为这样的情况下,ios不会触发change事件。因此,更推荐像上面这样提供一个值为空的禁用选项

4.修饰符 .lazy

<input v-model.lazy="msg" >只有在input失去焦点或者按回车的时候才会被赋值

插槽的使用

image.png

动态组件&异步组件

动态组件的应用:

  • 多标签的界面中
  • 可以运用keep-live标签来保持原来的状态
  • 实际应用中的商品详情页面,可能会有多种状态,拼团,抢购,默认,等可以通过动态组件实现,不用好多if语句判断了
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

异步组件应用:

去请求的时候


image.png

定义组件名

  • kebab-case
    -PasecalCase
    注意:在使用自定义组件的时候,最好使用kebab-case的方式

基础组件的自动化全局注册:有些不解,希望有人指教

prop

注意:

在子组件中通过props定义名字的使用的驼峰,在运用绑定传递的时候需要用kebab-case

Vue.component('blog-post',{
  props:['postTitle'],
  templete:'<h3>{{postTitle}}</h3>'
})
//
<blog-post  post-title="hello"></blog-post>

prop类型

当只在prop中写字符串的时候为数组类型
prop['title','likes','isPublished','commentIds','author']
当在prop中写类型的时候为对象类型
prop{
  title:String,
  likes:Number,
  isPublished:Boolean,
  commentIds:Array,
  author:Object
}

prop的传递有静态类型和动态类型

//静态类型
<blog-post title="My journey with Vue"></blog-post>
//动态类型
<blog-post v-bind:title="post.title"></blog-post>

当传递一个静态类型的数字、对象、布尔类型的时候需要通过v-bind方式传递,告诉vue这不是一个字符串,这是一个javascript表达式

<blog-post v-bind:likes="42"></blog-post>
<blog-post is-published></blog-post>//默认传递的是true
<blog-post v-bind:is-published="false"></blog-post>
...

单向数据流

父级prop的更新会向下流动到子组件中,但是反过来不行。这样会防止子组件意外改变父级组件的状态。
子组件改变父组件的值需要通过this.$emit('值',方法名)的方式更改,在组件中:方法名:父组件在定义一个方法名

prop验证

Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

注意:那些prop会在一个组件实例创建之前进行验证,所以实例属性如(data、computed等)再default或validator函数中不可用

自定义事件

.sync修饰符
在有些情况下,我们可能需要对一个prop进行“双向绑定”。也就是子组件可以修改父组件的问题
普通的this.$emit

myShow(){
  this.$emit('fun','修改的值传递')
}
//父组件
 <mycom v-on:fun="change"></mycom>

使用.sync修饰符

closeDiv() {
          this.$emit('update:show', false); //触发 input 事件,并传入新值
        }
//父组件
 <myComponent :show.sync='valueChild' style="padding: 30px 20px 30px 5px;border:1px solid #ddd;margin-bottom: 10px;"></myComponent>
<button @click="changeValue">toggle</button>
//当前中的valueChild就是子组件传递过来的值,不用父组件在创建方法接收  
export default{
    data(){
        return{
            valueChild:true,
        }
    },
    methods:{
        changeValue(){
            this.valueChild = !this.valueChild
        }
    }
}

注意:.sync后面不能是表达式

插槽:https://cn.vuejs.org/v2/guide/components-slots.html

处理边界情况

访问子组件实例,或子元素可以通过ref

<base-input ref="usernameInput"></base-input>
this.$refs.usernameInput

注意:ref只会在组件渲染完成之后生效,并且他们不是响应式的。 要避免在模板或计算属性中使用refs

依赖注入

意思就是如果子元素想要访问父组件中的方法
在父组件中

provide: function () {
  return {
    getMap: this.getMap
  }
}

在子组件中

inject: ['getMap']

注意:provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。

程序化的事件监听

  • $on(eventName,eventHandler)监听一个事件
  • $once(eventName,eventHandler) 一次性监听一个事件
  • $off(eventName,eventHander) 停止监听一个事件

应用场景:
使用第三方库的时候,例如时间插件

//平常我们使用
// 一次性将这个日期选择器附加到一个输入框上
// 它会被挂载到 DOM 上。
mounted: function () {
  // Pikaday 是一个第三方日期选择器的库
  this.picker = new Pikaday({
    field: this.$refs.input,
    format: 'YYYY-MM-DD'
  })
},
// 在组件被销毁之前,
// 也销毁这个日期选择器。
beforeDestroy: function () {
  this.picker.destroy()
}

潜在问题

需要在在这个组件实例中保存这个picker
难于程序化清理

通过程序化监听器解决,并且可以引用使用多个时间三方库

mounted: function () {
  var picker = new Pikaday({
    field: this.$refs.input,
    format: 'YYYY-MM-DD'
  })

  this.$once('hook:beforeDestroy', function () {
    picker.destroy()
  })
}
mounted: function () {
  this.attachDatepicker('startDateInput') //不同的ref  元素,使用picker
  this.attachDatepicker('endDateInput')
},
methods: {
  attachDatepicker: function (refName) {
    var picker = new Pikaday({
      field: this.$refs[refName],
      format: 'YYYY-MM-DD'
    })

    this.$once('hook:beforeDestroy', function () {
      picker.destroy()
    })
  }
}

组件之间的循环引用

例子:

<p>
  <span>{{ folder.name }}</span>
  <tree-folder-contents :children="folder.children"/>
</p>

tree-folder-contents 组件

<ul>
  <li v-for="child in children">
    <tree-folder v-if="child.children" :folder="child"/>
    <span v-else>{{ child.name }}</span>
  </li>
</ul>

需要在注册组件的时候这样引入:使用webpack的异步import

components: {
  TreeFolderContents: () => import('./tree-folder-contents.vue')
}

组件之间循环引用的问题所在:(没看懂,请大佬指教)

  • 为了解释这里发生了什么,我们先把两个组件称为 A 和 B。模块系统发现它需要 A,但是首先 A 依赖 B,但是 B 又依赖 A,但是 A 又依赖 B,如此往复。这变成了一个循环,不知道如何不经过其中一个组件而完全解析出另一个组件。为了解决这个问题,我们需要给模块系统一个点,在那里“A 反正是需要 B 的,但是我们不需要先解析 B。”

通过v-once 创建低开销的静态组件

ue.component('terms-of-service', {
  template: `
    <div v-once>
      <h1>Terms of Service</h1>
      ... a lot of static content ...
    </div>
  `
})

注意:
当你需要渲染大量静态内容时,极少数的情况下它会给你带来便利,除非你非常留意渲染变慢了,不然它完全是没有必要的——再加上它在后期会带来很多困惑。例如,设想另一个开发者并不熟悉 v-once 或漏看了它在模板中,他们可能会花很多个小时去找出模板为什么无法正确更新。

混入

例子:

var mixin = {
  data: function () {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created: function () {
    console.log(this.$data)
    // => { message: "goodbye", foo: "abc", bar: "def" }
  }
})
遇到冲突的data变量和方法名的策略
  • 同名的data变量优先选择组件内部的
  • 同名的钩子函数会合并成一个数组例如created 都会执行
var mixin = {
  created: function () {
    console.log('混入对象的钩子被调用')
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('组件钩子被调用')
  }
})

// => "混入对象的钩子被调用"
// => "组件钩子被调用"
  • 值为对象的选项,例如 method、components、 directives 将被合并为同一个对象。两个兑现键值对冲突的时候优先组件内部的
var mixin = {
  methods: {
    foo: function () {
      console.log('foo')
    },
    conflicting: function () {
      console.log('from mixin')
    }
  }
}

var vm = new Vue({
  mixins: [mixin],
  methods: {
    bar: function () {
      console.log('bar')
    },
    conflicting: function () {
      console.log('from self')
    }
  }
})

vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"

过滤器

<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>

自定义过滤器:

filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}

或者

Vue.filter('capitalize', function (value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

new Vue({
  // ...
})

当全局过滤器和局部过滤器重名时,采用局部过滤器

//过滤器可以串联
{{ message | filterA | filterB }}

//先将message传入到filterA方法中,然后再把筛选后的结果传入到filterB方法中进行筛选

还可以这样:
因为过滤器是javascript函数,因此可以接收参数

{{ message | filterA('arg1', arg2) }}

单元测试 (不明白,需要学习)

安全

第一原则:永远不要使用不可信任得模板
这样得做就等于允许在应用程序中执行任意得javascript,更糟得是如果在服务端渲染得话可能导致服务器被攻破。
例子:

new Vue({
  el: '#app',
  template: `<div>` + userProvidedString + `</div>` // 永远不要这样做
})

vue的安全措施:https://cn.vuejs.org/v2/guide/security.html

Html内容

不论使用模板还是渲染函数,内容都会被自动转义。
例如:

<h1>{{ userProvidedString }}</h1>

如果userProvideString变量中包含了

'<script>alert("hi")</script>' //被注入的恶意代码

则会被转义成

&lt;script&gt;alert(&quot;hi&quot;)&lt;/script&gt;  //没有办法执行script了

因此避免了脚本注入。该转义通过诸如textContent的浏览原生的Api完成,所以除非浏览器本身存在安全漏洞,否则不会存在安全漏洞

Attribute(属性)绑定 道理同
<h1 v-bind:title="userProvidedString">
  hello
</h1>

如果 userProvidedString 包含了:

'" onclick="alert(\'hi\')'//恶意代码

则会被转义成为如下 HTML:

&quot; onclick=&quot;alert('hi')

潜在危险

注入url
<a v-bind:href="userProvidedUrl">
  click me
</a>

如果没有对url进行过滤“防止javascript:通过”则会由潜在的安全问题。有些库可以帮助解决
sanitize-url
https://www.npmjs.com/package/@braintree/sanitize-url

注意:在前端进行了url过滤,那么久已经有安全问题了。用户提供的url永远需要通过后端在入库之前进行过滤。然后这个被注入的问题在每个客户端连接api时就会被阻止,包括原生移动应用。

注入样式
<a
  v-bind:href="sanitizedUrl"
  v-bind:style="userProvidedStyles"
>
  click me
</a>

假设sanitizedUrl已经被过滤过了。但通过userProvidedStyles,恶意用户仍然可以提供css来进行“点击诈骗”。将链接的样式设置为一个透明的方框覆盖在“登陆”上,然后再把按钮做成应用中的按钮的样子。它们就可以获取一个用户真实的登陆信息。
可以改为:

<a
  v-bind:href="sanitizedUrl"
  v-bind:style="{
    color: userProvidedColor,
    background: userProvidedBackground
  }"
>
  click me
</a>

推荐使用对象语法且只允许用户提供特定的可以安全控制的property的值

深入响应式原理

当把一个javascript对象传入vue实例作为data选项,vue将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转换为getter/setter.Object.defineProoperty是es5中一个无法shim的特性。是他们能够让vue追踪依赖。再属性被访问和修改时通知变更。
每一个实例都对应一个watcher实例,他会再组件渲染过程中把接触过的数据属性记录为依赖。之后当依赖的setter触发时,会通知watcher,从而使它关联的组件更新渲染。


image.png

检测变化的注意事项

Vue不能检测数组和对象的变化。

对象

vue无法检测property的添加和移除。
vue会在初始化实例时对属性执行getter/setter转化,所以属性必须在data上存在才能让vue将它转换为响应式。
对于已经创建得vue实例,vue不允许动态添加根级别得响应式
可以使用Vue.set(object,propertyName,value)
例如:

Vue.set(vm.someObject, 'b', 2)

或者

this.$set(this.someObject,'b',2)

有的时候可能需要为已有对象赋值多个新属性,比如使用Object.assign()或_.extend() 。但是这样添加到对象上得新属性不会触发更新。没有被监听到,添加这些属性。可以这样,用原对象与要混合进去得对象得属性一起创建一个新的对象。

this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
数组

vue不能检测的数组变动:
1.当你利用索引直接设置一个数组项时:

vm.items[indexOfItem] = newValue

2.当你修改数组的长度时

vm.items.length = newLength

举例:

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

可以触发更新的修改数组方式:

Vue.set(vm.items, indexOfItem, newValue)||vm.$set(vm.items, indexOfItem, newValue)

解决数组的长度

vm.items.splice(newLength)

异步更新队列

vue在更新dom时是异步执行的。只要侦听到数据变化,vue将开启一个队列,vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被堆入到队列中一次。
vue在内部对异步队列尝试使用原生的 Promise.then 、MutationObserver和setImmediate,如果执行环境不支持,则会采用setTimeout(fn,0)

var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false(这个时候还没更改完成)
//只有在执行下一个事件循环tick中更新
Vue.nextTick(function () {
  vm.$el.textContent === 'new message' // true
})

或者

Vue.component('example', {
  template: '<span>{{ message }}</span>',
  data: function () {
    return {
      message: '未更新'
    }
  },
  methods: {
    updateMessage: function () {
      this.message = '已更新'
      console.log(this.$el.textContent) // => '未更新'
      this.$nextTick(function () {
        console.log(this.$el.textContent) // => '已更新'
      })
    }
  }
})

因为$necxtTick()返回一个Promise对象,所以你可以使用新的es7的语法糖
async/await语法完成相同的事情
async 声明一个异步的function
await 等待一个异步方法执行完成 再去执行下面的代码

methods: {
  updateMessage: async function () {
    this.message = '已更新'
    console.log(this.$el.textContent) // => '未更新'
    await this.$nextTick()  等待
    console.log(this.$el.textContent) // => '已更新'
  }
}

在mounted钩子函数默认是this.$el.document获取不到

//可以这样
mounted: function () {
  this.$nextTick(function () {
    // 代码保证 this.$el 在 document 中
  })
}
注意:

在created钩子中通过$on绑定了事件,最好在组件销毁前,清除事件的监听

beforeDestroy: function () {
  eventHub.$off('add-todo', this.addTodo)
  eventHub.$off('delete-todo', this.deleteTodo)
},

vue和其它框架的对比:https://www.jianshu.com/p/eef0a8e0dc49

vue打包得时候总是出现好多js,希望只出现一个js

因为页面比较多,打包成一个js的话,文件会很大。否则第一次加载的时候半天都是白屏,一般都是按需加载,打包也是根据需要进行打包

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容