Vue全面知識點速查

什麼是Vue
  1. 什麼是: 基於MVVM設計模式的漸進式的純前端js框架
    (1). MVVM?
    (2). 漸進式: 不要求整個項目都用vue做,可以輕鬆和別的技術混搭,且會多少就可以先用多少!
    (3). 純前端js框架: 與nodejs無關!單靠瀏覽器就可運行!
    (4). 框架:
    a. 原生js: 不需要下載,瀏覽器就自帶的ES+DOM+BOM
    1). 優點: 萬能!
    2). 缺點: 繁瑣!
    b. jQuery函數庫: 基於原生js,重新封裝的一批函數的集合。簡化了傳統DOM每一步操作!
    1). 優點: 簡單!
    2). 缺點: 沒有簡化開發的步驟!依然包含大量重複的編碼!
    c. 框架: 已經包含核心功能的半成品項目!
    1). 優點: 根本上簡化了開發流程,幾乎避免了重複編碼
    2). 缺點: 需要轉變觀念,和做事方法!很難適應!
  2. 爲什麼: 便於大項目的開發,避免重複代碼,提高開發效率
  3. 何時: 凡是以數據爲主(增刪改查)的項目,都可用vue開發!
如何使用Vue
  1. 下載:
    (1). 下載vue.js文件,引入普通網頁中使用:
    <script src="js/vue.js"> —— 初學者
    版本: 2.6
    開發版: 未壓縮版: 包括完備的註釋,代碼格式和見名知義的變量名,同時帶有非常人性化的錯誤提示。
    生產版: 壓縮版: 去掉了註釋,代碼格式,極簡化了變量名,同時刪除了錯誤提示!
    (2). 使用腳手架代碼: —— 公司中
    版本: 3或4
  2. 我的第一個vue功能: 示例:單擊按鈕改變數量:
    jQuery版本:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="js/jquery-1.11.3.js"></script>
</head>
<body>
<button id="btnMinus">-</button><span >0</span><button id="btnAdd">+</button>
<script>
//DOM4步
//1. 查找出發事件的元素
//本例中: 先點擊+按鈕
$("#btnAdd")
//2. 綁定事件處理函數
.click(function(){
  //3. 查找要修改的元素
  //本例中查找當前按鈕兄弟中的span元素
  var $span=$(this).siblings("span")
  //4. 修改元素
  //獲取span的內容,轉爲整數
  var n=parseInt($span.html());
  //n+1
  n++;
  //再放回去
  $span.html(n);
})

//1. 查找出發事件的元素
//本例中: 先點擊+按鈕
$("#btnMinus")
//2. 綁定事件處理函數
.click(function(){
  //3. 查找要修改的元素
  //本例中查找當前按鈕兄弟中的span元素
  var $span=$(this).siblings("span")
  //4. 修改元素
  //獲取span的內容,轉爲整數
  var n=parseInt($span.html());
  //只有n>0時,才能-1
  n>0&&(n--);
  //再放回去
  $span.html(n);
})
</script>
</body>

Vue版本:

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!--0. 先引入vue.js-->
<script src="js/vue.js"></script>
</head>
<body>
<!--1. 做界面: 
  1.1 要求:只能有一個唯一的父元素
  1.2 用{{變量}}語法在HTML中標記出可能變化的位置——類似於模板字符串中的${變量}
  本例中: 只有span的內容可能會變
  1.3 用@事件名="處理函數名",爲元素綁定事件
-->
<div id="app">
  <button @click="minus">-</button>
  <span>{{n}}</span>
  <button @click="add">+</button>
</div>
<script>
//2. 定義一個對象,其中保存頁面上所需的所有變量
//data中的變量名應該和html中的{{}}裏的變量名保持一致
//通常HTML中有幾個{{變量}},data中就要有幾個屬性支持頁面上的變量
//本例中: HTML中只需要一個變量n
//一個功能的所有變量都要寫在data對象內!
var data={ n:0 }
//3. 創建一個Vue類型的子對象,將數據對象data和頁面元素區域div#app綁定起來!
new Vue({
//:左邊都不能改名
  el:"#app",//找到HTML中id爲app的元素區域
  data:data,//將保存數據的data對象包含進new Vue裏邊來!
      //{ n:0 } 就成了vue的成員
  //所有頁面上需要的事件處理函數都要寫在methods的成員中:
  methods:{
    //本例中: 頁面上需要兩個函數: add和minus
    //在事件處理函數中,只需要埋頭專心處理數據即可!new Vue()會自動保持變量和頁面內容同步!
    //且在methods的方法中,可用this.訪問當前對象自己data中的變量
    add:function(){
    //this->當前new Vue()對象!
      this.n++; //this.data.n++
    },
    minus:function(){
      if(this.n>0){
        this.n--;
      }
    }
  }
})
</script>
</body>
MVVM設計模式
  1. 什麼是: 對前端三大部分代碼的重新劃分
  2. 舊劃分:
    (1). HTML 專門定義網頁內容的語言
    (2). CSS專門定義網頁樣式的的語言
    (3). Js專門操作頁面內容和樣式,添加交互行爲的語言
  3. 問題: 因爲HTML和CSS很弱!缺少編程語言必須的要素!
    (1). 沒有變量,如果想讓內容雖程序自動改變,不可能!
    (2). 缺少必要的程序結構: 沒有分支和循環
    導致: JS要承擔一切操作頁面的代碼!導致JS代碼繁瑣,且重複代碼極多!
  4. 新劃分:
    (1). 界面/視圖(View):
    a. 包括傳統的HTML+CSS
    b. 增強了HTML的功能!
    1). 比如: HTML中可以寫變量!
    2). HTML中可以寫if else if else 也可以寫for循環
    3). HTML中可以寫事件綁定!
    (2). 模型數據(Model): 頁面上所有需要的變量,集中保存在一個對象中!
    問題: 模型數據中的變量值,不會自動跑到頁面上指定位置的!
    (3). 視圖模型(ViewModel): 用一個對象將視圖(View)和模型對象(Model)綁定起來!
    綁定結果: 數據模型中的變量值,可以自動跑到視圖中指定位置,無需任何js編碼!且模型對象中數據改變,視圖中對應位置的變量值跟着自動變化!
  5. MVVM的原理: Vue框架是如何實現MVVM設計模式的
    (1). new Vue()加載data對象
    a. 將data對象打散,data內部的屬性直接隸屬於new Vue()對象
    b. 將data中每個原始屬性隱姓埋名,隱藏
    c. 爲data中每個屬性請保鏢:
    1). Data中每個屬性都有一對兒get/set方法
    2). 今後只要想修改data中的變量都會自動觸發set()
    3). 在每個屬性的set方法中,都自動植入一個notify()函數調用,只要試圖修改data中的屬性值時,都會自動調用set(),只要自動調用set()勢必會自動notify()發出通知
    (2). 加載虛擬DOM樹:
    a. 通過el屬性值的選擇器找到要監控區域的父元素
    b. 創建虛擬DOM樹
    c. 掃描這個要監控的區域:
    1). 每發現一個{{變量}}的元素,就將該元素的信息,記錄進虛擬DOM樹,同時首次用data中同名變量的值,代替頁面中{{n}}的位置。
    2). 每發現一個@事件名="函數名"的元素,就自動變爲:
    On事件名=“new Vue().函數名”
    (3). 加載methods對象: methods對象中的所有方法,都會被打散,直接隸屬於new Vue()和data中被打散的屬性平級
    所以,在methods中的方法中,想操作data中的屬性,都可以寫爲"this.屬性名"即可!
    (4). 當觸發事件時,自動調用new Vue()中methods中指定的函數,執行其中this.屬性名的修改。修改會自動觸發屬性的set()方法,自動觸發set()內部的notify函數:
    a. 遍歷虛擬DOM樹,只找出受影響的個別元素
    b. 利用虛擬DOM樹提前封裝好的DOM操作,只修改頁面中受影響的個別元素!——效率高!
  6. 總結: MVVM的原理/Vue的綁定原理:
    訪問器屬性+觀察者模式+虛擬DOM樹
  7. 什麼是虛擬DOM樹:
    (1). 什麼是虛擬DOM樹: 僅保存可能發生變化的少量元素的精簡版DOM樹
    (2). 優點:
    a. 小, 遍歷快!
    b. 只更新受影響的元素,效率高!
    c. 封裝了DOM操作,無需我們程序員重複編碼!
綁定語法: 同模板字符串中的${}

插值語法: Interpolation

  1. 什麼是綁定語法: 讓HTML中的內容也可隨程序中的變量改變而自動改變!——也就是給HTML中添加變量功能
  2. 何時: 只要元素的內容中想隨變量自動改變
  3. 如何: <元素>xxxx{{變量名}}xxxx</元素>
  4. 原理:
    (1). 首次加載頁面內容時,會用data中同名變量的初始值代替{{變量名}}位置
    (2). 當data中同名變量在new Vue()中被更改時,自動更新{{變量名}}位置爲新值
  5. 總結: {{}}中可以放什麼,不能放什麼
    (1). 可以放: 變量,表達式,函數調用,創建對象,訪問數組元素,三目
    (2). 不能放: 程序結構(分支和循環),沒有返回值的函數調用
  6. 示例: 使用vue在頁面上顯示不同內容
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
  <h1>用戶名:{{uname}}</h1>
  <h2>性別:{{sex==1?"男":"女"}}</h2>
  <h3>下單時間:{{new Date(orderTime).toLocaleString()}}</h3>
  <h3>小計:¥{{(price*count).toFixed(2)}}</h3>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    uname:"dingding",
    sex:1,
    orderTime:1579507293225,
    price:12.5,
    count:5
    //將來這些值都來自於ajax請求
  }
})
</script>
</body>
指令(directive)
  1. 什麼是: 爲HTML元素添加新功能的特殊屬性
  2. 何時: 只要HTML元素需要某些特殊的功能時,就要使用對應的指令來實現
  3. 包括: 13種
v-bind
  1. 如果元素的屬性值希望動態改變:
    (1). 問題: 不能用{{}}綁定
    (2). 解決: 應該用v-bind指令:
    a. 標準寫法: <元素 v-bind:屬性名=“js表達式”>
    b. 強調: 一定不要加{{}},屬性名前加v-bind:,=後的"“就扮演了{{}}的角色!{{}}中能寫什麼,此時”"中就能寫什麼!
    c. 簡寫: 其實v-bind可省略!但是:不能省略!<元素:屬性名="js表達式">
    (3). 示例: 根據不同的變量值,動態改變
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<!--希望img的src屬性,隨變量PM25的值變化
  如果pm25<100, src變爲1.png
  否則如果pm25<200, src變爲2.png
  否則如果pm25<300, src變爲3.png
  否則src變爲4.png-->
<img :src="pm25<100?'img/1.png':
           pm25<200?'img/2.png':
           pm25<300?'img/3.png':
                    'img/4.png'" >
<h1>{{pm25<100?'img/1.png':
      pm25<200?'img/2.png':
      pm25<300?'img/3.png':
               'img/4.png'}}</h1>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    //本例中: 因爲頁面上只需要一個變量pm25,所以data中只有一個變量pm25
    pm25:125
  }
})
setInterval(function(){
  vm.pm25=Math.random()*400
},1000)
</script>
</body>
  1. 根據程序中的變量值控制一個元素顯示隱藏: 2種辦法:
v-show

