兩萬字Vue.js基礎學習筆記

Vue.js學習筆記

目錄

Vue 的核心庫只關注視圖層,方便與第三方庫或既有項目整合。

HTML + CSS + JS : 視圖 : 給用戶看,刷新後台給的數據

網絡通信 : axios

頁面跳轉 : vue-router

狀態管理:vuex

Vue-UI : ICE , Element UI

  1. VUE 概述
    Vue (讀音/vju/, 類似於view)是一套用於構建用戶界面的漸進式框架,發佈於2014年2月。與其它大型框架不同的是,Vue被設計為可以自底向上逐層應用。Vue的核心庫只關注視圖層,不僅易於上手,還便於與第三方庫(如: vue-router: 跳轉,vue-resource: 通信,vuex:管理)或既有項目整合
  2. 前端三要素
    HTML (結構) :超文本標記語言(Hyper Text Markup Language) ,決定網頁的結構和內容
    CSS (表現) :層疊樣式表(Cascading Style sheets) ,設定網頁的表現樣式
    JavaScript (行為) :是一種弱類型腳本語言,其源代碼不需經過編譯,而是由瀏覽器解釋運行,用於控制網頁的行為
  3. JavaScript框架
    jQuery: 大家熟知的JavaScript框架,優點是簡化了DOM操作,缺點是DOM操作太頻繁,影響前端性能;在前端眼裡使用它僅僅是為了兼容IE6、7、8;

Angular: Google收購的前端框架,由一群Java程序員開發,其特點是將後台的MVC模式搬到了前端並增加了模塊化開發的理念,與微軟合作,採用TypeScript語法開發;對後台程序員友好,對前端程序員不太友好;最大的缺點是版本迭代不合理(如: 1代-> 2代,除了名字,基本就是兩個東西;截止發表博客時已推出了Angular6)

React: Facebook出品,一款高性能的JS前端框架;特點是提出了新概念[虛擬DOM]用於減少真實DOM操作,在內存中模擬DOM操作,有效的提升了前端渲染效率;缺點是使用複雜,因為需要額外學習一門[JSX] 語言;

Vue:一款漸進式JavaScript框架,所謂漸進式就是逐步實現新特性的意思,如實現模塊化開發、路由、狀態管理等新特性。其特點是綜合了Angular (模塊化)和React (虛擬DOM)的優點;

Axios :前端通信框架;因為Vue 的邊界很明確,就是為了處理DOM,所以並不具備通信能力,此時就需要額外使用一個通信框架與服務器交互;當然也可以直接選擇使用jQuery提供的AJAX通信功能;

前端三大框架:Angular、React、Vue

ES6語法

1.不一樣的變量聲明:const和let

ES6推薦使用let聲明局部變量,相比之前的var(無論聲明在何處,都會被視為聲明在函數的最頂部)
let和var聲明的區別:

var x = '全局變量';
{
  let x = '局部變量';
  console.log(x); // 局部變量
}
console.log(x); // 全局變量
<body>
  <button>按鈕1</button>
  <button>按鈕2</button>
  <button>按鈕3</button>
  <button>按鈕4</button>
  <button>按鈕5</button>
  <script>
    // 變量作用域:變量在什麼位置內是可用的
    // ES5之前因為if和for都沒有塊級作用域的概念,所以在很多時候,我們都必須藉助於function的作月域來解決應用外面變量的問題
    // 閉包可以解決這個問題: 函數是一個作用域
    // ES6中加入了let,let它是有if和for塊級作用域

    // ES5語法
    // var btns = document.getElementsByTagName('button');
    // for(var i = 0; i<btns.length; i++){
    //   (function(num){
    //     bnts[num].addEventListener('click',function(){
    //       console.log('第'+num+'個按鈕被點擊');
    //     })
    //   })(i)
    // }

    // ES6語法
    const btns = document.getElementsByTagName('button')
    for (let i = 1; i < btns.length; i++) {
      btns[i].addEventListener('click', function () {
        console.log('第' + i + '個按鈕被點擊');
      })
    }
  </script>
</body>

let表示聲明變量,而const表示聲明常量,兩者都為塊級作用域;

const 聲明的變量都會被認為是常量,意思就是它的值被設置完成後就不能再修改了,且const定義標識符時,必須賦值:

建議:在ES6開發中,優先使用const,只有需要改變某一個標識符的時候才使用let。

const a = 1
a = 0 //報錯

如果const的是一個對象,對象所包含的值(內部屬性)是可以被修改的。抽象一點兒說,就是對象所指向的地址沒有變就行:

const student = { name: 'cc' }

student.name = 'yy';// 不報錯
student  = { name: 'yy' };// 報錯

有幾個點需要注意:

  • let 關鍵詞聲明的變量不具備變量提升(hoisting)特性
  • let 和 const 聲明只在最靠近的一個塊中(花括號內)有效
  • 當使用常量 const 聲明時,請使用大寫變量,如:CAPITAL_CASING
  • const 在聲明時必須被賦值

2.模板字符串

在ES6之前,我們往往這麼處理模板字符串:
通過「\」和「+」來構建模板

$("body").html("This demonstrates the output of HTML \
content to the page, including student's\
" + name + ", " + seatNumber + ", " + sex + " and so on.");

而對ES6來說

  1. 基本的字符串格式化。將表達式嵌入字符串中進行拼接。用${}來界定;
  2. ES6反引號(“)直接搞定;
$("body").html(`This demonstrates the output of HTML content to the page, 
including student's ${name}, ${seatNumber}, ${sex} and so on.`);

3.箭頭函數(Arrow Functions)

ES6 中,箭頭函數就是函數的一種簡寫形式,使用括號包裹參數,跟隨一個 =>,緊接着是函數體;

箭頭函數最直觀的三個特點。

  • 不需要 function 關鍵字來創建函數
  • 省略 return 關鍵字
  • 繼承當前上下文的 this 關鍵字
// ES5
var add = function (a, b) {
    return a + b;
};
// 使用箭頭函數
var add = (a, b) => a + b;

// ES5
[1,2,3].map((function(x){
    return x + 1;
}).bind(this));
    
// 使用箭頭函數
[1,2,3].map(x => x + 1);

細節:當你的函數有且僅有一個參數的時候,是可以省略掉括號的。當你函數返回有且僅有一個表達式的時候可以省略{} 和 return;

4. 函數的參數默認值

在ES6之前,我們往往這樣定義參數的默認值:

// ES6之前,當未傳入參數時,text = 'default';
function printText(text) {
    text = text || 'default';
    console.log(text);
}

// ES6;
function printText(text = 'default') {
    console.log(text);
}

printText('hello'); // hello
printText();// default

5.Spread / Rest 操作符

Spread / Rest 操作符指的是 …,具體是 Spread 還是 Rest 需要看上下文語境。

當被用於迭代器中時,它是一個 Spread 操作符:

function foo(x,y,z) {
  console.log(x,y,z);
}
 
let arr = [1,2,3];
foo(...arr); // 1 2 3

當被用於函數傳參時,是一個 Rest 操作符:當被用於函數傳參時,是一個 Rest 操作符:

function foo(...args) {
  console.log(args);
}
foo( 1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]

