我的Vue學(xué)習(xí)筆記

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}}

  1. 可以直接展示出data中的數(shù)據(jù)
  2. 還可以對數(shù)據(jù)進(jìn)行一些字符串的操作:{{intro.toUpperCase()}}
  3. 注意雙括號表達(dá)式是寫在兩個標(biāo)簽中間的,這意味著你可以將它與其它字符進(jìn)行拼接

v-text

  1. v-text是一個屬性,如果倪同時還在兩個標(biāo)簽中間寫上其它字符,其它字符并不會顯示出來
  2. 僅僅是純文本,data中的數(shù)據(jù)會被完完整整地展示出來

v-html

  1. data中的數(shù)據(jù)會以html的形式展示出來

v-bind

這些指令都不需要在雙引號內(nèi)部寫雙括號表達(dá)式

v-bind:href

  1. 作用就是給HTML標(biāo)簽綁定某個屬性,該屬性在data里面:v-bind:href="site"
  2. 可以簡寫為::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>
  1. 如果是字符串,那么引號里的是data里的屬性
  2. 如果是對象,對象里的就是樣式表里的選擇器
  3. 如果是數(shù)組,數(shù)組里的字符串也是樣式表里的選擇器
  4. 同樣是數(shù)組,但是數(shù)組里不是字符串,那就可以寫data里的數(shù)據(jù)

v-bind:style

<div
style="width: 300px; height: 200px; margin: 10px auto;"
:style="{backgroundColor: bgColor, fontSize: fSize}">樣式類可以是字符串</div>
  1. v-bind:style的方式同樣可以給元素綁定樣式,只不過是內(nèi)聯(lián)樣式。
  2. 注意屬性名采用駝峰命名法,屬性實在data中定義的。

v-on:click

  1. 就是給該元素綁定某個methods中的事件
  2. 可以不傳遞參數(shù):v-on:click="study",也可以傳遞參數(shù)@click="study('小撩')"
  3. 當(dāng)然,你應(yīng)該也看出來了,它可以簡寫:@click="study"
  4. 傳遞參數(shù)之后,用什么來接收參數(shù)呢?首先方法里面的形參一定是有的,方法里面,可以用${name},來接收參數(shù)。
  5. <button @click="flag = !flag">切換</button>,@click不僅可以用來綁定函數(shù),還可以直接把方法寫在里面。
study(name){
    alert(`${name},祝你學(xué)有所成!`);
}

v-model

  1. 該指令的作用就是將它所綁定的數(shù)據(jù)和data中的數(shù)據(jù)實現(xiàn)一個雙向綁定,實時互通。
  2. 用法就是:v-model="msg"

v-if

<div v-if="flag">今晚要上課!</div>
<div v-else>今晚不上課!</div>
  1. v-if還有與之配套的v-else
  2. v-if并不是通過設(shè)置css屬性display完成隱藏的

v-show

