1. 程式人生 > 實用技巧 >Vue筆記 - [常用特性]

Vue筆記 - [常用特性]

常用特性

表單的基本操作

獲取單選框中的值

  • 通過v-model
<!-- 
1、 兩個單選框需要同時通過v-model 雙向繫結 一個值 
2、 每一個單選框必須要有value屬性  且value 值不能一樣 
3、 當某一個單選框選中的時候 v-model  會將當前的 value值 改變 data 中的 資料

gender 的值就是選中的值,我們只需要實時監控他的值就可以了
-->
<input type="radio" id="male" value="1" v-model='gender'>
<label for="male">男</label>

<input type="radio" id="female" value="2" v-model='gender'>
<label for="female">女</label>

<script>
    new Vue({
         data: {
             // 預設會讓當前的 value 值為 2 的單選框選中
                gender: 2,  
            },
    })
</script>

獲取複選框中的值

  • 通過v-model
  • 和獲取單選框中的值一樣
  • 複選框 checkbox 這種的組合時 data 中的 hobby 我們要定義成陣列 否則無法實現多選
<!-- 
1、 複選框需要同時通過v-model 雙向繫結 一個值 
2、 每一個複選框必須要有value屬性  且value 值不能一樣 
3、 當某一個單選框選中的時候 v-model  會將當前的 value值 改變 data 中的 資料

hobby 的值就是選中的值,我們只需要實時監控他的值就可以了
-->
<div>
   <span>愛好:</span>
   <input type="checkbox" id="ball" value="1" v-model='hobby'>
   <label for="ball">籃球</label>
   <input type="checkbox" id="sing" value="2" v-model='hobby'>
   <label for="sing">唱歌</label>
   <input type="checkbox" id="code" value="3" v-model='hobby'>
   <label for="code">寫程式碼</label>
 </div>
<script>
    new Vue({
         data: {
                // 預設會讓當前的 value 值為 2 和 3 的複選框選中
                hobby: ['2', '3'],
            },
    })
</script>

獲取下拉框和文字框中的值

  • 通過v-model
<div>
    <span>職業:</span>
    <!--
1、 需要給select  通過v-model 雙向繫結 一個值 
2、 每一個option  必須要有value屬性  且value 值不能一樣 
3、 當某一個option選中的時候 v-model  會將當前的 value值 改變 data 中的 資料
occupation 的值就是選中的值,我們只需要實時監控他的值就可以了
-->
 	<!-- multiple  多選 -->
    <select v-model='occupation' multiple>
        <option value="0">請選擇職業...</option>
        <option value="1">教師</option>
        <option value="2">軟體工程師</option>
        <option value="3">律師</option>
    </select>
    <!-- textarea 是 一個雙標籤   不需要繫結value 屬性的  -->
    <textarea v-model='desc'></textarea>
</div>
<script>
    new Vue({
         data: {
                // 預設會讓當前的 value 值為 2 和 3 的下拉框選中
                 occupation: ['2', '3'],
             	 desc: 'nihao'
            },
    })
</script>
  • 例子程式碼:
<div id="app">
    <form action="http://itcast.cn">
        <div>
            <span>姓名:</span>
            <span>
          <input type="text" v-model='uname'>
        </span>
        </div>
        <div>
            <span>性別:</span>
            <span>
          <input type="radio" id="male" value="1" v-model='gender'>
          <label for="male">男</label>
          <input type="radio" id="female" value="2" v-model='gender'>
          <label for="female">女</label>
        </span>
        </div>
        <div>
            <span>愛好:</span>
            <input type="checkbox" id="ball" value="1" v-model='hobby'>
            <label for="ball">籃球</label>
            <input type="checkbox" id="sing" value="2" v-model='hobby'>
            <label for="sing">唱歌</label>
            <input type="checkbox" id="code" value="3" v-model='hobby'>
            <label for="code">寫程式碼</label>
        </div>
        <div>
            <span>職業:</span>
            <select v-model='occupation' multiple>
                <option value="0">請選擇職業...</option>
                <option value="1">教師</option>
                <option value="2">軟體工程師</option>
                <option value="3">律師</option>
            </select>
        </div>
        <div>
            <span>個人簡介:</span>
            <textarea v-model='desc'></textarea>
        </div>
        <div>
            <input type="submit" value="提交" @click.prevent='handle'>
        </div>
    </form>
