後端小白的VUE入門筆記, 前端高能慎入

賜我白日夢發表於2019-08-05

因為專案需要前後端分離,後端竟然不用控制view層了,頁面的跳轉後端不再幹涉,(前端的vue經過打包後成了一張index.html) 後端只需要響應給前端json串就ok,其實這不是爽歪歪?但是覺得還是奇奇怪怪,感覺前端是個黑盒了, 於是忍不住去學習了vue

感覺前端的框架帶來的這種前後端分離變化還是特別明顯的,後端確實不用再操心view層了,頁面的轉換有vue通過後端的切換不同的元件,後端基本上沒有什麼變化,但是相應資料基本上是清一色的json格式的資料了, 此外, 目前碰到的後端的安全框架 SpringSecurity的使用有了些許變化,起碼認證成功還是失敗,不能往指定的頁面跳轉了,轉而使用訊息+狀態碼提示,因為就一張index.html,還能往哪裡跳轉?

下面是近幾天的學習筆記, 還是再整理一遍,畢竟會忘

認識MVVM框架Vue

MV VM分別對應著

  • model : 資料模型,存放後端傳遞過來的資料
  • view : 檢視,其實就是html, 頁面
  • viewModel : vue 的例項

下面是一個入門的例子: 通過這個例子可以看到:

  • 我們new 出來vue的例項,然後把它關聯在了html中id為 app的程式碼塊,這樣目的是如果我們在這個程式碼塊中使用vue的模板語法,vue可以解析
  • data: 這個模組盛放的 mvvm中的第一個m

    其實這也可以看出,vue的開發模式,它的出現遮蔽掉了dom操作,我們再也不用document.getElementById(), 然後innnerHtml了, 現在的工作就是把後端給的值填充進data塊中的屬性欄位就ok,一旦發生改變,頁面會自動渲染上最新的值

<body>
<div id="app">
    <input type="text" v-model="username"><!-- 宣告式開發 dom監聽  -->
    <p>Haha {{username}}</p>   <!-- 資料繫結 -->
</div>

</body>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
    el:'#app', // 元素選擇器, 選出根路徑
    data:{ // Data Model 資料模型
       username:'哈哈'
    }
});
</script>

模板語法:

雙大括號 取值:

<p>{{msg}}</p>
<p>{{msg.toUpperCase()}}</p>  <!--  可以呼叫js函式 -->

嵌入文字或html

<p v-text="msg"></p>    <!--  相當於 textContent -->
<p v-html="msg"></p>   <!--  相當於 innerHtml -->

強制資料繫結,在原標籤前新增 :

<img :src="imaUrl" alt="">

繫結監聽事件: @符

  • 比較有趣的地方,如果在methods塊中,js函式的定義是無引數據的, 在html程式碼塊中可以直接寫函式名,而不寫小括號,因為java程式碼寫多了,看了想笑 ,(當然(),也可以寫上,但是js程式設計者會認為這是沒事找事)
<button @click="text"> 點我111 </button>
<button @click="text222('haha')"> 點我222 </button>

計算屬性

計算屬性,說白了就是vue給我們的一塊糖,讓我們定製資料的變化規則,然後vue幫我們渲染在html頁面上

  • 計算屬性是針對data中的欄位的操作
  • 計算屬性中的每一個函式,都分兩部分: get和set , 預設是get,作用是把這個方法的返回值渲染進頁面, set方法,就是重新設定值, 然後get會重新渲染html
  • 計算屬性是存在快取的,key就是函式的名字,value就是計算得到的值

例子:

<body>
<div id="app">
姓名: <input type="text" placeholder="FirstName" v-model="secondName"> <br>
姓名1: <input type="text" placeholder="FullName1" v-model="FullName1"> <br>
姓名3: <input type="text" placeholder="FullName3" v-model="FullName3">  <br>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 下面的全部回撥函式中, this都是vm物件
var vm = new Vue({
el: '#app',
data: {
    firstName: 'A',
    secondName: 'B',
},
computed: {
    FullName1() {
        return this.firstName + ' ' + this.secondName;
    },
    // todo  通過計算屬性實現雙向的資料繫結, 不受其他影響
    FullName3: {
        get() { // 計算並返回當前屬性的值
            return this.firstName + ' ' + this.secondName;
        },
        set(vel) {  // get 執行之後 把結果返回給 set
            const names = vel.split(' ');
            alert(names[0]);
            alert(names[1]);
            this.firstName = names[0];
            this.secondName = names[1];
        }
    }
}
});

