vue學(xué)習(xí)筆記(上)

Vue一文學(xué)會(huì)?

Vue大家都知道就是一個(gè)國(guó)內(nèi)非常流行的框架柳琢,最近因?yàn)檫^(guò)了許久沒(méi)用Vue對(duì)于Vue的許多早已淡忘衡怀,所以目前正在復(fù)習(xí)Vue順便記錄一下筆記够庙,以后忘了也可以進(jìn)行查證靠娱,因?yàn)檫@是根據(jù)自己的性格所寫(xiě)的筆記嗽元,可能大家會(huì)看不懂敛纲,如果看不懂請(qǐng)盡量不要試著看,可以去看看其他的博主寫(xiě)的文章剂癌!

起步

如果需要知道怎么使用webpack手動(dòng)搭建vue環(huán)境的麻煩傳送

那么我們還是使用Vue的官方cli進(jìn)行構(gòu)建Vue程序 npm i -g @vue/cli
然后我們 vue create -n suiyue_m -n選項(xiàng)你隨意加不加淤翔,是用來(lái)表示不初始化git的
然后就是一個(gè)選項(xiàng)欄

image.png

我之前已經(jīng)保存過(guò)一次選項(xiàng)了所以大家可以看看,我這里在選擇一次
image.png

選擇最后一項(xiàng)按回車(chē)鍵進(jìn)入二次選擇
image.png

空格鍵選擇
大家先跟著我的選擇選吧珍手,一般就是這樣办铡,最多就是多一個(gè)typescript什么的之類(lèi),然后我們直接敲回車(chē)
之后這里又有一個(gè)選項(xiàng)
image.png

意思是要不要開(kāi)啟歷史模式琳要,什么區(qū)別呢寡具?
就是路由的時(shí)候
默認(rèn)是啟用的(因?yàn)閅是大寫(xiě),n是小寫(xiě))稚补,啟用此歷史模式就是
example url: http://www.xxx.com/user/xiaoming/info
不啟用就是熟悉的
http://www.xxx.com/#/user/xiaoming/info (應(yīng)該沒(méi)記錯(cuò))
(啟用此模式需要配置服務(wù)器因?yàn)橥@樣發(fā)送http請(qǐng)求的話(huà)會(huì)請(qǐng)求對(duì)應(yīng)服務(wù)器對(duì)應(yīng)的文件夾下的index.html所以需要進(jìn)行配置,具體配置請(qǐng)移步Vue官方文檔,不放鏈接了厦坛,因?yàn)槲彝四莻€(gè)鏈接了)

所以我們選擇N五垮,主要是懶得配置服務(wù)器了


image.png

下面就是配置css預(yù)處理了,我喜歡用less大家隨意按照自己喜歡的擅長(zhǎng)的那種進(jìn)行選擇杜秸,下一個(gè)選項(xiàng)就是

image.png

語(yǔ)法檢測(cè)eslint然后我默認(rèn)就選擇第一個(gè)了放仗,代表eslint用來(lái)檢測(cè)語(yǔ)法沒(méi)有錯(cuò)誤,其他的不了解也不使用
image.png

下一個(gè)默認(rèn)也選擇第一個(gè)代表在保存時(shí)進(jìn)行語(yǔ)法檢查
image.png

這個(gè)的意思大意就是Babel撬碟,PostCss诞挨,ESLint這些配置的配置文件(個(gè)人理解,錯(cuò)誤勿怪)存放在一個(gè)單獨(dú)的文件還是跟package.json文件放在一起
那么就選擇第一項(xiàng)了,放在一個(gè)獨(dú)立的文件中
image.png

最后一個(gè)就是是否保存這次的選擇,然后因?yàn)槲冶4媪怂杂腥齻€(gè)選項(xiàng)大家隨意保存不保存谨设,如果敲y的話(huà)會(huì)讓你輸入這個(gè)preset的名字,默認(rèn)是不保存的堰乔,然后就開(kāi)始下載依賴(lài)什么的了
最后想說(shuō)的就是建議大家使用yarn或者pnpm這等之類(lèi)的npm包管理工具,因?yàn)樗俣瓤鞓侵魇褂玫氖莥arn
大家可以使用 npm i -g yarn進(jìn)行安裝
image.png

然后我們直接運(yùn)行Vue服務(wù)


image.png

默認(rèn)是不會(huì)打開(kāi)瀏覽器自動(dòng)運(yùn)行的需要大家手動(dòng)打開(kāi)瀏覽器查看頁(yè)面,好了關(guān)于vue-cli的用法就結(jié)束了

接下來(lái)就是Hello Vue的實(shí)現(xiàn)了,我們先更改一些配置文件
在文件夾的根目錄新建一個(gè)vue.config.js文件蜈敢,插入一下這些內(nèi)容將vue服務(wù)的端口改為3000,讓它自動(dòng)打開(kāi)瀏覽器汽抚,至于熱更新本身就熱更新了

image.png

然后我們重新yarn serve然后就會(huì)看到我們熟悉的一幕了
image.png

因?yàn)?000端口跑了react的項(xiàng)目所以緩存了圖標(biāo)

然后接下來(lái)就是Hello Vue了扶认,直接打開(kāi)App.vue
將template下面改成這樣


image.png

就是一個(gè)Hello Vue了


image.png

那么因?yàn)槲沂菑?fù)習(xí)我就想到什么寫(xiě)什么了

進(jìn)階

  • 計(jì)算屬性與監(jiān)聽(tīng)

    計(jì)算屬性殊橙,類(lèi)似于一個(gè)Vue屬性但是計(jì)算屬性是實(shí)時(shí)進(jìn)行計(jì)算出來(lái)的,計(jì)算屬性可以依賴(lài)Vue的屬性狱从,當(dāng)Vue的屬性改變時(shí)膨蛮,計(jì)算屬性會(huì)同步的改變,類(lèi)似于


    image.png

然后其實(shí)下面就是一個(gè)計(jì)算屬性永遠(yuǎn)不會(huì)更新的死節(jié)


image.png

因?yàn)橄旅娴倪@個(gè)計(jì)算屬性并沒(méi)有依賴(lài)任何Vue的屬性導(dǎo)致此計(jì)算屬性永遠(yuǎn)也不會(huì)更新季研,其實(shí)此計(jì)算屬性的效果也可以同樣使用methods進(jìn)行替代敞葛,類(lèi)似于


image.png

然后就是此方法與使用計(jì)算屬性的不同之處在于,當(dāng)頁(yè)面被觸發(fā)渲染時(shí)計(jì)算屬性所依賴(lài)的屬性改變時(shí)才會(huì)重新計(jì)算求值与涡,但是函數(shù)每一次頁(yè)面重新渲染時(shí)都會(huì)進(jìn)行重新的一次計(jì)算求值

監(jiān)聽(tīng)
使用屬性名作為函數(shù)名惹谐,每一次當(dāng)對(duì)應(yīng)的屬性值發(fā)生改變時(shí)會(huì)進(jìn)行調(diào)用對(duì)應(yīng)的函數(shù)(不進(jìn)行詳細(xì)的截圖了)

class與style的綁定

綁定class

image.png

使用v-bind指令綁定class傳入一個(gè)對(duì)象,屬性名決定類(lèi)名驼卖,是否指定此類(lèi)名決定值的真或者是假氨肌,同樣的我們也可以將class后面的屬性提出為一個(gè)對(duì)象指定變量的方式


image.png

同樣的class后面也可以指定為一個(gè)函數(shù)或者一個(gè)計(jì)算屬性要求是返回對(duì)象形式的

class綁定數(shù)組語(yǔ)法


image.png

渲染為


image.png

同時(shí)也可以在數(shù)組中使用三元運(yùn)算符來(lái)指定對(duì)應(yīng)的類(lèi)名
image.png

渲染為


image.png

最后就是還可以在數(shù)組之中使用對(duì)象來(lái)進(jìn)行混入


image.png

image.png

(對(duì)組件傳遞class類(lèi)名會(huì)默認(rèn)綁定到此組件的根元素身上)

style綁定

image.png

image.png

(在強(qiáng)調(diào)一遍,vue中指令后面的引號(hào)中的字符串會(huì)被當(dāng)做js代碼進(jìn)行執(zhí)行)

同樣的因?yàn)閟tyle是一個(gè)對(duì)象我們可以將這個(gè)對(duì)象取出直接指定變量的名字進(jìn)行綁定也可以類(lèi)class的綁定方法

style數(shù)組綁定法類(lèi)對(duì)象方法

指令

v-if

使用v-if后面的表達(dá)式為true時(shí)才會(huì)渲染對(duì)應(yīng)的html元素,否則不會(huì)渲染(使用的是上dom樹(shù)和下dom樹(shù))
我這里用了一下v-if

<template>
  <div id="app">
    <h1 v-if="isShow">Hello World</h1>
    <input type="button" value="點(diǎn)我上樹(shù)下樹(shù)" @click="shangxiashu">    
    <router-view/>
  </div>
</template>

<script>
export default {
  data(){
    return {
      isShow:true
    }
  },
  methods:{
    shangxiashu(){
      this.isShow = !this.isShow;
    }
  }
}
</script>

通過(guò)isShow控制h1標(biāo)簽的上樹(shù)與下樹(shù),這里使用的v-if是直接從dom樹(shù)上面刪除的,如果需要頻繁的操作dom需要使用另外一個(gè)指令(暫時(shí)忘了)

vue還提供了一個(gè)指令與此指令配套,就是v-else,我們稍微修改代碼

<template>
  <div id="app">
    <h1 v-if="isShow">Hello World</h1>
    <h2 v-else>h2 ---> Hello World</h2>
    <input type="button" value="點(diǎn)我切換元素" @click="shangxiashu">    
    <router-view/>
  </div>
</template>

<script>
export default {
  data(){
    return {
      isShow:true
    }
  },
  methods:{
    shangxiashu(){
      this.isShow = !this.isShow;
    }
  }
}
</script>

你會(huì)發(fā)現(xiàn)使用vue能夠讓你用極少的代碼來(lái)操作dom

vue2.1新增了一個(gè)v-else-if指令

v-else 必須添加到v-if或者v-else-if后面否則無(wú)法被識(shí)別

我們繼續(xù)修改代碼

<template>
  <div id="app">
    <h1 v-if="isShow <=6">Hello World</h1>
    <h2 v-else-if="isShow >6 && isShow<=10">h2 ---> Hello World</h2>
    <h3 v-else>h3 ---> Hello World</h3>
    <input type="button" value="點(diǎn)我切換元素" @click="shangxiashu">    
    <router-view/>
  </div>
</template>

<script>
export default {
  data(){
    return {
      isShow:1
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    }
  }
}
</script>

我們直接將isShow改變?yōu)閿?shù)字通過(guò)每一個(gè)區(qū)間的不同渲染不同的元素

v-show --> v-show指令只是單純的切換標(biāo)簽的display屬性,所以并不是真正的銷(xiāo)毀一個(gè)dom元素

v-if初始化加載開(kāi)銷(xiāo)低,每次切換開(kāi)銷(xiāo)高 v-show初始化開(kāi)銷(xiāo)高,但是切換開(kāi)銷(xiāo)低
用法一般是: 只需要渲染一兩次的dom元素可以使用v-if,需要頻繁顯示隱藏的一些效果需要v-show

v-for

<template>
  <div id="app">
    <ul>
      <li v-for="(item,index) in items" :key="index">
        {{ item }}
      </li>
    </ul>
    <router-view/>
  </div>
</template>

<script>
export default {
  data(){
    return {
      isShow:1,
      items:[
        "Foo","Bar"
      ]
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    }
  }
}
</script>

可以使用of替換in,這樣更像迭代器

v-for="item of items"

遍歷數(shù)組有兩個(gè)參數(shù)
(item,index)遍歷對(duì)象有三個(gè)參數(shù) (value,name,index)

數(shù)組更新檢測(cè)

data中數(shù)組的某一些方法也會(huì)觸發(fā)vue的視圖更新

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()
    當(dāng)data中的數(shù)組通過(guò)上面的方法數(shù)據(jù)發(fā)生改變時(shí)同樣會(huì)出發(fā)虛擬dom的更新進(jìn)而刷新視圖
    (上述方法統(tǒng)稱(chēng)為變異方法,就是會(huì)改變調(diào)用這些方法的數(shù)組)