</div>

<script>
    let app = new Vue({
        el: '#app',
        data: {
            uname: '張三',
            gender:1,
            hobby:['1','2'],
            occupation:['1','2','3'],
            desc:'我是誰?'
        },
        methods:{
            handle:function () {
                console.log(this.uname);
                console.log(this.gender);
                console.log(this.hobby);
                console.log(this.hobby.toString());
                console.log(this.occupation);
                console.log(this.desc);
            }
        }
    })
</script>

<style scope>

    form div {
        height: 40px;
        line-height: 40px;
    }
    form div:nth-child(4) {
        height: auto;
    }
    form div span:first-child {
        display: inline-block;
        width: 100px;
    }
</style>

表單修飾符

  • .number 轉換為數值

    • 注意點:
    • 當開始輸入非數字的字串時,因為Vue無法將字串轉換成數值
    • 所以屬性值將實時更新成相同的字串。即使後面輸入數字,也將被視作字串。
  • .trim 自動過濾使用者輸入的首尾空白字元

    • 只能去掉首尾的 不能去除中間的空格
  • .lazy 將input事件切換成change事件

    • .lazy 修飾符延遲了同步更新屬性值的時機。即將原本繫結在 input 事件的同步邏輯轉變為繫結在 change 事件上
  • 在失去焦點 或者 按下回車鍵時才更新

<!-- 自動將使用者的輸入值轉為數值型別 -->
<input v-model.number="age" type="number">

<!--自動過濾使用者輸入的首尾空白字元   -->
<input v-model.trim="msg">

<!-- 在“change”時而非“input”時更新 -->
<input v-model.lazy="msg" >

自定義指令

  • 內建指令不能滿足我們特殊的需求
  • Vue允許我們自定義指令

Vue.directive 註冊全域性指令

<!-- 
  使用自定義的指令,只需在對用的元素中,加上'v-'的字首形成類似於內部指令'v-if','v-text'的形式。 
-->
<input type="text" v-focus>
<script>
// 注意點: 
//   1、 在自定義指令中  如果以駝峰命名的方式定義 如  Vue.directive('focusA',function(){}) 
//   2、 在HTML中使用的時候 只能通過 v-focus-a 來使用 
    
// 註冊一個全域性自定義指令 v-focus
Vue.directive('focus', {
  	// 當繫結元素插入到 DOM 中。 其中 el為dom元素
  	inserted: function (el) {
    		// 聚焦元素
    		el.focus();
 	}
});
new Vue({
  el:'#app'
});
</script>

Vue.directive 註冊全域性指令 帶引數

<input type="text" v-color='msg'>
<script type="text/javascript">
    /*
      自定義指令-帶引數
      bind - 只調用一次,在指令第一次繫結到元素上時候呼叫

    */
    Vue.directive('color', {
      // bind宣告週期, 只調用一次,指令第一次繫結到元素時呼叫。在這裡可以進行一次性的初始化設定
      // el 為當前自定義指令的DOM元素  
      // binding 為自定義的函式形參   通過自定義屬性傳遞過來的值 存在 binding.value 裡面
      bind: function(el, binding){
        // 根據指令的引數設定背景色
        // console.log(binding.value.color)
        el.style.backgroundColor = binding.value.color;
      }
    });
    var vm = new Vue({
      el: '#app',
      data: {
        msg: {
          color: 'blue'
        }
      }
    });
</script>

自定義指令區域性指令

  • 區域性指令,需要定義在 directives 的選項 用法和全域性用法一樣
  • 區域性指令只能在當前元件裡面使用
  • 當全域性指令和區域性指令同名時以區域性指令為準