a. 如何: <元素 v-show="js條件">
b. 原理: new Vue()掃描到v-show時,自動執行js條件,如果條件爲true,則該元素原樣顯示。否則如果js條件執行結果爲false,則new Vue()自動爲當前元素加display:none,隱藏
c. 示例: 點按鈕控制對話框顯示和隱藏

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    .alert{
      width:300px;
      height:100px;
      position:fixed;
      padding:10px;
      top:50%;
      left:50%;
      margin-left:-160px;
      margin-top:-60px;
      background-color:#faf
    }
    .alert>span{
      cursor:pointer;
      border:1px solid #fff;
      float:right;
      padding:5px;
    }
  </style>
  <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
  <!--希望單擊按鈕時,讓對話框顯示-->
  <button @click="showIt">click me</button>
  <div v-show="show" class="alert">
    <!--希望點x時,關閉對話框-->
    <span @click="close">x</span>
    您的瀏覽器版本太低,請升級!
  </div>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    //本例中: 因爲頁面上需要一個bool類型的變量show,控制對話框的顯示隱藏
    show:false
  },
  methods:{
    //因爲頁面上單擊按鈕需要調用名爲showIt的函數,所以
    //showIt:function(){
    showIt(){
      //本例中: 調用showIt爲了讓對話框顯示!
      //所以: 
      this.show=true;
    },
    //因爲頁面上單擊x按鈕需要調用名爲close的函數,所以
    close(){
      //本例中: 調用close爲了關閉對話框
      this.show=false;
    }
  }
})
</script>
</body>
v-if

a. 也能控制一個元素顯示隱藏,上例中v-show可直接換成v-if。
b. 但是原理不同:v-if在掃描時,如果條件爲true,則保留該元素。否則如果條件爲false,則刪除該元素!
鄙視: v-show vs v-if
v-show 採用display:none方式隱藏元素,不改變DOM樹,效率高!
v-if 採用添加刪除元素方式控制元素顯示隱藏,可能頻繁修改DOM樹,效率低!
5. 控制兩個元素二選一顯示隱藏:
(1). <元素1 v-if=“條件”>
<元素2 v-else>
(2). 強調:
a. v-else後不要寫條件!(同js程序中的else)
b. v-if 和 v-else之間必須連着寫,不能插入任何其他元素
(3). 原理: 如果掃描到v-if時,自動執行條件。如果條件爲true,則保留v-if元素,刪除v-else元素。否則如果條件爲false,則刪除v-if元素,保留v-else元素
(4). 示例: 切換登錄和註銷狀態

<div id="app">
<!--希望如果已經登錄,顯示第一個div
    如果點註銷,希望狀態改爲未登錄-->
<div v-if="isLogin" id="div1">
  Welcome dingding | <a href="javascript:;" @click="logout">註銷</a>
</div>
<!--否則如果未登錄時,顯示第二個div
    如果點登錄,則狀態改爲已登錄-->
<div v-else id="div2">
  <a href="javascript:;" @click="login">登錄</a> | <a href="javascript:;">註冊</a>
</div>

<script>
var vm=new Vue({
  el:"#app",
  data:{
    //因爲界面上需要一個變量isLogin來表示是否登錄
    isLogin:false
  },
  methods:{
    //因爲頁面上單擊登錄,需要調用login函數
    login(){
      //點擊登錄,修改狀態爲已登錄
      this.isLogin=true;
    },
    //因爲頁面上單擊註銷,需要調用logout函數
    logout(){
      //點擊註銷,修改狀態爲未登錄
      this.isLogin=false;
    }
  }
})
</script>
  1. 多個元素多選一顯示:
    (1). <元素1 v-if=“條件1”>
    <元素2 v-else-if=“條件2”>
    … …
    <元素n v-else>
    (2). 強調:
    a. v-else後不要寫條件!(同js程序中的else)
    b. v-if和v-else-if 和 v-else之間必須連着寫,不能插入任何其他元素
    (3). 原理: 在new Vue掃描到這裏時:
    a. new Vue()會自動依次計算每個條件。
    b. 哪個條件爲true,就就保留哪個元素,刪除其他元素
    c. 如果所有條件都不滿足,只保留v-else元素,刪除之前其餘元素
    (4). 示例: 多個表情,多選一顯示
<div id="app">
<!--隨變量PM25的值變化,在四個img中選其一顯示,刪除其他img。
  如果pm25<100, 顯示1.png
  否則如果pm25<200, 顯示2.png
  否則如果pm25<300, 顯示3.png
  否則顯示4.png-->
<img v-if="pm25<100" src="img/1.png">
<img v-else-if="pm25<200" src="img/2.png">
<img v-else-if="pm25<300" src="img/3.png">
<img v-else src="img/4.png">
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    //本例中: 因爲頁面上只需要一個變量pm25,所以data中只有一個變量pm25
    pm25:125
  }
})
setInterval(function(){
  vm.pm25=Math.random()*400
},1000)
</script>
高頻筆試題(手寫) 觀察者模式
//觀察者(observer)模式: 變量值修改,所有關注的人都能自動得到通知,並收到新值
//保存數據的對象
var data={
  n:0, //外人想隨時獲得的變量,保存着外人關心的一個數據n
  observers:[],//用一個數組保存將來關心這個變量n的所有外人,這些覬覦這個變量n的外人,也稱爲觀察者
  //定義一個函數,專門修改變量n的值
  setN(n) {
    this.n = n
    //任何時候修改了變量n的值,都可以通知所有關注n的外人
    this.notifyAllObservers()
  },
  //定義一個函數,專門將關注變量n的外人(觀察者)對象,集中保存在data中的observers數組中,便於集中通知。
  addObs(observer) {
    this.observers.push(observer)
  },
  //定義一個函數,專門用於通知當前data中的observers數組中保存的關心變量n的外人們,n發生改變了
  notifyAllObservers() {
    //遍歷observers數組中每個外人對象
    this.observers.forEach(observer => {
      //每遍歷一個外人對象,就通知外人,它關心的變量n發生改變了,請及時獲取變量n的新值
      observer.getNewN()
    })
  }
}

//向data的observers數組中添加三個關心變量n的外人(觀察者)對象
for(var i=0;i<3;i++){
  data.addObs({
    //每個外人對象都包括兩個屬性和一個函數
    name:`obs${i}`, //外人的名字
    look: data, //每個外人都關心data對象中的變量,都緊緊盯着data對象
    //每個外人都準備一個函數,當data中變量改變時,可用於重新獲得data對象中n的新值。
    getNewN:function(){
      console.log(`${this.name} known that n is updated to ${this.look.n}`)
    }
  })
}

// 測試代碼
console.log("data將n改爲1時:")
data.setN(1)
console.log("data將n改爲2時:")
data.setN(2)
console.log("data將n改爲3時:")
data.setN(3)
v-for
  1. 反覆生成多個相同結構的HTML元素: v-for
    (1). <要反覆生成的元素 v-for="(value, i) of 數組">
    強調: v-for一定要放在那個要反覆生成的元素上,而不是放在父元素上!
    (2). 原理:
    a. 當new Vue()掃描到這裏時,自動遍歷of後的數組中每個元素
    b. 每遍歷一個元素,就創建一個當前HTML 元素的副本
    c. of前的兩個變量:
    1). value會自動獲得當前正在遍歷的數組元素值
    2). i 會自動獲得當前正在遍歷的下標位置
    d. 如果當前元素或子元素中,需要使用當前正在遍歷的元素值或下標,可用綁定語法來綁定value和i的值。
    強調: value和i的使用範圍僅限於當前元素及其子元素範圍內,不能在當前元素外使用!
    (3). 示例: 遍歷數組元素,反覆生成多個相同結構的元素
<div id="app">
  <ul>
    <!--本例中: 因爲要反覆生成多個li,所以v-for要寫在li上,而不是li的父元素ul上-->
    <li v-for="(value,i) of teachers" :key ="i">
      第{{i+1}}階段: {{value}}
    </li>
  </ul>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    teachers:["亮亮","然然","東東","濤濤"]
  }
})
</script>

(4)v-for還可:
a. 可遍歷數字下標的一切: 比如字符串
b. 可遍歷對象中每個屬性
示例: 遍歷對象中每個屬性,反覆生成多個html元素

<div id="app">
  <!--希望遍歷data中一個對象的每個屬性,反覆生成多個相同結構的HTML元素-->
  <ul>
    <li v-for="(value,key) of ym" :key ="key">{{key}} : {{value}}</li>
  </ul>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    ym:{
      math:89,
      chs:69,
      eng:91
    }
  }
})
</script>

c. v-for還會數數: 給v-for一個數字,他可以生成從1開始依次遞增的一個序列!
1).<要反覆生成的元素 v-for="i of 數字">
2). 原理: v-for會像人一樣,自動從1開始數數,每數一個數,就自動創建當前元素的一個副本。直到數到給定數字結束。
3). 其中: i會接住每次數的一個數字,可用於當前元素及子元素的綁定中。
示例: 根據頁數,生成指定個數的分頁按鈕

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
  ul{ list-style:none }
  ul>li{
    float:left; 
    border:1px solid #555;
    width:36px;
    height:36px;
    line-height:36px;
    text-align:center;
  }
  ul>li+li{
    border-left:0
  }
  </style>
  <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
  <ul>
    <li v-for="i of pageCount">{{i}}</li>
  </ul>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    pageCount:3
  }
})
</script>
</body>
splice

(5) : 如果v-for遍歷的是數組時,在程序中通過下標修改數組元素值,頁面上的HTML元素不會自動更改!
比如: this.teachers[0]="燕兒" 頁面上是不會變的!
因爲數組中的數字類型的下標012…無法添加訪問器屬性,也就不受監控!
解決: 今後,vue中修改數組中的元素值!必須用數組家函數!才能自動更新頁面。因爲函數都是受監控的。
比如: this.teachers.splice(0,1,"燕兒")
刪除0位置的1個元素,再在0位置放入"燕兒"
結果: 頁面會自動變化!

在這裏插入圖片描述
(6). 其實每次使用v-for時,都要同時綁定:key=“i”

:key=“i”

BS: 爲什麼v-for必須加:key
答: 因爲v-for反覆生成的多個元素,除了內容不同之外,從元素屬性上來看多個元素毫無差別!每個反覆生成的元素都是一樣的。所以,如果將來修改了數組中一個元素時,v-for因爲無法識別每個HTML元素,所以只能把所有的HTML元素重新生成一遍——效率低!
如果給每個元素都綁定:key="i"屬性,則每個HTML元素上都有一個唯一的標識key=“0” key=“1” … 。當將來修改了數組中每個位置的元素時,只需要修改對應key的HTML元素即可,其他HTML元素保持不變!——效率高!
總結: 避免修改數組元素時,重新生成所有HTML元素,而是隻更新其中一個HTML元素即可!提高修改效率!
如何:
1). 當遍歷數組時: <元素 v-for="(val,i) of 數組" :key="i"
Key的值依次是0 1 2 3…
2). 當遍歷對象時: <元素 v-for="(val,key) of 對象" :key="key"
Key的值依次是: 屬性名1 屬性名2 …
因爲一個對象內的屬性名肯定不會重複,所以,屬性名也可以當做:key唯一標識一個HTML元素

綁定HTML片段內容
v-html