由于JavaScript的某些限制,vue無(wú)法監(jiān)控兩類(lèi)數(shù)組的變化

  • items[0] = "Xiao Ming" 直接修改某一位置的值
  • items.length = 99 修改數(shù)組的長(zhǎng)度

為了解決第一類(lèi)問(wèn)題,可以使用下面的兩種方法

  1. Vue.set(items,0,"XiaoMing")
  2. items.splice(0,1,"XiaoMing")

解決第二類(lèi)問(wèn)題的方法
items.splice(newLength)

(我這里直接寫(xiě)的items,大家一定要使用vue的實(shí)例打點(diǎn)調(diào)用)

由于JavaScript的限制vue無(wú)法檢測(cè)對(duì)象屬性的添加和刪除,例如當(dāng)Vue對(duì)象實(shí)例化之后可以為這個(gè)實(shí)例化對(duì)象vm(viewModel)繼續(xù)添加屬性但是這時(shí)候添加的屬性vue是無(wú)法動(dòng)態(tài)監(jiān)控到的(此屬性沒(méi)有與vm進(jìn)行綁定)

解決方案:
Vue.set(obj,newProp,newPropValue))

(這代表是Vue這個(gè)對(duì)象的靜態(tài)方法,不是實(shí)例化vm的方法,實(shí)例化vm也有一個(gè)此方法為$set()這個(gè)方法是全局set方法的別名)

v-on

v-on:事件名.事件修飾符 --> 一個(gè)簡(jiǎn)單的使用

<template>
  <div id="app">
    <h1>{{counter}}</h1>
    <input type="button" value="click me" v-on:click="counter++">
    <router-view/>
  </div>
</template>

<script>
export default {
  data(){
    return {
      counter:0
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    }
  },
  mounted(){
    window.items = this.items;
  }
}
</script>
image.png

我們除了直接在v-on后面書(shū)寫(xiě)語(yǔ)句外我們還可以書(shū)寫(xiě)方法(methods中定義的)

<template>
  <div id="app">
    <h1>{{counter}}</h1>
    <input type="button" value="click me" v-on:click="add">
    <router-view/>
  </div>
</template>

<script>
export default {
  data(){
    return {
      counter:0
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    },
    add:function(){
      // this代表vue的實(shí)例
      this.counter++;
    }
  },
  mounted(){
    window.items = this.items;
  }
}
</script>

一樣可以實(shí)現(xiàn)點(diǎn)擊按鈕+1的操作,然后我們除了可以使用方法外我們還可以給這個(gè)方法進(jìn)行傳參,類(lèi)似于

<template>
  <div id="app">
    <h1>{{counter}}</h1>
    <input type="button" value="click me" v-on:click="add(99999999999)">
    <router-view/>
  </div>
</template>

<script>
export default {
  data(){
    return {
      counter:0
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    },
    add:function(num){
      num = num || 1;
      // this代表vue的實(shí)例
      this.counter += num;
    }
  },
  mounted(){
    window.items = this.items;
  }
}
</script>

有時(shí)候我們可能需要訪(fǎng)問(wèn)原聲事件對(duì)象的一些屬性,我們也可以將$event這個(gè)屬性傳遞給方法

<template>
  <div id="app">
    <h1>{{counter}}</h1>
    <input type="button" value="click me" v-on:click="add($event)">
    <router-view/>
  </div>
</template>

<script>
export default {
  data(){
    return {
      counter:0
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    },
    add:function(e,num){
      num = num || 1;
      // this代表vue的實(shí)例
      this.counter += num;
      // 當(dāng)前事件的事件對(duì)象
      console.log(e);
    }
  },
  mounted(){
    window.items = this.items;
  }
}
</script>
image.png

事件修飾符

按照vue官方的說(shuō)法就是事件中就是純粹的邏輯而不去處理dom事件的細(xì)節(jié)所以我們可以使用事件修飾符做一些操作事件細(xì)節(jié)的事情如:阻止默認(rèn)事件,阻止事件冒泡等等
比如說(shuō)我這里有一個(gè)a標(biāo)簽我這個(gè)a標(biāo)簽點(diǎn)擊之后就會(huì)跳轉(zhuǎn)到baidu去

<template>
  <div id="app">
    <h1>{{counter}}</h1>
    <a  target="_blank">點(diǎn)我+1</a>
    <router-view/>
  </div>
</template>

那我想干啥呢?我想要這個(gè)a標(biāo)簽點(diǎn)擊之后counter+1,所以我就這樣寫(xiě)

<template>
  <div id="app">
    <h1>{{counter}}</h1>
    <a  target="_blank" v-on="add">點(diǎn)我+1</a>
    <router-view/>
  </div>
</template>

<script>
export default {
  data(){
    return {
      counter:0
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    },
    add:function(e,num){
      num = num || 1;
      // this代表vue的實(shí)例
      this.counter += num;
      // 當(dāng)前事件的事件對(duì)象
      console.log(e);
    }
  },
  mounted(){
    window.items = this.items;
  }
}
</script>

那么你就會(huì)發(fā)現(xiàn)點(diǎn)擊了這個(gè)a標(biāo)簽之后counter+1后又跳轉(zhuǎn)到baidu去了,這就是所謂的默認(rèn)事件,顧名思義就是a標(biāo)簽本來(lái)就是表示一個(gè)鏈接的點(diǎn)擊之后自然是要跳轉(zhuǎn)到對(duì)應(yīng)的地址去,所以我們可以通過(guò)prevent事件修飾符阻止默認(rèn)事件,我們可以試試

<template>
  <div id="app">
    <h1>{{counter}}</h1>
    <a  target="_blank" v-on:click.prevent="add">點(diǎn)我+1</a>
    <router-view/>
  </div>
</template>

<script>
export default {
  data(){
    return {
      counter:0
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    },
    add:function(e,num){
      num = num || 1;
      // this代表vue的實(shí)例
      this.counter += num;
      // 當(dāng)前事件的事件對(duì)象
      console.log(e);
    }
  },
  mounted(){
    window.items = this.items;
  }
}
</script>

這樣之后不管你怎么點(diǎn)擊a標(biāo)簽也只會(huì)響應(yīng)click事件不會(huì)跳轉(zhuǎn)頁(yè)面了

常用的修飾符

  • stop 阻止冒泡
  • prevent 阻止默認(rèn)事件
  • capture 捕獲階段執(zhí)行事件
  • self 大致理解為捕獲或者冒泡到這個(gè)元素的事件不會(huì)執(zhí)行,只有直接出發(fā)此元素的事件才會(huì)被執(zhí)行
  • once 代表只執(zhí)行一次
  • passive 這個(gè)東西代表立即觸發(fā)默認(rèn)事件,同時(shí)會(huì)使prevent無(wú)效

按鍵修飾符 --> 可以通過(guò)按鍵修飾符綁定keyboard事件時(shí)監(jiān)聽(tīng)某一個(gè)鍵

  • enter
  • tab
  • delete (捕獲“刪除”和“退格”鍵)
  • esc
  • space
  • up
  • down
  • left
  • right

這是一個(gè)使用按鍵修飾符的小例子

<template>
  <div id="app">
    <h1>{{counter}}</h1>
    <a  target="_blank" v-on:click.prevent="add">點(diǎn)我+1</a>
    <br />
    <input type="text" placeholder="回車(chē)+10w" v-on:keyup.enter="keyHandle">
    <router-view/>
  </div>
</template>

<script>
export default {
  data(){
    return {
      counter:0
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    },
    add:function(e,num){
      num = num || 1;
      // this代表vue的實(shí)例
      this.counter += num;
      // 當(dāng)前事件的事件對(duì)象
      console.log(e);
    },
    keyHandle(){
      this.counter+=100000;
    }
  },
  mounted(){
    window.items = this.items;
  }
}
</script>

就輕松實(shí)現(xiàn)了按下特定的鍵做某些特定的事情了

同時(shí)我們也可以自定義按鍵修飾符
我們可以使用

Vue.config.keyCodes.w= 87

定義一個(gè)w的按鍵修飾符鍵盤(pán)碼為87

<template>
  <div id="app">
    <h1>{{counter}}</h1>
    <a  target="_blank" v-on:click.prevent="add">點(diǎn)我+1</a>
    <br />
    <input type="text" placeholder="回車(chē)+10w" v-on:keyup.w="keyHandle">
    <router-view/>
  </div>
</template>

<script>
export default {
  mounted(){
    Vue.config.keyCodes.w= 87
  },
  data(){
    return {
      counter:0
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    },
    add:function(e,num){
      num = num || 1;
      // this代表vue的實(shí)例
      this.counter += num;
      // 當(dāng)前事件的事件對(duì)象
      console.log(e);
    },
    keyHandle(){
      console.log("按下了w")
    }
  },
  mounted(){
    window.items = this.items;
  }
}
</script>

image.png

效果顯而易見(jiàn),同時(shí)vue2.1新增了系統(tǒng)修飾鍵(個(gè)人覺(jué)得就是幾個(gè)特殊的鍵)

  • .ctrl
  • .alt
  • .shift
  • .meta 鍵盤(pán)上的有win圖標(biāo)的鍵,mac就是花鍵

所以我們可以這樣玩

<template>
  <div id="app">
    <h1>{{counter}}</h1>
    <a  target="_blank" v-on:click.prevent.ctrl="add">按住ctrl點(diǎn)擊+1</a>
    <router-view/>
  </div>
</template>

<script>
export default {
  mounted(){
    Vue.config.keyCodes.w= 87
  },
  data(){
    return {
      counter:0
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    },
    add:function(e,num){
      num = num || 1;
      // this代表vue的實(shí)例
      this.counter += num;
    }
  },
  mounted(){
    window.items = this.items;
  }
}
</script>

這樣的話(huà)就是不按住ctrl鍵不會(huì)響應(yīng)點(diǎn)擊事件的,還是一個(gè)非常親名的功能
我這樣就可以制作一個(gè)按住alt+c清楚輸入的內(nèi)容的一個(gè)輸入框

<template>
  <div id="app">
    <h1>{{counter}}</h1>
    <a  target="_blank" v-on:click.prevent.ctrl="add">按住ctrl點(diǎn)擊+1</a>
    <br />
    <input type="text" v-on:keyup.alt.67="clear($event)">
    <router-view/>
  </div>
</template>

<script>
export default {
  mounted(){
    Vue.config.keyCodes.w= 87
  },
  data(){
    return {
      counter:0
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    },
    add:function(e,num){
      num = num || 1;
      // this代表vue的實(shí)例
      this.counter += num;
    },
    clear(e){
      e.target.value = "";
    }
  },
  mounted(){
    console.log(1);
    
    window.items = this.items;
  }
}
</script>

時(shí)候我們就可以驕傲的說(shuō)我們的輸入框有快捷鍵了
這里說(shuō)一下上面綁定keyup時(shí)使用的是 .alt.67 后面的67是鍵盤(pán)上的c鍵的鍵盤(pán)碼我們綁定事件時(shí)也可以直接將鍵盤(pán)碼點(diǎn)上去,然后就是
v-on有一個(gè)縮寫(xiě) 比如 v-on:click.prevent.ctrl ---> @:click.prevent.ctrl可以直接縮寫(xiě)為@

vue2.5新增了一個(gè)用于鍵盤(pán)事件的修飾符

  • exact
    我這里直接就將官方的說(shuō)明粘過(guò)來(lái)了,一看就懂
<!-- 即使 Alt 或 Shift 被一同按下時(shí)也會(huì)觸發(fā) -->
<button @click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的時(shí)候才觸發(fā) -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 沒(méi)有任何系統(tǒng)修飾符被按下的時(shí)候才觸發(fā) -->
<button @click.exact="onClick">A</button>

vue2.2新增了鼠標(biāo)的按鍵修飾符

  • .left
  • .right
  • .middle
    那么我們就可以實(shí)現(xiàn)一個(gè)右鍵點(diǎn)擊+1的操作了
<template>
  <div id="app">
    <h1>{{counter}}</h1>
    <a  target="_blank" v-on:click.prevent.right.exact="add">點(diǎn)擊右鍵+1(以使用exact不要按其他的鍵否則無(wú)效)</a>
    <br />
    <input type="text" v-on:keyup.alt.67="clear($event)">
    <router-view/>
  </div>
