Vue.js破冰系列-2HTML模板與內(nèi)部指令

1概述

上一章我們說了Vue是用于構(gòu)建用戶界面的框架,它最終呈現(xiàn)給用戶的是一個一個HTML標(biāo)簽。Vue可以使用HTML模板和render函數(shù)這2種方式來構(gòu)建這些HTML標(biāo)簽。當(dāng)開發(fā)者使用了HTML模板來構(gòu)建界面時,Vue內(nèi)部會將其編譯成render函數(shù),最后渲染到掛載點上。render函數(shù)生成的是虛擬DOM,虛擬DOM是基于js的,相比于DOM的操作,開銷會小很多。

Vue是實例需要一個掛載點,這個掛載點是由Vue的el選項指定的,el的類型可以是string或HTMLElement類型,當(dāng)為string時,使用css選擇器。

<html>
  <body>
    <div id="app">
      <h1>Hello World!</h1>
    </div>
    <script>
        var vm = new Vue({
        el:"#app"
      });
    </script>
  </body>
</html>

上面使用了el類型為string的方式查找掛載點,#app為css的id選擇器。Vue實例會將自身生成的DOM替換掉掛載點的元素,這里我們沒有定義如何生成DOM,所以,Vue會將掛載點的HTML提取出來當(dāng)作模板生成DOM,替換掉原來的HTML標(biāo)簽。

2 HTML模板

HTML模板有4種方式定義,分別是:

  • Vue實例不定義,由Vue將掛載點的元素提取出來生成模板,可以理解為將模板寫在掛載點內(nèi),如上面的例子。

  • 將模板以字符串的形式寫在實例的配置選項template中

    <div id="app"></div>
    <script>
      var vm = new Vue({
          el:"#app",
        template:"<h1>Hello World!</h1>"
      });
    </script>
    

    實例掛載后,會將template中的字符串,轉(zhuǎn)換為html標(biāo)簽,替換掉app的內(nèi)容。

  • 使用x-template,在template中定義模板有些弊端,因為它是字符串,所以缺少高亮的語法支持,當(dāng)需要多行顯示是,需要用到\,體驗不好。x-template是將模板的定義放在scirpt標(biāo)簽中,然后在template中使用#引用該模板。

    <div id="app"></div>
    <!--使用x-template定義模板-->
    <script type="text/x-template" id="tempHello">
      <h1>Hello World!</h1>
    </script>
    <!--使用template選項引用x-template定義的模板-->
    <script>
      var vm = new Vue({
        el:"#app",
        template:"#tempHello",
      });
    </script>
    

    在定義x-template模板時,需要注意的是,x-template定義的模板在script標(biāo)簽中,type屬性為text/x-template,需要配上id,后續(xù)的template中才能夠引用。在template選項中要使用#引用x-template模板(類似使用css的id選擇器)。

  • 使用單文件組件(single-file components)的方式定義模板(最常用),單文件組件是以.vue結(jié)尾的文件,一個文件就是一個組件,后續(xù)講解組件時再詳細說明。

3 內(nèi)部指令(directive)

在HTML模板中,html元素僅僅是界面的呈現(xiàn),我們還需要和vue實例進行交互,這就需要用到vue的指令。在vue中,指令是寫在html元素中,以v-開頭,后接表達式(有些不需要表達式),它是Vue實例數(shù)據(jù)與用戶界面之間的紐帶。如:

<div id="app">
  <p v-text="msg">text</p>
</div>
<script>
    var vm = new Vue({
    el:"#app",
    data:{
      msg:"Hello World!",
    }
  });
</script>

v-text指令后接表達式msg,msg是對實例data中Hello World!的引用。當(dāng)實例運行時,v-text指令所在的標(biāo)簽內(nèi)容會替換為Hello World!。有些指令可以在其后加上修飾符,從而達到特殊的效果。指令大致可以分為顯示類、渲染類,事件類3種。

3.1 顯示類指令

