因為專案需要前後端分離,後端竟然不用控制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頁面上
- 計算屬性是針對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)
}
}
});