高頻前端面試題匯總之Vue篇

Vue共識:
在 Vue 中我們習慣把虛擬DOM稱為 VNode,它既可以代表一個 VNode 節點,也可以代表一顆 VNode 樹。
組件的核心是它能夠產出一堆VNode。
對于 Vue 來說一個組件的核心就是它的渲染函數,組件的掛載本質就是執行渲染函數并得到要渲染的VNode,至于data/props/computed 這都是為渲染函數產出 VNode 過程中提供數據來源服務的,最關鍵的就是組件最終產出的VNode,因為這個才是要渲染的內容。


vue.png

一、Vue基礎

1. Vue的基本原理

當一個Vue實例創建時,vue會遍歷data選項的屬性,用 Object.defineProperty(vue3.0使用proxy )將它們轉為 getter/setter 并且在內部追蹤相關依賴,在屬性被訪問和修改時通知變化。 每個組件實例都有相應的 watcher程序實例,它會在組件渲染的過程中把屬性記錄為依賴,之后當依賴項的setter被調用時,會通知watcher重新計算,從而致使它關聯的組件得以更新。


12cd66d0cdf5233131cc966ff9e70a37.png

2. 雙向數據綁定的原理

vue.js 是采用數據劫持結合發布者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter,getter,在數據變動時發布消息給訂閱者,觸發相應的監聽回調。主要分為以下幾個步驟:

1、需要observe的數據對象進行遞歸遍歷,包括子屬性對象的屬性,都加上setter和getter這樣的話,給這個對象的某個值賦值,就會觸發setter,那么就能監聽到了數據變化

2、compile解析模板指令,將模板中的變量替換成數據,然后初始化渲染頁面視圖,并將每個指令對應的節點綁定更新函數,添加監聽數據的訂閱者,一旦數據有變動,收到通知,更新視圖

3、Watcher訂閱者是Observer和Compile之間通信的橋梁,主要做的事情是:
①在自身實例化時往屬性訂閱器(dep)里面添加自己
②自身必須有一個update()方法
③待屬性變動dep.notice()通知時,能調用自身的update()方法,并觸發Compile中綁定的回調,則功成身退。

4、MVVM作為數據綁定的入口,整合Observer、Compile和Watcher三者,通過Observer來監聽自己的model數據變化,通過Compile來解析編譯模板指令,最終利用Watcher搭起Observer和Compile之間的通信橋梁,達到數據變化 -> 視圖更新;視圖交互變化(input) -> 數據model變更的雙向綁定效果。

3. 使用 Object.defineProperty() 來進行數據劫持有什么缺點?

有一些對屬性的操作,使用這種方法無法攔截,比如說通過下標方式修改數組數據或者給對象新增屬性,vue 內部通過重寫函數解決了這個問題。在 Vue3.0 中已經不使用這種方式了,而是通過使用 Proxy 對對象進行代理,從而實現數據劫持。使用Proxy 的好處是它可以完美的監聽到任何方式的數據改變,唯一的缺點是兼容性的問題,因為這是 ES6 的語法。

4. MVVM和MVC的區別

MVC、MVP 和 MVVM 是三種常見的軟件架構設計模式,主要通過分離關注點的方式來組織代碼結構,優化我們的開發效率。
在開發單頁面應用時,往往一個路由頁面對應了一個腳本文件,所有的頁面邏輯都在一個腳本文件里。頁面的渲染、數據的獲取,對用戶事件的響應所有的應用邏輯都混合在一起,這樣在開發簡單項目時,可能看不出什么問題,當時一旦項目變得復雜,那么整個文件就會變得冗長,混亂,這樣對我們的項目開發和后期的項目維護是非常不利的。

(1)MVC

MVC 通過分離 Model、View 和 Controller 的方式來組織代碼結構。其中 View 負責頁面的顯示邏輯,Model 負責存儲頁面的業務數據,以及對相應數據的操作。并且 View 和 Model 應用了觀察者模式,當 Model 層發生改變的時候它會通知有關 View 層更新頁面。Controller 層是 View 層和 Model 層的紐帶,它主要負責用戶與應用的響應操作,當用戶與頁面產生交互的時候,Controller 中的事件觸發器就開始工作了,通過調用 Model 層,來完成對 Model 的修改,然后 Model 層再去通知 View 層更新。


