Vue.js渐进

Vue笔记系列
1、Vue.js入门
3、Vue.js进阶


API

以下会随用随记一些API,可能会不定期更新。

  • Vue.component( id, [definition] )
    注册或获取全局组件。注册还会自动使用给定的id设置组件的名称。
// 注册组件,传入一个扩展过的构造器
Vue.component('my-component', Vue.extend({ /* ... */ }))
// 注册组件,传入一个选项对象(自动调用 Vue.extend)
Vue.component('my-component', { /* ... */ })
// 获取注册的组件(始终返回构造器)
var MyComponent = Vue.component('my-component')
  • Vue.extend(options)
    使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。注意:data 选项是特例,在 Vue.extend() 中它必须是函数。
<!-- HTML -->
<div id="mount-point"></div>
// 创建构造器
var Profile = Vue.extend({
      template: '<p v-text="name"></p>',
          data: function () {
            return {
                name: '第一个构造器!'
            }
      }
})
// 创建 Profile 实例,并挂载到一个元素上(会替换#mount-pointer)。挂载的组件会把被挂载的元素替换掉。
new Profile().$mount('#mount-pointer');

结果如下:

<p>第一个构造器!</p>

如果挂载元素不想被替换掉,可以用以下方法:

var component = new Profile().$mount()
document.getElementById('mount-pointer').appendChild(component.$el)
  • Vue.set( object, key, value ) 设置对象的属性。
    Vue 不能检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化过程,所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。Vue 不允许在已经创建的实例上动态添加新的根级响应式属性(所以,set方法的object参数也不能是 Vue 实例,或者 Vue 实例的根数据对象)。可以使用 Vue.set(object, key, value) 方法将响应属性添加到嵌套的对象
    之前说过的v-for指令,当你利用索引直接设置一个项时,例如上面的example1.words[0] = {text: 'A'},如果想让视图更新,其中一种方法就是用set。
Vue.set(example1.items, 0, {text: 'A'})
  • Vue.nextTick( [callback, context] )  涉及到Vue的异步更新队列
    在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
<div id="example">{{message}}</div>
 
var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
  vm.$el.textContent === 'new message' // true
})

为毛第一次DOM里面的内容没有变,拿不到改变的内容,经过nextTick方法后才才能拿到改变的内容。
这是因为,当你设置 vm.someData = 'new value' ,该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。


全局配置

全局配置——Vue.config 是一个对象,包含 Vue 的全局配置。有以下属性:

  • silent    Vue.config.silent = true; 取消 Vue 所有的日志与警告,false时开启。
  • devtools  Vue.config.devtools = true; 配置是否允许 vue-devtools 检查代码。开发版本默认为 true,生产版本默认为 false。vue-devtools指的是一个浏览器插件,在谷歌应用里面有。
    vue.devtools.png

    安装之后,是在google 开发者工具的这里找到。
    2.png

组件

一、使用组件
  • 注册
    1、全局注册
    注册一个全局组件,可以使用 Vue.component(tagName, options)
    注意:对于自定义标签名,Vue.js 不强制要求遵循 W3C规则 (小写,并且包含一个短杠),但是建议这样写。
    组件在注册之后,便可以在父实例的模块中以自定义元素的形式使用。谨记要确保在初始化根实例之前注册了组件。并且,** el 和 data 选项必须是函数**。
<!-- HTML  -->
<div id="example">
  <my-component></my-component>
</div>
// 注册,这就是所谓的语法糖,因为下面的方法有点麻烦。
Vue.component('my-component', {
      template: '<div>我的第一个组件!</div>'
})
// 创建父实例
new Vue({
      el: '#example'
})

渲染为:

<div id="example">
      <div>我的第一个组件!</div>
</div>

2、构造器用作组件
可以使用 Vue.extend({...})创建一个组件构造器,extend 方法创建基础Vue构造器的子类,参数是一个对象,包含组件选项,这里要注意的特例是 el 和 data 选项,在 Vue.extend() 中,它们必须是函数注册组件的component方法也一样。这是因为,如果使用一个数据对象(是一个引用),那么所有的组件实例都共享这一个对象,这样就会牵一发而动全身。
有了这个构造器,我们既可以用全局注册的方式用 Vue.component(tag, constructor)注册,也可以利用该构造器构建一个实例,然后用 **Vue.$mount() **将该组件实例添加到DOM树上。