指令 用法 說明
v-text <div v-text="msg"></div> 以文本的方式更新元素的內(nèi)容,即使msg中是html標(biāo)簽,它只當(dāng)做字符串的方式處理,不會解析標(biāo)簽。即,只改變元素的textContent,與插值表達式相同<div>{{msg}}</div>
v-html <div v-html=“html”></div> 更新元素的 innerHTML 。注意:內(nèi)容按普通 HTML 插入 - 不會作為 Vue 模板進行編譯
v-cloak <div v-cloak>{{msg}}</div> v-cloak不接表達式,當(dāng)網(wǎng)速較慢時,vue實例還沒有加載完成,這時界面上會顯示{{msg}},在vue實例加載完成后,{{msg}}會替換為對應(yīng)的內(nèi)容,不過這使得頁面會閃動。當(dāng)使用v-cloak指令后,vue實例在編譯完成后,會移除該指令。通過這一特征,使用v-cloak再配合css的屬性選擇器[v-cloak]{display:none;}使用,達到在網(wǎng)速慢時界面不閃動的效果。

3.2 渲染類指令

指令 用法 說明
v-once <div v-once>{{msg}}</div> v-once不接表達式,通常配合其他指令使用,表示只渲染一次。渲染后,當(dāng)msg的內(nèi)容發(fā)生變化時,也不會重新渲染。
v-show <div v-show=“show”></div> v-show后接表達式,表達式返回true或false,根據(jù)表達式的值,修改該元素的display的樣式,從而達到顯示隱藏的效果。注意:因為是使用的display樣式,所以該元素始終都會被渲染,只是顯示/隱藏而已。

v-if v-if-else v-else

v-if、v-if-elsev-else標(biāo)簽需要組合使用,后接表達式。與其它語言的if/else一樣,表達式條件為true時進入該分支。

<div id="app">
  <p v-if="status==0">0</p>
  <p v-else-if="status==1">1</p>
  <p v-else="status==2">2</p>
</div>
<script>
    var vm = new Vue({
    el:"#app",
    data:{
      status:0,
    }
  });
</script>

v-show不同,使用這組指令時,當(dāng)條件為true時才渲染該分支對應(yīng)的元素/組件,當(dāng)條件為false時,對應(yīng)的元素/組件會被移除。

Vue會出于效率考慮,會復(fù)用已有的元素,而非重新渲染,這樣做有時會出現(xiàn)不符合需求的情況。比如,當(dāng)復(fù)用input的時候,不會改變input的value值。

<div id="app">
  <template v-if="showQQ">
    <label>QQ</label>
    <input placeholder="QQ">
  </template> 
  <template v-else>
    <label>Email</label>
    <input placeholder="Email">
  </template> 
</div>

當(dāng)showQQ為true時,顯示QQ輸入框,用戶在輸入內(nèi)容后,切換到Email,即改變了showQQ的值為false,這時第一個template的內(nèi)容將會被移除,但是vue為了提高效率,會重新引用這個label和input,并設(shè)置了對應(yīng)的值,不過,vue并沒有修改input的value的值,所以Email輸入框中的值還是我們之前輸入的QQ內(nèi)容。

要解決這個問題,只需要在input上加入key屬性,告訴vue這兩個input是完全獨立的,不要復(fù)用它們,注意key的值必須是唯一的。

v-ifv-show的區(qū)別:

  • v-if是條件渲染,v-show是條件顯示,v-if會根據(jù)條件銷毀元素/組件以及其綁定事件,而v-show總是會渲染,根據(jù)條件切換css樣式來控制顯示/隱藏。
  • v-show不能使用在template標(biāo)簽上

v-for

v-for通過遍歷數(shù)據(jù)源渲染指定內(nèi)容。

<ul>
  <li v-for="person in persons">{{person.name}}</li>
</ul>

<script>
  var vm = new Vue({
    data:{
      persons:[{name:"張三"},{name:"李四"},]
    }
  });
</script>

將v-for加到要渲染的標(biāo)簽上,使用alias in expression語法為當(dāng)前遍歷的元素提供別名。v-for的數(shù)據(jù)源可以是數(shù)組,也可以是對象,還可以是數(shù)字。

  • 當(dāng)數(shù)據(jù)源為數(shù)組時,別名指向的是數(shù)組元素,
  • 當(dāng)數(shù)據(jù)源為對象時,別名指向的是對象的值集合中的值。
  • 當(dāng)數(shù)據(jù)源為數(shù)字時,別名指向的是從1開始的數(shù)字,迭代到等于數(shù)據(jù)源的數(shù)字