dbcd93678d7afbc145082ca7c41d7841.png

(2)MVVM

MVVM 分為 Model、View、ViewModel 三者。

Model代表數據模型,數據和業務邏輯都在Model層中定義;
View代表UI視圖,負責數據的展示;
ViewModel負責監聽Model中數據的改變并且控制視圖的更新,處理用 戶交互操作;

Model和View并無直接關聯,而是通過ViewModel來進行聯系的,Model和ViewModel之間有著雙向數據綁定的聯系。因此當Model中 的數據改變時會觸發View層的刷新,View中由于用戶交互操作而改變的 數據也會在Model中同步。
這種模式實現了 Model和View的數據自動同步,因此開發者只需要專注 對數據的維護操作即可,而不需要自己操作DOM。


2003aeb436b48322038d4b21891f1758.png

(2)MVP

MVP 模式與 MVC 唯一不同的在于 Presenter 和 Controller。在 MVC 模式中我們使用觀察者模式,來實現當 Model 層數據發生變化的時候,通知 View 層的更新。這樣 View 層和 Model 層耦合在一起,當項目邏輯變得復雜的時候,可能會造成代碼的混亂,并且可能會對代碼的復用性造成一些問題。MVP 的模式通過使用 Presenter 來實現對 View 層和 Model 層的解耦。MVC 中的Controller 只知道 Model 的接口,因此它沒有辦法控制 View 層的更新,MVP 模式中,View 層的接口暴露給了 Presenter 因此我們可以在 Presenter 中將 Model 的變化和 View 的變化綁定在一起,以此來實現 View 和 Model 的同步更新。這樣就實現了對 View 和 Model 的解耦,Presenter 還包含了其他的響應邏輯。

5. Computed和Watch的區別

對于Computed:

它支持緩存,只有依賴的數據發生了變化,才會重新計算
不支持異步,當Computed中有異步操作時,無法監聽數據的變化
computed的值會默認走緩存,計算屬性是基于它們的響應式依賴進行緩存的,也就是基于data聲明過,或者父組件傳遞過來的props中的數據進行計算的。
如果一個屬性是由其他屬性計算而來的,這個屬性依賴其他的屬性,一般會使用computed
如果computed屬性的屬性值是函數,那么默認使用get方法,函數的返回值就是屬性的屬性值;在computed中,屬性有一個get方法和一個set方法,當數據發生變化時,會調用set方法。

對于Watch:

它不支持緩存,數據變化時,它就會觸發相應的操作
支持異步監聽
監聽的函數接收兩個參數,第一個參數是最新的值,第二個是變化之前的值
當一個屬性發生變化時,就需要執行相應的操作
監聽數據必須是data中聲明的或者父組件傳遞過來的props中的數據,當發生變化時,會出大其他操作,函數有兩個的參數:
    immediate:組件加載立即觸發回調函數
    deep:深度監聽,發現數據內部的變化,在復雜數據類型中使用,例如數組中的對象發生變化。需要注意的是,deep無法監聽到數組和對象內部的變化。

當想要執行異步或者昂貴的操作以響應不斷的變化時,就需要使用watch。

總結:

computed 計算屬性 : 依賴其它屬性值,并且 computed 的值有緩存,只有它依賴的 屬性值發生改變,下一次獲取 computed 的值時才會重新計算 computed 的值。
watch 偵聽器 : 更多的是觀察的作用,無緩存性,類似于某些數據的監聽回調,每 當監聽的數據變化時都會執行回調進行后續操作。

運用場景:

當我們需要進行數值計算,并且依賴于其它數據時,應該使用 computed,因為可以利 用 computed 的緩存特性,避免每次獲取值時,都要重新計算。
當我們需要在數據變化時執行異步或開銷較大的操作時,應該使用 watch,使用 watch 選項允許我們執行異步操作 ( 訪問一個 API ),限制我們執行該操作的頻率, 并在我們得到最終結果前,設置中間狀態。這些都是計算屬性無法做到的。

6. Computed 和 Methods 的區別

https://segmentfault.com/a/1190000014478664
methods與computed之間的差別:
(1)methods和computed里的方法在初始化執行過后,只要任何值有更新,那么所有在computed計算屬性里和其相關的值都會更新。

