vue 組件緩存清除實踐

寫在前面
  1. 關(guān)于 vue 組件緩存的用法很簡單,官網(wǎng)教程 講解的很詳細(xì),關(guān)于 vue 組件緩存的帶來的弊端網(wǎng)上也有很多探坑的文章,最明顯的就是緩存下來的組件如果不做處理,激活的時候就會命中緩存八秃,如果你這個時候希望有新的數(shù)據(jù)獲取,可能你需要在 activated 鉤子函數(shù)中做一些處理肉盹,當(dāng)然網(wǎng)上有一些做法是通過路由的元信息來做一些處理昔驱,如果對組件緩存原理深入了解就知道那些方法可能不能徹底解決問題;
  2. 很繁瑣上忍,因為我也做過骤肛,所以我不希望在每個緩存組件中都做處理,我更希望的是窍蓝,我想隨意銷毀某個緩存組件腋颠,我想進(jìn)行的是向下緩存而不是向上緩存或者都緩存,舉個例子吓笙,現(xiàn)在有一個列表頁淑玫,詳情頁,詳情頁子頁面面睛,我希望絮蒿,我離開子頁面的時候,子頁面銷毀叁鉴,離開詳情頁的時候土涝,詳情頁銷毀;
  3. 現(xiàn)在這些都成為可能了幌墓,不是很難理解回铛,但是需要你知道 vue 組件緩存 實現(xiàn)的過程,如果不理解克锣,可以參考 vue 技術(shù)揭秘之 keep-alive,因為實現(xiàn)過程是對緩存的逆操作腔长,本文只會介紹組件銷毀的實現(xiàn)袭祟,不會拓展緩存相關(guān)內(nèi)容。
demo 場景描述
  1. 組件注冊
    全局注冊四個路由級別非嵌套的組件捞附,包含 name巾乳、 template 選項您没,部分組件包含 beforeRouteLeave 選項, 分別為 列表 1胆绊、2氨鹏、3、4
    components.png
  2. 路由配置
    額外添加的就是路由元信息 meta压状,里面包含了兩個關(guān)鍵字段 levelcompName 前者后面會說仆抵,后者是對應(yīng)的組件名稱,即取的是組件的 name 字段
    routes.png
  3. 全部配置信息种冬,這里采用的是 vue 混入
    mixins.png
  4. 頁面結(jié)構(gòu)镣丑,頂部固定導(dǎo)航條,可以導(dǎo)航到對應(yīng)的列表


    view.png
  5. 現(xiàn)在點擊導(dǎo)航欄 1娱两、2莺匠、3、4 之后查看 vue-devtools 可以看到十兢,列表 1趣竣、2、3 都被緩存下來了
    unhandler-cache-result.png
需求描述

假設(shè)上述是一個層層嵌套邏輯旱物,列表1 > 列表2 > 列表3 > 列表4 遥缕,現(xiàn)在需要在返回的時候,依次銷毀低層級的組件异袄,所謂低層級指的是相對嵌套較深的通砍,例如列表4相對于列表1、2烤蜕、3都是低層級封孙。我們先來簡單實現(xiàn)這樣的一種需求

初級緩存組件清除實現(xiàn)
  • demo 場景描述之路由配置里面,我在元信息里面添加了一個 level 字段讽营,這個字段是用來描述當(dāng)前組件的級別虎忌,level 越高代表是深層嵌套的組件,從 1 起步橱鹏;
    component-level.png
  • 下面是具體去緩存的實現(xiàn)膜蠢,封裝的去緩存方法
// util.js
function inArray(ele, array) {
  let i = array.indexOf(ele)
  let o = {
    include: i !== -1,
    index: i
  }
  return o
}
/**
 * @param {Obejct} to 目標(biāo)路由
 * @param {Obejct} from 當(dāng)前路由
 * @param {Function} next next 管道函數(shù)
 * @param {VNode} vm 當(dāng)前組件實例
 * @param {Boolean} manualDelete 是否要手動移除緩存組件,彌補當(dāng)路由缺少 level 時莉兰,清空組件緩存的不足
 */
