vue3教程

全局API

createApp

返回一個提供應用上下文的應用實例。應用實例掛載的整個組件樹共享同一個上下文。

const app = createApp(Component);
app.mount('#root');

defineComponent

創建組件

import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    let counter = ref(0);
    const increment = () => { 
      counter.value += 1;
    }
    return {
      counter,
      increment
    }
  },
  render() {
    const {counter, increment} = this;
    return (
      <div>
        <p>Counter: {counter}</p>
        <button onClick={increment}>+</button>
      </div>
    );
  },
});

或者是一個 setup 函數,函數名稱將作為組件名稱來使用

import { defineComponent, ref } from 'vue';

const Parent = defineComponent(function Parent() {
  let age = ref(10);
  const incrementAge = () => { 
    age.value += 1;
  }
  return {
    age,
    incrementAge
  }
})
console.log('Parent', Parent)
// {
//   name: "Parent2"
//   setup: ? Parent2()
// }

defineAsyncComponent

創建一個異步加載組件

// 全局注冊
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
  import('./components/AsyncComponent.vue')
)
app.component('async-component', AsyncComp)
// 局部注冊
import {createApp, defineAsyncComponent } from 'vue'
createApp({
  components: {
    AsyncComponent: defineAsyncComponent(() =>
      import('./components/AsyncComponent.vue')
    )
  }
})

// 高階組件

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent({
  // 工廠函數
  loader: () => import('./Foo.vue')
  // 加載異步組件時要使用的組件
  loadingComponent: LoadingComponent,
  // 加載失敗時要使用的組件
  errorComponent: ErrorComponent,
  // 在顯示 loadingComponent 之前的延遲 | 默認值:200(單位 ms)
  delay: 200,
  // 如果提供了 timeout ,并且加載組件的時間超過了設定值,將顯示錯誤組件
  // 默認值:Infinity(即永不超時,單位 ms)
  timeout: 3000,
  // 定義組件是否可掛起 | 默認值:true
  suspensible: false,
  /**
   *
   * @param {*} error 錯誤信息對象
   * @param {*} retry 一個函數,用于指示當 promise 加載器 reject 時,加載器是否應該重試
   * @param {*} fail  一個函數,指示加載程序結束退出
   * @param {*} attempts 允許的最大重試次數
   */
  onError(error, retry, fail, attempts) {
    if (error.message.match(/fetch/) && attempts <= 3) {
      // 請求發生錯誤時重試,最多可嘗試 3 次
      retry()
    } else {
      // 注意,retry/fail 就像 promise 的 resolve/reject 一樣:
      // 必須調用其中一個才能繼續錯誤處理。
      fail()
    }
  }
})

resolveComponent

在當前應用中查找組件, 只能在 render 或 setup 函數中使用。

import { createApp, defineComponent, resolveComponent} from 'vue'
const app = createApp({});
app.component('ResolveComponent', {
  render(){
    return (<h1>This is a ResolveComponent</h1>)
  }
})
app.mount('#root');

defineComponent({
  render() {
    // return component or componentName;
    const ResolveComponent = resolveComponent('ResolveComponent');

    return (
      <div>
        <ResolveComponent />
      </div>
    )
  }
})

resolveDynamicComponent

查找組件

import {defineComponent, resolveDynamicComponent} from 'vue'
defineComponent({
  setup() {
    const isShow = ref(false);
    return {
      isShow
    }
  },
  render() {
    // return component or componentName;
   const ResDynamicComponent = resolveDynamicComponent({
      render() {
        return (
          <div>
            <h1>This is a ResolveDynamicComponent</h1>
          </div>
        )
      }
    })

    return (
      <div>
       {
          isShow ? <ResDynamicComponent /> : ''
        }
      </div>
    )
  }
})

resolveDirective

在當前應用中查找指令, 只能在 render 或 setup 函數中使用。

import { createApp, defineComponent, resolveDirective} from 'vue'
const app = createApp({})
app.directive('highlight', {})
defineComponent({
  setup() {
    // return Directive or undefined;
    const directive = resolveDirective('highlight');
  }
})

withDirectives

defineComponent({
  render() {
    const focus = resolveDirective('focus')
    return withDirectives(<input type="text" v-focus />, [[focus]])
  }
})

createRenderer

nextTick

將回調推遲到下一個 DOM 更新周期之后執行。在更改了一些數據以等待 DOM 更新后立即使用它。

mergeProps

 const props = mergeProps({
      class: 'active'
    },  this.$attrs)

4) 比較Vue2與Vue3的響應式(重要)