methods只有在調用的時候才會執行對應的方法,不會自動同步數據。
(2) computed是屬性訪問,而methods是函數調用
computed帶有緩存功能,而methods不是
computed其實是就是屬性,之所以與data區分開,只不過為了防止文本插值中邏輯過重,會導致不易維護

(3) computed定義的方法我們是以屬性的形式訪問的,和data里的屬性訪問形式一樣,{{function}}
但是methods定義的方法,我們必須要加上()來調用,如{{function()}},否則,視圖會出現function (){[native code]}的情況

7. slot是什么?有什么作用?原理是什么?

slot又名插槽,是Vue的內容分發機制,組件內部的模板引擎使用slot元素作為承載分發內容的出口。插槽slot是子組件的一個模板標簽元素,而這一個標簽元素是否顯示,以及怎么顯示是由父組件決定的。slot又分三類,默認插槽,具名插槽和作用域插槽。

(1) 默認插槽:又名匿名插槽,當slot沒有指定name屬性值的時候一個默認顯示插槽,一個組件內只有有一個匿名插槽。
(2) 具名插槽:帶有具體名字的插槽,也就是帶有name屬性的slot,一個組件可以出現多個具名插槽。
(3)作用域插槽:默認插槽、具名插槽的一個變體,可以是匿名插槽,也可以是具名插槽,該插槽的不同點是在子組件渲染作用域插槽時,可以將子組件內部的數據傳遞給父組件,讓父組件根據子組件的傳遞過來的數據決定如何渲染該插槽。

實現原理:當子組件vm實例化時,獲取到父組件傳入的slot標簽的內容,存放在vm.slot中,默認插槽為vm.slot.default,具名插槽為vm.slot.xxx,xxx 為插槽名,當組件執行渲染函數時候,遇到slot標簽,使用slot中的內容進行替換,此時可以為插槽傳遞數據,若存在數據,則可稱該插槽為作用域插槽。

slot的意思是插槽,想想你的電腦主板上的各種插槽,有插CPU的,有插顯卡的,有插內存的,有插硬盤的,所以假設有個組件是computer,組件computer:

<template>
<div>
  <slot name="CPU">這兒插你的CPU</slot>
  <slot name="GPU">這兒插你的顯卡</slot>
<slot></slot>
  <slot name="Memory">這兒插你的內存</slot>
  <slot name="Hard-drive">這兒插你的硬盤</slot>
</div>
</template>


那么組裝一個電腦,就可以在調用組件的頁面這么寫:

<template>
<computer>
  <div slot="CPU">Intel Core i7</div>
  <div slot="GPU">GTX980Ti</div>
<div>想加內容就加內容</div>
  <div slot="Memory">Kingston 32G</div>
  <div slot="Hard-drive">Samsung SSD 1T</divt>
</computer>
</template>
<script>
import computerfrom "./computer";
export default {
  name: "page",
  components: {
    computer
  },
  data() {
    return {
      
    };
  },
  computed: {},
  methods: {
    
  }
};
</script>

頁面顯示:Intel Core i7 GTX980Ti 想加內容就加內容 Kingston 32G Samsung SSD 1T


二、生命周期

vue生命周期.png
vue生命周期desc.png

使用建議:
1. beforeCreate:加載loading事件
2. created:結束loading、初始化、請求數據、實現函數自執行
3. mounted:拿回數據,配合路由鉤子做一些事
4. beforeDestory:destoryed:當前組件已被刪除,清空相關內容

1. created和mounted的區別

(1)created:在模板渲染成html前調用,即通常初始化某些屬性值,然后再渲染成視圖。
(2)mounted:在模板渲染成html后調用,通常是初始化頁面完成后,再對html的dom節點進行一些需要的操作。

2. 接口請求一般放在哪個生命周期中?

可以在鉤子函數 created、beforeMount、mounted 中進行調用,因為在這三個鉤子函數中,data 已經創建,可以將服務端端返回的數據進行賦值。

推薦在 created 鉤子函數中調用異步請求,因為在 created 鉤子函數中調用異步請求有以下優點:
    能更快獲取到服務端數據,減少頁面loading 時間;
    ssr不支持 beforeMount 、mounted 鉤子函數,所以放在 created 中有助于一致性;

三、組件通信

組件通信1.png