</template>

<script>
export default {
  mounted(){
    Vue.config.keyCodes.w= 87
  },
  data(){
    return {
      counter:0
    }
  },
  methods:{
    shangxiashu(){
      this.isShow++;
    },
    add:function(e,num){
      num = num || 1;
      // this代表vue的實(shí)例
      this.counter += num;
    },
    clear(e){
      e.target.value = "";
    }
  },
  mounted(){
    console.log(1);
    
    window.items = this.items;
  }
}
</script>

那么這時(shí)候就發(fā)現(xiàn)只有右鍵點(diǎn)擊才能夠+1了(而左鍵點(diǎn)擊又坑爹的跳轉(zhuǎn)了),并且當(dāng)我們按住ctrl之類(lèi)的系統(tǒng)修飾鍵是無(wú)效的

v-model --> 數(shù)據(jù)的雙向綁定

我們快速實(shí)現(xiàn)一個(gè)表單認(rèn)證

<template>
  <div id="app">
    <div>
      <form action="#">
        <p>
          <input type="text" v-model="username" placeholder="please type username">
        </p>
        <p>
          <input type="password" v-model="userpwd" placeholder="please type password">
        </p>
        <input type="submit" value="sumit" @click.prevent="submit">
      </form>
    </div>
    <router-view/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: "",
      userpwd: ""
    };
  },
  methods: {
    submit() {
      console.log(`username:${this.username}--->password:${this.userpwd}`);
    }
  }
};
</script>

同時(shí)此指令還有一些修飾符就如v-on一樣

  • .lazy 默認(rèn)使用input中的input事件進(jìn)行雙向綁定,使用此修飾符可將雙向綁定的事件改為change(當(dāng)輸入框失去焦點(diǎn)時(shí)更新)
  • .number 默認(rèn)輸入的任何字符都是字符串,如果你需要讓用戶(hù)輸入的數(shù)字轉(zhuǎn)換成number類(lèi)型可加
  • .trim 可以自動(dòng)去除用戶(hù)輸入字符串中的首尾空格

其他指令介紹

(v-if,v-model,v-for,v-on)

v-text --> 指令用于綁定后面值到元素的innerText中

<template>
  <div id="app">
    <div v-text="msg"></div>
    <router-view/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg:"<h1>Hello Vue</h1>"
    };
  },
  methods: {

  }
};
</script>

這樣就完成了綁定,這個(gè)指令會(huì)替換掉原本元素中的內(nèi)容(元素中的所有內(nèi)容都會(huì)被替換包括子元素)
你會(huì)發(fā)現(xiàn)這個(gè)指令解析出來(lái)就是一個(gè)單純的字符串

v-html

這個(gè)指令可以解決上面的問(wèn)題,v-text中的內(nèi)容無(wú)論是什么都會(huì)被解析為字符串
v-html與v-text類(lèi)似,但是會(huì)將類(lèi)似于html的代碼解析為html嵌入元素中

<template>
  <div id="app">
    <div v-html="msg">
      <p>lfjlsfsd </p>
    </div>
    <router-view/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg:"<h1>Hello Vue</h1>"
    };
  },
  methods: {

  }
};
</script>
image.png

上述兩個(gè)指令都會(huì)替換掉元素中的所有內(nèi)容,但是很靈活可以通過(guò)函數(shù)或者一些簡(jiǎn)單的表達(dá)式動(dòng)態(tài)的求值,同時(shí)替換元素中的所有內(nèi)容也算是一個(gè)缺點(diǎn),我們可以使用vue插值表達(dá)式就是雙大括號(hào){{}}進(jìn)行動(dòng)態(tài)解析某些值,插值表達(dá)式不會(huì)替換原宿所有內(nèi)容,解析后的值就會(huì)替換在插值表達(dá)式所在的位置,插值表達(dá)式計(jì)算出的值為純字符串

<template>
  <div id="app">
    <div>
      {{msg}}
    </div>
    <router-view/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg:"<h1>Hello Vue</h1>"
    };
  },
  methods: {

  }
};
</script>
image.png

有一種情況我們可以預(yù)見(jiàn)

<template>
  <div id="app">
    <div>
      <h1>Hello {{msg}}</h1>
    </div>
    <router-view/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg:"Vue"
    };
  },
  methods: {

  }
};
</script>

這時(shí)候渲染是沒(méi)有任何問(wèn)題的


image.png

但是當(dāng)我們進(jìn)入調(diào)試模式調(diào)低網(wǎng)速時(shí)
這個(gè)地方更改為slow 3G


image.png

無(wú)語(yǔ)了,我這里是vue cli服務(wù)的情況


image.png

模擬不了那種情況,我就直接說(shuō)了吧,大家應(yīng)該能夠明白是什么情況吧

當(dāng)網(wǎng)速慢的時(shí)候我們寫(xiě)的插值表達(dá)式可能就會(huì)原樣輸出(就是該咋樣咋樣的輸出,因?yàn)檫@個(gè)時(shí)候網(wǎng)速慢vue.js這個(gè)文件可能還沒(méi)有請(qǐng)求過(guò)來(lái),因?yàn)榭隙ㄊ莾?yōu)先載入html文件再?gòu)膆tml文件中的script標(biāo)簽中載入vuejs文件)這個(gè)時(shí)候就暴露了,等到vuejs請(qǐng)求下來(lái)才會(huì)更新這些插值表達(dá)式,但是這樣就有點(diǎn)坑了,不是很美觀(guān),所以vue提供了一個(gè)指令用來(lái)控制此等情況

v-cloak

這個(gè)指令就非常簡(jiǎn)單,就是插值表達(dá)式暴露到屏幕的時(shí)候可以為對(duì)應(yīng)的元素暴露一個(gè)v-cloak屬性
(我這個(gè)真的不行,模擬不了


image.png

從圖片中可以看出,這是服務(wù)端渲染html結(jié)束然后返回給客戶(hù)端的,這應(yīng)該是服務(wù)端渲染的(個(gè)人猜測(cè),但是8,9了))
就是當(dāng)有了這個(gè)v-cloak屬性時(shí)我們就可以寫(xiě)類(lèi)似的樣式



因?yàn)檫@個(gè)屬性在插值表達(dá)式解析完畢之后會(huì)自動(dòng)去除(removeAttr..)

v-once

顧名思義不說(shuō)了
(就是當(dāng)這個(gè)元素第一次渲染之后,之后的數(shù)據(jù)變化觸發(fā)render方法更新視圖時(shí)這個(gè)元素不會(huì)在被刷新了永遠(yuǎn)也不會(huì)了)

<template>
  <div id="app">
    <div>
     <h1 v-once>Hello {{msg}}</h1>
     <input type="button" value="改變msg的值" @click="changeValue">
    </div>
    <router-view/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg:"Vue"
    };
  },
  methods: {
    changeValue(){
      console.log(1)
      this.msg += "zzz"
      console.log(this.msg);
    }
  }
};
</script>

<style lang="less">
  div[v-cloak]{
    display: none;
  }
</style>

你就會(huì)發(fā)現(xiàn)無(wú)論你怎么點(diǎn)擊按鈕,試圖也不會(huì)刷新因?yàn)槌跏蓟芷谝呀?jīng)執(zhí)行過(guò)一次render了,所以以后不會(huì)刷新視圖了,哪怕數(shù)據(jù)怎么發(fā)生變化


image.png

除非你直接人肉修改此選項(xiàng)

<template>
  <div id="app">
    <div>
     <h1 v-once ref="aa">Hello {{msg}}</h1>
     <input type="button" value="改變msg的值" @click="changeValue">
    </div>
    <router-view/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg:"Vue"
    };
  },
  methods: {
    changeValue(){
      console.log(1)
      this.msg += "zzz"
      console.log(this.msg);
      // 暴力修改
      this.$refs.aa.innerText = "Hello World"
    }
  }
};
</script>

<style lang="less">
  div[v-cloak]{
    display: none;
  }
</style>

哈哈,一般是不會(huì)這么做的,因?yàn)檫@是不被vue提倡的

v-pre

跳過(guò)這個(gè)元素和它的子元素的編譯過(guò)程酌畜≡跚簦可以用來(lái)顯示原始 Mustache 標(biāo)簽。跳過(guò)大量沒(méi)有指令的節(jié)點(diǎn)會(huì)加快編譯桥胞。
例子就是

<template>
  <div id="app">
    <div>
     <h1 v-pre ref="aa" v-text="這是一個(gè)測(cè)試的文字">Hello {{msg}}</h1>
     <p>你看到Vue算我輸</p>
     <input type="button" value="test" @click="changeValue">
    </div>
    <router-view/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg:"Vue"
    };
  },
  methods: {
    changeValue(){
      console.log(1)
      this.msg += "zzz"
      console.log(this.msg);
      // 暴力修改
     console.log(this.$refs.aa);

    }
  }
};
</script>

<style lang="less">
  div[v-cloak]{
    display: none;
  }
</style>

就像于Vue不會(huì)管這個(gè)元素了,這個(gè)元素的所有都跟普通html一樣了,這些在vue中認(rèn)識(shí)的指令也無(wú)用了,瀏覽器不能認(rèn)識(shí)的一切都不會(huì)起作用了


image.png

v-slot

這個(gè)東西后期完善更新,需要先了解組件
(date:2019-06-23 13:21:54)

組件基礎(chǔ)

聲明一個(gè)組件的最簡(jiǎn)單的方法就是通過(guò)Vue.component方法,然后就是我想說(shuō)的就是因?yàn)関ue-cli聲明組件用的是另外一種方法所以我們只有新建一個(gè)文件夾不用打包工具來(lái)寫(xiě)了,我這里直接去node_modules文件夾下的vue中將dist目錄下的vuejs復(fù)制過(guò)來(lái)的,然后直接新建一個(gè)index.html文件夾如下


image.png

大家像我這樣寫(xiě)完這個(gè)index.html之后使用file協(xié)議在瀏覽器中打開(kāi)這個(gè)文件就會(huì)發(fā)現(xiàn)我之前所說(shuō)的這種情況了,現(xiàn)在我們將網(wǎng)絡(luò)改為slow 3G然后刷新,反正你就是會(huì)看到插值表達(dá)式會(huì)一閃而過(guò)就這個(gè)情況

算了其他的不多說(shuō)了,我們首先看一下最基礎(chǔ)的定義組件

<body>
    <div id="app">
        {{msg}}
        <hello-c></hello-c>
    </div>
    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("hello-c",{
            template:"<div>hello 組件</div>"
        })
        new Vue({
            data:{
                msg:"Hello Vue"
            }
        }).$mount("#app");
    </script>
</body>

然后我們就可以在瀏覽器中看見(jiàn)hello組件了,這樣便意味著我們已經(jīng)自己制作了一個(gè)組件了,大家可能在制作組件的時(shí)候有一點(diǎn)不爽的就是為什么template寫(xiě)html代碼沒(méi)有代碼提示什么都要手寫(xiě)就是不爽對(duì)吧,所以我們可以使用這種方法定義template

<body>
    <div id="app">
        {{msg}}
        <hello-c></hello-c>
    </div>

    <template id="hello-c">
        <div>
            <h1>hello組件</h1>
        </div>
    </template>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("hello-c",{
            template:"#hello-c"
        })
        new Vue({
            data:{
                msg:"Hello Vue"
            }
        }).$mount("#app");
    </script>
</body>

我們可以直接將組件的模板寫(xiě)在template標(biāo)簽中,這樣就又有了我們熟悉的代碼提示了(是不是非常棒),同時(shí)我們自定義的組件就跟新建Vue的實(shí)例一樣大多數(shù)關(guān)于組件的方法都會(huì)有比如說(shuō)data啊methods啊都有

這樣就繼承了Vue的大多數(shù)屬性

<body>
    <div id="app">
        {{msg}}
       <Counter></Counter>
    </div>

    <template id="counter">
        <div>
           <button type="button" @click="count++">你一共點(diǎn)擊了我{{count}}次</button>
        </div>
    </template>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("Counter",{
            template:"#counter",
            data(){
                return {
                    count:0
                }
            }
        })
        new Vue({
            data:{
                msg:"Hello Vue"
            }
        }).$mount("#app");
    </script>