計算屬性寫在computed塊中, 可以看到它常用的兩種寫法, 在使用是時候都是直接使用函式名就行,因為它並沒有引數

  • 函式名(){}
  • 物件名:{ get(){} , set(){} }

上面的FullName1以函式的,這種書寫格式是對 get方法的預設實現,方法的返回值會被渲染到頁面上

FullName3還重寫了set(val){} 方法, 如果我們在FullName3對應的輸入框裡面輸入新的值, val就是這個新的值,在set方法中,如果對當前vue例項的data中的屬性做了改動,這個改動是雙向的,頁面中所有使用對應欄位的地方的值,都會重新渲染

事件的監聽:

它的語法:

// 下面的全部回撥函式中, this都是vm物件
var vm = new Vue({
el: '#app',
data: {
    firstName:'',
    secondName:''
},
computed: {},
method: {},
watch: {/* 監視 */
    firstName: function (newVal) {
        this.firstName = newVal + ' ' + this.secondName;
    }
}
});

它會監聽data中的屬性,當使用者改變了data中屬性的值,就會觸發對應的回撥

class和style的繫結

class和style的屬性繫結同樣使用的是使用 : 強制屬性繫結

首先是寫好 css屬性,才能進一步使用vue將屬性樣式繫結在html上

head>
<meta charset="UTF-8">
<title>Title</title>
<style>
    .aClass{
        font-size: 30px; 
        color: #ff4400;
    }
    .bClass{
        color: #00b4ff;
    }
    .cClass{
        color: #1c036c;
    }
</style>
</head>

語法:

  • :class="data中的css屬性"
  • :class="{data中的css屬性:boolean, data中的css屬性:boolean}", 這種物件語法,就是當類名確定但是是否顯示不確定時使用,比如讓一個導航欄中的一個高亮顯示
  • :style="{color:activeColor,fontSize:fontSize +'px'}", style有單位的+單位
<body>
<div id="text">
    <h2>1. class繫結 :: class='X X X '</h2>
    <p :class="a">123123字串</p>
    <p :class="{aClass:isa, bClass:isb}">class是物件,繫結class 類名:boolean </p>
    <h2>2. style 繫結 </h2>
    <p :style="{color:activeColor,fontSize:fontSize +'px'}">2. style 繫結 </p>
<button @click="update" >點選</button>
</div>

下面的vue物件

<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
    el: '#text',
    data: {
        a:'aClass', // 關聯著最上面的css樣式
        isa:true,
        isb:false,
        activeColor:'red',
        fontSize:'30'
    },
    methods:{
        update() {
            this.a='bClass'
        }
    }
})

條件渲染

<p v-if="ok">deal</p> --> ok是vue中data的資料,ok為true時, 顯示
<p v-else>false</p>  --> 和 v-if成對出現, ok為false, 顯示

<p v-show="ok"> 成功 </p>
<p v-show="!ok"> 失敗 </p>

列表的渲染 v-for, 及對陣列的操作

下面的例子使用v-for遍歷陣列中的每一個資料, 遍歷的同時使用{{物件.屬性}}展示屬性,同時可以根據每個li的index繫結上不同的事件

<body>
<div id="text">
    <ul>
        <!-- 一旦有 v-for  加上key-->
        <li v-for="(p,index) in person" :key="index">
            {{p.name}} : {{p.age}} : {{index}}
            <button @click="deleteP(index)"> 刪除</button>
            <button @click="updateP(index,{name:'Cat',age:20})"> 更新</button>
        </li>
    </ul>
</div>

