vue從入門到精通學習筆記

瞭解Vue.js

認識Vue.js

  • Vue是一個漸進式框架,什麼漸進式?
    • 漸進式意味着你可以將Vue作爲你應用的一部分嵌入其中,帶來更豐富的交互體驗
  • 響應式
    • vue的響應式
      • 數據發生改變的時候,頁面也跟着改變
      • 可以直接在瀏覽器中的console下通過對象.屬性 = 值(app.message = ‘hello’)來改變數據,體驗vue的響應式
  • Vue的特點
    • 解耦視圖和數據
    • 可複用的組件
    • 前端路由技術
    • 狀態管理
    • 虛擬DOM

Vue.js安裝

  • CDN引入

    <!-- 開發環境版本,代碼沒有壓縮,包含了有幫助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 生產環境版本,優化了尺寸和速度 -->
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    
  • 下載和引用

    開發環境    https://vuejs.org/js/vue.js
    生產環境	https://vuejs.org/js/vue.min.js
    
  • npm安裝

    npm install vue
    

hello vue.js

  • 數據展示

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>Document</title>
    	<script src="./node_modules/vue/dist/vue.js"></script>
    </head>
    
    <body>
    	<div id="app">{{ message }}</div>
    	<script>
    		const app = new Vue({
    			el: '#app',
    			data() {
    				return {
    					message: 'hello vue.js'
    				}
    			}
    		})
    	</script>
    </body>
    
    </html>
    

    {{}}語法:叫Mustache(瑪斯塔)語法,可以進行數據計算,字符串的拼接。

    優點:可以完全的達到頁面與數據的分離

  • 列表展示(for循環,item是每項的值,index是每個項的索引)

    <div id="app">
    	<ul v-for = "item in message">
    		<li>{{ item }}</li>
    	</ul>
    </div>
    <script>
    	const app = new Vue({
    		el: '#app',
    		data() {
    			return {
    				message: ['少年的你','最好的我們','尋夢環遊記','匆匆那年']
    			}
    		}
    	})
    </script>
    
  • vue案例—計數器

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>Document</title>
    </head>
    
    <body>
    	<div id="app">
    		<!-- 方法1 -->
    		<!-- <button v-on:click="counter++">+</button>
    		<span>{{ counter }}</span>
    		<button v-on:click="counter--">-</button> -->
    		<!-- 方法2 -->
    		<input type="button" v-on:click="add" value="+" />
    		<span>{{ counter }}</span>
    		<input type="button" v-on:click="sub" value="-">
    	</div>
    	<script src="./node_modules/vue/dist/vue.js"></script>
    	<script>
    		new Vue({
    			el: '#app',
    			data() {
    				return {
    					counter: 0
    				}
    			},
    			methods: {
    				add:function() {
    					console.log('add');
    					this.counter++
    				},
    				sub:function() {
    					console.log('sub');
    					this.counter--
    				}
    			}
    		})
    	</script>
    </body>
    
    </html>
    
  • MVVM

    • Model View Model View

    • 搜索網站

      維基百科:https://www.wikipedia.org/

    • 綁定view和model實現數據的雙向綁定,你變我也變

    • 解釋

      • View層
        • 視圖層
        • 在前端開發過程中,通常就是DOM
        • 主要作用是給用戶展示各種信息
      • Model層
        • 數據層
        • 數據層是我們固定的四數據,更多的來自服務器,從網絡上請求下來的數據
        • 在我們計算過程中,就是從後面抽取過來的obj
      • ViewModel層
        • 視圖模型層
        • 視圖模型層是視圖層(View層)和模型層(Model層)溝通的橋樑
        • 一方面實現了數據的綁定,將Model層的實時改變渲染到視圖層上
        • 另一方面實現了DOM監聽,當DOM發生事件時,可以監聽到,並在對應情況下改變對應的數據

創建vue實例傳遞的opactions

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

vue的生命週期

生命週期:事務從誕生到消亡的過程。

vue生命週期:創建前/後,載入前/後,更新前/後,銷燬前/後。【重:面試會考】

生命週期函數,是vue自定義的公共函數,不需要創建,直接使用就🆗。

生命週期圖

在這裏插入圖片描述

生命鉤子函數

什麼是生命週期函數?

比如:

beforeMount: function() {
}

// 或者
beforeMount() {
}
  • Vue的所有生命週期函數都是自動綁定到this的上下文上。所以,使用箭頭函數的話,this指向的父級作用域,就會報錯。

錯誤的形式:

mounted:() => {
}

beforeCreate

img

在實例初始化之後,數據觀測和暴露了一些有用的實例屬性與方法。

實例初始化——new Vue()

數據觀測——在vue的響應式系統中加入data對象中所有數據,這邊涉及到vue的雙向綁定,可以看官方文檔上的這篇深度響應式原理 深度響應式原理

暴露屬性和方法——就是vue實例自帶的一些屬性和方法,我們可以看一個官網的例子,例子中帶$的屬性和方法就是vue實例自帶的,可以和用戶定義的區分開來

var data = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data
})

vm.$data === data // => true
vm.$el === document.getElementById('example') // => true

// $watch 是一個實例方法
vm.$watch('a', function (newValue, oldValue) {
  // 這個回調將在 `vm.a` 改變後調用
})
複製代碼

created

  • el屬性對生命週期的影響

img

// 有el屬性的情況下
new Vue({
el: '#app',
beforeCreate: function() {
  console.log('調用了beforeCreate')
},
created: function() {
  console.log('調用了created')
},
beforeMount: function() {
  console.log('調用了beforeMount')
},
mounted: function() {
  console.log('調用了mounted')
}
})

// 輸出結果
// 調用了beforeCreate
// 調用了created
// 調用了beforeMount
// 調用了mounted
複製代碼
// 在沒有el屬性的情況下,沒有vm.$mount

new Vue({
beforeCreate: function() {
  console.log('調用了beforeCreate')
},
created: function() {
  console.log('調用了created')
},
beforeMount: function() {
  console.log('調用了beforeMount')
},
mounted: function() {
  console.log('調用了mounted')
}
})

// 輸出結果
// 調用了beforeCreate
// 調用了created
複製代碼
// 在沒有el屬性的情況下,但是有vm.$mount方法

var vm = new Vue({
beforeCreate: function() {
  console.log('調用了beforeCreate')
},
created: function() {
  console.log('調用了created')
},
beforeMount: function() {
  console.log('調用了beforeMount')
},
mounted: function() {
  console.log('調用了mounted')
}
})

vm.$mount('#app')

// 輸出結果
// 調用了beforeCreate
// 調用了created
// 調用了beforeMount
// 調用了mounted
  • template屬性對生命週期的影響

img

主要分三種情況:

  1. 在實例內部有template屬性的時候,直接用內部的,然後調用render函數去渲染。
  2. 在實例內部沒有找到template,就調用外部的html。實例內部的template屬性比外部的優先級高。
  3. 要是前兩者都不滿足,那麼就拋出錯誤。

我們來看以下幾個例子:

new Vue({
  el: '#app',
  template: '<div id="app">hello world</div>'
})

//頁面上渲染出了hello world
複製代碼
<div id="app">hello world</div>

new Vue({
  el: '#app'
})

// 頁面上渲染出了hello world
複製代碼
//兩者都存在的時候

<div id="app">hello world2</div>

new Vue({
  el: '#app',
  template: '<div id="app">hello world1</div>'
})
// 頁面上渲染出了hello world1

從上述的例子可以看出內部的優先外部的。

  • 關於這個生命週期中的一些問題:

1、爲什麼el屬性的判斷在template之前? 因爲el是一個選擇器,比如上述例子中我們用到的最多的是id選擇器app,vue實例需要用這個el去template中尋找對應的。

2、實際上,vue實例中還有一種render選項,我們可以從文檔上看一下他的用法:

new Vue({
  el: '#app',
  render() {
    return (...)
  }
})

3、上述三者的渲染優先級:render函數 > template屬性 > 外部html

4、vue編譯過程——把tempalte編譯成render函數的過程。

beforeMount和mounted

life-mounted.png

<div id="app">
  <p>{{message}}</p>
</div>

new Vue({
  el: '#app',
  data: {
    message: 1
  },
  beforeMount: function() {
    console.log('調用了beforeMount');
    console.log(this.message)
    console.log(this.$el)
  },
  mounted: function() {
    console.log('調用了mounted');
    console.log(this.message)
    console.log(this.$el)
  }
})

// 輸出的結果:
// 調用了beforeMount
// 1
// <div>
// </div>

// 調用了mounted
// 1
// <div id="app">
//  <p>1</p>
// </div>

創建vue實例的$el,然後用它替代el屬性。

beforeUpdate和updated

img

這個過程中,我們會發現,當一個數據發生改變時,你的視圖也將隨之改變,整個更新的過程是:數據改變——導致虛擬DOM的改變——調用這兩個生命鉤子去改變視圖

  • 重點:這個數據只有和模版中的數據綁定了纔會發生更新。
// 沒綁定的情況

var vm = new Vue({
  el: '#app',
  template: '<div id="app"></div>',
  beforeUpdate: function() {
    console.log('調用了beforeUpdate')
  },
  updated: function() {
    console.log('調用了uodated')
  },
  data: {
    a: 1
  }
})

vm.a = 2
//這種情況在控制檯中是什麼都不會輸出的。
複製代碼
var vm = new Vue({
  el: '#app',
  template: '<div id="app">{{a}}</div>',
  beforeUpdate: function() {
    console.log('調用了beforeUpdate')
  },
  updated: function() {
    console.log('調用了uodated')
  },
  data: {
    a: 1
  }
})

vm.a = 2

// 輸出結果:
// 調用了beforeUpdate
// 調用了uodated
複製代碼

beforeDestory和destoryed

img

在beferoDestory生命鉤子調用之前,所有實例都可以用,但是當調用後,Vue 實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷燬。

不常用的生命鉤子函數

  • activated:當組件激活的時候調用
  • deactivated:當組件停用的時候調用
  • errorCaptured:這個生命鉤子可以看官網,2.5.0之後纔有的。當捕獲一個來自子組件的錯誤時被調用。

let vm = new Vue({
  el: '#app',
  data: {
    message: 1
  },
  template: '<div id="app"><p>{{message}}</p></div>',
  beforeCreate() {
    console.log('調用了beforeCreate')
    console.log(this.message)
    console.log(this.$el)
  },
  created() {
    console.log('調用了created')
    console.log(this.message)
    console.log(this.$el)
  },
  beforeMount() {
    console.log('調用了beforeMount')
    console.log(this.message)
    console.log(this.$el)
  },
  mounted() {
    console.log('調用了mounted')
    console.log(this.message)
    console.log(this.$el)
  },
  beforeUpdate() {
    console.log('調用了beforeUpdate')
    console.log(this.message)
    console.log(this.$el)
  },
  updated() {
    console.log('調用了updated')
    console.log(this.message)
    console.log(this.$el)
  },
  beforeDestory() {
    console.log('調用了beforeDestory')
    console.log(this.message)
    console.log(this.$el)
  },
  destoryed() {
    console.log('調用了Destoryed')
    console.log(this.message)
    console.log(this.$el)
  }
})

vm.message = 2
複製代碼
  • 輸出的結果:
// 調用了beforeCreate
// undefined
// undefined
// 調用了created
// 1
// undefined
// 調用了beforeMount
// 1
// <div></div>
// 調用了mounted
// 1
// <div id="app"><p>1</p></div>
// 調用了beforeUpdate
// 2
// <div id="app"><p>2</p></div>
// 調用了updated
// 2
// <div id="app"><p>2</p></div>

代碼規範:縮進,一般來說是縮進4個空格,但實際上縮進2個空格會更加的規範。

Vue的基礎語法

模板語法

插值操作( {{}} )

  • Mustach語法,也就是雙大括號({{}})語法

    • mustach:鬍子,鬍鬚

      <div id="app">
      	<h2>{{ message }}</h2>
      </div>
      <script src="./node_modules/vue/dist/vue.js"></script>
      <script>
      	new Vue({
      		el: '#app',
      		data() {
      			return {
      				message: 'hello meustach'
      			}
      		}
      	})
      </script>
      
    • Mustach語法,可以進行數值的計算,數據的綁定,字符串的拼接

