[TOC] Vue Vue是一個輕量級的前端框架,漸進式Javascript框架,它是當下國內很火的一個Javascript MVVM庫,它是以數據驅動和組件化的思想構建的。 相比較於Jquery的DOM操作,Vue是數據驅動的,不需要再通過獲取標簽對象在進行操作。通過一些特殊的語法,把DOM和數據 ...
目錄
Vue
Vue是一個輕量級的前端框架,漸進式Javascript框架,它是當下國內很火的一個Javascript MVVM庫,它是以數據驅動和組件化的思想構建的。
相比較於Jquery的DOM操作,Vue是數據驅動的,不需要再通過獲取標簽對象在進行操作。通過一些特殊的語法,把DOM和數據綁定在一起,一旦你創建了綁定,DOM將和數據保持同步,每當數據發生變化,DOM也會相應的更新。
Vue管理頁面很強大,它可以控制一個頁面的一個標簽,也可以控制一個頁面,甚至控制一個項目,我們只需要把一個項目的頁面一次性傳到前臺,不需要的可以先保存在客戶端的記憶體緩存中,當需要的時候在進行載入
Vue的優點
- 綜合了Angular和React的優點,上手快(一手文檔是中文),完全開源免費
- 單頁面的Web運用,服務於移動端,只需要向後臺請求數據就可以了
- MVVM設計模式
- 數據驅動,在緩存中根據數據處理DOM,再渲染給真實DOM
- 頁面緩存機制--虛擬DOM
- 數據的雙向綁定
MVVM模式
MVVM------Model-View-ViewModel
下圖描述了Vue中ViewModel是如何和View以及Model進行交互的。
ViewModel是Vue.js的核心,它是一個Vue實例。Vue實例是作用於某一個HTML元素上的,這個元素可以是HTML的body元素,也可以是指定了id的某個元素。
當創建了ViewModel後,雙向綁定是如何達成的呢?
首先,我們將上圖中的DOM Listeners和Data Bindings看作兩個工具,它們是實現雙向綁定的關鍵。
從View側看,ViewModel中的DOM Listeners工具會幫我們監測頁面上DOM元素的變化,如果有變化,則更改Model中的數據;
從Model側看,當我們更新Model中的數據時,Data Bindings工具會幫我們更新頁面中的DOM元素。
Hello World示例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World示例</title>
</head>
<body>
<!--這是我們的View-->
<div id="app">
{{ message }}
</div>
</body>
<script src="js/vue.js"></script>
<script>
// 這是我們的Model
var exampleData = {
message: 'Hello World!'
}
// 創建一個 Vue 實例或 "ViewModel"
// 它連接 View 與 Model
new Vue({
el: '#app',
data: exampleData
})
</script>
</html>
使用Vue的過程就是定義MVVM各個組成部分的過程的過程
- 定義View
- 定義Model
- 創建一個Vue(ViewModel)實例,用於連接View和Model
掛載點
在上述示例中,通過掛載點進行Vue實例綁定,進行了頁面綁定
選項對象的el屬性指向View,el:"#app"
表示該Vue實例掛載到<div id="app">...</div>
這個元素,data屬性指向Model,data: exampleData
表示我們的Model是exampleData對象。
Vue掛載點的註意事項:
- 掛載點只遍歷第一個匹配的結果
- html與body標簽不可以作為掛載點
- 掛載點的只一般就採用id選擇器(唯一性)
插值表達式與過濾器
在Vue中有很多種數據綁定方式,最基礎的形式是文本插值,使用一對{{ }}
包裹變數,如運行{{ message }}
會被數據對象的message屬性替換,所以頁面會輸出Hello World!
<div id="app">
<!-- 插值表達式 -->
<!-- 可以進行數據處理 -->
<h1>{{ msg.split('')[0] }}</h1>
<h2>{{ info + msg }}</h2>
<h3>{{ num * num }}</h3>
<!--num作為過濾器的參數,過濾器的返回值就是表達式的值-->
<h4>{{ num | my_filter }}</h4>
<!--過濾器f1被傳遞了四個參數a,b,c,d,並將過濾結果做出f2的參數再過濾-->
<h4>{{ a, b | f1(c, d) | f2 }}</h4>
<h4>{{ arr | f3 }}</h4>
</div>
<script src="js/vue.js"></script>
<script>
// 創建過濾器
Vue.filter('my_filter', function (v) {
console.log(v);
return 999
});
Vue.filter('f1', function (a, b, c, d) {
console.log(a, b, c, d);
// return '過濾後的邏輯結果'
return a + b + c + d
});
Vue.filter('f2', function (v) {
console.log(v);
return v * v
});
Vue.filter('f3', function (v) {
let new_arr = [];
for (n of v) {
if (n >= 60) {
new_arr.push(n)
}
}
return new_arr
});
// 產生Vue實例對象
new Vue({
el: '#app',
// data成員用來為vue控制的變數提供值
data: {
msg: 'message',
info: '信息',
num: 10,
a: 1,
b: 2,
c: 3,
d: 4,
arr: [23, 59, 62, 97]
}
})
</script>
Vue指令
- 文本指令
- 屬性指令
- 事件指令
- 表單指令
- 條件指令
文本指令
格式:
{{ 變數表達式 }}
:渲染變數v-text='變數表達式'
:渲染變數v-html='html標簽'
:渲染可被解析的html標簽v-once='被限制的變數'
:內容還是通過上面三種進行渲染, 如果內容中包括被限制的變數
,則渲染後的內容不會發生變化
<div id="app">
<!-- 文本指令 -->
<h2 v-text="msg + '!!!'"></h2>
<h2 v-text="htm"></h2>
<h2 v-html="htm"></h2>
<input type="text" v-model="msg">
<h3>{{ msg }}</h3>
<!--一次性渲染,插值表達式中的任何一個變數被限制,整個結果就不可變-->
<h3 v-once="htm">{{ msg + htm }}</h3>
<!-- 只要有v-once,這個標簽渲染完成後就不會發生變化 -->
<h3 v-once>{{ msg }}</h3>
</div>
<script src="js/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
msg: 'message',
htm: '<i>標簽內容是否被解析</i>'
}
})
</script>
屬性指令
格式:v-bind:屬性名='屬性值'
,可以簡寫成 :屬性名='屬性值'
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>屬性指令 - 控制樣式</title>
<style>
.div {
width: 200px;
height: 200px;
background-color: red;
}
.box {
width: 200px;
height: 200px;
}
.blue {
background-color: blue;
}
.green {
background-color: green;
}
</style>
</head>
<body>
<div id="app">
<div class="div" style="border-radius: 50%"></div>
<!--屬性指令:v-bind:屬性名="屬性值" => v-bind: 可以簡寫為 :
eg: v-bind:style="{color: 'red'}"
-->
<!--自定義屬性:沒有太多應用場景-->
<!--只是普通的自定義屬性-->
<div abc="xyz"></div>
<!-- 通過Vue進行變數替換 -->
<div v-bind:abc="xyz"></div>
<!--title屬性-->
<div :title="xyz" class="div" style="border-radius: 50%"></div>
<!--style屬性-->
<!--1)變數:變數的值為字典-->
<div :style="my_style"></div>
<!--2)字典中的多個變數-->
<div :style="{width: w, height: h, background: b}"></div>
<!--class屬性-->
<!--<div class="box blue"></div>-->
<div :class="c"></div>
<div :class="[c1, c2]"></div>
<div :class="[c1, 'blue']"></div>
<!--x為類名,是否生效有變數y(true|false)值決定-->
<!-- x只能是類名,不可以通過data進行變數替換 -->
<div :class="{x: y}"></div>
<div :class="[{'box': true}, c2]"></div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
xyz: 'ABC',
my_style: {
width: '100px',
height: '100px',
'background-color': 'cyan',
borderRadius: '50%'
},
w: '50px',
h: '50px',
b: 'red',
c: 'box blue',
c1: 'box',
c2: 'green',
y: true,
}
})
</script>
</html>
事件指令
格式:v-on:指令="fn變數"
,可以簡寫成 @指令="fn變數"
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>事件指令</title>
<style>
.div1 {
width: 200px;
height: 200px;
background-color: red;
}
/*沒有vue,不顯示,有vue,會移除該屬性 => 沒有閃爍的顯示內容*/
[v-cloak] {
display: none;
}
div {
width: 50px;
height: 50px;
background-color: red;
border-radius: 50%;
text-align: center;
line-height: 50px;
color: white;
cursor: pointer;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<!--事件指令 v-on:事件名="fn變數" => v-on: 可以簡寫為 @
v-on:click="clickAction"
@dblclick="dblclickAction"
-->
<!--內容操作:每點一次,內容+1-->
<h2 v-once="num">{{ num }}</h2>
<h2>{{ num }}</h2>
<div v-on:click="clickAction" class="div1">{{ num }}</div>
<hr>
<!--樣式操作:點擊切換背景顏色-->
<div @click="changeColor" class="div1" :style="{backgroundColor: bgColor}"></div>
<!--樣式操作:點擊切換整體樣式-->
<div @click="changeStyle" :style="my_style"></div>
<!--沒有傳值預設傳 事件對象 -->
<div @click="btnClick1">{{ msg }}</div>
<!--方法()不會直接調用方法,而是在點擊觸發後進行傳參,接收到的參數就是傳入的參數-->
<div @click="btnClick2(1, msg)">{{ msg }}</div>
<!--一旦書寫 方法() 就不再傳入事件對象,通過 $event 手動傳入事件對象-->
<div @click="btnClick3(msg, $event, $event)">{{ msg }}</div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
num: 100,
bgColor: 'cyan',
my_style: {
width: '100px',
height: '100px',
backgroundColor: 'orange'
},
msg: 'box'
},
methods: {
clickAction: function () {
// console.log(app.num);
// console.log(this.num);
this.num ++
},
changeColor () { // 方法的寫法
// if (this.bgColor == 'cyan') {
// this.bgColor = 'blue'
// } else {
// this.bgColor = 'cyan'
// }
// python:this.bgColor = 'cyan' if this.bgColor != 'cyan' else 'blue'
this.bgColor = this.bgColor != 'cyan' ? 'cyan' : 'blue'
},
changeStyle: () => { // 這種寫法,內部拿不到this(指向的是window)
app.my_style = app.my_style.backgroundColor == 'orange' ?
{
width: '200px',
height: '200px',
backgroundColor: 'yellow'
}
:
{
width: '100px',
height: '100px',
backgroundColor: 'orange'
}
},
btnClick1(ev) {
console.log(ev);
console.log(ev.clientX);
},
btnClick2(a, b, c) {
console.log(a, b, c)
},
btnClick3(a, b, c) {
console.log(a, b, c)
},
}
});
// 外界訪問實例內部的數據
console.log(app);
console.log(app.$el);
console.log(app.$data.num);
console.log(app.num);
</script>
</html>
表單指令
格式:v-model="變數"
:無簡寫方式
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>表單指令</title>
</head>
<body>
<div id="app">
<form action="">
<!--表單指令:v-model="變數"-->
<!--雙向綁定:一個地方修改值,所有地方的數據都會被更新-->
<div>
<input type="text" v-model="info" name="usr">
<input type="password" v-model="info" name="pwd">
<p>{{ info | infoFilter }}</p>
</div>
<div>
<!--單選框:v-model="變數存放的是某個單選框的value值,代表該選框選中"-->
男<input type="radio" name="sex" value="male" v-model="sex_val">
女<input type="radio" name="sex" value="female" v-model="sex_val">
</div>
<div>
<!--單獨的覆選框:v-model="true|false代表該選框是否選中"-->
是否同意<input v-model="cb_val" value="yes" type="checkbox" name="agree">
</div>
<div>
<!--群覆選框:v-model="存放選中選框value的數組"-->
男<input v-model="cbs_val" value="male" type="checkbox" name="hobby">
女<input v-model="cbs_val" value="female" type="checkbox" name="hobby">
哇塞<input v-model="cbs_val" value="others" type="checkbox" name="hobby">
<p>{{ cbs_val }}</p>
</div>
<div>
<input type="submit">
</div>
</form>
</div>
</body>
<script src="js/vue.js"></script>
<script>
Vue.filter('infoFilter', (info) => {
return info ? info : '初始內容'
});
new Vue({
el: '#app',
data: {
info: '',
// 預設值可以決定單選框預設選項
sex_val: 'female',
// 預設值為true,單一覆選框為選中,反之false為不選中
cb_val: 0,
// 數組中存在的值對應的覆選框預設為選中狀態
cbs_val: ["others"]
}
})
</script>
</html>
條件指令
格式:
v-if="true|false"
:條件為true
時顯示內容,且屏蔽下方分支為false
時隱藏不渲染到頁面v-else-if="true|false"
:v-if
不成立時才看v-else-if
、v-else-if
成立會屏蔽下方分支v-else="true|false"
:v-if
、v-else-if
都不成立,v-else
才成立v-show="true|false"
:條件為true
時顯示內容,為false
時隱藏渲染到頁面,但是display:none
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.btn1 { width: 400px; }
.box {
width: 200px;
height: 200px;
float: left;
}
.wrap:after {
content: '';
display: block;
clear: both;
}
.red { background-color: red }
.blue { background-color: blue }
</style>
<style>
.btn-wrap { height: 25px }
.btn {
width: 100px;
height: 25px;
float: left;
}
.page {
width: 300px;
height: 150px;
}
.p1 { background-color: pink }
.p2 { background-color: yellow }
.p3 { background-color: green }
</style>
</head>
<body>
<div id="app">
<!--條件指令
v-if="true|false"
v-show="true|false"
-->
<button class="btn1" @click="btnClick">{{ title }}</button>
<div class="wrap">
<!--v-if隱藏時不渲染,v-show隱藏時用display:none渲染-->
<!--v-if隱藏時在記憶體中建立緩存,可以通過key屬性設置緩存的鍵-->
<div class="box red" v-if="is_show" key="box_red"></div>
<div class="box blue" v-show="is_show"></div>
</div>
<div class="btn-wrap">
<button class="btn" @click="changePage('pink')">粉</button>
<button class="btn" @click="changePage('yellow')">黃</button>
<button class="btn" @click="changePage('green')">綠</button>
</div>
<div>
<!--v-if成立會屏蔽下方分支、v-else-if一樣f成立會屏蔽下方分支-->
<!--v-if不成立時才看v-else-if、v-else-if成立會屏蔽下方分支-->
<!--v-if、v-else-if都不成立,v-else才成立-->
<div class="page p1" v-if="page == 'pink'"></div>
<div class="page p2" v-else-if="page == 'yellow'"></div>
<div class="page p3" v-else></div>
</div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
title: '隱藏',
is_show: true,
page: localStorage.page || 'pink'
},
methods: {
btnClick() {
this.title = this.title == '隱藏' ? '顯示' : '隱藏';
this.is_show = !this.is_show;
},
changePage(page) {
this.page = page;
localStorage.page = page; // 永久緩存
// sessionStorage.page = page; // 臨時緩存
}
}
})
</script>
</html>
pre指令
格式:v-pre
,直接放在標簽中就會生效,只要標簽中有v-pre
指令,表示該標簽及其子標簽都會解除Vue控制
<div id="app">
<p v-pre>
<!--v-pre的內部解除vue控制,都是原義輸出-->
<!-- 頁面顯示:{{ msg }},此時{{ }}不是插值表達式 -->
<p>{{ msg }}</p>
<!-- html解析為:<span v-on="abc"></span>,此時的v-on不是指令,abc也不是變數,不會報錯 -->
<span v-on="abc"></span>
</p>
</div>
<script>
new Vue({
el: '#app',
data: {
msg: "message"
}
})
</script>
迴圈指令
格式:
- 列表的遍歷:
v-for="(值[, 索引] in 列表"
- 字典的遍歷:
v-for="(值[, 鍵[, 索引]]) in 字典"
<div id="app">
<!--列表的迴圈-->
<div>
<!--只取 值-->
<p p v-for="i in arr"> 值:{{ i }} </p>
<hr>
<!--只取 值和索引-->
<p v-for="(i, ind) in arr">第{{ ind + 1}}個值:{{ i }}</p>
<hr>
</div>
<!--字典的迴圈-->
<div>
<!--取value值-->
<p v-for="v in stu">
{{ v }}
</p>
<hr>
<!--取value,key值-->
<p v-for="(v, k) in stu">
{{ k }}: {{ v }}
</p>
<hr>
<!--取value,key,索引值-->
<p v-for="(v, k, ind) in stu">
{{ k, v, ind | f1 }}
</p>
<hr>
</div>
<!--列表套字典,迴圈遍歷-->
<div>
<!--以表格的形式呈現-->
<table border="1px" cellspacing="0">
<tbody>
<tr>
<th>項次</th>
<th>姓名</th>
<th>年齡</th>
<th>性別</th>
</tr>
<tr v-for="(stu, ind) in stus">
<td>{{ ind + 1 }}</td>
<td v-for="v in stu">{{ v }}</td>
</tr>
</tbody>
</table>
<hr>
<!--取出所有的值,鍵,索引-->
<!--以如下形式顯示
name: hades | age:27| gender:男
name: Tom | age:13 | gender:男
-->
<p v-for="stu in stus">
<span v-for="(v, k, ind) in stu">
<!--{{i?' | ' : ''}}{{k}}:{{v}}-->
{{ ind | f2 }}{{ k }}: {{ v }}
</span>
</p>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
arr: [1, 3, 5, 7, 4],
stu: {
'name': 'hades',
'age': 27,
'gender': 'mela'
},
stus: [
{
'name': 'hades',
'age': 26,
'gender': 'male'
},
{
'name': 'tom',
'age': 13,
'gender': 'male'
},
{
'name': 'jerry',
'age': 18,
'gender': 'female'
},
{
'name': 'bonnie',
'age': 16,
'gender': 'female'
}
]
},
filters: {
f1(k, v, ind) {
return k + ':' + v + '(' + ind + ')';
// return `${k}:${v}(${ind})`
},
f2(i) {
// i 為0 則為假,返回'',否則返回' | '
return i ? ' | ' : ''
}
}
})
</script>
案例
需求:一個評論框,用戶輸入評論後,把值顯示在評論框下方,同時清空評論框,當用戶點擊評論時,刪除評論
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
<style>
/*滑鼠懸浮字體變紅,游標變為小手爪*/
p:hover {
color: red;
cursor: pointer;
}
</style>
</head>
<body>
<div id="app">
<input type="text" v-model="info">
<button @click="addMsg">提交</button>
<p v-for="(msg, i) in msgs" @click="delMsg(i)">
<span>#{{ i+ 1 }}樓 </span>{{ msg }}
</p>
</div>
</body>
<script src="js/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
info: '',
msgs: JSON.parse(sessionStorage.msgs || '[]'),
},
methods: {
addMsg() {
let info = this.info;
// console.log(info);
if (info) {
// this.msgs.push(this.info); // 尾增
this.msgs.unshift(info); // 首增
// 內容提交完成後,清空輸入框內容
this.info = '';
// 保存在前臺,當頁面關閉後再次打開,數據不會發生變化
sessionStorage.msgs = JSON.stringify(this.msgs)
}
},
delMsg(i) {
//splice(開始的位置,刪除的個數,增加的值(可以是多個值))
this.msgs.splice(i, 1);
sessionStorage.msgs = JSON.stringify(this.msgs)
}
}
})
</script>
</html>