</body>

組件的生命周期
我覺(jué)得關(guān)于Vue組件的生命周期只需要一張圖就可以明白了

image.png

直接就將Vue官方的一張圖fetch過(guò)來(lái)了

  • beforeCreate() 在組件創(chuàng)建之前此時(shí)無(wú)法訪(fǎng)問(wèn)vue實(shí)例(this)
  • created() 創(chuàng)建完成之后已經(jīng)可以訪(fǎng)問(wèn)this實(shí)例和數(shù)據(jù)
  • beforeMount() 在組件掛載到視圖之前
  • mounted() 組件已經(jīng)掛載到視圖上以后
  • beforeUpdate() 改變數(shù)據(jù)觸發(fā)視圖更新之前
  • updated() 視圖已經(jīng)更新后
  • beforeDestroy() 組件將要被銷(xiāo)毀之前(下樹(shù),display:none不算,是銷(xiāo)毀)
  • destroyed() 組件銷(xiāo)毀之后

這就是Vue組件的生命周期了

<body>
    <div id="app">
        {{msg}}
       <Counter></Counter>
    </div>

    <template id="counter">
        <div>
           <button type="button" @click="count++">你一共點(diǎn)擊了我{{count}}次</button>
        </div>
    </template>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("Counter",{
            template:"#counter",
            data(){
                return {
                    count:0
                }
            },
            beforeCreate(){
                console.log("beforeCreate");
                
            },
            created(){
                console.log("created");
                
            },
            beforeMount(){
                console.log("beforeMount");
                
            },
            mounted(){
                console.log("mounted");
                
            },
            beforeUpdate(){
                console.log("beforeUpdate");
                
            },
            updated(){
                console.log("updated");
                
            },
            beforeDestroy(){
                console.log("beforeDestroy");
                
            },
            destroyed(){
                console.log("destroyed");
                
            }
        })
        new Vue({
            data:{
                msg:"Hello Vue"
            }
        }).$mount("#app");
    </script>
</body>
image.png

關(guān)于組件的生命周期就到這里了

組件的傳參
那么我們使用一個(gè)自定義組件肯定是需要?jiǎng)討B(tài)的顯示某些內(nèi)容的,這時(shí)候我們就需要傳遞一些參數(shù)過(guò)去了,那么組件之間是怎么傳參的呢?
通過(guò)props就可以實(shí)現(xiàn)組件之間的參數(shù)傳遞
我們看一下

<body>
    <div id="app">
        <h1>{{count}}</h1>
        <!-- 
            第一個(gè)count為props中聲明的可被接收的props,值為父組件中的count
            這里使用v-bind將不然count中的內(nèi)容不會(huì)被當(dāng)做js代碼解析
            v-bind:count 縮寫(xiě)為 :count 縮寫(xiě) :
        -->
        <input type="button" value="點(diǎn)我+1" @click="count++">
        <Counter :count="count"></Counter>
    </div>

    <template id="counter">
        <div>
           <button type="button" @click="count++">你一共點(diǎn)擊了我{{count}}次</button>
        </div>
    </template>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("Counter",{
            template:"#counter",
            // props屬性列舉了可被接收的參數(shù)
            props:['count']
        })
        new Vue({
            data:{
                count:0
            }
        }).$mount("#app");
    </script>
</body>

那么vue是不建議也不可以在子組件中直接操作props的,因?yàn)榇藀rops為父組件傳遞過(guò)來(lái)的,子組件無(wú)論怎么改變此props的值,當(dāng)父組件觸發(fā)虛擬DOM刷新頁(yè)面時(shí)都會(huì)重新傳遞props意味著子組件改變的props并不會(huì)長(zhǎng)久的儲(chǔ)存下去,所以Vue提供了一個(gè)類(lèi)似于自定義事件的這種機(jī)制

<body>
    <div id="app">
        <h1>{{count}}</h1>
        <!-- 
            第一個(gè)count為props中聲明的可被接收的props,值為父組件中的count
            這里使用v-bind將不然count中的內(nèi)容不會(huì)被當(dāng)做js代碼解析
            v-bind:count 縮寫(xiě)為 :count 縮寫(xiě) :
        -->
        <input type="button" value="點(diǎn)我+1" @click="count++">
        <!-- 通過(guò)在父組件中使用v-on指令監(jiān)聽(tīng)自定義事件名 -->
        <Counter :count="count" @mclick="count++"></Counter>
    </div>

    <template id="counter">
        <div>
            <!-- 使用$emit觸發(fā)一個(gè)自定義事件的事件名 -->
           <button type="button" @click="$emit('mclick')">你一共點(diǎn)擊了我{{count}}次</button>
        </div>
    </template>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("Counter",{
            template:"#counter",
            // props屬性列舉了可被接收的參數(shù)
            props:['count']
        })
        new Vue({
            data:{
                count:0
            }
        }).$mount("#app");
    </script>
</body>

兄弟們看注釋吧,代碼結(jié)合注釋這還不懂

(廢話(huà)一大堆:說(shuō)明不適合自學(xué)啊,這里說(shuō)一下我是自學(xué)的前端開(kāi)發(fā)的,不是不愿意去培訓(xùn)機(jī)構(gòu),相反其實(shí)還是挺想去的,只是因?yàn)榧彝サ脑?最主要是沒(méi)錢(qián)啊)所以只能苦逼的自學(xué)了,其實(shí)我想說(shuō)的是不管是去培訓(xùn)機(jī)構(gòu)還是自學(xué)還是在學(xué)校我想說(shuō)的就一點(diǎn),你永遠(yuǎn)也不可能會(huì)一直有老師帶著你,技術(shù)是在不斷的迭代的,誰(shuí)也不知道明天又有哪種框架哪種解決方案火了,就比如webassembly一樣,誰(shuí)知道那天會(huì)火說(shuō)不定到時(shí)候的前端真的可以說(shuō)又是一場(chǎng)革命了,你想到時(shí)候的前端有接近原生的代碼執(zhí)行效率,無(wú)論是開(kāi)發(fā)網(wǎng)站還是配合electron或者nw開(kāi)發(fā)跨平臺(tái)的桌面應(yīng)用或者搭配react native或uni-app或weex等等開(kāi)發(fā)移動(dòng)端的軟件,毫無(wú)疑問(wèn)的一點(diǎn)就是以后的前端誰(shuí)也說(shuō)不準(zhǔn),所以我覺(jué)得自學(xué)反而是好的,自學(xué)的過(guò)程中雖然很難但反而是最容易學(xué)通學(xué)透的因?yàn)槲覀儧](méi)有人為我們解答問(wèn)題,每一個(gè)問(wèn)題都是需要自己去研究解決的,可以說(shuō)每一個(gè)人都在學(xué)習(xí),不過(guò)有的人是在學(xué)校學(xué)習(xí)有的人在工作的時(shí)候?qū)W習(xí),或者說(shuō)每個(gè)人一直都在學(xué)習(xí),比如你剛出生你不學(xué)走路,不學(xué)說(shuō)話(huà),等等...)

咳咳扯遠(yuǎn)了,總而言之大家可以想象一下以后的前端是一個(gè)怎么樣的情況,現(xiàn)在webassembly已經(jīng)支持c/c++/rust語(yǔ)言編譯為asm.js這種編譯的JavaScript代碼進(jìn)行在瀏覽器端執(zhí)行了

我們接著說(shuō)一下,子組件通過(guò)自定義事件將自己的需求暴露給父組件,由父組件負(fù)責(zé)數(shù)據(jù)的更新(值是由父組件傳遞下來(lái)的,不父組件維護(hù)誰(shuí)維護(hù))
那么我想要求子組件點(diǎn)擊按鈕不跟父組件的按鈕一樣了,我要點(diǎn)擊子組件的按鈕我想+多少就多少應(yīng)該怎么辦呢?
那么我們繼續(xù)完善代碼

<body>
    <div id="app">
        <h1>{{count}}</h1>
        <!-- 
            第一個(gè)count為props中聲明的可被接收的props,值為父組件中的count
            這里使用v-bind將不然count中的內(nèi)容不會(huì)被當(dāng)做js代碼解析
            v-bind:count 縮寫(xiě)為 :count 縮寫(xiě) :
        -->
        <input type="button" value="點(diǎn)我+1" @click="count++">
        <!-- 通過(guò)在父組件中使用v-on指令監(jiān)聽(tīng)自定義事件名 -->
        <!-- 這里的$event就是傳遞過(guò)來(lái)的參數(shù) -->
        <Counter :count="count" @mclick="count+=$event"></Counter>
    </div>

    <template id="counter">
        <div>
            <!-- 使用$emit觸發(fā)一個(gè)自定義事件的事件名 -->
            <!-- 添加一個(gè)輸入框,輸入多少就+多少的count -->
            <p><input type="text" v-model.number="sCount"></p>
            <!-- 可以通過(guò)第二個(gè)參數(shù)給父組件的handle傳遞參數(shù) -->
           <button type="button" @click="$emit('mclick',sCount)">你一共點(diǎn)擊了我{{count}}次</button>
        </div>
    </template>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("Counter",{
            template:"#counter",
            // props屬性列舉了可被接收的參數(shù)
            props:['count'],
            data(){
                return {
                    sCount:0
                }
            }
        })
        new Vue({
            data:{
                count:0
            },
            methods:{
                addHandle(num){
                    this.count+=num;
                }
            }
        }).$mount("#app");
    </script>
</body>

這時(shí)候我們就可以通過(guò)自定義事件傳參的方式,來(lái)實(shí)現(xiàn)這個(gè)需求了,如果傳遞了多個(gè)參數(shù)請(qǐng)?jiān)诟附M件中@自定義事件后傳入一個(gè)函數(shù)接收多個(gè)參數(shù)

這里我又想實(shí)現(xiàn)一個(gè)自定義的輸入框,但是當(dāng)我輸入v-model的時(shí)候我發(fā)現(xiàn)這個(gè)指令失效了?

<body>
    <div id="app">
        <h1>{{msg}}</h1>

        <custom-input v-model="msg"></custom-input>
    </div>

    <template id="cinput">
        <input type="text">
    </template>
    
    <template id="counter">
        <div>
            <!-- 使用$emit觸發(fā)一個(gè)自定義事件的事件名 -->
            <!-- 添加一個(gè)輸入框,輸入多少就+多少的count -->
            <p><input type="text" v-model.number="sCount"></p>
            <!-- 可以通過(guò)第二個(gè)參數(shù)給父組件的handle傳遞參數(shù) -->
           <button type="button" @click="$emit('mclick',sCount,'hello')">你一共點(diǎn)擊了我{{count}}次</button>
        </div>
    </template>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("Counter",{
            template:"#counter",
            // props屬性列舉了可被接收的參數(shù)
            props:['count'],
            data(){
                return {
                    sCount:0
                }
            }
        })
        Vue.component("custom-input",{
            template:"#cinput",
            props:['value']
        })
        new Vue({
            data:{
                count:0,
                msg:"Hello"
            },
            methods:{
                addHandle(num,c){
                    console.log(c);
                    
                    this.count+=num;
                }
            }
        }).$mount("#app");

因?yàn)槲覀儾荒苤苯咏o自定義組件綁定v-model而是應(yīng)該給組件里面的元素綁定,但是如果我們給子組件中的input標(biāo)簽綁定v-model的話(huà)就會(huì)出現(xiàn)(因?yàn)殡p向綁定的值是父親的,但是與之綁定的視圖確實(shí)兒子的)父組件一但刷新props重新賦值為父組件的傳遞過(guò)來(lái)的值了,這個(gè)時(shí)候應(yīng)該怎么班呢?我們就應(yīng)該自己實(shí)現(xiàn)一個(gè)v-model了

通過(guò)打印發(fā)現(xiàn)我們是能夠得到v-model綁定的msg值的


image.png

然后我們就需要思考如何讓這個(gè)值與之產(chǎn)生關(guān)聯(lián),首先無(wú)論如何我們肯定需要先v-bind先單向?qū)?shù)據(jù)與視圖建立聯(lián)系才行,通過(guò)v-bind我們已經(jīng)能夠使input標(biāo)簽顯示msg的值了


image.png
image.png

