因為項目需要前后端分離,后端竟然不用控制view層了,頁面的跳轉后端不再干涉,(前端的vue經過打包后成了一張index.html) 后端只需要響應給前端json串就ok,其實這不是爽歪歪?但是覺得還是奇奇怪怪,感覺前端是個黑盒了, 於是忍不住去學習了vue
感覺前端的框架帶來的這種前后端分離變化還是特別明顯的,后端確實不用再操心view層了,頁面的轉換有vue通過后端的切換不同的組件,后端基本上沒有什么變化,但是相應數據基本上是清一色的json格式的數據了, 此外, 目前碰到的后端的安全框架 SpringSecurity的使用有了些許變化,起碼認證成功還是失敗,不能往指定的頁面跳轉了,轉而使用消息+狀態碼提示,因為就一張index.html,還能往哪里跳轉?
下面是近幾天的學習筆記, 還是再整理一遍,畢竟會忘
認識MVVM框架Vue
MV VM分別對應着
- model : 數據模型,存放后端傳遞過來的數據
- view : 視圖,其實就是html, 頁面
- viewModel : vue 的實例
下面是一個入門的例子: 通過這個例子可以看到:
- 我們new 出來vue的實例,然后把它關聯在了html中id為 app的代碼塊,這樣目的是如果我們在這個代碼塊中使用vue的模板語法,vue可以解析
- data: 這個模塊盛放的 mvvm中的第一個m
其實這也可以看出,vue的開發模式,它的出現屏蔽掉了dom操作,我們再也不用document.getElementById(), 然后innnerHtml了, 現在的工作就是把后端給的值填充進data塊中的屬性字段就ok,一旦發生改變,頁面會自動渲染上最新的值
<body>
<div id="app">
<input type="text" v-model="username"><!-- 聲明式開發 dom監聽 -->
<p>Haha {{username}}</p> <!-- 數據綁定 -->
</div>
</body>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:'#app', // 元素選擇器, 選出根路徑
data:{ // Data Model 數據模型
username:'哈哈'
}
});
</script>
模板語法:
雙大括號 取值:
<p>{{msg}}</p>
<p>{{msg.toUpperCase()}}</p> <!-- 可以調用js函數 -->
嵌入文本或html
<p v-text="msg"></p> <!-- 相當於 textContent -->
<p v-html="msg"></p> <!-- 相當於 innerHtml -->
強制數據綁定,在原標簽前添加 :
<img :src="imaUrl" alt="">
綁定監聽事件: @符
- 比較有趣的地方,如果在methods塊中,js函數的定義是無參數據的, 在html代碼塊中可以直接寫函數名,而不寫小括號,因為java代碼寫多了,看了想笑 ,(當然(),也可以寫上,但是js編程者會認為這是沒事找事)
<button @click="text"> 點我111 </button>
<button @click="text222('haha')"> 點我222 </button>
計算屬性
計算屬性,說白了就是vue給我們的一塊糖,讓我們定制數據的變化規則,然后vue幫我們渲染在html頁面上
現在是2020年的5月23號,當我再回來看computed時,想再補充一下,如何更好的理解計算屬性: 所謂計算屬性,說白了,其實就是根據data中現有的屬性計算得到一個新的屬性 ,此外,計算屬性函數是不需要我們手動執行的,會自動執行
- 計算屬性是針對data中的字段的操作
- 計算屬性中的每一個函數,都分兩部分: get和set , 默認是get,作用是把這個方法的返回值渲染進頁面, set方法,就是重新設置值, 然后get會重新渲染html
- 計算屬性是存在緩存的,key就是函數的名字,value就是計算得到的值
例子:
<body>
<div id="app">
姓名: <input type="text" placeholder="FirstName" v-model="secondName"> <br>
姓名1: <input type="text" placeholder="FullName1" v-model="FullName1"> <br>
姓名3: <input type="text" placeholder="FullName3" v-model="FullName3"> <br>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 下面的全部回調函數中, this都是vm對象
var vm = new Vue({
el: '#app',
data: {
firstName: 'A',
secondName: 'B',
},
computed: {
FullName1() {
return this.firstName + ' ' + this.secondName;
},
// todo 通過計算屬性實現雙向的數據綁定, 不受其他影響
FullName3: {
get() { // 計算並返回當前屬性的值
return this.firstName + ' ' + this.secondName;
},
set(vel) { // get 執行之后 把結果返回給 set
const names = vel.split(' ');
alert(names[0]);
alert(names[1]);
this.firstName = names[0];
this.secondName = names[1];
}
}
}
});
計算屬性寫在computed塊中, 可以看到它常用的兩種寫法, 在使用是時候都是直接使用函數名就行,因為它並沒有參數
- 函數名(){}
- 對象名:{ get(){} , set(){} }
上面的FullName1
以函數的,這種書寫格式是對 get方法的默認實現,方法的返回值會被渲染到頁面上
FullName3
還重寫了set(val){} 方法, 如果我們在FullName3
對應的輸入框里面輸入新的值, val就是這個新的值,在set方法中,如果對當前vue實例的data中的屬性做了改動,這個改動是雙向的,頁面中所有使用對應字段的地方的值,都會重新渲染
事件的監聽:
它的語法:
// 下面的全部回調函數中, this都是vm對象
var vm = new Vue({
el: '#app',
data: {
firstName:'',
secondName:''
},
computed: {},
method: {},
watch: {/* 監視 */
firstName: function (newVal) {
this.firstName = newVal + ' ' + this.secondName;
}
}
});
它會監聽data中的屬性,當用戶改變了data中屬性的值,就會觸發對應的回調
class和style的綁定
class和style的屬性綁定同樣使用的是使用 : 強制屬性綁定
首先是寫好 css屬性,才能進一步使用vue將屬性樣式綁定在html上
head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.aClass{
font-size: 30px;
color: #ff4400;
}
.bClass{
color: #00b4ff;
}
.cClass{
color: #1c036c;
}
</style>
</head>
語法:
- :class="data中的css屬性"
- :class="{data中的css屬性:boolean, data中的css屬性:boolean}", 這種對象語法,就是當類名確定但是是否顯示不確定時使用,比如讓一個導航欄中的一個高亮顯示
- :style="{color:activeColor,fontSize:fontSize +'px'}", style有單位的+單位
<body>
<div id="text">
<h2>1. class綁定 :: class='X X X '</h2>
<p :class="a">123123字符串</p>
<p :class="{aClass:isa, bClass:isb}">class是對象,綁定class 類名:boolean </p>
<h2>2. style 綁定 </h2>
<p :style="{color:activeColor,fontSize:fontSize +'px'}">2. style 綁定 </p>
<button @click="update" >點擊</button>
</div>
下面的vue對象
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#text',
data: {
a:'aClass', // 關聯着最上面的css樣式
isa:true,
isb:false,
activeColor:'red',
fontSize:'30'
},
methods:{
update() {
this.a='bClass'
}
}
})
條件渲染
<p v-if="ok">deal</p> --> ok是vue中data的數據,ok為true時, 顯示
<p v-else>false</p> --> 和 v-if成對出現, ok為false, 顯示
<p v-show="ok"> 成功 </p>
<p v-show="!ok"> 失敗 </p>
列表的渲染 v-for, 及對數組的操作
下面的例子使用v-for遍歷數組中的每一個數據, 遍歷的同時使用{{對象.屬性}}展示屬性,同時可以根據每個li的index綁定上不同的事件
<body>
<div id="text">
<ul>
<!-- 一旦有 v-for 加上key-->
<li v-for="(p,index) in person" :key="index">
{{p.name}} : {{p.age}} : {{index}}
<button @click="deleteP(index)"> 刪除</button>
<button @click="updateP(index,{name:'Cat',age:20})"> 更新</button>
</li>
</ul>
</div>
var vm = new Vue({
el: '#text',
data: {
person: [ /* vue 只會監視person的改變, 不會監視數組中數據的改變*/
{name: 'tom', age: 23},
{name: 'tom2', age: 223},
{name: 'tom2', age: 23},
{name: 'tom3', age: 232},
{name: 'tom5', age: 23}
]
},
methods: {
deleteP(index) {
this.person.splice(index, 1); //從index開始 刪除1個
},
updateP(index, person) {
// this.person[index]=person; 並沒有改變 persons , 從index開始,刪除1個 添加person
this.person.splice(index, 1, person)
}
如果我們更新的js是這樣寫的, 數組中的內容確實會被改變,但是頁面的上數據並不會更新
this.person[index]=person; 並沒有改變 persons , 從index開始,刪除1個 添加person
因為vue監聽的person的改變,person中只有一個數組,雖然數組中的數據變了, 但是數組沒變,所以我們使用vue的提供的splice進行數組的操作
splice(下標,數量,[新的對象數組])
他可以實現數組的增刪改的效果
- 刪除
//刪除起始下標為1,長度為2的一個值(len設置2)
var arr2 = ['a','b','c','d']
arr2.splice(1,2);
console.log(arr2);
//['a','d']
- 修改
//替換起始下標為1,長度為1的一個值為‘ttt’,len設置的1
var arr = ['a','b','c','d'];
arr.splice(1,1,'ttt');
console.log(arr);
//['a','ttt','c','d']
- 添加
var arr = ['a','b','c','d'];
arr.splice(1,0,'ttt');
console.log(arr);
//['a','ttt','b','c','d']
其他數組相關的操作
unshift()
添加到第一個shift()
添加到最后一個push()
壓棧,棧頂pop()
彈出sort()
排序reverse()
反轉
數組的映射,過濾,排序
js的箭頭函數和java8的lambda表達式特別像
- 映射
array.map(item=>item.id)
// 可以將數組中的每一個元素映射成他的id屬性
- 過濾
persons = person.filter(p => p.name.indexOf(searchModel)>=0);
// 保留數組中滿足條件的對象
- ES6的語法糖
把對象的指定字段存放進聲明的多個常量中
const{searchModel,person,orderType} = this;
- 排序
persons.sort(function (p1,p2) {
// 升序
if (orderType===1){
return p1.age-p2.age;
} else if (orderType===2){ // 降序
return p2.age-p1.age;
}
事件綁定相關
@click
綁定事件
<button @click="text1">text1</button>
<button @click="text2('haha')">text2</button>
<button @click="text3($event)">text3</button>
<button @click="text4">text4</button><!-- 如果沒有指定參數進去,傳遞進去的就是event-->
<button @click="text5(123,$event)">text5</button>
var vm = new Vue({
el:'#test',
methods:{
text1(){
alert("text 1");
},
text2(msg){
alert(msg);
},
text3(event){
alert(event.target.innerHTML);
},
text4(event){
alert(event.target.innerHTML);
},
text5(msg,event){
alert(msg+event.target.innerHTML);
},
可以看到@click
使用vue中method的函數時,如果沒有參數,可以簡寫,去掉(), 如果不寫參數,傳遞進去的是事件本身event , text三中通過event拿到了標簽的文本內容
@click.prevent
阻止事件的默認行為
<a href="http:www.baidu.com" @click.prevent="text8">百度一下</a> <!-- 阻止事件的默認行為 -->
監聽某個按鍵的點擊事件
<input type="text" @keyup.enter="text9"> <!-- @keyup.13(名字) 監聽某一個鍵的點擊事件 -->
收集表單數據
使用vue將用戶填入表單中的數據收集起來, 收集到哪里去? 其實是收集到 vue的data塊中的屬性中
其實就是在html使用v-model
暴力綁定dom監聽, 將單選框,輸入框,多選框中用戶輸入進去的內容和data中的屬性關聯起來
- input,textarea 等輸入框,收集起來的值就是用戶輸入進去的值
- 單選框 radio ,多選框 checkbox 等選擇框,收集起來的值的 html中的value屬性的值
<h1>表單中最終提交給后台的是 value值</h1><br>
<h2> 使用v-model實現表單數據的自動收集 </h2>
<form action="/XXX" @submit.prevent="handleSubmit" ><!-- 阻止表單的默認自動提交事件 -->
<span>用戶名:</span>
<input type="text" v-model="username"><br>
<span>密碼</span>
<input type="password" v-model="pwd" ><br>
<span>性別</span><br>
<input type="radio" id="female" value="女" v-model="sex">
<label for="female">女</label><br>
<input type="radio" id="male" value="男" v-model="sex">
<label for="male">男</label><br><br>
<span>愛好</span><br>
<input type="checkbox" id="basket" value="basket" v-model="likes">
<label for="basket">籃球</label>
<input type="checkbox" id="foot" value="foot" v-model="likes">
<label for="foot">足球</label>
<input type="checkbox" id="pingpang" value="pingpang" v-model="likes">
<label for="pingpang">乒乓球</label><br><br>
<span>城市</span><br>
<select v-model="cityId">
<option value="">未選擇</option>
<option :value="city.id" v-for="(city,index) in allCitys" :key="index">{{city.name}}</option>
</select>
<span>介紹:</span>
<textarea name="" id="" cols="30" rows="10" v-model="dec"></textarea>
<input type="submit" value="注冊"><br>
</form>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:'#test',
data:{
username:'',
pwd:'',
sex:'女',
likes:['foot'],
allCitys:[{id:1,name:'北京'},{id:2,name:"山東"},{id:3 ,name:"青島"}],
cityId:'3' /* 默認讓 3被選中*/,
dec:"哈哈"
},
methods:{
handleSubmit(){
alert(this.username+this.pwd);
alert(this.sex);
}
}
})
vue的生命周期
vue對象在創建初始化的過程中一次執行如下聲明周期相關的方法, 根據這個特性,通常把加載進入一個新的頁面中時去發送ajax請求的方法放到mounted(){},收尾工作放在beforeDestroy(){}
var vm = new Vue({
el: "#text",
data: {},
beforeCreate() { // 創建之前回調
console.log("beforeCreate");
},
created() { // 創建之后回調
console.log("created");
},
beforeMount() {
console.log("beforrMount");
},
// todo 常用, 異步操作, 比如發起ajax請求獲取數據, 添加定時器
mounted() { // 初始化顯示之后會立即調用一次
console.log("mounted");
this.intervalId = setInterval(() => {
console.log("干掉vm之后, 定時器還在跑, 內存泄露了");
this.isShow = !this.isShow;
}, 1000);
/*
如果下面不使用箭頭回調函數, this就是window, 而不是vm
* setInterval(() => {
this.isShow= !this.isShow;
},1000);
* */
},
// 更新階段
beforeUpdate() { //更新階段之前回調
console.log("beforeUpdate");
},
updated() { // 更新階段之后回調
console.log("updated");
},
// 死亡階段
// todo 常用 收尾工作
beforeDestroy() { // 死亡之前回調一次
console.log("beforeDestroy ");
clearInterval(this.intervalId);
},
destroyed() {
console.log("destroyed");
},
methods: {}
}
});
ES的語法糖,箭頭函數
比如在設置定時器時, 定時器中需要對vue的屬性進行操作,在定時器的代碼塊中this指的是定時器對象,es6的箭頭語法解決就這個問題, 在箭頭函數中this沒有的屬性,會到外層的vue中來找
this.intervalId = setInterval(() => {
console.log("干掉vm之后, 定時器還在跑, 內存泄露了");
this.isShow = !this.isShow;
}, 1000);
動畫
按照vue的下面的幾步要求, vue 會給目標元素添加或者移除特定的 css,實現動畫的效果
- 需要添加動畫的標簽被
<transition name="" > XXX </transition>
包裹
<div id="test">
<transition name="YYY">
<p v-show="isShow" class="">toggle</p>
<button @click="isShow=!isShow">toggle</button>
</transition>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#test',
data() {
return {
isShow: true
}
}
});
- 定義以 .YYY-開頭的 css屬性, 這個YYY就是上面自定義的YYY, 需要在這些自定義的屬性中指定過度的屬性以及隱藏的屬性
一個簡單的動畫效果標簽從隱藏->出現, 再從出現到隱藏的過程,就像下面這樣
v-enter v-enter-to v-leave v-leave-to
隱藏 出現 出現 隱藏
自定義這四個時期的狀態
/* 顯示的過度效果*/
.YYY-enter-active {
transition: all 1s;
}
/* 隱藏的過度效果*/
.YYY-leave-active {
transition: all 3s;
}
/* 從無到有的樣式 */
.YYY-enter {
opacity: 0;
}
/* 從有到無的樣式 */
.YYY-leave-to {
opacity: 0;
transform: translateX(20px); /* 離開時,向X軸的正方向移動20px*/
}
格式化時間的插件庫
點擊進入 moment.js網址 ,在這里可以找到需要引入的script標簽
點擊進入 moment.js的文檔 在文檔中可以找到對應的格式和例子
<div id="test">
<h2>顯示格式化的日期時間</h2>
<p>{{date}}</p>
<p>默認完整: {{date | dateFormat}}</p><!-- 一旦我們這么寫, 他就會把date的值,傳遞給dateFormat函數 -->
<p>年月日: {{date | dateFormat('YYYY-MM-DD')}}</p><!-- 一旦我們這么寫, 他就會把date的值,傳遞給dateFormat函數 -->
<p>時分秒: {{date | dateFormat('HH:mm:ss')}}</p><!-- 一旦我們這么寫, 他就會把date的值,傳遞給dateFormat函數 -->
</div>
/* 這個在官網上查找 */
<script type="text/javascript" src="https://cdn.bootcss.com/moment.js/2.21.0/moment.js"></script>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 自定義過濾器
Vue.filter('dateFormat',(value,format)=>{ /* Vue是函數對象 */
return moment(value).format(format || 'YYYY-MM-DD HH:mm:ss');
});
new Vue({
el:'#test',
data:{
date:new Date()
}
});
Es的語法糖
es6 的語法: 形參默認值 , 沒傳值的話,就使用默認值
function(value,format="YYYY-MM-DD"){
return moment(value).format(format);
}
vue的指令
常見的原生指令如下
v:text : 更新元素的textContent <br>
v:html : 更新元素的innerHtml<br>
v-if: true 如果為true,標簽才會輸出到頁面 <br>
v-else: 如果為false,標簽才會輸出到頁面 <br>
v-show: 通過控制display的樣式來控制顯示和隱藏<br>
v-for: 遍歷數組對象 <br>
v-on: 綁定監聽事件, 一般直接寫 @ <br>
v-bind: 強制綁定解析表達式 一般簡寫成 : <br>
v-model: 雙向數據綁定 <br>
ref: 指定唯一的標識, Vue對象可以通過 $els 屬性來訪問這個元素對象 <br>
v-cloak: 防止閃現可能應為網速的原因{{msg}} 一直解析不了, 於是用戶就看到它了,不友好, 於是 vue推出 與css配合 [v-cloak] {display:none} <br>
補充最后兩個
- ref 指定唯一的標識, Vue對象可以通過 $els 屬性來訪問這個元素對象
- 防止閃現可能應為網速的原因{{msg}} 一直解析不了, 於是用戶就看到它了,不友好, 於是 vue推出 與css配合 [v-cloak] {display:none}
例子:
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
[v-cloak] { /* 回去尋找有這個屬性名的標簽 [v-cloak] , 就是下面的p標簽 */
display:none
}
</style>
</head>
<body>
<div id="test">
<p ref="content123">哈哈哈哈</p>
<button @click="hint">提示</button>
// v-cloak="" + 上面的css 樣式避免 {{ }} 閃現的效果
<p v-cloak="">{{msg}}</p>
<br>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 注冊全局指令
Vue.directive('')
new Vue({
el:'#test',
data:{
msg: '嘿嘿'
} ,
methods:{
hint(){
// 因為 `<p ref="content123">哈哈哈哈</p>` 使用了ref,所以vue可以通過this.$refs.content123 找到指定的這個元素
alert(this.$refs.content123.textContent)
}
}
});