(1). 問題: {{}}不能綁定HTML片段內容。
因爲: {{}}本質相當於DOM中的textContent,會將HTML內容原樣顯示,不會被編譯!
(2). 解決: 今後只要要綁定的變量內容是一段HTML片段時,都用v-html來綁定!
(3). 如何:<元素 v-html="包含HTML內容的變量或表達式">
(4). 強調:
a. v-html會將綁定內容中的HTML內容,編譯後再顯示給人看
b. v-html也是指令,所以v-html後的""中可以寫js表達式,比如字符串拼接!
c. 用了v-html,就不要再元素開始標籤和結束標籤直接寫內容!因爲會被v-html內容替換!
(5). 示例: 綁定HTML內容

<div id="app">
  <h1>消息來源:{{html}}</h1>
  <h1 v-html="'消息來源:'+html">
    Welcome
  </h1>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    html:`<p>來自<a href="javascript:;">&lt;&lt;新華社&gt;&gt;</a>的消息</p>`
  }
})
</script>
防止用戶短暫看到{{}}

問題: 因爲vue代碼是放在js文件中,所以,如果網速慢,vue代碼暫時沒有下載下來時,用戶很可能短暫看到頁面上的綁定語法,用戶體驗不好!
解決: 2個辦法:

v-cloak

(1). 用v-cloak暫時隱藏帶有{{}}內容的元素:
a. 2步:
1). 在包含綁定語法{{}}的元素上添加v-cloak屬性
2). 在css中手動添加樣式: [v-cloak]{ display:none }
b. 原理:
1). 用屬性選擇器查找所有帶有v-cloak屬性的元素,暫時隱藏
2). 當new Vue()渲染完成時,自動找到所有v-cloak屬性,自動移除。
c. 示例: 使用v-cloak防止用戶短暫看到{{}}

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
  [v-cloak]{
    display:none
  }
  </style>
  <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
  <h1 v-cloak>Welcome: {{uname}}</h1>
</div>
<script>
setTimeout(function(){
  var vm=new Vue({
    el:"#app",
    data:{
      uname:"dingding"
    }
  })
},2000)
</script>
</body>

d. 問題: 既要在HTML中寫指令,又要手動添加css選擇器,步驟繁瑣的!

v-text

(2). 用v-text代替內容中{{}}語法,來綁定非HTML片段內容:
a. <元素 v-text=“原{{}}內容”></元素>
b. 原理:
1). 因爲綁定語法寫在了元素的屬性裏,所以,如果不是vue幫忙,用戶無論如何是看不到元素屬性中的內容的!
2). New Vue()讀取到v-text時,會解析v-text的內容,替換元素開始標籤和結束標籤之間的內容
c. 強調:
1). 和v-html不同,v-text等效於{{}}等效於DOM中的textContent,所以如果v-text中包含HTML片段,是不會被編譯,而是原樣顯示給人看!
2). v-text也是指令,所以v-text後的""中也可以寫js表達式,比如字符串拼接!
3). 用了v-text,也不要在元素開始標籤和結束標籤直接寫內容!因爲會被v-text內容替換!
d. 示例: 使用v-text防止用戶短暫看到{{}}

<div id="app">
  <h1 v-text="'Welcome:'+uname"></h1>
</div>
<script>
setTimeout(function(){
  var vm=new Vue({
    el:"#app",
    data:{
      uname:"dingding"
    }
  })
},2000)
</script>
事件綁定

(1). 標準寫法:
a. div#app中: <元素 v-on:事件名=“處理函數()”
b. new Vue()內:
methods:{
處理函數(形參變量){
//可用"this.屬性名"方式訪問data中的模型變量
}
}
(2). 簡寫:
a. v-on: 可用@簡寫
<元素 @事件名=“處理函數()”
b. 如果處理函數不需要傳參,()可省略
<元素 @事件名=“處理函數”
(3). 示例: 點擊按鈕,數量增長

<div id="app">
  <button @click="change(-1)">-</button>
  <span>{{n}}</span>
  <button @click="change(+1)">+</button>
</div>
<script>
var data={ n:0 }
var vm=new Vue({
  el:"#app",
  data:data,
  methods:{
    //本例中,因爲頁面上只需要一個change函數,所以methods中只添加一個change函數
    //但是頁面上調用change()函數時,傳入了一個參數值,所以定義change函數時需要定義一個形參來接住實參值
    change(i){
      //如果i==+1,說明本次想+1
      if(i==+1){
        this.n++;
      }else{//否則如果i!=+1,說明本次想-1
        //只有n>0時,才能-1
        if(this.n>0){
          this.n--;
        }
      }
    }
  }
});
</script>

(4). 事件對象:
a. 回顧: DOM中事件對象總是作爲事件處理函數的第一個參數值默認傳入:
元素.on事件名=function(e){
e->event
e.stopPropagation()
e.preventDefault()
e.target
e.offsetX e.offsetY
e.clientX e.clientY
}

vue中如何獲得事件對象 (vue中如何獲得鼠標位置)

1). 如果事件處理函數不需要傳入實參值時,則:
事件對象也是作爲處理函數第一個參數值自動傳入,也是在函數定義時,用一個形參e,就可接住——同DOM
示例: 使用e獲得鼠標位置:

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
  div{
    width:300px; height:100px;
    margin:20px;
  }
  #d1{
    background-color:#aaf
  }
  #d2{
    background-color:#ffa
  }
  </style>
  <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<div id="d1" @click="doit">d1</div>
<div id="d2">d2</div>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    
  },
  methods:{
    doit(e){//同DOM的e
      console.log(`點在d1的: x:${e.offsetX},y:${e.offsetY}`);
    }
  }
})
</script>
</body>
$event

BS: 如果既想傳入自定義實參值,又想獲得事件對象:
i. 藉助 $ event關鍵字: vue框架內部內置的專門指向事件對象的關鍵詞。用 $ event等效於用事件對象e
ii. 調用函數時,可將 $ event和其他實參值一起傳入函數中
iii. 定義函數時,可用普通的形參變量接住 $ event的值。
iv. 示例: 使用 $event獲得鼠標位置並傳入自定義實參值

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
  div{
    width:300px; height:100px;
    margin:20px;
  }
  #d1{
    background-color:#aaf
  }
  #d2{
    background-color:#ffa
  }
  </style>
  <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<div id="d1" @click="doit('d1',$event)">d1</div>
<div id="d2" @click="doit('d2',$event)">d2</div>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    
  },
  methods:{
    doit(name,e){//同DOM的e
      console.log(`點在${name}的: x:${e.offsetX},y:${e.offsetY}`);
    }
  }
})
</script>
</body>
v-once

僅在首次渲染頁面時綁定一次,即使之後模型變量再改變,也不會自動更新頁面: <元素 v-once>
示例: 顯示時間

<h1>當前系統時間: {{time}}</h1>
<!--希望上線時間只在首次打開網頁時綁定一次,之後,即使time變量值發生變化,也不會自動更新上線時間-->
<h1 v-once>上線時間: {{time}}</h1>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    time:new Date().toLocaleString()
  }
})
setInterval(function(){
  vm.time=new Date().toLocaleString()
},1000)
</script>
v-pre

防止元素內容中的{{}}被vue編譯,讓內容中的{{}}原樣顯示!
<元素 v-pre> xxx{{xx}}xxx </元素>

雙向綁定
  1. 單向綁定: 只能將data中的變量值,自動同步更新到HTML頁面中。但是,頁面上的修改,無法自動更新回data的變量中。(前面的11種都是)
    (data —> div#app 但是 div#app -x-> data)
  2. 雙向綁定: 既能將data中的變量值,自動同步更新到HTML頁面中。又能將頁面上的修改自動更新回data的變量中。
    (data <===> div#app)
  3. 何時使用雙向綁定: 只有綁定表單元素時,纔有必要用雙向綁定!因爲只有表單元素,用戶才能在頁面上修改的它的內容。
v-model
  1. 如何: 每種表單元素綁定的原理不同:
    1). 文本框/文本域: 綁定的是value屬性
    a. <input v-model:value="變量"/> / <textarea v-model:value="變量"></textarea>
    b. 原理: new Vue()掃描到這裏時,自動爲當前元素綁定事件,比如,如果<input type="text" v-model:value="變量">,就會翻譯爲: οninput=“vm.變量=當前文本框的新value”。
    c. 示例: 點按鈕,按回車,在文本框中輸入內容,都可獲得文本框中輸入的關鍵詞,執行搜索操作。
<div id="app">
  <!--在文本框上按回車可以查找-->
  <input type="text" v-model:value="keywords" @keyup="myKeyUp">
  <!--點擊按鈕可以查找-->
  <button @click="search">百度一下</button>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    keywords:"macbook i5"
  },
  methods:{
    search(){
      console.log(`查找 ${this.keywords} 相關的內容...`)
    },
       // $event
       //   ↓
    myKeyUp(e){
      //只有按回車才查找
      if(e.keyCode==13){
        //調用旁邊的search()函數
        this.search();
      }
    }
  },
  //想變量keywords只要被更改,就重新執行一次搜索
  watch:{
    keywords(){
      console.log("自動調用一次watch中的keywords函數...")
      this.search();
    }
  }//進入new Vue()中的一切data:{}, methods:{}, watch:{}都會被打散,最終都直接隸屬於new VUe()對象,都是平級的,所以可以this.方式,互訪。
})
</script>

(2). 單選按鈕: 綁定的是checked屬性
a. <input type="radio" name="sex" value="1" v-model:checked="變量">
<input type="radio" name="sex" value="0" v-model:checked="變量">
b. 原理:
(1). 從data->input: 用checked綁定的變量的值和當前radio的value做比較。如果checked綁定的值和value值相等,則當前radio選中。否則不選中。
在這裏插入圖片描述
(2). 切換選中一個radio後,用當前選中的radio的value值代替checked屬性綁定的data中的變量值。導致,頁面中其它關注這個變量的位置都自動發生改變。
在這裏插入圖片描述
示例: 選擇性別

<div id="app">
<label><input type="radio" name="sex" value="1" v-model:checked="sex"></label>
<label><input type="radio" name="sex" value="0" v-model:checked="sex"></label>
<h1>sex:{{sex}}</h1>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    sex:1
  }
})
</script>

(3). 雙向綁定select元素: 因爲改變select的選中項,改變的是中整個select的value值,所以應該綁定select元素的value
a.

<select v-model:value="變量">
		<option value="值1">文本1</option>
		<option value="值2">文本2</option>
		... ...

b. 原理:
1). 從data->select:用value綁定變量值,和每個option的value做比較,哪個option的value等於變量值,就選中哪個option在這裏插入圖片描述
2). 切換select中的選中項: 用新選中的option的value值替換select的value綁定的data中的變量值。導致所有關係這個變量的其它位置自動變化。在這裏插入圖片描述
c. 示例: 選擇訂單狀態:

<div id="app">
<select v-model:value="orderStatus">
  <option value="0">未付款</option>
  <option value="10">已付款</option>
  <option value="20">已發貨</option>
  <option value="30">已簽收</option>
</select>
<h1>orderStatus:{{orderStatus}}</h1>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    //0:未付款  10:已付款  20:已發貨 30:已簽收
    orderStatus:10
  }
})
</script>

(4). 雙向綁定一個checkbox的情況: 因爲選中或取消選中checkbox改變的它的checked屬性,所有v-model應該綁定checked屬性。且綁定的變量應該是bool類型
a. <input type="checkbox" v-model:checked="變量">
b. 原理:
1). 從data->input checkbox: 根據變量是true還是false,設置當前checkbox是否選中
2). 當切換checkbox的選中狀態後,就用新的checked屬性值(true或false),更新到綁定的data中的變量中
c. 示例: 同意,啓用表單元素,不同意,則禁用表單元素
在這裏插入圖片描述在這裏插入圖片描述