常見指令 (v-xxx)

v-once指令

  • 只顯示第一次綁定的值,不會跟着用戶的操作而改變

  • 元素和組件只渲染一次,不會跟着數據的改變而改變

v-html指令

  • 綁定html標籤
<div id="app">
    <!-- 顯示 點我  超鏈接   點擊之後就跳轉到https://pic.images.ac.cn/image/5e87056c7bec6網頁 -->
	<h2 v-html = "url"></h2>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
	const app = new Vue({
		el: '#app',
		data() {
			return {
				url: `<a href = "https://pic.images.ac.cn/image/5e87056c7bec6">點我</a>`
			}
		}
	})
</script>

v-text指令

  • 綁定文本值

    <div id="app">
    	<!-- 顯示 hello v-text,good -->
    	<h2>{{ message }},good</h2>
    	<!-- 顯示 hello v-text -->
    	<h2 v-text = "message">good</h2>
    </div>
    <script src="./node_modules/vue/dist/vue.js"></script>
    <script>
    	const app = new Vue({
    		el: '#app',
    		data() {
    			return {
    				message: 'hello v-text'
    			}
    		}
    	})
    </script>
    
  • 不靈活,只能綁定確定的值,不能進行數據拼接,添加拼接值後,拼接值會被v-text的值覆蓋

v-pre

  • 標籤作用一樣,寫什麼就展示什麼

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

    <div id="app">
    	<!-- 顯示 hello v-text,good -->
    	<h2>{{ message }},good</h2>
    	<!-- 顯示 {{ message }},good -->
    	<h2 v-pre>{{ message }},good</h2>
    </div>
    <script src="./node_modules/vue/dist/vue.js"></script>
    <script>
    	const app = new Vue({
    		el: '#app',
    		data() {
    			return {
    				message: 'hello v-text'
    			}
    		}
    	})
    </script>
    

v-clock

  • 在某些情況下,我們瀏覽器可能會直接顯示出未編譯的Mustach標籤

  • clock:斗篷

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>Document</title>
    	<style>
    		/* v-lock存在的時候不顯示內容 */
    		[v-lock] {
    			display: none;
    		}
    	</style>
    </head>
    
    <body>
    
    	<div id="app" v-lock>
    		<!-- 顯示 hello v-text,good -->
    		<h2>{{ message }},good</h2>
    		<!-- 顯示 {{ message }},good -->
    		<h2 v-pre>{{ message }},good</h2>
    	</div>
    	<script src="./node_modules/vue/dist/vue.js"></script>
    	<script>
    		// 在vue解析之前,div中有一個屬性v-clock
    		// vue解析完成後,div中沒有v-lock屬性
    		setTimeout(function () {
    			const app = new Vue({
    				el: '#app',
    				data() {
    					return {
    						message: 'hello v-text'
    					}
    				}
    			});
    		},1000);
    	</script>
    </body>
    
    </html>
    
  • 頁面加載的時候起一個類似緩衝的作用

綁定屬性(v-bind)

v-bind

  • 作用:動態綁定屬性

  • 縮寫: :

  • 預期:any(with argument) | Object(without argument)

  • 參數:attrOrProp(optional)

    
    <div id="app">
    	<!-- 通過圖片地址,動態綁定元素屬性,顯示圖片 -->
    	<!-- 圖片不會顯示 -->
    	<img src="imgUrl">
    	<!-- 圖片會顯示 -->
        <!-- v-bind的原型 --> 
    	<img v-bind:src="imgUrl" >
        <!-- v-bind的簡寫形式 -->   
    	<img :src="imgUrl" >
            
    	<!-- 通過 v-html 標籤綁定形式,動態顯示圖片 -->
    	<div v-html="img"></div>
    </div>
    <script src="./node_modules/vue/dist/vue.js"></script>
    <script>
    	new Vue({
    		el: '#app',
    		data() {
    			return {
    				// 圖片地址
    				imgUrl: 'http://imgs.aixifan.com/content/2016_07_10/1468158502.gif',
    				img: '<img src="http://imgs.aixifan.com/content/2016_07_10/1468158502.gif"/>'
    			}
    		}
    	})
    </script>
    
    

v-bind的語法糖

  • 也就是v-bind的簡寫方式,在綁定屬性之前,去掉v-bind,直接用v-bind的省略方式來綁定數據(v-bind:src -----》 :src)

動態綁定對象

class語法
<!DOCTYPE html>
<html lang="en">

	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
		<style>
			.active{
				color: red;
			}
		</style>
	</head>

	<body>
		<div id="app">
			<!-- 可以通過app.isactive來改變屬性的值(布爾值),展示不同的狀態 -->
			<!-- <h2 :class="{key1:value1, key2:value2}">{{name}}</h2> -->
			<!-- <h2 :class="{類名1:true, 類名2:boolean}">{{name}}</h2> -->
			<!-- 元素綁定的class和動態綁定的class在瀏覽器中加載會合並 -->
			<h2 class="title" :class="{active:isactive, line:isline}">{{name}}</h2>
			
			<!-- 簡化寫法:將綁定的class屬性寫成一個方法,然後綁定封裝後的方法,讓頁面看起來更加的簡潔 -->
			<h2 class="title" :class="getClass()">{{name}}</h2>
			<button type="button" v-on:click="check">按鈕</button>
		</div>
		<script src="./node_modules/vue/dist/vue.js"></script>
		<script type="text/javascript">
			var app = new Vue({
				el: '#app',
				data() {
					return {
						name: '小陳',
						isactive: true,
						isline: true
					}
				},
				methods: {
					// 通過按鈕點擊事件來切換綁定數據的顯示樣式
					check: function(){
						this.isactive = !this.isactive;
					},
					getClass: function() {
						return {active:this.isactive, line:this.isline}
					}
				}
			})
		</script>
	</body>

</html>

數組語法
<!DOCTYPE html>
<html lang="en">

	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
		<style>
			.active{
				color: red;
			}
		</style>
	</head>

	<body>
		<div id="app">
			<!-- 通過數組語法綁定多個class-->
			<!-- 綁定的class屬性有單引號時,當字符串解析 -->
			<!-- 在頁面顯示成	<h2 class="active line"></h2> -->
			<h2 :class="['active', 'line']">{{ name }}</h2>
			
			<!-- 綁定的class屬性沒有單引號時,當變量解析 -->
			<!-- 在頁面顯示成   <h2 class="aaa bbb"></h2> -->
			<h2 :class="[active, line]">{{ name }}</h2>
			
			<!-- 在頁面顯示成   <h2 class="aaa bbb"></h2> -->
			<h2 :class="getClass()">{{ name }}</h2>
		</div>
		<script src="./node_modules/vue/dist/vue.js"></script>
		<script type="text/javascript">
			var app = new Vue({
				el: '#app',
				data() {
					return {
						name: '小陳',
						active: 'aaa',
						line: 'bbb'
					}
				},
				methods: {
					getClass: function(){
						// 在使用方法綁定時,綁定數組中的屬性值一定要加上this
						// 不加 this 就會顯示active line is not defind
						// return [active, line]
						
						return [this.active, this.line]
					}
				}
			})
		</script>
	</body>

</html>

實例

  • 點擊列表中的哪一項,哪一項的值就變成紅色

    • 步驟
      • 創建html頁面
      • 引入vue.js包
      • 添加作用域(
      • 實例化Vue({})
      • 添加掛載點(el)
      • 添加data屬性及要操作的movie數組
      • 將data中movie數組中的數據渲染到頁面上
      • 給li標籤綁定class屬性,屬性的索引等於點擊的li的索引
      • 添加點擊事件,將綁定class屬性的索引賦值給點擊的索引
    <!DOCTYPE html>
    <html lang="en">
    
    	<head>
    		<meta charset="UTF-8">
    		<meta name="viewport" content="width=device-width, initial-scale=1.0">
    		<title>Document</title>
    		<style type="text/css">
    			ul li {
    				/* 鼠標移上去,添加小手樣式 */
    				cursor: pointer;
    			}
    			/* 元素綁定的class樣式 */
    			.active {
    				color: red;
    			}
    		</style>
    	</head>
    
    	<body>
    		<!-- 2 創建作用域 -->
    		<div id="app">
    			<ul>
    				<!--通過切換索引值改變class-->
    				<!-- 遍歷顯示在那個標籤裏面v-for循環就寫在那個標籤裏面 -->
    				<li v-for='(item,index) in movie' @click="change(index)" :class='{ active:index === i }'>{{ item }}</li>       
    			</ul>
    		</div>
    		<!-- 1 引包 -->
    		<script src="./node_modules/vue/dist/vue.js"></script>
    		<script type="text/javascript">
    			// 3 實例化vue
    			var app = new Vue({
    				// 4 添加掛載點 el
    				el: '#app',
    				data() {
    					return {
    						// 頁面加載的時候給第一個li標籤添加樣式
    						// 元素的索引從0開始
    						i: 0, 
    						// 頁面顯示的數組數據
    						movie: ['尋夢環遊記', '肖申克的救贖', '摩登家庭', '老友記']
    					}
    				},
    				methods: {
    					// 元素標籤點擊事件
    					change: function(index) {
    						// console.log(index);
    						// 將添加class屬性的索引變成用戶點擊的索引
    						this.i = index; 
    					}
    				}
    			})
    		</script>
    	</body>
    
    </html>
    

v-bind綁定style樣式

  • 在寫css屬性名的時候,我們可以採用
    • 駝峯命名法(fontSize)
    • 短橫線分割法(‘font-size’)
  • 綁定class有兩種語法
對象語法
<h2 :style="{fontSize: end + 'px'}">{{ message }}</h2>
style後面跟的是一個對象類型
對象的key是css屬性名稱
對象的value是具體賦的值,值可以來自data中的屬性
<!DOCTYPE html>
<html lang="zh">
<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></title>
</head>
<body>
	<div id="app">
		<!-- <h2 :style="{key: value}"></h2> -->
		<!-- key是css的屬性名   value是屬性值 -->
		<!-- 這樣編譯會報錯,編譯的時候瀏覽器把50px當成一個變量編譯的,但是變量不能以數組開頭 -->
		<!-- <h2 :style="{font-size: 50px}">{{ message }}</h2> -->
		<!-- 解決方法: 將 50px  變成  '50px' -->
		<h2 :style="{'font-size': '50px'}">{{ message }}</h2>
		<h2 :style="{fontSize: '50px'}">{{ message }}</h2>
		
		<!-- 直接引用變量finalSize -->
		<!-- 引用的變量的值有單位,直接引用變量 -->
		<h2 :style="{fontSize: finalSize, color: finalColor }">{{ message }}</h2>
		<!-- 通過函數封裝來改變樣式 -->
		<h2 :style="getStyle()">{{ message }}</h2>
		<!-- 引用變量的值沒有單位,在變量後面通過字符串的拼接來添加單位 -->
		<h2 :style="{fontSize: end + 'px'}">{{ message }}</h2>
	</div>
	<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript">
		new Vue({
			el: '#app',
			data() {
				return {
					message: 'HerayChen',
					finalSize: '60px',
					end: 60,
					finalColor: 'red'
				}
			},
			methods: {
				getStyle: function() {
					return {fontSize: this.finalSize, color: this.finalColor }
				}
			}
		})
	</script>
</body>
</html>
數組語法
<h2 v-bind:style="[fontColor,backgroundColor]">{{ message }}</h2>
style後面跟的是一個數組類型
多個值,分隔即可
<!DOCTYPE html>
<html lang="zh">
<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></title>
	<style type="text/css">
		
	</style>
</head>
<body>
	<div id="app">
		<h2 :style="[fontColor]">{{ message }}</h2>
	</div>
	<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript">
		new Vue({
			el: '#app',
			data() {
				return {
					message: 'HerayChen',
					fontColor: {color: 'red'}
				}
			}
		})
	</script>
</body>
</html>

計算屬性(computed)

計算屬性的基本使用

<!DOCTYPE html>
<html lang="zh">
	<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></title>
	</head>
	<body>
		<div id="app">
			<!-- 通過mustach語法實現拼接 -->
			<h2>{{ cname + ' ' + ename }}</h2>
			<h2>{{ cname }} {{ ename }}</h2>
			
			<!-- 通過函數方法實現拼接 -->
			<h2>{{ getFullName() }}</h2>
			
			<!-- 通過計算屬性實現拼接 -->
			<!-- 注意:計算屬性,在寫的時候是通過方法組合而成的,但是在使用的時候是當成屬性使用的,直接通過屬性名調用即可,不需要加雙小括號 -->
			<h2>{{ fullName }}</h2>
		</div>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {
						ename: 'HerayChen',
						cname: '肖晨'
					}
				},
				// computed 計算屬性
				computed: {
                    // fullName在這裏是一個屬性,不是一個函數
					fullName: function() {
						return this.cname + ' ' + this.ename;
					}
				},
				methods: {
					getFullName() {
						return this.cname + ' ' + this.ename;
					}
				}
			})
		</script>
	</body>