function destroyComponent (to, from, next, vm, manualDelete = false) {
  // 禁止向上緩存
  if (
      (
        from &&
        from.meta.level &&
        to.meta.level &&
        from.meta.level > to.meta.level
      ) ||
      manualDelete
    ) {
    const { data, parent, componentOptions, key } = vm.$vnode
    if (vm.$vnode && data.keepAlive) {
      if (parent && parent.componentInstance && parent.componentInstance.cache) {
        if (componentOptions) {
          const cacheCompKey = !key ?
                      componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
                      :
                      key
          const cache = parent.componentInstance.cache
          const keys = parent.componentInstance.keys
          const { include, index } = inArray(cacheCompKey, keys)
          // 清除緩存 component'key
          if (include && cache[cacheCompKey]) {
            keys.splice(index, 1)
            delete cache[cacheCompKey]
          }
        }
      }
    }
    // 銷毀緩存組件
    vm.$destroy()
  }
  next()
}
// 你可以把它掛載到 vue 原型上
Vue.prototype.$dc = destroyComponent
  • 然后你在全局混入的 beforeRouteLeave 鉤子函數(shù)里面執(zhí)行該方法了挑围, 最后一個參數(shù)允許你在組件內(nèi)的 beforeRouteLeave 里面執(zhí)行該方法來直接銷毀當(dāng)前組件
    remove-cache-method-1.png
  • 上述方法通過對比兩個組件之間級別(level),符合條件就會從緩存列表(cache, keys)中刪除緩存組件糖荒,并且會調(diào)用 $destroy 方法徹底銷毀緩存杉辙。
  • 雖然該方法能夠?qū)崿F(xiàn)上面的簡單邏輯,也能實現(xiàn)手動控制銷毀捶朵,但是有一些問題存在:
    1. 手動銷毀的時候蜘矢,只能銷毀當(dāng)前組件狂男,不能銷毀指定的某個緩存組件或者某些緩存組件
    2. 只會判斷目標(biāo)組件和當(dāng)前組件的級別關(guān)系,不能判斷在兩者之間緩存的組件是否要移除品腹,例如岖食,列表1、2舞吭、3 均緩存了泡垃,如果直接從列表3跳到列表1,那么列表2是沒有處理的镣典,還是處于緩存狀態(tài)的兔毙;
    3. 邊界情況,即如果目標(biāo)組件和當(dāng)前組件以及一樣兄春,當(dāng)前組件也不會銷毀澎剥,雖然你可以修正為 from.meta.level >= to.meta.level 但是有時候可能需要這樣的信息是可配置的
清除緩存的進(jìn)階
  • 為了解決上面的問題,下面是一個新的方案:既支持路由級別組件緩存的清除赶舆,又支持能定向清除某個或者一組緩存組件哑姚,且允許你調(diào)整整個項目清除緩存的邏輯;
  • 創(chuàng)建一個包含緩存存儲芜茵、配置以及清空方法的對象
// util.js
function inArray(ele, array) {
  let i = array.indexOf(ele)
  let o = {
    include: i !== -1,
    index: i
  }
  return o
}

function isArray (array) {
  return Array.isArray(array)
}

const hasOwnProperty = Object.prototype.hasOwnProperty
function hasOwn (key, obj) {
  return hasOwnProperty.call(obj, key)
}
// 創(chuàng)建管理緩存的對象
class manageCachedComponents {

  constructor () {
    this.mc_keepAliveKeys = []
    this.mc_keepAliveCache = {}
    this.mc_cachedParentComponent = {}
    this.mc_cachedCompnentsInfo = {}
    this.mc_removeCacheRule = {
      // 默認(rèn)為 true叙量,即代表會移除低于目標(biāo)組件路由級別的所有緩存組件,
      // 否則如果當(dāng)前組件路由級別低于目標(biāo)組件路由級別九串,只會移除當(dāng)前緩存組件
      removeAllLowLevelCacheComp: true,
      // 邊界情況绞佩,默認(rèn)是 true, 如果當(dāng)前組件和目標(biāo)組件路由級別一樣猪钮,是否清除當(dāng)前緩存組件
      removeSameLevelCacheComp: true
    }
  }