<div id="app">
  <input type="text" placeholder="請輸入用戶名" :disabled="!isAgree"><br/>
  <input type="password" placeholder="請輸入密碼" :disabled="!isAgree"><br/>
  <label><input type="checkbox" v-model:checked="isAgree">同意</label></br>
  <button :disabled="!isAgree">註冊</button>
  <h1>isAgree:{{isAgree}}</h1>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    isAgree:false //表示不同意
  }
})
</script>

(5). 簡寫: 其實以上所有v-model後的":屬性名"都可省略!v-model可自動根據所在的元素不同,選擇對應的的元素自動綁定。

監控函數 watch

(1). 雙向綁定過程中只要希望變量值一發生變化,就自動執行一項操作,可用watch,添加監控函數。
(2). new Vue({
el:"#app",
data:{ 變量: 值, … },
methods:{
函數(){ … }
},
watch:{ //監視
要監視的變量(){ //只要data中的變量被修改,watch中與變量同名的監視函數,就會被自動執行!
… …
}
}
})
(3). 強調: watch中的函數名必須和要監視的變量同名!

綁定樣式
綁定內聯樣式

(1). 不好的做法: 將元素的整個style屬性當做一個字符串來綁定
<元素 style=“固定不變的css屬性” :style=“變量”>
data:{
變量:“可能變化的css屬性” //比如: “top:50px; left:100px”
}
缺點: 不便於修改其中某一個css屬性。
(2). 好的做法: 將元素的style屬性當做一個對象來綁定:
優點: 便於修改單個css屬性
a. 只創建一個變量,但是變量值以對象方式保存多個css屬性
<元素 style=“固定不變的css屬性” :style=“變量”>
data:{
變量:{
Css屬性: 值,
… : …
}
//自動翻譯爲: 一個字符串: “css屬性:值; css屬性:值; …”
}
:style動態綁定的變量中的css屬性,經過編譯爲字符串後,自動和不帶:的固定不變的style中的css屬性最終合併爲一個style屬性。
示例: 用鍵盤上下左右方向鍵控制一個小方塊的移動:

<div id="app">
  <div id="pop" style="position:fixed; width:100px; height:100px; background-color:pink" :style="popStyle"></div>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    popStyle:{
      top:"0px",
      left:"0px"
    }
    //自動翻譯爲: popStyle:"top:0px; left:0px"
  }
})
window.onkeydown=function(e){
  //左: 37  上: 38  右: 39  下: 40
  if(e.keyCode==39){//右
    var left=parseInt(vm.popStyle.left);
    left+=20;
    vm.popStyle.left=left+"px";
  }else if(e.keyCode==40){//下
    var top=parseInt(vm.popStyle.top);
    top+=20;
    vm.popStyle.top=top+"px";
  }
}

</script>

b. 爲每個動態變化的css屬性都創建一個變量(創建多個變量),綁定時,"“中使用匿名對象:style=”{css屬性: 變量1, css屬性:變量2 }"
<元素 style=“固定不變的css屬性” :style="{css屬性1: 變量1, css屬性2: 變量2 , …}">
data:{
變量1: 值1,
變量2: 值2
}
結果: :style="{css屬性1: 變量1, css屬性2: 變量2 , …}",會被自動翻譯爲:
style=“css屬性1:變量1的值; css屬性2: 變量2的值”,再和其他寫死的不帶:的style合併
示例: 用鍵盤上下左右方向鍵控制一個小方塊的移動:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
  <div id="pop" style="position:fixed; width:100px; height:100px; background-color:pink" :style="{top, left}"></div>
  <!--自動翻譯爲: top:0px; left:0px;-->
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    top:"0px",
    left:"0px"
  }
});
window.onkeydown=function(e){
  //左: 37  上: 38  右: 39  下: 40
  if(e.keyCode==39){//右
    var left=parseInt(vm.left);
    left+=20;
    vm.left=left+"px";
  }else if(e.keyCode==40){//下
    var top=parseInt(vm.top);
    top+=20;
    vm.top=top+"px";
  }else if(e.keyCode==38){//上
    var top=parseInt(vm.top);
    top-=20;
    vm.top=top+"px";
  }else if(e.keyCode==37){//左
    var left=parseInt(vm.left);
    left-=20;
    vm.left=left+"px";
  }
}
</script>
</body>
</html>
綁定class

(1). style綁定內聯樣式的問題: 多數樣式的修改,都是同時修改多個css屬性,如果用style綁定,每個css屬性都要寫出來,代碼繁瑣!
(2). 解決: 將來只要批量修改一個元素的多個css屬性,應該用class方式,代替style方式
(3). 如何:
a. 不好的做法: class屬性也可以作爲一整條字符串綁定,但是問題依然是不便於只修改其中一個class。
b. 好的做法,將class作爲一個對象綁定:
1). 只聲明一個變量,但是變量值是一個對象,其中包含多個class名
i. <元素 class=“固定不變的class” :class=“變量”
ii. data:{
變量: { class1 : true或false, class2: true或false }
}
iii. 結果: 編譯時,僅將變量對象中值爲true的class,編譯進最終的class中,值爲false的class,不包含在最終的正式class字符串中。且動態綁定的:class會和不帶:綁定的固定不變的class,合併爲一個class!
iv. 示例: 驗證手機號格式:

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <style>
    /* 定義提示框的基礎樣式 */
    .msg{
      display:inline-block;
      width:160px;
      height:25px;
      border:1px solid #555;
      text-align:center;
      line-height:25px;
    }
    /* 定義提示框在驗證通過時的樣式 */
    .success{
      border:1px solid green;
      background-color:lightgreen;
      color:green
    }
    /* 定義提示框在驗證失敗時的樣式 */
    .fail{
      border:1px solid red;
      background-color:pink;
      color:red
    }
  </style>
</head>
<body>
<div id="app">
  <!-- 因爲要獲得用戶輸入的手機號進行驗證,所有必須用雙向綁定 -->
  <input type="text" v-model="phone">
  <!-- 因爲提示框的樣式可能在success和fail之間來回切換,所以動態綁定span的部分class 
      又因爲提示框的內容也可能隨驗證結果而動態變化,所以也要綁定一個變量msg-->
  <span class="msg" :class="msgClass">{{msg}}</span>
  <!--界面中共需要三個變量-->
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    //因爲頁面中需要三個變量,所以data中就要有三個變量
    phone:"", //實時接用戶在文本框中輸入的手機號
    msg:"", //根據驗證結果的對錯,綁定提示信息的內容
    //根據驗證結果的對錯,動態在success和fail兩個class之間來回切換。
    msgClass:{
      success:false,
      fail:false
    }
  },
  watch:{
    //因爲用戶一邊輸入,vue就一邊驗證,所以必須用watch隨時監控用於的輸入變化。
    phone(){
      //定義正則驗證phone的內容
      var reg=/^1[3-9]\d{9}$/;
      //如果用正則驗證phone的內容通過
      if(reg.test(this.phone)==true){
        //就修改msgClass應用成功的樣式,不應用失敗的樣式
        this.msgClass={
          success:true,
          fail:false
        }
        //修改span的提示信息內容
        this.msg="手機號格式正確!"
      }else{//否則如果驗證失敗
        //就修改msgClass應用失敗的樣式,不應用成功的樣式
        this.msgClass={
          success:false,
          fail:true
        }
        //修改span的提示信息內容
        this.msg="手機號格式正確!"
      }
    }
  }
})
</script>
</body>

2). 爲每個class都聲明一個變量,在綁定時,可用{}對象語法綁定:
i. <元素 class=“固定不變的class” :class="{class1: 變量1, class2: 變量2, …}"
ii. data:{
變量1: true或false,
變量2: true或false
}
iii. 結果: 編譯時,僅將:class="{}"中值爲true的class,編譯進最終的class中,值爲false的class,不包含在最終的正式class字符串中。且動態綁定的:class會和不帶:綁定的固定不變的class,合併爲一個class!
iv. 示例: 驗證手機號格式:

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <style>
    /* 定義提示框的基礎樣式 */
    .msg{
      display:inline-block;
      width:160px;
      height:25px;
      border:1px solid #555;
      text-align:center;
      line-height:25px;
    }
    /* 定義提示框在驗證通過時的樣式 */
    .success{
      border:1px solid green;
      background-color:lightgreen;
      color:green
    }
    /* 定義提示框在驗證失敗時的樣式 */
    .fail{
      border:1px solid red;
      background-color:pink;
      color:red
    }
  </style>
</head>
<body>
<div id="app">
  <!-- 因爲要獲得用戶輸入的手機號進行驗證,所有必須用雙向綁定 -->
  <input type="text" v-model="phone">
  <!-- 因爲提示框的樣式可能在success和fail之間來回切換,所以動態綁定span的部分class 
      又因爲提示框的內容也可能隨驗證結果而動態變化,所以也要綁定一個變量msg-->
  <!--                       class名  變量-->
  <!-- <span class="msg" :class="{success:success, fail:fail}">{{msg}}</span> -->
  <!--結果: 哪個class名後的變量值爲true,纔會進入最終的class字符串中。變量值爲false的class,不會出現在最終的class字符串中-->
  <span class="msg" :class="{success, fail}">{{msg}}</span>
  <!--界面中共需要四個變量-->
  <!--但是,兩個class和錯誤提示三個變量的值,都和驗證結果這一個數據有關!所有,其實只用一個變量就可控制三個值的變化-->
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    //因爲頁面中需要三個變量,所以data中就要有三個變量
    phone:"", //實時接用戶在文本框中輸入的手機號
    msg:"", //根據驗證結果的對錯,綁定提示信息的內容
    //根據驗證結果的對錯,動態在success和fail兩個class之間來回切換。
    success:false,
    fail:false
  },
  watch:{
    //因爲用戶一邊輸入,vue就一邊驗證,所以必須用watch隨時監控用於的輸入變化。
    phone(){
      //定義正則驗證phone的內容
      var reg=/^1[3-9]\d{9}$/;
      //如果用正則驗證phone的內容通過
      if(reg.test(this.phone)==true){
        //就修改msgClass應用成功的樣式,不應用失敗的樣式
        this.success=true;
        this.fail=false
        //修改span的提示信息內容
        this.msg="手機號格式正確!"
      }else{//否則如果驗證失敗
        //就修改msgClass應用失敗的樣式,不應用成功的樣式
        this.success=false;
        this.fail=true
        //修改span的提示信息內容
        this.msg="手機號格式正確!"
      }
    }
  }
})
</script>
</body>

iv. 示例: 優化: 儘量減少vue中data中的變量,便於維護。

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <style>
    /* 定義提示框的基礎樣式 */
    .msg{
      display:inline-block;
      width:160px;
      height:25px;
      border:1px solid #555;
      text-align:center;
      line-height:25px;
    }
    /* 定義提示框在驗證通過時的樣式 */
    .success{
      border:1px solid green;
      background-color:lightgreen;
      color:green
    }
    /* 定義提示框在驗證失敗時的樣式 */
    .fail{
      border:1px solid red;
      background-color:pink;
      color:red
    }
  </style>