</html>

計算屬性的複雜操作(通過for循環,計算總價格及es6 中的for-in方法)

<!DOCTYPE html>
<html lang="zh">
	<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></title>
	</head>
	<body>
		<div id="app">
			<h2>所有書的總價格爲:{{ books[0].price + books[1].price + books[2].price + books[3].price }}</h2>
			<h2>所有書的總價格爲:{{ totalPrice }}</h2>
			<h2>所有書的總價格爲:{{ tprice }}</h2>
		</div>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {
						books: [{
							id: 202001,
							name: '月亮與六便士',
							price: 50
						}, {
							id: 202002,
							name: '獨家的記憶',
							price: 35
						}, {
							id: 202003,
							name: '我在未來等你',
							price: 25
						}, {
							id: 202004,
							name: '愛',
							price: 250
						}]
					}
				},
				computed: {
					totalPrice: function() {
						let result = 0;
						for(let i = 0; i < this.books.length; i++){
							result += this.books[i].price;
						}
						return result;
					},
					tprice: function() {
						let result = 0;
						// es6中的for循環
						for (let i in this.books) {
							result += this.books[i].price;
						}
						return result;
					}
				}
			})
		</script>
	</body>
</html>

計算屬性的setter和getter

<!DOCTYPE html>
<html lang="zh">
<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></title>
</head>
<body>
	<div id="app">
		<h2>{{ fullName }}</h2>
	</div>
	<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript">
		new Vue({
			el: '#app',
			data() {
				return {
					cname: '肖晨',
					ename: 'HerayChen'
				}
			},
			computed: {
				// fullName: function() {
				// 	return this.cname + ' ' + this.ename;
				// },
				
				// 計算屬性一般沒有set方法,只讀屬性
				fullName: {
					set: function(newName) {
						// 只有值更改後纔會調用set方法
						// 要使用set方法,set方法一定是有參數的
						// 通過空格截取修改後的值
						const names = newName.split(' ');
						// 前面的部分是cname
						this.cname = names[0];
						// 後面的部分是ename
						this.ename = name[1];
						
					},
					get: function() {
						return this.cname + ' ' + this.ename;
					}
				}
			}
		})
	</script>
</body>
</html>

計算屬性和methods的對比

計算屬性(computed)比methods性能更高,methods加載一次調用一次(每次都會重新計算一次),計算屬性只調用一次(只計算一次)。

<!DOCTYPE html>
<html lang="zh">
<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></title>
</head>
<body>
	<div id="app">
		<!-- 直接拼接 -->
		<!-- 一般不使用這種方法,語法過於繁瑣 -->
		<h2>{{ cname + ' ' + ename }}</h2>
		<!-- 通過函數拼接 -->
		<!-- 調用多次多次,頁面加載一次調用一次 -->
		<h2>{{ getFullName() }}</h2>
		<!-- 通過計算屬性拼接 -->
		<!-- 調用多次 不管調用多少次,fullName只調用一次 -->
		<h2>{{ fullName }}</h2>
	</div>
	<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
	<script type="text/javascript">
		new Vue({
			el: '#app',
			data() {
				return {
					cname: '肖晨',
					ename: 'HerayChen'
				}
			},
			computed: {
				fullName: function() {
					return this.cname + ' ' + this.ename;
				}
			},
			methods: {
				getFullName: function() {
					return this.cname + ' ' + this.ename;
				}
			}
		})
	</script>
</body>
</html>

es6語法總結

let和const

var/let:變量,const:常量

  • let

    • 具有塊級作用域
    • 在if,for中var,沒有塊級作用域,(解決作用域方法:使用閉包,使用ES6語法)
    • 使用閉包解決var中沒有塊級作用域的原理是,函數是一個作用域【在javascript中只有函數是有作用域的】
  • const

    • 將某個變量變成常量

    • 常量是固定值,不可以再次賦值

    • const可以保證數據的安全性

    • 在es6開發中優先使用const,只有需要改變某一個標識符的時候才使用let

      注意:
      1 常量不可以再次修改
      const name = 'herayChen';
      name = 'xiaochen0';	// 錯誤
      2  常量聲明之後必須賦值
      const name;	//錯誤
      3 常量的含義是指向的對象不能修改,但是可以改變對象內部的屬性
      const obj = {
          name: 'xiaochen',
          age: 20
      }
      obj.age = 18;  //正確
      

對象字面量的增強寫法

在const obj = {}中,{}就是obj的字面量。

  • 屬性的增強寫法

    // 屬性的增強寫法
    const name = 'xiaochen';
    const age = 20;
    const sex = '女';
    
    // ES5中
    const obj = {
    	name: name,
    	age: age,
    	sex: sex
    }
    // ES6中
    const obj = {
    	name,
    	age,
    	sex
    }
    
  • 函數的增強寫法

    // 函數的增強寫法
    // ES5
    const obj = {
    	add: function() {
    		
    	},
    	reducte: function() {
    		
    	}
    }
    // ES6
    const obj = {
    	add() {
    		
    	},
    	reducte() {
    		
    	}
    }
    

    最近更新:typescript(microsoft),flow(facebook),angular(goole)【框架本身就使用的是typescript】

事件監聽(v-on)

  • 作用:綁定事件監聽器
  • 縮寫:@click
  • 預期:Function | Inline Statement | Object
  • 參數:event

基本使用

<div id="app">
	<h2>{{ counter }}</h2>
	<!-- 
		v-bind的語法糖:
		<h2 v-bind:title="">{{ counter }}</h2>
		<h2 :title="">{{ counter }}</h2>
	 -->
	<!-- 直接操作數據 -->
	<button type="button" @click="counter++">+</button>
	<button type="button" @click="counter--">-</button>
	<!-- 綁定函數,調用 -->
	<button type="button" v-on:click="increment">+</button>
	<!-- v-on中的語法糖:@click  -->
	<button type="button" @click="decrement">-</button>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				counter: 0
			}
		},
		methods: {
			increment() {
				this.counter++;
			},
			decrement() {
				this.counter--;
			}
		}
	})
</script>

v-on參數

情況一:

​ 如果該方法不需要額外參數,那麼方法後面的()可以省略,但是如果方法本身中有一個參數,那麼則會默認event參數傳遞進去。

情況二:

​ 如果需要同時傳遞某一個參數,同時需要event時,剋通過$event傳入事件。

<!DOCTYPE html>
<html lang="zh">
	<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></title>
	</head>
	<body>
		<div id="app">
			<!-- 1.事件調用的方法沒有參數 -->
			<!-- @click="btnClick"  或者 @click="btnClick()" -->
			<button @click="btn1Click">按鈕1</button>
			
			<!-- 2.有一個參數 -->
			<!-- 在事件定義時,寫函數時省略了小括號,但是方法本身是需要一個參數的 -->
			<!-- 打印輸出123 -->
			<button @click="btn2Click(num)">按鈕2</button>
			<!-- 沒有傳遞參數函數的形參是undefined -->
			<button @click="btn2Click()">按鈕2.1</button>
			<!-- 打印輸出MouseEvent對象 -->
			<!-- 在進行事件操作的時候瀏覽器會自動生成一個event對象 -->
			<!-- v-on綁定函數需要傳遞新參,但是沒有傳遞形參及小括號,就輸出event對象 -->
			<button @click="btn2Click">按鈕2.2</button>
			
			<!-- 3.有一個及多個參數 -->
			<!-- 在方法定義時,我們需要event對象,還需要其他參數 -->
			<!-- event is not defined -->
			<!-- 在這裏瀏覽器解析的時候是把event當成一個變量來解析的 -->
			<button @click="btn3Click(num, event)">按鈕3</button>
			<!-- 在調用方法的時候如何手動獲取event對象 ,通過 $event -->
			<button @click="btn3Click(num, $event)">按鈕3.1</button>
			
			
		</div> 
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {
						num: 123
					}
				},
				methods: {
					btn1Click() {
						console.log("btnClick");
					},
					btn2Click(abc) {
						console.log(abc);
					},
					btn3Click(abc, event) {
						console.log(abc, event);
					}
				}
			})
		</script>
	</body>
</html>

v-on修飾符

<!-- 1 停止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 2 阻止默認行爲 -->
<button @click.prevent="doThis"></button>
<!-- 3 阻止默認行爲,沒有表達式 -->
<button @click.stop></button>
<!-- 4 串聯修飾符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 5 鍵修飾符,鍵別名 -->
<button @click.enter="onEnter"></button>
<!-- 6 鍵修飾符,鍵代碼 -->
<button @click.13="onEnter"></button>
<!-- 7 點擊只會觸發一次 -->
<button @click.once="doThis"></button>

示例:

<!DOCTYPE html>
<html lang="zh">
	<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></title>
	</head>
	<body>
		<div id="app">
			<!-- 1 .stop阻止事件冒泡的使用 -->
			<div @click="divClick">
				<!-- 通過 stop阻止事件向上冒泡 -->
				<button @click.stop="btnClick">按鈕</button>
			</div>
			
			<!-- 2 .prevent修飾符的使用 -->
			<form action="baidu.com">
				<!-- 通過prevent阻止表單的默認提交 -->
				<input type="submit" value="提交" @click.prevent="submitClick"/>
			</form>
			
			<!-- 3 監聽鍵盤的鍵帽 -->
			<input type="text" @keyup.enter="keyUp" />
			
			<!-- 4 once修飾符的使用 -->
			<button type="button" @click.once="btn2Click">提交</button>
			
		</div>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {

					}
				},
				methods: {
					divClick() {
						console.log('divClick');
					},
					btnClick() {
						console.log('btnClick');
					},
					submitClick() {
						// 自定義提交
						console.log('submitClick');
					},
					keyUp() {
						// 監聽鍵帽,按下回車鍵後纔打印
						console.log('keyUp');
					},
					btn2Click() {
						// 只提交一次
						console.log('btn2Click');
					}
				}
			})
		</script>
	</body>
</html>

條件判斷

v-if的使用

<div id="app">
	<!--false不顯示,true顯示 -->
	<h2 v-if="isShow">good morning HerayChen</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				isShow: true
			}
		}
	})
</script>

v-if和v-else的使用

<div id="app">
	<!-- v-if中的條件爲true時,顯示v-if裏面的內容 -->
	<!-- v-if中的條件爲false時,顯示v-else裏面的內容 -->
	<h2 v-if="isShow">{{ message }}</h2>
	<h2 v-else>{{ now }}</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				message: 'good morning HerayChen',
				now: 'good afternoon HerayChen',
				isShow: false
			}
		}
	})
</script>

v-if,v-else-if,v-else的使用

層層判斷

<div id="app">
	<!-- 1 直接在標籤上使用 -->
	<h2 v-if="score >= 90">優秀</h2>
	<h2 v-else-if="score >= 80">良好</h2>
	<h2 v-else-if="score >= 60">及格</h2>
	<h2 v-else>繼續加油呀!</h2>
	
	<!-- 2 通過計算屬性來使用 -->
	<h1> {{ result }}</h1>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				score: 90
			}
		},
		computed: {
			result() {
				let showMessage = '';
				if (this.score >= 90) {
					showMessage = '優秀'
				} else if (this.score >= 80) {
					showMessage = '良好'
				} else if (this.score >= 60) {
					showMessage = '及格'
				} else{
					showMessage = '繼續加油呀~'
				}
				return showMessage;
			}
		}
	})