  /**
   * 添加緩存組件到緩存列表
   * @param {Object} Vnode 當(dāng)前組件實例
   */
  mc_addCacheComponentToCacheList (Vnode) {
    const { mc_cachedCompnentsInfo } = this
    const { $vnode, $route, includes } = Vnode
    const { componentOptions, parent } = $vnode
    const componentName = componentOptions.Ctor.options.name
    const compName = `cache-com::${componentName}`
    const { include } = inArray(componentName, includes)
    if (parent && include && !hasOwn(compName, mc_cachedCompnentsInfo)) {
      const { keys, cache } = parent.componentInstance
      const key = !$vnode.key
                  ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
                  : $vnode.key
      const routeLevel = $route.meta.level
      mc_cachedCompnentsInfo[compName] = {
        // 組件名稱
        componentName,
        // 緩存組件的 key
        key,
        // 組件路由級別
        routeLevel
      }
      // 所有緩存組件 key 的列表
      this.mc_keepAliveKeys = keys
      // 所有緩存組件 key-value 集合
      this.mc_keepAliveCache = cache
      // 所有緩存組件的父實例
      this.mc_cachedParentComponent = parent
    }
  }

  // 移除緩存 key
  mc_removeCacheKey (key, keys) {
    const { include, index } = inArray(key, keys)
    if (include) {
      return keys.splice(index, 1)
    }
  }

  /**
   * 從 keep-alive 實例的 cache 移除緩存組件并移除緩存 key
   * @param {String} key 緩存組件的 key
   * @param {String} componentName 要清除的緩存組件名稱
   */
  mc_removeCachedComponent (key, componentName) {
    const { mc_keepAliveKeys, mc_cachedParentComponent, mc_cachedCompnentsInfo } = this
    const { componentInstance } = mc_cachedParentComponent
    // 緩存組件 keep-alive 的 cache 和  keys
    const cacheList = componentInstance.cache
    const keysList = componentInstance.keys
    const { include } = inArray(key, keysList)
    if (include && cacheList[key]) {
      this.mc_removeCacheKey(key, keysList)
      this.mc_removeCacheKey(key, mc_keepAliveKeys)
      cacheList[key].componentInstance.$destroy()
      delete cacheList[key]
      delete mc_cachedCompnentsInfo[componentName]
    }
  }

  /**
   * 根據(jù)組件名稱移除指定的組件
   * @param {String|Array} componentName 要移除的組件名稱或者名稱列表
   */
  mc_removeCachedByComponentName (componentName) {
    if (!isArray(componentName) && typeof componentName !== 'string') {
      throw new TypeError(`移除的組件可以是 array 或者 string品山,當(dāng)前類型為: ${typeof componentName}`)
    }
    const { mc_cachedCompnentsInfo } = this
    if (isArray(componentName)) {
      const unKnowComponents = []
      for (const name of componentName) {
        const compName = `cache-com::${name}`
        if (hasOwn(compName, mc_cachedCompnentsInfo)) {
          const { key } = mc_cachedCompnentsInfo[compName]
          this.mc_removeCachedComponent(key, compName)
        } else {
          unKnowComponents.push(name)
        }
      }
      // 提示存在非緩存組件
      if (unKnowComponents.length) {
        let tips = unKnowComponents.join(` && `)
        console.warn(`${tips} 組件非緩存組件,請在移除緩存列表中刪除以上組件名`)
      }
      return
    }

    const compName = `cache-com::${componentName}`
    if (hasOwn(compName, mc_cachedCompnentsInfo)) {
      const { key } = mc_cachedCompnentsInfo[compName]
      this.mc_removeCachedComponent(key, compName)
    } else {
      console.warn(`${componentName} 組件非緩存組件烤低,請?zhí)砑诱_的緩存組件名`)
    }
  }