<div v-show="flag">今晚講Vue!</div>
<div v-show="!flag">今晚不講Vue!</div>
  1. 是通過設(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>
  1. v-for應(yīng)該用在li標(biāo)簽而不是ul標(biāo)簽上
  2. 注意括號里的第一個參數(shù)就相當(dāng)于數(shù)組里的每一項,使用它就可以用對象的付出調(diào)用其各個數(shù)據(jù)

注意:這里還涉及到shortid的使用

  1. 安裝shortidnpm i shortid --save

  2. 在該組件中導(dǎo)入:import shortId from 'shortid'

  3. 在data中要有personsKeys這個數(shù)據(jù),它是一個空的數(shù)組

  4. 注意要添加一個mounted掛載點,調(diào)用shortid自動產(chǎn)生一個隨機(jī)數(shù)作為id

mounted() {
    this.personsKeys = this.persons.map(v=>shortId.generate())
}
  1. 然后就可以如上面代碼部分那樣使用了
  2. v-for可能需要使用:key這個屬性,不然的話可能在控制臺會報警告

遍歷對象

<ul>
    <li v-for="(item, key) in persons[0]">
        {{key}} --- {{item}}
    </li>
</ul>
  1. key就是對象的key
  2. item相當(dāng)于對象的value

其它指令

v-pre

<p v-pre>{{intro}}</p>
  1. 貌似加了這個屬性之后,雙括號表達(dá)式就不會解析了,而會被當(dāng)作純文本展示在頁面上。
  2. 它并不需要參數(shù)

v-cloak

<p v-cloak>{{message}}</p>
  1. 當(dāng)某些情況下,Vue加載有點慢,這個時候可能就會以源碼的形式展示出來,比如這里的雙括號表達(dá)式,這個時候使用v-cloak就可以避免這個閃爍問題。

v-once

<p v-once>{{name}}</p>
  1. 它會使得雙括號表達(dá)式僅解析一次,之后即便通過v-model改變了data中的數(shù)據(jù)也不會更新

ref的使用

<p ref="fish">我是一只魚</p>
  1. 這東西并不是一個指令,好像是引用什么的吧。官方的解釋是:ref 被用來給元素或子組件注冊引用信息。引用信息將會注冊在父組件的 $refs 對象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子組件上,引用就指向組件。
  2. 其實可以把它看做id屬性,通過它獲取到該元素然后就可以進(jìn)行某些操作。
  3. 如果想要輸出它里面的內(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();
});
  1. 全局指令需要寫在main.js中
  2. Vue的指令都需要在前面加一個v-

自定義局部指令

directives: {
    'lower-word'(el, binding){
        console.log(el, binding);
        el.textContent = binding.value.toLowerCase();
    }
}
  1. 局部指令的定義使用的是directives這個鉤子選項
  2. 注意它和全局指令的定義有一些細(xì)微的不同。

計算屬性

  1. 計算屬性和data中的數(shù)據(jù)一樣,都可以使用v-model來進(jìn)行綁定
  2. 計算屬性都有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;
    }
}
  1. watch和data、computed是同一級屬性,可以用來對data中的數(shù)據(jù)進(jìn)行監(jiān)聽。
  2. watch里面每個函數(shù)的名字與data里的屬性相同,當(dāng)該屬性被改變,就會自動調(diào)用該方法。
  3. 該方法可以內(nèi)部同樣可以中data中的數(shù)據(jù)進(jìn)行運(yùn)算。

事件處理

事件對象

<button @click="clickBtn('撩課', $event)">點我</button>
  1. 可以在觸發(fā)事件的同時將參數(shù)和事件對象都傳遞過去,該事件對象里面有很多很多與時間相關(guān)的屬性

事件修飾符@click.prevent

<a  @click.prevent="aClick">撩課</a>
  1. 它的存在是可以阻止該元素默認(rèn)的行為的。
  2. 比如上面這個是個超鏈接,但是現(xiàn)在點擊之后并不會跳轉(zhuǎn),而是執(zhí)行aClick()方法
  3. 如果是form,也會阻止掉自動提交的方法。

事件修飾符@click.stop

<div style="width: 100px; height: 100px; background-color:red;" @click="divClick">
    <button @click.stop="btnClick">點我</button>
</div>
  1. 該修飾符是用在子元素標(biāo)簽上的,可以用來阻止事件冒泡。也就是說,使用之后,點擊子元素的標(biāo)簽并不會觸發(fā)父元素上的事件。

按鍵修飾符

<input type="text" @keyup.enter="dealKey">
dealKey(event){
    console.log(event);
    console.log(event['keyCode']);
}
  1. 使用@keyup可以在后面跟上很多種按鍵,也就是當(dāng)按下然后松開之后就會執(zhí)行該事件。
  2. 另外在觸發(fā)事件的對象里可以通過event['keyCode']獲取按鍵的ASCII碼

過濾器

全局過濾器