</head>
<body>
<div id="app">
  <!-- 因爲要獲得用戶輸入的手機號進行驗證,所有必須用雙向綁定 -->
  <input type="text" v-model="phone">
  <!-- 因爲提示框的樣式可能在success和fail之間來回切換,所以動態綁定span的部分class 
      又因爲提示框的內容也可能隨驗證結果而動態變化,所以也要綁定一個變量msg-->
  <!--                       class名  變量-->
  <!-- <span class="msg" :class="{success:success, fail:fail}">{{msg}}</span> -->
  <!--結果: 哪個class名後的變量值爲true,纔會進入最終的class字符串中。變量值爲false的class,不會出現在最終的class字符串中-->
  <!-- <span class="msg" :class="{success:isRight, fail:isRight==false}">{{isRight==true?"手機號可用":"手機號格式錯誤!"}}</span> -->
  <span class="msg" :class="isRight==true?'success':'fail'">{{isRight==true?"手機號可用":"手機號格式錯誤!"}}</span>
  <!--界面中共需要四個變量-->
  <!--但是,兩個class和錯誤提示三個變量的值,都和驗證結果這一個數據有關!所有,其實只用一個變量就可控制三個值的變化-->
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    //因爲頁面中需要三個變量,所以data中就要有三個變量
    phone:"", //實時接用戶在文本框中輸入的手機號
    isRight:false
  },
  watch:{
    //因爲用戶一邊輸入,vue就一邊驗證,所以必須用watch隨時監控用於的輸入變化。
    phone(){
      //定義正則驗證phone的內容
      var reg=/^1[3-9]\d{9}$/;
      //將驗證結果保存到變量isRight中,依次牽連着三個位置發生變化!
      this.isRight=reg.test(this.phone);
    }
  }
})
</script>
</body>
自定義指令
  1. 向Vue大家庭中添加自定義指令:
    Vue.directive(“指令名”, { //向Vue大家庭中添加一個新的指令,並制定指令名
    //回調函數: 當渲染後的元素被插入到DOM樹上之後,自動執行該回調函數
    inserted(當前指令所在的DOM元素對象){
    //對當前指令所在的DOM元素對象,執行DOM操作
    //在這裏對當前DOM元素執行的DOM操作,會自動應用到頁面上
    }
    })
    強調: 添加自定義指令時,指令名一定不要帶v-前綴!比如,想添加一個讓元素自動獲得焦點的自定義指令,可命名爲"focus"

  2. 使用自定義指令: 只要在想應用對應效果的元素上,添加"v-指令名"屬性即可。
    強調: 雖然定義指令時,指令名沒有加v-前綴,但是使用指令時,必須加v-前綴

  3. 原理:
    (1). Vue.directive(“指令名”,{
    inserted(domElem){
    對domElem執行原生DOM操作
    }
    })
    向Vue大家庭中添加一個新的自定義指令,關聯一個處理函數
    (2). new Vue()時,會掃描<div id="app">下的所有內容。
    (3). 每掃描到一個v-開頭的指令屬性,就會回Vue大家庭中找是否有對應的指令。如果有對應的指令,就調用指令關聯的處理函數,對指令所在的元素執行原生DOM操作。

  4. 示例: 讓文本框自動獲得焦點:

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <script>
    Vue.directive("focus",{
      inserted(domElem){
        //讓當前元素自動獲得焦點
        domElem.focus();
        //      DOM原生
      }
    })
  </script>
</head>
<body>
<div id="app">
  <input type="text" v-focus><button>百度一下</button>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    
  }
})
</script>
</body>

強調: new Vue()起到最重要的掃描HTML內容的作用,只有new Vue()才認識v-開頭的指令,所有,即使data中沒有值,只要用到了Vue相關的功能,new Vue()都不能省略!

計算屬性
  1. 什麼是: 程序中沒有保存該屬性的屬性值,每次綁定屬性時,都需要根據其他屬性的值動態計算獲得該屬性的屬性值。
  2. 爲什麼: 有些值,總是可以根據其他屬性值的變化,動態計算獲得。這樣的值,就沒必要在程序中再保存一份。因爲就算保存了,這個屬性所依賴的其他屬性值一旦發生變化,這個屬性的屬性值連帶也要改變。
  3. 何時: 今後,只要一個屬性的值可以根據其他屬性計算出來,都沒必要保存!
  4. 如何:
    (1). 定義計算屬性:
    new Vue({
    el:"#app",
    data:{
    頁面所需的變量
    },
    methods:{
    自定義函數或事件處理函數
    },
    watch:{
    監視某個變量的監視函數
    },
    computed:{
    屬性名(){
    return 根據其他屬性值計算後獲得的計算結果
    }
    }
    })
    (2). 使用計算屬性: 計算屬性,雖然本質是一個函數,但是在HTML中綁定語法中使用時,不要加()!
  5. 結果:
    (1). 只要計算屬性所依賴的另一個屬性值發生改變,同樣會通知計算屬性重新計算新的屬性值。
    (2). 計算屬性計算出的結果,會被Vue緩存起來,反覆使用,避免重複計算。即使反覆使用多次,也只在首次計算一次。
  6. 鄙視: methods vs computed
    (1). 用法:
    a. methods必須加()
    b. computed 不用加()
    (2). 反覆使用:
    a. methods中的方法,每調用一次,就會重新執行一次,執行結果,不會被vue緩存起來。
    b. computed中的計算屬性,只在第一次使用時計算一次,然後,結算結果,就會被Vue緩存起來,即使在別的地方反覆使用這個計算屬性,也不會重複計算,而是直接從緩存中獲取值。但是,當所依賴的其他屬性值發生變化時,計算才被迫重新計算一次。
  7. 如何選擇methods和computed:
    (1). 如果這次使用時,更關心函數的執行結果數據時,首選計算屬性
    (2). 如果這次使用時,更關心函數執行操作的過程,結果數據無所謂,甚至函數沒有返回值,則首選methods中的方法。
  8. 示例: 使用計算屬性綁定購物車總價:
<div id="app">
  <h3>總計: ¥{{total.toFixed(2)}}</h3>
  <ul>
    <li v-for="(item,i) of cart" :key="i">
      {{item.pid}} | {{item.pname}} | ¥{{item.price.toFixed(2)}} | {{item.count}} —— 小計:¥{{(item.price*item.count).toFixed(2)}}
    </li>
  </ul>
  <h3>總計: ¥{{total.toFixed(2)}}</h3>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    cart:[
      {pid:1, pname:"華爲",price:4455, count:2},
      {pid:2, pname:"小米",price:3455, count:1},
      {pid:3, pname:"OPPO",price:3356, count:3},
    ]
  },
  methods:{ //叫方法,所以用法同方法的用法——必須加()
    
  },
  computed:{ //叫屬性,所以用法同屬性的用法——不加()
    total(){
      console.log("調用了一次total()");
      var sum=0;
      for(var p of this.cart){
        sum+=p.price*p.count;
      }
      return sum;
    }
  }
})
</script>
過濾器
  1. 什麼是: 專門對變量的原始值進行加工後,再顯示的特殊函數

  2. 爲什麼: 個別變量的原始值不能直接給人看!
    比如: 性別 0和1 日期的毫秒數

  3. 何時: 如果一個變量的值不能直接給人看時,必須經過加工,才能給人看時

  4. 如何:
    (1). 向Vue大家庭中添加過濾器函數
    Vue.filter(“過濾器名字”, function(oldVal){ //接受一個變量的原始值
    return 根據oldVal的不同,動態返回的新值
    })
    (2). 在綁定語法中使用過濾器函數:
    {{變量 | 過濾器名 }}

  5. 結果:
    (1). 變量的原始值不會立刻顯示出來,而是先交給|後的過濾器函數
    (2). 再將過濾器處理後的返回值,返回出來,顯示在元素內容中

  6. 原理:
    (1). Vue.filter(“過濾器名”, function(oldVal){ return 新值 })
    定義一個過濾器函數,加入到Vue大家庭中備用
    (2). 當new Vue()掃描到{{}}中的|時,會回Vue大家庭中查找相應名稱的過濾器函數。
    (3). 只要找到,就先將|前的變量原始值,交給過濾器函數的oldVal參數,經過過濾器函數的加工,返回新值。顯示到當前綁定語法的位置。

  7. 示例: 過濾性別的1和0爲男和女

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <script>
    //sexFilter=function(){ ... }
    Vue.filter("sexFilter",function(oldVal){
      //性別接住的舊值可能是1或0
      return oldVal==1?"男":"女"
    })
  </script>
</head>
<body>
<div id="app">
  <h1>性別: {{sex | sexFilter}}</h1>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    sex:1
  }
})
</script>
</body>
  1. 過濾器可以加參數:
    (1). 定義過濾器時:
    Vue.filter(“過濾器名”,function(oldVal, 自定義形參, …){
    Return 新值
    })
    (2). 使用過濾器時:
    {{變量 | 過濾器名(自定義實參, …) }}
    (3). 示例: 根據參數值返回不同語言的性別
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <script>
    //sexFilter=function(){ ... }
    Vue.filter("sexFilter",function(oldVal,language){
      //性別接住的舊值可能是1或0
      //language參數可能接住cn或en,其中,默認是cn
      if(language=="en"){
        return oldVal==1?"Male":"Female"
      }else{
        return oldVal==1?"男":"女"
      }
    })
  </script>
</head>
<body>
<div id="app">
  <h1>性別: {{sex | sexFilter}}</h1>
  <h1>性別: {{sex | sexFilter("en")}}</h1>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    sex:1
  }
})
</script>
  1. 過濾器可以連用:
    (1). 綁定語法中: {{變量 | 過濾器1 |過濾器2 | … }}
    (2). 強調:
    a. 後一個過濾器2,進入的舊值,已經不是變量的原始值了,而是前一個過濾器加工後的中間值。
    b. 只有最後一個過濾器的返回值,纔會顯示到頁面上。如果希望前幾個過濾器的返回值也能一起顯示到頁面上,只能在最後一個過濾器中將新值拼接到上一步過濾器傳入的舊值上。
    (3). 示例:爲性別再額外添加圖標
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <script>
    //定義一個新的過濾器專門爲性別添加圖標
    Vue.filter("sexIcon",function(oldVal){
      //oldVal可能有六種:1  0  男  女   Male   Female
      if(oldVal==1||oldVal==0){
        return oldVal==1?"♂":"♀";
      }else{
        return oldVal=="男"||oldVal=="Male"?oldVal+"♂":oldVal+"♀";
      }
    });
    //sexFilter=function(){ ... }
    Vue.filter("sexFilter",function(oldVal,language){
      //性別接住的舊值可能是1或0
      //language參數可能接住cn或en,其中,默認是cn
      if(language=="en"){
        return oldVal==1?"Male":"Female"
      }else{
        return oldVal==1?"男":"女"
      }
    })
  </script>
