Vue
一、Vue概述
(一)Vue是什么
Vue.js(讀音 /vjuː/, 類似於 view) 是一套構建用戶界面的漸進式框架。Vue 只關注視圖層, 采用自底向上增量開發的設計。
Vue 的目標是通過盡可能簡單的 API 實現響應的數據綁定和組合的視圖組件。
Vue.JS是優秀的前端 JavaScript 框架
(二)為什么學習Vue
隨着項目業務場景的復雜,傳統模式(html+jquery)已無法滿足需求,就出現了Angular/React/Vue等框架
企業需求、主流框架之一、易用、靈活、高效
(三)Vue能做什么
最大程度上解放了 DOM 操作
單頁web項目開發
傳統網站開發
什么是工具(庫)、什么是框架
1.庫(插件):是一種封裝好的特定方法集合,對項目的侵入性較小,提供給開發者使用,控制權在使用者手中,如果某個庫無法完成某些需求,可以很容易切換到其它庫實現需求 (jquery)
2.框架:是一套架構,會基於自身特點向用戶提供一套相當完整的解決方案,而且控制權在框架本身;對項目的侵入性較大,使用者要按照框架所規定的某種特定規范進行開發,項目如果需要更換框架,則需要重新架構整個項目
二、Vue核心特征
① 解耦視圖與數據
② M-V-VM模型 關注模型和視圖
M:即Model,模型,包括數據和一些基本操作。
V:即View,視圖,頁面渲染結果
VM:即View-Model,模型和視圖間的雙向操作
③ 雙向數據綁定
(一)MVVM之前
開發人員從后端獲取需要的數據模型,然后要通過DOM操作Model渲染到View中。而后當用戶操作視圖,我們還需要通過DOM獲取View中的數據,然后同步到Model中
(二)MVVM之后
而MVVM中的VM要做的事情就是把DOM操作完全封裝起來,開發人員不用再關心Model和View之間是如何互相影響的:
- 只要我們Model發生了改變,View上自然就會表現出來。
- 當用戶修改了View,Model中的數據也會跟着改變
把開發人員從繁瑣的DOM操作中解放出來,把關注點放在如何操作Model上
三、Vue入門
(一)下載安裝
vue是一個前端框架,也是其實是一個js文件,下載vue.js文件並在頁面中引入 vue.js的下載方式: ① 可以引入在線的vue.js(公共的CDN服務) <!-- 開發環境版本,包含了用幫助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 或 <!-- 生產環境版本,優化了尺寸和速度 --> <script src="https://cdn.jsdelivr.net/npm/vue"></script> ② 可以離線下載vue.js 開發版本: https://vuejs.org/js/vue.js 生產版本: https://vuejs.org/js/vue.min.js ③ npm包資源管理器,可以下載vue.js (前端專用) 初始化:npm init -y 安裝vue:npm install vue --save 注:切記重啟計算機
(二)第一個vue
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h2>{{name}},歡迎你!</h2> </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script type="text/javascript"> //生成一個Vue實例 var app=new Vue({ el:"#app",//el ,即是element。要渲染的頁面元素 data:{//數據 name:"優就業" } }) </script> </body> </html> Vue參數詳解: 1. body中,設置Vue管理的視圖<div id="app"></div> 2. 引入vue.js 3. 實例化Vue對象 new Vue(); 4. 設置Vue實例的選項:如el、data... new Vue({選項:值}); 5. 在<div id='app'></div>中通過{{ }}使用data中的數據
四、Vue常見指令
指令 (Directives) 是帶有 v- 前綴的特殊attribute。是Vue框架提供的語法,擴展了html標簽的功能、大部分的指令的值是js的表達式。用於取代DOM操作
(一)v-text
和 v-html
類似innerText和innerHTML ① v-text:更新標簽中的內容 ② v-html:更新標簽中的內容/標簽 案例: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>v-text、v-html</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <!-- 插值表達式 {{}} v-text 不識別html文本們只能原樣輸出,直接覆蓋原本標簽的內容 v-html 能識別html文本,直接覆蓋原本標簽的內容 --> <p v-text="a">您好</p> <p v-html="b"></p> <p v-html="c"></p> <p>你好,{{msg}}</p> </div> </body> <script> let app = new Vue({ el:"#app", data:{ msg:"helloworld", a:'hi', b:'hello', c:'<h1>嗨</h1>' } }); </script> </html>
(二)v-if
和 v-show
根據表達式的boolean值進行判斷是否渲染該元素 案例: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="vue.js"></script> </head> <body> <!-- v-if v-show 都可以用於顯示或者隱藏元素 區別: v-if 使用注釋 隱藏元素 v-show 使用樣式 display:none 隱藏元素 --> <div id="app"> <p v-if="show">{{name}}</p> <p v-show="show">{{name}}</p> </div> </body> <script> let app = new Vue({ el:"#app", data:{ name:'張三', show:true } }) </script> </html>
(三)v-on
① 作用:使用 v-on 指令綁定 DOM 事件,並在事件被觸發時執行一些 JavaScript 代碼。 ② 語法: v-on:事件名.修飾符 = "methods中的方法名"; v-on的簡寫方法: @事件名.修飾符 = "methods中的方法名"; ③ 案例: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>v-on</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <p>當前計數: {{count}}</p> <!-- <button type="button" v-on:click="jia()">+</button> <button type="button" v-on:click="jian()">-</button> --> <!-- <button type="button" @click="jia()">+</button> <button type="button" @click="jian()">-</button> --> <button type="button" @click="count++">+</button> <button type="button" @click="count--">-</button> </div> </body> <script> let app = new Vue({ el:"#app", data:{ count:0 }, methods:{ jia(){ this.count++; }, jian(){ this.count--; } } }); </script> </html>
v-on綁定事件案例:
練習-顯示隱藏
內容顯示時,按鈕是隱藏功能。隱藏內容后,按鈕是顯示功能,顯示、隱藏共用一個按鈕.
代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>v-on</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<p v-show="condition">你能看見我嗎</p>
<button @click="change()">{{btn}}</button>
</div>
</body>
<script>
let app = new Vue({
el:"#app",
data:{
btn:'隱藏',
condition:true
},
methods:{
change(){
this.btn = this.btn=="隱藏" ?"顯示":"隱藏";
this.condition = this.btn=="隱藏"?true:false;
}
}
})
</script>
</html>
(四)v-for
① 作用:列表渲染,當遇到相似的標簽結構時,就用v-for去渲染 ② 格式: (item,index) in 數組或集合 參數item:數組中的每個元素 參數index:數組中元素的下標 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>v-for</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <!-- 遍歷固定次數、固定內容 <p v-for="(item, index) in 100">好好學習,天天向上</p> --> <!-- 遍歷數組--> <p v-for="(stu, index) in stuArr">{{index}}--{{stu.name}}--{{stu.age}}</p> </div> </body> <script> let app = new Vue({ el:"#app", data:{ stuArr:[ { id:1, name:'張三', age:20 }, { id:2, name:'李四', age:22 }, { id:3, name:'王五', age:25 } ] } }); </script> </html>
(五)v-bind
① 作用: 可以綁定標簽上的任何屬性 ② 格式:v-bind:屬性="值" ③ 簡寫格式::屬性="值" ④ 屬性值一部分進行替換的格式::屬性="'常量值' + vue對象data中的數據" ⑤ 案例: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>v-bind</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <!-- 如果直接寫屬性名 , 不認識vue中定義的變量 --> <a href="addr">京東</a> <!-- 通過v-bind綁定的屬性,能識別vue中定義的變量、方法--> <a v-bind:href="addr">百度</a> <a :href="addr2">中公</a> <a :href="'http://'+addr3">淘寶</a> </div> </body> <script> let app = new Vue({ el:"#app", data:{ addr:"http://www.baidu.com", addr2:"http://www.offcn.com", addr3:"www.taobao.com" } }); </script> </html>
練習-點擊換樣式
藍色
color:blue;
border:1px solid blue;
box-shadow:0px 0px 3px black;
紅色
color:red;
border:1px solid red;
box-shadow:0px 0px 3px black;
(六)v-model
① 作用:表單元素的綁定 ② 特點:雙向數據綁定 (1)vue對象中的數據發生變化可以更新到界面 (2)通過界面可以更改vue對象中數據 (3)v-model 會忽略所有表單元素的 value、 checked 、 selected 特性的初始值而總是將 Vue 實 例的數據作為數據來源。應該在data選項中聲明初始值。 ③ 案例: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>v-model</title> <script src="vue.js"></script> </head> <body> <div id="app"> 請輸入您的個人信息: 姓名:<input type="text" v-model="name" /> 年齡: <input type="text" v-model="age"> <hr> 請核對您的個人信息 <br> 姓名: {{name}} <br> 年齡: {{age}} </div> </body> <script> let app = new Vue({ el:"#app", data:{ name:'張三', age:20 } }) </script> </html>
(七)計算屬性
計算屬性可以時刻關注在vue中定義的關聯的變量。 當這些關聯的變量值發生變化,計算屬性也會發生變化. 代碼 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>計算屬性</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <input type="text" v-model="num1" /> + <input type="text" v-model="num2" /> <p> <span>{{num1}}</span> + <span>{{num2}}</span> = <span>{{sum}}</span> </p> <p> <span>{{num1}}</span> * <span>{{num2}}</span> = <span>{{cheng}}</span> </p> </div> </body> <script> const app = new Vue({ el:'#app', data:{ num1:0, num2:0 }, computed:{ sum(){ return this.num1*1 + this.num2*1; }, cheng(){ return this.num1 * this.num2; } } }); </script> </html>
案例說明:
1.修改數量、移除書籍后,總價會發生變化
2.當一本書籍都沒有了,提示購物車為空,表格消失
代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>作業</title>
<script src="vue.js"></script>
</head>
<body>
<div id="app">
<table v-show="items.length>0" width="700" style="text-align: center;" border="1" cellspacing="0" cellpadding="0">
<tr>
<th></th>
<th>書籍名稱</th>
<th>出版日期</th>
<th>價格</th>
<th>購買數量</th>
<th>操作</th>
</tr>
<tr v-for="(item, index) in items">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<td>¥{{item.price}}</td>
<td><button v-on:click="--item.count">-</button>{{item.count}}<button @click="++item.count">+</button></td>
<td><button @click="items.splice(index,1)">移除</button></td>
</tr>
</table>
<p v-show="items.length>0">總價:¥{{sum}}</p>
<p v-show="items.length==0">購物車為空</p>
</div>
</body>
<script>
let app = new Vue({
el:'#app',
data:{
items:[
{id:1,name:'<<算法導論>>',date:'2006-9',price:85,count:1},
{id:2,name:'<<UNIX編程藝術>>',date:'20016-2',price:59,count:1},
{id:3,name:'<<編程珠璣>>',date:'2003-1',price:39,count:1},
{id:4,name:'<<代碼大全>>',date:'2012-7',price:128,count:1}
]
},
methods: {
jian(book){
if(book.count>1)
book.count--;
},
jia(book){
book.count++;
},
remove(index){
this.items.splice(index,1);
}
},
computed:{
sum(){
let sum = 0;
for(let i = 0 ; i < this.items.length;i++){
let book = this.items[i];
sum+=book.price * book.count;
}
return sum;
}
}
});
</script>
</html>
五、Vue的生命周期
每個 Vue 實例在被創建時都要經過一系列的初始化過程 :創建實例,裝載模板,渲染模板等等。Vue為生命周期中的每個狀態都設置了鈎子函數(監聽函數)。每當Vue實例處於不同的生命周期時,對應的函數就會被觸發調用。
鈎子函數: 如:created代表在vue實例創建后;我們可以在Vue中定義一個created函數,代表這個時期的構造函數:
六、組件
(一)定義全局組件
我們通過Vue的component方法來定義一個全局組件。 Vue.component(組件名,{組件參數}) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <con></con> <con></con> </div> </body> <script> let con = Vue.component("con",{ template:`<div><p>次數{{count}}</p> <button type="button" @click="--count">-</button> <button type="button" @click="++count">+</button></div>`, data(){ return { count:0 } } }); let app = new Vue({ el:'#app' }) </script> </html> 特點: - 組件其實也是一個Vue實例,因此它在定義時也會接收:data、methods、生命周期函數等 - 不同的是組件不會與頁面的元素綁定,否則就無法復用了,因此沒有el屬性。 - 但是組件渲染需要html模板,所以增加了template屬性,值就是HTML模板 - 全局組件定義完畢,任何vue實例都可以直接在HTML中通過組件名稱來使用組件了。 - data的定義方式比較特殊,必須是一個函數。 注: 定義組件要在Vue對象之前聲明 模板template中只能有一個root(根)
(二)定義局部組件
一旦全局注冊,就意味着即便以后你不再使用這個組件,它依然會隨着Vue的加載而加載。因此,對於一些並不頻繁使用的組件,我們會采用局部注冊。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> </div> <div id="app1"> <con></con> <con></con> </div> </body> <script> let con = { template: `<div><p>次數{{count}}</p> <button type="button" @click="--count">-</button> <button type="button" @click="++count">+</button></div>`, data() { return { count: 0 } } } let app = new Vue({ el: '#app' }) let app1 = new Vue({ el: '#app1', components: { con: con } }) </script> </html> - components就是當前vue對象組件集合。 - 其key就是子組件名稱 - 其值就是組件對象的屬性 - 效果與剛才的全局注冊是類似的,不同的是,這個conn組件只能在當前的Vue實例中使用 注:定義組件要在Vue對象之前聲明
七、Vue的Ajax(axios)
在Vue.js中發送網絡請求本質還是ajax,我們可以使用插件方便操作。
(一)安裝
下載axios.js <script src="https://unpkg.com/axios/dist/axios.min.js"></script> 用離線版的axios.js
(二)axios請求
GET請求
axios.get('/user?id=12345') .then(response => { console.log(response.data); }); axios.get('/user?id=12345').then(function(response){ })
POST請求
axios.post('/user', "name=迪麗熱巴&age=23") .then(response => { console.log(response.data); });
(三)跨域問題
什么是跨域?
指的是瀏覽器不能執行其他網站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對javascript施加的安全限制。
什么是同源策略?
是指協議,域名,端口都要相同,其中有一個不同都會產生跨域,在請求數據時,瀏覽器會在控制台中報一個異常,提示拒絕訪問。
跨域問題怎么出現的?
開發一些前后端分離的項目,比如使用 Servlet + Vue 開發時,后台代碼在一台服務器上啟動,前台代碼在另外一台電腦上啟動,此時就會出現問題。
比如:后台 地址為 http://192.168.70.77:8081
前台 地址為 http://192.168.70.88:8080
此時 ip 與 端口號不一致, 不符合同源策略,造成跨域問題。
處理思路
使用CORS,全稱是"跨域資源共享"(Cross-origin resource sharing)。它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。
值得注意的是,目前主流瀏覽器都支持cors,會自動發出cors請求。在簡單請求下,cors請求和同源請求的區別主要是瀏覽器的請求頭信息中會多了一個origin字段。 這個字段表示本次請求來自哪個源(協議 + 域名 + 端口)。服務器根據這個值,決定是否同意這個請求。
因此在后續解決方案中,主要是服務器需要處理orgin這個請求頭字段,同意跨域請求。
如何解決: 后台解決(自定義過濾器) package com.offcn.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebFilter("/*") public class CorsFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) servletResponse; HttpServletRequest request = (HttpServletRequest) servletRequest; // 不使用*,自動適配跨域域名,避免攜帶Cookie時失效 String origin = request.getHeader("Origin"); response.setHeader("Access-Control-Allow-Origin", origin); // 自適應所有自定義頭 String headers = request.getHeader("Access-Control-Request-Headers"); response.setHeader("Access-Control-Allow-Headers", headers); response.setHeader("Access-Control-Expose-Headers", headers); // 允許跨域的請求方法類型 response.setHeader("Access-Control-Allow-Methods", "*"); // 預檢命令(OPTIONS)緩存時間,單位:秒 response.setHeader("Access-Control-Max-Age", "3600"); // 明確許可客戶端發送Cookie,不允許刪除字段即可 response.setHeader("Access-Control-Allow-Credentials", "true"); filterChain.doFilter(request, response); } @Override public void destroy() { } }