Vue.filter('wholeMoneyFormat', (value)=>{
   return '¥' + Number(value).toFixed(4);
});
<p>{{money | wholeMoneyFormat}}</p>
  1. 全局過濾器需要寫在main.js中,如上面的代碼所示
  2. 上面的過濾器的意思是:將傳過來的參數(shù)轉(zhuǎn)成數(shù)字,并且保留四位小數(shù),然后在前面加上一個人民幣的符號并返回。
  3. 使用的時候需要使用雙括號表達(dá)式,第一個是需要過濾的參數(shù),第二過濾器的名字,中間用豎線隔開。

局部過濾器

filters: {
    moneyFormat(value){
        return '¥' + Number(value).toFixed(2);
    },
    timeFormat(value, format='YYYY-MM-DD HH:mm:ss'){
        return moment(value).format(format);
    }
}
  1. 局部過濾器的定義如上面的代碼所示。使用方法和全局過濾器是完全一樣的
  2. 第一個金錢過濾器,是將傳遞過來的參數(shù)轉(zhuǎn)成保留兩位小數(shù)的數(shù)字,并且在前面加上一個人民幣符號。
  3. 第二個時間過濾器需要使用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>
  1. 在Vue中使用動畫,需要使用transition標(biāo)簽,該標(biāo)簽必須具備一個name屬性
  2. 我們可以通過設(shè)置一個v-if來切換該元素出現(xiàn)和消失時的動畫
  3. 然后在樣式表中寫好終點的樣式以及進(jìn)行中的樣式。選擇器的名字需要使用到上面提到的name屬性,比如:.fade-enter, .fade-leave-to,.fade-enter-active, .fade-leave-active
  4. 下面這個就是一個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

  1. 注意:如果圖片采用:src這種方式導(dǎo)入的話,需要先導(dǎo)入這樣圖片import pic from '@/assets/img_02.jpg',然后將其作為data中的一個數(shù)據(jù)
  2. 兩個CSS類,第一個表示進(jìn)入時候的動畫以及持續(xù)時間。第二個表示消失時候的動畫以及持續(xù)時間,并且表明和第一個動畫是相反的。
  3. 詳細(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>
  1. 該動畫庫的使用情況如上面的代碼
  2. 安裝animate.css:npm i animate.css --save
  3. 然后導(dǎo)入animate.cssimport animate from 'animate.css'
  4. transition中的三個屬性分別表示進(jìn)入時候的動畫、消失時候的動畫,以及進(jìn)入和消失的持續(xù)時間。

生命周期

  1. Vue的生命周期按順序有:beforeCreate、created、beforeMountmounted、beforeUpdate、updatedbeforeDestroy、destroyed
  2. 一般來說只要該組件啟用,前面四個生命周期都會很快依次調(diào)用。
  3. 然后,每次修改data中的數(shù)據(jù),都會調(diào)用beforeUpdate、updated兩個方法
  4. 最后,如果想要進(jìn)入銷毀的生命周期需要主動觸發(fā)下面代碼的這個方法
  5. 另外,定時器如果在生命周期里,注意它是異步的,即便已經(jīng)銷毀可能定時器仍然在工作。這個時候最好在銷毀的生命周期里設(shè)置一個清除定時器的方法。
destory(){
    this.$destroy();
}

組件通信

子組件通知父組件,我觸發(fā)了某個方法

父組件

  1. App.vue在組件上面需要綁定事件
  2. 在methods中寫事件
<CustomEvents @btnClick="deleteP"/>
deleteP(args){
    console.log(args);
    this.$refs.word.remove();
}

子組件

  1. 子組件同樣需要給某個元素綁定事件
  2. 但不是自己處理事件,而是告訴父元素,自己觸發(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
}
  1. 父組件通過在組件上寫:age的形式,向子組件傳遞數(shù)據(jù)和方法。這些數(shù)據(jù)和方法都是在父組件中已經(jīng)定義好的
  2. 子組件需要通過props這個鉤子選項接收父組件傳遞過來的數(shù)據(jù)和方法,有上面兩種形式
  3. 然后子組件就可以像使用自己的數(shù)據(jù)和方法一樣使用父組件的數(shù)據(jù)和方法

自定義事件

  1. 由子組件發(fā)送給父組件,函數(shù)名稱,函數(shù)參數(shù)this.$emit('addTodo', todo);
  2. 父組件需要監(jiān)聽這個組件:<Header ref="header"/>
  3. 然后給父組件綁定自定義事件的監(jiān)聽:this.$refs.header.$on('addTodo', this.addTodo);注意這是寫在mounted這個鉤子選項里的;然后不用寫()來接參數(shù),這種寫法就會自動把todo參數(shù)傳遞過來

發(fā)布訂閱模式

  1. 安裝pubsub插件:npm install --save pubsub-js
  2. 在App.vue和item中引入pubsub-js:import PubSub from 'pubsub-js',反正哪里需要發(fā)布、訂閱就需要引入
  3. 子組件發(fā)布消息:PubSub.publish('delTodo', this.index)
  4. 父組件接收消息。代碼如下。注意,同樣寫在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>
  1. footer中不用自己寫代碼了,直接把插槽留出來就行。使用的是<slot>標(biāo)簽,注意必須有name屬性。
  2. 父組件里面直接往子組件里面插入
  3. 這樣就可以直接調(diào)用父組件里面的方法和計算屬性了

Vuex

  1. 安裝:vue add vuex

Vue router

Vue-router的基本使用

  1. 安裝:npm install vue-router --save
  2. 新建router.js文件
  3. 在main.js中引入,并且放入Vue的實例中
  4. 新建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>
  1. 關(guān)于獲取路由參數(shù)的代碼在上邊
  2. 所謂的獲取路由參數(shù)其實就是指,數(shù)據(jù)是在鏈接上的,我們需要通過某種方式獲取到這些數(shù)據(jù)
  3. 通過路由傳遞參數(shù)主要有幾種方式:
  4. {path: '/mine/:name/:sex', name: 'mine', component: Mine},通過在路由上寫冒號
  5. {path: '/mine', name: 'mine', component: Mine, props: {name: '小撩'}},通過props獲取數(shù)據(jù),注意需要在該組件內(nèi)部寫出props這個鉤子選項,并且內(nèi)容為這些數(shù)據(jù)的變量名
  6. {path: '/mine/:name/:sex', name: 'mine', component: Mine, props: true},通過以上兩種方式獲取數(shù)據(jù)
  7. {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}
    ]
})
  1. 所謂的嵌套路由就是斜杠后面還有斜杠
  2. 嵌套路由的配置其它文件并沒有什么不同,主要還是在router.js上的不同
  3. 嵌套路由的配置主要小心:子路由不需要在前面寫/