<!-- HTML  -->
<div id="example">
  <my-component></my-component>
</div>
// 创建构造器
var Profile = Vue.extend({
      template: '<p v-text="name"></p>',
          data: function () {
            return {
                name: '第一个构造器组件!'
            }
      }
})
// 注册
Vue.component('my-component',Profile)
// 创建父实例
new Vue({
      el: '#example'
})

渲染为:

<div id="example">
      <p>第一个构造器组件!</p>
</div>

3、局部注册
通过使用组件实例选项注册,可以使组件仅在另一个实例/组件的作用域中可用。即在注册的对象参数中添加 components 成员,components成员的标签就只在改组建内使用,不在全局DOM树中使用局部注册的组件。

//实例作用域
var Child = {
  template: '<div>一个局部组件!</div>'
}
new Vue({
  // ...
  components: {
    // <my-component> 将只在父模板可用
    'my-component': Child
  }
})
 
//组件作用域
<div id="comp-ex">
      <contact></contact>
</div>
<script>
  var Person = Vue.extend({   // constructor
    template: "<div><span>name:</span> {{name}}  <span>Age: </span> {{age}}</div>",
    data: function() {
      return {
        name: ' li ming',
        age: 22
      }
    }
  });
  var Contact = Vue.extend({
    template: "<div><span>Tel: </span> {{tel}}, <span>E-mail: </span> {{email}}<person></person></div>",
    data: function() {
      return {
        tel: '152-0101-1010',
        email: 'admin#163.com'
      }
    },
    components: {
      'person': Person      // 局部注册 person 标签
    }
  })
  Vue.component('contact', Contact)
  var you = new Vue({       // init component, render template
    el: '#comp-ex'
  })
</script>

子组件只能在父组件的template中使用。注意下面两种子组件的使用方式是错误的:

  1. 以子标签的形式在父组件中使用
<div id="app">
    <parent-component>
        <child-component></child-component>
    </parent-component>
</div>

因为当子组件注册到父组件时,Vue.js会编译好父组件的模板,模板的内容已经决定了父组件将要渲染的HTML。
<parent-component>…</parent-component>相当于运行时,它的一些子标签只会被当作普通的HTML来执行,<child-component></child-component>不是标准的HTML标签,会被浏览器直接忽视掉。

  1. 在父组件标签外使用子组件
<div id="app">
    <parent-component>
    </parent-component>
    <child-component>
    </child-component>
</div>

运行这段代码,浏览器会提示以下错误:

在父组件标签外使用子组件报错

4、is特性
一些 HTML 元素,如<ul> <ol> <table> <select>,限制什么元素可以放在它里面。自定义元素不在白名单上,将被放在元素的外面,因而渲染不正确。这时应当使用 is 特性,指示它是一个自定义元素。

<table>
  <my-row>...</my-row>
</table>
<!-- 自定义组件 <my-row> 被认为是无效的内容,因此在渲染的时候会导致错误。需要使用特殊的 is 属性 -->
<table>
  <tr is="my-row"></tr>
</table>
二、组件通信

良好的流程: Vue.js 中,父子组件的关系可以总结为 props down, events up 。父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。

父子组件通信图示

1、Prop显式声明

  • (1)、使用prop传递数据
    组件实例的作用域是孤立的。这意味着不能并且不应该在子组件的模板内直接引用父组件的数据。可以使用 props 把数据传给子组件。prop 是父组件用来传递数据的一个自定义属性。子组件需要显式地用 props选项声明 “prop”:

读到这里,我们掌握的组件的构造选项对象的属性包括了:

  • template,要渲染的内容
  • data,数据,必须是一个函数,函数返回一个对象
  • props,从父组件传递数据到子组件。
<div id="comp-ex">
<!--  注意在HTML中属性要写kebab-case(短横线隔开) -->
  <child my-message='qiqihaobenben'></child>
  <child my-message='qiqihaobenben'></child>