</script>

條件渲染案例(條件判斷)

選擇不同的方式登錄

在此過程中遇到的小問題:

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

問題解答:

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

解決方案:

  • 如果我們不希望Vue出現類似重複利用的問題,可以給對應的input添加key
  • 並且需要保證key的不同
<div id="app">
	<span v-if="isUser">
		<label for="userName">手機賬號</label>
		<!-- 給input標籤添加key屬性,防止在頁面中輸入了賬號然後想切換登錄方式,輸入的數據沒有清除 -->
		<!-- 這種現象的原因是:vue的虛擬DOM , 元素內部的複用問題-->
		<!-- 給input添加key屬性,將key作爲一個標識,決定能不能在其他地方進行使用 -->
		<!-- 當不需要在其他地方使用時,key的值一定唯一 -->
		<input type="text" id="userName" placeholder="手機賬號" key="userName"/>
	</span>
	<span v-else>
		<label for="email">郵箱</label>
		<input type="text" id="email" placeholder="郵箱" key="email"/>
	</span>
	<button @click="isUser = !isUser">切換類型</button>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				isUser: true
			}
		}
	})
</script>

v-show

<div id="app">
	<!-- v-if條件爲false是,包含v-if指令的元素,根本就不會存在DOM-->
	<!-- 創建 刪除... -->
	<h2 v-if="isShow">{{ message }}</h2>
	
	<!-- v-show是通過使用display:none來實現的 -->
	<!-- display:block   display:none -->
	<h2 v-show="isShow">{{ message }}</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				message: '知識改變命運,學習成就未來',
				isShow: true
			}
		}
	})
</script>

v-if和v-show對比:

  • v-if和v-show都可以決定一個元素是否渲染
    • 選擇
      • v-if條件爲false時,壓根不會有對應的元素在DOM中
      • v-show條件爲fasle是,僅僅是將元素的display屬性設置爲none
      • 當需要頻繁的切換顯示隱藏之間切片很頻繁時,使用v-show
      • 當只有一次切換時,通過使用v-if

循環遍歷

v-for遍歷數組

<div id="app">
	<!-- 在遍歷過程中沒有使用索引值 -->
	<ul>
		<li v-for="item in movies">{{ item }}</li>
	</ul>
	<!-- 在遍歷過程中獲取索引值 -->
	<ul>
		<li v-for="(item, index) in movies">{{ index+1 }}.{{ item }}</li>
	</ul>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				movies: ['尋夢環遊記', '肖申克的救贖', '愛的冒險', '冰雪奇緣']
			}
		}
	})
</script>

v-for遍歷對象

<div id="app">
	<ul>
		<!-- 在遍歷對象中  item是對象中的每個屬性的值 value -->
		<li v-for="item in movies">{{ item }}</li>
	</ul>
	
	<ul>
		<!-- key 屬性名 -->
		<!-- item 屬性值 -->
		<li v-for="(item,key) in movies">{{ key }}-{{ item }}</li>
	</ul>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				movies:{
					id: 1,
					name: '動物世界',
					actor: '李易峯',
					time: '2018-05-04'
				}
			}
		}
	})
</script>

組件的key屬性

  • 官方推薦使用v-for是,給對應的元素或者組件添加上一個key屬性
  • 爲什麼要使用key屬性呢?
    • 這個其實和Vue的Diff算法有關係
  • 當某一層有很多相同的節點時,也就是列表節點是,我們希望插入一個新的節點
    • 我們希望在b和c之間插入f,Diffy算法執行起來是這樣的。
    • 即把D更新成C,E更新成D,最後再插入E,這樣做效率不太高
  • 所以我們需要使用key來給每一個節點做一個唯一標識
    • Diff算法就可以正確的識別此節點
    • 找到正確的位置插入新的值
  • key的作用是爲了高效的更新虛擬DOM


數組中的響應式方法

1 push()
2 pop()
3 shift()
4 unshift()
5 splice()
6 sort()
7 reverse()
8 Vue中的set方法

<!DOCTYPE html>
<html lang="zh">
	<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></title>
	</head>
	<body>
		<div id="app">
			<ul>
				<li v-for="item in letter" :key="item">{{ item }}</li>
			</ul>
			<button type="button" @click="btnClick">按鈕</button>
		</div>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {
						letter: ['A', 'B', 'C', 'D', 'E'],
						nums: [1, 3, 4, 77, 33, 6]
					}
				},
				methods: {
					btnClick() {
						// 1 push()
						// 在數組後添加一個元素
						// Math.random().toString(36).substr(2) 隨機生成字母和數字的組合
						this.letter.push(Math.random().toString(36).substr(2));
						// 一次添加多個元素
						this.letter.push('aaa', 'bbb', 'ccc');

						// 2 pop()
						// 刪除數組的最後一個元素
						this.letter.pop();

						// 3 shift()
						// 刪除數組的第一個元素
						this.letter.shift();

						// 4 unshift()
						// 在數組最前面添加一個元素
						this.letter.unshift(Math.random().toString(36).substr(2));

						// 5 splice()
						// splice方法可以刪除元素/插入元素/替換元素
						// 刪除
						this.letter.splice(1, 2);
						// splice(1, 2);//從第一個元素開始,向後刪除2個元素		顯示ADE
						// 如果沒有傳第二個參數就刪除從起始位置後的所有元素

						// 替換
						// 顯示AFGHI
						// 從第一個元素開始,向後替換四個元素
						this.letter.splice(1, 4, 'F', 'G', 'H', 'I');
						
						// 插入
						// 顯示ABCDFGHIE
						// 在數組的第四個元素後添加'F', 'G', 'H', 'I'
						this.letter.splice(4, 0, 'F', 'G', 'H', 'I');
						
						// 6 sort()
						// 數組的排序
						// 打印輸出 [1, 3, 33, 4, 6, 77]
						console.log(this.nums.sort());
						
						// 7 reverse()
						// 翻轉數組
						// 顯示EDCBA
						this.letter.reverse();

						// 8 Vue中的set方法
						// set(要修改的對象, 索引值, 修改後的值)
						// 顯示 bbb B C D E 
						Vue.set(this.letter, 0, 'bbbb')

						// 注意:通過索引值,更改數組中的元素,這個不是響應式的
						// this.letter[0] = 'bbb';

						// 使用可變參數(...)的函數
						// function add(...num) {
						// 	// 打印輸出[1, 2, 3, 4, 4, 5, 5]
						// 	console.log(num);
						// }
						// add(1, 2, 3, 4, 4, 5, 5);
					}
				}
			})
		</script>
	</body>
</html>

階段案例(圖書購物車)

<!DOCTYPE html>
<html lang="zh">
	<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></title>
		<style type="text/css">
			table {
				border: 1px solid #e9e9e9;
				border-collapse: collapse;
				border-spacing: 0;
			}

			th,
			td {
				padding: 8px 16px;
				border: 1px solid #E9E9E9;
				text-align: center;
			}

			th {
				background-color: #7F7F7F;
				color: #5c6b77;
				font-weight: 600;
			}
		</style>
	</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 v-for="value in item">{{ value }}</td> -->
							<td>{{ item.id }}</td>
							<td>{{ item.name }}</td>
							<td>{{ item.data }}</td>
							<!-- 1 使用toFixed直接保留小數 -->
							<!-- <td>{{ '¥' + item.price.toFixed(2) }}</td> -->
							<!-- 2 通過函數的封裝調用保留小數 -->
							<!-- <td>{{ getFinalPrice(item.price) }}</td> -->
							<!-- 3 過濾器 -->
							<td>{{ item.price | getFinalPrice }}</td>
							<td>
								<button type="button" @click="increment(index)">+</button>
								{{ item.count }}
								<button type="button" @click="decrement(index)" :disabled="item.count <= 1">-</button>
							</td>
							<td>
								<button type="button" @click="removeHandel(index)">移出</button>
							</td>
						</tr>
					</tbody>
				</table>
				<h2>總價格:{{ totalPrice | getFinalPrice }}</h2>
			</div>
			<div v-else>
				<h2>購物車爲空</h2>
			</div>

		</div>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">

		</script>
		<script type="text/javascript">
			new Vue({
				el: '#app',
				data() {
					return {
						books: [{
							id: 1,
							name: '《算法導論》',
							data: '2006-9',
							price: 85.00,
							count: 1
						}, {
							id: 2,
							name: '《計算機概論》',
							data: '2018-9',
							price: 35.00,
							count: 1
						}, {
							id: 3,
							name: '《網絡技術與基礎》',
							data: '2006-9',
							price: 84.00,
							count: 1
						}, {
							id: 4,
							name: '《編程珠璣》',
							data: '2008-9',
							price: 39.00,
							count: 1
						}, {
							id: 5,
							name: '《代碼大全》',
							data: '2006-9',
							price: 85.00,
							count: 1
						}]
					}
				},
				computed: {
					totalPrice() {
						// 1 普通的for循環
						// let total = 0;
						// for (let i = 0; i < this.books.length; i++) {
						// 	total += this.books[i].price * this.books[i].count;
						// }
						// return total;

						// 2 es6中的for let in循環
						// let total = 0;
						// for (let i in this.books) {
						// 	// console.log(i);  i是數組的索引
						// 	total += this.books[i].price * this.books[i].count;
						// }
						// return total;

						// 3 es6中拿到數組中每一項的索引值
						// let total = 0;
						// for (let item of this.books) {
						// 	// item 是數組中每一項的值
						// 	// console.log(s);
						// 	total += item.price * item.count;
						// }
						// return total;

						// 高階函數reduce
						return this.books.reduce(function(preVaule, book) {
							return preVaule + book.price * book.count;
						}, 0);
					}
				},
				methods: {
					// 保留小數  函數
					// getFinalPrice(price) {
					// 	return '¥' + price.toFixed(2)
					// }

					// 加法函數
					increment(index) {
						// console.log('increment',index);
						this.books[index].count++
					},
					// 減法函數
					decrement(index) {
						// console.log('decrement',index);
						this.books[index].count--
					},
					removeHandel(index) {
						this.books.splice(index, 1)
					}
				},
				// 過濾器
				filters: {
					getFinalPrice(price) {
						return '¥' + price.toFixed(2)
					}
				}
			})
		</script>
	</body>
</html>

高階函數(filter,map,reduce)

  1. 去除所有小於100的數組
  2. 將所有小於100的數組轉成: 全部乘以2
  3. 將所有new2Num的數字相加,得到最終的結果

普通js實現:

const num = [10, 20, 30, 40, 50, 200];
// 創建一個空數組
let newNum = [];
// 1 通過for循環將所有小於100的數據,添加到新數組中
for (let s of num) {
    if (s < 100) {
        newNum.push(s)
    }
}
// 創建一個空數組
let new2Num = [];
// 2 通過for循環遍歷1中得到的小於100的數據,將1中的數組的每一項都乘以2
for (let n of newNum) {
	new2Num.push(n * 2);
}
console.log(new2Num);

// 3 遍歷2中得到的數組,將數組中每項值相加
let total = 0;
for (let n of new2Num) {
	console.log(total += n);
}

通過高階函數實現:

const num = [10, 20, 30, 40, 50, 200];
	// 高階函數
	// 1 過濾
	// filter函數  必須返回一個布爾值
	// 當函數返回true時,函數內部會自動將這次回調的n加入到新的數組中
	// 當函數返回false時,函數內部會過濾掉不符合條件的n
	let newNum = num.filter(function(n) {
		// console.log(n); //filter函數會回調6次
		return n < 100;
	});
	console.log(newNum); //打印輸出[10, 20, 30, 40, 50]

	// 2 map
	let new2Num = newNum.map(function(n) {
		return n * 2;
	});
	console.log(new2Num); //打印輸出[20, 40, 60, 80, 100]

	// 3 reduce
	// 作用: 對數組中所有的值進行彙總
	// preVaule 上一次遍歷返回的值
	// 第一次:preVaule: 0  n: 20
	// 第二次: preVaule: 第一次返回的值  n: 40
	let total = new2Num.reduce(function(preVaule, n) {
		return preVaule + n;
	}, 0);
	console.log(total);	// 打印輸出300