全局路由前置和后置守衛(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;
  1. 主要通過前置守衛(wèi)來控制什么情況下用戶可以進(jìn)入頁面
  2. 如果用戶進(jìn)的是登錄頁,無需檢查直接放行
  3. 如果用戶進(jìn)的是其它頁,檢查是否登錄,如果沒有登錄前往登錄頁,如果已經(jīng)登錄放行
  4. 其它情況一律放行

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>
  1. 登錄的這個頁面主要負(fù)責(zé)處理登錄相關(guān)的事務(wù)
  2. 當(dāng)用戶點擊登錄按鈕之后,首先將全局的isLogin設(shè)置為true
  3. 獲取用戶的回調(diào)地址,如果有回調(diào)地址則直接前往;如果沒有則前往主頁

數(shù)據(jù)本地化

  1. 手寫一個工具類
  2. 在app.vue中導(dǎo)入該工具類
  3. 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));
    }
}
  1. 使用localStorage無論是讀還是取,都需要傳遞一個key,注意這里僅僅一個數(shù)組,所以使用了一個字符串常量作為key
  2. 第一個讀取數(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
    }
}
  1. 取數(shù)據(jù)需要深度監(jiān)視,因為數(shù)組里面還有對象,只有深度監(jiān)視能夠監(jiān)聽數(shù)組里面對象屬性的改變。
  2. immediate如果為true,就是當(dāng)初始化的時候就立即執(zhí)行handler方法,否則就是當(dāng)todos發(fā)生改變才會執(zhí)行handler方法。