vue2的響應式

  • 核心:
    • 對象: 通過defineProperty對對象的已有屬性值的讀取和修改進行劫持(監視/攔截)
    • 數組: 通過重寫數組更新數組一系列更新元素的方法來實現元素修改的劫持
Object.defineProperty(data, 'count', {
    get () {}, 
    set () {}
})

  • 問題
    • 對象直接新添加的屬性或刪除已有屬性, 界面不會自動更新
    • 直接通過下標替換元素或更新length, 界面不會自動更新 arr[1] = {}

Vue3的響應式

new Proxy(data, {
    // 攔截讀取屬性值
    get (target, prop) {
        return Reflect.get(target, prop)
    },
    // 攔截設置屬性值或添加新屬性
    set (target, prop, value) {
        return Reflect.set(target, prop, value)
    },
    // 攔截刪除屬性
    deleteProperty (target, prop) {
        return Reflect.deleteProperty(target, prop)
    }
})

proxy.name = 'tom'   

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Proxy 與 Reflect</title>
</head>
<body>
  <script>

    const user = {
      name: "John",
      age: 12
    };

    /* 
    proxyUser是代理對象, user是被代理對象
    后面所有的操作都是通過代理對象來操作被代理對象內部屬性
    */
    const proxyUser = new Proxy(user, {

      get(target, prop) {
        console.log('劫持get()', prop)
        return Reflect.get(target, prop)
      },

      set(target, prop, val) {
        console.log('劫持set()', prop, val)
        return Reflect.set(target, prop, val); // (2)
      },

      deleteProperty (target, prop) {
        console.log('劫持delete屬性', prop)
        return Reflect.deleteProperty(target, prop)
      }
    });
    // 讀取屬性值
    console.log(proxyUser===user)
    console.log(proxyUser.name, proxyUser.age)
    // 設置屬性值
    proxyUser.name = 'bob'
    proxyUser.age = 13
    console.log(user)
    // 添加屬性
    proxyUser.sex = '男'
    console.log(user)
    // 刪除屬性
    delete proxyUser.sex
    console.log(user)
  </script>
</body>
</html>

5) setup細節

  • setup執行的時機

    • 在beforeCreate之前執行(一次), 此時組件對象還沒有創建
    • this是undefined, 不能通過this來訪問data/computed/methods / props
    • 其實所有的composition API相關回調函數中也都不可以
  • setup的返回值

    • 一般都返回一個對象: 為模板提供數據, 也就是模板中可以直接使用此對象中的所有屬性/方法
    • 返回對象中的屬性會與data函數返回對象的屬性合并成為組件對象的屬性
    • 返回對象中的方法會與methods中的方法合并成功組件對象的方法
    • 如果有重名, setup優先
    • 注意:
    • 一般不要混合使用: methods中可以訪問setup提供的屬性和方法, 但在setup方法中不能訪問data和methods
    • setup不能是一個async函數: 因為返回值不再是return的對象, 而是promise, 模板看不到return對象中的屬性數據
  • setup的參數

    • setup(props, context) / setup(props, {attrs, slots, emit})
    • props: 包含props配置聲明且傳入了的所有屬性的對象
    • attrs: 包含沒有在props配置中聲明的屬性的對象, 相當于 this.$attrs
    • slots: 包含所有傳入的插槽內容的對象, 相當于 this.$slots
    • emit: 用來分發自定義事件的函數, 相當于 this.$emit
<template>
  <h2>App</h2>
  <p>msg: {{msg}}</p>
  <button @click="fn('--')">更新</button>

  <child :msg="msg" msg2="cba" @fn="fn"/>
</template>

<script lang="ts">
import {
  reactive,
  ref,
} from 'vue'
import child from './child.vue'

export default {

  components: {
    child
  },

  setup () {
    const msg = ref('abc')

    function fn (content: string) {
      msg.value += content
    }
    return {
      msg,
      fn
    }
  }
}
</script>

<template>
  <div>
    <h3>{{n}}</h3>
    <h3>{{m}}</h3>

    <h3>msg: {{msg}}</h3>
    <h3>msg2: {{$attrs.msg2}}</h3>

    <slot name="xxx"></slot>

    <button @click="update">更新</button>
  </div>
</template>

<script lang="ts">

import {
  ref,
  defineComponent
} from 'vue'

