1. 組件
- 組件 (Component) 是 Vue.js 最強大的功能之一
- 組件可以擴展 HTML 元素乌奇,封裝可重用的代碼
1.1 組件注冊
全局注冊
- Vue.component('組件名稱', { })
- 全局組件注冊后,任何vue實例都可以用
<div id="example">
<!-- 2、 組件使用 組件名稱 是以HTML標簽的形式使用 -->
<my-component></my-component>
<my-component></my-component>
<my-component></my-component>
</div>
<script>
// 注冊組件
// 1借跪、 my-component 就是組件中自定義的標簽名
Vue.component('my-component', {
data: function() {
return {
count: 0
}
}
template: '<div>{{count}}</div>'
})
// 創(chuàng)建根實例
new Vue({
el: '// example'
})
</script>
注意:
- data值必須是一個函數(shù),同時這個函數(shù)要求返回一個對象
- 組件模板必須是單個根元素
- 組件模板的內(nèi)容可以是模板字符串(es6語法张峰,需要瀏覽器提供支持)
<div id="app">
<!--
4恍风、 組件可以重復(fù)使用多次
因為data中返回的是一個對象,所以每個組件中的數(shù)據(jù)是私有的
即每個實例可以維護一份被返回對象的獨立的拷貝
-->
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<!--必須使用短橫線的方式使用駝峰式命名組件 -->
<hello-world></hello-world>
</div>
<script type="text/javascript">
//(1)如果使用駝峰式命名組件磨总,那么在使用組件的時候嗦明,只能在字符串模板中用駝峰的方式使用組件,
//(2)但是在普通的標簽?zāi)0逯序窖啵仨毷褂枚虣M線方式使用組件(1)
Vue.component('HelloWorld', {
data: function(){
return {
msg: 'HelloWorld'
}
},
template: '<div>{{msg}}</div>'
});
Vue.component('button-counter', {
// 1娶牌、組件參數(shù)的data值必須是函數(shù)
// 同時這個函數(shù)要求返回一個對象
data: function(){
return {
count: 0
}
},
// 2、組件模板必須是單個根元素
// 3馆纳、組件模板的內(nèi)容可以是模板字符串
template: `
<div>
<button @click="handle">點擊了{{count}}次</button>
<button>測試123</button>
// (2)在字符串模板中可以使用駝峰的方式使用組件
<HelloWorld></HelloWorld>
</div>
`,
methods: {
handle: function(){
this.count += 2;
}
}
})
var vm = new Vue({
el: '// app',
data: {
}
});
</script>
補充:組件命名方式有短橫線“ - ”和駝峰式2種
局部注冊
- 只能在當(dāng)前注冊它的vue實例中使用
<div id="app">
<my-component></my-component>
</div>
<script>
// 定義組件的模板
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
//局部注冊組件
components: {
// <my-component> 將只在父模板可用 一定要在實例上注冊了才能在html文件中使用
'my-component': Child
}
})
</script>
1.2 Vue組件之間傳值
父組件向子組件傳值
- 以屬性的形式綁定值到子組件身上诗良,一種是靜態(tài)的值,一種是動態(tài)綁定(:)
- 子組件用屬性props鲁驶,以數(shù)組方式接收(單向數(shù)據(jù)傳送)
- 在props中使用駝峰形式鉴裹,模板中需要使用短橫線的形式(html不區(qū)分大小寫)字符串形式的模板中沒有這個限制
<div id="app">
<div>{{pmsg}}</div>
<!--1、menu-item在APP中嵌套著 故menu-item為子組件 -->
<!-- 給子組件傳入一個靜態(tài)的值钥弯,類型為字符串 -->
<menu-item title='來自父組件的值'></menu-item>
<!-- 2径荔、 需要動態(tài)的數(shù)據(jù)的時候 需要屬性綁定的形式 此時ptitle是父組件data中的數(shù)據(jù)(類型不變).
傳的值可以是數(shù)字、布爾脆霎、字符串总处、對象、數(shù)組等等
html不區(qū)分大小寫,屬性用短橫線的形式
-->
<menu-item :title='ptitle' my-content='hello'></menu-item>
</div>
<script type="text/javascript">
Vue.component('menu-item', {
// 3睛蛛、 子組件用屬性props接收父組件傳遞過來的數(shù)據(jù)
props: ['title', 'myContent'],
data: function() {
return {
msg: '子組件本身的數(shù)據(jù)'
}
},
template: '<div>{{msg + "----" + title + "-----" + myContent}}</div>'
});
var vm = new Vue({
el: '// app',
data: {
pmsg: '父組件中內(nèi)容',
ptitle: '動態(tài)綁定屬性'
}
});
</script>
子組件向父組件傳值
- 子組件用
$emit()
觸發(fā)事件 -
$emit()
第一個參數(shù)為 自定義的事件名稱 第二個參數(shù)為需要傳遞的數(shù)據(jù) - 父組件用v-on 監(jiān)聽子組件的事件
<div id="app">
<div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
<!-- 2 父組件用v-on 監(jiān)聽子組件的事件
這里 enlarge-text是從$emit中的第一個參數(shù)對應(yīng) handle 為對應(yīng)的事件處理函數(shù)
-->
<menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
子組件向父組件傳值-攜帶參數(shù)
*/
Vue.component('menu-item', {
props: ['parr'],
template: `
<div>
<ul>
<li :key='index' v-for='(item,index) in parr'>{{item}}</li>
</ul>
// // // 1辨泳、子組件用$emit()觸發(fā)事件
// // // 第一個參數(shù)為 自定義的事件名稱 第二個參數(shù)為需要傳遞的數(shù)據(jù)
<button @click='$emit("enlarge-text", 5)'>擴大父組件中字體大小</button>
<button @click='$emit("enlarge-text", 10)'>擴大父組件中字體大小</button>
</div>
`
});
var vm = new Vue({
el: '// app',
data: {
pmsg: '父組件中內(nèi)容',
parr: ['apple','orange','banana'],
fontSize: 10
},
methods: {
handle: function(val){
// 擴大字體大小
this.fontSize += val;
}
}
});
</script>
兄弟之間的傳遞
- 兄弟之間傳遞數(shù)據(jù)需要借助于事件中心虱岂,通過事件中心傳遞數(shù)據(jù)
- 提供事件中心 var hub = new Vue()
- 傳遞數(shù)據(jù)方,通過一個事件觸發(fā)hub.$emit(方法名菠红,傳遞的數(shù)據(jù))
- 接收數(shù)據(jù)方第岖,通過mounted(){} 鉤子中 觸發(fā)hub.$on()方法名
- 銷毀事件 通過hub.$off()方法名銷毀之后無法進行傳遞數(shù)據(jù)
<div id="app">
<div>父組件</div>
<div>
<button @click='handle'>銷毀事件</button>
</div>
<test-tom></test-tom>
<test-jerry></test-jerry>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
兄弟組件之間數(shù)據(jù)傳遞
*/
//1、 提供事件中心
var hub = new Vue();
Vue.component('test-tom', {
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>TOM:{{num}}</div>
<div>
<button @click='handle'>點擊</button>
</div>
</div>
`,
methods: {
handle: function(){
//2试溯、觸發(fā)兄弟組件的事件 事件觸發(fā)hub.$emit(方法名蔑滓,傳遞的數(shù)據(jù))
hub.$emit('jerry-event', 2);
}
},
mounted: function() {
// 3、監(jiān)聽事件:接收數(shù)據(jù)val遇绞,通過mounted(){} 鉤子中 觸發(fā)hub.$on(方法名
hub.$on('tom-event', (val) => {
this.num += val;
});
}
});
Vue.component('test-jerry', {
data: function(){
return {
num: 0
}
},
template: `
<div>
<div>JERRY:{{num}}</div>
<div>
<button @click='handle'>點擊</button>
</div>
</div>
`,
methods: {
handle: function(){
//2键袱、傳遞數(shù)據(jù)方,通過一個事件觸發(fā)hub.$emit(方法名摹闽,傳遞的數(shù)據(jù)) 觸發(fā)兄弟組件的事件
hub.$emit('tom-event', 1);
}
},
mounted: function() {
// 3蹄咖、接收數(shù)據(jù)方,通過mounted(){} 鉤子中 觸發(fā)hub.$on()方法名
hub.$on('jerry-event', (val) => {
this.num += val;
});
}
});
var vm = new Vue({
el: '// app',
data: {
},
methods: {
handle: function(){
//4付鹿、銷毀事件 通過hub.$off()方法名銷毀之后無法進行傳遞數(shù)據(jù)
hub.$off('tom-event');
hub.$off('jerry-event');
}
}
});
</script>
1.3 組件插槽
- 作用:父組件向子組件傳遞內(nèi)容<slot></slot>
- 組件的最大特性就是復(fù)用性澜汤,而用好插槽能大大提高組件的可復(fù)用能力
匿名插槽
<div id="app">
<!-- 組件標簽中的內(nèi)容會替換掉slot內(nèi)容 如果不傳值 則使用 slot 中的默認值 -->
<alert-box>有bug發(fā)生</alert-box>
<alert-box>有一個警告</alert-box>
<alert-box></alert-box>
</div>
<script type="text/javascript">
/*
組件插槽:父組件向子組件傳遞內(nèi)容
*/
Vue.component('alert-box', {
template: `
<div>
<strong>ERROR:</strong>
// 當(dāng)組件渲染的時候,這個 <slot> 元素將會被替換為“組件標簽中嵌套的內(nèi)容”舵匾。
// 插槽內(nèi)可以包含任何模板代碼俊抵,包括 HTML
<slot>默認內(nèi)容</slot>
</div>
`
});
var vm = new Vue({
el: '// app',
data: {
}
});
</script>
</body>
</html>
具名插槽
- 具有名字的插槽
- 使用 <slot> 中的 "name" 屬性綁定元素
<div id="app">
<base-layout>
<!-- 2、 通過slot屬性來指定, 這個slot的值必須和下面slot組件的name值對應(yīng)上
如果沒有匹配到 則放到匿名的插槽中 -->
<p slot='header'>標題信息</p>
<p>主要內(nèi)容1</p>
<p>主要內(nèi)容2</p>
<p slot='footer'>底部信息信息</p>
</base-layout>
<base-layout>
<!-- 注意點:template臨時的包裹標簽最終不會渲染到頁面上坐梯,用于包裹多個標簽
因為一個slot 的name值 只能有一個slot屬性對應(yīng)-->
<template slot='header'>
<p>標題信息1</p>
<p>標題信息2</p>
</template>
<p>主要內(nèi)容1</p>
<p>主要內(nèi)容2</p>
<template slot='footer'>
<p>底部信息信息1</p>
<p>底部信息信息2</p>
</template>
</base-layout>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
具名插槽
*/
Vue.component('base-layout', {
template: `
<div>
<header>
// // // 1徽诲、 使用 <slot> 中的 "name" 屬性綁定元素 指定當(dāng)前插槽的名字
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
// // // 注意點:
// // // 具名插槽的渲染順序,完全取決于模板吵血,而不是取決于父組件中元素的順序
<slot name='footer'></slot>
</footer>
</div>
`
});
var vm = new Vue({
el: '// app',
data: {
}
});
</script>
</body>
</html>
作用域插槽
- 父組件對子組件的內(nèi)容進行加工處理
- 既可以復(fù)用子組件的slot谎替,又可以使slot內(nèi)容不一致
<div id="app">
<!--
1、當(dāng)我們希望li 的樣式由外部使用組件的地方定義蹋辅,因為可能有多種地方要使用該組件院喜,
但樣式希望不一樣 這個時候我們需要使用作用域插槽
-->
<fruit-list :list='list'>
<!-- 2、 父組件中使用了<template>元素,而且包含scope="slotProps",
slotProps在這里只是臨時變量晕翠,用于獲取子組件slot綁定的數(shù)據(jù)
--->
<template slot-scope='slotProps'>
<strong v-if='slotProps.info.id==3' class="current">
{{slotProps.info.name}}
</strong>
<span v-else>{{slotProps.info.name}}</span>
</template>
</fruit-list>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
/*
作用域插槽
*/
Vue.component('fruit-list', {
props: ['list'],
template: `
<div>
<li :key='item.id' v-for='item in list'>
// // // 3喷舀、 在子組件模板中,<slot>元素上有一個類似props傳遞數(shù)據(jù)給組件的寫法msg="xxx",
// // // 插槽可以提供一個默認內(nèi)容,如果如果父組件沒有為這個插槽提供了內(nèi)容淋肾,會顯示默認的內(nèi)容硫麻。
如果父組件為這個插槽提供了內(nèi)容,則默認的內(nèi)容會被替換掉
<slot :info='item'>{{item.name}}</slot>
</li>
</div>
`
});
var vm = new Vue({
el: '// app',
data: {
list: [{
id: 1,
name: 'apple'
},{
id: 2,
name: 'orange'
},{
id: 3,
name: 'banana'
}]
}
});
</script>
</body>
</html>
購物車案例
1. 實現(xiàn)組件化布局
- 把靜態(tài)頁面轉(zhuǎn)換成組件化模式
- 把組件渲染到頁面上
<div id="app">
<div class="container">
<!-- 2樊卓、把組件渲染到頁面上 -->
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 1拿愧、 把靜態(tài)頁面轉(zhuǎn)換成組件化模式
// 1.1 標題組件
var CartTitle = {
template: `
<div class="title">我的商品</div>
`
}
// 1.2 商品列表組件
var CartList = {
// 注意點 : 組件模板必須是單個根元素
template: `
<div>
<div class="item">
<img src="img/a.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/b.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/c.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/d.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/e.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
</div>
`
}
// 1.3 商品結(jié)算組件
var CartTotal = {
template: `
<div class="total">
<span>總價:123</span>
<button>結(jié)算</button>
</div>
`
}
// // 1.4 定義一個全局組件 my-cart
Vue.component('my-cart',{
// // 1.6 引入子組件
template: `
<div class='cart'>
<cart-title></cart-title>
<cart-list></cart-list>
<cart-total></cart-total>
</div>
`,
// 1.5 注冊子組件
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
}
});
var vm = new Vue({
el: '// app',
data: {
}
});
</script>
2、實現(xiàn) 標題和結(jié)算功能組件
- 標題組件實現(xiàn)動態(tài)渲染
- 從父組件把標題數(shù)據(jù)傳遞過來 即 父向子組件傳值
- 把傳遞過來的數(shù)據(jù)渲染到頁面上
- 結(jié)算功能組件
- 從父組件把商品列表list 數(shù)據(jù)傳遞過來 即 父向子組件傳值
- 把傳遞過來的數(shù)據(jù)計算最終價格渲染到頁面上
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 2.2 標題組件 子組件通過props形式接收父組件傳遞過來的uname數(shù)據(jù)
var CartTitle = {
props: ['uname'],
template: `
<div class="title">{{uname}}的商品</div>
`
}
// 2.3 商品結(jié)算組件 子組件通過props形式接收父組件傳遞過來的list數(shù)據(jù)
var CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>總價:{{total}}</span>
<button>結(jié)算</button>
</div>
`,
computed: {
// 2.4 計算商品的總價 并渲染到頁面上
total: function() {
var t = 0;
this.list.forEach(item => {
t += item.price * item.num;
});
return t;
}
}
}
Vue.component('my-cart',{
data: function() {
return {
uname: '張三',
list: [{
id: 1,
name: 'TCL彩電',
price: 1000,
num: 1,
img: 'img/a.jpg'
},{
id: 2,
name: '機頂盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
},{
id: 3,
name: '海爾冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
},{
id: 4,
name: '小米手機',
price: 1000,
num: 1,
img: 'img/d.jpg'
},{
id: 5,
name: 'PPTV電視',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
}
},
// 2.1 父組件向子組件以屬性傳遞的形式 傳遞數(shù)據(jù)
// 向 標題組件傳遞 uname 屬性 向 商品結(jié)算組件傳遞 list 屬性
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
<cart-list></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
}
});
var vm = new Vue({
el: '// app',
data: {
}
});
</script>
3. 實現(xiàn)列表組件刪除功能
- 從父組件把商品列表list 數(shù)據(jù)傳遞過來 即 父向子組件傳值
- 把傳遞過來的數(shù)據(jù)渲染到頁面上
- 點擊刪除按鈕的時候刪除對應(yīng)的數(shù)據(jù)
- 給按鈕添加點擊事件把需要刪除的id傳遞過來
- 子組件中不推薦操作父組件的數(shù)據(jù)有可能多個子組件使用父組件的數(shù)據(jù) 我們需要把數(shù)據(jù)傳遞給父組件讓父組件操作數(shù)據(jù)
- 父組件刪除對應(yīng)的數(shù)據(jù)
- 給按鈕添加點擊事件把需要刪除的id傳遞過來
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var CartTitle = {
props: ['uname'],
template: `
<div class="title">{{uname}}的商品</div>
`
}
// 3.2 把列表數(shù)據(jù)動態(tài)渲染到頁面上
var CartList = {
props: ['list'],
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img"/>
<div class="name">{{item.name}}</div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
// 3.3 給按鈕添加點擊事件把需要刪除的id傳遞過來
<div class="del" @click='del(item.id)'>×</div>
</div>
</div>
`,
methods: {
del: function(id){
// 3.4 子組件中不推薦操作父組件的數(shù)據(jù)有可能多個子組件使用父組件的數(shù)據(jù)
// 我們需要把數(shù)據(jù)傳遞給父組件 讓父組件操作數(shù)據(jù)
this.$emit('cart-del', id);
}
}
}
var CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>總價:{{total}}</span>
<button>結(jié)算</button>
</div>
`,
computed: {
total: function() {
// 計算商品的總價
var t = 0;
this.list.forEach(item => {
t += item.price * item.num;
});
return t;
}
}
}
Vue.component('my-cart',{
data: function() {
return {
uname: '張三',
list: [{
id: 1,
name: 'TCL彩電',
price: 1000,
num: 1,
img: 'img/a.jpg'
},{
id: 2,
name: '機頂盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
},{
id: 3,
name: '海爾冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
},{
id: 4,
name: '小米手機',
price: 1000,
num: 1,
img: 'img/d.jpg'
},{
id: 5,
name: 'PPTV電視',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
}
},
// 3.1 從父組件把商品列表list 數(shù)據(jù)傳遞過來 即 父向子組件傳值
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
// 3.5 父組件通過事件綁定 接收子組件傳遞過來的數(shù)據(jù)
<cart-list :list='list' @cart-del='delCart($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
},
methods: {
// 3.6 根據(jù)id刪除list中對應(yīng)的數(shù)據(jù)
delCart: function(id) {
// 1碌尔、找到id所對應(yīng)數(shù)據(jù)的索引
var index = this.list.findIndex(item=>{
return item.id == id;
});
// 2浇辜、根據(jù)索引刪除對應(yīng)數(shù)據(jù)
this.list.splice(index, 1);
}
}
});
var vm = new Vue({
el: '// app',
data: {
}
});
</script>
</body>
</html>
4. 實現(xiàn)組件更新數(shù)據(jù)功能 上
- 將輸入框中的默認數(shù)據(jù)動態(tài)渲染出來
- 輸入框失去焦點的時候 更改商品的數(shù)量
- 子組件中不推薦操作數(shù)據(jù) 把這些數(shù)據(jù)傳遞給父組件 讓父組件處理這些數(shù)據(jù)
- 父組件中接收子組件傳遞過來的數(shù)據(jù)并處理
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var CartTitle = {
props: ['uname'],
template: `
<div class="title">{{uname}}的商品</div>
`
}
var CartList = {
props: ['list'],
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img"/>
<div class="name">{{item.name}}</div>
<div class="change">
<a href="">-</a>
// 1. 將輸入框中的默認數(shù)據(jù)動態(tài)渲染出來
// 2. 輸入框失去焦點的時候 更改商品的數(shù)量 需要將當(dāng)前商品的id 傳遞過來
<input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
<a href="">+</a>
</div>
<div class="del" @click='del(item.id)'>×</div>
</div>
</div>
`,
methods: {
changeNum: function(id, event){
// 3 子組件中不推薦操作數(shù)據(jù) 因為別的組件可能也引用了這些數(shù)據(jù)
// 把這些數(shù)據(jù)傳遞給父組件 讓父組件處理這些數(shù)據(jù)
this.$emit('change-num', {
id: id,
num: event.target.value
});
},
del: function(id){
// 把id傳遞給父組件
this.$emit('cart-del', id);
}
}
}
var CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>總價:{{total}}</span>
<button>結(jié)算</button>
</div>
`,
computed: {
total: function() {
// 計算商品的總價
var t = 0;
this.list.forEach(item => {
t += item.price * item.num;
});
return t;
}
}
}
Vue.component('my-cart',{
data: function() {
return {
uname: '張三',
list: [{
id: 1,
name: 'TCL彩電',
price: 1000,
num: 1,
img: 'img/a.jpg'
}]
},
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
// 4 父組件中接收子組件傳遞過來的數(shù)據(jù)
<cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
},
methods: {
changeNum: function(val) {
//4.1 根據(jù)子組件傳遞過來的數(shù)據(jù)券敌,跟新list中對應(yīng)的數(shù)據(jù)
this.list.some(item=>{
if(item.id == val.id) {
item.num = val.num;
// 終止遍歷
return true;
}
});
},
delCart: function(id) {
// 根據(jù)id刪除list中對應(yīng)的數(shù)據(jù)
// 1、找到id所對應(yīng)數(shù)據(jù)的索引
var index = this.list.findIndex(item=>{
return item.id == id;
});
// 2柳洋、根據(jù)索引刪除對應(yīng)數(shù)據(jù)
this.list.splice(index, 1);
}
}
});
var vm = new Vue({
el: '// app',
data: {
}
});
</script>
5. 實現(xiàn)組件更新數(shù)據(jù)功能 下
- 子組件通過一個標識符來標記對用的用戶點擊 + - 或者輸入框輸入的內(nèi)容
- 父組件拿到標識符更新對應(yīng)的組件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.container {
}
.container .cart {
width: 300px;
margin: auto;
}
.container .title {
background-color: lightblue;
height: 40px;
line-height: 40px;
text-align: center;
/*color: // fff;*/
}
.container .total {
background-color: // FFCE46;
height: 50px;
line-height: 50px;
text-align: right;
}
.container .total button {
margin: 0 10px;
background-color: // DC4C40;
height: 35px;
width: 80px;
border: 0;
}
.container .total span {
color: red;
font-weight: bold;
}
.container .item {
height: 55px;
line-height: 55px;
position: relative;
border-top: 1px solid // ADD8E6;
}
.container .item img {
width: 45px;
height: 45px;
margin: 5px;
}
.container .item .name {
position: absolute;
width: 90px;
top: 0;left: 55px;
font-size: 16px;
}
.container .item .change {
width: 100px;
position: absolute;
top: 0;
right: 50px;
}
.container .item .change a {
font-size: 20px;
width: 30px;
text-decoration:none;
background-color: lightgray;
vertical-align: middle;
}
.container .item .change .num {
width: 40px;
height: 25px;
}
.container .item .del {
position: absolute;
top: 0;
right: 0px;
width: 40px;
text-align: center;
font-size: 40px;
cursor: pointer;
color: red;
}
.container .item .del:hover {
background-color: orange;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var CartTitle = {
props: ['uname'],
template: `
<div class="title">{{uname}}的商品</div>
`
}
var CartList = {
props: ['list'],
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img"/>
<div class="name">{{item.name}}</div>
<div class="change">
// 1. + - 按鈕綁定事件
<a href="" @click.prevent='sub(item.id)'>-</a>
<input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
<a href="" @click.prevent='add(item.id)'>+</a>
</div>
<div class="del" @click='del(item.id)'>×</div>
</div>
</div>
`,
methods: {
changeNum: function(id, event){
this.$emit('change-num', {
id: id,
type: 'change',
num: event.target.value
});
},
sub: function(id){
// 2 數(shù)量的增加和減少通過父組件來計算 每次都是加1 和 減1 不需要傳遞數(shù)量 父組件需要一個類型來判斷 是 加一 還是減1 以及是輸入框輸入的數(shù)據(jù) 我們通過type 標識符來標記 不同的操作
this.$emit('change-num', {
id: id,
type: 'sub'
});
},
add: function(id){
// 2 數(shù)量的增加和減少通過父組件來計算 每次都是加1 和 減1 不需要傳遞數(shù)量 父組件需要一個類型來判斷 是 加一 還是減1 以及是輸入框輸入的數(shù)據(jù) 我們通過type 標識符來標記 不同的操作
this.$emit('change-num', {
id: id,
type: 'add'
});
},
del: function(id){
// 把id傳遞給父組件
this.$emit('cart-del', id);
}
}
}
var CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>總價:{{total}}</span>
<button>結(jié)算</button>
</div>
`,
computed: {
total: function() {
// 計算商品的總價
var t = 0;
this.list.forEach(item => {
t += item.price * item.num;
});
return t;
}
}
}
Vue.component('my-cart',{
data: function() {
return {
uname: '張三',
list: [{
id: 1,
name: 'TCL彩電',
price: 1000,
num: 1,
img: 'img/a.jpg'
},{
id: 2,
name: '機頂盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
},{
id: 3,
name: '海爾冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
},{
id: 4,
name: '小米手機',
price: 1000,
num: 1,
img: 'img/d.jpg'
},{
id: 5,
name: 'PPTV電視',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
}
},
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
// 3 父組件通過事件監(jiān)聽 接收子組件的數(shù)據(jù)
<cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
},
methods: {
changeNum: function(val) {
// 4 分為三種情況:輸入框變更待诅、加號變更、減號變更
if(val.type=='change') {
// 根據(jù)子組件傳遞過來的數(shù)據(jù)熊镣,跟新list中對應(yīng)的數(shù)據(jù)
this.list.some(item=>{
if(item.id == val.id) {
item.num = val.num;
// 終止遍歷
return true;
}
});
}else if(val.type=='sub'){
// 減一操作
this.list.some(item=>{
if(item.id == val.id) {
item.num -= 1;
// 終止遍歷
return true;
}
});
}else if(val.type=='add'){
// 加一操作
this.list.some(item=>{
if(item.id == val.id) {
item.num += 1;
// 終止遍歷
return true;
}
});
}
}
}
});
var vm = new Vue({
el: '// app',
data: {
}
});
</script>
</body>
</html>
2.前后端交互
2.1 接口調(diào)用的方式有哪些
原生ajax
基于jQuery的ajax
Fetch
Promise
2.2 url 地址格式有哪些
-
傳統(tǒng)的url
協(xié)議+域名/ip地址+端口+路徑+請求參數(shù)+錨點
-
Restful形式的url
請求方式:增刪改查 post delete put get
3.異步編程與promise
3.1異步
- JavaScript的執(zhí)行環(huán)境是「單線程」
- 所謂單線程卑雁,是指JS引擎中負責(zé)解釋和執(zhí)行JavaScript代碼的線程只有一個,也就是一次只能完成一項任務(wù)绪囱,這個任務(wù)執(zhí)行完后才能執(zhí)行下一個测蹲,它會「阻塞」其他任務(wù)。這個任務(wù)可稱為主線程
- 異步模式可以一起執(zhí)行多個任務(wù)
- JS中常見的異步調(diào)用
- 定時器
- ajax
- 事件函數(shù)
3.2promise
- 主要解決異步深層嵌套的c問題
- promise 提供了簡潔的API 使得異步操作更加容易
<script type="text/javascript">
/*
1. Promise基本使用
我們使用new來構(gòu)建一個Promise Promise的構(gòu)造函數(shù)接收一個參數(shù)鬼吵,是函數(shù)扣甲,并且傳入兩個參數(shù): resolve,reject齿椅, 分別表示異步操作執(zhí)行成功后的回調(diào)函數(shù)和異步操作執(zhí)行失敗后的回調(diào)函數(shù)
*/
var p = new Promise(function(resolve, reject){
//2. 這里用于實現(xiàn)異步任務(wù) setTimeout
setTimeout(function(){
var flag = false;
if(flag) {
//3. 正常情況
resolve('hello');
}else{
//4. 異常情況
reject('出錯了');
}
}, 100);
});
// 5 Promise實例生成以后琉挖,可以用then方法指定resolved狀態(tài)和reject狀態(tài)的回調(diào)函數(shù)
// 在then方法中,你也可以直接return數(shù)據(jù)而不是Promise對象媒咳,在后面的then中就可以接收到數(shù)據(jù)了
p.then(function(data){
console.log(data)
},function(info){
console.log(info)
});
</script>
基于Promise發(fā)送Ajax請求
<script type="text/javascript">
/*
基于Promise發(fā)送Ajax請求
*/
function queryData(url) {
// 1.1 創(chuàng)建一個Promise實例
var p = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
// 1.2 處理正常的情況
resolve(xhr.responseText);
}else{
// 1.3 處理異常情況
reject('服務(wù)器錯誤');
}
};
xhr.open('get', url);
xhr.send(null);
});
return p;
}
// 注意: 這里需要開啟一個服務(wù)
// 在then方法中粹排,你也可以直接return數(shù)據(jù)而不是Promise對象种远,在后面的then中就可以接收到數(shù)據(jù)了
queryData('http://localhost:3000/data')
.then(function(data){
console.log(data)
// 1.4 想要繼續(xù)鏈式編程下去 需要 return
return queryData('http://localhost:3000/data1');
})
.then(function(data){
console.log(data);
return queryData('http://localhost:3000/data2');
})
.then(function(data){
console.log(data)
});
</script>
3.3 Promise 基本API
實例方法
.then()
- 得到異步任務(wù)正確的結(jié)果
.catch()
- 獲取異常信息
.finally()
- 成功與否都會執(zhí)行(不是正式標準)
<script type="text/javascript">
/*
Promise常用API-實例方法
*/
// console.dir(Promise);
function foo() {
return new Promise(function(resolve, reject){
setTimeout(function(){
// resolve(123);
reject('error');
}, 100);
})
}
// foo()
// .then(function(data){
// console.log(data)
// })
// .catch(function(data){
// console.log(data)
// })
// .finally(function(){
// console.log('finished')
// });
// --------------------------
// 兩種寫法是等效的
foo()
.then(function(data){
// 得到異步任務(wù)正確的結(jié)果
console.log(data)
},function(data){
// 獲取異常信息
console.log(data)
})
// 成功與否都會執(zhí)行(不是正式標準)
.finally(function(){
console.log('finished')
});
</script>
靜態(tài)方法
.all()
-
Promise.all
方法接受一個數(shù)組作參數(shù)涩澡,數(shù)組中的對象(p1、p2肌访、p3)均為promise實例(如果不是一個promise烛亦,該項會被用Promise.resolve
轉(zhuǎn)換為一個promise)圣勒。它的狀態(tài)由這三個promise實例決定
.race()
-
Promise.race
方法同樣接受一個數(shù)組作參數(shù)。當(dāng)p1, p2, p3中有一個實例的狀態(tài)發(fā)生改變(變?yōu)?code>fulfilled或rejected
)粥帚,p的狀態(tài)就跟著改變。并把第一個改變狀態(tài)的promise的返回值限次,傳給p的回調(diào)函數(shù)
區(qū)別:返回數(shù)據(jù)的條件不同芒涡,all是所有執(zhí)行完,race是只要有一個執(zhí)行完就返回結(jié)果
<script type="text/javascript">
/*
Promise常用API-對象方法
*/
// console.dir(Promise)
function queryData(url) {
return new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
// 處理正常的情況
resolve(xhr.responseText);
}else{
// 處理異常情況
reject('服務(wù)器錯誤');
}
};
xhr.open('get', url);
xhr.send(null);
});
}
var p1 = queryData('http://localhost:3000/a1');
var p2 = queryData('http://localhost:3000/a2');
var p3 = queryData('http://localhost:3000/a3');
Promise.all([p1,p2,p3]).then(function(result){
// all 中的參數(shù)[p1,p2,p3]和 返回的結(jié)果順序一一對應(yīng)["HELLO TOM", "HELLO JERRY", "HELLO SPIKE"]
console.log(result) //["HELLO TOM", "HELLO JERRY", "HELLO SPIKE"]
})
Promise.race([p1,p2,p3]).then(function(result){
// 由于p1執(zhí)行較快卖漫,Promise的then()將獲得結(jié)果'P1'费尽。p2,p3仍在繼續(xù)執(zhí)行,但執(zhí)行結(jié)果將被丟棄羊始。
console.log(result) // "HELLO TOM"
})
</script>
3.4 fetch
- Fetch API是新的ajax解決方案 ,fetch不是ajax的進一步封裝旱幼,而是原生js,沒有使用XMLHttpRequest對象
- 基于標準的Promises實現(xiàn)突委,支持async/await柏卤,F(xiàn)etch會返回Promise
- 使用fetch(url, options).then()
<script type="text/javascript">
//參數(shù):請求路徑冬三,請求參數(shù) Fetch會返回Promise 所以我們可以使用then 拿到請求成功的結(jié)果
fetch('http://localhost:3000/fdata').then(function(res){
// text()方法屬于fetchAPI的一部分,它返回一個Promise實例對象缘缚,用于獲取后臺返回的數(shù)據(jù)
return res.text();
}).then(function(data){
// 在這個then里面我們能拿到最終的數(shù)據(jù)
console.log(data);
})
//或者
fetch('http://localhost:3000/fdata').then(function(res){
res.text().then(function(data){
console.log(data);
});
})
</script>
fetch API 中的 HTTP 請求
- fetch(url, options).then()
- HTTP協(xié)議勾笆,它給我們提供了很多的方法,如POST忙灼,GET匠襟,DELETE,UPDATE该园,PATCH和PUT
- 默認的是 GET 請求
- 需要在options對象中指定對應(yīng)的method method:請求使用的方法
- post和普通請求的時候 需要在options設(shè)置請求頭 headers 和 body
<script type="text/javascript">
/*
Fetch API 調(diào)用接口傳遞參數(shù)
*/
// 1.1 GET參數(shù)傳遞 - 傳統(tǒng)URL通過url酸舍?的形式傳參
fetch('http://localhost:3000/books?id=123', {
// 省略不寫 默認的是GET
method: 'get'
})
.then(function(data) {
// 它返回一個Promise實例對象,用于獲取后臺返回的數(shù)據(jù)
return data.text();
}).then(function(data) {
// 在這個then里面拿到最終的數(shù)據(jù)
console.log(data);//123
});
// 服務(wù)端:接收請求和響應(yīng)
// 設(shè)置允許跨域訪問該服務(wù)
app.all('*', function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
app.get('/books', (req, res) => {
res.send(req.query.id);
})
// 1.2 GET參數(shù)傳遞 restful形式的URL 通過/的形式傳遞參數(shù) 即id=456和id后臺的配置有關(guān)
fetch('http://localhost:3000/books/456', {
method: 'get'
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
// 服務(wù)端:接收請求和響應(yīng)
// ... 設(shè)置允許跨域訪問該服務(wù)
app.get('/books/:id', (req, res) => {
res.send(req.params.id);
})
// 2.1 DELETE請求方式參數(shù)傳遞 刪除id 是 id=789
fetch('http://localhost:3000/books/789', {
method: 'delete'
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
// 服務(wù)端:接收請求和響應(yīng)
// ... 設(shè)置允許跨域訪問該服務(wù)
app.delete('/books/:id', (req, res) => {
res.send(req.params.id);
})
// 3 POST請求傳參
fetch('http://localhost:3000/books', {
method: 'post',
// 3.1 傳遞數(shù)據(jù)
body: 'uname=lisi&pwd=123',
// 3.2 設(shè)置請求頭
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
// 服務(wù)端:接收請求和響應(yīng)
// ...設(shè)置允許跨域訪問該服務(wù)
const bodyParser = require('body-parse');
//處理參數(shù)
app.use(bodyParser.urlencoded({ extended: false }));
app.post('/books', (req, res) => {
// 這里的req.body是引入了body-parse模塊處理的結(jié)果
res.send(req.body.uname + '---' + req.body.pwd);
})
// POST請求傳參
fetch('http://localhost:3000/books', {
method: 'post',
body: JSON.stringify({
uname: '張三',
pwd: '456'
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
// 服務(wù)端:接收請求和響應(yīng)
// ...設(shè)置允許跨域訪問該服務(wù)
// 處理參數(shù)
const bodyParser = require('body-parse');
app.use(bodyParser.json());
app.post('/books', (req, res) => {
// 這里的req.body是引入了body-parse模塊處理的結(jié)果
res.send(req.body.uname + '---' + req.body.pwd);
})
// PUT請求傳參 修改id是123的
fetch('http://localhost:3000/books/123', {
method: 'put',
body: JSON.stringify({
uname: '張三',
pwd: '789'
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
//...
app.put('/books/:id', (req, res) => {
res.send(req.params.id + '---' + req.body.uname + '---' + req.body.pwd);
})
</script>
fetchAPI 中 響應(yīng)格式
- 用fetch來獲取數(shù)據(jù)里初,如果響應(yīng)正常返回啃勉,我們首先看到的是一個response對象,其中包括返回的一堆原始字節(jié)双妨,這些字節(jié)需要在收到后淮阐,需要我們通過調(diào)用方法將其轉(zhuǎn)換為相應(yīng)格式的數(shù)據(jù),比如
JSON
刁品,BLOB
或者TEXT
等等
/*
Fetch響應(yīng)結(jié)果的數(shù)據(jù)格式
*/
fetch('http://localhost:3000/json').then(function(res){
return res.json(); // 將獲取到的數(shù)據(jù)使用 json 轉(zhuǎn)換對象
// return res.text(); // 將獲取到的數(shù)據(jù) 轉(zhuǎn)換成字符串
}).then(function(data){
// console.log(data.uname)
// console.log(typeof data)
console.log(obj.uname,obj.age,obj.gender)
})
3.5 axios
- 基于promise泣特,用于瀏覽器和node.js的http庫
- 支持瀏覽器和node.js
- 支持promise
- 能攔截請求和響應(yīng)
- 自動轉(zhuǎn)換JSON數(shù)據(jù)
- 能轉(zhuǎn)換請求和響應(yīng)數(shù)據(jù)
axios基礎(chǔ)用法
- get和 delete請求傳遞參數(shù)
- 通過傳統(tǒng)的url 以 ? 的形式傳遞參數(shù)
- restful 形式傳遞參數(shù)
- 通過params 形式傳遞參數(shù)
- post 和 put 請求傳遞參數(shù)
- 通過選項傳遞參數(shù)
- 通過 URLSearchParams 傳遞參數(shù)
// 1. 發(fā)送get 請求
axios.get('http://localhost:3000/adata').then(function(res){
// 拿到 res 是一個對象 所有的對象都存在 res 的data 屬性里面
// 注意:data屬性用于獲取后臺的實際數(shù)據(jù)
// console.log(res.data)
console.log(res)
})
//服務(wù)端
app.get('/adata', (req, res) => {
res.send('ok');
})
// 2. get 請求傳遞參數(shù)
// 2.1 通過傳統(tǒng)的url 以 ? 的形式傳遞參數(shù)
axios.get('http://localhost:3000/axios?id=123').then(res => {
console.log(res)
})
//服務(wù)端
app.get('/adata', (req, res) => {
res.send(req.query.id);
})
// 2.2 restful 形式傳遞參數(shù)
axios.get('http://localhost:3000/axios/123').then(res => {
console.log(res)
})
//服務(wù)端
app.get('/adata/:id', (req, res) => {
res.send(req.params.id);
})
// 2.3 通過params 形式傳遞參數(shù)
axios.get('http://localhost:3000/axios', {
params: {
id: 789
}
}).then(res => {
console.log(res)
})
//服務(wù)端(同2.1)
app.get('/adata', (req, res) => {
res.send(req.query.id);
})
// 3 axios delete 請求傳參 傳參的形式和 get 請求一樣
axios.delete('http://localhost:3000/axios', {
params: {
id: 111
}
}).then(function(ret){
console.log(ret.data)
})
//服務(wù)端(同2.1)
app.delete('/adata', (req, res) => {
res.send(req.query.id);
})
// 4 axios 的 post 請求
// 4.1 通過選項傳遞參數(shù)
axios.post('http://localhost:3000/axios', {
uname: 'lisi',
pwd: 123
}).then(function(ret){
console.log(ret.data)
})
//服務(wù)端
app.post('/axios', (req, res) => {
res.send(req.body.uname + '---' + req.body.pwd)
})
// 4.2 通過 URLSearchParams 傳遞參數(shù)
var params = new URLSearchParams();
params.append('uname', 'zhangsan');
params.append('pwd', '111');
axios.post('http://localhost:3000/axios', params).then(function(ret){
console.log(ret.data)
})
// 5 axios put 請求傳參 和 post 請求一樣
axios.put('http://localhost:3000/axios/123', {
uname: 'lisi',
pwd: 123
}).then(function(ret){
console.log(ret.data)
})
//服務(wù)端
app.put('/axios/:id', (req, res) => {
res.send(req.params.id + '---' + req.body.uname + '---' + req.body.pwd)
})
axios響應(yīng)結(jié)果
data 實際響應(yīng)回來的數(shù)據(jù)
headers 響應(yīng)頭
status響應(yīng)狀態(tài)碼
axios 全局配置
// 配置公共的請求頭
axios.defaults.baseURL = 'https://api.example.com';
// 配置 超時時間
axios.defaults.timeout = 2500;
// 配置公共的請求頭
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// 配置公共的 post 的 Content-Type
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios 攔截器
- 請求攔截器
- 請求攔截器的作用是在請求發(fā)送前進行一些操作
- 例如在每個請求體里加上token,統(tǒng)一做了處理如果以后要改也非常容易
- 請求攔截器的作用是在請求發(fā)送前進行一些操作
- 響應(yīng)攔截器
- 響應(yīng)攔截器的作用是在接收到響應(yīng)后進行一些操作
- 例如在服務(wù)器返回登錄狀態(tài)失效挑随,需要重新登錄的時候状您,跳轉(zhuǎn)到登錄頁
- 響應(yīng)攔截器的作用是在接收到響應(yīng)后進行一些操作
// 1. 請求攔截器
axios.interceptors.request.use(function(config) {
console.log(config.url) //http://localhost:3000/adata
// 1.1 任何請求都會經(jīng)過這一步 在發(fā)送請求之前做些什么
config.headers.mytoken = 'nihao';
// 1.2 這里一定要return 否則配置不成功
return config;
}, function(err){
// 1.3 對請求錯誤做點什么
console.log(err)
})
// 2. 響應(yīng)攔截器
axios.interceptors.response.use(function(res) {
// 2.1 在接收響應(yīng)做些什么
var data = res.data;
return data;
}, function(err){
// 2.2 對響應(yīng)錯誤做點什么
console.log(err)
})
axios.get('http://localhost:3000/adata').then(function(data){
console.log(data)//服務(wù)端返回的“Hello axios!”
})
4. async 和 await
async/await是ES7引入的
- async關(guān)鍵字放到函數(shù)前面
- async函數(shù)都會隱式返回一個
promise
- async函數(shù)都會隱式返回一個
-
await
關(guān)鍵字只能在async函數(shù)中使用- ? await后面可以直接跟一個 Promise實例對象
- ? await函數(shù)不能單獨使用
- async/await 讓異步代碼看起來、表現(xiàn)起來更像同步代碼
// 1. async 基礎(chǔ)用法
// 1.1 async作為一個關(guān)鍵字放到函數(shù)前面
async function queryData() {
// 1.2 await關(guān)鍵字只能在使用async定義的函數(shù)中使用 await后面可以直接跟一個 Promise實例對象
var ret = await new Promise(function(resolve, reject){
setTimeout(function(){
resolve('nihao')
},1000);
})
// console.log(ret.data)
return ret;
}
// 1.3 任何一個async函數(shù)都會隱式返回一個promise 我們可以使用then 進行鏈式編程
queryData().then(function(data){
console.log(data)
})
// 2. async 函數(shù)處理多個異步函數(shù)
axios.defaults.baseURL = 'http://localhost:3000';
async function queryData() {
// 2.1 添加await之后 當(dāng)前的await 返回結(jié)果之后才會執(zhí)行后面的代碼
var info = await axios.get('async1');
// 2.2 讓異步代碼看起來兜挨、表現(xiàn)起來更像同步代碼
var ret = await axios.get('async2?info=' + info.data);
return ret.data;
}
queryData().then(function(data){
console.log(data)
})
5. 圖書列表案例
1. 基于接口案例-獲取圖書列表
- 導(dǎo)入axios 用來發(fā)送ajax
- 把獲取到的數(shù)據(jù)渲染到頁面上
<div id="app">
<div class="grid">
<table>
<thead>
<tr>
<th>編號</th>
<th>名稱</th>
<th>時間</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- 5. 把books 中的數(shù)據(jù)渲染到頁面上 -->
<tr :key='item.id' v-for='item in books'>
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date }}</td>
<td>
<a href="">修改</a>
<span>|</span>
<a href="">刪除</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
1. 導(dǎo)入axios
<script type="text/javascript" src="js/axios.js"></script>
<script type="text/javascript">
/*
圖書管理-添加圖書
*/
// 2 配置公共的url地址 簡化后面的調(diào)用方式
axios.defaults.baseURL = 'http://localhost:3000/';
axios.interceptors.response.use(function(res) {
return res.data;
}, function(error) {
console.log(error)
});
var vm = new Vue({
el: '// app',
data: {
flag: false,
submitFlag: false,
id: '',
name: '',
books: []
},
methods: {
// 3 定義一個方法 用來發(fā)送 ajax
// 3.1 使用 async 來 讓異步的代碼 以同步的形式書寫
queryData: async function() {
// 調(diào)用后臺接口獲取圖書列表數(shù)據(jù)
// var ret = await axios.get('books');
// this.books = ret.data;
// 3.2 發(fā)送ajax請求 把拿到的數(shù)據(jù)放在books 里面
this.books = await axios.get('books');
}
},
mounted: function() {
// 4 mounted 里面 DOM已經(jīng)加載完畢 在這里調(diào)用函數(shù)
this.queryData();
}
});
</script>
2 添加圖書
- 獲取用戶輸入的數(shù)據(jù) 發(fā)送到后臺
- 渲染最新的數(shù)據(jù)到頁面上
methods: {
handle: async function(){
if(this.flag) {
// 編輯圖書
// 就是根據(jù)當(dāng)前的ID去更新數(shù)組中對應(yīng)的數(shù)據(jù)
this.books.some((item) => {
if(item.id == this.id) {
item.name = this.name;
// 完成更新操作之后膏孟,需要終止循環(huán)
return true;
}
});
this.flag = false;
}else{
// 1.1 在前面封裝好的 handle 方法中 發(fā)送ajax請求
// 1.2 使用async 和 await 簡化操作 需要在 function 前面添加 async
var ret = await axios.post('books', {
name: this.name
})
// 1.3 根據(jù)后臺返回的狀態(tài)碼判斷是否加載數(shù)據(jù)
if(ret.status == 200) {
// 1.4 調(diào)用 queryData 這個方法 渲染最新的數(shù)據(jù)
this.queryData();
}
}
// 清空表單
this.id = '';
this.name = '';
},
}
3 驗證圖書名稱是否存在
- 添加圖書之前發(fā)送請求驗證圖示是否已經(jīng)存在
- 如果不存在 往后臺里面添加圖書名稱
- 圖書存在與否只需要修改submitFlag的值即可
watch: {
name: async function(val) {
// 驗證圖書名稱是否已經(jīng)存在
// var flag = this.books.some(function(item){
// return item.name == val;
// });
var ret = await axios.get('/books/book/' + this.name);
if(ret.status == 1) {
// 圖書名稱存在
this.submitFlag = true;
}else{
// 圖書名稱不存在
this.submitFlag = false;
}
}
},
4. 編輯圖書
- 根據(jù)當(dāng)前書的id 查詢需要編輯的書籍
- 需要根據(jù)狀態(tài)位判斷是添加還是編輯
methods: {
handle: async function(){
if(this.flag) {
// 4.3 編輯圖書 把用戶輸入的信息提交到后臺
var ret = await axios.put('books/' + this.id, {
name: this.name
});
if(ret.status == 200){
// 4.4 完成添加后 重新加載列表數(shù)據(jù)
this.queryData();
}
this.flag = false;
}else{
// 添加圖書
var ret = await axios.post('books', {
name: this.name
})
if(ret.status == 200) {
// 重新加載列表數(shù)據(jù)
this.queryData();
}
}
// 清空表單
this.id = '';
this.name = '';
},
toEdit: async function(id){
// 4.1 flag狀態(tài)位用于區(qū)分編輯和添加操作
this.flag = true;
// 4.2 根據(jù)id查詢出對應(yīng)的圖書信息 頁面中可以加載出來最新的信息
// 調(diào)用接口發(fā)送ajax 請求
var ret = await axios.get('books/' + id);
this.id = ret.id;
this.name = ret.name;
},
5 刪除圖書
- 把需要刪除的id書籍 通過參數(shù)的形式傳遞到后臺
deleteBook: async function(id){
// 刪除圖書
var ret = await axios.delete('books/' + id);
if(ret.status == 200) {
// 重新加載列表數(shù)據(jù)
this.queryData();
}
}