</head>
<body>
<div id="app">
  <h1>性別: {{sex | sexFilter | sexIcon }}</h1>
  <h1>性別: {{sex | sexFilter("en") | sexIcon }}</h1>
  <h1>性別: {{sex | sexIcon }}</h1>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    sex:1
  }
})
</script>
</body>
axios
  1. 什麼是axios: 基於Promise的專門發送ajax請求的函數庫
  2. 爲什麼: 總結髮送ajax請求:
    (1). xhr4步/6步
    (2). 自己封裝函數,考慮不全面
    (3). jQuery中 $ .ajax: 問題,在vue中幾乎不再使用DOM操作,幾乎不用jQuery了。如果單是爲了引入$.ajax函數而引入整個jQuery庫,有點兒小題大做。
    (4). Vue官方提供了一套發送ajax請求的組件: vue-resource,後來,Vue發現哪個框架都有自己的發送ajax請求就得函數,而且都大同小異,所以,Vue認爲自己沒有必要再重新開放按一套ajax函數庫,所以vue-resource已經不再維護。
    (5). Vue官方幫我們選了一個時髦好用的ajax函數庫: axios,所以將來在框架中發送ajax請求,幾乎都用axios。
  3. 何時: 只要在Vue框架中,發送ajax請求服務器端數據,都用axios
  4. 如何:
    (1). 準備: 在項目中引入axios.js,才能引入axios函數庫
    <script src="js/axios.js"> 引入的順序和vue.js無關
    (2). 設置所有服務器端接口的公共域名部分
    axios.defaults.baseURL=“服務器端域名地址部分”
    (3). 發送get請求:
    axios.get(“url”,{ //向服務器端接口地址url發送get請求
    params:{ //攜帶參數
    參數1: 值1,
    … : …
    }
    //自動翻譯爲"?參數1=值1&參數2=值2&…"
    //}).then(function(result){
    }).then(res=>{
    res.data纔是服務器端返回的結果
    })
    (4). 發送post請求:
    axios.post(“url”, “變量1=值1&變量2=值2&…”)
    .then(res=>{
    res.data時服務器端返回的結果
    })
    (5). 示例: 使用axios向新浪雲上的接口地址發送get和post請求,傳參,並接受響應結果
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/axios.min.js">
    //axios={ 
    //  get(){ ... },
    //  post(){ ... }
    //}
  </script>
  <script src="js/qs.min.js">
    //專門就將對象語法轉爲查詢字符串語法
    //{ uname:"dingding", upwd:"123456"}
    // ↓ Qs.stringify(對象) 
    //"uname=dingding&upwd=123456"
  </script>
</head>
<body>
  <script>
  //先定義所有接口統一的服務器端域名部分
  axios.defaults.baseURL="http://xzserver.applinzi.com"
  //向服務器端請求學子商城首頁商品數組,包含6個商品對象的信息
  axios.get("/index")
      .then(res=>{
        console.log(res.data);
      })
  //想獲取5號商品的詳細信息: 要求: 攜帶一個lid參數,值爲要查詢的商品編號
  axios.get("/details",{
    params:{lid:5}
  }).then(res=>{
    console.log(res.data) //返回5號商品的詳細信息
  })
  //用用戶名dingding,密碼123456,調用登錄接口
  axios.post("/users/signin",
    //"uname=dingding&upwd=123456"
    Qs.stringify({ uname:"dingding", upwd:"123456"})
  ).then(res=>{
    console.log(res.data);
  })
  </script>
</body>
組件
  1. 什麼是: 頁面中擁有專屬的HTML、CSS、js和數據的可重用的獨立功能區域

  2. 爲什麼: 1. 重用!2. 便於大項目的分工協作!3. 松耦合!

  3. 何時: 只要頁面中出現一個功能,可能被反覆使用,或需要多人分工協作時,都用組件。

  4. 如何創建一個組件:
    在這裏插入圖片描述
    強調: 因爲HTML標籤名不區分大小寫,所組件名如果包含多個單詞,絕對不能用駝峯命名!必須用-分割多個單詞。
    比如: myCounter,錯的。因爲myCounter可能被轉換爲全大寫或全消息的標籤名,使用時會有歧義。
    my-counter,對的。不會被轉換。

  5. 如何使用組件: Vue中的組件本質就是一個可重用的自定義HTML標籤而已
    在頁面中<組件名></組件名>

  6. 原理:
    (1). Vue.component(“my-counter”,{})其實是向當前頁面的Vue大家庭中添加一個Vue組件對象。
    (2). new Vue()在頁面中掃描到一個自定義標籤時,就會回vue的大家庭中查找是否有同名的組件
    (3). 如果找到這個組件,就先複製組件的模板中的HTML片段,代替頁面中自定義標籤所在位置。
    (4). 爲本次組件的使用,臨時調用一次data()函數,返回一個本次組件使用專屬的數據對象。
    (5). 創建組件對象,監控當前組件的小區域。
    在這裏插入圖片描述
    new Vue() vs 組件
    在這裏插入圖片描述

  7. 示例: 定義修改數量的組件,並重用
    (1). 1_my-counter.js

Vue.component("my-counter",{
  //大多數屬性同new Vue()
  //1. 定義可反覆使用的組件的HTML片段模板
  template:`<div>
    <button @click="change(-1)">-</button>
    <span>{{count}}</span>
    <button @click="change(+1)">+</button>
  </div>`,
  //2. 定義data()函數,可返回一個新的數據對象
  data(){
    return {//相當於以前new Vue()中的data{}
      //因爲組件模板中需要一個變量count
      count:0
    }
  },
  //3. 之後的內容和new Vue()中就完全一樣了
  methods:{
    change(i){
      this.count+=i;
      this.count<0&&(this.count=0)
    }
  }
})

(2). 1_component.html

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <script src="1_my-counter.js">
    //Vue.component("my-counter",{ ... })
  </script>
</head>
<body>
<div id="app">
  <my-counter></my-counter>
  <my-counter></my-counter>
  <my-counter></my-counter>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    
  }
})
</script>
</body>
  1. 其實,當組件的template模板被替換到頁面上之後,new Vue()是不放心的。萬一這次新加載的組件內容中又包含內嵌的不認識的標籤和指令屬性怎麼辦?
    所以,new Vue()會重新掃描本次替換上來的模板HTML片段的內容。如果又包含不認識的指令,就再次去Vue家庭中查找指令。如果又遇到不認識的標籤就再次去Vue大家庭中查找組件
組件化開發
  1. 什麼是: 今後一個網頁都是由組件拼接而成

  2. 爲什麼: 1. 重用!2. 便於大項目的分工協作!3. 松耦合!

  3. 何時: 今後幾乎所有的項目都是組件化開發完成的

  4. 如何:
    (1). 拿到一個網頁後,先劃分網頁中的組件共有多個少,以及包含關係是什麼
    (2). 創建多個組件js文件,每個組件js文件中都創建一個組件對象
    (3). 父組件中可用子組件標籤,在父組件內部插入子組件
    (4). 所有組件的js文件,應該都引入new Vue()所在的網頁中

  5. 原理:
    (1). 網頁中new Vue()掃描到<div id="app">下不認識的標籤,就回Vue大家庭中查找是否包含該組件定義
    (2). 如果找到該組件定義,則用組件的template,替換頁面中組件標籤所在的位置
    (3). new Vue()絕不是隻掃描一次,而是每替換一次組件的模板片段,就重新掃描新加入的模板片段中,是否又包含更子級的不認識的標籤。
    (4). 只要掃描到不認識的標籤,就會繼續回Vue大家庭中,查找是否包含該組件定義。然後用組件的模板片段,代替剛纔組件中不認識的標籤出現的位置。依次類推,直到所有標籤new Vue()都認識,也就是所有標籤都變成瀏覽器原生HTML的標籤後,new Vue()才停止掃描。
    在這裏插入圖片描述

定義子組件

(1). 什麼是子組件: 規定只能在一個指定的父組件內使用的組件
(2). 爲什麼: 因爲有些組件,必須只能用在指定的父組件內,纔有意義。如果離開指定父組件,毫無意義,甚至會出錯。
(3). 何時: 今後,只要一個組件規定只能在某個父組件內使用時,都用子組件
(4). 根組件、全局組件、子組件:
a. 根組件: new Vue() ——監控整個頁面,負責掃描和綁定頁面中所有內容。一個頁面中只有一個根組件
b. 全局組件: Vue.component() —— 可用在任何位置!沒有限制!
c. 子組件: 僅限於在某個指定父元素內才能使用的組件對象。一旦出了這個規定的父組件,使用子組件就會報錯。
(5). 如何使用子組件:
a. 創建子組件對象: 2個特點:
1). 不要用Vue.component()創建,而只要創建一個普通的js對象即可!
2). 雖然是普通的js對象,但是對象中的內容要和Vue.component()中的內容格式相同。
在這裏插入圖片描述
7. 示例: 使用組件拼接todo案例的HTML界面(暫時不含功能和數據)
(1). 2_todo.js

Vue.component("todo",{
  template:`<div>
    <h1>待辦事項列表</h1>
    <todo-add></todo-add>
    <todo-list></todo-list>
  </div>`,
  components:{//規定todoAdd和todoList兩組件只能在當前父組件todo內使用
    todoAdd, todoList
//vue會自動將駝峯命名的組件對象名,翻譯爲-分割
//     ↓         ↓
//<todo-add> <todo-list>
  }//表示今後todoAdd和todoList只能在todo內使用
})

(2). 2_todo-add.js

//定義子組件不要用Vue.component(),只能創建普通對象,且對象名必須用駝峯命名
var todoAdd={
  template:`<div>
    <input type="text"/>
    <button>+</button>
  </div>`
} 

(3). 2_todo-list.js

var todoList={ 
  template:`<ul>
    <todo-item></todo-item>
    <todo-item></todo-item>
    <todo-item></todo-item>
  </ul>`,
  components:{ //規定todoItem組件只能在當前父組件todoList內使用
    todoItem 
  }
}

(4). 2_todo-item.js

//對象的變量名,必須使用駝峯命名
//且,對象的變量名,將來會被自動翻譯爲子組件的標籤名。
var todoItem={//{}內保持組件的內容格式不變。
  template:`<li>
    1 - 喫飯 <a href="javascript:;">x</a>
  </li>`
}

(5). 2_todo.html

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <!--子組件一定要在父組件之前引入!-->
  <script src="2_todo-add.js">
    //Vue.component("todo-add",{ ... })
  </script>
  <script src="2_todo-item.js">
    //Vue.component("todo-item",{ ... })
  </script>
  <script src="2_todo-list.js">
    //Vue.component("todo-list",{ ... })
  </script>
  <script src="2_todo.js">
    //Vue.component("todo",{ ... })
  </script>
</head>
<body>
<div id="app">
  <todo></todo>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    
  }
})
</script>
</body>
組件間傳遞數據

(1). 父給子:
a. 問題: Vue中父組件data中的數據成員,子組件不能直接使用!因爲Vue中每個組件的data數據都是自己專屬的!所以必須父組件主動傳給子組件才能用:
b. 第一步: 父組件中: 在子組件的開始標籤上用:將自己data中的變量綁定給子組件的一個自定義屬相上
在這裏插入圖片描述
c. 第二步: 子組件用props屬性,從父組件綁定的自定義屬性中取出父組件給的值。子組件用props屬性取出的父組件給的值,使用方法和子組件自己的data中的變量用法完全一樣:即可用於綁定語法,又可在js程序中用this.訪問。
在這裏插入圖片描述在這裏插入圖片描述
d. 升級: 爲todo案例添加數據綁定(暫時不實現刪除和添加操作)
1). 2_todo.js

Vue.component("todo",{
  template:`<div>
    <h1>待辦事項列表</h1>
    <todo-add></todo-add>
    <!--爹todo,通過:綁定語法,將自己data中的變量tasks,賦值給子組件todo-list的自定義屬性tasks中-->
    <todo-list :tasks="tasks"></todo-list>
  </div>`,
  data(){
    return {
      tasks:["喫飯","睡覺","打亮亮","學習"]
    }
  },
  components:{
    todoAdd, todoList
//vue會自動將駝峯命名的組件對象名,翻譯爲-分割
//     ↓         ↓
//<todo-add> <todo-list>
  }//表示今後todoAdd和todoList只能在todo內使用
})