v-for有多種可選參數(shù)形式:

<div v-for="item in array"></div>
<div v-for="(item,index) in array"></div>

<div v-for="value in object"></div>
<div v-for="(value,key) in object"></div>
<div v-for="(value,key,index) in object"></div>

<div v-for="n in number"></div>

3.3 事件類指令

事件類指令包括v-on,v-bind,v-model3個指令,如果說顯示類指令和渲染類指令負責(zé)界面的呈現(xiàn),那么事件類指令就負責(zé)定義交互邏輯,這些指令非常常用。

3.3.1 v-on 監(jiān)聽事件

使用v-on來監(jiān)聽事件,這些事件包括HTML元素的DOM原生事件,也可能是組件的自定義事件,組件使用$emit()方法來觸發(fā)事件。v-on縮寫為@符號

<div id="app">
  <!-- v-on指令接收javascript代碼 -->
  <button v-on:click="counter += 1">點擊</button>
  <!-- v-on語法糖形式 -->
  <button @click="counter += 1">點擊</button>
  <p>點擊了按鈕 {{ counter }} 次</p>
</div>
<script>
    var vm = new Vue({
  el: "#app",
  data: {
    counter: 0
  }
})
</script>

事件的處理有3種方式,第1種為直接將js代碼加在v-on指令中,如上所示。當(dāng)代碼量較多時,這種方式不可行,此時可用第2種方式,將事件的處理邏輯封裝到方法中,把方法與事件綁定起來。

<div id="app">
  <!-- v-on指令接收方法名 -->
  <button @click="increment">點擊</button>
  <p>點擊了按鈕 {{ counter }} 次</p>
</div>
<script>
    var vm = new Vue({
  el: "#app",
  data: {
    counter: 0
  },
  methods:{
    increment(){
      this.counter++;
    }
  }
})
</script>

第3種是使用內(nèi)斂語句調(diào)用方法,這種方式和第1種基本一樣,只是第1種是執(zhí)行語句,這里是執(zhí)行方法,這種方式很常用,它比第2種方式的優(yōu)勢在于,可以傳遞$event參數(shù)。Vue中使用變量$event表示原始DOM事件的Event 對象,拿到event對象后我們可以做很多事,比如獲取觸發(fā)事件的元素、鍵盤按鍵的狀態(tài)、鼠標(biāo)的位置、鼠標(biāo)按鈕的狀態(tài)等。

<div id="app">
  <!-- v-on指令使用內(nèi)斂語句調(diào)用方法 -->
  <button @click="sayHello('張三',$evnet)">點擊</button>
  <p>{{ msg }} </p>
</div>
<script>
    var vm = new Vue({
   el: "#app",
   data: {
     msg: "",
   },
   methods:{
     sayHello(name,event){
       if(evnet){
         event.preventDefault();
       }
       this.msg = "Hello " + name;
     }
   }
    })
</script>

下面列出一些常用的event事件標(biāo)準(zhǔn)屬性和方法,具體參考MDN DOM Event接口

屬性/方法 描述
ctrlKey 當(dāng)鼠標(biāo)事件觸發(fā)時,如果 control 鍵被按下,則返回 true;
altKey 當(dāng)鼠標(biāo)事件觸發(fā)的時,如果alt 鍵被按下,返回true;
shiftKey 當(dāng)鼠標(biāo)事件觸發(fā)時,如果 shift 鍵被按下,則返回 true;
button 當(dāng)鼠標(biāo)事件觸發(fā)的時,如果鼠標(biāo)按鈕被按下,將會返回一個數(shù)值。
clientX 鼠標(biāo)指針在點擊元素(DOM)中的X坐標(biāo)。
clientY 鼠標(biāo)指針在點擊元素(DOM)中的Y坐標(biāo)。
screenX 鼠標(biāo)指針相對于全局(屏幕)的X坐標(biāo)
screenY 鼠標(biāo)指針相對于全局(屏幕)的Y坐標(biāo)
bubbles 表示該事件是否在DOM中冒泡。
cancelable 用來表示這個事件是否可以取消。
currentTarget 返回注冊這個事件監(jiān)聽的對象。
eventPhase 事件流正在處理哪個階段。
target 觸發(fā)該事件的元素。
type 返回當(dāng)前 Event 對象表示的事件的名稱,及事件的類型(不區(qū)分大小寫)
initEvent() 初始化新創(chuàng)建的 Event 對象的屬性。
preventDefault() 取消事件,不執(zhí)行與事件關(guān)聯(lián)的默認動作。
stopPropagation() 停止事件冒泡。