然后我記得v-model的時(shí)候我有提到過(guò),v-model的雙向數(shù)據(jù)綁定是通過(guò)元素的input事件進(jìn)行綁定的可以通過(guò)lazy修飾符改為change事件,所以這個(gè)時(shí)候我要干什么大家應(yīng)該都知道了吧

<body>
    <div id="app">
        <h1>{{msg}}</h1>

        <custom-input v-model="msg"></custom-input>
    </div>

    <template id="cinput">
        <input type="text" :value="value" @input="$emit('input', $event.target.value)">
    </template>
    
    <template id="counter">
        <div>
            <!-- 使用$emit觸發(fā)一個(gè)自定義事件的事件名 -->
            <!-- 添加一個(gè)輸入框,輸入多少就+多少的count -->
            <p><input type="text" v-model.number="sCount"></p>
            <!-- 可以通過(guò)第二個(gè)參數(shù)給父組件的handle傳遞參數(shù) -->
           <button type="button" @click="$emit('mclick',sCount,'hello')">你一共點(diǎn)擊了我{{count}}次</button>
        </div>
    </template>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("Counter",{
            template:"#counter",
            // props屬性列舉了可被接收的參數(shù)
            props:['count'],
            data(){
                return {
                    sCount:0
                }
            }
        })
        Vue.component("custom-input",{
            template:"#cinput",
            props:['value'],
            created(){
                console.log(this.value);
            }
        })
        new Vue({
            data:{
                count:0,
                msg:"Hello"
            },
            methods:{
                addHandle(num,c){
                    console.log(c);
                    
                    this.count+=num;
                }
            }
        }).$mount("#app");
    </script>
</body>

使用v-on綁定input事件在代碼中emit一個(gè)input事件到自定義組件身上,這樣就可以被v-model接收到了,從而完成雙向數(shù)據(jù)綁定,懂了嗎?

Vue提供了一個(gè)自定義組件用于動(dòng)態(tài)顯示我們的自定義組件的

<body>
    <div id="app">
        <h1>{{msg}}</h1>

        <!-- 
            真的不想寫(xiě)注釋了,但是我還是要寫(xiě),vue指令后面引號(hào)中的字符會(huì)被當(dāng)做js代碼解析就像執(zhí)行eval一樣
            is屬性制定了一個(gè)要顯示的自定義組件名稱(chēng)
         -->
         <input type="button" value="點(diǎn)我切換組件" @click="type == 'custom-input' ? type = 'Counter' : type = 'custom-input'">
        <component :is="type"></component>
    </div>

    <template id="cinput">
        <input type="text" :value="value" @input="$emit('input', $event.target.value)">
    </template>
    
    <template id="counter">
        <div>
            <!-- 使用$emit觸發(fā)一個(gè)自定義事件的事件名 -->
            <!-- 添加一個(gè)輸入框,輸入多少就+多少的count -->
            <p><input type="text" v-model.number="sCount"></p>
            <!-- 可以通過(guò)第二個(gè)參數(shù)給父組件的handle傳遞參數(shù) -->
           <button type="button" @click="$emit('mclick',sCount,'hello')">你一共點(diǎn)擊了我{{count}}次</button>
        </div>
    </template>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("Counter",{
            template:"#counter",
            // props屬性列舉了可被接收的參數(shù)
            props:['count'],
            data(){
                return {
                    sCount:0
                }
            }
        })
        Vue.component("custom-input",{
            template:"#cinput",
            props:['value'],
            beforeDestroy(){
                console.log('beforeDestroy')
            },
            destroyed(){
                console.log("destroyed")
            }
        })
        new Vue({
            data:{
                count:0,
                msg:"Hello",
                type:"custom-input"
            },
            methods:{
                addHandle(num,c){
                    console.log(c);
                    
                    this.count+=num;
                }
            }
        }).$mount("#app");
    </script>
</body>

component標(biāo)簽用于控制渲染我們的組件通過(guò)is屬性動(dòng)態(tài)渲染不同的組件已達(dá)到更好的交互效果,這里組件的切換是上樹(shù)下樹(shù)級(jí)別的


image.png

因?yàn)榻M件執(zhí)行了生命周期中的destroyed方法
(那么關(guān)于Vue的基礎(chǔ)就已經(jīng)說(shuō)完了恳守,接下來(lái)就是Vue深入組件的一個(gè)部分了)

深入了解組件

組件注冊(cè)

首先關(guān)于組件命名的要求考婴,按照Vue官方所說(shuō)的那樣組件命名有兩種方式
第一種:custom-input 就是字母之間或者組件名之中必須出現(xiàn)一個(gè)橫線(xiàn)用以區(qū)分原生標(biāo)簽
第二種:首字母大寫(xiě) CustomInput 駝峰命名法,大家隨意喜歡那種都是可以的

然后就是到目前為止我們注冊(cè)組件還是使用的是Vue.component這種注冊(cè)組件的方式是全局注冊(cè)的催烘,全局注冊(cè)的組件無(wú)論在哪一種層級(jí)還是哪一個(gè)Vue實(shí)例都是可以使用的沥阱,接下來(lái)給大家說(shuō)說(shuō)如何局部注冊(cè)組件,局部注冊(cè)的組件只能在當(dāng)前注冊(cè)的組件中使用

<body>
    <div id="app">
        {{msg}}
        <!-- 這里Vue會(huì)自動(dòng)將后面首字母大寫(xiě)的字母改為小寫(xiě)并且加上橫線(xiàn) -->
        <Custom-div></Custom-div>
    </div>
    <script src="./lib/vue.js"></script>
    <script>
        var CustomInput = {
            template:`
            <div>我是自定義input組件</div>
            `
        }
        var CustomDiv = {
            template:`
            <div>我是自定義div組件</div>
            `,
            components:{
                CustomInput
            }
        }
        var vm = new Vue({
            el:"#app",
            data() {
                return {
                    msg:"Hello Vue"
                }
            },
            components:{
                // 鍵值對(duì)相同可以直接寫(xiě)一個(gè)在這里
                CustomDiv
            }
        })
    </script>
</body>

然后我們?cè)谶@里一共定義了兩個(gè)組件其中一個(gè)組件注冊(cè)為另一個(gè)組件的子組件伊群,且這個(gè)組件注冊(cè)到Vue實(shí)例上考杉,那么我們接下來(lái)直接在html中訪(fǎng)問(wèn)這個(gè)CustomInput組件看看會(huì)是什么情況,你會(huì)發(fā)現(xiàn)Vue不出意外的報(bào)錯(cuò)了


image.png
    <div id="app">
        {{msg}}
        <!-- 這里Vue會(huì)自動(dòng)將后面首字母大寫(xiě)的字母改為小寫(xiě)并且加上橫線(xiàn) -->
        <Custom-div></Custom-div>
        <Custom-input></Custom-input>
    </div>

那么我們接下來(lái)將這個(gè)組件放到customdiv組件中試試

<body>
    <div id="app">
        {{msg}}
        <!-- 這里Vue會(huì)自動(dòng)將后面首字母大寫(xiě)的字母改為小寫(xiě)并且加上橫線(xiàn) -->
        <Custom-div></Custom-div>

    </div>
    <script src="./lib/vue.js"></script>
    <script>
        var CustomInput = {
            template: `
            <h1>我是自定義Input組件</h1>
            `
        }
        var CustomDiv = {
            // 子組件放在html代碼中
            template: `
            <div>
                <p>我是自定義div組件</p>
                <Custom-input></Custom-input>
            </div>
            `,
            components: {
                CustomInput
            }
        }
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    msg: "Hello Vue"
                }
            },
            components: {
                // 鍵值對(duì)相同可以直接寫(xiě)一個(gè)在這里
                CustomDiv
            }
        })
    </script>
</body>
image.png

那么我們發(fā)現(xiàn)不出意外組件已經(jīng)正常渲染了在岂,因?yàn)閂ue實(shí)例的template已經(jīng)掛在到div#app上了奔则,所以我們的子組件一定要寫(xiě)在template上而不是那個(gè)標(biāo)簽中的內(nèi)部

Prop

HTML 中的特性名是大小寫(xiě)不敏感的,所以瀏覽器會(huì)把所有大寫(xiě)字符解釋為小寫(xiě)字符蔽午。這意味著當(dāng)你使用 DOM 中的模板時(shí)易茬,camelCase (駝峰命名法) 的 prop 名需要使用其等價(jià)的 kebab-case (短橫線(xiàn)分隔命名) 命名:

Vue.component('blog-post', {
  // 在 JavaScript 中是 camelCase 的
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
}

<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>

那么這個(gè)限制在字符串模板是沒(méi)有限制的,前面也說(shuō)了template屬性可以給一個(gè)選擇器用來(lái)定位html template標(biāo)簽中的內(nèi)容,這就是所謂的DOM模板,字符串模板就是我下面這樣template之后直接手寫(xiě)這個(gè)模板,但是你也知道的沒(méi)有代碼提示純手寫(xiě)賊難受

<body>
    <div id="app">
        {{msg}}
        <!-- 這里Vue會(huì)自動(dòng)將后面首字母大寫(xiě)的字母改為小寫(xiě)并且加上橫線(xiàn) -->
        <Custom-div></Custom-div>

    </div>
    <script src="./lib/vue.js"></script>
    <script>
        var CustomInput = {
            template: `
                <h1>我是自定義Input組件</h1>
                `,
            props:["mData"],
            // 字符串模板中不需要m-data
            mounted() {
                console.log(this.mData);
            },
        }
        var CustomDiv = {
            // 子組件放在html代碼中
            template: `
                <div>
                    <p>我是自定義div組件</p>
                    <Custom-input mData="Hello"></Custom-input>
                </div>
                `,
            components: {
                CustomInput
            }
        }
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    msg: "Hello Vue"
                }
            },
            components: {
                // 鍵值對(duì)相同可以直接寫(xiě)一個(gè)在這里
                CustomDiv
            }
        })
    </script>
</body>

那么關(guān)于props首先要了解的就是組件中props屬性的類(lèi)型,前面我們寫(xiě)的props都是數(shù)組的形式出現(xiàn)的就像我上面貼的代碼一樣,其實(shí)props屬性還可以是一個(gè)對(duì)象,且看下方代碼

<body>
    <div id="app">
        <!-- 使用v-bind -->
        <my-component :username="username" :password="password" :age="age" ></my-component>
    </div>

    <template id="mcom">
        <div>
            我接受的username是{{username}} 我接受的password是{{password}} <br>
            我接受的age是{{age}}
        </div>
    </template>
    
    <script src="./lib/vue.js"></script>
    <script>
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    username:"小鋼炮",
                    password:"xiaogangpao",
                    // 故意寫(xiě)一個(gè)字符串
                    age:"18"
                }
            },
            components:{
                // 我這里只是把對(duì)象直接寫(xiě)在這里的,不會(huì)看不懂吧
                myComponent:{
                    template:"#mcom",
                    props:{
                        // 那么這里就有三個(gè)props需要傳遞過(guò)來(lái)
                        // 名稱(chēng)就是鍵及老,類(lèi)型就是值
                        username:String,
                        password:String,
                        age:Number
                    }
                }
            }
        })
    </script>
</body>

那么我這里故意將age設(shè)置為字符串不出所料的是果真是報(bào)錯(cuò)了,出乎意料的是值還是顯示出來(lái)了


image.png

那么我將age重新改為number類(lèi)型的時(shí)候報(bào)錯(cuò)就消失了


image.png

就不貼代碼了,這是應(yīng)該知道怎么改的

那么我們可以直接這樣傳遞對(duì)象

        <my-component :obj="mobj"></my-component>

    <script>
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    mobj: {
                        username: "小鋼炮",
                        password: "xiaogangpao",
                        // 故意寫(xiě)一個(gè)字符串
                        age: 18
                    }
                }
            },
            components: {
                // 我這里只是把對(duì)象直接寫(xiě)在這里的抽莱,不會(huì)看不懂吧
                myComponent: {
                    template: "#mcom",
                    props: ["obj"],
                    mounted() {
                        console.log(this.obj)
                    },
                }
            }
        })
    </script>