簡化高階函數:

const num = [10, 20, 30, 40, 50, 200];
let total = num.filter(function(n) {
	return n < 100;
}).map(function() {
	return n * 2;
}).reduce(function(preVaule, n) {
	return preVaule + n;
}, 0);
console.log(total);

高階函數和es6簡化:

let total = num.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n);
console.log(total);

v-mode(數據的雙向綁定)

  • 表單控件在實際開發中是非常常見的,特別是對於用戶信息的提交,需要大量的表單

  • Vue中使用v-model指令來實現表單元素和數據的雙向綁定

    <div id="app">
    	<input type="text" v-model="message" />
    	<h2>{{ message }}</h2>
    	
    	<!-- v-model的實現原理 --><!-- v-on:input監聽用戶輸入的事件 -->
    	<!-- 1 v-bind綁定value屬性 -->
    	<!-- 2 v-on指令給當前元素綁定input事件 -->
    	<input type="text" :value="name" @input="valueChange"/>
    	<input type="text" :value="name" @input="name = $event.target.value"/>
    	<h2>{{ name }}</h2>
    </div>
    <script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript">
    	new Vue({
    		el: '#app',
    		data() {
    			return {
    				message: 'hello xiaochen',
    				name: 'Heray'
    			}
    		},
    		methods: {
    			valueChange(event) {
    				this.name = event.target.value;
    			}
    		}
    	})
    </script>
    

v-model:radio(單選框)

<div id="app">
	<label for="male">
		<!-- 單選按鈕只能選擇一個  添加name屬性 或者 v-model屬性-->
		<input type="radio" id="male" name="sex" value="男" v-model="sex"></label>
	<label for="female">
		<input type="radio" id="female" name="sex" value="女" v-model="sex"></label>
	<h2>你選擇的性別是:{{ sex }}</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				// 給sex添加值,讓頁面加載的時候默認選中男
				sex: '男'
			}
		}
	})
</script>

v-model:checkbox(複選框【單個複選框,多個複選框】)

<div id="app">
	<!-- 1 checkbox單選框 -->
	<label for="agreement">
		<input type="checkbox" id="agreement" v-model="isAgreement" />同意協議
	</label>
	<h2>你的選擇是: {{ isAgreement }}</h2>
	<!-- 只有統一協議才能下一步,isAgreement爲false時下一步按鈕是禁用的狀態 -->
	<button type="button" :disabled="!isAgreement">下一步</button>
	<hr />
	<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">唱歌
	<input type="checkbox" value="滑板" v-model="hobbies">滑板
	<h2>你的愛好是:{{ hobbies }}</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				// 單選框對應的是一個布爾值
				isAgreement: false,
				// 多選框對應的是一個數組
				hobbies: []
			}
		}
	})
</script>

v-model:select(一個,多個)

<div id="app">
	<!-- 1 選擇一個 -->
	<select name="selected" v-model="fruit">
		<option value="蘋果">蘋果</option>
		<option value="香蕉">香蕉</option>
		<option value="梨子">梨子</option>
		<option value="櫻桃">櫻桃</option>
		<option value="草莓">草莓</option>
		<option value="車釐子">車釐子</option>
	</select>
	<h2>你喜歡的水果是:{{ fruit }}</h2>
	<hr />
	<!-- 2 選擇多個 -->
	<select name="selected" v-model="fruits" multiple="multiple">
		<option value="蘋果">蘋果</option>
		<option value="香蕉">香蕉</option>
		<option value="梨子">梨子</option>
		<option value="櫻桃">櫻桃</option>
		<option value="草莓">草莓</option>
		<option value="車釐子">車釐子</option>
	</select>
	<h2>你喜歡的水果是:{{ fruits }}</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				fruit:'草莓',
				fruits:[]
			}
		}
	})
</script>

值綁定

<div id="app">
	<label  v-for="(item, index) in hobbies" :for="index">
		<input type="checkbox"  :value="item" :id="index" v-model="hobbies">{{ item }}
	</label>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				hobbies: ['街舞', 'rapper', '滑板', '拉丁']
			}
		}
	})
</script>

修飾符

lazy修飾符:

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

number修飾符:

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

trim修飾符:

  • 如果用戶輸入的內容首尾有跟多空格,通常我們希望去掉
  • trim修飾符可以過濾內容左右兩邊的空格
<div id="app">
	<!-- 1 lazy修飾符 -->
	<!-- 在失去焦點和回車後更新綁定的數據 -->
	姓名:<input type="text" v-model.lazy="name"/>
	<h2>{{ name }}</h2>
	
	<!-- 2 number修飾符 -->
	<!-- 不管怎麼輸入,都只顯示number類型的數據 -->
	<!-- <input type="number" v-model="age"/>輸入的是數字,但是顯示出來的數據是string類型的 -->
	年齡:<input type="text" v-model.number="age"/>
	<h2>{{ age }}-{{ typeof age }}</h2>
	
	<!-- 3 trim修飾符 -->
	<!-- 去除輸入內容的前後空格 -->
	愛好:<input type="text" v-model.trim="hobby"/>
	<h2>{{ hobby }}</h2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {
				name: 'xiaochen',
				age: 18,
				hobby: ''
			}
		}
	})
</script>

組件開發

什麼組件化?

一個完成的頁面分成很多個組件,每個組件都用於實現頁面的一個功能塊,每個組件又能進行細分

Vue組件化思想

  • 它提供了一種抽象,讓我們可以開發出一個個獨立可複用的小組件來構造我們的應用
  • 任何應用都會被抽象成一顆組件樹
    • 數據結構:數組/棧/堆/樹結構

組件化思想的應用:

  1. 有了組件化的思想,我們在之後的開發中就要充分的利用它
  2. 儘可能的將頁面拆分成一個個小的,可複用的組件
  3. 這樣讓我們的代碼更加方便組織和管理,並且擴展性也更強

註冊組件的基本步驟

組件的使用分成三個步驟:

  1. 創建組件構造器
  2. 註冊組件
  3. 使用組件

在這裏插入圖片描述

<div id="app">
	<!-- 3 使用組件 -->
	<my-info></my-info>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 1 創建組件構造器對象
	const cnp = Vue.extend({
		// 自定義組件的模板
        template: `
			<div>
				<h2>標題</h2>
				<p>內容</p>
				<p>尾部</p>
			</div>
		`
	})
	<!-- // 2 註冊組件 -->
    //Vue.component('註冊組件的標籤名',組件構造器名)
	Vue.component('my-info',cnp)
	new Vue({
		el: '#app',
		data() {
			return {

			}
		}
	})
</script>

全局組件和局部組件

<div id="app">
	<!-- 3 使用全局組件 -->
	<my-info></my-info>
	<!-- 使用局部組件 -->
	<cpns></cpns>
</div>
<div id="app2">
	<!-- 使用全局組件 -->
	<my-info></my-info>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 1 創建組件構造器對象
	const cnp = Vue.extend({
		template: `
			<div>
				<h2>標題</h2>
				<p>內容</p>
				<p>尾部</p>
			</div>
		`
	})
	<!-- // 2 註冊組件(全局組件,意味着可以在多個Vue的實力下面使用) -->
	Vue.component('my-info',cnp)
	new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		<!-- 自定義局部組件,局部組件只能在掛載點範圍之內使用 -->
		components: {
			<!-- cpns使用組件的標籤名 -->
			cpns: cnp
		}
	})
	new Vue({
		el:'#app2'
	})
</script>

父組件和子組件

<div id="app">
	<!-- <dad></dad> -->
	<brother></brother>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 第一個組件構造器
	const dad = Vue.extend({
		template: 
		`
			<div>
				<h2>頭部</h2>
				<div>身體</div>
				<div>腳部</div>
			</div>
		`
	})
	<!-- // 第二個組件構造器 -->
	const brother = Vue.extend({
		template: 
		`
			<div>
				<h2>head</h2>
				<div>body</div>
				<div>foot</div>
				<dad></dad>
			</div>
		`,
		components: {
			<!-- 在brother組件裏面使用dad組件裏面的內容 -->
			<!-- 然後使用brother組件,就可以顯示brother組件和dad組件的內容 -->
			<!-- 要想使用組件必須註冊組件 -->
			<!-- 組件名:組件模板 -->
			dad: dad
		}
	})
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			<!-- dad: dad, -->
			brother: brother
		}
	})
</script>

註冊組件的語法糖

<div id="app">
	<!-- 全局組件 -->
	<daD></daD>
	<!-- 局部組件 -->
	<cpn2></cpn2>
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 註冊全局組件
	const dad = Vue.component('dad', {
		template:
		`
			<div>
				<h2>我是dad模板中全局組件的內容</h2>
			</div>
		`
	});
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			<!-- 註冊局部組件 -->
			'cpn2': {
				template:
				 `
				<div>
					<h2>我是dad模板中局部組件的內容</h2>
				</div>
				`
			}
		}
	});
</script>      

模板的分離寫法

模板的分離寫法:

<div id="app">
	<!-- 全局組件 -->
	<daD></daD>
	<!-- 局部組件 -->
	<cpn2></cpn2>
</div>

<!-- 1 通過script標籤提取模板 -->
<!-- 注意類型必須是:text/x-template -->
<script type="text/x-template" id="dad">
	<div>
		<h2>我是dad模板中全局組件的內容</h2>
	</div>
</script>

<!-- 2 通過template標籤 -->
<template id="cpn2">
	<div>
		<h2>我是dad模板中局部組件的內容</h2>
	</div>
</template>

<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 註冊全局組件
	const dad = Vue.component('dad', {
		// 通過id引用模板
		template: `#dad`
	});
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			<!-- 註冊局部組件 -->
			'cpn2': {
				template:`#cpn2`
			}
		}
	});
</script>

組件可以訪問Vue實例數據

  • 組件是一個單獨功能模塊的封裝:
    • 這個模塊有屬於自己的HTML模板,也應該有屬性自己的數據data
  • 組件裏面是不能訪問vue實例裏面的數據的。

組件數據的存放:

  1. 組件對象也有一個data屬性(也可以有methods等屬性)
  2. 只是這個data屬性必須是一個函數
  3. 而且這個函數返回一個對象,對象內部保存着數據
<div id="app">
	<!-- 全局組件 -->
	<daD></daD>
	<!-- 局部組件 -->
	<cpn2></cpn2>
</div>

<!-- 1 通過script標籤提取模板 -->
<!-- 注意類型必須是:text/x-template -->
<script type="text/x-template" id="dad">
	<div>
<h2>{{ message }}</h2>
</div>
</script>

<!-- 2 通過template標籤 -->
<template id="cpn2">
	<div>
		<h2>{{ message }}</h2>
	</div>
</template>

<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 註冊全局組件
	const dad = Vue.component('dad', {
		// 通過id引用模板
		template: `#dad`,
		data() {
			return {
				message: '我是dad模板中全局組件的內容'
			}
		}
	});
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			<!-- 註冊局部組件 -->
			'cpn2': {
				template: `#cpn2`,
				data() {
					return {
						message: '我是dad模板中局部組件的內容'
					}
				}
			}
		}
	});
</script>

組件中的數據存放爲什麼是一個函數(面試可能會問)

函數每次都會返回一個新的新的對象,產生不同的數據。

對象每次都作用在一個屬性上,一個改變所有的都改變({}可能會相互影響)。

<div id="app">
	<cpn></cpn>
	<cpn></cpn>
</div>
<!-- 組件模板 -->
<template id="cpn">
	<div>
		<h2>當前計數:{{ num }}</h2>
		<button type="button" @click="increment">+</button>
		<button type="button" @click="decrement">-</button>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 1 註冊組件
	Vue.component('cpn', {
		template: `#cpn`,
		// data是一個函數,函數每次都會返回一個新的對象
		// 這樣同時使用一個組件,點擊一個組件,就不會改變其他組件的值
		
		// 如果data {} 返回的是一個對象,每次作用的都是一個同養的對象,一個改變其他的都改變
		// 使用{},會產生連鎖反應
		data() {
			return {
				num: 0
			}
		},
		methods: {
			increment() {
				this.num++
			},
			decrement() {
				this.num--
			}
		}
	})
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		}
	})
