計算屬性
使用場景
如果一個結果需要依賴data中的數據,但是需要經過一些邏輯處理,才能得到你想要的數據。此時就可以使用計算屬性。
例如:要對給定的字符串做翻轉處理之后再來顯示。
<div id="app"><!-- 此處邏輯復雜 --><h3>{{msg.split('').reverse().join('')}}</h3></div><script src="./vue.js"></script><script>const vm = new Vue({el: '#app',data: {msg: 'javascript'}})</script>
定義格式
在vue實例中,補充computed配置項。
new Vue({// 聲明計算屬性computed: {//屬性名字(計算屬性名稱)//屬性的值(計算屬性處理函數)計算屬性名1 () {// 對依賴的數據進行處理,且進行returnreturn },計算屬性名2 () {// 對依賴的數據進行處理,且進行returnreturn }}
})
computed 是vue的配置選項,它的值是一個對象,其中可定義多個計算屬性,每個計算屬性就是一個函數。
- 屬性名稱: 計算屬性的名稱
- 屬性的值:計算屬性的素材函數
- 對需要依賴的數據,進行邏輯上的處理
- 處理完畢后,需要return出去,返回的值就是計算屬性的值
使用格式
在兩個地方使用:
- 模板
- 用插值表達式 {{計算屬性名}}
- 用其它指令
- 在實例內
- this.計算屬性名
示例
顛倒字符串
<div id="app"><!-- 邏輯復雜 --><h3>{{msg.split('').reverse().join('')}}</h3><!-- 計算屬性 和data類似--><h3>{{reverseMsg}}</h3></div><script src="./vue.js"></script><script>const vm = new Vue({el: '#app',data: {msg: 'hi vue'},// 聲明計算屬性computed: {//屬性名字(計算屬性名稱)//屬性的值(計算屬性處理函數)reverseMsg () {// 對依賴的數據進行處理,且進行returnreturn this.msg.split('').reverse().join('')}}})</script>
- 在模板中使用計算屬性,和使用data的方式是一樣的。
- 雖然在計算屬性中聲明的是函數,但是在模板中使用,當中數據來使用,不需要加括號。
總結:
-
什么時間用:需要對數據進行復雜的邏輯加工,產生新的數據時。
-
定義: 就是一個特殊的配置項
computed
。其中有多個函數。 -
使用:計算屬性的使用方式與data中的數據項一致;
- 計算屬性-計算:這個值是對原數據進行計算之后得到的新的數據
- 計算屬性-屬性:它的使用方法與原數據一樣。
this.計算屬性名
,{{計算屬性名}}
-
執行的時機: 如果計算屬性中依賴的數據項變化時,它會自動調用。
computed有緩存
問:
當我們在模板中來顯示一份經過對數據項進行復雜計算之后的結果時,我們有兩種解決方案:
- 計算屬性
- 函數
應該如何選擇?
答:
-
methods定義函數,如果在模板中使用,每使用一次,就相當于調用了一次,處理邏輯會重新執行。
-
computed定義計算屬性,如果在模板中使用,使用多次,但是如果依賴的數據不發生改變,計算屬性對應的函數不會重新執行。
- 計算屬性會做緩存,提高渲染的性能。
示例
<div id="app"><h3>學習計算屬性</h3><p>計算屬性:{{ reversedMsg }}</p><p>計算屬性:{{ reversedMsg }}</p><p>計算屬性:{{ reversedMsg }}</p><hr><p>函數:{{fReversedMsg()}}</p><p>函數:{{fReversedMsg()}}</p><p>函數:{{fReversedMsg()}}</p></div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><script>// 計算屬性的特點:緩存// - 如果計算屬性所依賴的數據項并沒有發生變化,則就算使用多個計算函數,其函數也只執行一次// 因為它把結果緩存起來了。const vm = new Vue({el: '#app',data: {msg: 'javascript'},methods: {fReversedMsg () {console.log( '函數 fReversedMsg' )//把msg的翻轉一下let newMsg = this.msg.split('').reverse().join('')return newMsg}},computed: {reversedMsg () {console.log( 'reversedMsg' )//把msg的翻轉一下let newMsg = this.msg.split('').reverse().join('')return newMsg}}})// setTimeout (function() {vm.msg = "abc"// 由于計算屬性有緩存,雖然在頁面上用到三次,但它的函數體只執行一次。// 對于普通的函數,在頁面上用到了三次,它就會執行三次},2000)
</script>
總結:
- 計算屬性有緩存,提高渲染性能。
- 如果在頁面上需要用到 對現有的數據進行加工得到新數據,則時要使用計算屬性
案例-資產列表(續)
目標
-
計算總資產(計算屬性)
-
根據輸入的內容篩選符合條件的資產(計算屬性)
篩選
思路:
- 給篩選輸入框添加雙向綁定
- 添加一個計算屬性:根據輸入框的內空對list進行篩選
- 把計算屬性的值顯示在表格中
求和
思路:
- 補充一個計算屬性,對cList進行循環,求出它的price的和
代碼
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet"><style>.red {color:red}</style>
</head>
<body><div id="app"><div class="container"><div class="form-group"><div class="input-group"><!-- 使用自定義指令來讓input自動獲取焦點 .trim 去掉空格--><input v-focus v-model.trim="key" type="text" class="form-control" placeholder="搜索"></div></div><table class="table table-bordered table-hover"><thead><tr><th>編號</th><th>資產名稱</th><th>價格</th><th>操作</th></tr></thead><tbody><!-- <tr v-for="(循環變量,索引變量) in 數據">在計算屬性的基礎上進行循環 --><tr v-for="(item,idx) in cList"><td>{{item.id}}</td><td>{{item.name}}</td><!-- 如果價格超過100,就有red這個類 --><!-- <td class="red">{{item.price}}</td> --><!-- 三元--><!-- <td :class='item.price > 100 ? "red" : ""'>{{item.price}}</td> --><!-- :class= 放一個對象如果對象中的屬性值是true,則把對象中的屬性名添加到類名中--><!-- 過濾器:把12345 ===> ¥12,345 1. 在前面加一個¥2. 千分位的分隔,加,--><!-- <td :class="{red:item.price>100}">{{item.price | currency}}</td> --><td :class="{red:item.price>100}">{{item.price | $}}</td><td><a href="#" @click.prevent="hDel(idx)">刪除</a></td></tr></tbody><tfoot><tr><!-- colspan合并單元格:把4列合成一個 --><td colspan="4">共計:{{cTotal | $}}</td></tr></tfoot></table><!-- 添加資產 --><form class="form-inline"><div class="form-group"><div class="input-group"><input v-model.trim="name" type="text" class="form-control" placeholder="資產名稱"></div></div> <div class="form-group"><div class="input-group"><input v-model.trim.number="price" type="text" class="form-control" placeholder="價格"></div></div> <!-- 阻止表單提交 --><button class="btn btn-primary" @click.prevent="hAdd">添加資產</button></form></div></div><!-- <script src="https://cdn.bootcdn.net/ajax/libs/vue/1.0.11/vue.js"></script> --><script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script><script>// 目標: 用vue.js實現// 1. 把表格數據顯示出來// 2. 實現刪除功能// 3. 添加功能// 4. 標注:如果價格超過100,標紅色顯示。// 步驟:// 1.引入vue.js// 2. 實例化vue對象 // 三大件: el,data,methods// 3. 實現功能// (1) 循環渲染數據: v-for// (2) 刪除數據:@click,并傳入要刪除的下標// (3) 標注紅色 動態的添加樣式,只有>100才加。 使用對象來綁定class.// (4) 添加功能// 1) 基本輸入框結構// 2) 點擊添加// - 收集用戶的輸入信息:v-model,補一些必要的修飾符。// - 簡單判空// - 添加到list中// (5) 計算總和// 補充一個計算屬性,在其中對list進行循環,算出總共的price// (6) 篩選功能// 在篩選區域的input框中輸入關鍵字,則在下面的list中找出對應的項,其它項應該要隱藏起來// - 給input添加v-model,收集用戶需要的關鍵字// - 定義一個計算屬性,根據關鍵字來在list中進行數據的過濾,找出符合關鍵字要求的項const vm = new Vue({el: '#app',data: {name: '', // 名稱price: 0, // 價格list: [{ id: 1, name: '外套', price: 199 },{ id: 2, name: '褲子', price: 34 },{ id: 3, name: '鞋', price: 25.4 },{ id: 4, name: '頭發', price: 19900 },{ id: 5, name: '梳子', price: 899 }],key: '' // 搜索關鍵字},computed: {cList () {if(this.key === '') {return this.list}// 這個計算屬性依賴于 key 和list,只有一個變化,則它會重新執行// 如果視圖上沒有用計算屬性,那么,這個依賴項變化時,計算屬性不會執行console.log("cList",Date.now())// 根據this.key對list進行過濾// 對于this.list中的每一項,如果其中name包含 this.key關鍵字,則保留let newList = this.list.filter(item => {// 如果其中name包含 this.key關鍵字if(item.name.includes( this.key )) {return true} else {return false}})// console.log( newList )return newList},// 由于添加了篩選功能,則要對整個的計算屬性cList中數據進行循環,計算出 總共的pricecTotal: function() {// 這個計算屬性依賴于:另一個計算屬性 cList// 當cList 變化時,這個計算屬性也會重新執行let totalPrice = 0this.cList.forEach(item => {totalPrice = totalPrice + item.price})return totalPrice.toFixed(2)}},methods: {hDel (index) {console.log('要刪除的下標是',index)// 刪除數組中指定位置的元素this.list.splice(index, 1)},hAdd () {// 收集用戶的輸入信息// 簡單判空if(this.name ==='') {return }if(this.price == '' || this.price < 0) {return }// 添加到list中console.log(this.name,this.price)// 向數組中添加一個元素let id = 1if (this.list.length > 0) {// 新的id = 數組中最后一個元素的id值+1id = this.list[this.list.length-1].id + 1}this.list.push({id: id,name: this.name,price: this.price})}},filters: {// 定義過濾器// 過濾器的名字就是$$: function (value, _currency) {// 正則表達式: 全局匹配 三個連續的,且之后也是數值的 數值var digitsRE = /(\d{3})(?=\d)/g;value = parseFloat(value);// 判斷是否數值if (!isFinite(value) || !value && value !== 0) return '';// 設置金額的符號_currency = _currency != null ? _currency : '¥';// 取絕對值,保存兩位有效數字,轉成字符串var stringified = Math.abs(value).toFixed(2);// 獲取整數部分。 -3 表示倒數3位var _int = stringified.slice(0, -3);// 整數部分以3為基準長度劃分,剩下幾位var i = _int.length % 3;// 取出頭部// 12345 ---> 12,var head = i > 0 ? _int.slice(0, i) + (_int.length > 3 ? ',' : '') : '';// 取出小數字部分var _float = stringified.slice(-3);// 判斷符號var sign = value < 0 ? '-' : '';// 整體輸出return _currency + sign + head + _int.slice(i).replace(digitsRE, '$1,') + _float;}// $: function(value) {// // 1. 在前面加一個¥// // 2. 千分位的分隔,加, // // 有難度,與vue,與過濾器沒有關系。請大家自己在下面做練習。 // // 我們這里直接去vue的源碼中抄一段出來,直接用。// // 在Vue1.0版本中,它是自帶這個貨幣金額過濾器// console.log(value)// return '¥'+value// }},directives: {focus: {inserted (el) {el.focus()}}}})// list: [// { id: 1, name: '外套', price: 99 },// { id: 2, name: '褲子', price: 34 },// { id: 3, name: '鞋', price: 25.4 },// { id: 4, name: '頭發', price: 19900 }// ]</script>
</body>
</html>
computed: {cList () {if(this.key) {return this.list.filter(it => it.name.includes(this.key))}else {return this.list}},cTotal () {return this.cList.reduce((total,it)=>{ return total+it.price*1}, 0)}
},
自已寫接口
在開發過程中,經常會遇到后端接口滯后的情況:前端頁面寫完了,后端的接口沒有就緒,此時,我們就可以先準備接口。
前后端分離開發
代碼層的分離
- 代碼上的分離(前后端是同一個項目,開發目錄上的區分)
- 項目級的分離(前端是一個單獨的項目,后臺是一個單獨的項目)
開發的內容的分離
- 后端負責業務邏輯,寫接口
- 前端負責業面交互,調接口。
我們的理解:必須后臺先提供接口,前端才能進行開發。
分離的意義:前后端可以并行開發。
- 收到任務
- 前端后端一起制定接口規則,形成接口文檔:接口名,功能,參數,返回值…
- 并行開發
- 后端開發接口:接口1,接口2,…
- 前端寫頁面,調用接口(自己按第2步的接口文檔要求,寫真的假接口)
json-server
前端自己寫接口,可以用nodejs自己寫,也可以借助第三方現成的工具:json-server
json-server快速地根據json文件,生成接口
json ----json-server-----> 接口
在后端接口沒有交付之前,使用json-server快速創建模擬接口,推進前端開發進程。
在后端接口實現之后,再切換回去。
使用步驟
全局安裝json-server
它是依賴于nodejs的第三方包。需要全局安裝(不需要初始化)。
npm i json-server -g# 安裝完成
C:\Users\fanyoufu\AppData\Roaming\npm\json-server -> C:\Users\fanyoufu\AppData\Roaming\npm\node_modules\json-server\lib\cli\bin.js
+ json-server@0.16.1
updated 1 package in 12.418s
##
只需要安裝一次就行了。
準備空文件夾
在任意目錄下,準備一個空文件夾,取名mock-server(可改成其它名字)
創建json文件
在文件夾中新建一個名為db.json文件(可改其它名稱,注意名字是是英文)
初始化結構
在db.json文件中,按json格式的要求,去定義一個對象:
- 鍵值對格式
- 用雙引號括起來
{"assets": [{ "id": 1, "name": "外套", "price": 99 },{ "id": 2, "name": "褲子", "price": 34 },{ "id": 3, "name": "鞋","price": 25.4 },{ "id": 4, "name": "頭發", "price": 19900 }]
}
啟動接口服務
根據上面創建的db.json自動地生成接口。
在此文件夾下,打開命令行窗口,輸入命令json-server db.json
(json-server后有空格)
如果沒有錯誤,則運行結果如下:
測試
在瀏覽器中訪問上面地址
注意:
- json格式。
- 屬性名 —> 接口的地址
- 屬性值 —> 接口返回數據 (數組|對象)
原理圖
RESTful接口
json-server提供的接口是符合RESTful接口規范的。
在寫接口時,每個程序員都有自己的寫法:取不同的名字,例如:實現添加資產
A同學: localhost:3000/addAsset | delAsset
B同學: localhost:3000/insertAsset | deleteAsset
C同學: localhost:3000/tjzj | sczj
針對上述問題,提出一套約定:
RESTful接口的規范是通過
- 請求方式來決定操作類別(添加,刪除,修改,查詢)
- 用名詞來表示要操作的目標
以上面啟動的服務為例:
接口地址 | 請求方式 | 操作類型 |
---|---|---|
/assets | GET | 獲取全部數據 |
/assets/1 | GET | 獲取單個數據 |
/assets | POST | 添加操作 {name,price} |
/assets/1 | DELETE | 刪除操作 |
/assets/1 | PUT | 完整修改{name,price} |
/assets/1 | PATCH | 局部修改{name} |
以上接口規則,就是restful規則。json-server提供的就是符合restful規則的接口。
postman測試
使用postmant來測試json-server提供的接口。
獲取全部數據
GET: http://localhost:3000/assets
獲取單個數據
以id為查詢依據
GET: http://localhost:3000/assets/1
根據條件查詢數據
json-server服務器提供了條件查詢的功能
格式: ? 字段**_like**=值
不可以加引號!
添加操作
以json格式傳遞參數,id會自動增加。
POST:http://localhost:3000/assets/
{"name": "電腦", "price": 6880}
另一個操作示例:
刪除操作
以id為查詢依據
DELETE: http://localhost:3000/assets/1
完整修改
以json格式傳遞參數
PUT: http://localhost:3000/assets/3
要求必須傳入一個完整的對象(如果這一條記錄中有100個屬性,則要傳入100個屬性),因為它會整體覆蓋這條數據。
局部修改
以json格式傳遞參數
PATCH: http://localhost:3000/assets/3
只需要傳入要更改的字段(要修改哪個屬性就傳入哪個屬性 )。
axios調用接口
我們發ajax請求接口,有如下方式:
- 原生的。純手寫。XMLHttpRequest…
- $.ajax()。jquery中的ajax
- axios。它是一個專業的,只發ajax請求的工具。它可以在nodejs中和瀏覽器使用。
下載并使用
axios是一個js工具,要想使用,就要先下載。
http://www.axios-js.com/docs/
- axios提交數據的時候,默認的數據類型(content-type)是 application/json
下載地址:https://unpkg.com/axios/dist/axios.min.js
格式
完整寫法
axios({// 請求方式 get|post|put|patch|delete 大小寫不區分method: 'get',// 路徑上傳參在url后進行拼接url: 'http://localhost:3000/brands/1',// ?后鍵值對傳參params: {},// 請求體傳參data: {},// 請求頭傳參headers: {}
}).then(res=>{console.log('成功')
}).catch(err=>{console.log('失敗')
})
簡寫格式
對于比較簡單接口調用,可以采用簡寫格式:
格式:
axios.請求方式(url, .......).then().catch()
示例:
// get類型的 參數要放在寫params中
axios.get(url,params: {}).then(res=>{console.log('成功')})
.catch(err=>{console.log('失敗')
})
// post類型 直接寫參數
axios.post(url,{}).then(res=>{console.log('成功')})
.catch(err=>{console.log('失敗')
})
示例
通過json-server啟動接口服務器,然后再寫axios代碼來進行調用測試
查詢
// 1. 查詢// 所有// axios.get('http://localhost:3000/assets').then(res=>{// // res是響應對象 // res.data是后臺響應內容(數據)// console.log(res.data)// })// // 單個// axios.get('http://localhost:3000/assets/2').then(res=>{// // res是響應對象 // res.data是后臺響應內容(數據)// console.log(res.data)// })// 篩選條件查詢// 方式1:自己手動在地址欄?后進行拼接// 方式2:針對get傳參,?后鍵值對傳參,固定傳參方式 {params:{//傳參對象 }}axios.get('http://localhost:3000/assets',{params:{name_like:'奧'}}).then(res=>{// res是響應對象 // res.data是后臺響應內容(數據)console.log(res.data)})
添加
// 2. 添加// 除去get請求外,函數的第二個參數都是請求體傳參,是對象類型。axios.post('http://localhost:3000/assets',{name:"寶馬",price:500000}).then(res=>{console.log('成功')})
刪除
// 3. 刪除axios.delete('http://localhost:3000/assets/2').then(res=>{console.log('成功')})
修改
// 4. 修改 完整// axios.put('http://localhost:3000/assets/3',{name:"長安奔奔",price:36000}).then(res=>{// console.log('成功')// })// 5. 修改 局部axios.patch('http://localhost:3000/assets/3',{name:"奔奔"}).then(res=>{console.log('成功')})
總結:不同方式的參數怎么傳遞給后臺。
案例-資產列表-接口版
目標:
-
用json-server啟動后端接口服務器;
-
axios發請求
-
用vue管理視圖
渲染列表
目標:用axios請求json-server提供的接口,并顯示數據出來。
準備靜態頁面
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="./bootstrap.min.css">
</head><body><div id="app"><div class="container"><!-- 搜索 --><form class="form-inline" style="padding: 20px 0"><input type="text" class="form-control" placeholder="輸入關鍵字進行搜索"></form><!-- 表格 --><table class="table table-bordered table-hover"><thead><tr><th>編號</th><th>資產名稱</th><th>創建時間</th><th>操作</th></tr></thead><tbody><tr><td>1</td><td>xxx</td><td>xxxx</td><td><a href="#">刪除</a></td></tr></tbody></table><!-- 添加資產 --><form class="form-inline"><input type="text" class="form-control" placeholder="資產名稱"> <button class="btn btn-primary">添加資產</button></form></div></div>
</body></html>
創建vue實例,實現列表渲染
實現渲染列表,大致步驟:
- 在data配置項中,聲明一個列表數據list,先設置一些假數據。
- 在模板當中根據list和數據中的字符段進行渲染
- 使用v-for指令
- 注意處理無數據的情況。
vue實例如下:
<script src="./vue.js"></script>
<script src="./axios.min.js"></script>
<script>const vm = new Vue({el: '#app',data: {// 資產列表數據list: [{id:1,name:'t',price:1}]}})
</script>
對應模板如下:
<tbody><tr v-for="item in list" :key="item.id"><td>{{item.id}}</td><td>{{item.name}}</td><td>{{item.price}}</td><td><a href="#">刪除</a></td></tr><tr v-if="list.length===0"><td colspan="4" style="text-align: center;">暫無數據</td></tr>
</tbody>
發ajax請求,取回真數據
現在一切就緒,只需在某個時間點,向后臺接口發請求,拿回數據,給list重新賦值即可
以前:window.onload = function(){} 默認渲染,頁面加載完成之后
或者:$(function(){}) 默認渲染,頁面文章加載完成之后
在vue中,需要默認渲染,vue實例初始化完畢后,發請求獲取數據進行渲染。
vue提供一個配置選項
created
類型是函數,實例化后執行的函數。
<script src="./vue.js"></script><script src="./axios.min.js"></script><script>const vm = new Vue({el: '#app',data: {// 資產列表數據list: []},created () {// vue實例后執行(回調函數,鉤子函數),在某個時機會被調用的函數。this.get()},methods: {// 獲取列表數據get() {axios.get("http://localhost:3000/assets").then(res => {this.list = res.data})this.name = ""}}})</script>
總結:一般在created函數中獲取頁面需要的初始化數據。
刪除資產
目標:點擊頁面上的刪除按鈕,把對應的數據刪除掉。
步驟:
-
給a標簽綁定點擊事件(注意阻止默認行為)
-
定義處理函數,給函數傳入資產ID
-
在函數中
-
彈出確認框提示
-
點擊確認框之后,發刪除請求
-
如果成功之后,更新列表(移除刪除的行)
-
<td><a @click.prevent="hDel(item.id)" href="#">刪除</a></td>
// 刪除資產函數
hDel(id) {if (confirm("你確定要刪除" + id)) {axios.delete("http://localhost:3000/assets/" + id).then(res => {this.get()})}
},
添加資產
目標:在點擊頁面上的添加時,可以實現添加新資產的功能
步驟:
- 雙向數據綁定,輸入資產的表單元素
- 綁定表單的提交事件,阻止默認行為
- 在methods中定義一個事件函數,在函數中:
- 組織需要提交給后臺的數據,{name,price},id后臺不需要,自動自增。
- 提交數據前,對name進行校驗
- 發起添加請求,如果成功,重新獲取后臺列表數據即可。
- 之前輸入的內容清空
在案例中落地的代碼:
<!-- 添加資產 -->
<form class="form-inline"><div class="form-group"><div class="input-group"><input type="text" v-model.trim="name" class="form-control" placeholder="資產名稱"></div></div> <div class="form-group"><div class="input-group"><input type="text" v-model.number="price" class="form-control" placeholder="價格"></div></div> <button class="btn btn-primary" @click.prevent="hAdd">添加</button>
</form>
data: {list: [],
+ name: "",
+ price: 0,
},
// 添加資產函數
hAdd() {if (!this.name) {return}if (!this.price) {return}axios.post("http://localhost:3000/assets", {name: this.name,price: this.price,}).then(res => {this.get()})this.name = ""
}