6.二進制和八進制字面量

ES6 支持二進制和八進制的字面量,通過在數字前面添加 0o 或者0O 即可將其轉換為八進制值:

let oValue = 0o10;
console.log(oValue); // 8
 
let bValue = 0b10; // 二進制使用 `0b` 或者 `0B`
console.log(bValue); // 2

7.對象和數組解構

// 對象
const student = {
    name: 'Sam',
    age: 22,
    sex: '男'
}
// 數組
// const student = ['Sam', 22, '男'];

// ES5;
const name = student.name;
const age = student.age;
const sex = student.sex;
console.log(name + ' --- ' + age + ' --- ' + sex);

// ES6
const { name, age, sex } = student;
console.log(name + ' --- ' + age + ' --- ' + sex);

8.對象超類

ES6 允許在對象中使用 super 方法:

var parent = {
  foo() {
    console.log("Hello from the Parent");
  }
}
 
var child = {
  foo() {
    super.foo();
    console.log("Hello from the Child");
  }
}
 
Object.setPrototypeOf(child, parent);
child.foo(); // Hello from the Parent
             // Hello from the Child

9.for…of 和 for…in

for…of 用於遍歷一個迭代器,如數組:

let letters = ['a', 'b', 'c'];
letters.size = 3;
for (let letter of letters) {
  console.log(letter);
}
// 結果: a, b, c

for…in 用來遍歷對象中的屬性:

 let stus = ["Sam", "22", "男"];
 for (let stu in stus) {
   console.log(stus[stu]);
  }
// 結果: Sam, 22, 男

10.ES6中的類

ES6 中支持 class 語法,不過,ES6的class不是新的對象繼承模型,它只是原型鏈的語法糖表現形式。

函數中使用 static 關鍵詞定義構造函數的的方法和屬性:

class Student {
  constructor() {
    console.log("I'm a student.");
  }
 
  study() {
    console.log('study!');
  }
 
  static read() {
    console.log("Reading Now.");
  }
}
 
console.log(typeof Student); // function
let stu = new Student(); // "I'm a student."
stu.study(); // "study!"
stu.read(); // "Reading Now."

類中的繼承和超集:

class Phone {
  constructor() {
    console.log("I'm a phone.");
  }
}
 
class MI extends Phone {
  constructor() {
    super();
    console.log("I'm a phone designed by xiaomi");
  }
}
 
let mi8 = new MI();

extends 允許一個子類繼承父類,需要注意的是,子類的constructor 函數中需要執行 super() 函數。
當然,你也可以在子類方法中調用父類的方法,如super.parentMethodName()。
在 這裡 閱讀更多關於類的介紹。

有幾點值得注意的是:

  • 類的聲明不會提升(hoisting),如果你要使用某個 Class,那你必須在使用之前定義它,否則會拋出一個 ReferenceError 的錯誤
  • 在類中定義函數不需要使用 function 關鍵詞

1、基本認識vue

  • 輕量級,體積小是一個重要指標。Vue.js 壓縮後有隻有 20多kb (Angular 壓縮後 56kb+,React 壓縮後 44kb+)
  • 移動優先。更適合移動端,比如移動端的 Touch 事件
  • 易上手,學習曲線平穩,文檔齊全
  • 吸取了 Angular(模塊化)和 React(虛擬 DOM)的長處,並擁有自己獨特的功能,如:計算屬性
  • Vue是一個漸進式的框架可以將Vue作為你應用的一 部分嵌入其中,帶來更豐富的交互體驗。
  • 開源,社區活躍度高
  • Vue有很多特點和Web開發中常見的高級功能
    • 解耦視圖和數據.
    • 可復用的組件
    • 前端路由技術
    • 狀態管理
    • 虛擬DOM

下載地址

<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
  • NPM安裝

初識vue.js

<body>
    
    <div id="app">{{message}}</div> //執行到這裡顯然出對應的HTML

    <script src="../js/vue.js"></script>
    <script>
        //執行這裡創建Vue實例,並且對原HTML進行解析和修改。
        //編程範式:聲明式編程
        const app = new Vue({   //創建Vue實例
            el:'#app',   //用於指定要掛載的元素
            data:{       //定義數據
                message:"洛依塵!"
            }
        })
        //元素js的做法(編程範式:命令式編程)
        // 1.創建div元素,設置id屬性
        // 2.定義一個變量叫message
        // 3.將message變量放在前面的div元素中顯示
    </script>
</body>

MVVM 模式

MVVM 源自於經典的 Model–View–Controller(MVC)模式(期間還演化出了 Model-View-Presenter(MVP)模式,可忽略不計)。MVVM 的出現促進了 GUI 前端開發與後端業務邏輯的分離,極大地提高了前端開發效率。MVVM 的核心是 ViewModel 層,它就像是一個中轉站(value converter),負責轉換 Model 中的數據對象來讓數據變得更容易管理和使用,該層向上與視圖層進行雙向數據綁定,向下與 Model 層通過接口請求進行數據交互,起呈上啟下作用。

如下圖所示:

img

簡單畫了一張圖來說明 MVVM 的各個組成部分:

img

分層設計一直是軟件架構的主流設計思想之一,MVVM 也不例外。

View 層

View 是視圖層,也就是用戶界面。前端主要由 HTML 和 CSS 來構建,為了更方便地展現 ViewModel 或者 Model 層的數據,已經產生了各種各樣的前後端模板語言,比如 FreeMarker、Marko、Pug、Jinja2等等,各大 MVVM 框架如 KnockoutJS,Vue,Angular 等也都有自己用來構建用戶界面的內置模板語言。

Model 層

Model 是指數據模型,泛指後端進行的各種業務邏輯處理和數據操控,主要圍繞數據庫系統展開。後端的處理通常會非常複雜

ViewModel 層

ViewModel 是由前端開發人員組織生成和維護的視圖數據層。在這一層,前端開發者對從後端獲取的 Model 數據進行轉換處理,做二次封裝,以生成符合 View 層使用預期的視圖數據模型。需要注意的是 ViewModel 所封裝出來的數據模型包括視圖的狀態和行為兩部分,而 Model 層的數據模型是只包含狀態的,比如頁面的這一塊展示什麼,那一塊展示什麼這些都屬於視圖狀態(展示),而頁面加載進來時發生什麼,點擊這一塊發生什麼,這一塊滾動時發生什麼這些都屬於視圖行為(交互),視圖狀態和行為都封裝在了 ViewModel 里。這樣的封裝使得 ViewModel 可以完整地去描述 View 層。由於實現了雙向綁定,ViewModel 的內容會實時展現在 View 層,這是激動人心的,因為前端開發者再也不必低效又麻煩地通過操縱 DOM 去更新視圖,MVVM 框架已經把最臟最累的一塊做好了,我們開發者只需要處理和維護 ViewModel,更新數據視圖就會自動得到相應更新,真正實現數據驅動開發。看到了吧,View 層展現的不是 Model 層的數據,而是 ViewModel 的數據,由 ViewModel 負責與 Model 層交互,這就完全解耦了 View 層和 Model 層,這個解耦是至關重要的,它是前後端分離方案實施的重要一環。