</script>

父子組件的通信

通信:相互交流,數據傳遞。在大組件(父組件)獲取所有的後臺數據,然後在傳遞給小組件(子組件),渲染到頁面。

如何進行父子組件的通信?

  1. 通過props(是properties屬性的縮寫)向子組件中傳遞數據
  2. 通過自定義事件向父組件發送消息

父傳子props

<div id="app">
	<!-- 在父組件裏面使用子組件 -->
	<!-- 在頁面中顯示[ "速度與激情", "肖申克的救贖", "友情之上", "摩登少年" ]  和  hello -->
	<cpn v-bind:cmovies="movies" :cmessage="message"></cpn>
	
	<!-- 在傳值後,如果沒有用v-bind,則會把綁定字段,當成字符串解析 -->
	<!-- 在頁面中顯示movies message -->
	<cpn cmovies="movies" cmessage="message"></cpn>
</div>

<template id="cpn">
	<div>
		<p>{{ cmovies }}</p>
		<!-- 循環遍歷父組件中傳遞過來的數據 -->
		<ul>
			<li v-for="item in cmovies">{{ item }}</li>
		</ul>
		<h2>{{ cmessage }}</h2>
	</div>
</template>

<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 自定義組件
	const cpn = {
		template: `#cpn`,
		// 父傳子props
		// 1 數組
		props: ['cmovies','cmessage'],
		// 2 對象
		prpos: {
			// 1 類型的限制
			// cmovies: Array,
			// cmessage: String
			
			// 2 提供一些默認值,以及必傳值
			cmessage: {
				type: String,
				// 沒有傳值提供的默認值
				default: 'aa',
				// 傳值時cmessage是必須傳的值
				required: true
			},
			cmovies: {
				type: Array,
				// 類型是對象或者數組時,默認值必須是一個函數
				default() {
					return []
				}
			}
		}
	}
	
	const app = new Vue({
		el: '#app',
		data() {
			return {
				message: 'hello',
				movies:['速度與激情', '肖申克的救贖', '友情之上', '摩登少年']
			}
		},
		components: {
			cpn
		}
	})
</script>

在這裏插入圖片描述

props數據驗證

驗證支持的數據類型:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
  • 自定義數據類型
Vue.component('my-component',{
    props: {
        //基礎的類型檢查(null,匹配任何數)
        propA: Number,
        //多個可能的類型
        propB: [String, Number],
        //必填的字符
        propC: {
            type: String,
            required: true
        },
        //帶有默認值的數字
        propD: {
            type: Number,
            default: 100
        },
        // 帶有默認值的對象
        propE: {
            type: Object,
            default() {
                // 返回一個默認值
                return { message:'hello' }
            }
        },
        // 自定義驗證函數
        propF: {
            validator(value){
                //這個值必須匹配下列字符中的一個
                return ['success', 'warning', 'danger'].indexOf(value) !== -1
            }
        }
    }
})

自定義數據類型:

function Person(firstName, lastName){
    this.firstName = firstName
    this.lastName = lastName
}
Vue.component('blog-post', {
    props: {
        // 自定義數據類型
        author: Person
    }
})

props中的駝峯標識

在子組件中,props中的數據使用了駝峯命名,在父組件中使用必須去掉駝峯(遇到大寫字母在前面加-分隔,然後將大寫字母變成小寫字母)

<div id="app">
	<!-- 子組件是在父組件中有使用的 -->
	<!-- 在子組件上綁定(v-bind)父組件傳遞過來的數據 -->
	<!-- v-bind不支持駝峯命名 -->
	<!-- <cpn :cinfo = "info"></cpn> -->
	
	<!-- props中綁定的數據是cInfo,使用了駝峯必須 -->
	<!-- 使用是必須將 cInfo  寫成 c-info-->
	<!-- 遇到大寫字母在前面加-,然後將其變成小寫使用 -->
	<cpn :c-info = "info"></cpn>
</div>
<template id="cpn">
	<!-- 子組件的模板中有很多個標籤,必須有一個跟標籤將其包裹 -->
	<div>
		<!-- 使用父組件中傳遞過來的數據 -->
		<!-- <h2>{{ cinfo }}</h2> -->
		
		<!-- props中使用了駝峯 -->
		<!-- 在頁面中顯示 { "name": "蕭辰", "age": 18, "hobbie": [ "街舞", "吉他", "滑板" ] } -->
		<h2>{{ cInfo }}</h2>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 子組件
	const cpn = {
		template: `#cpn`,
		props: {
			// cinfo: {
			cInfo: {	
				type:Object,
				default() {
					return {
						
					}
				}
			}
		}
	};
	// 父組件
	const app = new Vue({
		el: '#app',
		data() {
			return {
				info: {
					name: '蕭辰',
					age: 18,
					hobbie: ['街舞', '吉他', '滑板']
				}
			}
		},
		components: {
			cpn
		}
	})
</script>

子組件向父組件中傳遞數據(自定義事件)

自定義事件的流程:

  1. 在子組件中,通過$emit()來觸發時間
  2. 在父組件中,通過v-on來監聽子組件事件
<!-- 父組件模板 -->
<div id="app">
	<!-- 監聽v-bind子組件發射出的時間 -->
	<!-- 子組件監聽子組件傳遞過來的事件時綁定的事件不能使用駝峯命名 -->
	<!--  cpnClick(item) 寫成 cpnClick這樣也是可以的,從子組件傳遞過來的方法,會默認將參數也傳遞過來,就像event一樣-->
	<cpn @itemclick="cpnClick(item)"></cpn>
</div>
<!-- 子組件模板 -->
<template id="cpn">
	<div>
		<button v-for="item in cartegories" @click="btnClick(item)">{{ item.name }}</button>
	</div>
</template>

<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	// 子組件
	const cpn = {
		template: '#cpn',
		data() {
			return {
				cartegories:[
					{
						id: 'a',
						name: '熱門推薦'
					},
					{
						id: 'b',
						name: '手機數碼'
					},{
						id: 'c',
						name: '家用家電'
					},{
						id: 'd',
						name: '手機辦公'
					},{
						id: 'e',
						name: '精選服飾'
					}
				]
			}
		},
		methods: {
			btnClick(item) {
				// 查看用戶點擊的信息
				// console.log(item);
				// 將用戶點擊的信息傳到父組件中
				// 向父組件中發射自定義事件事件,及傳遞的參數
				this.$emit('itemclick', item)
			}
		}
	}
	// 父組件
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			cpn
		},
		methods: {
			// 父組件事件
			cpnClick(item) {
				console.log(item);
			}
		}
	})
</script>

在這裏插入圖片描述

父子組件通信實例

<!DOCTYPE html>
<html lang="zh">
	<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></title>
	</head>
	<body>
		<div id="app">
			<cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"></cpn>
		</div>
		<!-- 子組件模板 -->
		<template id="cpn">
			<div>
				<h2>props1:{{ number1 }}</h2>
				<h2>data:{{ cnumber1 }}</h2>
				<!-- <input type="text" v-model="cnumber1"/> -->
				<input type="text" :value="cnumber1" @input="num1Input" />
				<h2>props2:{{ number2 }}</h2>
				<h2>data:{{ cnumber2 }}</h2>
				<!-- <input type="text" v-model="cnumber2"/> -->
				<input type="text" :value="cnumber2" @input="num2Input" />
			</div>
		</template>
		<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
		<script type="text/javascript">
			const app = new Vue({
				el: '#app',
				data() {
					return {
						num1: 1,
						num2: 0
					}
				},
				methods: {
					num1change(value) {
						this.num1 = parseInt(value);
					},
					num2change(value) {
						this.num2 = parseInt(value);
					}
				},
				components: {
					cpn: {
						template: `#cpn`,
						props: {
							number1: Number,
							number2: Number
						},
						data() {
							return {
								cnumber1: this.number1,
								cnumber2: this.number2
							}
						},
						methods: {
							num1Input(event) {
								// 1 將input中的value賦值到cnumber1
								this.cnumber1 = event.target.value;

								// 2 爲了讓父組件可以修改值,發出一個事件
								this.$emit('num1change', this.cnumber1);

								// 3 同時修改cnumber2的值
								this.cnumber2 = this.cnumber1 * 100;
								this.$emit('num2change', this.cnumber2)
							},
							num2Input(event) {
								this.cnumber2 = event.target.value;
								this.$emit('num2change', this.cnumber2);

								// 3 同時修改cnumber2的值
								this.cnumber1 = this.cnumber2 / 100;
								this.$emit('num1change', this.cnumber1)
							}
						}
					}
				}
			})
		</script>
	</body>
</html>

在這裏插入圖片描述

父子組件的訪問方式

父組件訪問子組件childrenchildren或refs

  1. $children 得到的是一個 數組

    this.$children[].訪問對象
    
  2. $refs 得到的是一個 對象

    this.$refs.添加ref的數值.訪問對象
    
  3. 實例

<div id="app">
	<cpn></cpn>
	
	<cpn ref="aaa"></cpn>
	<button type="button" @click="btnClick">點我</button>
</div>
<template id="cpn">
	<div>
		我是子組件
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		methods: {
			btnClick() {
				// 1 通過$children去拿子組件中的值
				// // 拿到的子組件是一個數組類型的
				// // console.log(this.$children);
				// // 通過$children調用子組件的showMessage方法
				// // this.$children[0].showMessage();
				// // this.$children[0].name;
				// for (let c of this.$children) {
				// 	// 打印輸出showMessage
				// 	c.showMessage();
				// 	// 打印輸出xiaochen
				// 	console.log(c.name);
				// }
				
				// 2 通過$refs去拿子組件中的值
				// this.$refs默認是一個空對象
				console.log(this.$refs);
				// 要使用$refs取值,需要在標籤上面添加ref屬性,然後通過this.$refs.ref屬性值拿值
				// 打印輸出 showMessage
				this.$refs.aaa.showMessage();    
				// 打印輸出 xiaochen
				console.log(this.$refs.aaa.name);    
			}
		},
		components: {
			cpn: {
				template: `#cpn`,
				data() {
					return {
						name: 'xiaochen'
					}
				},
				methods: {
					showMessage() {
						console.log('showMessage');
					}
				}
			}
		}
	})
</script>

子組件訪問父組件parentparent,root

  1. $parent

    this.$parent.訪問父組件的屬性
    
  2. $root

    // 訪問根組件,Vue實例
    this.$root
    

實例:

<div id="app">
	<cpn>
	</cpn>
</div>
<template id="cpn">
	<div>
		<h2>我是cpn組件</h2>
		<ccpn></ccpn>
	</div>
</template>
<template id="ccpn">
	<div>
		<h2>我是子組件</h2>
		<button @click="btnClick">按鈕</button>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	const app = new Vue({
		el: '#app',
		data() {
			return {
				
			}
		},
		components: {
			cpn: {
				template: `#cpn`,
				data() {
					return {
						name: '我是cpn組件的name'
					}
				},
				components: {
					ccpn: {
						template: `#ccpn`,
						methods: {
							btnClick() {
								// 1 訪問父組件$parent
								// 打印出來是一個VueComponent對象
								console.log(this.$parent);
								// 打印輸出 我是cpn組件的name
								console.log(this.$parent.name);
								
								// 2 訪問根組件$root
								// 打印Vue 實例
								console.log(this.$root);
							}
						}
					}
				}
			}
		}
	})
</script>

插槽slot(組件化高級)

爲什麼使用組件的插槽:

  • 爲了讓我們封裝的組件更加具有擴展性
  • 讓使用者可以決定組件內部的一些內容到底展示什麼

插槽的基本使用

使用slot插槽,佔據位置,在使用的時候傳入對應的值,然後傳入的值自己會填補插槽佔據的位置。

插槽的基本使用:

  1. 插槽的基本使用
  2. 插槽的默認值
  3. 如果有多個值,同時放入組件進行替換時,一起作爲替換元素
<div id="app">
	<cpn></cpn>
	<hr />
	<cpn>
		<button type="button">點我</button>
	</cpn>
	<hr />
	<cpn><b>我是傳入值</b></cpn>