v-on修飾符

Vue為v-on提供了事件修飾符,在一定程度上簡化了在方法中處理$event對象的邏輯。

  • .stop - 調(diào)用 event.stopPropagation()
  • .prevent - 調(diào)用 event.preventDefault()。
  • .capture - 添加事件偵聽器時使用 capture 模式。
  • .self - 只當(dāng)事件是從偵聽器綁定的元素本身觸發(fā)時才觸發(fā)回調(diào)。
  • .{keyCode | keyAlias} - 只當(dāng)事件是從特定鍵觸發(fā)時才觸發(fā)回調(diào)。
  • .native - 監(jiān)聽組件根元素的原生事件。
  • .once - 只觸發(fā)一次回調(diào)。
  • .left - (2.2.0) 只當(dāng)點擊鼠標(biāo)左鍵時觸發(fā)。
  • .right - (2.2.0) 只當(dāng)點擊鼠標(biāo)右鍵時觸發(fā)。
  • .middle - (2.2.0) 只當(dāng)點擊鼠標(biāo)中鍵時觸發(fā)。
  • .passive - (2.3.0) 以 { passive: true } 模式添加偵聽器

其中.capture表示該事件使用捕獲模式(默認是冒泡模式),我們在用js原生添加事件監(jiān)聽使用如下方法,event是事件名稱,如click;listener是為該事件注冊的方法,useCapture是使用捕獲模式還是冒泡模式。

addEventListener(event, listener, useCapture)  

useCapture默認是false的,也就是說默認使用冒泡事件,關(guān)于捕獲和冒泡可以簡單的理解,捕獲模式是事件傳播的方向是由根元素向目標(biāo)前進,冒泡模式表示事件的傳播方向是由目標(biāo)元素向根元素前進。

3.3.2 v-bind 屬性綁定

將表達式動態(tài)綁定到HTML元素/組件的屬性上,縮寫為:符號

<div id="app">
  <a v-bind:href="url">百度</a>
    <!-- v-bind語法糖形式 -->
    <a :href="url">百度</a>
</div>

<script>
    var vm = new Vue({
    el:"#app",
    data:{
      url:"www.baidu.com",
    }
  });
</script>

v-bind的寫法為v-bind:foo="bar",其中foo表示v-bind的參數(shù),代表的是原生/組件的屬性名,bar是預(yù)期值,即屬性對應(yīng)的值。

參數(shù)時可選的,也就是說,v-bind可指定屬性名也可不指定屬性名,當(dāng)不帶參數(shù)時,要綁定到一個包含鍵值對的預(yù)期值對象,對象的key就是參數(shù),對象key對應(yīng)的value就是參數(shù)對應(yīng)的值,如下:

<img v-bind="{src:'a.png',alt='呵呵',height:32,width:32}">
<!--等價于-->
<img src="a.png" alt="呵呵" height="32" width="32">

可以利用無參指令,一次性綁定多個屬性。

在向自定義組件綁定屬性值時(通過props傳遞),數(shù)據(jù)是單向流動的,父組件向子組件傳遞數(shù)據(jù),反之不行。常規(guī)的做法是,父組件通過子組件的props傳值給子組件,同時在子組件上注冊監(jiān)聽事件,子組件通過$emit方法觸發(fā)父組件的監(jiān)聽方法,修改props對應(yīng)的值。