然后可能我們需要傳遞對(duì)象的屬性進(jìn)去,那么vue就提供了一個(gè)非常簡(jiǎn)便的方法,那么可能我們正常需要這樣寫(xiě)

        <my-component :username="mobj.username" :password="mobj.password" :age="mobj.age" ></my-component>

那么Vue就提供了一個(gè)簡(jiǎn)單的方法快速通過(guò)props傳遞一個(gè)對(duì)象的所有屬性

<my-component v-bind="mobj" ></my-component>
    <script>
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    mobj: {
                        username: "小鋼炮",
                        password: "xiaogangpao",
                        // 故意寫(xiě)一個(gè)字符串
                        age: 18
                    }
                }
            },
            components: {
                // 我這里只是把對(duì)象直接寫(xiě)在這里的,不會(huì)看不懂吧
                myComponent: {
                    template: "#mcom",
                    // 這里就可以接收所有對(duì)象的屬性
                    props: ["username","password","age"],
                    mounted() {
                        // 這里就可以進(jìn)行打印了
                        console.log(this.username,this.password,this.age)
                    },
                }
            }
        })
    </script>

大家記住,這種方法一定要在元素中props中寫(xiě)對(duì)象的屬性,不要傻乎乎的寫(xiě)對(duì)象的名字在哪里啊

單向數(shù)據(jù)流

大家應(yīng)該也知道我之前有說(shuō)過(guò)從父組件傳遞下來(lái)的props是不能進(jìn)行修改的,因?yàn)槟呐履憬邮盏膒rops如何修改,當(dāng)父組件觸發(fā)虛擬DOM時(shí)props又會(huì)進(jìn)行重新傳遞導(dǎo)致子組件重新渲染新的props值,這就是所謂的數(shù)據(jù)的單向流動(dòng),即數(shù)據(jù)總是自頂向下一層一層傳遞的(可能這時(shí)候大家就會(huì)想了,如果層級(jí)非常多的話(huà)豈不是從頂級(jí)組件傳遞下來(lái)會(huì)有那么幾個(gè)組件只負(fù)責(zé)props傳遞數(shù)據(jù)到目標(biāo)組件上,那么這個(gè)問(wèn)題我們需要使用vuex進(jìn)行解決,后面說(shuō)),這樣我們就非常清楚數(shù)據(jù)的一個(gè)流動(dòng),并且如果子組件修改了props那么數(shù)據(jù)的流向也不是很明確了,那么如果確實(shí)需要修改props呢?這里有兩種解決方案,要么就是直接將傳遞過(guò)來(lái)的props綁定到自己的data上

props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}

另一種方法呢就是直接通過(guò)計(jì)算屬性依賴(lài)這個(gè)props的值計(jì)算我們需要的值

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

兩種方法各有各的應(yīng)用場(chǎng)景,這里就不深入了解了

注意在 JavaScript 中對(duì)象和數(shù)組是通過(guò)引用傳入的骄恶,所以對(duì)于一個(gè)數(shù)組或?qū)ο箢?lèi)型的 prop 來(lái)說(shuō)食铐,在子組件中改變這個(gè)對(duì)象或數(shù)組本身將會(huì)影響到父組件的狀態(tài)。

props驗(yàn)證
這個(gè)驗(yàn)證跟react的prop-types庫(kù)非常相似

                    props: {
                        // 基礎(chǔ)的類(lèi)型檢查 (`null` 和 `undefined` 會(huì)通過(guò)任何類(lèi)型驗(yàn)證)
                        // username:String,
                        // 多個(gè)可能的類(lèi)型
                        // age:[String,Number],

                        age: {
                            type: Number,
                            // 指定默認(rèn)值
                            default: 18
                        },

                        password: {
                            // 指定類(lèi)型
                            type: String,
                            // 必須傳遞
                            required: true
                        },

                        // 自定義驗(yàn)證函數(shù)
                        username: {
                            validator: function (value) {
                                // 這個(gè)值必須匹配下列字符串中的一個(gè)
                                return ['success', 'warning', 'danger'].indexOf(value) !== -1
                            }
                        }

                        // 也可以是一個(gè)帶有默認(rèn)值的對(duì)象
                        /*   obj:{
                              type:Object,
                              // 對(duì)象或者數(shù)組的默認(rèn)值必須從一個(gè)factory function(工廠(chǎng)函數(shù))中取
                              default(){
                                  return ["a",18,"c"]
                              }
                          } */
                    },

那么這個(gè)時(shí)候定義之后我們回到瀏覽器刷新就會(huì)發(fā)現(xiàn)


image.png

報(bào)錯(cuò)了,但是值也取到了,那么我們修改一下這個(gè)函數(shù)

                        // 自定義驗(yàn)證函數(shù)
                        username: {
                            validator: function (value) {
                                // 這個(gè)值必須匹配下列字符串中的一個(gè)
                                return ['小鋼炮', 'warning', 'danger'].indexOf(value) !== -1
                            }
                        }

這樣就ok了

注意那些 prop 會(huì)在一個(gè)組件實(shí)例創(chuàng)建之前進(jìn)行驗(yàn)證僧鲁,所以實(shí)例的屬性 (如 data虐呻、computed 等) 在 default 或 validator 函數(shù)中是不可用的。

類(lèi)型檢查
type 可以是下列原生構(gòu)造函數(shù)中的一個(gè):

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
    額外的寞秃,type 還可以是一個(gè)自定義的構(gòu)造函數(shù)斟叼,并且通過(guò) instanceof 來(lái)進(jìn)行檢查確認(rèn)。例如春寿,給定下列現(xiàn)成的構(gòu)造函數(shù):
function Person (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}
//你可以使用:
Vue.component('blog-post', {
  props: {
    author: Person
  }
})
//來(lái)驗(yàn)證 author prop 的值是否是通過(guò) new Person 創(chuàng)建的朗涩。

如果有這么一種情況,我們的組件是用來(lái)發(fā)布出去的,那么不知道用戶(hù)會(huì)怎么樣的去傳遞屬性,那么如果假如說(shuō)我使用我這個(gè)組件,我傳遞了一個(gè)我根本就沒(méi)有在props中定義過(guò)的屬性會(huì)怎么樣呢?

<body>
    <div id="app">
        <!-- 直接使用v-bind等于你需要傳遞所有屬性的對(duì)象 -->
        <my-component v-bind="mobj" class="red" data-data-picker="activated"></my-component>
    </div>

    <template id="mcom">
        <div class="black">
            Hello my
        </div>
    </template>

    <script src="./lib/vue.js"></script>
    <script>
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    mobj: {
                        username: "小鋼炮",
                        password: "xiaogangpao",
                        age: 18
                    }
                }
            },
            components: {
                myComponent: {
                    template: "#mcom",
                    props: {
                        // 基礎(chǔ)的類(lèi)型檢查 (`null` 和 `undefined` 會(huì)通過(guò)任何類(lèi)型驗(yàn)證)
                        // username:String,
                        // 多個(gè)可能的類(lèi)型
                        // age:[String,Number],

                        age: {
                            type: Number,
                            // 指定默認(rèn)值
                            default: 18
                        },

                        password: {
                            // 指定類(lèi)型
                            type: String,
                            // 必須傳遞
                            required: true
                        },

                        // 自定義驗(yàn)證函數(shù)
                        username: {
                            validator: function (value) {
                                // 這個(gè)值必須匹配下列字符串中的一個(gè)
                                return ['小鋼炮', 'warning', 'danger'].indexOf(value) !== -1
                            }
                        }

                        // 也可以是一個(gè)帶有默認(rèn)值的對(duì)象
                        /*   obj:{
                              type:Object,
                              // 對(duì)象或者數(shù)組的默認(rèn)值必須從一個(gè)factory function(工廠(chǎng)函數(shù))中取
                              default(){
                                  return ["a",18,"c"]
                              }
                          } */
                    },
                    mounted() {
                        console.log(this.username, this.password, this.age)
                    },
                }
            }
        })
    </script>

那么你會(huì)發(fā)現(xiàn),上面我的這個(gè)自定義組件的根元素有一個(gè)已有的類(lèi)名,我在使用的時(shí)候傳遞了兩個(gè)沒(méi)有定義的屬性過(guò)去,我們看看html架構(gòu)是個(gè)什么情況


image.png

你會(huì)發(fā)現(xiàn)沒(méi)有被props定義的屬性都會(huì)直接繼承到組件的唯一根元素身上,如果你不想組件的根元素繼承這些屬性的話(huà),你可以在定義組件的時(shí)候加上

inheritAttrs: false

那么我這里加一下,刷新的時(shí)候發(fā)現(xiàn)


image.png

我們沒(méi)有被props接收的屬性都被忽略了,class合并了后面有說(shuō)原因,那么我們使用了這個(gè)選項(xiàng)之后就沒(méi)辦法使用傳遞過(guò)來(lái)的屬性了嗎?其實(shí)Vue將沒(méi)有被props接收的自定義屬性都封裝到了一個(gè)組件實(shí)例的$attrs屬性上,我們修改打印一下

mounted() {
        console.log(this.$attrs)
  },
image.png
<body>
    <div id="app">
        <base-input
        v-model="username"
        required
        placeholder="type your username"
        ></base-input>
    </div>

    <template id="baseInput">
            <!-- 將所有的$attrs屬性都傳遞到props上 -->
        <label>
            <input type="text"
            v-bind="$attrs"
            :value="value"
            @input="$emit('input',$event.target.value)"
            >
        </label>
    </template>

    <script src="./lib/vue.js"></script>
    <script>
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    username:"suiuye"
                }
            },
            components: {
                baseInput:{
                    template:"#baseInput",
                    inheritAttrs:false,
                    props:["value"]
                }
            }
        })
    </script>
</body>
image.png

Vue官方說(shuō)過(guò)的一句話(huà)還是非常哲學(xué)的,對(duì)哲學(xué):
這個(gè)模式允許你在使用基礎(chǔ)組件的時(shí)候更像是使用原始的 HTML 元素,而不會(huì)擔(dān)心哪個(gè)元素是真正的根元素,沒(méi)錯(cuò)你會(huì)發(fā)現(xiàn)上面的baseInput這個(gè)自定義的組件我完全當(dāng)成input再用,因?yàn)閷傩允裁吹亩际怯玫膇nput的屬性

注意 inheritAttrs: false 選項(xiàng)不會(huì)影響 style 和 class 的綁定绑改。意思就是說(shuō)加了這個(gè)屬性,class和style還是會(huì)被合并,不會(huì)被這個(gè)屬性影響,我上面就是一個(gè)活生生的例子

自定義事件

不同于組件和 prop谢床,事件名不存在任何自動(dòng)化的大小寫(xiě)轉(zhuǎn)換。而是觸發(fā)的事件名需要完全匹配監(jiān)聽(tīng)這個(gè)事件所用的名稱(chēng).
(事件名不存在組件名那樣的自動(dòng)轉(zhuǎn)換大寫(xiě)字母的名稱(chēng)加橫線(xiàn),事件名必須$emit()什么事件名就必須@什么事件名稱(chēng))

借用Vue官方的一句話(huà)
(不同于組件和 prop厘线,事件名不會(huì)被用作一個(gè) JavaScript 變量名或?qū)傩悦锻龋跃蜎](méi)有理由使用 camelCase 或 PascalCase 了。并且 v-on 事件監(jiān)聽(tīng)器在 DOM 模板中會(huì)被自動(dòng)轉(zhuǎn)換為全小寫(xiě) (因?yàn)?HTML 是大小寫(xiě)不敏感的)造壮,所以 v-on:myEvent 將會(huì)變成 v-on:myevent——導(dǎo)致 myEvent 不可能被監(jiān)聽(tīng)到覆履。

因此,我們推薦你始終使用 kebab-case 的事件名。)

意思就是在template中@的事件名如果有大寫(xiě)字母會(huì)被自動(dòng)轉(zhuǎn)換成小寫(xiě)導(dǎo)致事件永遠(yuǎn)不會(huì)被監(jiān)聽(tīng)到,所以可以說(shuō)是自定義事件必須使用短橫線(xiàn)命名法類(lèi)似于 CustomEvent ---> custom-event ,除非是字符串模板否則可以說(shuō)是必須使用短橫線(xiàn)命名法

自定義組件的v-model --> vue2.2新增特性
比如說(shuō)我們自定義一個(gè)文本輸入框