</div>
<template id="cpn">
	<div>
		<h2>我是組件</h2>
		<slot></slot>
		<h5>content</h5>
		<!-- 給插槽添加一個默認值,調用的時候沒有加別的參數,就使用默認值,有參數,就是用傳遞的值 -->
		<slot><button>我是插槽傳遞的默認值</button></slot>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			cpn: {
				template: `#cpn`
			}
		}
	})
</script>

如何封裝插槽

抽取共性,保留不同。

  • 最好的方式就是將共性抽取到組件中,將不同暴露爲插槽
  • 一旦預留了插槽,就可以讓使用者根據自己的需求,決定插槽中插入什麼內容
  • 是搜索框,還是文字,還是菜單,由調用者自己來決定

具名插槽

給插槽設置name值,然後使用的使用根據solt等於的name值進行替換。

<div id="app">
	<cpn>
		<!-- 替換name爲center插槽 -->
		<h1 slot="center">標題</h1>
	</cpn>
</div>
<template id="cpn">
	<div>
		<slot name="left">
			<h2>我是左邊插槽</h2>
		</slot>
		<slot name="center">
			<h2>我是中間插槽</h2>
		</slot>
		<slot name="right">
			<h2>我是右邊插槽</h2>
		</slot>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	const app = new Vue({
		el: '#app',
		data() {
			return {

			}
		},
		components: {
			cpn: {
				template: `#cpn`
			}
		}
	})
</script>

編譯的作用域

父模板的所有東西都會在父級作用域內編譯,子組件模板的所有東西都會在自己作用域內編譯。

<div id="app">
	<!-- 這個是根據實例裏面的isShow來決定的 -->
	<!-- 命令使用時不關心組件的位置,只關心作用域 -->
	<!-- 顯示  app的data中的isShow是true -->
	<cpn v-show="isShow"></cpn>
</div>
<template id="cpn">
	<div>
		<h2>我是子組件</h2>
		<!-- 不顯示 -->
		<!-- cpn組件中的isShow是false -->
		<button v-show="isShow"></button>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	const app = new Vue({
		el: '#app',
		data() {
			return {
				isShow: true
			}
		},
		components: {
			cpn: {
				template: `#cpn`,
				data() {
					return {
						isShow: false
					}
				}
			}
		}
	})
</script>

作用域插槽

作用域插槽的目的就是:

父組件替換插槽的標籤,但是內容由子組件來提供。

實例:

  • 子組件中包括一組數據,比如:pLanguages[‘Javascript’, ‘Python’, ‘Swift’, ‘Go’, ‘C++’]

  • 需要在多個界面進行展示:

    • 某些界面是以水平方向——展示的
    • 某些界面是以列表形式展示的
    • 某些界面直接展示——一個數組
  • 內容在子組件,希望父組件告訴我們如何展示?

    • 利用slot插槽就可以了
<div id="app">
	<!-- 子組件默認展示 -->
	<cpn></cpn>
	
	<!-- 拿到子組件中的數據,然後在父組件中展示成不同的樣式  -->
	<!-- 目的:獲取子組件中的pLanguages -->
	<cpn>
		<template slot-scope="slot">
			<!-- 通過slot引用插槽對象 -->
			<!-- 通過slot.data     拿到的就是  子組件中傳遞的:data="pLanguages" -->
			<span v-for="item in slot.data">{{item}}  -</span>
			<div>{{ slot.data.join(' - ')}}</div>
		</template>
	</cpn>
</div>
<template id="cpn">
	<div>
		<!-- 這裏data可以爲任何值  -->
		<slot :data="pLanguages">
			<ul>
				<!-- 子組件展示 -->
				<li v-for="item in pLanguages">{{ item }}</li>
			</ul>
		</slot>
	</div>
</template>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	const app = new Vue({
		el: '#app',
		data() {
			return {
				isShow: true
			}
		},
		components: {
			cpn: {
				template: `#cpn`,
				data() {
					return {
						pLanguages: ['Javascript', 'Python', 'Swift', 'Go', 'C++']
					}
				}
			}
		}
	})
</script>

模塊化開發

js原始功能:

  • 做表單驗證活動話
  • 隨着ajax的出現,前後端開始分離,爲了應對代碼量的與日俱增,將js分成多個js文件來編輯
  • 但這種方式依然不能避免一些災難性問題:
    • 全局變量同名
    • js文件過多,執行順序的問題
  • 解決:使用閉包()() ————》代碼不可複用
    • 將每一個js文件封裝成一個閉包,在閉包中通過創建空對象,將使用的值存入空對象中,然後return回去
    • 在別的js文件使用的時候,通過封裝的閉包名.要使用的對象

常見的模塊化規範:

CommonJS,AMD,CMD,也有ES6中的Modules

CommonJS只是一個規範,CommonJS的實現是node.js。

CommonJS

模塊化的兩個核心:導出(export)和導入(require)

CommonJS的導出:

moule.export = {
	name: 'xiaochen',
	doit(a, b) {
		return a + b;
	}
}

CommonJS的導入:

//CommonJS模塊
let { name,doit } = require('moduleA');

//等同於
let m = require('moduleA');
let name = m.name;
...

ES6的模塊化實現

export

export指令用於導出變量:

export let name = 'xiaochen'
export let age = 18
export let dream = 'free'

也可以:

let name = 'xiaochen'
let age = 18
let dream = 'free'

export {
	name, age, dream
}

導出函數和類:

export function sum(a, b) {
    return a + b;
}

export class Person {
    run() {
        console.log('想上學的第一天');
    }
}

import {sum, Person} from './a.js'
// 實例化類
const p = new Person();
// 調用類裏面的函數
p.run();

export default:

某些情況下,一個模塊包含某個功能,我們並不希望給這個功能命名,而且讓導入這自己來命名

  • 這個時候就可以使用 export default
  • 注意:export default 在同一個模塊中,不允許同時存在多個
const time = '二零二零年四月七日'

// 1 
export { time }
// 2
export const time = '二零二零年四月七日'
//要使用上面的數據
import {time} from 'a.js'

// 在一個js中文件中,只能有一個default導出
export default time
import t from 'a.js'

import

export指令導出了模塊對外提供的接口後,我們就可以通過import命令來加載這個對應的模塊了

  • 首先我們需要在HTML代碼中引入兩個js文件,並且類型需要設置module

    <script src = "info.js" type = "module"></script>
    <script src = "main.js" type = "module"></script>
    
  • import指令用於導入模塊內容,比如mian.js的代碼

    import {name, age, sex} from './info.js'
    console.log(name, age, sex);
    
  • 統一全部導入

    import * as info from './info.js'
    console.log(info.name);
    

webpack

認識webpack

什麼是webpack?

官網:webpack.js.org

從本質上將,webpack是一個現代的JavaScript應用的靜態模塊打包工具。

  • 靜態
  • 打包

和grunt/gulp的對比

  • grunt/gulp的核心是Task
    • 我們可以配置一系列的task,並且定義task要處理的事物(例如ES6,ts轉換,圖片的壓縮,scss轉化成css)
    • 之後讓grunt/gulp來依次執行這些task,而且讓它流程自動化
    • grunt/gulp也被稱爲前端自動化任務管理工具
  • 什麼時候用到grunt/gulp
    • 如果工程模塊依賴非常簡單,甚至是沒有用到模塊化的概念
    • 只要進行簡單的合併,壓縮,就使用grunt/gulp即可
    • 但是如果整個項目使用了模塊化管理,而且相互依賴非常強,我們就可以使用更加強大的webpack
  • gulp和webpack的區別
    • grunt/gulp更加強調的是前端流程的自動化,模塊化不是它的核心
    • webpack更加強調模塊化開發管理,而文件壓縮合並,預處理功能,是他附帶的功能

webpack的安裝

  • webpack是基於node.js的
  • node爲了可以正常的運行執行很多代碼面必須其中包含很多包
  • 在安裝node時,會自動安裝npm(node package manager)

查看node版本:

node -v

全局安裝webpack(這裏我們先制定版本號3.6.0,因爲vue cli2依賴該版本):

  1. 查看電腦中有沒有安裝過webpack

    webpack --version  或者    webpack -v
    
  2. 沒有 安裝

    全局安裝webpack:

    npm install webpack@3.6.0 -g
    

    本地安裝webpack:

    --save-dev是開發時依賴【devDependencies】,項目打包後不需要繼續使用

    npm install webpack@3.6.0 --save-dev
    

webpack的起步

文檔解釋:

dist文件 distribution(發佈)
文件 解釋
src文件 主要文件,一般項目的文件都寫在src這個文件夾裏面

打包:

// webpack  打包文件main.js的路徑   打包成dist下面的bundle.js文件
// webpack在打包的時候會自動處理依賴文件
// 所以只需要打包入口文件就好了
webpack ./src/main.js ./dist/bundle.js

webpack的配置

配置webpack讓項目在運行的時候,直接webpack就可以運行項目,不需要webpack ./src/main.js ./dist/bundle.js這樣運行

步驟:

  1. 在項目文件中創建webpack.config.js文件

  2. 給webpack.config.js添加配置

    // path 依賴了node中的path包,所以要安裝node中的所有依賴項
    // npm init --yes  安裝所有依賴包
    // 就會去全局找path
    cost path = require('path');
    
    module.exports = {
    	// 入口
    	entry: './src/main/.js',
    	// 出口
    	output: {
    		// 路徑  path  是絕對路徑
    		// 動態獲取路徑
    		path: path.resolve(__dirname, 'dist'),
    		// 文件名   bundle  打包
    		filename: 'bundle.js'
    	}
    }
    
  3. pacjage.json中自定義啓動

    爲了在命令行工具中能夠 通過 npm run build 運行項目,在package.json文件的script腳本中添加依賴項

    讓我們在執行npm run build命令的時候來package.json文件中找到webpack

    當在命令行工具中執行npm run build的時候相當於執行webpack命令,運行的時候num run build會先去查找本地的webpack,這樣防止因webpack版本的問題而引發錯誤

    "build": "webpack"
    

    通過npm run build打包後的文件是全局的

loader的使用

  • loader是webpack中一個非常核心的概念

  • webpack用來做什麼呢?

    • 在我們之前的實例中,我們主要是用webpack來處理我們寫的js代碼,並且webpack會自動處理js之間相關的依賴
    • 但是,在開發中我們不僅僅有基本的js代碼處理,我們也需要加載css,圖片,也包括一些高級的將ES6轉成ES5的代碼,將scss,less轉成css,將.jsx,vue文件轉成js文件等等。
    • 對於webpack本身的能力來說,對於這些轉化是不支持的
    • 那怎麼辦呢?給webpack擴展對應的loader就可以了。
  • loader使用過程:

    • 步驟一:通過npm安裝需要使用的loader

      • css-loader

        npm install --save-dev css-loader
        
      • style-loader

        npm install --save-dev style-loader
        
    • 步驟二:在webpack.config.js中的modules關鍵字下進行配置

      在module中css-loader只負責將css文件進行加載,style-loader將樣式添加到DOM中

      使用多個loader時,是從右向左

      module: {
          rules: [
              {
                  test: /\.css$/
                  use: ['style-loader', 'css-loader']
              }
          ]
      }
      
    • 步驟三:配置,也就是再次打包,npm run build

less文件處理–準備

要想使用less文件更改樣式要安裝less-loader:

npm install --save-dev less-loader less

在webpack.config.js文件中配置less:

// webpack.config.js
module.exports = {
    ...
    module: {
        rules: [{
            test: /\.less$/,
            use: [{
                loader: "style-loader" // creates style nodes from JS strings
            }, {
                loader: "css-loader" // translates CSS into CommonJS
            }, {
                loader: "less-loader" // compiles Less to CSS
            }]
        }]
    }
};

圖片配置

下載url-loader:

npm install --save-dev url-loader

配置url-loader:

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              // 當加載的圖片,小於limit時,會將圖片編譯成base64字符串形式
              // 當加載的圖片,大於limit時,會使用file-loader模塊進行加載,通過webpack打包後會編譯成base64字符串的形式
              limit: 8192	// 8192 8kb
              
              // 打包後webpack自動幫我們生成一個非常長的名字,但真是開發中,我們可能對打包圖片的名字有一定的要求,比圖將所有的圖片放在一個文件夾中,跟圖片原來的名稱,同時也要防止重複
               // [name]:圖片原來的名字   [hash:8]:哈希值保留8位  [ext]:圖片的擴展名
               name: 'img/[name].[hash:8].[ext]'
            }
          }
        ]
      }
    ]
  }
}

