09Vue 項(xiàng)目最佳實(shí)踐

09項(xiàng)目最佳實(shí)踐

資源:

項(xiàng)目配置策略

基礎(chǔ)配置:指定應(yīng)用上下文锭魔、端口號(hào)棒仍,vue.config.js

const port = 8080

module.exports = {
  publicPath: '/best-practice', // 部署應(yīng)用包時(shí)的基本 URL
  devServer: {
    port,
  }
}

配置 Webpack:configureWebpack

范例:設(shè)置一個(gè)組件存放路徑的別名,vue.config.js

const path = require('path')

module.exports = {
  configureWebpack: {
    resolve: {
      alias: {
        comps: path.join(__dirname, 'src/components')
      }
    }
  }
}

范例:設(shè)置一個(gè) Webpack 配置項(xiàng)用于頁面 title,vue.config.js

modules.exports = {
  configureWebpack: {
    name:'林慕-Vue項(xiàng)目實(shí)戰(zhàn)'  
  }
}

在宿主頁面使用 lodash 插值語法使用它,./public/index.html

<title><%= webpackConfig.name %></title>

webpack-merge 合并出最終選項(xiàng)

范例:基于環(huán)境有條件地配置,vue.config.js

configureWebpack: config => {
  config.resolve.alias.comps = path.join(__dirname, 'src/components')
  if (process.env.NODE_ENV === 'development') {
    config.name = '林慕-Vue項(xiàng)目實(shí)踐'
  }else {
    config.name = 'Vue Best Practice'
  }
}

結(jié)合上面的例子可以看到径荔,Webpack 有兩種常見的配置方式,第一種是結(jié)合某些特性直接修改 configureWebpack脆霎,第二種是傳遞一個(gè)函數(shù)給 configureWebpack总处,返回一個(gè)用于合并的配置對(duì)象。

配置 Webpack:chainWebpack 稱為鏈?zhǔn)讲僮骶χ耄梢愿?xì)粒度控制 Webpack 內(nèi)部配置鹦马。

范例:svg icon 引入

npm i svg-sprite-loader -D
  • 修改規(guī)則和新增規(guī)則忆肾,vue.config.js
// resolve 定義一個(gè)絕對(duì)路徑獲取函數(shù)
const path = require('path')

function resolve(dir) {
  return path.join(__dirname, dir)
}
// 鏈?zhǔn)脚渲?chainWebpack(config) {
  // 配置 svg 規(guī)則排除 icons 目錄中 svg 文件處理
  // 目標(biāo)給 svg 規(guī)則增加一個(gè)排除選項(xiàng) exclude:['path/to/icon']
  config.module.rule('svg')
    .exclude.add(resolve('src/icons'))

  // 新增 icons 規(guī)則荸频,設(shè)置 svg-sprite-loader 處理 icons 目錄中的 svg
  config.module.rule('icons')
    .test(/\.svg$/)
    .include.add(resolve('./src/icons')).end()
    .use('svg-sprite-loader')
    .loader('svg-sprite-loader')
    .options({symbolId: 'icon-[name]'})
}
  • 使用圖標(biāo),App.vue
<template>
  <svg>
    <use xlink:href='#icon-wx' />
  </svg>
</template>
<script>
import '@/icons/svg/wx.svg'
</script>
  • 自動(dòng)導(dǎo)入
  1. 創(chuàng)建 icons/index.js

自動(dòng)化加載 svg 目錄下的所有 svg 文件客冈,使用 Webpack 提供require.context() 指定 svg 為固定上下文疾渣。

import Vue from ’vue'
import SvgIcon from '@/components/SvgIcon.vue'

const req = require.context('./svg', false, /\.svg$/)
// keys返回上下文中所有文件名
req.keys().map(req)

// 注冊(cè) svg-icon 組件
Vue.component('svg-icon', SvgIcon)
  1. 創(chuàng)建 SvgIcon 組件惠啄,components/SvgIcon.vue
<template>
  <svg :class="svgClass" v-on="$listeners">
    <use :xlink:href="iconName" />
  </svg>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    iconClass: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: ''
    }
  },
  computed: {
    iconName() {
      return `#icon-${this.iconClass}`
    },
    svgClass() {
      if(this.className) {
        return 'svg-icon' + this.className
      }  else {
        return 'svg-icon'
      }
    }
  }
}
</script>