如圖所示:
A和B、B和C、B和D都是父子關系,C和D是兄弟關系,A和C是隔代關系(可能隔多代)。
eg:
props、emit/on、vuex、parent/children、attrs/listeners和provide/inject

方法一、props/$emit

父組件A通過props的方向子組件B傳遞,B到A通過在B組件中$emit,A組件中v-on的方式實現。
eg:
子組件

<template>
  <div class="navBar">
    <div class="navBarItem" v-for="item in dataList" :key="item.id" :index="item.id">
      <span v-if="item.isNow" class="nowTitle" @click="navClick(item)">{{item.title}}</span>
      <span v-else class="title" @click="navClick(item)">{{item.title}}</span>
      <span class="separator">></span>
    </div>
  </div>
</template>
<script>
export default {
  name: 'navBar',
  data () {
    return {}
  },
  props: {
    dataList: Array
  },
  methods: {
    navClick (item) {
      this.$emit('navClick', item)
    }
  }
}
</script>

父組件:

<template>
  <NavBar :dataList="navBarData" @navClick="navClick"></NavBar>
</template>
<script>
import NavBar from '@/components/NavBar'
export default {
  name: 'detail',
  data () {
    return {
 navBarData: [
        { title: '鯨選資源', url: '/whaleselect', id: 1, isNow: false },
        { title: '文件詳情', url: '/jx', id: 2, isNow: true }
      ]}
  },
  props: {
    dataList: Array
  },
  methods: {
    navClick ({ url, id }) {
      this.$utils.link(`${url}/` + id)
    }
  },
 components: {
    NavBar,
   
  }
}
</script>

1.父組件向子組件傳值

父組件通過props向下傳遞數據給子組件

2.子組件向父組件傳值(通過事件形式)

子組件通過this.$emit('方法名',傳遞的值),父組件定義同名方法即可

EventBus

EventBus事件總線適用于父子組件、非父子組件等之間的通信;
全局或者公共組件注冊一個vue實例,利用里面的注冊喝監聽事件。

(1)創建事件中心管理組件之間的通信

// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()

(2)發送事件

假設我們有兩個兄弟組件C和D:

<template>
  <div>
    <C></C>
    <D></D>
  </div>
</template>

<script>
import Cfrom './C.vue'
import D from './D.vue'
export default {
  components: { C, D}
}
</script>

在C組件中發送事件:

<template>
  <div>
    <button @click="add">加法</button>    
  </div>
</template>

<script>
import {EventBus} from './event-bus.js' // 引入事件中心

export default {
  data(){
    return{
      num:0
    }
  },
  methods:{
    add(){
      EventBus.$emit('addition', {
        num:this.num++
      })
    }
  }
}
</script>

(3)接收事件

在D組件中發送事件:

<template>
  <div>求和: {{count}}</div>
</template>

<script>
import { EventBus } from './event-bus.js'
export default {
  data() {
    return {
      count: 0
    }
  },
  mounted() {
    EventBus.$on('addition', param => {
      this.count = this.count + param.num;
    })
  }
}
</script>

在上述代碼中,這就相當于將num值存貯在了事件總線中,在其他組件中可以直接訪問。事件總線就相當于一個橋梁,不用組件通過它來通信。

雖然看起來比較簡單,但是這種方法也有不變之處,如果項目過大,使用這種方式進行通信,后期維護起來會很困難。

方法三 Vuex

vuex.png

1.原理

Vuex實現了一個單向數據流,在全局擁有一個State存放數據,當組件要更改State中的數據時,必須通過Mutation進行,Mutation同時提供了訂閱者模式供外部插件調用獲取State數據的更新。當所有異步操作(常見于調用后端接口異步獲取更新數據)或批量的同步操作需要走Action,但是Action也是無法直接修改State的,還是需要通過Mutation來修改State的數據。最后,根據State的變化,渲染到視圖上。

(1)vuex下的store.js文件

import Vuex from 'vuex'
export default new Vuex.Store({
  // 定義狀態 值 方法
  state: {
 // 獲取文件夾
    getfolderscallback: function () {},
    // 驗證用戶的Id的token
    token: '',
    // 用戶id
    uid: '',
    // 是否登錄
    isLogin: false,
    // 用戶名
    username: '',
    // 用戶頭像
    logo: ''
  },
  mutations: {
   // 獲取文件夾
    getfolders (state) {
      state.getfolderscallback()
    },
    // 設置上面的全局變量
    setAttr (state, data) {
      state[data.name] = data.val
    }
  }
})