<input type="text" v-color='msg'>
 <input type="text" v-focus>
 <script type="text/javascript">
    /*
      自定義指令-區域性指令
    */
    var vm = new Vue({
      el: '#app',
      data: {
        msg: {
          color: 'red'
        }
      },
   	  //區域性指令,需要定義在  directives 的選項
      directives: {
        color: {
          bind: function(el, binding){
            el.style.backgroundColor = binding.value.color;
          }
        },
        focus: {
          inserted: function(el) {
            el.focus();
          }
        }
      }
    });
  </script>

計算屬性 computed

  • 模板中放入太多的邏輯會讓模板過重且難以維護 使用計算屬性可以讓模板更加的簡潔
  • 計算屬性是基於它們的響應式依賴進行快取的
  • computed比較適合對多個變數或者物件進行處理後返回一個結果值,也就是數多個變數中的某一個值發生了變化則我們監控的這個值也就會發生變化
<div id="app">
    <!--  
當多次呼叫 reverseString  的時候 
只要裡面的 num 值不改變 他會把第一次計算的結果直接返回
直到data 中的num值改變 計算屬性才會重新發生計算
-->
    <div>{{reverseString}}</div>
    <div>{{reverseString}}</div>
    <!-- 呼叫methods中的方法的時候  他每次會重新呼叫 -->
    <div>{{reverseMessage()}}</div>
    <div>{{reverseMessage()}}</div>
</div>
<script type="text/javascript">
    /*
      計算屬性與方法的區別:計算屬性是基於依賴進行快取的,而方法不快取
    */
    var vm = new Vue({
        el: '#app',
        data: {
            msg: 'Nihao',
            num: 100
        },
        methods: {
            reverseMessage: function(){
                console.log('methods')
                return this.msg.split('').reverse().join('');
            }
        },
        //computed  屬性 定義 和 data 已經 methods 平級 
        computed: {
            //  reverseString   這個是我們自己定義的名字 
            reverseString: function(){
                console.log('computed')
                var total = 0;
                //  當data 中的 num 的值改變的時候  reverseString  會自動發生計算  
                for(var i=0;i<=this.num;i++){
                    total += i;
                }
                // 這裡一定要有return 否則 呼叫 reverseString 的 時候無法拿到結果    
                return total;
            }
        }
    });
</script>

偵聽器 watch

  • 使用watch來響應資料的變化
  • 一般用於非同步或者開銷較大的操作
  • watch 中的屬性 一定是data 中 已經存在的資料
  • 當需要監聽一個物件的改變時,普通的watch方法無法監聽到物件內部屬性的改變,只有data中的資料才能夠監聽到變化,此時就需要deep屬性對物件進行深度監聽
<div id="app">
    <div>
        <span>名:</span>
        <span>
            <input type="text" v-model='firstName'>
        </span>
    </div>
    <div>
        <span>姓:</span>
        <span>
            <input type="text" v-model='lastName'>
        </span>
    </div>
    <div>{{fullName}}</div>
</div>

<script type="text/javascript">
    /*
              偵聽器
            */
    var vm = new Vue({
        el: '#app',
        data: {
            firstName: 'Jim',
            lastName: 'Green',
            // fullName: 'Jim Green'
        },
        //watch  屬性 定義 和 data 已經 methods 平級 
        watch: {
            //   注意:  這裡firstName  對應著data 中的 firstName 
            //   當 firstName 值 改變的時候  會自動觸發 watch
            firstName: function(val) {
                this.fullName = val + ' ' + this.lastName;
            },
            //   注意:  這裡 lastName 對應著data 中的 lastName 
            lastName: function(val) {
                this.fullName = this.firstName + ' ' + val;
            }
        }
    });
</script>
<div id="app">
   <div>
       <span>使用者名稱: </span>
       <span>
           <input type="text" v-model.lazy="uname">
       </span>
       <span>{{tip}}</span>
   </div>
</div>

<script>
    let app = new Vue({
        el: '#app',
        data: {
            uname: '',
            tip:''
        },
        methods:{
            checkName:function(uname){
                var that = this;
                setTimeout(function () {
                    if (uname === 'admin'){
                        that.tip = '使用者名稱已存在,請更換一個'
                    }else{
                        that.tip = '使用者名稱可以使用'
                    }
                }, 2000);
            }
        },
        watch:{
            uname:function (val) {
                this.checkName(val);
                this.tip = '正在驗證....'
            }
        }
    })