  /**
   * 移除路由級別的緩存組件
   * @param {Object} toRoute 跳轉(zhuǎn)路由記錄
   * @param {Object} Vnode 當(dāng)前組件實例
   */
  mc_removeCachedByComponentLevel (toRoute, Vnode) {
    const { level, compName } = toRoute.meta
    const { mc_cachedCompnentsInfo, mc_removeCacheRule } = this
    const componentName = Vnode.$vnode.componentOptions.Ctor.options.name
    // exp-1-目標(biāo)組件非緩存組件肘交,不做處理,但可以根據(jù)業(yè)務(wù)邏輯結(jié)合 removeCachedByComponentName 函數(shù)來處理
    // exp-2-目標(biāo)組件是緩存組件扑馁,但是未添加 level涯呻,會默認(rèn)你一直緩存,不做處理
    // exp-3-當(dāng)前組件非緩存組件腻要,目標(biāo)組件為緩存組件复罐,不做處理,參考 exp-1 的做法
    // 以下邏輯只確保是兩個緩存組件之間的跳轉(zhuǎn)
    if (
        level &&
        compName &&
        mc_cachedCompnentsInfo['cache-com::' + compName] &&
        mc_cachedCompnentsInfo['cache-com::' + componentName]
      ) {
      const { removeAllLowLevelCacheComp, removeSameLevelCacheComp } = mc_removeCacheRule
      if (removeAllLowLevelCacheComp) {
        const cachedCompList = []
        // 查找所有不小于當(dāng)前組件路由級別的緩存組件雄家,即代表要銷毀的組件
        for (const cacheItem in mc_cachedCompnentsInfo) {
          const { componentName, routeLevel } = mc_cachedCompnentsInfo[cacheItem]
          if (
              // 排除目標(biāo)緩存組件市栗,不希望目標(biāo)組件也被刪除
              // 雖然會在 activated 鉤子函數(shù)里面重新添加到緩存列表
              componentName !== compName &&
              Number(routeLevel) >= level &&
              // 邊界處理
              removeSameLevelCacheComp
            ) {
              cachedCompList.push(mc_cachedCompnentsInfo[cacheItem])
          }
        }

        if (cachedCompList.length) {
          cachedCompList.forEach(cacheItem => {
            const { key, componentName } = cacheItem
            const compName = 'cache-com::' + componentName
            this.mc_removeCachedComponent(key, compName)
          })
        }
        return
      }
      // 只移除當(dāng)前緩存組件
      const { routeLevel } = mc_cachedCompnentsInfo['cache-com::' + componentName]
      if (Number(routeLevel) >= level && removeSameLevelCacheComp) {
        this.mc_removeCachedByComponentName(componentName)
      }
    }
  }
}
// 你可以把它掛載到 vue 原型上
Vue.prototype.$mc = new manageCachedComponents()
  • 使用起來非常簡單,只需要你在全局的 activated 函數(shù)里面執(zhí)行添加緩存方法,在全局 beforeRouteLeave 里面執(zhí)行移除方法方法即可
    remove-cache-method-2.png

    你還可以在組件內(nèi)的 beforeRouteLeave 鉤子函數(shù)里面執(zhí)行移除某些組件的邏輯
    remove-custom-cache.png
  • 使用上述方法需要注意的事項是
    1. 給緩存組件添加組件名稱填帽;
    2. 需要在路由記錄里面配置好 compName 選項,并且組織好你的 level咙好,因為在實際業(yè)務(wù)比 demo 復(fù)雜很多篡腌;
    3. 緩存組件會激活 activated 鉤子,你需要在該函數(shù)里面執(zhí)行添加緩存的方法勾效,不然整個清緩存是不起作用的嘹悼;
    4. 默認(rèn)的清除規(guī)則是移除所有低層級的緩存組件(即緩存組件列表1、2层宫、3杨伙,從列表3跳到列表1,列表2萌腿、3均會清除)限匣;
    5. 邊界情況的也會清除(即如果列表2、3 的 level 相同毁菱,從列表3跳到列表2米死,會清除列表3的緩存);
  • 你可能注意到了一個問題贮庞,在整個項目中配置不支持動態(tài)修改的峦筒,即在整個項目中緩存移除的規(guī)則是不同時支持兩種模式的,不想麻煩做是因為 vue 混入的緣故窗慎,全局的 beforeRouteLeave 會在組件內(nèi) beforeRouteLeave 之前執(zhí)行物喷,所以你懂得...不過你無需擔(dān)心有死角的清除問題,因為你可以通過 mc_removeCachedByComponentName 該方法來清除任意你想要銷毀的組件遮斥。