2). 2_todo-list.js

var todoList={
  template:`<ul>
    <!--比如: 名爲tasks兜裏的爹的tasks數組照樣可用於當前子組件中v-for遍歷-->
     <!--如果當前todoList的子組件todoItem中繼續需要todoList中的數據,也可通過:綁定語法,由當前組件放入子組件上的自定義屬性中。可以放進多個自定義屬性中,像多個口袋一樣-->
    <!--比如:todo-item子組件主要當前組件遍歷出的任務名task和任務序號i,所以在todo-item上添加兩個自定義屬性,綁定task和i變量的值-->
    <todo-item v-for="(task,i) of tasks" :task="task" :i="i" :key="i"></todo-item>
  </ul>`,
  //孩子todoList組件,從爹給的名爲tasks的兜裏掏出爹給的tasks數組,就像使用自己的tasks數組一樣使用。
  props:["tasks"],
  components:{ 
    todoItem 
  }
}

3). 2_todo-item.js

//對象的變量名,必須使用駝峯命名
//且,對象的變量名,將來會被自動翻譯爲子組件的標籤名。
var todoItem={//{}內保持組件的內容格式不變。
  template:`<li>
    <!--從爹給的口袋裏獲得的數據,可在孩子裏用於綁定語法中,就像用自己data中的數據一樣方便-->
    {{i+1}} - {{task}} <a href="javascript:;">x</a>
  </li>`,
  //孩子todoItem組件,可從爹給的兩個口袋task和i中取出爹給的兩個值:任務名和任務序號。
  props:[ "task", "i" ]
}
SPA: Single Page Application
  1. 什麼是: 整個應用程序只有一個完整的HTML頁面,其它所謂的頁面,其實都是組件而已。所謂的切換頁面或跳轉頁面,其實都是在一個頁面內,切換不同的組件而已。
  2. 何時: 幾乎移動端的項目都用單頁面應用
  3. 爲什麼:
    在這裏插入圖片描述
  4. 如何:
    (1). 只創建一個唯一完整的HTML頁面,包含new Vue()
    (2). 創建其他"頁面"對應的組件文件
    強調: 在SPA應用當中,所謂的"頁面",其實都是包含頁面片段內容的子組件
    (3). 創建路由對象,包含路由字典:
    a. 路由器對象: 隨時監控地址欄變化,並能根據路由字典中規定,查找對應組件,替換唯一完整的HTML頁面中指定區域 的對象。
    b. 路由字典: 包含所有相對路徑及其對應的組件對象名的數組
    c. 路由器中必須包含路由字典才能正常工作
    d. 如何:
    1). 引入<script src="js/vue-router.js">
    2). 創建路由器對象和路由字典
var router=new VueRouter({
  			routes:[
    				{path:"/", component: Index},
    				{path:"/details", component: Details},
    				{path:"/products", component: Products},
    				{path:"*", component: NotFound }
  			]
		})

(4). 將之前所有組件對象,路由字典對象都引入唯一完整的HTML頁面中
a. 引入順序: vue-router.js, 子組件,父組件,路由器對象
b. 在<div id="app">中添加<router-view></router-view>爲將要到來的頁面組件片段佔位。
c. 在new Vue()中引入router對象
(5). 如果包含全局組件,比如頁頭:
a. 先創建頁頭組件js: my-header.js
Vue.component(“my-header”,{ … })
b. 再將全局組件引入唯一完成的HTML頁面中<script src="my-header.js">
c. 最後,在<div id="app">中,<router-view>外部,添加全局組件標籤

<div id="app">
		<my-header></my-header>
		<router-view></router-view>
	</div>

d. 結果: 將來切換頁面組件時,僅替換router-view部分。Router-view外部的部分保持不變,頁頭就出現在每個頁面的頂部,成了多個頁面共有的部分了。
5. 路由跳轉:
(1).HTML中寫死的:
a. 不要用<a href="xxx">
b. 必須用<router-link to="/相對路徑">文本</router-link>
(2). Js中跳轉: this.$router.push("/相對路徑")
6. 路由傳參:
(1). 在路由字典中配置路由對象,允許傳參

var router=new VueRouter({
	routes:[
		...
		{path:"/details/:lid", component: Details, props:true}
	]                參數名               將參數lid的值直接變成props中的屬性
	結果: 從此,想進入/details,必須帶參數值!不帶參數值不讓進!
})

(2)在這裏插入圖片描述
7. 示例: 實現單頁面應用
(1). 3_SPA.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <script src="js/vue-router.js"></script>
  <script src="3_index.js"></script>
  <script src="3_details.js"></script>
  <script src="3_products.js"></script>
  <script src="3_not_found.js"></script>
  <script src="3_router.js">
    //var router=new VueRouter({ ... })
  </script>
  <script src="3_my_header.js"></script>
</head>
<body>
<div id="app">
  <my-header></my-header>
  <router-view></router-view>
</div>
<script>
var vm=new Vue({
  el:"#app",
  data:{
    
  },
  //router:router
  router
})
</script>
</body>
</html>

(2). 3_index.js

var Index={
  template:`<div style="background-color:#C8BFE7">
    <h1>這裏是首頁</h1>
    <button @click="goToDetails">查看1號商品的詳情</button><br/>
    <router-link to="/details/2">查看2號商品的詳情</router-link>
  </div>`,
  methods:{
    goToDetails(){
      this.$router.push("/details/1")
    }
  }
}

(3). 3_details.js

var Details={
  template:`<div style="background-color:#FF7F27">
    <h1>這裏是詳情頁</h1>
    <h2>顯示{{lid}}號商品的詳細信息...</h2>
    <button @click="back">返回首頁</button>
  </div>`,
  props:[ "lid" ],
  methods:{
    back(){
      this.$router.push("/")
    }
  }
}

(4) 3_products.js

var Products={
  template:`<div style="background-color:#B5E61D">
    <h1>這裏是商品列表頁</h1>
  </div>`
}

(5). 3_not_found.js

var NotFound={
  template:`<h1 style="color:red">您訪問的頁面不存在!<br>404:Not Found</h1>`
}

(6). 3_router.js

var router=new VueRouter({
  routes:[
    {path:"/", component: Index},
    {path:"/details/:lid", component: Details,props:true},
    {path:"/products", component: Products},
    {path:"*", component: NotFound }
  ]
})

(7) 3_my_header.js

Vue.component("my-header",{
  template:`<div>
    <h1>這裏是頁頭</h1>
    <ul>
      <li><router-link to="/">首頁</router-link></li>
      <li><router-link to="/products">商品列表頁面</router-link></li>
    </ul>
  </div>`
})
VUE腳手架
  1. 什麼是: 已經包含核心功能的半成品項目
  2. 爲什麼:
    (1). 簡單: 已經包含了核心功能,避免了大量重複編碼
    (2). 規範: 文件夾結構和文件夾名,文件名都已標準化,降低了不同項目之間的差異。
  3. 何時: 今後幾乎所有前端項目的開發都是用腳手架完成的。
安裝生成腳手架代碼的工具 vue create
  1. 如何:
    (1). 安裝生成腳手架代碼的工具: npm i -g @vue/cli
    安裝之後: 輸入vue -V , 看到版本號就算成功
    (2). 用生成腳手架代碼的工具爲本次項目創建一套腳手架代碼結構:
    a. 在想要生成項目的目錄,地址欄裏寫cmd,打開命令行
    b. 在命令行中輸入vue create xzvue
    c. Your connection to the default npm registry seems to be slow.
    你現在的鏈接是鏈接到默認的國外的npm倉庫,看起來有些慢
    Use https://registry.npm.taobao.org for faster installation? (Y/n)
    是否使用國內的淘寶鏡像來更快速安裝 輸入Y按回車
    d. ? Please pick a preset: 請選擇一個預置的設置
    default (babel, eslint) //使用默認設置
    > Manually select features //手動選擇功能
    按方向鍵下,選第二個,手動選擇功能,按回車
    e. ? Check the features needed for your project: (Press <space> to select, <a> to toggle all
    選擇你的項目需要的功能(按空格選中或取消選中,按a全選所有)
    , <i>to invert selection)
    (*) Babel 將瀏覽器不認識的ES6甚至更高版本的js代碼,翻譯爲ES5版本的js
    ( ) TypeScript
    ( ) Progressive Web App (PWA) Support
    (*) Router VUE中實現單頁面應用的核心——路由器組件
    (*) Vuex 管理共享狀態
    ( ) CSS Pre-processors
    > ( ) Linter / Formatter Linter 是代碼質量檢查工具,會將格式不規範也報錯!一定不要選擇
    ( ) Unit Testing
    ( ) E2E Testing
    按方向鍵上下移動,移動到想要選中的功能上,按空格鍵選中Babel、Router、Vuex,取消選中Linter
    f. Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) 輸入n 按回車
    是否使用history模式作爲路由器的標識(生產環境中需要配置首頁重定向才能使用)
    Vue-router默認採用#/相對路徑 方式實現客戶端路由地址:
    比如: http://localhost:5500/index.html#/
    http://localhost:5500/index.html#/details/2
    history模式:
    http://localhost:5500/
    http://localhost:5500/details/2
    默認瀏覽器會將所有地址欄中的地址發給服務器端請求資源,只要#地址留在客戶端,不發給服務器。
    但是,http://localhost:5500/details/2 我們本來想在客戶端路由切換頁面組件,但是瀏覽器也會發送給服務器端查找,因爲服務器端沒有這個資源,所以,很可能報錯!
    所以,將來,要麼就用默認#/相對路徑方式,要麼可用histroy,必須同時請服務器端修改配置!
    g. Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
    你想把本次的配置保存在哪個位置?(用方向鍵選擇)
    In dedicated config files //把每種組件的配置,放在各自單獨的配置文件中
    > In package.json //將所有配置集中放在一個package.json文件中
    按方向鍵下,選in package.json,按回車
    h. Save this as a preset for future projects? (y/N)
    是否保存這次的配置爲將來項目的預定義配置
    輸入N,按回車
    i. 看到結果: 說明用腳手架工具生成項目源代碼結構成功

    Successfully created project xzvue.
    👉 Get started with the following commands:
    $ cd xzvue
    $ npm run serve

    j. 在windows資源管理器中,進入當前文件夾,就看到爲這次項目生成的半成品項目源代碼文件夾xzvue。
    k. 每做一個新項目,這套a-h的步驟都要重新操作一遍。
    在這裏插入圖片描述

運行 npm run serve

l. 其實剛創建完的腳手架項目源代碼包含示例網頁:
用vscode打開剛創建的腳手架項目文件夾
右鍵點擊項目中的package.json文件,選在終端/命令行中打開
在彈出的命令行窗口中,輸入npm run serve
Npm run serve: 做了兩件事:
編譯腳手架項目代碼爲瀏覽器認識的ES5代碼
同時啓動簡易開發服務器運行網站中網頁
打開瀏覽器,在地址欄中輸入: http://localhost:8080,可看到示例網頁
或在命令行窗口中,按ctrl點連接地址: http://localhost:8080,也可查看示例網頁
強調: 從此live server 退出歷史舞臺!不再使用live server運行!所有vue項目都用npm run serve運行
在這裏插入圖片描述