</script>

過濾器

  • Vue.js允許自定義過濾器,可被用於一些常見的文字格式化。
  • 過濾器可以用在兩個地方:雙花括號插值和v-bind表示式。
  • 過濾器應該被新增在JavaScript表示式的尾部,由“管道”符號指示
  • 支援級聯操作
  • 過濾器不改變真正的data,而只是改變渲染的結果,並返回過濾後的版本
  • 全域性註冊時是filter,沒有s的。而區域性過濾器是filters,是有s的
<div id="app">
    <input type="text" v-model='msg'>
    <!-- upper 被定義為接收單個引數的過濾器函式,表示式  msg  的值將作為引數傳入到函式中 -->
    <div>{{msg | upper}}</div>
    <!--  
支援級聯操作
upper  被定義為接收單個引數的過濾器函式,表示式msg 的值將作為引數傳入到函式中。
然後繼續呼叫同樣被定義為接收單個引數的過濾器 lower ,將upper 的結果傳遞到lower中
-->
    <div>{{msg | upper | lower}}</div>
    <div :abc='msg | upper'>測試資料</div>
</div>

<script type="text/javascript">
    //  lower  為全域性過濾器     
    Vue.filter('lower', function(val) {
        return val.charAt(0).toLowerCase() + val.slice(1);
    });
    var vm = new Vue({
        el: '#app',
        data: {
            msg: ''
        },
        //filters  屬性 定義 和 data 已經 methods 平級 
        //  定義filters 中的過濾器為區域性過濾器 
        filters: {
            //   upper  自定義的過濾器名字 
            //    upper 被定義為接收單個引數的過濾器函式,表示式  msg  的值將作為引數傳入到函式中
            upper: function(val) {
                //  過濾器中一定要有返回值 這樣外界使用過濾器的時候才能拿到結果
                return val.charAt(0).toUpperCase() + val.slice(1);
            }
        }
    });
</script>

過濾器中傳遞引數

<div id="box">
    <!--
filterA 被定義為接收三個引數的過濾器函式。
其中 message 的值作為第一個引數,
普通字串 'arg1' 作為第二個引數,表示式 arg2 的值作為第三個引數。
-->
    {{ message | filterA('arg1', 'arg2') }}
</div>
<script>
    // 在過濾器中 第一個引數 對應的是  管道符前面的資料   n  此時對應 message
    // 第2個引數  a 對應 實參  arg1 字串
    // 第3個引數  b 對應 實參  arg2 字串
    Vue.filter('filterA',function(n,a,b){
        if(n<10){
            return n+a;
        }else{
            return n+b;
        }
    });

    new Vue({
        el:"#box",
        data:{
            message: "哈哈哈"
        }
    })

</script>
  • 案例 - 使用過濾器處理時間
<div id="app">
    <div>{{date | format('yyyy-MM-dd hh:mm:ss')}}</div>
</div>

<script>
    Vue.filter('format',function (val,arg){
        function dateFormat(date, format) {
            if (typeof date === "string") {
                var mts = date.match(/(\/Date\((\d+)\)\/)/);
                if (mts && mts.length >= 3) {
                    date = parseInt(mts[2]);
                }
            }
            date = new Date(date);
            if (!date || date.toUTCString() == "Invalid Date") {
                return "";
            }
            var map = {
                "M": date.getMonth() + 1, //月份
                "d": date.getDate(), //日
                "h": date.getHours(), //小時
                "m": date.getMinutes(), //分
                "s": date.getSeconds(), //秒
                "q": Math.floor((date.getMonth() + 3) / 3), //季度
                "S": date.getMilliseconds() //毫秒
            };

            format = format.replace(/([yMdhmsqS])+/g, function(all, t) {
                var v = map[t];
                if (v !== undefined) {
                    if (all.length > 1) {
                        v = '0' + v;
                        v = v.substr(v.length - 2);
                    }
                    return v;
                } else if (t === 'y') {
                    return (date.getFullYear() + '').substr(4 - all.length);
                }
                return all;
            });
            return format;
        }
        return dateFormat(val,arg);
    })
    let app = new Vue({
        el: '#app',
        data: {
            date: new Date()
        }
    })