(2)頁面改變值

  mounted () {
    // 設置全局變量-方法-獲取文件夾
    this.$store.commit('setAttr', {
      name: 'getfolderscallback',
      val: this.getfolders
    })
  },}
  methods: {
    getuser () {
      // 本地判斷Cookie,判斷用戶是否登錄
      if (this.$utils.getCookie(this.$glb.fmCookieName) !== null) {
        this.$api.post('/center/getuser', {}, res => {
          if (!res.status) {
            return
          }
          this.$store.commit('setAttr', {name: 'isLogin', val: true})
          this.$store.commit('setAttr', {name: 'logo', val: res.data.logo})
          this.$store.commit('setAttr', {name: 'uid', val: res.data.userid})
          this.$store.commit('setAttr', {name: 'token', val: res.data.token})
          this.$store.commit('setAttr', {name: 'username', val: res.data.username})
        })
      }
    },
  // 上傳文件框選擇文件夾
    getfolders () {
      this.$api.post('/file/foldertreelist', {}, (res) => {
        this.folderList = res.data
      })
    },
}

(3)頁面調用vuex State中的值

<template>
  <!-- 登錄或未登錄 --S-->
  <div class="isLoginBox">
    <!-- 登錄 -->
    <div class="loginBox" v-if="this.$store.state.isLogin">
退出
    </div>
    <!-- 未登錄 -->
    <div class="unLoginBox" v-else>
      <div class="loginOrRegister">
        <span class="login" >登錄</span>
        <span class="line"></span>
        <span class="register">注冊</span>
      </div>
    </div>
  </div>
  <!-- 登錄或未登錄 --E-->
