Vue安裝
安裝腳手架,使用全局安裝就可以
npm install -g @vue/cli
安裝完使用這個命令查看vue cli的版本
vue -V
初始化一個Vue項目
vue create lk-demo
然后會進(jìn)入一大堆的選項
1. Manually select features
2. 空格選擇,A全選,手動配置
運(yùn)行項目
npm run serve
將項目打包到dist目錄
npm run build
如果只有配置文件,那么需要手動安裝依賴才能運(yùn)行
npm install
Vue指令
雙括號表達(dá)式{{intro}}
- 可以直接展示出data中的數(shù)據(jù)
- 還可以對數(shù)據(jù)進(jìn)行一些字符串的操作:
{{intro.toUpperCase()}} - 注意雙括號表達(dá)式是寫在兩個標(biāo)簽中間的,這意味著你可以將它與其它字符進(jìn)行拼接
v-text
- v-text是一個屬性,如果倪同時還在兩個標(biāo)簽中間寫上其它字符,其它字符并不會顯示出來
- 僅僅是純文本,data中的數(shù)據(jù)會被完完整整地展示出來
v-html
- data中的數(shù)據(jù)會以html的形式展示出來
v-bind
這些指令都不需要在雙引號內(nèi)部寫雙括號表達(dá)式
v-bind:href
- 作用就是給HTML標(biāo)簽綁定某個屬性,該屬性在data里面:
v-bind:href="site" - 可以簡寫為:
:href="site"
v-bind:class
<div :class="oneClass">樣式類可以是字符串</div>
<div :class="{classOne: true, classTwo: true}">樣式類可以是對象</div>
<div :class="['classOne', 'classTwo']">樣式類可以是數(shù)組</div>
<div :class="[oneClass, twoClass]">樣式類可以是數(shù)組</div>
- 如果是字符串,那么引號里的是data里的屬性
- 如果是對象,對象里的就是樣式表里的選擇器
- 如果是數(shù)組,數(shù)組里的字符串也是樣式表里的選擇器
- 同樣是數(shù)組,但是數(shù)組里不是字符串,那就可以寫data里的數(shù)據(jù)
v-bind:style
<div
style="width: 300px; height: 200px; margin: 10px auto;"
:style="{backgroundColor: bgColor, fontSize: fSize}">樣式類可以是字符串</div>
- v-bind:style的方式同樣可以給元素綁定樣式,只不過是內(nèi)聯(lián)樣式。
- 注意屬性名采用駝峰命名法,屬性實在data中定義的。
v-on:click
- 就是給該元素綁定某個methods中的事件
- 可以不傳遞參數(shù):
v-on:click="study",也可以傳遞參數(shù)@click="study('小撩')" - 當(dāng)然,你應(yīng)該也看出來了,它可以簡寫:
@click="study" - 傳遞參數(shù)之后,用什么來接收參數(shù)呢?首先方法里面的形參一定是有的,方法里面,可以用
${name},來接收參數(shù)。 -
<button @click="flag = !flag">切換</button>,@click不僅可以用來綁定函數(shù),還可以直接把方法寫在里面。
study(name){
alert(`${name},祝你學(xué)有所成!`);
}
v-model
- 該指令的作用就是將它所綁定的數(shù)據(jù)和data中的數(shù)據(jù)實現(xiàn)一個雙向綁定,實時互通。
- 用法就是:
v-model="msg"
v-if
<div v-if="flag">今晚要上課!</div>
<div v-else>今晚不上課!</div>
- v-if還有與之配套的v-else
- v-if并不是通過設(shè)置css屬性display完成隱藏的
v-show
<div v-show="flag">今晚講Vue!</div>
<div v-show="!flag">今晚不講Vue!</div>
- 是通過設(shè)置
display: none;來隱藏元素的。因此如果需要頻繁進(jìn)行顯示、隱藏操作的話,它是比v-if更好的選擇。
v-for
遍歷數(shù)組
<ul>
<li v-for="(person, index) in persons" :key="personsKeys[index]">
ID: {{personsKeys[index]}} ---- {{index}} ) 姓名:{{person.name}}, 年齡:{{person.age}}, 性別:{{person.sex}}
</li>
</ul>
- v-for應(yīng)該用在li標(biāo)簽而不是ul標(biāo)簽上
- 注意括號里的第一個參數(shù)就相當(dāng)于數(shù)組里的每一項,使用它就可以用對象的付出調(diào)用其各個數(shù)據(jù)
注意:這里還涉及到shortid的使用
安裝
shortid:npm i shortid --save在該組件中導(dǎo)入:
import shortId from 'shortid'在data中要有
personsKeys這個數(shù)據(jù),它是一個空的數(shù)組注意要添加一個mounted掛載點,調(diào)用shortid自動產(chǎn)生一個隨機(jī)數(shù)作為id
mounted() {
this.personsKeys = this.persons.map(v=>shortId.generate())
}
- 然后就可以如上面代碼部分那樣使用了
- v-for可能需要使用
:key這個屬性,不然的話可能在控制臺會報警告
遍歷對象
<ul>
<li v-for="(item, key) in persons[0]">
{{key}} --- {{item}}
</li>
</ul>
- key就是對象的key
- item相當(dāng)于對象的value
其它指令
v-pre
<p v-pre>{{intro}}</p>
- 貌似加了這個屬性之后,雙括號表達(dá)式就不會解析了,而會被當(dāng)作純文本展示在頁面上。
- 它并不需要參數(shù)
v-cloak
<p v-cloak>{{message}}</p>
- 當(dāng)某些情況下,Vue加載有點慢,這個時候可能就會以源碼的形式展示出來,比如這里的雙括號表達(dá)式,這個時候使用v-cloak就可以避免這個閃爍問題。
v-once
<p v-once>{{name}}</p>
- 它會使得雙括號表達(dá)式僅解析一次,之后即便通過v-model改變了data中的數(shù)據(jù)也不會更新
ref的使用
<p ref="fish">我是一只魚</p>
- 這東西并不是一個指令,好像是引用什么的吧。官方的解釋是:ref 被用來給元素或子組件注冊引用信息。引用信息將會注冊在父組件的 $refs 對象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子組件上,引用就指向組件。
- 其實可以把它看做id屬性,通過它獲取到該元素然后就可以進(jìn)行某些操作。
- 如果想要輸出它里面的內(nèi)容,可以采用這種方式。注意這里的innerHTML只會輸出<p>標(biāo)簽中間部分的html內(nèi)容
console.log(this.$refs.fish.innerHTML);
自定義全局和局部指令
自定義全局指令
Vue.directive('upper-word', (el, binding)=>{
console.log(el, binding);
el.textContent = binding.value.toUpperCase();
});
- 全局指令需要寫在main.js中
- Vue的指令都需要在前面加一個
v-
自定義局部指令
directives: {
'lower-word'(el, binding){
console.log(el, binding);
el.textContent = binding.value.toLowerCase();
}
}
- 局部指令的定義使用的是
directives這個鉤子選項 - 注意它和全局指令的定義有一些細(xì)微的不同。
計算屬性
- 計算屬性和data中的數(shù)據(jù)一樣,都可以使用v-model來進(jìn)行綁定
- 計算屬性都有
set()和get()方法,get()用于對data中的數(shù)據(jù)進(jìn)行計算,set()用于將從它獲取到的數(shù)據(jù)賦給data中的數(shù)據(jù)
fullNameTwo: {
get(){
// console.log(`調(diào)用了fullNameTwo的getter方法`);
return this.firstName + '·' + this.lastName;
},
set(value){
// console.log(`調(diào)用了fullNameTwo的setter方法,值:${value}`);
// 1.更新firstName和lastName
let names = value.split('·');
console.log(names);
this.firstName = names[0];
this.lastName = names[1];
}
}
數(shù)據(jù)監(jiān)聽watch
// 配置watch
watch: {
// 監(jiān)聽firstName
firstName(value){
console.log(`watch監(jiān)視到firstName發(fā)生改變:${value}`);
// 更新fullNameThree
this.fullNameThree = value + '·' + this.lastName;
},
// 監(jiān)聽lastName
lastName(value){
console.log(`watch監(jiān)視到lastName發(fā)生改變:${value}`);
// 更新fullNameThree
this.fullNameThree = this.firstName + '·' + value;
}
}
- watch和data、computed是同一級屬性,可以用來對data中的數(shù)據(jù)進(jìn)行監(jiān)聽。
- watch里面每個函數(shù)的名字與data里的屬性相同,當(dāng)該屬性被改變,就會自動調(diào)用該方法。
- 該方法可以內(nèi)部同樣可以中data中的數(shù)據(jù)進(jìn)行運(yùn)算。
事件處理
事件對象
<button @click="clickBtn('撩課', $event)">點我</button>
- 可以在觸發(fā)事件的同時將參數(shù)和事件對象都傳遞過去,該事件對象里面有很多很多與時間相關(guān)的屬性
事件修飾符@click.prevent
<a @click.prevent="aClick">撩課</a>
- 它的存在是可以阻止該元素默認(rèn)的行為的。
- 比如上面這個是個超鏈接,但是現(xiàn)在點擊之后并不會跳轉(zhuǎn),而是執(zhí)行aClick()方法
- 如果是form,也會阻止掉自動提交的方法。
事件修飾符@click.stop
<div style="width: 100px; height: 100px; background-color:red;" @click="divClick">
<button @click.stop="btnClick">點我</button>
</div>
- 該修飾符是用在子元素標(biāo)簽上的,可以用來阻止事件冒泡。也就是說,使用之后,點擊子元素的標(biāo)簽并不會觸發(fā)父元素上的事件。
按鍵修飾符
<input type="text" @keyup.enter="dealKey">
dealKey(event){
console.log(event);
console.log(event['keyCode']);
}
- 使用@keyup可以在后面跟上很多種按鍵,也就是當(dāng)按下然后松開之后就會執(zhí)行該事件。
- 另外在觸發(fā)事件的對象里可以通過
event['keyCode']獲取按鍵的ASCII碼
過濾器
全局過濾器
Vue.filter('wholeMoneyFormat', (value)=>{
return '¥' + Number(value).toFixed(4);
});
<p>{{money | wholeMoneyFormat}}</p>
- 全局過濾器需要寫在main.js中,如上面的代碼所示
- 上面的過濾器的意思是:將傳過來的參數(shù)轉(zhuǎn)成數(shù)字,并且保留四位小數(shù),然后在前面加上一個人民幣的符號并返回。
- 使用的時候需要使用雙括號表達(dá)式,第一個是需要過濾的參數(shù),第二過濾器的名字,中間用豎線隔開。
局部過濾器
filters: {
moneyFormat(value){
return '¥' + Number(value).toFixed(2);
},
timeFormat(value, format='YYYY-MM-DD HH:mm:ss'){
return moment(value).format(format);
}
}
- 局部過濾器的定義如上面的代碼所示。使用方法和全局過濾器是完全一樣的
- 第一個金錢過濾器,是將傳遞過來的參數(shù)轉(zhuǎn)成保留兩位小數(shù)的數(shù)字,并且在前面加上一個人民幣符號。
- 第二個時間過濾器需要使用moment插件,將value中的事件轉(zhuǎn)成format中的時間格式。當(dāng)然也可以在調(diào)用的時候手動修改過濾的時間格式。
<p>{{time | timeFormat('YYYY-MM-DD')}}</p>
Vue過渡和動畫
只使用類
<button @click="show = !show">切換</button>
<transition name="fade">
<div class="box" v-if="show">撩課學(xué)院</div>
</transition>
- 在Vue中使用動畫,需要使用transition標(biāo)簽,該標(biāo)簽必須具備一個name屬性
- 我們可以通過設(shè)置一個v-if來切換該元素出現(xiàn)和消失時的動畫
- 然后在樣式表中寫好終點的樣式以及進(jìn)行中的樣式。選擇器的名字需要使用到上面提到的name屬性,比如:
.fade-enter,.fade-leave-to,.fade-enter-active,.fade-leave-active - 下面這個就是一個CSS的例子
.fade-enter, .fade-leave-to{
opacity: 0;
transform: translateX(200px) scale(3);
}
.fade-enter-active, .fade-leave-active{
transition: all 2s ease-in-out;
}
使用@keyframes
- 注意:如果圖片采用:src這種方式導(dǎo)入的話,需要先導(dǎo)入這樣圖片
import pic from '@/assets/img_02.jpg',然后將其作為data中的一個數(shù)據(jù) - 兩個CSS類,第一個表示進(jìn)入時候的動畫以及持續(xù)時間。第二個表示消失時候的動畫以及持續(xù)時間,并且表明和第一個動畫是相反的。
- 詳細(xì)代碼如下
<button @click="flag = !flag">切換</button>
<p></p>
<transition name="bounce">
<img v-if="flag" :src="pic" alt="">
</transition>
.bounce-enter-active {
animation: bounce 1s;
}
.bounce-leave-active {
animation: bounce 1s reverse;
}
@keyframes bounce {
0% {
transform: scale(0);
}
25% {
transform: scale(0.2);
}
50% {
transform: scale(0.4);
}
75% {
transform: scale(0.6);
}
100% {
transform: scale(1);
}
}
導(dǎo)入外部的動畫CSS庫
<button @click="flag = !flag">切換</button>
<p></p>
<transition
enter-active-class="animated rollIn"
leave-active-class="animated rollOut"
:duration="{ enter: 1000, leave: 500 }"
>
<img v-if="flag" :src="pic" alt="">
</transition>
- 該動畫庫的使用情況如上面的代碼
- 安裝animate.css:
npm i animate.css --save - 然后導(dǎo)入
animate.css:import animate from 'animate.css' - transition中的三個屬性分別表示進(jìn)入時候的動畫、消失時候的動畫,以及進(jìn)入和消失的持續(xù)時間。
生命周期
- Vue的生命周期按順序有:
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed - 一般來說只要該組件啟用,前面四個生命周期都會很快依次調(diào)用。
- 然后,每次修改data中的數(shù)據(jù),都會調(diào)用
beforeUpdate、updated兩個方法 - 最后,如果想要進(jìn)入銷毀的生命周期需要主動觸發(fā)下面代碼的這個方法
- 另外,定時器如果在生命周期里,注意它是異步的,即便已經(jīng)銷毀可能定時器仍然在工作。這個時候最好在銷毀的生命周期里設(shè)置一個清除定時器的方法。
destory(){
this.$destroy();
}
組件通信
子組件通知父組件,我觸發(fā)了某個方法
父組件
- App.vue在組件上面需要綁定事件
- 在methods中寫事件
<CustomEvents @btnClick="deleteP"/>
deleteP(args){
console.log(args);
this.$refs.word.remove();
}
子組件
- 子組件同樣需要給某個元素綁定事件
- 但不是自己處理事件,而是告訴父元素,自己觸發(fā)了哪個事件,參數(shù)是什么
btnClick(){
// 告訴父組件,我點擊了按鈕
this.$emit('btnClick', {name: '哈哈哈', sex:'男'});
// TODO
}
父組件向子組件傳遞數(shù)據(jù)和方法:props
<PropsComponent :age=25 :person="p" :log-person="logPerson" />
props: {
name: String,
age: Number,
person: Object,
logPerson: Function
}
*/
props: {
name: {type: String, required: true, default: '撩課'},
age: {type: Number, required: true, default: 20},
person: Object,
logPerson: Function
}
- 父組件通過在組件上寫
:age的形式,向子組件傳遞數(shù)據(jù)和方法。這些數(shù)據(jù)和方法都是在父組件中已經(jīng)定義好的 - 子組件需要通過props這個鉤子選項接收父組件傳遞過來的數(shù)據(jù)和方法,有上面兩種形式
- 然后子組件就可以像使用自己的數(shù)據(jù)和方法一樣使用父組件的數(shù)據(jù)和方法
自定義事件
- 由子組件發(fā)送給父組件,函數(shù)名稱,函數(shù)參數(shù)
this.$emit('addTodo', todo); - 父組件需要監(jiān)聽這個組件:
<Header ref="header"/> - 然后給父組件綁定自定義事件的監(jiān)聽:
this.$refs.header.$on('addTodo', this.addTodo);注意這是寫在mounted這個鉤子選項里的;然后不用寫()來接參數(shù),這種寫法就會自動把todo參數(shù)傳遞過來
發(fā)布訂閱模式
- 安裝pubsub插件:
npm install --save pubsub-js - 在App.vue和item中引入pubsub-js:
import PubSub from 'pubsub-js',反正哪里需要發(fā)布、訂閱就需要引入 - 子組件發(fā)布消息:
PubSub.publish('delTodo', this.index) - 父組件接收消息。代碼如下。注意,同樣寫在mounted這個鉤子選項下,token就是子組件傳遞過來的參數(shù)
PubSub.subscribe('delTodo', (msg, token)=>{
// console.log(msg, token);
this.delTodo(token);
});
插槽
<label>
<slot name="isCheck"></slot>
</label>
<span>
<slot name="finish"></slot>
</span>
<slot name="delete"></slot>
<Footer>
<input slot="isCheck" type="checkbox" v-model="isCheck"/>
<span slot="finish">已完成{{finishedCount}}件 / 總計{{todos.length}}件</span>
<button slot="delete" class="btn btn-warning" @click="delFinishedTodos">清除已完成任務(wù)</button>
</Footer>
- footer中不用自己寫代碼了,直接把插槽留出來就行。使用的是<slot>標(biāo)簽,注意必須有name屬性。
- 父組件里面直接往子組件里面插入
- 這樣就可以直接調(diào)用父組件里面的方法和計算屬性了
Vuex
- 安裝:
vue add vuex
Vue router
Vue-router的基本使用
- 安裝:npm install vue-router --save
- 新建router.js文件
- 在main.js中引入,并且放入Vue的實例中
- 新建view頁面,并且在App.vue中配置路由導(dǎo)航和路由出口
router.js
import Vue from 'vue'
import Router from 'vue-router'
// 引入頁面
import Home from './views/Home'
import About from './views/About'
Vue.use(Router);
export default new Router({
// history模式就是沒有#
// mode: 'history',
routes: [
{ path: '/', redirect: '/home' },
// 下面這個就是重定向到一個命名路由,name就是路由的名字
// { path: '/', redirect: {name: 'about'} },
// 下面這個是使用一個方法重定向到目標(biāo)路由
// { path: '/', redirect: to => { return '/home'}},
// 這里name屬性僅僅只是這個路由的名字,暫時沒有用處
{path: '/home', name: 'home', component: Home},
{path: '/about', name: 'about', component: About},
]
})
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false;
new Vue({
router,
render: h => h(App),
}).$mount('#app');
在App.vue中配置路由導(dǎo)航和路由出口
<!--設(shè)置路由導(dǎo)航-->
<div id="nav">
<router-link to="/home">首頁</router-link>
<router-link to="/about">關(guān)于</router-link>
</div>
<!--設(shè)置路由出口-->
<router-view></router-view>
路由history和hash模式
獲取路由參數(shù)
import Vue from 'vue'
import Router from 'vue-router'
// 引入頁面
import Home from './views/Home'
import About from './views/About'
import Mine from './views/Mine'
Vue.use(Router);
/*
let func = ({params, query})=>{
return {
name: params.name,
sex: params.sex,
height: query.height,
dog: query.dog,
}
};
*/
let func = (route)=>{
return {
name: route.params.name,
sex: route.params.sex,
height: route.query.height,
dog: route.query.dog,
}
};
export default new Router({
routes: [
{ path: '/', redirect: '/home' },
{path: '/home', name: 'home', component: Home},
{path: '/about', name: 'about', component: About},
// {path: '/mine/:name/:sex', name: 'mine', component: Mine}
// {path: '/mine', name: 'mine', component: Mine, props: {name: '小撩'}}
// {path: '/mine/:name/:sex', name: 'mine', component: Mine, props: true}
{path: '/mine/:name/:sex', name: 'mine', component: Mine, props: func}
]
})
Mine.vue
<template>
<div id="mine">
<h2>個人中心</h2>
<p>------------------------------------------</p>
<h2>根據(jù)路由對象獲取的路徑參數(shù)</h2>
<p>姓名:{{$route.params.name}}</p>
<p>性別:{{$route.params.sex}}</p>
<p>身高:{{$route.query.height}}</p>
<p>小狗:{{$route.query.dog}}</p>
<h2>根據(jù)屬性對象獲取的路徑參數(shù)</h2>
<p>姓名:{{name}}</p>
<p>性別:{{sex}}</p>
<p>身高:{{height}}</p>
<p>小狗:{{dog}}</p>
</div>
</template>
<script>
export default {
name: "Mine",
props: ['name', 'sex', 'height', 'dog'],
created() {
console.log(this.$route);
/*
console.log(this.$route);
console.log(this.$route.path);
console.log(this.$route.params);
console.log(this.$route.query);
*/
// console.log(this.$router);
}
}
</script>
<style scoped>
#mine{
width: 300px;
height: 500px;
background-color: orange;
margin: 0 auto;
}
h2{
color: green;
}
</style>
- 關(guān)于獲取路由參數(shù)的代碼在上邊
- 所謂的獲取路由參數(shù)其實就是指,數(shù)據(jù)是在鏈接上的,我們需要通過某種方式獲取到這些數(shù)據(jù)
- 通過路由傳遞參數(shù)主要有幾種方式:
-
{path: '/mine/:name/:sex', name: 'mine', component: Mine},通過在路由上寫冒號 -
{path: '/mine', name: 'mine', component: Mine, props: {name: '小撩'}},通過props獲取數(shù)據(jù),注意需要在該組件內(nèi)部寫出props這個鉤子選項,并且內(nèi)容為這些數(shù)據(jù)的變量名 -
{path: '/mine/:name/:sex', name: 'mine', component: Mine, props: true},通過以上兩種方式獲取數(shù)據(jù) -
{path: '/mine/:name/:sex', name: 'mine', component: Mine, props: func},通過地址和方法傳遞數(shù)據(jù)
嵌套路由
import Vue from 'vue'
import Router from 'vue-router'
// 一級界面
import Home from './views/Home'
import About from './views/About'
import Mine from './views/Mine'
// 二級界面
import News from './views/News'
import Shop from './views/Shop'
Vue.use(Router);
export default new Router({
routes: [
{ path: '/', redirect: '/home' },
{
path: '/home',
name: 'home',
component: Home,
children: [
{ path: '/home', redirect: '/home/news' },
{path: 'news', name: 'news', component: News},
{path: 'shop', name: 'shop', component: Shop},
]
},
{path: '/about', name: 'about', component: About},
{path: '/mine', name: 'mine', component: Mine}
]
})
- 所謂的嵌套路由就是斜杠后面還有斜杠
- 嵌套路由的配置其它文件并沒有什么不同,主要還是在router.js上的不同
- 嵌套路由的配置主要小心:子路由不需要在前面寫/
全局路由前置和后置守衛(wèi)
import Vue from 'vue'
import Router from 'vue-router'
// 一級界面
import Login from './views/Login'
import DashBoard from './views/DashBoard'
// 二級界面
import Home from './views/Home'
// import About from './views/About'
import Mine from './views/Mine'
const About = ()=> import('./views/About');
Vue.use(Router);
const router = new Router({
routes: [
{ path: '/', redirect: '/dashboard' },
{
path: '/dashboard',
name: 'dashboard',
component: DashBoard,
children: [
{ path: '/dashboard', redirect: '/dashboard/home' },
{path: 'home', name: 'home', component: Home,},
{path: 'about', name: 'about', component: About},
{path: 'mine', name: 'mine', component: Mine}
],
},
{path: '/login', name: 'login', component: Login}
]
});
// 全局路由前置守衛(wèi)
router.beforeEach((to, from, next)=>{
// console.log(to, from);
if(to.path !== '/login'){ // 驗證是否登錄
if(window.isLogin){ // 已經(jīng)登錄
next();
}else { // 沒有登錄
// 將你要去的地址也傳到登錄頁去,這樣當(dāng)你登錄成功之后可以立即重定向到該頁
// next('/login?redirect='+ to.path);
// next('/login?redirect=/dashboard/mine');
next('/login');
}
}else { // 不需要驗證
next();
}
// 放行
next();
});
// 全局路由后置守衛(wèi)
router.afterEach((to, from) => {
// console.log('來了!');
});
export default router;
- 主要通過前置守衛(wèi)來控制什么情況下用戶可以進(jìn)入頁面
- 如果用戶進(jìn)的是登錄頁,無需檢查直接放行
- 如果用戶進(jìn)的是其它頁,檢查是否登錄,如果沒有登錄前往登錄頁,如果已經(jīng)登錄放行
- 其它情況一律放行
mine.vue
export default {
name: "Mine",
beforeRouteEnter(to, from, next){
console.log('進(jìn)入之前調(diào)用');
next();
},
beforeRouteUpdate(to, from, next){
console.log('路由的參數(shù)變了');
next();
},
beforeRouteLeave(to, from, next){
console.log('路由離開前調(diào)用');
next();
}
}
這里主要設(shè)置了幾個生命周期的鉤子選項,在進(jìn)入這個組件之前、路由參數(shù)改變、以及路由離開的時候都會分別調(diào)用這里的方法
login.vue
<template>
<div>
<h2>登錄界面</h2>
<button @click="login">登錄</button>
</div>
</template>
<script>
export default {
name: "Login",
methods: {
login(){
// 1. 登錄成功
window.isLogin = true;
// 2. 獲取回調(diào)地址
const redirect = this.$route.query.redirect;
if(redirect){ // 有回調(diào)地址
this.$router.push(redirect);
}else { // 沒有回調(diào)地址
// 去首頁
this.$router.replace('/');
}
}
}
}
</script>
<style scoped>
</style>
- 登錄的這個頁面主要負(fù)責(zé)處理登錄相關(guān)的事務(wù)
- 當(dāng)用戶點擊登錄按鈕之后,首先將全局的isLogin設(shè)置為true
- 獲取用戶的回調(diào)地址,如果有回調(diào)地址則直接前往;如果沒有則前往主頁
數(shù)據(jù)本地化
- 手寫一個工具類
- 在app.vue中導(dǎo)入該工具類
- data中的數(shù)據(jù)需要從localStorage中讀取,當(dāng)todos數(shù)據(jù)發(fā)生任何改變就要把該數(shù)據(jù)存儲到localStorage
工具類
const LK_TODO = 'lk_todo';
export default {
readTodos(){
return JSON.parse(localStorage.getItem(LK_TODO) || '[]');
},
saveTodos(todos){
console.log(todos);
localStorage.setItem(LK_TODO, JSON.stringify(todos));
}
}
- 使用localStorage無論是讀還是取,都需要傳遞一個key,注意這里僅僅一個數(shù)組,所以使用了一個字符串常量作為key
- 第一個讀取數(shù)據(jù)的方法內(nèi)部有
|| '[]'是為了在任何情況下都能夠讀到數(shù)據(jù),哪怕是一個空的數(shù)組
讀數(shù)據(jù)
todos: localStorageUtil.readTodos()
取數(shù)據(jù)
watch: {
// 深度監(jiān)視
todos: {
handler: localStorageUtil.saveTodos,
deep: true, // 深度監(jiān)視
// immediate: true
}
}
- 取數(shù)據(jù)需要深度監(jiān)視,因為數(shù)組里面還有對象,只有深度監(jiān)視能夠監(jiān)聽數(shù)組里面對象屬性的改變。
- immediate如果為true,就是當(dāng)初始化的時候就立即執(zhí)行handler方法,否則就是當(dāng)todos發(fā)生改變才會執(zhí)行handler方法。
UI框架
elementUI
- element文檔:https://element.eleme.cn/#/zh-CN/component/layout
- 安裝:
npm i element-ui -S - 給Vue添加UI庫:
vue add element - 選項:Fully import、N、CN
-
vue ui:使用可視化面板管理項目(一般不會用的)
做完以上幾步就會發(fā)現(xiàn),自動集成了plugins/element.js,并且自動在main.js中導(dǎo)入了。然后就可以直接在app.vue中使用了。
- 復(fù)制HTML代碼到模板里面
- 復(fù)制script代碼data部分到app.vue里,注意這里和vue的代碼結(jié)構(gòu)是完全一樣的。
VantUI
- 文檔:https://youzan.github.io/vant/#/zh-CN/
- 安裝:
npm i vant -S - 配置按需加載,引入babel:
npm i babel-plugin-import -D - 配置babel.config.js,可以從文檔里復(fù)制粘貼
- 在main.js從引入需要的組件,或者單獨(dú)做一個js文件,然后通過import來引入
配置babel.config.js
module.exports = {
presets: [
'@vue/app'
],
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
};
在main.js中引入需要的組件
import { Button } from 'vant';
Vue.use(Button);
import { Cell, CellGroup } from 'vant';
Vue.use(Cell).use(CellGroup);
import { DatetimePicker } from 'vant';
Vue.use(DatetimePicker);
插件的安裝和使用
shortid
<ul>
<li v-for="(person, index) in persons" :key="personsKeys[index]">
ID: {{personsKeys[index]}} ---- {{index}} ) 姓名:{{person.name}}, 年齡:{{person.age}}, 性別:{{person.sex}}
</li>
</ul>
- 安裝
shortid:npm i shortid --save - 在該組件中導(dǎo)入:
import shortId from 'shortid' - 在data中要有
personsKeys這個數(shù)據(jù),它是一個空的數(shù)組 - 注意要添加一個mounted掛載點,調(diào)用shortid自動產(chǎn)生一個隨機(jī)數(shù)作為id
mounted() {
this.personsKeys = this.persons.map(v=>shortId.generate())
}
- 然后就可以如上面代碼部分那樣使用了
- v-for可能需要使用
:key這個屬性,不然的話可能在控制臺會報警告
moment
- 安裝:
npm i moment --save - 使用:注意任何插件的使用,都需要在該組件內(nèi)部首先導(dǎo)入
import moment from 'moment'。然后就可以像調(diào)用函數(shù)一樣使用該插件了
案例匯總
v-for排序小案例
- 數(shù)據(jù)來源于data中已經(jīng)定義好的
- v-for的使用和之前遍歷數(shù)組的時候是一樣的,同樣用到了shortid,但是并沒有把shortid展示在頁面上,而是用索引加1的方式
- v-for里的in,是已經(jīng)排序好的數(shù)組,該數(shù)組是使用計算屬性而得到的
- 排序分為默認(rèn),按年齡升序、降序,是通過向計算屬性里傳遞不同參數(shù)而區(qū)別開的。
- 排序的代碼如下。注意需要先過濾以下數(shù)組,然后根據(jù)條件進(jìn)行排序
computed: {
filterPersons() {
// 1. 獲取數(shù)據(jù)
let {searchName, persons, orderType} = this;
// 2. 取出數(shù)組中的數(shù)據(jù)
let arr = [...persons];
// 3. 過濾數(shù)組
if (searchName.trim()) {
arr = persons.filter(p => p.name.indexOf(searchName) !== -1);
}
// 4. 排序
if (orderType) {
arr.sort((p1, p2) => {
if (orderType === 1) { // 降序
return p2.age - p1.age
} else { // 升序
return p1.age - p2.age
}
});
}
return arr;
}
},
表單添加刪除小案例
- data里有有已經(jīng)存在的數(shù)據(jù)和一個新的空對象對象,這個空對象可以設(shè)定一些默認(rèn)值
- 每個input標(biāo)簽都會設(shè)置v-model,分別和空對象里的各個屬性綁定起來
- 添加數(shù)據(jù)的時候:首先使用解構(gòu)賦值從該對象中獲取輸入框的數(shù)據(jù);然后對數(shù)據(jù)進(jìn)行驗證,比如不能為空;插入數(shù)據(jù)直接使用數(shù)組的unshift方法就可以;最后記得要清空這個空對象,讓其恢復(fù)默認(rèn)值
- 點擊刪除按鈕的時候:直接調(diào)用splice方法就可以,第一個參數(shù)是索引,第二個參數(shù)是從該索引的位置刪除幾個元素
todoList
todoList第一版
Header
- Header的主要任務(wù)是為todoList添加新任務(wù),主要方法就是添加todo
- 方法的實現(xiàn)是在App.vue,然后使用
:addTodo="addTodo"的方式向子組件傳遞自己的方法。當(dāng)然子組件需要用props來接收。 - 子組件只負(fù)責(zé)一個數(shù)據(jù)title,因為創(chuàng)建的時候任務(wù)不可能完成,所以finished也就是那個復(fù)選框默認(rèn)設(shè)置為false就行了。
- 子組件仍需要一些其它的操作:①:判斷用戶輸入的是否是空字符串;②:根據(jù)用戶的輸入拼接一個todo,它主要就是包含title、finished兩個數(shù)據(jù);③:調(diào)用父組件的添加方法;④:清空title數(shù)據(jù)
List
- List的主要任務(wù)就是:①:當(dāng)鼠標(biāo)移動到某一條todo上時,該todo的背景顏色變?yōu)榛疑?,并且顯示出來刪除按鈕
- App.vue需要向List傳遞todos數(shù)據(jù)和刪除某一條todo的方法
- List使用props接收todos和刪除方法。然后使用v-for對todos進(jìn)行遍歷,當(dāng)然遍歷的每一個item就是它的子組件,List需要向item傳遞它的數(shù)據(jù)todo、該todo在todos中的索引,以及刪除todo的方法
- 子子組件item首先需要接收上面?zhèn)鬟f過來的兩個數(shù)據(jù)一個方法。
- item實現(xiàn)鼠標(biāo)移入就出現(xiàn)刪除按鈕以及背景顏色的改變是通過
@mouseenter、@mouseleave兩個觸發(fā)事件的方式,分別向同一個事件傳遞true、false兩個不同的參數(shù)。 - 背景顏色是通過設(shè)置
:style使用的是data里面的某個值,這樣就可以使用方法改變data的這個屬性來調(diào)整背景顏色 - 按鈕的顯示和隱藏使用的是v-show,同樣是data里的一個數(shù)據(jù)控制true還是false,這樣我們在方法里就可以設(shè)置該屬性為true,來讓按鈕顯示出來
- 刪除該todo直接使用父組件里的方法就可以了
Footer
- footer的作用主要是:①:當(dāng)上面的每一條todo都選中的時候,它的復(fù)選框也要選中;②:全選;③:點擊刪除所有finished的按鈕。
- 選中所有todo和刪除已經(jīng)完成的todo都是在app.vue中定義的。然后將這兩個方法以及todos的數(shù)據(jù)全部傳遞給footer組件
- footer里面很重要的一個功能就是要知道目前已經(jīng)完成的todo數(shù)量,這是通過計算屬性實現(xiàn)的。代碼見下方。使用的是數(shù)組的reduce這個方法,其實就是一個遍歷+1計數(shù)的操作。
- footer里面很重要的一個功能就是全選,也就是當(dāng)上面全部選中它也要選中,它如果選中,上面也要全部選中。這是通過給該復(fù)選框設(shè)置一個v-model,并且綁定一個計算屬性實現(xiàn)的。
- 其中g(shù)et()是處理第一個的,判斷條件就是目前已經(jīng)完成的todo數(shù)量和todos的長度相等,并且長度不為0.
- 其中set()是處理第二個的,它會將目前該計算屬性的值true、false作為參數(shù)調(diào)用父組件的全選方法。父組件的全選方法就會遍歷整個todos,將其中的finished屬性全部設(shè)定為參數(shù)。需要注意的是,這里有一個小竅門,就是footer的全選按鈕和v-model綁定,它選中那么set()方法的參數(shù)就是true。
- 最后刪除所有已經(jīng)完成的todo則很簡單了,不需要傳遞任何參數(shù),直接調(diào)用父組件的方法即可。
finishedCount(){
return this.todos.reduce((total, todo)=> total + (todo.finished ? 1 : 0), 0);
},