Vue提供了.sync修飾符,它實際上是一個語法糖,加了.sync的prop,vue會自動的為其生成一個事件監(jiān)聽,監(jiān)聽的名稱為update:propName,如要子組件要修改prop的值,只需調(diào)用this.$emit('update:propName',value)即可,如下所示:

<body>
    <div id="app">
        <h3>{{msg}}</h3>
        <!-- 引用組件,將msg賦值到組件的title屬性上,
            并使用.sync修飾符(語法糖)添加v-on事件監(jiān)聽,
            該事件名稱的格式為 update:propName,對應(yīng)到這里是update:title-->
        <son :title.sync="msg" />
    </div>

    <!-- 使用x-template定義組件的HTML模板 -->
    <script type="text/x-template" id="son">
        <div>
            <p>{{title}}</p>
            <button @click="btnClicked">點我</button>
        </div>
    </script>

    <script>
        // 組件定義
        Vue.component('son', {
            //定義組件屬性
            props:['title'],
            //找到id為son的HTML模板
            template: "#son",
            methods: {
                btnClicked() {
                    //觸發(fā)由.sync生成的upadte:title事件
                    this.$emit("update:title","子組件改變后的標(biāo)題")
                }
            },
        });
        var vm = new Vue({
            el: "#app",
            data: {
                msg: "標(biāo)題"
            }
        });
    </script>
</body>

注意:.sync修飾符后面不能接表達式。

3.3.3 class與style增強綁定

v-bind可以綁定元素的所有屬性,其中最常用的是元素的class和style屬性,不過當(dāng)這兩個屬性的邏輯較為復(fù)雜時,使用字符串的方式返回該屬性值體驗非常差,所以,Vue增強了對class和style的綁定。

class的綁定

使用v-bind綁定class有對象語法和數(shù)字語法兩種方式,在對象語法中key表示css的類名,value是個bool值,為true時,該key對應(yīng)的類名加入class中,為false時,對應(yīng)的項從class中移除。

<div class="cell" :class="{'active':isActive,'error':isError}"></div>
<script>
var vm = new Vue({
  data:{
    isActive:true,
    isError:false,
  }
});
</script>

上面的代碼最終生成的class為:

<div class="cell active"></div>

v-bind:class生成的動態(tài)綁定類會與class屬性中的類合并。這isActive的值為true,表示active會加入到class類中,isError為false,div的class中會移除error類。

數(shù)組語法就相對簡單,只要是數(shù)組中的字符串,都會加到class類中。在數(shù)組中可以使用三元表達式來動態(tài)的加載類。

<div :class="[cell,isActive?'active':'',isError?'error':'']"></div>

style內(nèi)聯(lián)樣式綁定

v-bind:sytle也有對象語法和數(shù)據(jù)語法,但是對象語法較為常見

<div :style="cellStyle"></div>

<script>
var vm = new Vue({
  cellStyle:{
    fontSize:14+'px',
    color:'red'
  }
});
</script>

結(jié)果為:

<div style="color:red;font-size:14px;"></div>

3.3.4 v-model雙向綁定

v-model指令用于在表單類元素上實現(xiàn)數(shù)據(jù)的雙向綁定,它的本質(zhì)也是監(jiān)聽表單類元素的輸入事件,更新綁定的數(shù)據(jù),vue對以下列表單元素做了處理。

<!-- 輸入框元素,綁定value屬性,監(jiān)聽input事件 -->
<input type="text" v-model="text">

<!-- 文本域元素,綁定value屬性,監(jiān)聽input事件 -->
<textarea v-model="text"></textarea>

<!-- 單個復(fù)選框時,v-model綁定一個bool值,該值指示checkbox添加/移除checked屬性,監(jiān)聽change事件 -->
<input type="checkbox" v-model="checked" id="checkbox1">
<label for="cbox1">{{checkd}}</label>

<!-- 
  多個復(fù)選框時,v-model綁定一個數(shù)組,與value屬性配合使用,監(jiān)聽change事件。
    選中復(fù)選框時,將value值push到數(shù)組中,否則,將value值從數(shù)組中移除。     