<style scoped>
.svg-icon{
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>

使用svg

<svg-icon icon-class="wx"></svg-icon>

環(huán)境變量和模式

如果想給多種環(huán)境做不同配置吧趣,可以利用 Vue-cli 提供的模式仁热。默認(rèn)有 development、production渠缕、test 三種模式鸽素,對(duì)應(yīng)的,它們的配置文件形式是 .env.development亦鳞。

范例:定義一個(gè)開發(fā)時(shí)可用的配置項(xiàng)馍忽,創(chuàng)建 .env.dev

# 只能用于服務(wù)器
foo=bar
# 可用于客戶端
VUE_APP_DONG=dong

注:如果需要在客戶端加多個(gè)變量棒坏,需要以 VUE_APP_xxx 這種形式,VUE_APP 為前綴遭笋。

修改 mode 選項(xiàng)覆蓋模式名稱坝冕,package.json

"serve": "vue-cli-service serve --mode dev"

權(quán)限控制

路由分為兩種:constantRoutes 和 asyncRoutes,前者是默認(rèn)路由可直接訪問坐梯,后者中定義的路由需要先登錄徽诲,獲取角色并過濾后動(dòng)態(tài)加入到 Router 中。

image.png
  • 路由定義吵血,router/index.js

  • 創(chuàng)建用戶登錄頁面,views/Login.vue

  • 路由守衛(wèi):創(chuàng)建 ./src/permission.js偷溺,并在 main.js 引入

用戶登錄狀態(tài)維護(hù)

維護(hù)用戶登錄狀態(tài):路由守衛(wèi) => 用戶登錄 => 獲取 token 并緩存

image.png
  • 路由守衛(wèi):src/permission.js

  • 請(qǐng)求登錄:components/Login.vue

  • user 模塊:維護(hù)用戶數(shù)據(jù)蹋辅、處理用戶登錄等,store/modules/user.js

  • 測(cè)試

用戶角色獲取和權(quán)限路由過濾

登錄成功后挫掏,請(qǐng)求用戶信息獲取用戶角色信息侦另,然后根據(jù)角色過濾 asyncRoutes,并將結(jié)果動(dòng)態(tài)添加至 router

  • 維護(hù)路由信息尉共,實(shí)現(xiàn)動(dòng)態(tài)路由生成邏輯褒傅,store/modules/permission.js

  • 獲取用戶角色,判斷用戶是否擁有訪問權(quán)限袄友,permission.js

// 引入store
import store from './store'

router.beforeEach(async (to, from, next) => {
  //...
  if (hasToken) {
    if (to.path === '/login') { }
    else {
      // 若用戶角色已附加則說明權(quán)限以判定殿托,動(dòng)態(tài)路由已添加
      const hasRoles = store.getters.roles && store.getters.roles.length > 0

      if (hasRoles) {
        // 說明用戶以獲取過角色信息,放行
        next()
      } else {
        try {
          // 先請(qǐng)求獲取用戶信息
          const { roles } = await store.dispatch('user/getInfo')
          // 根據(jù)當(dāng)前用戶角色過濾出可訪問路由
          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
          // 添加至路由器
          router.addRoutes(accessRoutes)
          // 繼續(xù)路由切換剧蚣,確保addRoutes完成
          next({ ...to, replace: true })
        } catch (error) {
          // 出錯(cuò)需重置令牌并重新登錄(令牌過期支竹、網(wǎng)絡(luò)錯(cuò)誤等原因)
          await store.dispatch('user/resetToken')
          next(`/login?redirect=${to.path}`)
          alert(error || '未知錯(cuò)誤')
        }
      }
    }
  } else {
    // 未登錄...
  }
})

異步獲取路由表

可以當(dāng)用戶登錄后向后端請(qǐng)求可訪問的路由表,從而動(dòng)態(tài)生成可訪問頁面鸠按,操作和原來是相同的礼搁,這里多了一步將后端返回路由表中組件名稱和本地的組件映射步驟:

// 前端組件名和組件映射表
const map = {
  // xx:require('@/views/xx.vue').default // 同步的方式
  xx: () => import('@/views/xx.vue') // 異步的方式
}
// 服務(wù)端返回的asyncRoutes
const asyncRoutes = [
  {
    path: '/xx', component: 'xx'
  }
]
// 遍歷asyncRoutes,將component替換為map[component]
function mapComponent (asyncRoutes) {
  asyncRoutes.forEach(route => {
    route.component = map[route.component]
    if (route.children) {
      route.children.map(child => mapComponent(child))
    }
  })
}

mapComponent(asyncRoutes)

按鈕權(quán)限

頁面中某些按鈕、鏈接有時(shí)候需要更細(xì)粒度權(quán)限控制目尖,這時(shí)候可以封裝一個(gè)指令 v-permission,放在需要控制的按鈕上瑟曲,從而實(shí)現(xiàn)按鈕級(jí)別權(quán)限控制。

  • 創(chuàng)建指令莹捡,src/directives/permission.js

  • 測(cè)試,About.vue

該指令只能管控掛載指令的元素扣甲,對(duì)于那些額外生成的和指令無關(guān)的元素?zé)o能為力齿椅,比如:

<el-tabs>
<el-tab-pane label="?戶管理" name="first" v-permission="['admin',
'editor']">
?戶管理</el-tab-pane>
<el-tab-pane label="配置管理" name="second" v-permission="['admin',
'editor']">
配置管理</el-tab-pane>
<el-tab-pane label="角色管理" name="third" v-permission="['admin']">
??管理</el-tab-pane>
<el-tab-pane label="定時(shí)任務(wù)補(bǔ)償" name="fourth" v-permission="['admin',
'editor']">
定時(shí)任務(wù)補(bǔ)償</el-tab-pane>
</el-tabs>

此時(shí)只能使用 v-if 來實(shí)現(xiàn)

<template>
  <el-tab-pane v-if="checkPermission(['admin'])">
</template>

<script>
export default {
  methods:{
    checkPermission(permissionRoles){
      return roles.some(role =>{
        return permissionRoles.includes(role)
      })
    }
  }
}
</script>

自定義指令參考

自動(dòng)生成導(dǎo)航菜單

導(dǎo)航菜單是根據(jù)路由信息并結(jié)合權(quán)限判斷而動(dòng)態(tài)生成的启泣,它需要對(duì)應(yīng)路由的多級(jí)嵌套涣脚,所以要用到遞歸組件。

  • 創(chuàng)建側(cè)邊欄組件寥茫,components/Sidebar/index.vue

  • 創(chuàng)建側(cè)邊欄項(xiàng)目組件,layout/components/Sidebar/SidebarItem.vue

  • 創(chuàng)建側(cè)邊欄菜單項(xiàng)組件芭梯,layout/components/Sidebar/Item.vue

數(shù)據(jù)交互

數(shù)據(jù)交互流程:

API 服務(wù) => axios 請(qǐng)求 => 本地 mock/線上 mock/服務(wù)器 api

封裝 request

對(duì) axios 做一次封裝弄喘,統(tǒng)一處理配置、請(qǐng)求和響應(yīng)攔截累奈。

安裝 axios:

npm i axios -S
  • 創(chuàng)建 @/utils/request.js

  • 設(shè)置 VUE_APP_BASE_API 環(huán)境變量急但,創(chuàng)建 .env.development 文件

  • 編寫服務(wù)接口,創(chuàng)建 @/api/user.js

數(shù)據(jù) mock

數(shù)據(jù)模擬兩種常見方式戒努,本地 mock 和線上 easy-mock突委。

本地 mock:利用 webpack-dev-server 提供的 before 鉤子可以訪問 express 實(shí)例,從而定義接口缘缚。

  • 修改 vue.config.js敌蚜,給 devServer 添加相關(guān)代碼

  • 調(diào)用接口,@/store/modules/user.js

線上 esay-mock

諸如 easy-mock 這類線上 mock 工具優(yōu)點(diǎn)是使用簡(jiǎn)單齐媒,mock 工具庫也比較強(qiáng)大纷跛,還能根據(jù) swagger 規(guī)范生成接口喻括。

使用步驟:

  1. 登錄 easy-mock

若遠(yuǎn)程不可用,可以搭建本地 easy-mock 服務(wù)(nvm + node + redis + mongodb)