</script>

生命週期

  • 事物從出生到死亡的過程
  • Vue例項從建立 到銷燬的過程 ,這些過程中會伴隨著一些函式的自呼叫。我們稱這些函式為鉤子函式

常用的 鉤子函式

beforeCreate 在例項初始化之後,資料觀測和事件配置之前被呼叫 此時data 和 methods 以及頁面的DOM結構都沒有初始化 什麼都做不了
created 在例項建立完成後被立即呼叫此時data 和 methods已經可以使用 但是頁面還沒有渲染出來
beforeMount 在掛載開始之前被呼叫 此時頁面上還看不到真實資料 只是一個模板頁面而已
mounted el被新建立的vm.$el替換,並掛載到例項上去之後呼叫該鉤子。 資料已經真實渲染到頁面上 在這個鉤子函式裡面我們可以使用一些第三方的外掛
beforeUpdate 資料更新時呼叫,發生在虛擬DOM打補丁之前。 頁面上資料還是舊的
updated 由於資料更改導致的虛擬DOM重新渲染和打補丁,在這之後會呼叫該鉤子。 頁面上資料已經替換成最新的
beforeDestroy 例項銷燬之前呼叫
destroyed 例項銷燬後呼叫

陣列變異方法

  • 在 Vue 中,直接修改物件屬性的值無法觸發響應式。當你直接修改了物件屬性的值,你會發現,只有資料改了,但是頁面內容並沒有改變
  • 變異陣列方法即保持陣列方法原有功能不變的前提下對其進行功能拓展
push() 往陣列最後面新增一個元素,成功返回當前陣列的長度
pop() 刪除陣列的最後一個元素,成功返回刪除元素的值
shift() 刪除陣列的第一個元素,成功返回刪除元素的值
unshift() 往陣列最前面新增一個元素,成功返回當前陣列的長度
splice() 有三個引數,第一個是想要刪除的元素的下標(必選),第二個是想要刪除的個數(必選),第三個是刪除 後想要在原位置替換的值
sort() sort() 使陣列按照字元編碼預設從小到大排序,成功返回排序後的陣列
reverse() reverse() 將陣列倒序,成功返回倒序後的陣列

替換陣列

  • 不會改變原始陣列,但總是返回一個新陣列
filter filter() 方法建立一個新的陣列,新陣列中的元素是通過檢查指定陣列中符合條件的所有元素。
concat concat() 方法用於連線兩個或多個數組。該方法不會改變現有的陣列
slice slice() 方法可從已有的陣列中返回選定的元素。該方法並不會修改陣列,而是返回一個子陣列

動態陣列響應式資料

  • Vue.set(a,b,c) 讓 觸發檢視重新更新一遍,資料動態起來
  • a是要更改的資料 、 b是資料的第幾項、 c是更改後的資料

案例(圖書管理)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style type="text/css">
        .grid {
            margin: auto;
            width: 530px;
            text-align: center;
        }
        .grid table {
            border-top: 1px solid #C2D89A;
            width: 100%;
            border-collapse: collapse;
        }
        .grid th,td {
            border: 1px dashed #F3DCAB;
            height: 35px;
            line-height: 35px;
        }
        .grid th {
            background-color: #F3DCAB;
        }
        .grid .book {
            padding-bottom: 10px;
            padding-top: 5px;
            background-color: #F3DCAB;
        }
        .grid .total {
            height: 30px;
            line-height: 30px;
            background-color: #F3DCAB;
            border-top: 1px solid #C2D89A;
        }
    </style>