2019/05/04 - 新增對 TS 支持
  • 如果你是 vue + ts 的開發(fā)方式峦失,可以采用下面的方式,由于當(dāng)前 vue (或者 <2.6.10)的版本對 ts 支持不是很好伏伐,所以大部分是采用 vue-shims.d.ts 的方式來進(jìn)行模塊拓展宠进,更多的使用細(xì)節(jié)可參考 vue 官網(wǎng)對 Typescript 的支持 以及 Typescript 模塊拓展
  • 下面是文件相對位置關(guān)系


    file.png
  • vue-shims.d.ts 文件內(nèi)容
/*
 * @description: 模塊拓展類型定義文件
 */
import Vue, { VNode } from 'vue'
import { Route } from 'vue-router'
import ManageCachedComponents from './clear-cache'

export type ElementType = string | number

export interface KeepAliveCachedComponent {
  [key: string]: VNode
}

interface CtorOptions {
  name: string
  [key: string]: any
}

declare module 'vue/types/vue' {
  interface Vue {
    $route: Route
    $mc: ManageCachedComponents
    includes: string[]
    keys?: ElementType[]
    cache?: KeepAliveCachedComponent
  }
  interface VueConstructor {
    cid: number
    options: CtorOptions
  }
}
  • cache-clear.ts 文件
/*
 * @description: TS 版本的緩存移除
 */

import Vue, { VNode } from 'vue'
import { Route } from 'vue-router'
import { ElementType } from './vue-shim'

interface CachedComponentList {
  componentName: string,
  key: string,
  routeLevel: number
}

interface RemoveCachedRules {
  removeAllLowLevelCacheComp: boolean
  removeSameLevelCacheComp: boolean
}

const hasOwnProperty = Object.prototype.hasOwnProperty

const inArray = (ele: ElementType, array: ElementType[]) => {
  const i = array.indexOf(ele)
  const o = {
    include: i !== -1,
    index: i
  }
  return o
}

const isArray = (array: any) => {
  return Array.isArray(array)
}

const hasOwn = (key: ElementType, obj: object) => {
  return hasOwnProperty.call(obj, key)
}

export default class ManageCachedComponents {
  private mc_keepAliveKeys: ElementType[] = []
  private mc_cachedParentComponent: VNode = <VNode>{}
  private mc_cachedComponentsInfo: CachedComponentList = <CachedComponentList>{}
  public mc_removeCacheRule: RemoveCachedRules = {
    removeAllLowLevelCacheComp: true,
    removeSameLevelCacheComp: true
  }

  /**
   * 從緩存列表中移除 key
   */
  private mc_removeCacheKey (key: ElementType, keys: ElementType[]) {
    const { include, index } = inArray(key, keys)
    include && keys.splice(index, 1)
  }

  /**
   * 從 keep-alive 實例的 cache 移除緩存組件并移除緩存 key
   * @param key 緩存組件的 key
   * @param componentName 要清除的緩存組件名稱
   */
  private mc_removeCachedComponent (key: string, componentName: string) {
    const { mc_keepAliveKeys, mc_cachedParentComponent, mc_cachedComponentsInfo } = this
    const { componentInstance } = mc_cachedParentComponent
    const cacheList = componentInstance.cache
    const keysList = componentInstance.keys
    const { include } = inArray(key, keysList)
    if (include && cacheList[key]) {
      this.mc_removeCacheKey(key, keysList)
      this.mc_removeCacheKey(key, mc_keepAliveKeys)
      cacheList[key].componentInstance.$destroy()
      delete cacheList[key]
      delete mc_cachedComponentsInfo[componentName]
    }
  }