先安裝 node 8.x贫奠、redis 和 mongodb

啟動(dòng)命令:

  • 切 node v8: nvm list , nvm use 8.16.0

  • 起 redis: redis-server

  • 起 mongodb: mongod

  • 起 easy-mock 項(xiàng)目: npm run dev

  1. 創(chuàng)建一個(gè)項(xiàng)目

  2. 創(chuàng)建需要的接口

// user/login
{
  "code": function({ _req }) {
    const { username } = _req.body;
    if (username === "admin" || username === "jerry") {
      return 1
    } else {
      return 10008
    }
  },
  "data": function({ _req }) {
    const { username } = _req.body;
    if (username === "admin" || username === "jerry") {
      return username
    } else {
      return ''
    }
  }
}
// user/info
{
  code: 1,
    "data": function({ _req }) {
      return _req.headers['authorization'].split(' ')[1] === 'admin' ?
        ['admin'] : ['editor']
    }
}
  1. 調(diào)用:修改 base_url脖律,.env.development
VUE_APP_BASE_API = 'http://localhost:7300/mock/5e9032aab92b8c71eb235ad5'

解決跨域

如果請(qǐng)求的接口在另一臺(tái)服務(wù)器上腕侄,開發(fā)時(shí)則需要設(shè)置代理避免跨域問題冕杠。

  • 添加代理配置,vue.config.js

  • 創(chuàng)建一個(gè)獨(dú)立接口服務(wù)器柒桑,~/server/index.js

項(xiàng)目測(cè)試

測(cè)試分類

常見的開發(fā)流程里噪舀,都有測(cè)試人員飘诗,他們不管內(nèi)部實(shí)現(xiàn)機(jī)制昆稿,只看最外層的輸入輸出,這種我們成為黑盒測(cè)試净响。比如你寫一個(gè)加法的頁面喳瓣,會(huì)設(shè)計(jì) N 個(gè)用例,測(cè)試加法的正確性配乓,這種測(cè)試我們稱之為 E2E測(cè)試惠毁。

還有一種測(cè)試叫做白盒測(cè)試鞠绰,我們針對(duì)一些內(nèi)部核心實(shí)現(xiàn)邏輯編寫測(cè)試代碼,稱之為單元測(cè)試屿笼。

更負(fù)責(zé)一些的我們稱之為集成測(cè)試,就是集合多個(gè)測(cè)試過的單元一起測(cè)試志电。

組件的單元測(cè)試有很多好處:

  • 提供描述組件行為的文檔

  • 節(jié)省手動(dòng)測(cè)試的時(shí)間

  • 減少研發(fā)新特性時(shí)產(chǎn)生的 bug

  • 改進(jìn)設(shè)計(jì)

  • 促進(jìn)重構(gòu)

準(zhǔn)備工作

在 vue-cli 中蛔趴,預(yù)置了 Mocha + Chai 和 Jest 兩套單測(cè)方案孝情,我們的演示代碼使用 Jest,他們語法基本一致魁亦。

新建 Vue 項(xiàng)目時(shí)

  • 選擇特性 Unit Testing 和 E2E Testing
image.png
  • 單元測(cè)試解決方案選擇:Jest
image.png
  • 端到端測(cè)試解決方案選擇:Cypress
image.png

在已存在項(xiàng)目中集成

集成 Jest:

vue add @vue/unit-jest

集成 cypress:

vue add @vue/e2e-cypress

編寫單元測(cè)試

單元測(cè)試:是指對(duì)軟件中的最小測(cè)試單元進(jìn)行檢查和驗(yàn)證洁奈。

  • 新建 test/unit/test.spec.js利术,*.spec.js 是命名規(guī)范

function add (num1, num2) {
  return num1 + num2
}

// 測(cè)試套件 test suite
describe('test', () => {
  // 測(cè)試用例 test case
  it('測(cè)試add函數(shù)', () => {
    // 斷言 assert
    expect(add(1, 3)).toBe(3)
    expect(add(1, 3)).toBe(4)
    expect(add(-2, 3)).toBe(1)
  })
})

執(zhí)行單元測(cè)試

  • 執(zhí)行:
npm run test:unit