</div>
<script>
  Vue.component('child', {
      // 就像 data 一样,prop 可以用在模板内
    template: '<span>{{message}}</span>',
      //声明 props
      // HTML特性不区分大小写,名字形式为 camelCase 的prop用作特性时,写在HTML中要用短横线隔开,否则不起作用,如上。
    props: ['myMessage'],  
      // 同样也可以在 vm 实例中像 “this.message” 这样使用
    data: function (){
        return {
            message: this.myMessage
        }
    }
  })
  var me = new Vue({
    el: '#comp-ex'
  })
<script>

输出结果为:

<div id="comp-ex">
     <span>qiqihaobenben qiqihaobenben</span>
</div>
  • (2)、命名风格
    HTML特性不区分大小写,但是名字形式为 camelCase 的prop用作特性时,需要转为 kebab-case(短横线隔开)。在html中的属性使用短横线隔开,而在js的template中的标识使用的驼峰命名,可以参考上面的例子。

  • (3)、props的动态
    上面的例子使用节点属性方式向子组件传递数据,如果父组件的数据变化,子组件并不会随之变化,这就是其动态性,如果要绑定动态的值,应该使用 v-bind 指令,这样每当父组件的数据变化时,也会传递给子组件

<div>
  <input v-model="parentMsg">
  <br>
  <child v-bind:my-message="parentMsg"></child>
</div>

动态和非动态这两种传递方式在传递数字时是不同的,如下:

<!-- 传递了一个字符串"1" -->
<comp some-prop="1"></comp>
<!-- 传递实际的数字1 -->
<comp v-bind:some-prop="1"></comp>
虽然html渲染的时候并不太区分字符串和数字,但是注意有这种区别。
  • (4)、单项数据流
    prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。
    注意:在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。

  • (5)、prop验证
    本来不想说的,可是看文档时怎么用怎么不对,后来想通了,所以就拿出来说一下。
    看官方文档可以知道,type 可以是下面原生构造器:

  • String

  • Number

  • Boolean

  • Function

  • Object

  • Array
    其中的除了String,其他的类型都需要动态绑定才能验证正确,否则得到的就是一水的String类型。

//js属性验证
props: {
    myAge: {
            type: [Number,Boolean,Object,Array,Function]
            }
      }
<!--以下都会报错,Expected XXX, got String.-->
<my-component my-age="12"></my-component>
<my-component my-age="true"></my-component>
<my-component my-age="{}"></my-component>
<my-component my-age="[]"></my-component>
<my-component my-age="consoleOne"></my-component>
<!--正确的做法是用v-bind来绑定属性-->
<my-component v-bind:my-age="12"></my-component>
...
...

另外,default和required这两个验证规则,需要组件的属性完全不存在时才会生效

//设置默认值
props: {
        myName: {
            default: 2
        }
    }
//设置必传项
props: {
        myName: {
            required: true
        }
    }
<!--以下,不管是必传项或者默认值都不会有效果-->
<my-component my-name="" ></my-component>
<!--只有这个属性在组件上真的没有,才会触发验证效果-->
<my-component ></my-component>

以上,当 prop 验证失败了,如果使用的是开发版本会抛出一条警告。

2、自定义事件

  • 首先:子组件可以用 this.$parent 访问它的父组件,根实例的后代可以用 this.$root 访问它,父组件有一个数组 this.$children 包含它所有的子元素。

尽管可以访问父链上任意的实例,不过子组件应当避免直接依赖父组件的数据。另外子组件中修改父组件的状态是非常糟糕的,因为:

  • 这让父组件与子组件紧密地耦合
  • 这样的话,只看父组件很难理解父组件的状态,因为它可能被任意子组件修改!理想情况下,只有组件自己能修改它的状态
    父组件是使用 props 传递数据给子组件,如果子组件要把数据传递回去,那就是自定义事件!
  • (1)使用v-on绑定自定义事件
    每个 Vue 实例都实现了事件接口(Events interface),即:
    父组件——使用 $on(eventName) 监听事件
    子组件——使用 $emit(eventName) 触发事件

下面的列子跟官网的几乎一样,但是区别在于,是通过传参来改变父组件的状态的赋值。