</head>
<body>
<div id="app">
    <div class="grid">
        <div>
            <h1>圖書管理</h1>
            <div class="book">
                <div>
                    <label for="id">
                        編號:
                    </label>
                    <input type="text" id="id" v-model='id' :disabled="flag">
                    <label for="name">
                        名稱:
                    </label>
                    <input type="text" id="name" v-model.trim='name'>
                    <button @click='handle' :disabled="submitFlag">提交</button>
                </div>
            </div>
        </div>
        <div class="total">
            <span>圖書總數: </span>
            <span>{{total}}</span>
        </div>
        <table>
            <thead>
            <tr>
                <th>編號</th>
                <th>名稱</th>
                <th>時間</th>
                <th>操作</th>
            </tr>
            </thead>
            <tbody>
            <tr :key='item.id' v-for='item in books'>
                <td>{{item.id}}</td>
                <td>{{item.name}}</td>
                <td>{{item.date|format('yyyy-MM-dd hh:mm:ss')}}</td>
                <td>
                    <a href="" @click.prevent="modify(item.id)">修改</a>
                    <span>|</span>
                    <a href="" @click.prevent="deleteBook(item.id)">刪除</a>
                </td>
            </tr>
            </tbody>
        </table>
    </div>
</div>
<script type="text/javascript">
    Vue.filter('format', function(value, arg) {
        function dateFormat(date, format) {
            if (typeof date === "string") {
                var mts = date.match(/(\/Date\((\d+)\)\/)/);
                if (mts && mts.length >= 3) {
                    date = parseInt(mts[2]);
                }
            }
            date = new Date(date);
            if (!date || date.toUTCString() == "Invalid Date") {
                return "";
            }
            var map = {
                "M": date.getMonth() + 1, //月份
                "d": date.getDate(), //日
                "h": date.getHours(), //小時
                "m": date.getMinutes(), //分
                "s": date.getSeconds(), //秒
                "q": Math.floor((date.getMonth() + 3) / 3), //季度
                "S": date.getMilliseconds() //毫秒
            };
            format = format.replace(/([yMdhmsqS])+/g, function(all, t) {
                var v = map[t];
                if (v !== undefined) {
                    if (all.length > 1) {
                        v = '0' + v;
                        v = v.substr(v.length - 2);
                    }
                    return v;
                } else if (t === 'y') {
                    return (date.getFullYear() + '').substr(4 - all.length);
                }
                return all;
            });
            return format;
        }
        return dateFormat(value, arg);
    })
    var vm = new Vue({
        el: '#app',
        data: {
            id: '',
            name: '',
            submitFlag: false,
            flag: false,
            books: [{
                id: 1,
                name: '三國演義',
                date: 252569975000
            },{
                id: 2,
                name: '水滸傳',
                date: 252569975000
            },{
                id: 3,
                name: '紅樓夢',
                date: 252569975000
            },{
                id: 4,
                name: '西遊記',
                date: 252569975000
            }]
        },
        methods:{
            handle: function () {
                if (this.name === '' || this.name.length === 0) {
                    return;
                }
                if (this.flag){
                    this.books.some( (item) =>{
                        if (item.id === this.id) {
                            item.name = this.name;
                            return true;
                        }
                    });
                    this.flag = false;
                }else{
                    //新增圖書
                    let book = [];
                    book.id = this.id;
                    book.name = this.name;
                    book.date = new Date();
                    this.books.push(book);
                }
                //清空表單
                this.id = '';
                this.name = '';
            },
            modify: function (id) {
                this.flag = true;
                let book  =  this.books.filter(function (item) {
                    return item.id === id;
                });
                this.id = book[0].id;
                this.name = book[0].name;
            },
            deleteBook: function (id) {
                // 方法一:查詢下標刪除元素
                // let index = this.books.findIndex(function (item) {
                //     return item.id === id;
                // });
                // this.books.splice(index, 1);
                // 方法二:使用filter刪除元素
                this.books = this.books.filter(function (item) {
                    return item.id !== id;
                });
            }
        },
        computed:{
            total: function () {
                return this.books.length;
            }
        },
        watch:{
            id:function (value) {
                let flag = this.books.some(function (item) {
                    return item.id === parseInt(value);
                });
                this.submitFlag = flag;
            },
            name: function (value) {
                let flag = this.books.some(function (item) {
                    return item.name === value;
                });
                this.submitFlag = flag;
            }
        }
    });
</script>
</body>
</html>