var vm = new Vue({
el: '#text',
data: {
person: [  /*  vue 只會監視person的改變, 不會監視陣列中資料的改變*/
    {name: 'tom', age: 23},
    {name: 'tom2', age: 223},
    {name: 'tom2', age: 23},
    {name: 'tom3', age: 232},
    {name: 'tom5', age: 23}
]
},
methods: {
deleteP(index) {
    this.person.splice(index, 1);  //從index開始 刪除1個
},
updateP(index, person) {
    //  this.person[index]=person; 並沒有改變 persons  , 從index開始,刪除1個 新增person
    this.person.splice(index, 1, person)
}

如果我們更新的js是這樣寫的, 陣列中的內容確實會被改變,但是頁面的上資料並不會更新

 this.person[index]=person; 並沒有改變 persons  , 從index開始,刪除1個 新增person

因為vue監聽的person的改變,person中只有一個陣列,雖然陣列中的資料變了, 但是陣列沒變,所以我們使用vue的提供的splice進行陣列的操作

splice(下標,數量,[新的物件陣列])

他可以實現陣列的增刪改的效果

  • 刪除
//刪除起始下標為1,長度為2的一個值(len設定2)
var arr2 = ['a','b','c','d']
arr2.splice(1,2);
console.log(arr2); 
//['a','d']
  • 修改
//替換起始下標為1,長度為1的一個值為‘ttt’,len設定的1
var arr = ['a','b','c','d'];
arr.splice(1,1,'ttt');
console.log(arr);        
//['a','ttt','c','d'] 
  • 新增
var arr = ['a','b','c','d'];
arr.splice(1,0,'ttt');
console.log(arr);        
//['a','ttt','b','c','d']

其他陣列相關的操作

  • unshift()新增到第一個
  • shift() 新增到最後一個
  • push() 壓棧,棧頂
  • pop()彈出
  • sort()排序
  • reverse() 反轉

陣列的對映,過濾,排序

js的箭頭函式和java8的lambda表示式特別像

  • 對映
array.map(item=>item.id)
// 可以將陣列中的每一個元素對映成他的id屬性 
  • 過濾
persons = person.filter(p => p.name.indexOf(searchModel)>=0); 
// 保留陣列中滿足條件的物件
  • ES6的語法糖
    把物件的指定欄位存放進宣告的多個常量中
const{searchModel,person,orderType} = this;
  • 排序
persons.sort(function (p1,p2) {
                // 升序
                if (orderType===1){
                    return p1.age-p2.age;
                } else if (orderType===2){ // 降序
                    return p2.age-p1.age;
                }

事件繫結相關

@click繫結事件

<button @click="text1">text1</button>
<button @click="text2('haha')">text2</button>
<button @click="text3($event)">text3</button>
<button @click="text4">text4</button><!-- 如果沒有指定引數進去,傳遞進去的就是event-->
<button @click="text5(123,$event)">text5</button>

 var vm = new Vue({
el:'#test',
methods:{
    text1(){
        alert("text 1");
    },
    text2(msg){
        alert(msg);
    },
    text3(event){
        alert(event.target.innerHTML);
    },
    text4(event){
        alert(event.target.innerHTML);
    },
    text5(msg,event){
        alert(msg+event.target.innerHTML);
    },

可以看到@click使用vue中method的函式時,如果沒有引數,可以簡寫,去掉(), 如果不寫引數,傳遞進去的是事件本身event , text三中通過event拿到了標籤的文字內容

@click.prevent阻止事件的預設行為

<a href="http:www.baidu.com" @click.prevent="text8">百度一下</a>  <!-- 阻止事件的預設行為 -->

監聽某個按鍵的點選事件

<input type="text" @keyup.enter="text9"> <!--  @keyup.13(名字)  監聽某一個鍵的點選事件 -->

收集表單資料

使用vue將使用者填入表單中的資料收集起來, 收集到哪裡去? 其實是收集到 vue的data塊中的屬性中

其實就是在html使用v-model暴力繫結dom監聽, 將單選框,輸入框,多選框中使用者輸入進去的內容和data中的屬性關聯起來

  • input,textarea 等輸入框,收集起來的值就是使用者輸入進去的值
  • 單選框 radio ,多選框 checkbox 等選擇框,收集起來的值的 html中的value屬性的值
<h1>表單中最終提交給後臺的是 value值</h1><br>
<h2> 使用v-model實現表單資料的自動收集 </h2>
<form action="/XXX"  @submit.prevent="handleSubmit" ><!-- 阻止表單的預設自動提交事件 -->
   <span>使用者名稱:</span>
   <input type="text" v-model="username"><br>
   <span>密碼</span>
   <input type="password"  v-model="pwd" ><br>

   <span>性別</span><br>
   <input type="radio" id="female" value="女" v-model="sex">
   <label for="female">女</label><br>
   <input type="radio" id="male"  value="男" v-model="sex">
   <label for="male">男</label><br><br>

   <span>愛好</span><br>
   <input type="checkbox" id="basket" value="basket" v-model="likes">
   <label for="basket">籃球</label>
   <input type="checkbox" id="foot" value="foot"  v-model="likes">
   <label for="foot">足球</label>
   <input type="checkbox" id="pingpang"  value="pingpang"   v-model="likes">
   <label for="pingpang">乒乓球</label><br><br>

   <span>城市</span><br>
   <select v-model="cityId">
       <option value="">未選擇</option>
       <option :value="city.id" v-for="(city,index) in allCitys" :key="index">{{city.name}}</option>
   </select>
   <span>介紹:</span>
   <textarea name="" id="" cols="30" rows="10" v-model="dec"></textarea>

<input type="submit" value="註冊"><br>

    </form>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>

<script type="text/javascript">

var vm = new Vue({
    el:'#test',
    data:{
        username:'',
        pwd:'',
        sex:'女',
        likes:['foot'],
        allCitys:[{id:1,name:'北京'},{id:2,name:"山東"},{id:3 ,name:"青島"}],
        cityId:'3' /* 預設讓 3被選中*/,
        dec:"哈哈"
    },

    methods:{
        handleSubmit(){
            alert(this.username+this.pwd);
            alert(this.sex);
        }
    }
})

vue的生命週期

vue物件在建立初始化的過程中一次執行如下宣告週期相關的方法, 根據這個特性,通常把載入進入一個新的頁面中時去傳送ajax請求的方法放到mounted(){},收尾工作放在beforeDestroy(){}

var vm = new Vue({
el: "#text",
data: {},

beforeCreate() {  // 建立之前回撥
    console.log("beforeCreate");
},

created() {  // 建立之後回撥
    console.log("created");
},

beforeMount() {
    console.log("beforrMount");
},

// todo 常用, 非同步操作, 比如發起ajax請求獲取資料, 新增定時器
mounted() { // 初始化顯示之後會立即呼叫一次
    console.log("mounted");
    this.intervalId = setInterval(() => {
        console.log("幹掉vm之後, 定時器還在跑, 記憶體洩露了");
        this.isShow = !this.isShow;
    }, 1000);

    /*
      如果下面不使用箭頭回撥函式, this就是window, 而不是vm
    * setInterval(() => {
        this.isShow= !this.isShow;
    },1000);
    * */
},

// 更新階段
beforeUpdate() {  //更新階段之前回撥
    console.log("beforeUpdate");
},

updated() {  // 更新階段之後回撥
    console.log("updated");
},

// 死亡階段
// todo 常用 收尾工作
beforeDestroy() {  // 死亡之前回撥一次
    console.log("beforeDestroy ");
    clearInterval(this.intervalId);
},

destroyed() {
    console.log("destroyed");
},

methods: {}
}
});

ES的語法糖,箭頭函式

比如在設定定時器時, 定時器中需要對vue的屬性進行操作,在定時器的程式碼塊中this指的是定時器物件,es6的箭頭語法解決就這個問題, 在箭頭函式中this沒有的屬性,會到外層的vue中來找

this.intervalId = setInterval(() => {
    console.log("幹掉vm之後, 定時器還在跑, 記憶體洩露了");
    this.isShow = !this.isShow;
}, 1000);

動畫

按照vue的下面的幾步要求, vue 會給目標元素新增或者移除特定的 css,實現動畫的效果

  1. 需要新增動畫的標籤被 <transition name="" > XXX </transition>包裹

<div id="test">
   <transition name="YYY">
    <p v-show="isShow" class="">toggle</p>
    <button @click="isShow=!isShow">toggle</button>

    </transition>
</div>


<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
    new Vue({
        el: '#test',
        data() {
            return {
                isShow: true
            }
        }
    });
  1. 定義以 .YYY-開頭的 css屬性, 這個YYY就是上面自定義的YYY, 需要在這些自定義的屬性中指定過度的屬性以及隱藏的屬性

一個簡單的動畫效果標籤從隱藏->出現, 再從出現到隱藏的過程,就像下面這樣

v-enter  v-enter-to   v-leave    v-leave-to
  隱藏     出現         出現       隱藏

自定義這四個時期的狀態

/* 顯示的過度效果*/
.YYY-enter-active {
    transition: all 1s;
}

/* 隱藏的過度效果*/
.YYY-leave-active {
    transition: all 3s;
}

/* 從無到有的樣式  */
.YYY-enter {
    opacity: 0;
}

/* 從有到無的樣式  */
.YYY-leave-to {
    opacity: 0;
    transform: translateX(20px);  /* 離開時,向X軸的正方向移動20px*/
}

格式化時間的外掛庫

點選進入 moment.js網址 ,在這裡可以找到需要引入的script標籤

點選進入 moment.js的文件 在文件中可以找到對應的格式和例子

 <div id="test">
    <h2>顯示格式化的日期時間</h2>
    <p>{{date}}</p>
    <p>預設完整: {{date | dateFormat}}</p><!--  一旦我們這麼寫, 他就會把date的值,傳遞給dateFormat函式 -->
    <p>年月日: {{date | dateFormat('YYYY-MM-DD')}}</p><!--  一旦我們這麼寫, 他就會把date的值,傳遞給dateFormat函式 -->
    <p>時分秒: {{date | dateFormat('HH:mm:ss')}}</p><!--  一旦我們這麼寫, 他就會把date的值,傳遞給dateFormat函式 -->

</div>

/*  這個在官網上查詢 */
<script  type="text/javascript" src="https://cdn.bootcss.com/moment.js/2.21.0/moment.js"></script>

<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
        // 自定義過濾器
    Vue.filter('dateFormat',(value,format)=>{ /*  Vue是函式物件 */
      return moment(value).format(format || 'YYYY-MM-DD HH:mm:ss');
    });

    new Vue({
        el:'#test',
        data:{
            date:new Date()
        }
    });

Es的語法糖

es6 的語法: 形參預設值 , 沒傳值的話,就使用預設值

 function(value,format="YYYY-MM-DD"){
     return moment(value).format(format);
 }

vue的指令

常見的原生指令如下

v:text : 更新元素的textContent <br>
v:html : 更新元素的innerHtml<br>
v-if: true    如果為true,標籤才會輸出到頁面 <br>
v-else:    如果為false,標籤才會輸出到頁面 <br>
v-show:  通過控制display的樣式來控制顯示和隱藏<br>
v-for:    遍歷陣列物件 <br>
v-on:   繫結監聽事件, 一般直接寫  @ <br>
v-bind:  強制繫結解析表示式  一般簡寫成 : <br>
v-model:  雙向資料繫結 <br>
ref:  指定唯一的標識, Vue物件可以通過 $els 屬性來訪問這個元素物件  <br>
v-cloak: 防止閃現可能應為網速的原因{{msg}} 一直解析不了, 於是使用者就看到它了,不友好, 於是 vue推出 與css配合 [v-cloak] {display:none}  <br>

補充最後兩個

  • ref 指定唯一的標識, Vue物件可以通過 $els 屬性來訪問這個元素物件
  • 防止閃現可能應為網速的原因{{msg}} 一直解析不了, 於是使用者就看到它了,不友好, 於是 vue推出 與css配合 [v-cloak] {display:none}

例子:

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        [v-cloak] {   /*  回去尋找有這個屬性名的標籤  [v-cloak]   , 就是下面的p標籤 */
            display:none
        }
    </style>
</head>
<body>

<div id="test">
    <p ref="content123">哈哈哈哈</p>
    <button @click="hint">提示</button>
    //  v-cloak="" + 上面的css 樣式避免 {{ }} 閃現的效果
    <p v-cloak="">{{msg}}</p>
    <br>

</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
    // 註冊全域性指令
    Vue.directive('')

    new Vue({
       el:'#test',
       data:{
        msg: '嘿嘿'
       } ,
        methods:{
            hint(){
              //  因為 `<p ref="content123">哈哈哈哈</p>` 使用了ref,所以vue可以通過this.$refs.content123 找到指定的這個元素
                alert(this.$refs.content123.textContent)
            }
        }
    });

相關文章