斷言 API 簡(jiǎn)介

  • describe:定義一個(gè)測(cè)試套件

  • it:定義一個(gè)測(cè)試用例

  • expect:斷言的判斷條件

剛剛僅展示了 toBe低矮,更多斷言 API

測(cè)試 Vue 組件

Vue 官方提供了用于單元測(cè)試的實(shí)用工具庫 @vue/test-utils

  • 創(chuàng)建一個(gè) Vue 組件 components/Kaikeba.vue

  • 測(cè)試該組件军掂,test/unit/kaikeba.spec.js

import Kaikeba from '@/components/Kaikeba.vue'
describe('Kaikeba.vue', () => {
  // 檢查組件選項(xiàng)
  it('要求設(shè)置created?命周期', () => {
    expect(typeof Kaikeba.created).toBe('function')
  })
  it('message初始值是vue-test', () => {
    // 檢查data函數(shù)存在性
    expect(typeof Kaikeba.data).toBe('function')
    // 檢查data返回的默認(rèn)值
    const defaultData = Kaikeba.data()
    expect(defaultData.message).toBe('vue-test')
  })
})
image.png

檢查 mounted 之后預(yù)期結(jié)果

使用 @vue/test-utils 掛載組件

import { mount } from '@vue/test-utils'
it("mount之后測(cè)data是開課吧", () => {
  const wrapper = mount(Kaikeba);
  expect(wrapper.vm.message).toBe("開課吧");
});
it("按鈕點(diǎn)擊后", () => {
  const wrapper = mount(KaikebaComp);
  wrapper.find("button").trigger("click");
  // 測(cè)試數(shù)據(jù)變化
  expect(wrapper.vm.message).toBe("按鈕點(diǎn)擊");
  // 測(cè)試html渲染結(jié)果
  expect(wrapper.find("span").html()).toBe("<span>按鈕點(diǎn)擊</span>");
  // 等效的?式
  expect(wrapper.find("span").text()).toBe("按鈕點(diǎn)擊");
});

測(cè)試覆蓋率

Jest 自帶覆蓋率蝗锥,很容易統(tǒng)計(jì)我們測(cè)試代碼是否全面。如果用的 mocha税课,需要使用 istanbul 來統(tǒng)計(jì)覆蓋率痊剖。

  • package.json 里修改 jest 配置
"jest": {
  "collectCoverage": true,
  "collectCoverageFrom": ["src/**/*.{js,vue}"],
}

若采用獨(dú)立配置陆馁,則修改 jest.config.js:

module.exports = {
  "collectCoverage": true,
  "collectCoverageFrom": ["src/**/*.{js,vue}"]
}
  • 在此執(zhí)行 npm run test:unit
image.png

%stmts 是語句覆蓋率(statement coverage):是不是每個(gè)語句都執(zhí)行了?
%Branch 分支覆蓋率(branch coverage):是不是每個(gè) if 代碼塊都執(zhí)行了击狮?
%Funcs 函數(shù)覆蓋率(function coverage):是不是每個(gè)函數(shù)都調(diào)用了?
%Lines 行覆蓋率(line coverage):是不是每一行都執(zhí)行了寸莫?

可以看到我們 kaikeba.vue 的覆蓋率是100%档冬,我們修改?下代碼:

<template>
  <div>
    <span>{{ message }}</span>
    <button @click="changeMsg">點(diǎn)擊</button>
  </div>
</template>
<script>
export default {
  data () {
    return {
      message: "vue-text",
      count: 0
    };
  },
  created () {
    this.message = "開課吧";
  },
  methods: {
    changeMsg () {
      if (this.count > 1) {
        this.message = "count?于1";
      } else {
        this.message = "按鈕點(diǎn)擊";
      }
    },
    changeCount () {
      this.count += 1;
    }
  }
};
</script>

現(xiàn)在的代碼酷誓,依然是測(cè)試沒有報(bào)錯(cuò)盐数,但是覆蓋率只有66%了,而且沒有覆蓋的代碼行數(shù)玫氢,都標(biāo)記了出來漾峡,繼續(xù)努力加測(cè)試吧。

Vue 組件單元測(cè)試 cookbook:https://cn.vuejs.org/v2/cookbook/unit-testing-vue-components.html