<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>
       var allTotal = {number: 0}  //这个是统计两个按钮的点击的次数,这样就能直接赋给父组件
        Vue.component('button-counter', {
          template: '<button v-on:click="increment">{{ counter }}</button>',
          data: function () {
            return {
              counter: 0,
              allCounter: allTotal
            }
          },
          methods: {
            increment: function () {
              this.counter += 1;
              this.allCounter.number += 1;  // 准备给父组件的自定义事件方法传参
              this.$emit('increment',this.allCounter.number);
            }
          },
        })
        new Vue({
          el: '#counter-event-example',
          data: {
            total: 0
          },
          methods: {
            incrementTotal: function (value) {
              this.total = value;
            }
          }
        })
  • (2)给组件绑定原生事件
    在某个组件的根元素上监听一个原生事件。可以使用 .native 修饰 v-on 。这就是说所有的原生事件如果在组件的根元素上不加.native,vue会自动认为它是自定义事件

  • (3)使用自定义事件的表单输入组件
    使用 v-model 来进行数据双向绑定。牢记:这个指令仅仅是一个语法糖。

<input v-model="something">
<!--上下两种方式是等价的-->
<input v-bind:value="something" v-on:input="something = $event.target.value">

所以在组件中使用时,它相当于下面的简写:

<custom-input v-bind:value="something" v-on:input="something = arguments[0]"></custom-input>
<!--改写成语法糖如下-->
<custom-input v-model="something"></custom-input>

尤其注意,这个地方有点绕:如果是自定义的表单组件,并且父组件在加载这个表单组件时使用了v-model指令,那么作为子组件,接收到的prop应该是value而不是something。

  • (4)子组件的索引
<div id="parent">
  <user-profile ref:profile></user-profile>
</div>
<script>
  var parent = new Vue({el: '#parent'});
  var child = parent.$refs.profile; // 可以得到子组件
</script>
3、内容分发
  • (1)编译作用域
    父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。说白了,就是一眼看上去,在谁里面就是谁的
<div id="app">
        <my-component v-show="display"></my-component>
    </div>

    <template id="myComponent">
        <table>
            <tr>
                <th colspan="3">{{msg}}</td>
            </tr>
        </table>
    </template>
    <script>
        var my = Vue.extend({
            template: '#myComponent',
            data : function (){
                return {
                    msg : '这是子组件',
                    display: false
                }
            }
        })
        var vm = new Vue({
            el: '#app',
            data: {
                display: true
            },
            components: {
                'myComponent': my
            }
        })
    </script>

在my-component标签上使用指令v-show="display",这个display数据是来源于Vue实例vm ,还是my-component组件呢?
答案是Vue实例

Vue实例vm设置的display是true,所以展示出来
  • (2)单个Slot
    下面的代码在定义my-component组件的模板时,指定了一个<slot></slot>元素。
<div id="app">
    <my-component>
        <h1>这是父组件的内容!</h1>
    </my-component>

    <my-component>
    </my-component>
</div>
<template id="myComponent">
    <div class="content">
        <h2>这是一个子组件!</h2>
        <slot>如果没有分发内容,则显示slot中的内容</slot>
        <p>Hello,Vue.js</p>
    </div>
</template>
  
<script>
    Vue.component('my-component', {
        template: '#myComponent'
    })

    new Vue({
        el: '#app'
    })
</script>

第一个<my-component>标签有一段分发内容 <h1>这是父组件的内容!</h1>,渲染组件时显示了这段内容。
第二个<my-component>标签则没有,渲染组件时则显示了slot标签中的内容。

单个slot
  • (3)具名slot
    <slot> 元素可以用一个特殊的属性 name 来配置如何分发内容。多个 slot 可以有不同的名字。具名 slot 将匹配内容片段中有对应 slot 特性的元素。
<div id="app">
    <my-component>
        <div slot="header">
            这是一个头部
        </div>
        <p>neirong</p>
        <p>neirong</p>
        <div slot="footer">
            这是一个底部
        </div>
    </my-component>
</div>
<template id="myComponent">
    <div class="container">
      <header>
        <slot name="header"></slot>
      </header>
      <main>
        <slot></slot>
      </main>
      <footer>
        <slot name="footer"></slot>
      </footer>
    </div>
</template>
 
<script>
    Vue.component('my-component', {
        template: '#myComponent'
    })

    new Vue({
        el: '#app'
    })
</script>