</template>
<script>
export default {
  name: 'SignUpBar',
  data () {
    return {
    }
  },
  methods: {
    
}
</script>

2.各模塊在核心流程中的主要功能:

1.Vue Components∶ Vue組件。HTML頁面上,負責接收用戶操作等交互行為,執行dispatch方法觸發對應action進行回應。

  1. dispatch∶操作行為觸發方法,是唯一能執行action的方法。
    3.actions∶ 操作行為處理模塊,由組件中的$store.dispatch('action 名稱',data1)來觸發。然后由commit()來觸發mutation的調用,間接更新state。負責處理Vue Components接收到的所有交互行為。包含同步/異步操作,支持多個同名方法,按照注冊的順序依次觸發。向后臺API請求的操作就在這個模塊中進行,包括觸發其他action以及提交mutation的操作。該模塊提供了Promise的封裝,以支持action的鏈式觸發。
  2. commit∶狀態改變提交操作方法。對mutation進行提交,是唯一能執行mutation的方法。
  3. mutations∶狀態改變操作方法,由actions中的commit('mutation 名稱')來觸發。是Vuex修改state的唯一推薦方法,其他修改方式在嚴格模式下將會報錯。該方法只能進行同步操作,且方法名只能全局唯一。操作之中會有一些hook暴露出來,以進行state的監控等。
  4. state∶ 頁面狀態管理容器對象。集中存儲Vuecomponents中data對象的零散數據,全局唯一,以進行統一的狀態管理。頁面顯示所需的數據從該對象中進行讀取,利用Vue的細粒度數據響應機制來進行高效的狀態更新。
    7。 getters∶ state對象讀取方法。圖中沒有單獨列出該模塊,應該被包含在了render中,Vue Components通過該方法讀取全局state對象。

3.Vuex(狀態:數組)與localStorage(字符串)

vuex是vue的狀態管理器,存儲的數據是響應式的。但是并不會保存起來,刷新之后就回到初始狀態,具體做飯應該在vuex里數據改變的時候拷貝一份保存到localStorge里面,刷新之后,如果localStorge里有保存的數據,取出來再替換store里面的state

方法四、 attrs/listeners

方法五、依賴注入 provide/inject

祖先組件中通過provider來提供變量,然后在子孫組件中通過inject來注入變量。provide/inject API主要解決了跨級組件間的通信問題,不過它的使用場景,主要是子組件獲取上級組件的狀態,跨級組件間建立了一種主動提供與依賴注入的關系。

project / inject是Vue提供的兩個鉤子,和data、methods是同級的。并且project的書寫形式和data一樣。

project 鉤子用來發送數據或方法
inject鉤子用來接收數據或方法

eg:兩個組件:A.vue和B.vue,B是A的子組件

// A.vue
export default {
  provide: {
    name: '測試張三'
  }
}
// B.vue
export default {
  inject: ['name'],
  mounted () {
    console.log(this.name);  // 測試張三
  }
}

核心用法:在A.vue里,設置了一個provide:name,值是測試張三,它的作用就是將name這個變量提供給它的所有子組件。
在B.vue中,通過inject注入了從A組件中提供的name變量,在B組件中,就可以直接通過this.name 訪問這個變量,值就是測試張三
注意:**provide和inject綁定并不是可響應的。這是可以為之的。然而,如果你傳入了一個可監聽的對象,那么其對象的屬性還是可響應的。A.vue的name如果改變了,B.vue的this.name是不改變的,仍然是測試張三

provide與inject實現數據響應式

兩個辦法:
(1)provide祖先組件的實例,然后在子孫組件中注入依賴,這樣就可以在子孫組件中直接修改祖先組件的實例的屬性,不過這種方法有個缺點就是這個實例上掛載很多沒有必要的東西,eg:props,methods
(2)使用2.6最新API Vue.observable 優化響應式 provide

provide和inject.png
// A 組件 
<div>
      <h1>A 組件</h1>
      <button @click="() => changeColor()">改變color</button>
      <ChildrenB />
      <ChildrenC />
</div>
......
  data() {
    return {
      color: "blue"
    };
  },
  // provide() {
  //   return {
  //     theme: {
  //       color: this.color //這種方式綁定的數據并不是可響應的
  //     } // 即A組件的color變化后,組件D、E、F不會跟著變
  //   };
  // },
  provide() {
    return {
      theme: this//方法一:提供祖先組件的實例
    };
  },
  methods: {
    changeColor(color) {
      if (color) {
        this.color = color;
      } else {
        this.color = this.color === "blue" ? "red" : "blue";
      }
    }
  }
  // 方法二:使用2.6最新API Vue.observable 優化響應式 provide
  // provide() {
  //   this.theme = Vue.observable({
  //     color: "blue"
  //   });
  //   return {
  //     theme: this.theme
  //   };
  // },
  // methods: {
  //   changeColor(color) {
  //     if (color) {
  //       this.theme.color = color;
  //     } else {
  //       this.theme.color = this.theme.color === "blue" ? "red" : "blue";
  //     }
  //   }
  // }

// F 組件 
<template functional>
  <div class="border2">
    <h3 :style="{ color: injections.theme.color }">F 組件</h3>
  </div>
</template>
<script>
export default {
  inject: {
    theme: {
      //函數式組件取值不一樣
      default: () => ({})
    }
  }
};
</script>

方法五、parent/children 與ref

ref:如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子組件上,引用就指向組件實例;
parent/children:訪問父/子實例
注意:這兩種都是直接得到組件實例,使用后可以直接調用組件的方法或訪問數據。
(1)用ref來訪問組件:

// component-a 子組件
export default {
  data () {
    return {
      title: 'Vue.js'
    }
  },
  methods: {
    sayHello () {
      window.alert('Hello');
    }
  }
}

// 父組件(頁面)
<template>
  <component-a ref="comA"></component-a>
</template>
<script>
  export default {
    mounted () {
      const comA = this.$refs.comA;
      console.log(comA.title);  // Vue.js
      comA.sayHello();  // 彈窗
    }
  }
</script>

不過,這兩種方法的弊端是:無法在跨級或者兄弟間通信

// parent.vue
<component-a></component-a>
<component-b></component-b>
<component-b></component-b>

總結

常用使用場景可以分為三類:

(1)父子通信:
父to子傳遞數據是通過props,子to父是通過emit; 通過父鏈/子鏈也可以通信(parent/children); ref也可以訪問組件實例; provide/inject API;attrs/$listeners;

(2)兄弟通信:
EventBus;
Vuex;

(3跨級通信:
Event Bus;
Vuex;
provide/inject API;
attrs/listeners;

四、

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

推薦閱讀更多精彩內容