<body>
    <div id="app">
        <!-- 這里將msg傳入了組件內(nèi) -->
        <!-- <base-input v-model="msg"></base-input> <br> -->
        <base-input v-model="msg"></base-input>
        {{msg}}
    </div>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("base-input",{
            model:{
                // 監(jiān)聽(tīng)根元素的value
                prop:'value',
                // 當(dāng)監(jiān)聽(tīng)根元素的input事件,如果改為change就會(huì)無(wú)效,因?yàn)橄旅娌](méi)有$emit(change),這里寫(xiě)什么下面就要emit什么
                event:'input'
            },
            props:['value'],
            template:`
            <input type="text" :value="value" @change="$emit('input',$event.target.value)" />
            `
        })
        
        
        new Vue({
            data: {
               msg:"Hello Vue"
            },
            methods: {

            }
        }).$mount("#app");
    </script>
</body>

這樣我們就自定義了一個(gè)input輸入框,但是個(gè)人感覺(jué)好像沒(méi)什么用處似的,有的時(shí)候我們可能很想監(jiān)聽(tīng)我們的自定義組件的原聲事件,那么v-model提供了一個(gè)修飾符

  • .native --> 用于綁定原生事件

可以這么認(rèn)為硝全,native就是一個(gè)把組件變回原生DOM的一種方法栖雾,給vue組件綁定事件的時(shí)候,一定要加上native伟众,如果是普通元素就不需要,默認(rèn)vue會(huì)將原生事件綁定到組件的根元素上,如果根元素不支持此事件就會(huì)靜默的失敗,例如

    <div id="app">
        <base-input @focus.native="onFocus"></base-input>
        {{msg}}
    </div>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("base-input",{
            // 我們就可以直接將$listeners屬性綁定到對(duì)應(yīng)的元素上
            template:`
            <div>
            <input type="text" v-on="$listeners" />
            </div>
            `
        })

因?yàn)閐iv獲取不了焦點(diǎn)那么綁定事件就會(huì)靜默失敗,所以vue官方提供了一個(gè)$listeners屬性,我們可以將我們定義的處理函數(shù)添加到這個(gè)屬性上然后綁定到一個(gè)特定的元素上,下面是vue官方的一個(gè)演示

Vue.component('base-input', {
  inheritAttrs: false,
  props: ['label', 'value'],
  computed: {
    inputListeners: function () {
      var vm = this
      // `Object.assign` 將所有的對(duì)象合并為一個(gè)新對(duì)象
      return Object.assign({},
        // 我們從父級(jí)添加所有的監(jiān)聽(tīng)器
        this.$listeners,
        // 然后我們添加自定義監(jiān)聽(tīng)器析藕,
        // 或覆寫(xiě)一些監(jiān)聽(tīng)器的行為
        {
          // 這里確保組件配合 `v-model` 的工作
          input: function (event) {
            vm.$emit('input', event.target.value)
          }
        }
      )
    }
  },
  template: `
    <label>
      {{ label }}
      <input
        v-bind="$attrs"
        v-bind:value="value"
        v-on="inputListeners"
      >
    </label>
  `
})

vue2.3新增的一個(gè)修飾符

  • .sync

這是一個(gè)v-bind的修飾符用于將props進(jìn)行雙向綁定

    <div id="app">
        <base-input v-bind:value.sync="msg"></base-input>
        {{msg}}
    </div>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("base-input",{
            props:['value'],
            template:`
            <input type="text"
            :value="value"
            @input="$emit('update:value', $event.target.value)"
            />
            `
            // 這里的update:是一個(gè)固定寫(xiě)法后面跟著你要更新的屬性名
            // 我這里要更新input的value屬性就寫(xiě)value
        })

插槽

關(guān)于插槽就要說(shuō)一下前面的v-slot指令了,前面的v-slot就是跟插槽有關(guān)的,那么我們需要先理解一下什么是插槽,先看一下下面的一個(gè)小例子

    <div id="app">
        <base-div></base-div>
    </div>

    <script src="../lib/vue.js"></script>
    <script>
    Vue.component("base-div",{
        template:`<div></div>`
    }) 

我這樣聲明使用這個(gè)組件是沒(méi)有什么問(wèn)題的,但是如果我要在組件中添加某些內(nèi)容呢,肯定是這樣的!

        <base-div>
        Hello World
    </base-div>

然后我們就慢慢等待這頁(yè)面打印出Hello World但是,頁(yè)面真的能夠打印出來(lái)嗎?


image.png

那么就會(huì)發(fā)現(xiàn)實(shí)際的渲染結(jié)果可以說(shuō)是差強(qiáng)人意,什么都沒(méi)有,相信大家都會(huì)犯這樣一個(gè)錯(cuò)誤吧(我都犯過(guò))總是把我們自定義的組件當(dāng)成html元素一樣往里面插入內(nèi)容甚至,有時(shí)候插入子組件也插入在這里面,這肯定是不對(duì)的,正確的做法是

//清空組件中的內(nèi)容
        <base-div></base-div>
//模板中寫(xiě)Hello World
        template:`<div>Hello World</div>`

這是渲染結(jié)果

最終的渲染結(jié)果就非常正確了,那么我們肯定希望能夠在組件標(biāo)簽中直接插入內(nèi)容了,方便啊,還有就是非常直觀(guān),因?yàn)槟阋粋€(gè)組件包括子組件還有內(nèi)容全在DOM模板或者字符串模板中,那么我們定位組件的時(shí)候都必須在DOM模板或者字符串模板中進(jìn)行編輯,這樣的話(huà)乍一看html代碼會(huì)非常懵逼,我們維護(hù)的時(shí)候就必須一層一層的去尋找模板查看了,大家應(yīng)該懂這個(gè)意思吧?

所以Vue提供了插槽的特性,插槽就可以讓我們直接渲染組件標(biāo)簽中的內(nèi)容,我們立馬嘗試吧

我們首先將組件改會(huì)原本的狀態(tài)

然后我們繼續(xù)在組件標(biāo)簽中添加Hello World內(nèi)容,然后我們只需要在組件模板中添加一個(gè)標(biāo)簽就行了

    Vue.component("base-div",{
        template:`
        <div>
//只需要添加一個(gè)slot標(biāo)簽就行了
            <slot></slot>
        </div>
        `
    }) 

然后我們就會(huì)驚奇的發(fā)現(xiàn)內(nèi)容成功被渲染出來(lái)了

渲染成功

這就是插槽了,就是提供了一個(gè)接口用于防止組件標(biāo)簽中的內(nèi)容,關(guān)于插槽的默認(rèn)值,某些情況下我們總希望我們沒(méi)有給插槽數(shù)據(jù)的時(shí)候插槽能夠渲染一個(gè)默認(rèn)的值,如下


一個(gè)case

這樣的渲染是常有的,我們可能就是希望組件按鈕的值能夠動(dòng)態(tài)的改變,但是我們還是希望當(dāng)我們不傳遞任何值的話(huà)能夠出現(xiàn)一個(gè)默認(rèn)情況,那么我們可以這樣做


image.png

那么插槽其實(shí)是可以訪(fǎng)問(wèn)其所在層級(jí)的內(nèi)容的,例如


插槽訪(fǎng)問(wèn)數(shù)據(jù)是有作用域的,插槽的作用域其實(shí)就相當(dāng)于自定義組件的作用于,你就當(dāng)那么自定義組件標(biāo)簽就一個(gè)div,那么作用域就是外層父親了,這里插槽的作用域就是外邊的#app,插槽無(wú)法訪(fǎng)問(wèn)同組件中的內(nèi)容,例如這里我傳遞一些數(shù)據(jù)進(jìn)去

<body>
    <div id="app">
        <button-submit :mdataa="mdata">
            <!-- 這里訪(fǎng)問(wèn)不了button-submit組件的作用域 -->
            <!-- 失敗 -->
            {{mdataa}}
        </button-submit>
    </div>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("button-submit", {
            props:["mdataa"],
            // 如果沒(méi)有傳遞值,就會(huì)是提交,如果傳遞了那么是什么就是什么
            template: `
            <div>
            <button type="button">
            <slot>提交</slot>
            </button?>

            {{mdataa}}
            </div>
            `
        })

        new Vue({
            data: {
                msg: "Hello Vue",
                mdata:"這是數(shù)據(jù),傳遞的props插槽無(wú)法訪(fǎng)問(wèn)"
            }
        }).$mount("#app");
    </script>
</body>

像這個(gè)例子



Vue毫不客氣的報(bào)錯(cuò)并且不會(huì)獲得任何數(shù)據(jù)

具名插槽

聽(tīng)名字就知道就是有具體名字的插槽了,什么意思呢?具名插槽有什么用?
有時(shí)我們需要向一個(gè)組件中傳遞多個(gè)插槽(一個(gè)slot就是一個(gè)插槽),那么肯定這樣是不對(duì)的

<body>
    <div id="app">
        <button-submit :mdataa="mdata">
            <!-- 這里訪(fǎng)問(wèn)不了button-submit組件的作用域 -->
            <!-- 失敗 -->
            我
        </button-submit>
    </div>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("button-submit", {
            props:["mdataa"],
            // 如果沒(méi)有傳遞值,就會(huì)是提交,如果傳遞了那么是什么就是什么
            template: `
            <div>
            <button type="button">
            <slot>提交</slot>
            <slot>提交</slot>
            <slot>提交</slot>
            <slot>提交</slot>
            </button?>

            {{mdataa}}
            </div>
            `
        })

        new Vue({
            data: {
                msg: "Hello Vue",
                mdata:"這是數(shù)據(jù),傳遞的props插槽無(wú)法訪(fǎng)問(wèn)"
            }
        }).$mount("#app");
    </script>
</body>

這樣的話(huà)就相當(dāng)于重復(fù)了幾次插槽的內(nèi)容而已,完全無(wú)法分隔開(kāi)各插槽之間的內(nèi)容,這個(gè)時(shí)候具名插槽的作用就來(lái)到了,我們可以這樣書(shū)寫(xiě)代碼實(shí)現(xiàn)不同插槽之間的分隔

<body>
    <div id="app">
        <base-layout>
            header

            nav

            section

            footer
        </base-layout>
    </div>

    <template id="base-layout">
        <div>
            <!-- 
                對(duì)于一下組件我們可能需要往不同的標(biāo)簽中插入不同的內(nèi)容
                所以我們?cè)诿總€(gè)標(biāo)簽中放一個(gè)插槽進(jìn)去,但是這樣根本無(wú)法實(shí)現(xiàn)我們的需求
                這樣只會(huì)讓一個(gè)插槽重復(fù)四次罷了
             -->
            <header>
                <slot></slot>
            </header>
            <nav>
                <slot></slot>
            </nav>
            <section>
                <slot></slot>
            </section>
            <footer>
                <slot></slot>
            </footer>
        </div>
    </template>
<body>
    <div id="app">
        <base-layout>
            <!-- 
            為了對(duì)應(yīng)插槽的具體名字我們需要使用DOM模板
            模板使用v-slot指令,參數(shù)就是對(duì)應(yīng)插槽的名字
            這個(gè)指令是下面這樣寫(xiě),跟平常的指令不一樣
         -->
            <template v-slot:header>
                header
            </template>
            <template v-slot:nav>
                nav
            </template>
            <template v-slot:section>
                section
            </template>
            <template v-slot:footer>
                footer
            </template>
        </base-layout>
    </div>

    <template id="base-layout">
        <div>
            <!-- 
                對(duì)于一下組件我們可能需要往不同的標(biāo)簽中插入不同的內(nèi)容
                所以我們?cè)诿總€(gè)標(biāo)簽中放一個(gè)插槽進(jìn)去,但是這樣根本無(wú)法實(shí)現(xiàn)我們的需求
                這樣只會(huì)讓一個(gè)插槽重復(fù)四次罷了,所以插槽提供了一個(gè)name屬性用于指定具體的名字
             -->
            <header>
                <slot name="header"></slot>
            </header>
            <nav>
                <slot name="nav"></slot>
            </nav>
            <section>
                <slot name="section"></slot>
            </section>
            <footer>
                <slot name="footer"></slot>
            </footer>
        </div>
    </template>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("base-layout", {
            template: "#base-layout"
        })

        new Vue({
            data: {
                msg: "Hello Vue",
                mdata: "這是數(shù)據(jù),傳遞的props插槽無(wú)法訪(fǎng)問(wèn)"
            }
        }).$mount("#app");
    </script>