export default defineComponent({
  name: 'child',

  props: ['msg'],

  emits: ['fn'], // 可選的, 聲明了更利于程序員閱讀, 且可以對分發的事件數據進行校驗

  data () {
    console.log('data', this)
    return {
      // n: 1
    }
  },

  beforeCreate () {
    console.log('beforeCreate', this)
  },

  methods: {
    // update () {
    //   this.n++
    //   this.m++
    // }
  },

  // setup (props, context) {
  setup (props, {attrs, emit, slots}) {

    console.log('setup', this)
    console.log(props.msg, attrs.msg2, slots, emit)

    const m = ref(2)
    const n = ref(3)

    function update () {
      // console.log('--', this)
      // this.n += 2 
      // this.m += 2

      m.value += 2
      n.value += 2

      // 分發自定義事件
      emit('fn', '++')
    }

    return {
      m,
      n,
      update,
    }
  },
})
</script>

6) reactive與ref-細節

  • 是Vue3的 composition API中2個最重要的響應式API
  • ref用來處理基本類型數據, reactive用來處理對象(遞歸深度響應式)
  • 如果用ref對象/數組, 內部會自動將對象/數組轉換為reactive的代理對象
  • ref內部: 通過給value屬性添加getter/setter來實現對數據的劫持
  • reactive內部: 通過使用Proxy來實現對對象內部所有數據的劫持, 并通過Reflect操作對象內部數據
  • ref的數據操作: 在js中要.value, 在模板中不需要(內部解析模板時會自動添加.value)
<template>
  <h2>App</h2>
  <p>m1: {{m1}}</p>
  <p>m2: {{m2}}</p>
  <p>m3: {{m3}}</p>
  <button @click="update">更新</button>
</template>

<script lang="ts">
import {
  reactive,
  ref
} from 'vue'

export default {

  setup () {
    const m1 = ref('abc')
    const m2 = reactive({x: 1, y: {z: 'abc'}})

    // 使用ref處理對象  ==> 對象會被自動reactive為proxy對象
    const m3 = ref({a1: 2, a2: {a3: 'abc'}})
    console.log(m1, m2, m3)
    console.log(m3.value.a2) // 也是一個proxy對象

    function update() {
      m1.value += '--'
      m2.x += 1
      m2.y.z += '++'

      m3.value = {a1: 3, a2: {a3: 'abc---'}}
      m3.value.a2.a3 += '==' // reactive對對象進行了深度數據劫持
      console.log(m3.value.a2)
    }

    return {
      m1,
      m2,
      m3,
      update
    }
  }
}
</script>

7) 計算屬性與監視

  • computed函數:

    • 與computed配置功能一致
    • 只有getter
    • 有getter和setter
  • watch函數

    • 與watch配置功能一致
    • 監視指定的一個或多個響應式數據, 一旦數據變化, 就自動執行監視回調
    • 默認初始時不執行回調, 但可以通過配置immediate為true, 來指定初始時立即執行第一次
    • 通過配置deep為true, 來指定深度監視
  • watchEffect函數

    • 不用直接指定要監視的數據, 回調函數中使用的哪些響應式數據就監視哪些響應式數據
    • 默認初始時就會執行第一次, 從而可以收集需要監視的數據
    • 監視數據發生變化時回調
<template>
  <h2>App</h2>
  fistName: <input v-model="user.firstName"/><br>
  lastName: <input v-model="user.lastName"/><br>
  fullName1: <input v-model="fullName1"/><br>
  fullName2: <input v-model="fullName2"><br>
  fullName3: <input v-model="fullName3"><br>

</template>

<script lang="ts">
/*
計算屬性與監視
1\. computed函數: 
  與computed配置功能一致
  只有getter
  有getter和setter
2\. watch函數
  與watch配置功能一致
  監視指定的一個或多個響應式數據, 一旦數據變化, 就自動執行監視回調
  默認初始時不執行回調, 但可以通過配置immediate為true, 來指定初始時立即執行第一次
  通過配置deep為true, 來指定深度監視
3\. watchEffect函數
  不用直接指定要監視的數據, 回調函數中使用的哪些響應式數據就監視哪些響應式數據
  默認初始時就會執行第一次, 從而可以收集需要監視的數據
  監視數據發生變化時回調
*/

import {
  reactive,
  ref,
  computed,
  watch,
  watchEffect
} from 'vue'