-->
<input type="checkbox" id="jack" value="jack" v-model="names">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="john" v-model="names">
<label for="john">John</label>

<!-- 單選按鈕,v-model綁定字符串,與value值配合使用-->
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>

<!-- 
    選擇框,v-model綁定value值,監(jiān)聽change事件,也分為單選和多選,
    因為實際使用select標(biāo)簽較少(樣式不統(tǒng)一),這里不在演示
-->

修飾符

  • .lazy修飾符適用于輸入框,設(shè)置后該修飾符后,v-model不再監(jiān)聽input事件,轉(zhuǎn)而監(jiān)聽change事件。也就是當(dāng)用戶輸入文本時,數(shù)據(jù)不是事實的同步,而是在文本框失去焦點或回車時更新。

    <input type="text" v-model.lazy="text">
    
  • .number將輸入框的內(nèi)容轉(zhuǎn)換為數(shù)字類型

  • .trim自動過濾用戶輸入的首尾空白字符

3.3.5 自定義組件的v-model

v-model指令不僅可以在表單元素上實現(xiàn)數(shù)據(jù)的雙向綁定,還可以在自定義組件上實現(xiàn)雙向綁定。上面說了,v-model其實是語法糖,它的本質(zhì)是綁定值到元素的指定屬性上,并監(jiān)聽元素的輸入事件,vue在表單元素中為我們實現(xiàn)了該功能,而在自定義組件上,這個功能需要我們自己實現(xiàn)。

v-model在自定義組件上默認綁定value屬性,監(jiān)聽input事件。

<body>
    <div id="app">
        <h3>{{count}}</h3>
        <!-- 在自定義組件上使用v-model指令 -->
        <my-component v-model="count" />
    </div>

    <script type="text/x-template" id="myComponent">
        <div>
            <p>{{value}}</p>
            <button @click="btnClicked">點我</button>
        </div>
    </script>

    <script>
        // 組件定義
        Vue.component('myComponent', {
            //組件中定義value屬性
            props:["value"],
            template: "#myComponent",
            methods: {
                btnClicked() {
                    // 發(fā)送input事件
                    this.$emit("input",this.value+1);
                }
            },
        });
        var vm = new Vue({
            el: "#app",
            data: {
                count: 0,
            },
        });
    </script>
</body>

因為v-model默認綁定的是value屬性,所以需要在組件中定義一個value。v-model指令默認監(jiān)聽input事件(自動生成),所以,執(zhí)行通過$emit觸發(fā)input事假即可。

如果希望改變v-model在組件上默認的屬性和事件,可以使用model選項,model選項接收一個對象,這個對象有兩個屬性:prop和event。prop的值默認是value,event的值默認是input。

<script>
    Vue.component('myComponent',{
    model:{
      //修改v-model的默認綁定的value屬性和input監(jiān)聽事件
      prop:"count",
      event:"countChange"
    },
    props:["count"],
    template:"#myComponent",
    methods:{
      btnClicked(){
        this.$emit("contChange",this.count+1);
      }
    }
  });
</script>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Vue 實例 屬性和方法 每個 Vue 實例都會代理其 data 對象里所有的屬性:var data = { a:...
    云之外閱讀 2,373評論 0 6
  • 主要還是自己看的,所有內(nèi)容來自官方文檔。 介紹 Vue.js 是什么 Vue (讀音 /vju?/,類似于 vie...
    Leonzai閱讀 3,540評論 0 25
  • vue概述 在官方文檔中,有一句話對Vue的定位說的很明確:Vue.js 的核心是一個允許采用簡潔的模板語法來聲明...
    li4065閱讀 7,624評論 0 25
  • 組件(Component)是Vue.js最核心的功能,也是整個架構(gòu)設(shè)計最精彩的地方,當(dāng)然也是最難掌握的。...
    六個周閱讀 5,770評論 0 32
  • 一、了解Vue.js 1.1.1 Vue.js是什么? 簡單小巧、漸進式、功能強大的技術(shù)棧 1.1.2 為什么學(xué)習(xí)...
    蔡華鵬閱讀 3,497評論 0 3

友情鏈接更多精彩內(nèi)容