UI框架

elementUI

  1. element文檔:https://element.eleme.cn/#/zh-CN/component/layout
  2. 安裝:npm i element-ui -S
  3. 給Vue添加UI庫:vue add element
  4. 選項:Fully import、N、CN
  5. vue ui:使用可視化面板管理項目(一般不會用的)

做完以上幾步就會發(fā)現(xiàn),自動集成了plugins/element.js,并且自動在main.js中導(dǎo)入了。然后就可以直接在app.vue中使用了。

  1. 復(fù)制HTML代碼到模板里面
  2. 復(fù)制script代碼data部分到app.vue里,注意這里和vue的代碼結(jié)構(gòu)是完全一樣的。

VantUI

  1. 文檔:https://youzan.github.io/vant/#/zh-CN/
  2. 安裝:npm i vant -S
  3. 配置按需加載,引入babel:npm i babel-plugin-import -D
  4. 配置babel.config.js,可以從文檔里復(fù)制粘貼
  5. 在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>
  1. 安裝shortidnpm i shortid --save
  2. 在該組件中導(dǎo)入:import shortId from 'shortid'
  3. 在data中要有personsKeys這個數(shù)據(jù),它是一個空的數(shù)組
  4. 注意要添加一個mounted掛載點,調(diào)用shortid自動產(chǎn)生一個隨機(jī)數(shù)作為id
mounted() {
    this.personsKeys = this.persons.map(v=>shortId.generate())
}
  1. 然后就可以如上面代碼部分那樣使用了
  2. v-for可能需要使用:key這個屬性,不然的話可能在控制臺會報警告

moment

  1. 安裝:npm i moment --save
  2. 使用:注意任何插件的使用,都需要在該組件內(nèi)部首先導(dǎo)入import moment from 'moment'。然后就可以像調(diào)用函數(shù)一樣使用該插件了

案例匯總

v-for排序小案例

  1. 數(shù)據(jù)來源于data中已經(jīng)定義好的
  2. v-for的使用和之前遍歷數(shù)組的時候是一樣的,同樣用到了shortid,但是并沒有把shortid展示在頁面上,而是用索引加1的方式
  3. v-for里的in,是已經(jīng)排序好的數(shù)組,該數(shù)組是使用計算屬性而得到的
  4. 排序分為默認(rèn),按年齡升序、降序,是通過向計算屬性里傳遞不同參數(shù)而區(qū)別開的。
  5. 排序的代碼如下。注意需要先過濾以下數(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;
    }
},

表單添加刪除小案例

  1. data里有有已經(jīng)存在的數(shù)據(jù)和一個新的空對象對象,這個空對象可以設(shè)定一些默認(rèn)值
  2. 每個input標(biāo)簽都會設(shè)置v-model,分別和空對象里的各個屬性綁定起來
  3. 添加數(shù)據(jù)的時候:首先使用解構(gòu)賦值從該對象中獲取輸入框的數(shù)據(jù);然后對數(shù)據(jù)進(jìn)行驗證,比如不能為空;插入數(shù)據(jù)直接使用數(shù)組的unshift方法就可以;最后記得要清空這個空對象,讓其恢復(fù)默認(rèn)值
  4. 點擊刪除按鈕的時候:直接調(diào)用splice方法就可以,第一個參數(shù)是索引,第二個參數(shù)是從該索引的位置刪除幾個元素

todoList

todoList第一版