export default {

  setup () {
    const user = reactive({
      firstName: 'A',
      lastName: 'B'
    })

    // 只有getter的計算屬性
    const fullName1 = computed(() => {
      console.log('fullName1')
      return user.firstName + '-' + user.lastName
    })

    // 有getter與setter的計算屬性
    const fullName2 = computed({
      get () {
        console.log('fullName2 get')
        return user.firstName + '-' + user.lastName
      },

      set (value: string) {
        console.log('fullName2 set')
        const names = value.split('-')
        user.firstName = names[0]
        user.lastName = names[1]
      }
    })

    const fullName3 = ref('')

    /* 
    watchEffect: 監視所有回調中使用的數據
    */
    /* 
    watchEffect(() => {
      console.log('watchEffect')
      fullName3.value = user.firstName + '-' + user.lastName
    }) 
    */

    /* 
    使用watch的2個特性:
      深度監視
      初始化立即執行
    */
    watch(user, () => {
      fullName3.value = user.firstName + '-' + user.lastName
    }, {
      immediate: true,  // 是否初始化立即執行一次, 默認是false
      deep: true, // 是否是深度監視, 默認是false
    })

    /* 
    watch一個數據
      默認在數據發生改變時執行回調
    */
    watch(fullName3, (value) => {
      console.log('watch')
      const names = value.split('-')
      user.firstName = names[0]
      user.lastName = names[1]
    })

    /* 
    watch多個數據: 
      使用數組來指定
      如果是ref對象, 直接指定
      如果是reactive對象中的屬性,  必須通過函數來指定
    */
    watch([() => user.firstName, () => user.lastName, fullName3], (values) => {
      console.log('監視多個數據', values)
    })

    return {
      user,
      fullName1,
      fullName2,
      fullName3
    }
  }
}
</script>

8) 生命周期

vue2.x的生命周期

lifecycle.png

vue3的生命周期

lifecycle (1).png

與 2.x 版本生命周期相對應的組合式 API

  • beforeCreate -> 使用 setup()
  • created -> 使用 setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

新增的鉤子函數

組合式 API 還提供了以下調試鉤子函數:

  • onRenderTracked
  • onRenderTriggered
<template>
<div class="about">
  <h2>msg: {{msg}}</h2>
  <hr>
  <button @click="update">更新</button>
</div>
</template>

<script lang="ts">
import {
  ref,
  onMounted,
  onUpdated,
  onUnmounted, 
  onBeforeMount, 
  onBeforeUpdate,
  onBeforeUnmount
} from "vue"

export default {
  beforeCreate () {
    console.log('beforeCreate()')
  },

  created () {
    console.log('created')
  },

  beforeMount () {
    console.log('beforeMount')
  },

  mounted () {
    console.log('mounted')
  },

  beforeUpdate () {
    console.log('beforeUpdate')
  },

  updated () {
    console.log('updated')
  },

  beforeUnmount () {
    console.log('beforeUnmount')
  },

  unmounted () {
     console.log('unmounted')
  },

  setup() {

    const msg = ref('abc')

    const update = () => {
      msg.value += '--'
    }

    onBeforeMount(() => {
      console.log('--onBeforeMount')
    })

    onMounted(() => {
      console.log('--onMounted')
    })

    onBeforeUpdate(() => {
      console.log('--onBeforeUpdate')
    })

    onUpdated(() => {
      console.log('--onUpdated')
    })

    onBeforeUnmount(() => {
      console.log('--onBeforeUnmount')
    })

    onUnmounted(() => {
      console.log('--onUnmounted')
    })

    return {
      msg,
      update
    }
  }
}
</script>

<template>
  <h2>App</h2>
  <button @click="isShow=!isShow">切換</button>
  <hr>
  <Child v-if="isShow"/>
</template>

<script lang="ts">
import Child from './Child.vue'
export default {

  data () {
    return {
      isShow: true
    }
  },

  components: {
    Child
  }
}
</script>

09) 自定義hook函數

  • 使用Vue3的組合API封裝的可復用的功能函數

  • 自定義hook的作用類似于vue2中的mixin技術

  • 自定義Hook的優勢: 很清楚復用功能代碼的來源, 更清楚易懂

  • 需求1: 收集用戶鼠標點擊的頁面坐標

    hooks/useMousePosition.ts

import { ref, onMounted, onUnmounted } from 'vue'
/* 
收集用戶鼠標點擊的頁面坐標
*/
export default function useMousePosition () {
  // 初始化坐標數據
  const x = ref(-1)
  const y = ref(-1)

  // 用于收集點擊事件坐標的函數
  const updatePosition = (e: MouseEvent) => {
    x.value = e.pageX
    y.value = e.pageY
  }

  // 掛載后綁定點擊監聽
  onMounted(() => {
    document.addEventListener('click', updatePosition)
  })

  // 卸載前解綁點擊監聽
  onUnmounted(() => {
    document.removeEventListener('click', updatePosition)
  })

  return {x, y}
}

<template>
<div>
  <h2>x: {{x}}, y: {{y}}</h2>
</div>
</template>

<script>

import {
  ref
} from "vue"
/* 
在組件中引入并使用自定義hook
自定義hook的作用類似于vue2中的mixin技術
自定義Hook的優勢: 很清楚復用功能代碼的來源, 更清楚易懂
*/
import useMousePosition from './hooks/useMousePosition'