爲腳手架添加axios模塊

① 在腳手架生成的項目文件夾內,比如: 這裏是xzvue文件夾內
在地址欄輸入cmd,打開命令行
② 本地安裝axios模塊:
npm i -save axios
③ 在腳手架項目源代碼的src/main.js中,new Vue()前引入axios模塊
import axios from "axios" //node_modules中安裝的模塊,引入時都不用加路徑
④設置axios對象的基礎路徑屬性:
axios.defaults.baseURL="http://服務器端域名"
比如: 學子商城項目中: axios.dafaults.baseURL=“http://xzserver.applinzi.com”
⑤ 將axios對象放入Vue的原型對象中
Vue.prototype.axios=axios;
⑥結果: 因爲所有組件對象都是Vue類型的子對象,所以在所有組件內,任何位置都可用this.axios.get()和this.axios.post()訪問Vue.prototype中的axios對象裏的函數。

腳手架項目結構

(1). .git文件夾(隱藏): 本地git倉庫文件夾 配置Git倉庫忽略的一些文件(不會上傳)
(2). node_modules文件夾: 保存腳手架項目運行的依賴包。
(3). public文件夾:
a. 保存着整個單頁面應用唯一完整的index.html文件
b. 將來還可能保存所有頁面都需要的公共的css文件和js文件,比如:
bootstrap.css , jquery.js , bootstrap.js
1). 可在public文件夾下新建css和js文件夾
2). 將所有頁面都需要共用的第三方的css和js保存進去
3). 在唯一完整的index.html,頂部引入所有頁面所需的第三方的css和js
c. 將來網頁中用的所有圖片,也都應該保存在public下!
所以,拷貝舊項目中的img文件夾到public下
(4). src文件夾: 放所有我們自己編寫的HTML,css和js
a. 根組件: 原SPA中唯一完整的頁面的HTML和new Vue()不再放在一個.html文件裏,而是分爲兩個文件了:
1). App.vue 只放根組件<div id="app">的HTML內容和根組件的css樣式
2). main.js中放根組件new Vue({}) 以及對new Vue()的配置
3). 結果: 運行時,App.vue中的<div id="app">和main.js中new Vue()會重新被編譯回唯一完整的index.html頁面內。
b. 多個頁面組件:
1). 所有頁面組件都要放在src/views/文件夾下
2). 每個頁面組件都是一個.vue文件
3). 每個.vue文件中都包含三部分:

<template>
	編寫當前頁面組件的HTML片段
</template>

<script>//原頁面組件對象中所有js內容,不再含template屬性
{
	data(){  return { ... }  },
	... 
}
 </script>

<style>
 當前頁面所需的css樣式,注意組件間樣式衝突(這個組件下的子元素統一樣式類名開頭)
</style>

c. 路由器對象: src/router/index.js
同SPA中的路由器對象幾乎完全一樣

new VueRouter({
	routers:[
		{path:"/",component:Index},
		{path:"/details/:lid",component:Details, props:true},
		...
		{path:"*", component: NotFound }
	]
})

說明: router對象已經在main.js中被加入到new Vue()裏了.
d. components文件夾: 集中保存所有全局組件對象文件和子組件的文件。

腳手架和SPA應用代碼的結構
  1. 其實: 腳手架代碼的結構和SPA應用代碼的結構本質是相同的。用已知釋新知。
    (1). 回顧: SPA代碼結構:
    s
    (2). 腳手架代碼結構: 其實就是SPA的代碼結構
    在這裏插入圖片描述
es6模塊化開發在腳手架中的應用

(1). 舊SPA應用中的引入問題: 所有組件對象都要先集中引入到Index.html中,再以全局對象方式使用。如果單看某一個組件對象文件,根本看不出依賴關係。——不直觀
在這裏插入圖片描述
(2). 每個.vue文件和.js文件,默認都是一個模塊對象
(3). 每個模塊對象都可向外拋出自己內部的成員,供外部訪問
export default {
要拋出的組件對象的內容
}
(4). 一個模塊想使用另一個模塊的內容時,無需經過第三方,即可直接找到模塊文件引入其中的內容。
import 變量名 from “目標文件的相對路徑”
(5). 結果: 就可把目標文件中拋出的內容,引入自己文件內,像使用自己的變量和對象一樣,使用其他文件模塊中的內容。——減少中間商賺差價!
在這裏插入圖片描述

用VUE腳手架項目開發
  1. 在public/文件夾中添加網站所需的img文件夾和所有頁面共用的第三方css和js,並且在唯一完整的index.html頁面中,<head></head>中引入第三方的css和js
  2. 在src/views/文件夾下,創建多個頁面組件,將來程序中有幾個頁面,就創建幾個.vue文件
    每個頁面.vue文件內都包含三部分內容:
    <template>中包含這個頁面的HTML片段:
    eg:學子商城項目中: 回到舊jQuery項目中,找到對應的頁面.html文件,複製其中的<main>部分代碼到,Vue腳手架項目中的頁面.vue中的<template>
    <style>中包含這個頁面的css代碼:
    eg:學子商城項目中: 回到舊jQuery項目中,找到對應的頁面.css文件,複製其中所有css代碼到,Vue腳手架項目中的頁面.vue中的<style>
  3. 在路由器對象中,添加每個頁面的路由地址
    在src/router/index.js中:
    ① 先引入所有頁面的組件對象:
    import 頁面名 from “頁面組件的相對路徑”

    ② 再修改routes數組中的路由字典條目:
    {path:"/路徑名", component: 頁面名}
  4. 全局組件:
    ① 在src/components/文件下
    新建一個普通的組件.vue文件,同樣包含三部分
    <template>內包含組件的HTML片段
    <script>內包含組件的vue js代碼
    <style>內包含組件的css代碼
    ② 在main.js中,在new Vue()前
    a. 找到全局組件文件所在的位置,將全局組件的內容以模塊對象方式引入到main.js中備用
    import 組件名 from “./components/組件名.vue”
    比如: 想引入頁頭組件:
    import MyHeader from “./components/MyHeader”
    b. 將引入的組件對象,變成一個真正的全局組件!
    Vue.component(“標籤名”, 組件名)
    比如: 想將引入的頁頭組件對象,變成真正的全局組件
    Vue.component(“my-header”,MyHeader);
    c. 結果: 將來在任何頁面或組件的任何位置都可通過<my-header></my-header>來引入全局組件。
    比如: 想給將來所有頁面上方都加頁頭: 在App.vue中
<div id="app">
   <my-header></my-header>
   <router-view/>
</div>
  1. 子組件:

① 在src/components/文件夾下新建 子組件.vue文件
在子組件.vue文件中編寫這個子組件的內容
比如: src/components/Carousel.vue

<template>
		<script>
		<style>

② 在父組件<script>中用import引入子組件對象

<script>
   import 組件名 from "子組件路徑"
   比如: import Carsouel from "../components/ Carsouel "

③ 在父組件的<script>中的export default 中

export default {
		... ...
		components:{
			組件名
		比如: Carsouel
		}
	}

④ 結果: 在當前組件或頁面內,就可用<carsouel></carsouel>來引入子組件

  1. 所有頁面都需要的css代碼寫在App.vue中的<style>
  2. 使用axios動態獲取數據:
    (1). 在用腳手架工具生成完項目源代碼後,就要爲項目添加axios模塊(見腳手架安裝和配置部分)
    (2). 在當前組件的生命週期鉤子函數中的created()函數內,或mounted()函數內都可發送axios請求。
    (3). 在axios.get().then(res=>{})會調函數中,將服務器返回的數據(res.data),分門別類的賦值給data中的變量
    (4). 在頁面/組件的<template>中,找到需要這些變量的位置,使用前面提到的綁定語法,綁定數據到頁面指定位置
組件的生命週期

①生命週期: 一個組件的加載過程
② 回顧: 網頁加載過程: 也有生命週期
先加載HTML和JS,當HTML和JS加載完成後,提前觸發DOMContentLoaded事件,
所以,我們可以在DOMContentLoaded中編寫發送ajax請求的代碼。這樣,只要頁面加載到這個階段,事件觸發,就會自動向服務器發送請求。
然後,當所有網頁內容加載完,還會觸發一個事件: window.onload。凡是寫在window.onload事件中的代碼,都會在整個頁面加載完自動觸發執行。
③ 問題:組件不是頁面,無法觸發頁面的加載完成事件,但是,我們也想在組件加載完自動發送ajax請求!
④ 解決: 其實組件加載過程,也有生命週期的階段,每個階段也能自動觸發一個回調函數。但是因爲這個回調函數不是網頁標準的事件,所以這種特殊的回調函數,稱爲生命週期中的鉤子函數
⑤ BS 列舉: Vue組件加載過程共分四個階段,每個階段前後,都會自動觸發一個鉤子函數。共8個鉤子函數:
1.0 beforeCreate(){ ... }
1. 創建(create)階段: 創建vue對象,並創建vue中的data對象(只有data對象才能更新頁面,只要是data創建完都可以寫axios)
1.1 created(){ ... axios.get() ... }
2.0 beforeMount(){ ... }
2. 掛載(mount)階段: 掃描網頁內容,建立虛擬DOM樹,並首次更新頁面中的內容
2.1 mounted(){ ... axios.get() ... }
************** 首次加載過程到此結束*****************
3.0 beforeUpdate(){ ... }
3. 更新(update)階段: 當組件的data中的變量被修改時,自動觸發該階段
3.1 updated(){ ... }
4.0 beforeDestroy(){ ... }
4. 銷燬(destroy)階段: 主動調用專門的函數銷燬一個組件時才自動觸發該階段
4.1 destroyed(){ ... }
⑥ BS: 父組件和子組件生命週期函數的執行順序:
父組件beforeCreate()
父組件created()
父組件 beforeMount()
父組件掃描頁面內容時,發現不認識的子組件標籤,開始進入子組件的加載過程
子組件beforeCreate()
子組件created()
子組件 beforeMount()
子附件 mounted()
父組件才繼續先後掃描後續內容
父組件的mounted()

總結
  1. 元素內容需要動態改變: {{變量或js表達式}}
  2. 元素屬性值需要動態改變: :屬性名="變量或js表達式"
  3. 控制一個元素顯示隱藏: 首選 v-show=“條件”
  4. 控制兩個元素二選一顯示:
    <元素1 v-if=“條件”>
    <元素2 v-else>
  5. 控制多個元素多選一顯示:
    <元素1 v-if=“條件1”>
    <元素2 v-else-if=“條件2”>
    … …
    <元素n v-else>
  6. 反覆生成多個相同結構的HTML元素時:
    <元素 v-for="(value,i) of 數組/字符串/對象/數字" :key=“i”>
  7. 綁定HTML片段內容:
    <元素 v-html=“包含HTML內容的變量或表達式”></元素>
  8. 防止用戶短暫看到{{}}語法:
    (1). <style>[v-cloak]{display:none}</style>
    <元素 v-cloak>
    (2). <元素 v-text=“包含{{}}的js表達式”></元素>
  9. 綁定事件: <元素 @事件名=“處理函數(參數值,$event)”
  10. 只在首次加載頁面時綁定一次: v-once
  11. 阻止內容中的{{}}被vue編譯: v-pre
  12. 只要綁定表單元素的值,都用雙向綁定v-model
    <表單元素 v-model=“變量”>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章