image-20210508173816636

Vue實例的options

  • el:
    類型 : string | HTMLElement
    作用 : 決定之後Vue實例會管理哪一個DOM。
  • data:
    類型 : Object | Function (組件當中data必須是一個函數)
    作用 : Vue實例對應的數據對象。
  • methods:
    類型 : { [key: string]: Function }
    作用 : 定義屬於Vue的一 些方法,可以在其他地方調用,也可以在指令中使用。

vue實例的生命周期

一、解析

1、什麼是生命周期:從Vue實例創建、運行、到銷毀期間,總是伴隨着各種各樣的事件,這些事件,統稱為生命周期!

2、生命周期鉤子 = 生命周期函數 = 生命周期事件

3、主要的生命周期函數分類:

– 創建期間的生命周期函數:

+ beforeCreate:實例剛在內存中被創建出來,此時,還沒有初始化好 data 和 methods 屬性

+ created:實例已經在內存中創建OK,此時 data 和 methods 已經創建OK,此時還沒有開始 編譯模板

+ beforeMount:此時已經完成了模板的編譯,但是還沒有掛載到頁面中

+ mounted:此時,已經將編譯好的模板,掛載到了頁面指定的容器中顯示

– 運行期間的生命周期函數:

+ beforeUpdate:狀態更新之前執行此函數, 此時 data 中的狀態值是最新的,但是界面上顯示的 數據還是舊的,因為此時還沒有開始重新渲染DOM節點

+ updated:實例更新完畢之後調用此函數,此時 data 中的狀態值 和 界面上顯示的數據,都已經完成了更新,界面已經被重新渲染好了!

– 銷毀期間的生命周期函數:

+ beforeDestroy:實例銷毀之前調用。在這一步,實例仍然完全可用。

+ destroyed:Vue 實例銷毀後調用。調用後,Vue 實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷毀。

<body>
  <div id="app">
    <input type="button" value="修改msg" @click="msg='No'">
    <h3 id="h3">{{ msg }}</h3>
  </div>

  <script>
    // 創建 Vue 實例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        msg: 'ok'
      },
      methods: {
        show() {
          console.log('執行了show方法')
        }
      },
      beforeCreate() { // 這是我們遇到的第一個生命周期函數,表示實例完全被創建出來之前,會執行它
        console.log(this.msg)
        this.show()
        // 注意: 在 beforeCreate 生命周期函數執行的時候,data 和 methods 中的 數據都還沒有沒初始化
      },
      created() { // 這是遇到的第二個生命周期函數
        console.log(this.msg)
        this.show()
        //  在 created 中,data 和 methods 都已經被初始化好了!
        // 如果要調用 methods 中的方法,或者操作 data 中的數據,最早,只能在 created 中操作
      },
      beforeMount() { // 這是遇到的第3個生命周期函數,表示 模板已經在內存中編輯完成了,但是尚未把 模板渲染到 頁面中
        console.log(document.getElementById('h3').innerText)
        // 在 beforeMount 執行的時候,頁面中的元素,還沒有被真正替換過來,只是之前寫的一些模板字符串
      },
      mounted() { // 這是遇到的第4個生命周期函數,表示,內存中的模板,已經真實的掛載到了頁面中,用戶已經可以看到渲染好的頁面了
        console.log(document.getElementById('h3').innerText)
        // 注意: mounted 是 實例創建期間的最後一個生命周期函數,當執行完 mounted 就表示,實例已經被完全創建好了,此時,如果沒有其它操作的話,這個實例,就靜靜的 躺在我們的內存中,一動不動
      },


      // 接下來的是運行中的兩個事件
      beforeUpdate() { // 這時候,表示 我們的界面還沒有被更新【數據被更新了嗎?  數據肯定被更新了】
        console.log('界面上元素的內容:' + document.getElementById('h3').innerText)
        console.log('data 中的 msg 數據是:' + this.msg) 
        // 得出結論: 當執行 beforeUpdate 的時候,頁面中的顯示的數據,還是舊的,此時 data 數據是最新的,頁面尚未和 最新的數據保持同步
      },
      updated() {
        console.log('界面上元素的內容:' + document.getElementById('h3').innerText)
        console.log('data 中的 msg 數據是:' + this.msg)
        // updated 事件執行的時候,頁面和 data 數據已經保持同步了,都是最新的
      }
    });
  </script>
</body>

img

2、基本語法

1、插值操作

Mustache語法

<div id="app">
	<h2>{{massage}}</h2>
  <h2>{{massage}},洛依塵</h2>
  <h2>{{firstName+lastName}}</h2>
  <h2>{{firstName+' '+lastName}}</h2>
  <h2>{{counter*2}}</h2>
</div>

<body>
	<script src="../js/vue.js"></script>
	<script>
		const app = new Vue({
			el: "#app",
			data: {
				massage: "你好",
        firstName:"kobe",
        lastName:"bryant",
        counter:200
			}
		})
	</script>
</body>

v-once指令