</body>

這樣我們就分隔開(kāi)了每一個(gè)插槽,這就是具名插槽了,那么其實(shí)默認(rèn)的<slot></slot>也是有名字的叫做default,我們也可以指令v-slot:default或者不指定名字的標(biāo)簽當(dāng)會(huì)傳入到默認(rèn)的插槽中,如果沒(méi)有提供默認(rèn)插槽那么就會(huì)放棄對(duì)應(yīng)默認(rèn)插槽中的內(nèi)容

image.png

image.png

關(guān)于具名插槽就介紹到此了,接下來(lái)說(shuō)一下作用于插槽

作用域插槽

簡(jiǎn)單來(lái)說(shuō)就是讓插槽中的內(nèi)容訪(fǎng)問(wèn)對(duì)應(yīng)組件中的內(nèi)容

<body>
    <div id="app">
        <base-layout>
            <!-- 但是插槽中訪(fǎng)問(wèn)不了此數(shù)據(jù),訪(fǎng)問(wèn)的是父親#app中的數(shù)據(jù)所以會(huì)報(bào)錯(cuò) -->
            {{username}}
        </base-layout>
    </div>

    <template id="base-layout">
        <div>
            <!-- 這里是能夠訪(fǎng)問(wèn)自己組件中的數(shù)據(jù)的 -->
            {{username}}
            <slot></slot>
        </div>
    </template>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("base-layout", {
            template: "#base-layout",
            data(){
                return {
                    username:"suiyue"
                }
            }
        })

        new Vue({
            data: {
                msg: "Hello Vue",
                mdata: "這是數(shù)據(jù),傳遞的props插槽無(wú)法訪(fǎng)問(wèn)"
            }
        }).$mount("#app");
    </script>
</body>
image.png

這就代表著默認(rèn)插槽的作用域與其所在的組件同級(jí)(不是父子是同級(jí))所以無(wú)法訪(fǎng)問(wèn)其所在組件的一些數(shù)據(jù)

那么我們就需要這么玩了

<body>
    <div id="app">
        <base-layout>
            <!-- 但是插槽中訪(fǎng)問(wèn)不了此數(shù)據(jù),訪(fǎng)問(wèn)的是父親#app中的數(shù)據(jù)所以會(huì)報(bào)錯(cuò) -->
            <!-- 需要使用DOM模板標(biāo)簽接受插槽props -->
            <!-- 
                通過(guò)v-slot參數(shù)為對(duì)應(yīng)的插槽名稱(chēng) 等號(hào)后面的制定一個(gè)變量用于接受插槽props
                所有的插槽props都會(huì)變成屬性綁定到此變量上
            -->
            <template v-slot:default = "my_props" >
                {{my_props.username}}
            </template>
        </base-layout>
    </div>

    <template id="base-layout">
        <div>
            <!-- 這里是能夠訪(fǎng)問(wèn)自己組件中的數(shù)據(jù)的 -->
            {{username}}
            <!-- 
                使用v-bind指令將username屬性綁定在特定的插槽上
                通過(guò)這樣的方式傳遞數(shù)據(jù)給插槽,這樣的方式叫做插槽props
             -->
             <!-- 不用v-bind后面是字符串 -->
            <slot :username="username"></slot>
        </div>
    </template>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("base-layout", {
            template: "#base-layout",
            data(){
                return {
                    username:"suiyue"
                }
            }
        })

        new Vue({
            data: {
                msg: "Hello Vue",
                mdata: "這是數(shù)據(jù),傳遞的props插槽無(wú)法訪(fǎng)問(wèn)"
            }
        }).$mount("#app");
    </script>
</body>
作用域插槽效果

這樣我們就成功的將數(shù)據(jù)fetch到了,以上就是作用于插槽的使用講解了

上述作用于插槽在提供組件中如果沒(méi)有具名插槽時(shí)可以簡(jiǎn)化書(shū)寫(xiě)為


image.png

還可以進(jìn)一步簡(jiǎn)化

image.png

上述簡(jiǎn)寫(xiě)方法只能在組件中沒(méi)有具名插槽中使用,如果組件中有具名插槽還請(qǐng)不要使用簡(jiǎn)寫(xiě),否則語(yǔ)法會(huì)無(wú)效并且Vue會(huì)發(fā)出警告,切記

解構(gòu)插槽prop

作用域插槽的內(nèi)部工作原理是將你的插槽內(nèi)容包括在一個(gè)傳入單個(gè)參數(shù)的函數(shù)里

function (slotProps) {
  // 插槽內(nèi)容
}

所以我們可以直接對(duì)插槽進(jìn)行解構(gòu),像這樣



瀏覽器正常渲染輸出,同時(shí)我們可以在解構(gòu)時(shí)重命名插槽


image.png

我們?cè)O(shè)置可以直接給插槽賦默認(rèn)值

image.png

vue2.6+ 新增動(dòng)態(tài)插槽名

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>

就是可以通過(guò)一個(gè)變量或者計(jì)算屬性函數(shù)等等動(dòng)態(tài)計(jì)算插槽名字

具名插槽的縮寫(xiě),跟v-on和v-bind一樣具名插槽也是有縮寫(xiě)的,具名插槽的縮寫(xiě) v-slot:header ---> #header v-slot:縮寫(xiě)為#,跟其他指令的縮寫(xiě)一樣該縮寫(xiě)只有當(dāng)插槽有參數(shù)才有用,這意味著一下語(yǔ)法是無(wú)效的

<!-- 這樣會(huì)觸發(fā)一個(gè)警告 -->
<current-user #="{ user }">
  {{ user.firstName }}
</current-user>

我們必須明確制定他的參數(shù)才行

<current-user #default="{ user }">
  {{ user.firstName }}
</current-user>

異步組件

在大型應(yīng)用中,我們可能需要將應(yīng)用分割成小一些的代碼塊凳厢,并且只在需要的時(shí)候才從服務(wù)器加載一個(gè)模塊账胧。為了簡(jiǎn)化,Vue 允許你以一個(gè)工廠(chǎng)函數(shù)的方式定義你的組件先紫,這個(gè)工廠(chǎng)函數(shù)會(huì)異步解析你的組件定義治泥。Vue 只有在這個(gè)組件需要被渲染的時(shí)候才會(huì)觸發(fā)該工廠(chǎng)函數(shù),且會(huì)把結(jié)果緩存起來(lái)供未來(lái)重渲染.

image.png

這一個(gè)簡(jiǎn)單的例子演示了如何異步加載組件,你可以在此函數(shù)中執(zhí)行任何異步的請(qǐng)求最后通過(guò)resolve傳遞給vue

斷斷續(xù)續(xù)才發(fā)現(xiàn)居然寫(xiě)了這么多,所以我決定拆分開(kāi)來(lái)寫(xiě)了,因?yàn)関ue還有差不多這么一篇文章的知識(shí)還沒(méi)有接觸,所以我決定將另外的一些知識(shí)什么的都寫(xiě)在下一篇文章中,同時(shí)發(fā)現(xiàn)自己寫(xiě)得有那么一點(diǎn)點(diǎn)的亂,我決定最后來(lái)一個(gè)總結(jié)吧

最后來(lái)一個(gè)總結(jié)

  • 計(jì)算屬性 -->obj

需要依賴(lài)于組件的data才能夠進(jìn)行數(shù)據(jù)的更新,如果data不更新,計(jì)算屬性永遠(yuǎn)不更新

  • 監(jiān)聽(tīng) --->obj

監(jiān)聽(tīng)的屬性為函數(shù)名,每一次這個(gè)屬性的改變都會(huì)當(dāng)做參數(shù)傳給對(duì)應(yīng)的函數(shù),然后自定義邏輯

  • class與style的綁定

對(duì)象法 class: key --> 類(lèi)名 value-->bool(決定是否綁定此類(lèi)名)
style: key --->樣式名(駝峰) value---> 樣式的值
數(shù)組法 class: 每一個(gè)變量都表示類(lèi)名,值為bool
style -->每一個(gè)變量必須是一個(gè)對(duì)象,key-value代表樣式名和樣式值
同時(shí)也可以使用三元運(yùn)算符,或者直接引用data中的對(duì)象或者數(shù)組等等

  • 指令
    • v-if
    • v-else-if (必須在v-if后,否則無(wú)效)
    • v-else (必須在上面兩個(gè)指令后,否則無(wú)效)
    • v-show
    • v-for
    • v-on --> @
      -->事件修飾符
  1. stop
  2. prevent
  3. capture
  4. self
  5. once
  6. passive
    -->按鍵修飾符
  7. enter
  8. tab
  9. delete (捕獲“刪除”和“退格”鍵)
  10. esc
  11. space
  12. up
  13. down
  14. left
  15. right
    -->系統(tǒng)修飾鍵
  16. .ctrl
  17. .alt
  18. .shift
  19. .meta
    --->2.5新增鍵盤(pán)事件修飾符
  20. .exact
    --->2.2新增鼠標(biāo)按鍵修飾符
  21. .left
  22. .right
  23. .middle
    --->組件的修飾符
  24. .native
  • v-bind --> (:)
    -->修飾符
  1. .sync
<div id="app">
        <base-input v-bind:value.sync="msg"></base-input>
        {{msg}}
    </div>

    <script src="../lib/vue.js"></script>
    <script>
        Vue.component("base-input",{
            props:['value'],
            template:`
            <input type="text"
            :value="value"
            @input="$emit('update:value', $event.target.value)"
            />
            `
            // 這里的update:是一個(gè)固定寫(xiě)法后面跟著你要更新的屬性名
            // 我這里要更新input的value屬性就寫(xiě)value
        })
  • v-model
    --->修飾符
  1. .lazy
  2. .number
  3. .trim
  • v-text

  • v-html

  • v-choak

  • v-once

  • v-pre

  • v-slot

  • 數(shù)組更新檢測(cè)

data中數(shù)組的某一些方法也會(huì)觸發(fā)vue的視圖更新

  1. push()
  2. pop()
  3. shift()
  4. unshift()
  5. splice()
  6. sort()
  7. reverse()
  • 組件的生命周期
  • beforeCreate()
  • created()
  • beforeMount()
  • mounted()
  • beforeUpdate()
  • updated()
  • beforeDestroy()
  • destroyed()
  • 自定義事件

通過(guò)組件內(nèi)$emit一個(gè)事件,外部通過(guò)v-on接受事件

  • 組件的全局和局部注冊(cè)
  • 組件prop
  • 單向數(shù)據(jù)流
  • props驗(yàn)證
  • 插槽
    • 具名插槽
    • 作用域插槽
    • 解構(gòu)插槽prop
    • vue2.6+ 動(dòng)態(tài)插槽名 v-slot:[] v-bind:[]

好了差不多就是這樣了,接下來(lái)我會(huì)寫(xiě)得非常簡(jiǎn)潔了,下一章見(jiàn)!

最后如果你有什么學(xué)習(xí)困惑或者對(duì)于前端有什么想跟我探討的可以加下QQ群聯(lián)系我 , 78484------5854

latest update 2019-06-27 16:46:18

最后編輯于
?著作權(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)離奇詭異檬洞,居然都是意外死亡狸膏,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)添怔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)湾戳,“玉大人,你說(shuō)我怎么就攤上這事广料≡喝” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵性昭,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我县遣,道長(zhǎng)糜颠,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任萧求,我火速辦了婚禮其兴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘夸政。我一直安慰自己元旬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著匀归,像睡著了一般坑资。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上穆端,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天袱贮,我揣著相機(jī)與錄音,去河邊找鬼体啰。 笑死攒巍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的荒勇。 我是一名探鬼主播柒莉,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼沽翔!你這毒婦竟也來(lái)了兢孝?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤搀擂,失蹤者是張志新(化名)和其女友劉穎西潘,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(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
  • 文/蒙蒙 一驾锰、第九天 我趴在偏房一處隱蔽的房頂上張望卸留。 院中可真熱鬧,春花似錦椭豫、人聲如沸耻瑟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)喳整。三九已至谆构,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間算柳,已是汗流浹背低淡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(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

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