Header
  1. Header的主要任務(wù)是為todoList添加新任務(wù),主要方法就是添加todo
  2. 方法的實現(xiàn)是在App.vue,然后使用:addTodo="addTodo"的方式向子組件傳遞自己的方法。當(dāng)然子組件需要用props來接收。
  3. 子組件只負(fù)責(zé)一個數(shù)據(jù)title,因為創(chuàng)建的時候任務(wù)不可能完成,所以finished也就是那個復(fù)選框默認(rèn)設(shè)置為false就行了。
  4. 子組件仍需要一些其它的操作:①:判斷用戶輸入的是否是空字符串;②:根據(jù)用戶的輸入拼接一個todo,它主要就是包含title、finished兩個數(shù)據(jù);③:調(diào)用父組件的添加方法;④:清空title數(shù)據(jù)
List
  1. List的主要任務(wù)就是:①:當(dāng)鼠標(biāo)移動到某一條todo上時,該todo的背景顏色變?yōu)榛疑?,并且顯示出來刪除按鈕
  2. App.vue需要向List傳遞todos數(shù)據(jù)和刪除某一條todo的方法
  3. List使用props接收todos和刪除方法。然后使用v-for對todos進(jìn)行遍歷,當(dāng)然遍歷的每一個item就是它的子組件,List需要向item傳遞它的數(shù)據(jù)todo、該todo在todos中的索引,以及刪除todo的方法
  4. 子子組件item首先需要接收上面?zhèn)鬟f過來的兩個數(shù)據(jù)一個方法。
  5. item實現(xiàn)鼠標(biāo)移入就出現(xiàn)刪除按鈕以及背景顏色的改變是通過@mouseenter、@mouseleave兩個觸發(fā)事件的方式,分別向同一個事件傳遞true、false兩個不同的參數(shù)。
  6. 背景顏色是通過設(shè)置:style使用的是data里面的某個值,這樣就可以使用方法改變data的這個屬性來調(diào)整背景顏色
  7. 按鈕的顯示和隱藏使用的是v-show,同樣是data里的一個數(shù)據(jù)控制true還是false,這樣我們在方法里就可以設(shè)置該屬性為true,來讓按鈕顯示出來
  8. 刪除該todo直接使用父組件里的方法就可以了
Footer
  1. footer的作用主要是:①:當(dāng)上面的每一條todo都選中的時候,它的復(fù)選框也要選中;②:全選;③:點擊刪除所有finished的按鈕。
  2. 選中所有todo和刪除已經(jīng)完成的todo都是在app.vue中定義的。然后將這兩個方法以及todos的數(shù)據(jù)全部傳遞給footer組件
  3. footer里面很重要的一個功能就是要知道目前已經(jīng)完成的todo數(shù)量,這是通過計算屬性實現(xiàn)的。代碼見下方。使用的是數(shù)組的reduce這個方法,其實就是一個遍歷+1計數(shù)的操作。
  4. footer里面很重要的一個功能就是全選,也就是當(dāng)上面全部選中它也要選中,它如果選中,上面也要全部選中。這是通過給該復(fù)選框設(shè)置一個v-model,并且綁定一個計算屬性實現(xiàn)的。
  5. 其中g(shù)et()是處理第一個的,判斷條件就是目前已經(jīng)完成的todo數(shù)量和todos的長度相等,并且長度不為0.
  6. 其中set()是處理第二個的,它會將目前該計算屬性的值true、false作為參數(shù)調(diào)用父組件的全選方法。父組件的全選方法就會遍歷整個todos,將其中的finished屬性全部設(shè)定為參數(shù)。需要注意的是,這里有一個小竅門,就是footer的全選按鈕和v-model綁定,它選中那么set()方法的參數(shù)就是true。
  7. 最后刪除所有已經(jīng)完成的todo則很簡單了,不需要傳遞任何參數(shù),直接調(diào)用父組件的方法即可。
finishedCount(){
    return this.todos.reduce((total, todo)=> total + (todo.finished ? 1 : 0), 0);
},
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容