可以看出仍然可以**有一个匿名 slot **,它是默认 slot ,作为找不到匹配的内容片段的备用插槽。如果没有默认的 slot ,这些找不到匹配的内容片段将被抛弃。

  • (4)作用域插槽
    作用域插槽是一种特殊类型的插槽,用作使用一个(能够传递数据到)可重用模板替换已渲染元素。
    数据传递,可重用,自然而然的想到循环
  • 基于官网给出一个完整的例子
<div id="app">
    <my-awesome-list :items="items">
      <template slot="item" scope="props">
        <li>{{ props.text }}</li>
      </template>
    </my-awesome-list>
</div>
 
<script>
    Vue.component('my-awesome-list',{
        template: `<ul>
                    <slot name="item"
                        v-for="item in items"
                        :text="item.text">
                   
                    </slot>
                </ul>`,
        props: ['items']
    })
    var demo = new Vue({
        el: '#app',
        data: {
            items: [
                {text: 'aaaaa'},
                {text: 'bbbbb'},
                {text: 'ccccc'},
                {text: 'ddddd'},
                {text: 'eeeee'}
            ]
        }
    })
</script>
三、组件小贴士
  • (1)组件的命名
    当注册组件(或者 props)时,可以使用 kebab-case ,camelCase ,或 TitleCase。但是,在 HTML 模版中,请使用 kebab-case 形式:
// 在组件定义中
components: {
  // 使用 kebab-case 形式注册
  'kebab-cased-component': { /* ... */ },
  // register using camelCase
  'camelCasedComponent': { /* ... */ },
  // register using TitleCase
  'TitleCasedComponent': { /* ... */ }
}
 
<!-- 在HTML模版中始终使用 kebab-case -->
<kebab-cased-component></kebab-cased-component>
<camel-cased-component></camel-cased-component>
<title-cased-component></title-cased-component>

注意:当使用字符串模式时,可以不受 HTML 的 case-insensitive 限制。
待续……


生命周期

生命周期图示
1、生命周期的各阶段
  • (1)beforeCreate
    在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
  • (2)created
    实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
  • (3)beforeMount
    在挂载开始之前被调用:相关的 render 函数首次被调用。该钩子在服务器端渲染期间不被调用。
  • (3)mounted
    el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。该钩子在服务器端渲染期间不被调用。
  • (4)beforeUpdate
    数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
    你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
    该钩子在服务器端渲染期间不被调用。
  • (5)updated
    由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
    当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。
    该钩子在服务器端渲染期间不被调用。
  • (6)activated
    keep-alive 组件激活时调用。该钩子在服务器端渲染期间不被调用。
  • (7)deactivated
    keep-alive 组件停用时调用。该钩子在服务器端渲染期间不被调用。
  • (8)beforeDestroy
    实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用。
  • (8)destroyed
    Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
2、实例方法
  • (1) vm.$mount( [elementOrSelector] )
    手动地挂载一个未挂载的实例,返回值是实例自身。因而可以链式调用其它实例方法。
    如果没有提供 elementOrSelector 参数,模板将被渲染为文档之外的的元素,并且你必须使用原生DOM API把它插入文档中。
var MyComponent = Vue.extend({
  template: '<div>Hello!</div>'
})
// 创建并挂载到 #app (会替换 #app)
new MyComponent().$mount('#app')
// 同上
new MyComponent({ el: '#app' })
// 或者,在文档之外渲染并且随后挂载,这种方式不会替换#app
var component = new MyComponent().$mount()
document.getElementById('app').appendChild(component.$el)
  • (2) vm-destroy()
    完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。触发 beforeDestroy 和 destroyed 的钩子。
    注意:在大多数场景中你不应该调用这个方法。最好使用 v-if 和 v-for 指令以数据驱动的方式控制子组件的生命周期。
  • (3)vm.$nextTick( [callback] )  涉及到Vue的异步更新队列
    回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。
    应用上,在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue ,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上:
Vue.component('example', {
  template: '<span>{{ message }}</span>',
  data: function () {
    return {
      message: 'not updated'
    }
  },
  methods: {
    updateMessage: function () {
      this.message = 'updated'
      console.log(this.$el.textContent) // => '没有更新'
      this.$nextTick(function () {
        console.log(this.$el.textContent) // => '更新完成'
      })
    }
  }
})
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容