  /**
   * 添加緩存組件到緩存列表
   * @param Vue 當(dāng)前組件實例
   */
  mc_addCacheComponentToCacheList (Vue: Vue) {
    const { mc_cachedComponentsInfo } = this
    const { $vnode, $route, includes } = Vue
    const { componentOptions, parent } = $vnode
    const componentName = componentOptions.Ctor.options.name
    const compName = `cache-com::${componentName}`
    const { include } = inArray(componentName, includes)
    if (parent && include && !hasOwn(compName, mc_cachedComponentsInfo)) {
      const { keys, cache } = parent.componentInstance
      const key = !$vnode.key
                  ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
                  : $vnode.key
      const routeLevel = $route.meta.level
      mc_cachedComponentsInfo[compName] = {
        componentName,
        key,
        routeLevel
      }
      this.mc_keepAliveKeys = keys
      this.mc_cachedParentComponent = parent
    }
  }

  /**
   * 根據(jù)組件名稱移除指定的組件
   * @param componentName 要移除的組件名稱或者名稱列表
   */
  mc_removeCachedByComponentName (componentName: string | string[]) {
    if (!isArray(componentName) && typeof componentName !== 'string') {
      throw new TypeError(`移除的組件可以是 array 或者 string,當(dāng)前類型為: ${typeof componentName}`)
    }
    const { mc_cachedComponentsInfo } = this
    if (isArray(componentName)) {
      const unKnowComponents = []
      for (const name of componentName) {
        const compName = `cache-com::${name}`
        if (hasOwn(compName, mc_cachedComponentsInfo)) {
          const { key } = mc_cachedComponentsInfo[compName]
          this.mc_removeCachedComponent(key, compName)
        } else {
          unKnowComponents.push(name)
        }
      }
      // 提示存在非緩存組件
      if (unKnowComponents.length) {
        let tips = unKnowComponents.join(` && `)
        console.warn(`${tips} 組件非緩存組件藐翎,請在移除緩存列表中刪除以上組件名`)
      }
      return
    }

    const compName = `cache-com::${componentName}`
    if (hasOwn(compName, mc_cachedComponentsInfo)) {
      const { key } = mc_cachedComponentsInfo[compName]
      this.mc_removeCachedComponent(key, compName)
    } else {
      console.warn(`${componentName} 組件非緩存組件材蹬,請?zhí)砑诱_的緩存組件名`)
    }
  }

  /**
   * 移除路由級別的緩存組件
   * @param toRoute 跳轉(zhuǎn)路由記錄
   * @param Vue 當(dāng)前組件實例
   */
  mc_removeCachedByComponentLevel (toRoute: Route, Vue: Vue) {
    const { level, compName } = toRoute.meta
    const { mc_cachedComponentsInfo, mc_removeCacheRule } = this
    const componentName = Vue.$vnode.componentOptions.Ctor.options.name
    if (
        level &&
        compName &&
        mc_cachedComponentsInfo['cache-com::' + compName] &&
        mc_cachedComponentsInfo['cache-com::' + componentName]
      ) {
      const { removeAllLowLevelCacheComp, removeSameLevelCacheComp } = mc_removeCacheRule
      if (removeAllLowLevelCacheComp) {
        const cachedCompList = []
        for (const cacheItem in mc_cachedComponentsInfo) {
          const { componentName, routeLevel } = mc_cachedComponentsInfo[cacheItem]
          if (
              componentName !== compName &&
              Number(routeLevel) >= level &&
              removeSameLevelCacheComp
            ) {
              cachedCompList.push(mc_cachedComponentsInfo[cacheItem])
          }
        }

        if (cachedCompList.length) {
          cachedCompList.forEach(cacheItem => {
            const { key, componentName } = cacheItem
            const compName = 'cache-com::' + componentName
            this.mc_removeCachedComponent(key, compName)
          })
        }
        return
      }
      const { routeLevel } = mc_cachedComponentsInfo['cache-com::' + componentName]
      if (Number(routeLevel) >= level && removeSameLevelCacheComp) {
        this.mc_removeCachedByComponentName(componentName)
      }
    }
  }
}
  • 如果 vue3.0 出來以后,就不需要 vue-shims.d.ts 文件了吝镣,到時候使用 ts 會更加方便堤器,當(dāng)然更希望尤大能夠增加緩存操作的 api,這樣就不再為了緩存而造各種輪子了末贾。