export default {
  setup() {

    const {x, y} = useMousePosition()

    return {
      x,
      y,
    }
  }
}
</script>

  • 利用TS泛型強化類型檢查

  • 需求2: 封裝發ajax請求的hook函數

    hooks/useRequest.ts

import { ref } from 'vue'
import axios from 'axios'

/* 
使用axios發送異步ajax請求
*/
export default function useUrlLoader<T>(url: string) {

  const result = ref<T | null>(null)
  const loading = ref(true)
  const errorMsg = ref(null)

  axios.get(url)
    .then(response => {
      loading.value = false
      result.value = response.data
    })
    .catch(e => {
      loading.value = false
      errorMsg.value = e.message || '未知錯誤'
    })

  return {
    loading,
    result,
    errorMsg,
  }
}

<template>
<div class="about">
  <h2 v-if="loading">LOADING...</h2>
  <h2 v-else-if="errorMsg">{{errorMsg}}</h2>
  <!-- <ul v-else>
    <li>id: {{result.id}}</li>
    <li>name: {{result.name}}</li>
    <li>distance: {{result.distance}}</li>
  </ul> -->

  <ul v-for="p in result" :key="p.id">
    <li>id: {{p.id}}</li>
    <li>title: {{p.title}}</li>
    <li>price: {{p.price}}</li>
  </ul>
  <!-- <img v-if="result" :src="result[0].url" alt=""> -->
</div>
</template>

<script lang="ts">
import {
  watch
} from "vue"
import useRequest from './hooks/useRequest'

// 地址數據接口
interface AddressResult {
  id: number;
  name: string;
  distance: string;
}

// 產品數據接口
interface ProductResult {
  id: string;
  title: string;
  price: number;
}

export default {
  setup() {

    // const {loading, result, errorMsg} = useRequest<AddressResult>('/data/address.json')
    const {loading, result, errorMsg} = useRequest<ProductResult[]>('/data/products.json')

    watch(result, () => {
      if (result.value) {
        console.log(result.value.length) // 有提示
      }
    })

    return {
      loading,
      result, 
      errorMsg
    }
  }
}
</script>

10) toRefs

把一個響應式對象轉換成普通對象,該普通對象的每個 property 都是一個 ref

應用: 當從合成函數返回響應式對象時,toRefs 非常有用,這樣消費組件就可以在不丟失響應式的情況下對返回的對象進行分解使用

問題: reactive 對象取出的所有屬性值都是非響應式的

解決: 利用 toRefs 可以將一個響應式 reactive 對象的所有原始屬性轉換為響應式的 ref 屬性

<template>
  <h2>App</h2>
  <h3>foo: {{foo}}</h3>
  <h3>bar: {{bar}}</h3>
  <h3>foo2: {{foo2}}</h3>
  <h3>bar2: {{bar2}}</h3>

</template>

<script lang="ts">
import { reactive, toRefs } from 'vue'
/*
toRefs:
  將響應式對象中所有屬性包裝為ref對象, 并返回包含這些ref對象的普通對象
  應用: 當從合成函數返回響應式對象時,toRefs 非常有用,
        這樣消費組件就可以在不丟失響應式的情況下對返回的對象進行分解使用
*/
export default {

  setup () {

    const state = reactive({
      foo: 'a',
      bar: 'b',
    })

    const stateAsRefs = toRefs(state)

    setTimeout(() => {
      state.foo += '++'
      state.bar += '++'
    }, 2000);

    const {foo2, bar2} = useReatureX()

    return {
      // ...state,
      ...stateAsRefs,
      foo2, 
      bar2
    }
  },
}

function useReatureX() {
  const state = reactive({
    foo2: 'a',
    bar2: 'b',
  })

  setTimeout(() => {
    state.foo2 += '++'
    state.bar2 += '++'
  }, 2000);

  return toRefs(state)
}

</script>

11) ref獲取元素

利用ref函數獲取組件中的標簽元素

功能需求: 讓輸入框自動獲取焦點

<template>
  <h2>App</h2>
  <input type="text">---
  <input type="text" ref="inputRef">
</template>

<script lang="ts">
import { onMounted, ref } from 'vue'
/* 
ref獲取元素: 利用ref函數獲取組件中的標簽元素
功能需求: 讓輸入框自動獲取焦點
*/
export default {
  setup() {
    const inputRef = ref<HTMLElement|null>(null)

    onMounted(() => {
      inputRef.value && inputRef.value.focus()
    })

    return {
      inputRef
    }
  },
}
</script>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容