Vue Test Utils 使用指南:https://vue-test-utils.vuejs.org/zh/

E2E 測(cè)試

借用瀏覽器的能力,站在用戶測(cè)試人員的角度牺陶,輸入框辣之,點(diǎn)擊按鈕等,完全模擬用戶狮鸭,這個(gè)和具體的框架關(guān)系不大多搀,完全模擬瀏覽器的行為。

運(yùn)行 E2E 測(cè)試

npm run test:e2e

修改 e2e/spec/test.js

// https://docs.cypress.io/api/introduction/api.html
describe('端到端測(cè)試惯退,搶測(cè)試人員的飯碗', () => {
  it('先訪問?下', () => {
    cy.visit('/')
    // cy.contains('h1', 'Welcome to Your Vue.js App')
    cy.contains('span', '開課吧')
  })
})
image.png

測(cè)試未通過催跪,因?yàn)闆]有使用 Kaikeba.vue,修改 App.vue

<div id="app">
  <img alt="Vue logo" src="./assets/logo.png">
  <!-- <HelloWorld msg="Welcome to Your Vue.js App"/> -->
  <Kaikeba></Kaikeba>
</div>
import Kaikeba from './components/Kaikeba.vue'
export default {
  name: 'app',
  components: {
    HelloWorld,Kaikeba
  }
}

測(cè)試通過~

測(cè)試用戶點(diǎn)擊

// https://docs.cypress.io/api/introduction/api.html
describe('端到端測(cè)試荣倾,搶測(cè)試?員的飯碗', () => {
  it('先訪問?下', () => {
  cy.visit('/')
  // cy.contains('h1', 'Welcome to Your Vue.js App')
  cy.contains('#message', '開課吧')
    cy.get('button').click()
    cy.contains('span', '按鈕點(diǎn)擊')
  })
})

總結(jié)

image.png

這篇文章還是比較淺顯易懂的舌仍,像項(xiàng)目配置策略者娱,權(quán)限控制和自動(dòng)生成導(dǎo)航菜單這三個(gè)還是需要了解學(xué)習(xí)的,至于單元測(cè)試的話推姻,就見仁見智了框沟,如果你項(xiàng)目很緊急的話忍燥,單元測(cè)試和業(yè)務(wù)開發(fā)并行幾乎是不可能的。但是厂捞,如果你的項(xiàng)目對(duì)于標(biāo)準(zhǔn)化作業(yè)這一塊很看中的話队丝,單元測(cè)試還是很有必要的。

本文項(xiàng)目 demo:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末臭墨,一起剝皮案震驚了整個(gè)濱河市膘盖,隨后出現(xiàn)的幾起案子侠畔,更是在濱河造成了極大的恐慌,老刑警劉巖掺冠,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異斥黑,居然都是意外死亡眉厨,警方通過查閱死者的電腦和手機(jī)憾股,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茴恰,“玉大人斩熊,你說我怎么就攤上這事粉渠。” “怎么了霸株?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵去件,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我肠牲,道長(zhǎng)靴跛,這世上最難降的妖魔是什么梢睛? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任识椰,我火速辦了婚禮腹鹉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘愉阎。我一直安慰自己,他們只是感情好榜旦,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布溅呢。 她就那樣靜靜地躺著咐旧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铣墨。 梳的紋絲不亂的頭發(fā)上踏兜,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音肉盹,去河邊找鬼疹尾。 笑死纳本,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的繁成。 我是一名探鬼主播巾腕,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼叁鉴!你這毒婦竟也來了佛寿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤蜡饵,失蹤者是張志新(化名)和其女友劉穎袭祟,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體您没,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氨鹏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年压状,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了种冬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡莺匠,死狀恐怖十兢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情遥缕,我是刑警寧澤宵呛,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布宝穗,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏橱鹏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一挑围、第九天 我趴在偏房一處隱蔽的房頂上張望糖荒。 院中可真熱鬧,春花似錦蜘矢、人聲如沸综看。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽羡鸥。三九已至忠寻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赶舆,已是汗流浹背祭饭。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工倡蝙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人猪钮。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓胆建,卻偏偏與公主長(zhǎng)得像笆载,于是被迫代替她去往敵國(guó)和親涯呻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子腻要,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345