寫在最后
  • 這篇文章主要參考的是 vue組件緩存源碼闸溃,感興趣的可以看一下;
  • 本文為原創(chuàng)文章,如果需要轉(zhuǎn)載辉川,請注明出處表蝙,方便溯源,如有錯誤地方乓旗,可以在下方留言府蛇,歡迎斧正,demo 已經(jīng)上傳到 關(guān)于vue緩存清除的個人git倉庫
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屿愚,一起剝皮案震驚了整個濱河市汇跨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌妆距,老刑警劉巖穷遂,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異娱据,居然都是意外死亡蚪黑,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門吸耿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祠锣,“玉大人,你說我怎么就攤上這事咽安“橥” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵妆棒,是天一觀的道長澡腾。 經(jīng)常有香客問我,道長糕珊,這世上最難降的妖魔是什么动分? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮红选,結(jié)果婚禮上澜公,老公的妹妹穿的比我還像新娘。我一直安慰自己喇肋,他們只是感情好坟乾,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蝶防,像睡著了一般甚侣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上间学,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天殷费,我揣著相機(jī)與錄音印荔,去河邊找鬼。 笑死详羡,一個胖子當(dāng)著我的面吹牛仍律,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播实柠,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼染苛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了主到?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤躯概,失蹤者是張志新(化名)和其女友劉穎登钥,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體娶靡,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡牧牢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了姿锭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片塔鳍。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖呻此,靈堂內(nèi)的尸體忽然破棺而出轮纫,到底是詐尸還是另有隱情,我是刑警寧澤焚鲜,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布掌唾,位于F島的核電站,受9級特大地震影響忿磅,放射性物質(zhì)發(fā)生泄漏糯彬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一葱她、第九天 我趴在偏房一處隱蔽的房頂上張望撩扒。 院中可真熱鬧,春花似錦吨些、人聲如沸搓谆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挽拔。三九已至,卻和暖如春但校,著一層夾襖步出監(jiān)牢的瞬間螃诅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留术裸,地道東北人倘是。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像袭艺,于是被迫代替她去往敵國和親搀崭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • # 傳智播客vue 學(xué)習(xí)## 1. 什么是 Vue.js* Vue 開發(fā)手機(jī) APP 需要借助于 Weex* Vu...
    再見天才閱讀 3,540評論 0 6
  • vue筆記 一.vue實例 vue的生命周期 beforeCreate(創(chuàng)建前), created(創(chuàng)建后), b...
    秋殤1002閱讀 1,054評論 0 1
  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容猾编,還有我對于 Vue 1.0 印象不深的內(nèi)容瘤睹。關(guān)于...
    云之外閱讀 5,050評論 0 29
  • VUE Vue :數(shù)據(jù)驅(qū)動的M V Vm框架 m :model(后臺提供數(shù)據(jù)),v :view(頁面)答倡,vM(模板...
    wudongyu閱讀 5,401評論 0 11
  • 一:什么是閉包轰传?閉包的用處? (1)閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)瘪撇。在本質(zhì)上获茬,閉包就 是將函數(shù)內(nèi)部和函數(shù)外...
    xuguibin閱讀 9,602評論 1 52