安裝file-loader:

npm install --save-dev file-loader

安裝後,文檔中使用的圖片是打包後dist文件夾中的圖片,

在webpack.config.js文件配置圖片使用的路徑:

在output中加入publicPath

output: {
    publicPath: 'dist/'
}

ES6語法處理

ES6的語法直接轉成ES5,需要使用babel。

在webpack中直接使用babel對應的loader就可以了。

安裝:

npm install --save-dev babel-loader@7 babel-core babel-preset-es2015

配置webpack.config.js文件:

env:environment環境的縮寫。

module: {
  rules: [
    {
      test: /\.js$/,
      // exclude:排除
      // include:包含
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['es2015']
        }
      }
    }
  ]
}

webpack中配置Vue

  • 在項目中使用vue.js進行開發,那麼必須對其有依賴,所以我們先要進行安裝:

    --save dev是開發時依賴,因爲我們後續是在實際項目中使用vue的,所以並不是開發時依賴,使用--save

    npm install vue --save
    
    // 簡寫
    npm install vue -S
    
  • 使用vue進行開發

    // 導入vue
    import Vue from 'vue'
    
    //使用
    new Vue({
        el: '#app',
        ...
    })
    
  • vue在發佈的時候,發佈了兩個版本

    • runtime-only

      • 代碼中,不可以有任何的template
    • runtime-complier

      • 代碼中,可以有template,因爲有complier可以用於編譯template
    • 要解決這個問題,只需要在webpack.config.js中配置

      resolve: {
          // 文件引入時省略後綴名配置
          extensions: ['.js', '.css', '.vue'],
          // alias 別名
          alias: {
              // 如果導入vue後,去vue文件中去找vue.esm.js文件,這個文件中包含了complier
              'vue$':'vue/dist/vue.esm.js'
          }
      }
      

el和template的區別,及vue文件的封裝

  1. template在解析的時候會替換el,再去修改html中的代碼

  2. 但是如果將所有替換的代碼都寫在template裏面,看着就很感覺很複雜,我們可以將template中的信息提取成一個組件

    // ------------------------------2.app.js文件------------------------------------
    // App對象中的代碼可以封裝到一個app.js文件裏面
    export default {
        template: 
        `
    		<div>
    			....
    		</div>
    	`,
        data() {
            return {
                
            }
        }
        ...   // 將所有的內容抽取到一個組件裏面使用
    }
    
    // -----------------------------3.創建App.vue文件--------------------------------
    //讓模板於組件分離
    <template>
        <div>
    		....
    	</div>
    </template>
    
    <script>
        export default {
    		name: "App",
            data() {
           		return {
                
            	}
        	}
    		...
    	}
    </script>
    
    <style>
        //樣式
    </style>
    
    
    // ------------------------------1.main.js文件------------------------------------
    //const App = {
    //    template: 
    //    `
    //		<div>
    //			....
    //		</div>
    //	`,
    //    data() {
    //        return {
                
    //        }
    //    }
    //    ...   // 將所有的內容抽取到一個組件裏面使用
    //}
    
    
    // 引入App組件內容
    //import App from './vue/app'
    import App from './vue/App.vue'
    new Vue({
        el: '#app',
        template: '<App />',
        components: {
            App	// 在組件裏面註冊App
        }
    })
    

    但是在編譯過程中不能識別App.vue文件,所以我們需要對vue文件進行一個封裝,就要使用vue-loadervue-template-complier

    安裝:

    vue-loader讓vue文件進行一個加載,vue-template-complier 對vue文件進行編譯

    npm install vue-loader vue-template-complier --save-dev
    

    配置:

    {
        test: /\.vue$/,
        use: ['vue-loader']
    }
    

    因爲vue-loader版本的原因,可能會報錯,說vue-loader was used without the corresponding plugin,在package.json文件中將vue-loader的版本改成^13.0.0(13到14版本之間),然後在命令行工具中執行npm install重新安裝一下

plugin

  • plugin是什麼?
    • plugin是插件的意思,通常是用於對某個現有的框架的進行擴展
    • webpack中的插件,就是對webpack現有功能的各種擴展,比如打包優化,文件壓縮等。
  • loader和plugin的區別:
    • loader主要用於轉換某些類型的模塊,它是一個轉換器
    • plugin是插件,它是對webpack本身的一個擴展,是一個擴展器。
  • plugin的使用過程:
    • 通過npm安裝需要使用的plugins(某些webpack已經內置的插件不需要安裝)
    • 在webpack.config.js中的plugins中配置插件

添加版權的plugin

BannerPlugin,屬於webpack自帶的插件。

修改webpack.config.js的文件:

const path = require('path');
//導入webpack
const webpack = require('webpack');

modeule.exports = {
   ...
   plugins: [
        new webpack.BannerPlugin('最終版權歸aaa所有')
   ]
}

重新打包:

npm run build/webpack

打包html的plugin

  • 目前,我們的index.html頁面存放在項目的根目錄下

    • 在真實發布項目的時候,發佈的是dist文件夾中的內容,但是dist文件夾中如果沒有index.html文件,那麼打包的js文件也就沒有意義了
    • 所以我們需要將index.html文件打包到dist文件夾中,這個時候就可以使用HtmlWebpackPlugin插件
  • HtmlWebpackPlugin插件可以:

    • 自動生成一個index.html文件(可以指定模板來生成)
    • 將打包的js文件,自動通過script標籤插入到body中
  • 安裝HtmlWebpackPlugin插件

    npm install html-webpack-plugin --save-dev
    
  • 使用插件,修改webpack.config.js文件中plugins部分的內容

    • 這裏的template表示根據什麼模板來生成index.html

    • 另外我們需要刪除之前在output中添加的publicPath屬性

    • 否則插入的script標籤中的src可能會有問題

      const path = require('path');
      //導入webpack
      const webpack = require('webpack');
      // 導入HtmlWebpackPlugin插件
      const HtmlWebpackPlugin = require('html-webpack-plugin');
      
      modeule.exports = {
         ...
         plugins: [
              new webpack.BannerPlugin('最終版權歸aaa所有'),
             	// 這樣使用之後,就會在文件中的dist文件夾下面生成index.html文件
              new HtmlWebpackPlugin({
                  // 讓template根據index.html文件來生成index.html
                  template: 'index.html'
              })
         ]
      }
      

js壓縮的Plugin

  • 在項目發佈之前,我們必須要對js等文件進行壓縮處理

    • 對js文件進行壓縮,使用的是第三方插件unlifyjs-webpack-plugin,並且版本號指定1.1.1,和cli2保持一致

      npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
      
  • 修改webpack.config.js文件,使用插件:

    const path = require('path');
    //導入webpack
    const webpack = require('webpack');
    // 導入unlifyjs-webpack-plugin插件
    // 在開發階段不建議使用uglifyJsPlugin插件,代碼在修改後不宜調試
    const uglifyJsPlugin = require('unlifyjs-webpack-plugin');
    
    module.exports = {
       ...
       plugins: [
            new webpack.BannerPlugin('最終版權歸aaa所有'),
           	new uglifyJsPlugin()
       ]
    }
    
  • 查看打包後的bunlde.js文件,最後已經被壓縮過了

搭建本地服務器

  • webpack提供了一個可選的本地服務器,這個本地服務器基於node.js搭建,內部使用express框架,可實現瀏覽器自動刷新顯示修改後的結果。

  • 不過它是一個單獨的模塊,在webpack中使用之前需要先安裝

    npm install --save-dev webpack-dev-server@2.9.1
    
  • devserver也是作爲webpack中的一個選項,選項本身可以設置以下屬性:

    • contentBase:爲那個文件提供本地服務,默認是根文件夾,我們這裏要填寫./dist
    • port: 端口號
    • inline:頁面實時刷新
    • historyApiFallback:在SPA頁面中,依賴HTML5的history模式
  • webpack.config.js文件配置修改:

    devServer: {
        contentBase: './dist',
        inline: true
    }
    
  • 我們還可以配置package.json中的script:

    • –open參數標識直接打開瀏覽器

      "build": "webpack"
      "dev": "webpack-dev-server --open"
      

配置完成後運行:

​ 在命令窗口通過npm run dev運行

配置(webpack.config.js)結構分離

將開發時依賴和發佈時依賴分離。

  • 在主文件夾下創建build文件夾,在build文件夾下面創建base.config.js(公共),prod.config.js(開發時依賴包)和dev.config.js(運行時依賴)

  • 安裝webpack-merge,將分離的文件進行合併

    npm install webpack-merge --save-dev
    
  • 使用

    base.config.js中:

    const path = require('path');
    //導入webpack
    const webpack = require('webpack');
    
    modeule.exports = {
    	// 入口
    	entry: './src/main/.js',
    	// 出口
    	output: {
    		// 路徑  path  是絕對路徑
    		// 動態獲取路徑
    		path: path.resolve(__dirname, 'dist'),
    		// 文件名   bundle  打包
    		filename: 'bundle.js'
    	},
    	module: {
    		rules: [{
    			test: /\.css$/
    			use: ['style-loader', 'css-loader']
    		}]
    	},
    	plugins: [
    		new webpack.BannerPlugin('最終版權歸aaa所有')
    		new HtmlWebpackPlugin({
    			// 讓template根據index.html文件來生成index.html
    			template: 'index.html'
    		})
    	]
    }
    

    prod.config.js中:

    const uglifyJsPlugin = require('unlifyjs-webpack-plugin');
    
    // 導入webpack-merge
    const webpackMerage = require('webpack-merge');
    
    //拿到baseConfig 
    const baseConfig = require('./base.config');
    
    //將config文件合併導出
    module.exports = webpackMerage(baseConfig, {
       ...
       plugins: [
            new webpack.BannerPlugin('最終版權歸aaa所有'),
           	new uglifyJsPlugin()
       ]
    })
    

    dev.config.js中:

    // 導入webpack-merge
    const webpackMerage = require('webpack-merge');
    
    //拿到baseConfig 
    const baseConfig = require('./base.config');
    
    module.exports = webpackMerage(baseConfig,{
        devServer: {
        	contentBase: './dist',
        	inline: true
    	}
    })
    

    最後在使用時候,將package.json文件中的build改爲:

    更改一下config的指向:

    "build": "webpack --config ./build/prod.config.js",
    "dev": "webpack-dev-server --open --config ./build/dev.config.js"
    

    然後使用:

    run run dev
    

    但是這樣打包後的文件在build文件下面生成的dist文件夾裏面,爲了解決這個問題,就將base.config.js文件中output下的path改爲:

    path: path.resolve(__dirname, '../dist')
    

Vue cli腳手架詳解

什麼是Vue CLI

  • CLI是Command-Line Interface,翻譯爲命令行界面,俗稱腳手架
  • Vue CLI是官方發佈的vue.js項目腳手架
  • 使用vue-cli可以快速搭建Vue開發環境及對應的webpack配置

Vue CLI使用前提-Node

腳手架應用於webpack,webpack基於node。

Vue CLI的使用

  • 安裝Vue腳手架,-g(global:全局的)

    npm install -g @vue/cli
    
  • 注意:上面安裝的是Vue CLI3的版本,如果需要想按照Vue CLI2的方式初始化項目是不可以的。

    安裝Vue CLI2:

    npm install -g @vue/cli-init
    
  • 安裝完成後,查看是否安裝成功:

    vue --version
    
  • Vue CLI2初始化項目:

    • vue init webpack my-project(項目名稱)

在這裏插入圖片描述

  • Vue CLI3初始化項目

    • vue create my-project

      chrome中的 v8引擎(使用c++實現,v8是開源的),將js代碼直接編譯成二進制代碼,在瀏覽器中執行

      js -》 瀏覽器

      js -》字節碼 -》瀏覽器

    在這裏插入圖片描述

視頻材料鏈接: https://pan.baidu.com/s/1dxV3TSKpHoFhZo-oM3stFg
提取碼: epuk

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章