該指令後面不需要跟任何表達式(比如之前的v-for後面是由跟表達式的

該指令表示元素和組件(組件後面才會學習)只渲染- 次,不會隨着數據的改變而改變

<h2 v-once>{{massage}}</h2>

v-html

該指令後面往往會跟上一個string類型
會將string的html解析出來並且進行渲染

	<h2>{{url}}</h2>  <!-- <a href="www.baidu.com">百度一下</a> -->
	<h2 v-html="url"></h2>  <!-- 百度一下 -->

v-text

都是用於將數據顯示在界面中,但是通常只接受一個string類型

	<h2 v-text="massage">,洛依塵</h2>	<!--你好-->

v-pre

用於跳過這個元素和它子元素的編譯過程,用於顯示原本的Mustache語法

	<h2 v-pre>{{massage}}</h2> <!-- {{massage}} -->

v-cloak

在某些情況下,f防止瀏覽器可能會直接顯然出未編譯的Mustache標籤。

<style>
		[v-cloak] {
			display: none;
		}
	</style>
</head>

<div id="app" v-cloak>
	<h2>{{massage}}</h2>
	<h2 v-once>{{massage}}</h2>
	<h2>{{url}}</h2> <!-- <a href="www.baidu.com">百度一下</a> -->
	<h2 v-html="url"></h2> <!-- 	百度一下 -->
	<h2 v-text="massage">,洛依塵</h2>
	<!--你好-->
	<h2 v-pre>{{massage}}</h2> <!-- {{massage}} -->
</div>

<body>
	<script src="../js/vue.js"></script>
	<script>
		//在vue解析之前,div里有一個屬性c-cloak
		//在vue解析之後,div里沒有一個屬性c-cloak
		const app = new Vue({
			el: "#app",
			data: {
				massage: "你好",
				url: '<a href="www.baidu.com">百度一下</a>'
			}
		})
	</script>
</body>

2、綁定屬性

v-bind

v-bind用於綁定一個或多個屬性值,或者向另-一個組件傳遞props值

<div id="app">
    
  <!-- <img src="{{imgURL}}" alt=""> -->
  <!--  錯誤的用法:這裡不可以使用mustache語法-->
    
  <!-- 正確用法:使用v-band指令 -->
  <a v-bind:href="aHref">百度</a>
  <img v-bind:src='imgURL' alt="" >

  <!-- 語法糖的寫法 -->
  <a :href="aHref">百度</a>
  <img :src='imgURL' alt="" >
    
</div>

<body>
	<script src="../js/vue.js"></script>
	<script>
		const app = new Vue({
			el: "#app",
			data: {
				massage: "你好",
        imgURL:'//www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png',
        aHref:"//www.baidu.com/"
			}
		})
	</script>
</body>

v-bind 動態綁定class(對象語法)

  • 可以通過{ }綁定一個類
  • 也可以通過判斷,傳入多個值
  • 和普通類同時存在,並不衝突
  • 如果過於複雜,可以放在一個methods或者computed中
<style>
    .active{
      color: red;
    }
  </style>
</head>

<div id="app">
	<!-- <h2 class="active">{{massage}}</h2>
  <h2 :class="active">{{massage}}</h2> -->
  
  <!-- <h2 :class="{類名1:ture,類名2:boolean}">{{massage}}</h2> 對class對象進行選擇-->
  <h2 v-bind:class="{active: isActive , line: isLine}">{{massage}}</h2>   
  <!-- 將內容放在一個methods里,並調用 -->
  <h2 v-bind:class="getClasses()">{{massage}}</h2>   
  <button v-on:click="bntClick">按鈕</button>
  <!-- 監聽按鈕,使用bntClick方法 -->
</div>

<body>
	<script src="../js/vue.js"></script>
	<script>
		const app = new Vue({
			el: "#app",
			data: {
				massage: "你好",
        active:'active',
        isActive:true,  //設置boolean值決定是否啟用
        isLine:true
			},
      methods:{
        bntClick: function(){
          this.isActive=!this.isActive
        },
        getClasses:function(){
          return {active: this.isActive, line: this.isLine}
        }
      }
		})
	</script>
</body>

v-bind 動態綁定class(數組語法)

<div id="app">
  <!-- 如果在[]數組裡的元素加了引號,代表他是一個字符串,而不是引用一個變量 -->
  <h2 :class="[active,line]">{{massage}}</h2>
  <h2 :class="['active','line']">{{massage}}</h2>
  <h2 :class="getClasses()">{{massage}}</h2>
</div>

<body>
	<script src="../js/vue.js"></script>
	<script>
		const app = new Vue({
			el: "#app",
			data: {
				massage: "你好",
        active:"aaa",
        line:"bbb"
			},
      methods:{
        getClasses:function(){
          return [this.active,this.line]
        }
      }
		})
	</script>
</body>

小作業:點擊列表中的哪一項, 那麼該項的文字變成紅色

<style>
		.active {
			color: red;
		}
	</style>
</head>

<div id="app">
	<ul>
		<li v-for="(item, index) in movies" :class="{active: currentIndex === index}" @click="liClick(index)">
            <!-- {active: currentIndex === index}當currentIndex === index為true時,改變顏色  -->
			{{index}}.{{item}}
		</li>
	</ul>
</div>

<body>
	<script src="../js/vue.js"></script>
	<script>
		const app = new Vue({
			el: "#app",
			data: {
				movies: ['海王', '火影忍者', '進擊的巨人', '死神'],
				currentIndex: 0
			},
			methods: {
				liClick(index) {
					this.currentIndex = index
				}
			}
		})
	</script>
</body>

v-bind 綁定style

<div id="app">	<!-- <h2 :style="{key(屬性名):value(屬性值)}">{{massage}}</h2> -->  <!-- 這裡要加' '要不然vue會去解析50px這個變量然後報錯 -->  <h2 :style="{fontSize: '50px'}">{{massage}}</h2>    <!-- finalSize當成一個變量在使用 -->  <h2 :style="{fontSize: finalSize}">{{massage}}</h2>  <!-- 也可以拼接 -->  <h2 :style="{fontSize: finalSize + 'px',color:finalColor}">{{massage}}</h2>  <!-- 數組語法 -->  <h2 :style="[baseStyle,baseStyle1]">{{massage}}</h2></div><body>	<script src="../js/vue.js"></script>	<script>		const app = new Vue({			el: "#app",			data: {				massage: "你好",        finalSize: 100,        finalColor: 'red',        baseStyle:{color:'red'},        baseStyle1:{fontSize:'75px'}			}		})	</script></body>

3、計算屬性

一、什麼是計算屬性

  模板內的表達式非常便利,但是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板過重且難以維護。

二、計算屬性的用法

  在一個計算屬性里可以完成各種複雜的邏輯,包括運算、函數調用等,只要最終返回一個結果就可以。

<div id="app">	<h2>{{firstName+' '+lastName}}</h2>  <h2>{{fullName}}</h2>  <h2>總價格:{{totalPrice}}</h2></div><body>	<script src="../js/vue.js"></script>	<script>		const app = new Vue({			el: "#app",			data: {				firstName:"luo",        lastName:"yichen",        books:[          {id:100, name: 'java核心技術' , price:100},          {id:101, name: 'c核心技術' , price:100},          {id:102, name: 'php核心技術' , price:100},          {id:103, name: 'python核心技術' , price:100}        ]			},      // computed: 計算屬性()      computed:{        fullName:function(){          return this.firstName+' '+this.lastName        },        totalPrice:function(){          let result =0          for (let i=0;i < this.books.length; i++){            result += this.books[i].price          }          return result;        }      }		})	</script>

計算屬性的getter和setter

每個計算屬性都包含一個getter和一個setter

  • 在上面的例子中,我們只是使用getter來讀取。
  • 在某些情況下,你也可以提供一個setter方法 (不常用)。
  • 在需要寫setter的時候,代碼如下: .
<div id="app">  <h2>{{fullName}}</h2></div><body>  <script src="../js/vue.js"></script>  <script>    const app = new Vue({      el: "#app",      data: {        firstName: "luo",        lastName: "yichen",      },      computed: {        // fullName: function () {        //   return this.firstName + ' ' + this.lastName        // }        // 計算屬性一般沒有set值,只讀屬性。        fullName:{          set: function(newValue){            const names = newValue.split(" ");            this.firstName = names[0];            this.lastName = names[1];          },           get: function(){            return this.firstName + ' ' + this.lastName          }        },        // 簡潔寫法        // fullName:function(){        //   return this.firstName+' '+this.lastName        // }      }    })  </script></body>

計算屬性與methods對比

<div id="app">  <!-- 通過拼接:語法過於繁瑣 -->  <h2>{{firstName}} {{lastName}}</h2>  <!-- 通過定義methods 每次都要調用-->  <h2>{{getFullName()}}</h2>  <!-- 通過computed 如果沒有發生改變只需要調用一次-->  <h2>{{fullName}}</h2></div><body>  <script src="../js/vue.js"></script>  <script>    const app = new Vue({      el: "#app",      data: {        firstName: "luo",        lastName: "yichen"      },      methods: {        getFullName: function () {          return this.firstName + ' ' + this.lastName        }      },      computed: {        fullName: function () {          return this.firstName + ' ' + this.lastName        }      }    })  </script></body>

4、事件監聽

可以用 v-on 指令監聽 DOM 事件,並在觸發時運行一些 JavaScript 代碼。

獲取到瀏覽器參數的event對象: $event

<div id="app">	<h2>{{counter}}</h2>  <button v-on:click="increment">+</button>  <button v-on:click="decrement">-</button>  <!-- 語法糖 當沒參數時()可以不用寫 -->  <button @click="increment">+</button>  <button @click="decrement">-</button>  <!-- 事件調用時沒有參數 -->  <button @click="bnt1Click()">按鈕1</button>  <button @click="bnt1Click">按鈕1</button>  <!-- 在事件定義前,寫函數省略了小括號,但是方法本身需要一個參數,這個時候  Vue會將瀏覽器默認生成的event事件對象作為參數傳入方法中 -->  <button @click="bnt2Click(123)">按鈕2</button>   <button @click="bnt2Click()">按鈕2</button>  <button @click="bnt2Click">按鈕2</button>  <!-- 定義方法時,我們需要event對象,同時又需要其他參數 -->  <!-- 在調用方式時,如何手動的獲取到瀏覽器參數的event對象: $event -->  <button @click="bnt3Click('abc',$event)">按鈕3</button></div><body>	<script src="../js/vue.js"></script>	<script>		const app = new Vue({			el: "#app",			data: {				counter: 0			},      methods:{        increment(){          this.counter++        },        decrement(){          this.counter--        },        bnt1Click(){          console.log("bnt1Click");        },        bnt2Click(abc){          console.log("--------------",abc);        },        bnt3Click(abc,event){          console.log("++++++++++++++", abc,event);        }      }		})    // 如果我們函數需要參數,但是沒有傳入參數,那麼函數的形參為undefined    function abc(name){      console.log(name);    }    abc()	</script></body>

v-on的修飾符

  1. .stop修飾符的使用

    當多對標籤進行重疊的時候, 你點擊最上面的一層標籤的時候, 會自動的冒泡到他下面的所有標籤上面
    .stop就是阻止冒泡使用的

  2. .prevent修飾符的使用

    form表單提交時候或者在點擊a標籤的時候, 會阻止提交或跳轉

  3. .keyup監聽某個鍵盤的鍵帽

    監聽某個鍵盤的鍵位

  4. .once修飾符的使用

    綁定後只會觸發一次

<div id="app">  <!-- 1. .stop -->  <div @click='divClick'>    aaaaa    <button @click.stop='bntClick'>按鈕</button>  </div>  <!-- 2. .prevent  -->  <form action="baidu">    <input type="submit" value="提交" @click.prevent='submitClick'>  </form>  <!-- 3. 監聽某個鍵盤的鍵位 -->  <input type="text" @keyup.enter="keyUp">  <!-- 4. once修飾符的使用 -->  <button @click.once='bnt2Click'>按鈕</button></div><body>  <script src="../js/vue.js"></script>  <script>    const app = new Vue({      el: "#app",      data: {        massage: "你好"      },      methods: {        bntClick() {          console.log('bnt')        },        divClick() {          console.log('div')        },        submitClick() {          console.log("submitClick")        },        keyUp() {          console.log("up")        },        bnt2Click() {          console.log('bnt2');        }      }    })  </script></body>

5、條件判斷

v-if的原理:
v-if後面的條件為false時,對應的元素以及其子元素不會渲染。
也就是根本沒有不會有對應的標籤出現在DOM中。

<div id="app">  <h2 v-if="score>90">優秀</h2>  <h2 v-else-if="score>80">良好</h2>  <h2 v-else-if="score>60">及格</h2>  <h2 v-else>不及格</h2>  <h1>{{result}}</h1>  <h2 v-if="isShow">{{massage}}</h2>  <h1 v-else>當isShow為false時顯示我</h1></div><body>  <script src="../js/vue.js"></script>  <script>    const app = new Vue({      el: "#app",      data: {        massage: "你好",        isShow: true,        score: 99      },      computed: {        result() {          let showMessage = '';          if (this.score >= 90) {            showMessage = "優秀"          } else if (this.score >= 80) {            showMessage = "良好"          }          // ...          return showMessage        }      }    })  </script></body>

用戶切換的小案例

<div id="app">	<span v-if="isUser">    <label for="username">用戶賬號</label>    <input type="text" id="username" placeholder="用戶賬號" key='username'>  </span>  <span v-else>    <label for="emailname">用戶郵箱</label>    <input type="text" id="emailname" placeholder="用戶郵箱" key='emailname'>  </span>  <button @click="isUser = !isUser">切換類型</button></div><body>	<script src="../js/vue.js"></script>	<script>		const app = new Vue({			el: "#app",			data: {				isUser:true			}		})	</script></body>

小問題:

  • 如果我們在有輸入內容的情況下,切換了類型,我們會發現文字依然顯示之前的輸入的內容。
  • 但是按道理講,我們應該切換到另外一個input元素中了。
  • 在另一個input元素中,我們並沒有輸入內容。
  • 為什麼會出現這個問題呢?

問題解答:

  • 這是因為Vue在進行DOM渲染時,出於性能考慮,會儘可能的復用已經存在的元素,而不是重新創建新的元素。
  • 在上面的案例中, Vue內部會發現原來的input元素不再使用,直接作為else中的input來使用了.

解決方案:

  • 如果我們不希望Vue出現類似重複利用的問題,可以給對應的input添加key
  • 並且我們需要保證key的不同

Virtual DOM 是什麼?

img

Virtual DOM 其實就是一棵以 JavaScript 對象( VNode 節點)作為基礎的樹,用對象屬性來描述節點,實際上它只是一層對真實 DOM 的抽象。最終可以通過一系列操作使這棵樹映射到真實環境上。

簡單來說,可以把Virtual DOM 理解為一個簡單的JS對象,並且最少包含標籤名( tag)、屬性(attrs)和子元素對象( children)三個屬性。不同的框架對這三個屬性的命名會有點差別。

對於虛擬DOM,咱們來看一個簡單的實例,就是下圖所示的這個,詳細的闡述了模板 → 渲染函數 → 虛擬DOM樹 → 真實DOM的一個過程

img

Virtual DOM 作用是什麼?

虛擬DOM的最終目標是將虛擬節點渲染到視圖上。但是如果直接使用虛擬節點覆蓋舊節點的話,會有很多不必要的DOM操作。例如,一個ul標籤下很多個li標籤,其中只有一個li有變化,這種情況下如果使用新的ul去替代舊的ul,因為這些不必要的DOM操作而造成了性能上的浪費。

為了避免不必要的DOM操作,虛擬DOM在虛擬節點映射到視圖的過程中,將虛擬節點與上一次渲染視圖所使用的舊虛擬節點(oldVnode)做對比,找出真正需要更新的節點來進行DOM操作,從而避免操作其他無需改動的DOM。

其實虛擬DOM在Vue.js主要做了兩件事:

  • 提供與真實DOM節點所對應的虛擬節點vnode
  • 將虛擬節點vnode和舊虛擬節點oldVnode進行對比,然後更新視圖

v-if和v-show的區別

v-show控制的是節點的display屬性 v-if是將節點刪除了 如果節點需要頻繁顯示隱藏 使用v-show性能更佳!

<div id="app">  <!-- v-if: 當條件為false時,包含v-if指令的元素,根本就不會存在dom中 -->  <h2 v-if='isShow' id="aaa">{{massage}}</h2>  <!-- V- show:當條件為false時,v-show只是給我們的元素添加一個行內樣式: display: none -->  <h2 v-show='isShow' id="bbb">{{massage}}</h2></div><body>  <script src="../js/vue.js"></script>  <script>    const app = new Vue({      el: "#app",      data: {        massage: "你好",        isShow: true      }    })  </script></body>

6、循環遍歷

當我們有一組數據需 要進行渲染時,我們就可以使用v-for來完成。
v-for的語法類似於JavaScript中的for循環。
格式如下: item in items的形式。

<div id="app">  <!-- 1.在遍歷的過程中,沒有使用索引值(下標值) -->  <ul>    <li v-for='item in names'>{{item}}</li>  </ul>  <!-- 2.在遍歷過程中,獲取索引值 -->  <ul>    <li v-for='(item,index) in names'>      {{index+1}}.{{item}}    </li>  </ul>  <!-- 1.在遍歷對象的過程中,如果只是獲取一個值,那麼獲取到的是value -->  <ul>    <li v-for="item in info">{{item}}</li>  </ul>  <!-- 2., 獲取key和value 格式(value,key) -->  <ul>    <li v-for="(value,key) in info">{{value}}-{{key}}</li>  </ul>  <!-- 3. 獲取key和value和index 格式(value,key,index)-->  <ul>    <li v-for="(value,key,index) in info">{{value}}-{{key}}-{{index}}</li>  </ul></div><body>  <script src="../js/vue.js"></script>  <script>    const app = new Vue({      el: "#app",      data: {        names: ['why', 'who', 'what', 'where']	//遍曆數組      },      info: {									//遍歷對象        name: 'lyc',        age: 18,        height: 1.88      }    })  </script></body>

v-for加key屬性

  • 為什麼需要這個key屬性呢(了解) ?

    • 這個其實和Vue的虛擬DOM的Diff算法有關係。
    • 這裡我們借用React’ S diff algorithm中的一張圖來簡單說明一下:
  • 當某一層有很多相同的節點時,也就是列表節點時,我們希望插入一一個新
    的節點

    • 我們希望可以在B和C之間加一一個F , Diff算法默認執行起來是這樣的。
    • 即把C更新成F , D更新成C , E更新成D ,最後再插入E ,是不是很沒有
      效率?
  • 所以我們需要使用key來給每個節點做一個唯一標識

    • Diff算法就可以正確的識別此節點

    • 0找到正確的位置區插入新的節點。

所以一句話, key的作用主要是為了高效的更新虛擬DOM。

image-20210517163534869

image-20210517163043021

哪些數組方法是響應式的

<body>  <script src="../js/vue.js"></script>  <script>    const app = new Vue({      el: "#app",      data: {        letters: ['A', 'C', 'B', 'D', 'E']      },      methods: {        btnClick() {          // 1.push()   在數組最後添加元素          this.letters.push('aaa','bbb')          // 2.pop()      在數組最後刪除一個元素          this.letters.pop();          // 3.shift()   刪除在數組第一個元素          this.letters.shift();          // 4.unshift() 在數組最前面添加元素          this.letters.unshift('ddd','ddd');          // 5.splice()   刪除/插入/替換元素          // 刪除元素: 第一參數傳入你從第幾個元素開始刪除,第二參數傳入你要刪除的幾個元素(如果沒有傳,就刪除後面所有元素)          // 插入元素: 第二個傳入0,後面跟上要添加的值          // 替換元素: 第二參數傳入你要刪除元素,後面追加你要寫入的元素完成替換          this.letters.splice(1,3,'m','n','l')          // 6.sort()     排序          this.letters.sort()          // 7.reverse()  反轉          this.letters.reverse()          // 注意:通過索引值直接來修改數組中的元素 不是響應式          // this.letters[0]='bbbbbbbbbbbb'          // set(要修改的對象,索引值,修改後的值)          Vue.set(this.letters,0,'bbbbbb')        }      }    })    // 擴展知識:可變參數    // function sum(...sum){    //   console.log(sum);    // }    // sum(11,223,44,56,77,889,9,1)  </script></body>

6、表單綁定

  • 表單控件在實際開發中是非常常見的。特別是對於用戶信息的提交,需要大量的表單。
  • Vue中使用v-model指令來實現表單元素和數據的雙向綁定。
<body>  <div id="app">    <input type="text" v-model="massage">    {{massage}}  </div>  <script src="../js/vue.js"></script>  <script>    const app = new Vue({      el: "#app",      data: {        massage: "你好"      }    })  </script></body>
  • 當我們在輸入框輸入內容時
  • 因為input中的v-model綁定了message ,所以會實時將輸入的內容傳遞給message , message發生改變。
  • 當message發生改變時,因為上面我們使用Mustache語法,將message的值插入到DOM中,所以DOM會發生響應的改變。
  • 所以,通過v-model實現了雙向的綁定。

原理:

  <div id="app">    <!-- <input type="text" v-model="massage"> -->    <!-- <input type="text" :value="massage" @input="valueChange"> -->    <input type="text" :value="massage" @input="massage = $event.target.value">    {{massage}}  </div>  <script src="../js/vue.js"></script>  <script>    const app = new Vue({      el: "#app",      data: {        massage: "你好"      },      methods: {        valueChange(event) {          this.massage = event.target.value;        }      }    })  </script></body>

v-model結合radio類型

<div id="app">  <label for="">    <input type="radio" id="male" value="男" v-model="sex">男  </label>  <label for="">    <input type="radio" id="female" value="女" v-model="sex">女  </label>  <h2>{{sex}}</h2></div><body>  <script src="../js/vue.js"></script>  <script>    const app = new Vue({      el: "#app",      data: {        message: "你好",        sex:'男'      }    })  </script></body>

v-model結合checkbox類型

單個勾選框: .

  • v-modelI即為布爾值。
  • 此時input的value並不影響v-model的值。

多個複選框:

  • 多個複選框時,因為可以選中多個,所以對應的data中屬性是一個數組。
  • 當選中某一個時 ,就會將input的value添加到數組中。
<div id="app">  <!-- 單選框 -->  <!-- <label for="agree">    <input type="checkbox" id="agree" v-model="isAgree">同意協議  </label>  <h2>{{isAgree}}</h2>  <button :disabled="!isAgree">下一步</button> -->  <!-- 多選框 -->    <!-- <input type="checkbox" value="籃球" v-model="hobbies">籃球    <input type="checkbox" value="足球" v-model="hobbies">足球    <input type="checkbox" value="排球" v-model="hobbies">排球    <input type="checkbox" value="手球" v-model="hobbies">手球    <h2>{{hobbies}}</h2> -->    <label v-for="item in originHobbies" :for="item">      <input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}    </label>  </div><body>  <script src="../js/vue.js"></script>  <script>    const app = new Vue({      el: "#app",      data: {        message: "你好",        isAgree: false,//單選框        hobbies:[],//多選框         originHobbies:['籃球','足球','乒乓球','桌球','高爾夫球']          //也可以通過值綁定來從服務器獲取值      }    })  </script></body>

v-model結合select

單選:只能選中一個值。

  • v-model綁定的是一個值。
  • 當我們選中option中的一個時,會將它對應的value賦值到mySelect中

多選:可以選中多個值。

  • v-model綁定的是一個數組。
  • 當選中多個值時,就會將選中的option對應的value添加到數組mySelects中
<body>  <div id="app">    <!-- 1、選擇一個 -->    <select name="abc" id=""  v-model="fruit">      <option value="蘋果">蘋果</option>      <option value="香蕉">香蕉</option>      <option value="榴槤">榴槤</option>      <option value="西瓜">西瓜</option>    </select>    <h2>{{fruit}}</h2>    <!-- 2、選擇多個 -->    <select name="abc" id=""  v-model="fruits" multiple>      <option value="蘋果">蘋果</option>      <option value="香蕉">香蕉</option>      <option value="榴槤">榴槤</option>      <option value="西瓜">西瓜</option>    </select>    <h2>{{fruits}}</h2>  </div>    <script src="../js/vue.js"></script>    <script>      const app = new Vue({        el: "#app",         data: {          message: "你好",          fruit:"香蕉",          fruits:[]        }      })    </script></body>

v-model的修飾符

lazy修飾符:

  • 默認情況下, v- model默認是在input事件中同步輸入框的數據的。
  • 也就是說, 一旦有數據發生改變對應的data中的數據就會自動發生
    改變。
  • lazy修飾符可以讓數據在失去焦點或者回車時才會更新:

number修飾符:

  • 默認情況下,在輸入框中無論我們輸入的是字母還是數字,都會被
    當做字符串類型進行處理。
  • 但是如果我們希望處理的是數字類型,那麼最好直接將內容當做數
    字處理。
  • number修飾符可以讓在輸入框中輸入的內容自動轉成數字類型:

trim修飾符:

  • 如果輸入的內容首尾有很多空格,通常我們希望將其去除
  • trim修飾符可以過濾內容左右兩邊的空格
<div id="app">  <!-- 1.修飾符:lazy -->  <input type="text" v-model.lazy="message">  <h2>{{message}}</h2>  <!-- 2.修飾符:number -->  <input type="number" v-model.number="age">  <h2>{{typeof age}}</h2>  <!-- 3.修飾符:trim -->  <input type="text" v-model.trim="name">  <h2>{{name}}</h2></div><body>   <script src="../js/vue.js"></script>  <script>    const app = new Vue({      el: "#app",      data: {        message: "你好",        age:12,        name:''      }    })  </script></body>

綜合-書籍購物車案例

  • HTML
<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta http-equiv="X-UA-Compatible" content="IE=edge">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>Document</title>  <link rel="stylesheet" href="./style.css"></head><body>  <div id="app">    <div v-if="books.length">      <table>        <thead>          <tr>            <th></th>            <th>書籍名稱</th>            <th> 出版日期</th>            <th> 價格</th>            <th> 購買數量</th>            <th> 操作</th>          </tr>        </thead>        <tbody>          <tr v-for="(item,index) in books">            <td>{{item.id}}</td>            <td>{{item.name}}</td>            <td>{{item.date}}</td>            <td>{{item.price | showPrice}}</td>            <td>              <button @click="decrement(index)" v-bind:disabled="item.count <= 1">-</button>              {{item.count}}              <button @click="increment(index)">+</button>            </td>            <td><button @click="removeHandler(index)">移除</button></td>          </tr>        </tbody>      </table>      <h2>        總價格: {{totalPrice | showPrice}}      </h2>    </div>    <div v-else>      <h1>購物車為空</h1>    </div>  </div>  <script src="../js/vue.js"></script>  <script src="./main.js"></script></body></html>
  • CSS
table{  border: 1px solid #000;  border-collapse: collapse;  border-spacing: 0;}th,td{  padding: 8px 16px;  border: 1px solid #000;  text-align: left;}th{  background-color: #f7f7f7;  color: #5c6b77;  font-weight: 600;}
  • JS
const app = new Vue({  el: "#app",  data: {    books: [{        id: 1,        name: '《算法導論》',        date: "2006-9",        price: 85.00,        count: 1      },      {        id: 2,        name: '《算法導論》',        date: "2006-9",        price: 85.00,        count: 1      },      {        id: 3,        name: '《算法導論》',        date: "2006-9",        price: 85.00,        count: 1      },      {        id: 4,        name: '《算法導論》',        date: "2006-9",        price: 85.00,        count: 1      }    ]  },  methods: {    // getFinalPrice(price){    //   return '¥'+price.toFixed(2) //toFixed(2)保留兩位小數    // }    increment(index) {      this.books[index].count++    },    decrement(index) {      this.books[index].count--    },    removeHandler(index) {      this.books.splice(index, 1)    }  },  filters: { //過濾器    showPrice(price) {      return '¥' + price.toFixed(2)    }  },  computed: {    totalPrice() {      let totalPrice = 0;      for (let i = 0; i < this.books.length; i++) {        totalPrice += this.books[i].price * this.books[i].count;      }      return totalPrice;    }  }})

JS高階函數

編程範式:命令式編程/聲明式編程

編程範式:面向對象編程(第一公民:對象)/函數式編程(第一公民:函數)

filter/map/reduce

filter中的回調函數有一個要求:必須返回一個 boolean值

true:當返回true時,函數內部會自動將這次回調的n加入到新的數組中

false:當返回false時,函數內部會過濾掉這次的n

  • 基本寫法
const nums = [10,20,111,222,444,40,50]// 1.需求:取出小於100的數字let newNums = []for(let n of nums){  if(n > 100){    newNums.push(n)  }}// 2.需求:將所有小於160的數字進行轉化:全部*2let new2Nums = []for(let n of newNums){  new2Nums.push(n*2)}// 3.需求:將所有new2Nums數字相加,得到最終的記過let total = 0for(let n of new2Nums){  total +=n}console.log(total)
  • 高階寫法
const nums = [10,20,111,222,444,40,50]// 1.filter函數的使用let newNums = nums.filter(function(n){  return n<100})console.log(newNums)// 2.map函數的使用let new2Nums = newNums.map(function(n){  return n*2})console.log(new2Nums)// 3.reduce函數的使用// reduce作用對數組中所有的內容進行匯總let total = new2Nums.reduce(function(preValue,n){  return preValue + n;},0)console.log(total)// 第一次: revalue 0  n 20// 第二次: revalue 20 n 40// 第二次: revalue 60 n 80// 第二次: revalue 140 n 100// 240
  • 高階綜合寫法
const nums = [10,20,111,222,444,40,50]// 綜合let total = nums.filter(function(n){  return n<100}).map(function(n){  return n*2}).reduce(function(preValue,n){  return preValue + n;},0) //初始化console.log(total)//使用箭頭函數進一步簡化let total = nums.filter(n => n<100).map(n => n*2).reduce((pre,n) => pre+n)console.log(total)

3、組件化開發

一、什麼是組件化開發

人面對複雜問題的處理方式:
任何一個人處理信息的邏輯能力都是有限的
所以,當面對一個非常複雜的問題時,我們不太可能一次性搞定一大堆的內容。
但是,我們人有一種天生的能力,就是將問題進行拆解。
如果將一個複雜的問題,拆分成很多個可以處理的小問題,再將其放在整體當中,你會發現大的問題也會迎刃而解。
組件化也是類似的思想:
如果我們將一個頁面中所有的處理邏輯全部放在一起,處理起來就會變得非常複雜,而且不利於後續的管理以及擴展。
但如果,我們講一個頁面拆分成一個個小的功能塊,每個功能塊完成屬於自己這部分獨立的功能,那麼之後整個頁面的管理和維護就變得非常容易了。

img

img

我們將一個完整的頁面分成很多個組件。
每個組件都用於實現頁面的一個功能塊。
而每一個組件又可以進行細分。

二、Vue組件化思想

組件化是Vue.js中的重要思想
它提供了一種抽象,讓我們可以開發出一個個獨立可復用的小組件來構造我們的應用。
任何的應用都會被抽象成一顆組件樹。

img

組件化思想的應用:
有了組件化的思想,我們在之後的開發中就要充分的利用它。
儘可能的將頁面拆分成一個個小的、可復用的組件。
這樣讓我們的代碼更加方便組織和管理,並且擴展性也更強。
所以,組件是Vue開發中,非常重要的一個篇章,要認真學習。

三、註冊組件的基本步驟

組件的使用分成三個步驟:
創建組件構造器
註冊組件
使用組件。
我們來看看通過代碼如何註冊組件
查看運行結果:
和直接使用一個div看起來並沒有什麼區別。
但是我們可以設想,如果很多地方都要顯示這樣的信息,我們是不是就可以直接使用來完成呢?

img

img

img

四、註冊組件步驟解析

這裡的步驟都代表什麼含義呢?
1.Vue.extend():
調用Vue.extend()創建的是一個組件構造器。
通常在創建組件構造器時,傳入template代表我們自定義組件的模板。
該模板就是在使用到組件的地方,要顯示的HTML代碼。
事實上,這種寫法在Vue2.x的文檔中幾乎已經看不到了,它會直接使用下面我們會講到的語法糖,但是在很多資料還是會提到這種方式,而且這種方式是學習後面方式的基礎。
2.Vue.component():
調用Vue.component()是將剛才的組件構造器註冊為一個組件,並且給它起一個組件的標籤名稱。
所以需要傳遞兩個參數:1、註冊組件的標籤名 2、組件構造器
3.組件必須掛載在某個Vue實例下,否則它不會生效。
我們來看下面我使用了三次
而第三次其實並沒有生效:
第三步的解析

img

<div id="app">    <my-cpn></my-cpn></div><body>  <script src="../js/vue.js"></script>  <script>    //1.創建組件構造器對象    const cpnC = Vue.extend({      template: `        <div>          <h2>我是標題</h2>          <p>我是內容,紅紅火火恍恍惚惚</p>          <p>我是內容,紅紅火火恍恍惚惚</p>        </div>`    })    // 2.註冊組件    Vue.component('my-cpn',cpnC)    const app = new Vue({      el: "#app",      data: {        message: "你好"      }    })  </script></body>

五、全局組件和局部組件

當我們通過調用Vue.component()註冊組件時,組件的註冊是全局的
這意味着該組件可以在任意Vue示例下使用。
如果我們註冊的組件是掛載在某個實例中, 那麼就是一個局部組件

img

img

<div id="app">    <cpn></cpn></div><div id="app2">  <cpn></cpn></div><body>  <script src="../js/vue.js"></script>  <script>    //1.創建組件構造器對象    const cpnC = Vue.extend({      template: `        <div>          <h2>我是標題</h2>          <p>我是內容,紅紅火火恍恍惚惚</p>          <p>我是內容,紅紅火火恍恍惚惚</p>        </div>`    })    // 2.註冊組件(全局組件,意味着可以在多個Vue的實力下面使用)    Vue.component('my-cpn',cpnC)    const app = new Vue({      el: "#app",      data: {        message: "你好"      },      //在Vue實例內註冊組件就是局部組件      components:{        //cpn使用組件時的標籤名        cpn:cpnC      }    })        const app2 = new Vue({      el: "#app2",      data: {        message: "你好"      }    })  </script></body>

六、父子組件

父子組件錯誤用法:以子標籤的形式在Vue實例中使用

  • 因為當子組件註冊到父組件的components時, Vue會編譯好父組件的模塊
  • 該模板的內容已經決定了父組件將要渲染的HTML (相當於父組件中已經有了子組件中的內容了)
  • 是只能在父組件中被識別的。
  • 類似這種用法, 是會被瀏覽器忽略的。
<div id="app">	<cpn1></cpn1>  <cpn2></cpn2></div><body>	<script src="../js/vue.js"></script>	<script>    // 1.創建第一個組件構造器(子組件)    const cpnC1 = Vue.extend({      template:`      <div>      <h2>我是標題</h2>      <p>我是內容,紅紅火火恍恍惚惚</p>      </div>      `    })    // 2.創建第二個組件構造器(父組件)    const cpnC2 = Vue.extend({      template:`      <div>      <h2>我是標題</h2>      <p>我是內容,呵呵呵呵呵呵呵呵</p>      <cpn1></cpn1>      </div>      `,      components:{  //在父組件中註冊子組件        cpn1:cpnC1      }    })    //  可以把Vue當成一個root組件		const app = new Vue({			el: "#app",			data: {				massage: "你好"			},      components:{        cpn2:cpnC2      }		})	</script></body>

七、組件註冊語法糖

在上面註冊組件的方式,可能會有些繁瑣。

  • Vue為了簡化這個過程,提供了註冊的語法糖。
  • 主要是省去了調用Vue.extend()的步驟,而是可以直接使用一個對象來代替。

語法糖註冊全局組件和局部組件:

<div id="app">  <cpn1></cpn1>  <cpn2></cpn2></div><body>  <script src="../js/vue.js"></script>  <script>    // 1.全局組件註冊的語法糖    // 1.創建組件構造器    // const cpn1 = Vue.extend({    //   template:`    //   <div>    //   <h2>我是標題</h2>    //   <p>我是內容,紅紅火火恍恍惚惚</p>    //   </div>    //   `    // })    // 2.註冊組件    Vue.component('cpn1', {      template: `      <div>      <h2>我是標題</h2>      <p>我是內容,紅紅火火恍恍惚惚</p>      </div>      `    })    // 註冊局部組件的語法糖    const app = new Vue({      el: "#app",      data: {        massage: "你好"      },      components: {        'cpn2': {          template: `      <div>      <h2>我是標題</h2>      <p>我是內容,呵呵呵呵呵呵呵</p>      </div>      `        }      }    })  </script></body>

八、模板的分離寫法

剛才,我們通過語法糖簡化了Vue組件的註冊過程,另外還有一個地方的寫法比較麻煩,就是template模塊寫法。
如果我們能將其中的HTML分離出來寫,然後掛載到對應的組件上,必然結構會變得非常清晰。
Vue提供了兩種方案來定義HTML模塊內容:

  • 使用