Vue 学习笔记

一、创建一个界面

第一个 没用的 故 删除

二、vue 关键字

2.1 v-if条件判断

<div id="app">
  <label>
    <h2 v-if="isShow">test</h2>
    <h2 v-else>1010101 else</h2>
  </label>
  <button type="button" @click="btnClick">btn</button>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      isShow:true
    },
    methods:{
      btnClick(){
        this.isShow=!this.isShow
      }
    }
  })
</script>

2.2 v-show

<div id="app">
  <label>
<!--    v-if: 条件为false时 将包含的元素从dom中删掉
        v-show: 只仅仅隐藏 加了个 display:none
-->
    <h2 v-show="isShow">test</h2>
  </label>
  <button type="button" @click="btnClick">btn</button>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      isShow:true
    },
    methods:{
      btnClick(){
        this.isShow=!this.isShow
      }
    }
  })

2.3 created 和 mounted

Vue中实例或者组件从创建到消灭中间经过的一系列过程。

img

  • created:在模板渲染成html前调用,即通常初始化某些属性值(从后端获取数据),然后再渲染成视图。

  • mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。

2.3 mustache(胡须)

mustache{{}}不仅仅可以写变量 也可以写简单的表达式。

<template>
  <div id="app">
    <h2>{{a+''+b}}</h2>
    <h2>{{count*2}}</h2>
  </div>
</template>

<script>
  export default {
    name: "Choice",
    data(){
      return{
        a:"11",
        b:"22",
        count:100
      }
    },
    create:function () {

    }
  }
</script>
<style scoped>
</style>

2.4 v-once

<h2 v-once>{{count}}</h2> 只会改变一次

2.5 v-html

解析变量html内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
  <h2 v-html="a"></h2>
</div>
<script>
  const vm=new Vue({
    el:'#app',
    data:{
        a:'<h1>太帅了/h1>',
    }
  })
</script>
</body>
</html>

2.6 v-text

与mustache相似 显示文本 不够灵活不建议使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<!--  会直接覆盖,you不够灵活-->
  <h2 v-text="a">,you</h2>
</div>
<script>
  const vm=new Vue({
    el:'#app',
    data:{
        a:'<h1>太帅了</h1>',
    }
  })
</script>
</body>
</html>

2.7 v-pre

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
 <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
 <h2>{{a}}</h2>
<!--  会原封不动的显示里面的内容 不作任何的解析-->
 <h2 v-pre>{{a}}</h2>
</div>
<script>
 const vm=new Vue({
   el:'#app',
   data:{
       a:'<h1>太帅了</h1>',
   }
 })
</script>
</body>
</html>

2.8 cloak

翻译为斗篷

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
  <style>
    [v-cloak]{
      display: none;
    }
  </style>
</head>
<body>
<!--  一旦vue对app进行解析 v-cloak会将对app进行自定义的样式操作 直到完成解析并且v-cloak在div中删除-->
<div id="app" v-cloak>
  <h2>{{a}}</h2>
</div>
<script>
  //设置延时1000ms
  setTimeout(function () {
    const vm = new Vue({
      el: '#app',
      data: {
        a: '<h1>太帅了</h1>',
      }
    })
  }, 1000)
</script>
</body>
</html>

2.9 v-bind 动态绑定属性

动态地显示图片 也就是可以控制图片内容显示

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
  <style>
  </style>
</head>
<body>
<div id="app">
  <h2>{{url}}</h2>
  <!--  这里不可以使用胡须,只有内容位置才可以插入-->
  <img src="{{url}}" alt="">
  <!--  在html关键字前面加入v-bind: 动态绑定到data里面的url中 变成可以变化的src-->
  <img v-bind:src="url" alt="">
  <!--  可以缩写成  :-->
  <!--  <img :src="url" alt="">-->
  <a :href="url">图片地址</a>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      url: 'https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF'
    }
  })
</script>
</body>
</html>

2.9.1 动态绑定css

对象语法(常用)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
  <style>
    .s1{
      color: red;
    }
    .s2{
      color: blue;
    }
    .s3{
      font-family: 宋体;
    }
  </style>
</head>
<body>
<div id="app">
<!--  {里面是对象 key:value}  isS1 为true时加入到class里-->
  <h2 :class="{s1:isS1,s2:isS2}">TEST</h2>
  <button @click="btn">变色</button>

<!--vue 会将两个class合并 不会覆盖-->
  <h2 class="s3" :class="{s1:isS1,s2:isS2}">TEST</h2>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      isS1:true,
      isS2:false,
    },
    methods:{
      btn:function () {
        this.isS1=!this.isS1;
      }
    }
  })
</script>
</body>
</html>
数组语法
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
  <style>
    .s1{
      color: red;
    }
    .s2{
      color: blue;
    }
    .s3{
      font-family: 宋体;
    }
  </style>
</head>
<body>
<div id="app">
  <h2 class="s3" :class="getClass()">TEST</h2>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      isS1:true,
      isS2:false,
      s1:'s1',
      s2:'s2',
    },
    methods:{
      btn:function () {
        this.isS1=!this.isS1;
      },
      getClass:function () {
        //别忘了this 
        return [this.s1,this.s2];
      }
    }
  })
</script>
</body>
</html>

2.9.2 动态绑定style

对象语法
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
  <style>
  </style>
</head>
<body>
<div id="app">
<!--  不加引号 vue会当变量去解析-->
  <h3 :style="getStyles()">test</h3>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      num:0,
      fontSize1: 90
      myColor:'red'
    },
    methods:{
      getStyles:function () {
          return {fontSize:this.fontSize1 + 'px',color:this.myColor}
      }
    }
  })
</script>
</body>
</html>
数组语法(不常用 参考上面)

2.10 Computed

2.10.1 基本用法

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
  <style>
  </style>
</head>
<body>
<div id="app">
  <h3>{{name}}</h3>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      firstName:'Big',
      lastName:'Boy'
    },
    computed:{
      name:function () {
        return this.firstName+' '+this.lastName
      }
    }
  })
</script>
</body>
</html>

2.10.2 复杂使用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
  <style>
  </style>
</head>
<body>
<div id="app">
<!--  不加引号 vue会当变量去解析-->
  <h3>{{totalPrice}}</h3>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      books:[
        {id:1,name:'book1',price:98},
        {id:2,name:'book2',price:998},
        {id:3,name:'book3',price:998},
        {id:4,name:'book4',price:998}
      ]
    },
    computed:{
      totalPrice:function () {
        let sum=0;
        for(let i of this.books){//es6
          sum+=i.price;
        }
        return sum
      }
    },
    methods:{
      getStyles:function () {
          return {fontSize:this.fontSize1 + 'px',color:this.myColor}
      }
    }
  })
</script>
</body>
</html>

getter and setter

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
  <style>
  </style>
</head>
<body>
<div id="app">
  <h3>{{name}}</h3>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      firstName:'Big',
      lastName:'Boy'
    },
    computed:{
      //只读属性
      name:{
        //计算属性一般是没有set方法
        set:function (newValue) {
          const names=newValue.split(' ');
          this.firstName=names[0];
          this.lastName=names[1];
        },
        get:function () {
            return this.firstName+' '+this.lastName
        }
      }
    }
  })
</script>
</body>
</html>

计算属性(computed)对比方法(methods)

  • 第一 在界面上可以当一个属性引用不需要加()直接引用
  • 第二 计算属性的结果放在缓存中,复用效率高于方法

2.11 v-on

参数问题

如果函数需要参数 但是调用的时候没有输入 vue会将浏览器生产的event对象传到这个函数的第一个参数里

<div id="app">
  <h3>{{num}}</h3>
  <button @click="add">add</button>
</div>
<script>

  const vm = new Vue({
    el: '#app',
    data: {
      num:0
    },
    methods:{
      add(event){
        this.num++;
        console.log(event);
      }
    }
  })
</script>

多参数时用$event取event

如果不加 event会被当做变量在vue中寻找

<div id="app">
  <h3>{{num}}</h3>
  <button @click="add(11,$event)">add</button>
</div>
<script>

  const vm = new Vue({
    el: '#app',
    data: {
      num:0
    },
    methods:{
      add(abc,event){
        this.num+=abc;
        console.log(event);
      }
    }
  })
</script>

v-on修饰符

click.stop

阻止点击事件继续传播

点击add divClick不会被打印

<div id="app" @click="divClick">
  <h3>div</h3>
  <button @click.stop="btnClick">add</button>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      num:0
    },
    methods:{
      divClick(){
        console.log("divClick");
      },
      btnClick(){
        console.log("btnClick")
      }
    }
  })
click.prevent

阻止默认事件行为

<div id="app">
  <button type="submit" @click.prevent="btnClick">btn</button>
</div>
<script>

  const vm = new Vue({
    el: '#app',
    data: {
    },
    methods:{
      btnClick(){
        console.log("btnClick")
      }
    }
  })
</script>
xx.once

只监听一次事件 如下

keyup.enter
<div id="app">
  <label>
    <input @keyup.enter="btnClick1"/>
  </label>
  <button type="submit" @click.once="btnClick2">btn</button>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
    },
    methods:{
      btnClick1(){
        console.log("keyup enter")
      },
      btnClick2(){
        console.log("keyup enter")
      }
    }
  })
</script>

2.12 v-for

<div id="app">
  <ul>
<!--   若是 item in yuge 那item仅仅是值-->
    <li v-for="(value,key,index) in yuge">
      {{value}} {{key}} {{index}}
    </li>
  </ul>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      yuge:{
        height:1.99,
        age:18,
        name:'shuaiguo'
      }
    },
    methods:{
    }
  })
</script>
为什么建议使用v-for设置一个:key

在向中间插入元素时 如果没有key 虚拟DOM的diff算法只能一个个将后面元素后移 效率低下 使用key作为数据标识可在中间直接插入数据 高效的更新虚拟DOM

特别:vue中数组哪个方法是响应式

  • 1.push()
    2.pop()
    3.shift()
    4.unshift()
    5.splice() 拼接
    6.sort() 将…分类
    7.reverse() 颠倒 反转
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <ul>
      <li v-for="item in letters" :key="item">{{item}}</li>
    </ul>
    <button @click="btnClick">按钮</button>
  </div>

  <script src="../vue.min.js"></script>
  <script>
    const app=new Vue({
      el:'#app',
      data:{
        message:'hello',
        letters:['a','b','c','d'],
      },
      methods:{
        btnClick(){
          // 1.push方法
          // this.letters.push('aaa');

          //  2.通过索引值修改数组中的元素(不是响应式的)
          // this.letters[0]='bbbbb';

          //pop():删除数组中的最后一个元素
          this.letters.pop();

          //shift():删除数组中的第一个元素
          this.letters.shift();

          // unshift():在数组最前面添加元素
          this.letters.unshift('aaa','bbb','ccc');

          // splice()作用:删除元素/插入元素/替换元素
          //删除元素:第二个参数传入你要删除几个元素(如果没有传参数,就删除后面的所有元素)
          //替换元素:第二个参数辨识要替换几个元素,后面是用于替换前面的元素
          // 插入元素:第二元素为0,并且后面跟上要插入的元素
          // splice(start)
          this.letters.splice(1,3,'s','u','n');

          // sort():
          this.letters.sort();

          // reverse()
          this.letters.reverse();

          // set(要修改的对象,索引值,修改后的值)
          //this.letters[0]='bbbb' 这个vue不会响应!!!! 可以用splice
          Vue.set(this.letters,0,'bbbb');


        }

      }
    })

    //es6可变参数语法
    function sum(...num) {
      console.log(num);
    }
    sum(20,30,40,50,60,70);

  </script>
</body>
</html>

2.13 v-model

数据双向绑定 注意胡须什么的都是响应式 不会反过来影响数据

CheckBox
<div id="app">
  <h2>您的爱好是?:</h2>
<!--  :for="item" 值绑定-->
  <label v-for="item in list" :for="item">
    <input type="checkbox" v-model="act" :id="item" name="act" :value="item">{{item}}
  </label>

  <h2>{{act}}</h2>
</div>
<script>

  const vm = new Vue({
    el: '#app',
    data: {
      //注意这个地方是数组类型 单选框时对应的是boolean值
      act:[],
      list:['唱','跳','RUP']
    }
  })
</script>
Select
<div id="app">
  <h2>您的爱好是?:</h2>
  <select name="nameS" v-model="act">
    <option value="1">1</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="2">2</option>
  </select>
  <h2>{{act}}</h2>
</div>
<script>

  const vm = new Vue({
    el: '#app',
    data: {
      act:''
    }
  })
</script>
修饰符
lazy
<div id="app">
  <h2>您的爱好是?:</h2>
<!--  v-model.lazy 敲回车才绑定-->
  <input type="text" v-model.lazy="act">
  <h2>{{act}}</h2>
</div>
<script>

  const vm = new Vue({
    el: '#app',
    data: {
      act:''
    }
  })
</script>
number
<div id="app">
  <h2>您的爱好是?:</h2>
<!--  v-model.number act将一直保持number类型-->
  <input type="number" v-model.number="act">
  <h2>{{act+' '+typeof act}}</h2>
</div>
<script>

  const vm = new Vue({
    el: '#app',
    data: {
      act:0
    }
  })
</script>
trim

去除左右两边多余空格

v-model.trim

三、组件

3.1 组件内值必须为函数

为什么必须是函数呢?

因为用对象data(){ title:'dasfaa'}会导致所有组件的复用实例的数据是共享的 而用函数每次复用都会创建一个新的数据储存地址

<div id="app">
  <t1></t1>
</div>

<template id="t1">
  <div>
    <h2>qwer</h2>
    <h2>111111111111{{title}}</h2>
  </div>
</template>
<script>

  //注册一个全局组件
  Vue.component('t1',{
    template:'#t1',
    //组件里必须是 返回函数
    data(){
      return{
        title:'dasfaa'
      }
    }
  })

  const vm = new Vue({
    el: '#app',
    data: {
      act:0
    }
  })
</script>

4.2 父子组件通信与访问

在这里插入图片描述

4.2.1 父传子 props

props支持的数据验证

String

Number

Boolean

Function

Object

Array

Symbol

<div id="app">
<!--  //模板里多了一个属性 就可以用bind-->
  <t1 :act="act" :list1="movies"></t1>
</div>

<template id="t1">
  <div>
    <h2>Data: {{act}}</h2>
    <ul>
      <li v-for="item in list1" :key="item">{{item}}</li>
    </ul>
  </div>
</template>
<script>

  const t1={
    template:'#t1',
    //父传子用 props
    props:{
      //对象可以指定类型
      act:{
        validator(value) {
          console.log(value);
          return false;
        }
      },
      list1:{
        type:Array,
        //可以设置默认值 如果类型是数组或者对象时 默认值必须是一个函数
        default(){
          return ['1','2']
        },
        //用来验证数据
        validator(value){
          console.log(value);
          //必须是其中一个
          return ['m11','m33','4m'].indexOf(value) !==-1
        },
        //使用这个组件时候 必须传值给这个属性
        required:true
      }
    },
    data(){
      return{
        list1: []
      }
    }
  }
  const vm = new Vue({
    el: '#app',
    data: {
      act:77,
      movies:['m1','m2','m3']
    },
    components:{
      t1  //cs6
    }
  })
</script>

4.2.2 子传父 $emit

<div id="app">
<!--  父组件 vue默认吧item传过去-->
  <t1 @btnclick="copyData"></t1>
</div>

<template id="t1">
  <div>
    <button @click="notifyClick('btn click')">button</button>
  </div>
</template>
<script>

  const t1={
    template:'#t1',
    //父传子用 props
    props:{
      //对象可以指定类型
      act:{
        validator(value) {
          console.log(value);
          return false;
        }
      }
    },
    data(){
      return{
        list1: []
      }
    },
    methods:{
      notifyClick(item){
        //不能用驼峰命名   发出一个名为btnclick的事件 可以在父组件中调用
        this.$emit('btnclick',item)
      }
    }
  }
  const vm = new Vue({
    el: '#app',
    data: {
      act:77,
      movies:['m1','m2','m3']
    },
    components:{
      t1  //cs6
    },
    methods: {
      copyData(v){
        console.log(v+"----------copy")
      }
    }
  })
</script>

4.2.3 父访问子 c h i l d r e n / children/ children/refs

<div id="app">
  <t1 ref="t01"></t1>
  <t1 ref="t02"></t1>
  <t1 ref="t03"></t1>
  <button @click="btnClick">button</button>
</div>

<script>

  const vm = new Vue({
    el: '#app',
    data: {
      num1: 0,
      num2: 0
    },
    components: {
      t1:{
        data(){
          return{
            name:'子组件内的name'
          }
        },
        methods: {
          message() {
            console.log("子组件的message方法...");
          }
        }
      }
    },
    methods: {
      btnClick(){
        // 1.$children
        //开发中一般不用children 不好维护
        // console.log(this.$children);
        // for (let t of this.$children){
        //   console.log(t.name);
        //   t.message()
        // }

        //2$refs 对象类型默认为空需要在使用子组件时 加入ref命名
        console.log(this.$refs);
      }
    }
  })
</script>

4.3 slot

4.3.1 基本使用

<div id="app">
  <t1></t1>
  <t1>
<!--    所有元素替换到slot中-->
    <span>data</span>
    <p>data2</p>
  </t1>
  <t1></t1>
</div>
<template id="t1">
  <div>
    <h2>--1122--</h2>
<!--    定义个slot相当于html元素占位符-->
    <slot>
<!--      设置默认元素-->
      <button>按钮</button>
    </slot>
  </div>
</template>
<script>

  const vm = new Vue({
    el: '#app',
    data: {
    },
    components: {
      t1:{
        template:'#t1',
        data(){
          return{
          }
        },
        methods: {
        }
      }
    },
    methods: {
    }
  })
</script>

4.3.2 具名使用

<div id="app">
  <t1>
    <p slot="s2">t1 test</p>
    <p slot="s1">t2 test</p>
  </t1>
<!--  要指定name 要不然什么都不显示-->
  <t1><p>t3 test</p></t1>
</div>
<template id="t1">
  <div>
    <h2>----</h2>
    <slot name="s1"><p>1</p></slot>
    <slot name="s2"><p>2</p></slot>
    <slot name="s3"><p>3</p></slot>

  </div>
</template>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
    },
    components: {
      t1:{
        template:'#t1',
        data(){
          return{
          }
        },
        methods: {
        }
      }
    },
    methods: {
    }
  })
</script>

4.3.3 v-slot 作用域插槽

vue 官网link

让父组件的插槽内容能够访问子组件中才有的数据

<div id="app">
  <t1></t1>
  <t1>
<!--    <template slot-scope="slot">
现在这样写:v-slot:插槽名 =“自定义属性名”  在template中
-->
    <template v-slot="slotProps">
      <span>{{slotProps.flowers.toString()}}</span>
    </template>
  </t1>
</div>
<template id="t1">
  <div>
    <h2>----</h2>
    <slot :flowers="flowers">
      <ul>
        <li v-for="item in flowers" :key="item">
          {{item}}
        </li>
      </ul>
    </slot>

  </div>
</template>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
    },
    components: {
      t1:{
        template:'#t1',
        data(){
          return{
            flowers:['clover','rose','lily','...']
          }
        },
        methods: {
        }
      }
    },
    methods: {
    }
  })
</script>

四、Vue CLI

安装 创建项目

官网链接 写的很明白

注意

  • 先安装node

  • Vue CLI 的包名称由 vue-cli 改成了 @vue/cli。 如果你已经全局安装了旧版本的 vue-cli (1.x 或 2.x),你需要先通过 npm uninstall vue-cli -gyarn global remove vue-cli 卸载它。

对Vue中 runtime-compiler 和 runtime-only 两种模式的理解

区别

  1. runtime-only 比 runtime-compiler 轻 6kb
  2. runtime-only 运行更快
  3. runtime-only 只能识别render函数,不能识别template,.vue文件中的template也是被 vue-template-compiler 翻译成了render函数,所以只能在.vue里写 template

五、vue router

5.1 url的hash HTML5的history

通过url的hash HTML5的history可以使页面不刷新

浏览器console输入地址栏
location.hash=‘wokao’http://localhost:8080/#/wokao
history.pushState({},’’,‘wokao2’)http://localhost:8080/wokao2
history.back()/history.go(-1)/history.forward(1)http://localhost:8080/#/wokao
history.replaceState({},’’,‘wo’)http://localhost:8080/wo
  • history.pushState是一个栈
  • history.replaceState替换当前路由 被替换的路由嗝屁了
  • history.go(-2)从栈里出两个 history.go(1) 从栈里压入一个history.forward一个道理 类似go
  • 地址中有#表示使用的是hash值

5.2 vue-router 简单使用

  1. 创建界面文件

    Home.vue

    <template>
        <div>
            <h2>Home</h2>
        </div>
    </template>
    
    <script>
      export default {
        name: "home"
      }
    </script>
    
    <style scoped>
    
    </style>
    

    About.vue

    <template>
        <div>
            <h2>About</h2>
        </div>
    </template>
    
    <script>
      export default {
        name: "About"
      }
    </script>
    
    <style scoped>
    
    </style>
    
  2. 书写路由

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Home from "../views/Home";
    import About from "../views/About";
    
    Vue.use(VueRouter)
    
    const routes = [
      {
        path:'/home',
        component:Home
      },
      {
        path:'/about',
        component:About
      }
    ]
    
    const router = new VueRouter({
      routes
    })
    
    export default router
    
  3. 使用路由

    在APP.vue中书写

    <template>
      <div id="app">
        <router-link to="/home">home  </router-link>
        <router-link to="/about">about</router-link>
        <router-view/>
    
      </div>
    </template>
    
    <style>
    </style>
    

5.3 修改路由的默认值 改hash为history

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from "../views/Home";
import About from "../views/About";

Vue.use(VueRouter)

const routes = [
  {
    path:'',
      /*
  {
    path:'',
    component:Home
  }这种方式地址栏不会有home
      */
    redirect:'/home'
  },
  {
    path:'/home',
    component:Home
  },
  {
    path:'/about',
    component:About
  }
]

const router = new VueRouter({
  routes,
    //默认mode为hash
  mode:'history'
})

export default router

5.3 router-link属性

属性作用
to="/home"指定路由
tag=“button”指定渲染成什么元素
replace不会留下history记录
active-class=""自定义router-link-active的名字
router-link-active

当某个路由被激活router自动加入class这个样式router-link-active 所以可以设置激活状态

也可以统一设置class 在router/index.js 下添加linkActiveClass

const router = new VueRouter({
  routes,
  mode:'history',
  linkActiveClass:'main'
})

演示代码

<template>
  <div id="app">
    <router-link style="margin-right: 10px" to="/home" tag="button" replace active-class="main">home</router-link>
    <router-link to="/about" tag="button" replace>about</router-link>
    <router-view/>
  </div>
</template>

<style>
  .router-link-active{
    color: #42b983;
  }
  .main{
    color: blueviolet;
  }
</style>

5.4 代码跳转路由

<template>
    <div id="app">
        <!--    <router-link style="margin-right: 10px" to="/home" tag="button">home</router-link>-->
        <!--    <router-link to="/about" tag="button" replace>about</router-link>-->
        <button @click="goHome">home</button>
        <button @click="goAbout">about</button>
        <router-view/>
    </div>
</template>

<script>
  export default {
    name: 'APP',
    methods: {
      goAbout() {
        //vue 会给所有的组件内置$router  $route为当前活跃的路由
        
        //防止重复点击 添加路由报错
        if (this.$route.path !== '/about')
          this.$router.replace('/about')
        else
          console.log('重复点击');
      },
      goHome() {
        if (this.$route.path !== '/home')
          this.$router.replace('/home')
        else
          console.log('重复点击');
      }
    }
  }
</script>

<style>
</style>

5.5 动态路由

user.vue

<template>
    <div>
        <h2>userId:{{$route.params.userId}}</h2>
        <h2>userId:{{$route.params.username}}</h2>
    </div>
</template>

<script>
  export default {
    name: "User"
  }
</script>

<style scoped>

</style>

添加路由

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from "../views/Home";
import About from "../views/About";
import User from "../views/User";

Vue.use(VueRouter)

const routes = [
  {
    path:'',
    redirect:'/home'
  },
  {
    path:'/home',
    component:Home
  },
  {
    path:'/about',
    component:About
  },
  {
    path:'/user/:userId&:username',
    component:User
  }
]

const router = new VueRouter({
  routes,
  mode:'history',
  linkActiveClass:'main'
})

export default router

app.vue

<template>
    <div id="app">
      <h1>APP Components</h1>
        <router-link style="margin-right: 10px" to="/home" tag="button">home</router-link>
        <router-link style="margin-right: 10px" :to="'/user/'+userId+'&'+username" tag="button">goUser</router-link>
        <router-link to="/about" tag="button" replace>about</router-link>
        <router-view/>
    </div>
</template>

<script>
  export default {
    name: 'APP',
    data(){
      return{
        userId:'1',
        username:'yuge'
      }
    },
    methods: {}
  }
</script>

<style>
    .main {
        color: blueviolet;
    }

</style>

5.6 路由懒加载

当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

import Vue from 'vue'
import VueRouter from 'vue-router'
// import Home from "../views/Home";
// import About from "../views/About";
// import User from "../views/User";

Vue.use(VueRouter)
//路由懒加载 实现动态加载
//一个路由会被分为一个js文件 当使用时动态加载
const Home=()=>import('../views/Home')
const About=()=>import('../views/About')
const User=()=>import('../views/User')

const routes = [
  {
    path:'',
    redirect:'/home'
  },
  {
    path:'/home',
    component:Home
  },
  {
    path:'/about',
    component:About
  },
  {
    path:'/user/:userId&:username',
    component:User
  }
]

const router = new VueRouter({
  routes,
  mode:'history',
  linkActiveClass:'main'
})

export default router

5.7嵌套路由

创建HomeNews.vue

<template>
    <div>
<!--        // ul>li{新闻$}*4 按一个tab-->
        <ul>
            <li>新闻1</li>
            <li>新闻2</li>
            <li>新闻3</li>
            <li>新闻4</li>
        </ul>
    </div>
</template>

<script>
  export default {
    name: "HomeNews"
  }
</script>

<style scoped>

</style>

配路由

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)
//路由懒加载
const Home=()=>import('../views/Home')
const HomeNews=()=>import('../views/HomeNews')

const routes = [
  {
    path:'',
    redirect:'/home'
  },
  {
    path:'/home',
    component:Home,
    children:[
      {
        path:'',
        redirect:'news'
      },
      {
        // 不能加 /  vue会自动加入
        path:'news',
        component:HomeNews
      }
    ]
  }
]

const router = new VueRouter({
  routes,
  mode:'history',
  linkActiveClass:'main'
})

export default router

home.vue

<template>
    <div>
        <h2>Home</h2>
        <router-link to="/home/news">homeNews</router-link>
        <router-view/>
    </div>
</template>

<script>
  export default {
    name: "home"
  }
</script>

<style scoped>

</style>

5.8 参数传递

app.vue

<template>
    <div id="app">
        <h1>APP Components</h1>
        <router-link style="margin-right: 10px" to="/home" tag="button">home</router-link>

        <router-link :to="{path:'/profile',query:{name:'rose',id:2,age:18}}" tag="button">
            profile
        </router-link>
        <button @click="goProfile">goProfile 2.0</button>

        <router-link style="margin-right: 10px" :to="'/user/'+id+'&'+name" tag="button">goUser</router-link>
        <button @click="goUser">goUser 2.0</button>

        <router-link to="/about" tag="button" replace>about</router-link>
        <router-view/>
    </div>
</template>

<script>
  export default {
    name: 'APP',
    data() {
      return {
        id: '1',
        name: 'yuge',
        age: 18
      }
    },
    methods: {
      goUser() {
        this.$router.replace( '/user/'+this.id+'&'+this.name)
      },
      goProfile() {
        this.$router.replace({
          path: '/profile',
          query: {
            name: this.name,
            id: this.id,
            age: this.age
          }
        })

      }
    }
  }
</script>

<style>
    router-link,button{
        margin-right: 10px
    }

</style>

profile.vue

<template>
    <div>
        <h1>Profile Component</h1>
        <p>{{$route.query.name+' '+$route.query.id+' '+$route.query.age}}</p>
    </div>
</template>

<script>
  export default {
    name: "Profile",

  }
</script>

<style scoped>

</style>

router/index

{
  path:'/profile',
  component:Profile
}

5.9 全局导航守卫

导航守卫 | Vue Router (vuejs.org)

router/index

import Vue from 'vue'
import VueRouter from 'vue-router'
// import Home from "../views/Home";
// import About from "../views/About";
// import User from "../views/User";

Vue.use(VueRouter)
//路由懒加载
const Home=()=>import('../views/Home')
const HomeNews=()=>import('../views/HomeNews')
const About=()=>import('../views/About')
const User=()=>import('../views/User')
const Profile=()=>import('../views/Profile')

const routes = [
  {
    path:'',
    redirect:'/home'
  },
  {
    path:'/home',
    component:Home,
      //定义元数据
    meta:{
      title:'首页'
    },
    children:[
      {
        path:'',
        redirect:'news'
      },
      {
        // 不能加 /  vue会自动加入
        path:'news',
        component:HomeNews,
        meta:{
          title:'首页-新闻'
        }
      }
    ]

  },
  {
    path:'/about',
    component:About,
    meta:{
      title:'关于'
    },
    beforeEnter:((to,from,next)=>{
      console.log('about beforeEnter');
      next()
    })
  },
  {
    path:'/user/:userId&:username',
    component:User,
    meta:{
      title:'用户'
    }
  },
  {
    path:'/profile',
    component:Profile,
    meta:{
      title:'我的'
    }
  }
]

const router = new VueRouter({
  routes,
  mode:'history',
  linkActiveClass:'main'
})
//全局前置守卫
router.beforeEach((to,from,next)=>{
  document.title=to.meta.title
  next()
})

export default router

5.10 keep-alive

app.vue

keep-alive保持组件不被销毁

<keep-alive  exclude="about,profile">
  <router-view/>
</keep-alive>
<template>
    <div>
        <h2>Home</h2>
        <router-link to="/home/news">homeNews</router-link>
        <router-view/>
    </div>
</template>

<script>
  export default {
    name: "home",
    data() {
      return {
        path:'/home/news'
      }
    },
    destroyed(){
      console.log('home onDestroyed');
    },
    //activated 和 deactivated 只有使用keep-alive才会有效
    //所以那时候可以用created 和 destroyed
    activated(){
      console.log('activated');
      this.$router.replace(this.path)
    },
    deactivated () {
      console.log('deactivated');
    },
    beforeRouterLeave(to,from,next){
      this.path=this.$route.path
      next()
    }
  }
</script>

<style scoped>

</style>

六、Vuex

vuex 作用是用来创建和管理全局变量 而且可以实现数据响应式

6.1 vuex基本使用

  • 安装 npm install vuex --save

  • 在src下创建store/index.js

  • import Vue from 'vue'
    import Vuex from 'vuex'
    
    //安装插件
    Vue.use(Vuex)
    //创建对象
    const store = new Vuex.Store({
      state:{
        counter:0
      }
    })
    
    export default store
    
  • <template>
        <div id="app">
            <h2>{{$store.state.counter}}</h2>
            <button @click="$store.state.counter++">+</button>
            <button @click="$store.state.counter--">-</button>
        </div>
    </template>
    
    <script>
    
      export default {
        name: 'APP',
        components: {
        },
        data() {
          return {}
        },
        methods: {
        }
      }
    </script>
    
    <style>
        @import "./assets/css/base.css";
        button{
            width: 50px;
            margin-right: 10px;
        }
    
    </style>
    

    不建议直接修改$store.state的数据,直接修改无法得知是哪个页面修改的数据 不利于找错改错

6.2 Vue Devtools安装

Vue Devtools下载

6.3 Vuex-store

6.3.1 响应式规则

  1. vuex 中store 中的 state是响应式的, 当state 中的数据发生变化的时候, vue 组件会自动更新。当我们修改vuex 中store 对象中的数据,都会加入到 vue 整个响应式中, 在vuex 中 store 对象中state 等价于 单个组件 data 存储数据源的地方; 每一个属性都会有一个依赖, Dep => 对应多个watcher
  2. vue: 响应式系统: 在 data 数据源中保存的数据都会被保存到vue 的响应式系统中, 而相应式系统会监听属性的变化。 当属性发生变化时, 会通知页面中所有用到该属性的地方。
  3. 后边追加到响应式系统中: 就不会是响应式的数据。 做到响应式系统数据: 就必须在data 数据初始化好。 被 vue 数据监听。
//info没有address也会增加这个字段并且添加到响应式中
Vue.set(state.info, 'address',  '山东省')
//delete state.info.adress 这个做不到响应式
Vue.delete(state.info,'address')

6.3.2 store基本属性

import Vue from 'vue'
import Vuex from 'vuex'
import {INCREMENT} from "./mutations-type";
//modules
const moduleA={
  state:{
    name:'shuaige',
    address:'shangdong hubaojian'
  },
  mutations:{
    //名字不要重复
    updateName(state){
      state.name='big shuaige'
    }
  },
  getters: {
    fullAddress(state,getters,rootState){
      return 'address:'+state.address+rootState.info[0].age
    }
  },
  actions: {
    updateANameAsync(context){
      setTimeout(()=>context.commit('updateName'),1000)
    }
  },
  modules:{
    //最好不要在嵌套
  }
}
//安装插件
Vue.use(Vuex)
//创建对象

const store = new Vuex.Store({
  state: {
    info: [{
      name: 'yuge',
      age: 18
    }],
    counter: 0
  },
  /**
   * 注意 修改state唯一的途径是mutations
   * 里面的操作都是响应式的
   */
  mutations: {
    //自定义参数可以是基本类型 也可以是对象
    //[INCREMENT] mutations类型常量 为了防止变量名书写错误 定义变量名全局使用常量
    [INCREMENT](state, num) {
      state.counter += num
    },
    decrement(state, payload) {
      state.counter -= payload.num
    },
    updateInfo(state, payload) {
      state.info.push(payload)
    }
  },
  /**
   * 类似计算属性
   */
  getters: {
    //可以有两个参数 对应store里的state getters
    powerCounter(state, getters) {
      // 强行使用
      return state.counter * getters.computeCounter(1)
    },
    // 由于不能接受getters 函数不能接受其他参数 可以返回一个函数来自定义操作
    computeCounter(state) {
      return (num) => state.counter * num
    }
  },
  /**
   * 处理异步操作 一定放到action中
   */
  actions: {
    //context相当于store
    aUpdateInfo(context) {
      //Promise 是js新特性 建议百度
      return new Promise(((resolve, reject) => {
        setTimeout(() => {
          context.commit('updateInfo', {name: 'gyy', age: 21})
          //给调用的地方做回调
          resolve('update success')
        }, 1000)
      }))


    }
  },
  /**
   *为了避免store过大 vue允许将数据分为模块
   */
  modules:{
    a:moduleA
  }
})

export default store

使用

<template>
    <div id="app">
<!--        mutations-->
        <h2>{{$store.state.counter}}</h2>
        <h2>{{$store.state.info}}</h2>
<!--        getters-->
        square:<h2>{{$store.getters.powerCounter}}</h2>
        multiply:<h2>{{$store.getters.computeCounter(18)}}</h2>

        <button @click="addition(1)">+1</button>
        <button @click="subtraction(1)">-1</button>
        <button @click="updateInfo">updateInfo</button>
<!--        modules-->
        <h2>{{$store.state.a.name}}</h2>
            <h2>getters:{{$store.getters.fullAddress}}</h2>

            <button @click="updateAName">updateAName</button>
            <button @click="updateANameAsync">updateANameAsync</button>
    </div>
</template>

<script>
import {INCREMENT} from "./store/mutations-type";

export default {
    name: 'APP',
    components: {
    },
    data() {
      return {}
    },
    methods: {
      addition(num){
        this.$store.commit(INCREMENT,num)
      },
      subtraction(num){
        this.$store.commit({
          type:'decrement',
          //在这种写法下所有的参数会变成一个对象
          num
        })
      },
      updateInfo(){
        this.$store.dispatch('aUpdateInfo').then(result=>{
          console.log(result);
        })
      },
      updateAName(){
        this.$store.commit('updateName')
      },
      updateANameAsync(){
        this.$store.dispatch('updateANameAsync')
      }

    }
  }
</script>

<style>
    @import "./assets/css/base.css";
    button{
        width: 150px;
        margin-right: 10px;
    }

</style>

6.3.3 目录组织

在这里插入图片描述

七、网络模块封装(axios)

为了防止 因为过度依赖第三方网络请求而导致项目嗝屁时修改项目难度较大 所以要进行网络请求封装

使用说明 · Axios 中文说明 · 看云 (kancloud.cn)

7.1 axios基本使用

import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')

axios({
  url:'http://123.207.32.32:8000/home/data',
  method:'get',
  params:{
    type:'pop',
    page:1
  }
}).then(res=>{//内部支持promise
  console.log(res);
})

7.2 axios发送并发请求

axios.all([axios(),axios()]).then(results=>{})

const axios1 = {
  url: 'http://123.207.32.32:8000/home/data',
  method: 'get',
  params: {
    type: 'pop',
    page: 1
  }
}
const axios2 = {
  url: 'http://123.207.32.32:8000/home/multidata',
  method: 'get'
}

axios.all([axios(axios1),axios(axios2)]).then(results=>{
  console.log(results);
})
//也可以用axios.spread将每个请求分开
axios.all([axios(axios1), axios(axios2)]).then(axios.spread((res1, res2)=> {
  console.log(res1);
  console.log(res2);
}))

7.3 axios 配置信息

axios.defaults.baseURL='http://123.207.32.32:8000'   //默认为 http://localhost:8080
axios.defaults.timeout=5000    //ms

const axios1 = {
  url: '/home/data',
  method: 'get',
  params: {
    type: 'pop',
    page: 1
  }
}
const axios2 = {
  url: '/home/multidata',
  method: 'get'
}

axios.all([axios(axios1),axios(axios2)]).then(results=>{
  console.log(results);
})
//也可以用axios.spread将每个请求分开
axios.all([axios(axios1), axios(axios2)]).then(axios.spread((res1, res2)=> {
  console.log(res1);
  console.log(res2);
}))

7.3 axios 实例和模块封装

在这里插入图片描述

封装1

import axios from 'axios'

export function request(config,success,failure){
  const instance=axios.create({
    baseURL:'http://123.207.32.32:8000',
    timeout:5000
  })

  instance(config)
    .then(res=>success(res))
    .catch(err=>failure(err))
}
import Vue from 'vue'
import App from './App.vue'
import {request} from "./network/request";

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')

const axios1 = {
  url: '/home/multidata',
  method: 'get'
}

request(axios1,res=>{
  console.log(res)
},err=>{
  console.log(err);
})

封装2

import axios from 'axios'

export function request(config){
  const instance=axios.create({
    baseURL:'http://123.207.32.32:8000',
    timeout:5000
  })

  instance(config.baseConfig)
    .then(res=>config.success(res))
    .catch(err=>config.failure(err))
}
import Vue from 'vue'
import App from './App.vue'
import {request} from "./network/request";

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')

const axios2 = {
  baseConfig:{
    url: '/home/multidata',
    method: 'get'
  },
  success(res){
    console.log(res);
  },
  failure(err){
    console.log(err);
  }
}

request(axios2)

封装3(建议)

import axios from 'axios'

export function request(config){
  const instance=axios.create({
    baseURL:'http://123.207.32.32:8000',
    timeout:5000
  })

  return instance(config.baseConfig)
}
import Vue from 'vue'
import App from './App.vue'
import {request} from "./network/request";

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')

const axios2 = {
  baseConfig:{
    url: '/home/multidata',
    method: 'get'
  }
}

request(axios2)
  .then(res=>console.log(res))
  .catch(err=>console.log(err))

7.4 axios拦截器

import axios from 'axios'

export function request(config){
  const instance=axios.create({
    baseURL:'http://123.207.32.32:8000',
    timeout:5000
  })
  //请求拦截
  instance.interceptors.request.use(config=>{
    console.log(config);
    return config
  })
  //相应拦截
  instance.interceptors.response.use(res=>{
    //一般用来过滤数据
    console.log(res);
    return res.data
  },err=>{
    console.log(err);
    return err
  })

  return instance(config.baseConfig)
}

作业

4.1 动态绑定css

作业需求:点击列表中的哪一项,那么该项的文字变成红色,默认第一项变红

答案

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
  <style>
    .s1{
      color: red;
    }
    .s2{
      color: blue;
    }
  </style>
</head>
<body>
<div id="app">
  <ul>
<!--    index渲染时会替换成对应下标 只需控制num即可-->
    <li v-for="(item,index) in myList" :class="{s1:index===num}" @click="change(index)">{{item}}</li>
  </ul>
</div>
<script>
  const vm = new Vue({
    el: '#app',
    data: {
      myList:['t1111','t1111','t1111','t1111','t1111'],
      num:0
    },
    methods:{
      change:function (index) {
          this.num=index
      }
    }
  })
</script>
</body>
</html>

4.2父子组件数据双向绑定

需求:num1 num2 父子组件数据双向绑定 当num1发生改变 num2变成num1的100倍 当num2发生改变 num1变成num1的100分之一

ans:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
  <style>
  </style>
</head>
<body>
<div id="app">
  <h2>num1:{{num1}}</h2>
  <h2>num2:{{num2}}</h2>
  <t1 :num1="num1" @num1change="num1Change"
      :num2="num2" @num2change="num2Change"></t1>
</div>

<template id="t1">
  <div>
    <h2>dnum1:{{dnum1}}</h2>
    <h2>dnum2:{{dnum2}}</h2>

    dnum1<input type="text" :value="dnum1" @input="dnum1Change">
    <hr/>
    dnum2<input type="text" :value="dnum2" @input="dnum2Change">
  </div>
</template>
<script>

  const t1={
    template:'#t1',
    props:{
      num1:Number,
      num2:Number
    },
    data(){
      return{
        dnum1:this.num1,
        dnum2:this.num2
      }
    },
    methods:{
      dnum1Change(event){
        this.dnum1=event.target.value;
        this.$emit('num1change',this.dnum1)

        this.dnum2=this.dnum1*100;
        this.$emit('num2change',this.dnum2)
      },
      dnum2Change(event){
        this.dnum2=event.target.value;
        this.$emit('num2change',this.dnum2)

        this.dnum1=this.dnum2/100;
        this.$emit('num1change',this.dnum1)
      }
    }
  }
  const vm = new Vue({
    el: '#app',
    data: {
      num1:0,
      num2:0
    },
    components:{
      t1  
    },
    methods: {
      num1Change(v){
        this.num1=v
      },
      num2Change(v){
        this.num2=v
      }
    }
  })
</script>
</body>
</html>

拓展用 watch

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
  <style>
  </style>
</head>
<body>
<div id="app">
  <h2>num1:{{num1}}</h2>
  <h2>num2:{{num2}}</h2>
  <t1 :num1="num1" @num1change="num1Change"
      :num2="num2" @num2change="num2Change"></t1>
</div>

<template id="t1">
  <div>
    <h2>dnum1:{{dnum1}}</h2>
    <h2>dnum2:{{dnum2}}</h2>

    dnum1<input type="text" v-model="dnum1">
    <hr/>
    dnum2<input type="text" v-model="dnum2">
  </div>
</template>
<script>

  const t1 = {
    template: '#t1',
    //父传子用 props
    props: {
      num1: Number,
      num2: Number
    },
    data() {
      return {
        dnum1: this.num1,
        dnum2: this.num2
      }
    },
    methods: {},
    //用来监听属性变化
    watch: {
      //函数名要和变量名相同 可以有两个参数一般只用一个
      dnum1(newValue, oldValue) {
        //因为改变了dnum2 所以dnum2(newValue)将会收到改变 故不用再写发送num2change
        this.dnum2 = newValue * 100;
        this.$emit('num1change', newValue)
      },
      dnum2(newValue) {
        this.dnum1 = newValue / 100;
        this.$emit('num2change', newValue)
      }
    }

  }
  const vm = new Vue({
    el: '#app',
    data: {
      num1: 0,
      num2: 0
    },
    components: {
      t1  //cs6
    },
    methods: {
      num1Change(v) {
        this.num1 = v
      },
      num2Change(v) {
        this.num2 = v
      }
    }
  })
</script>
</body>
</html>
	

4.3 tabBar 案例

目录结构

在这里插入图片描述

tabbar.vue

<template>
    <div id="tab-bar">
        <slot></slot>
    </div>
</template>

<script>
  export default {
    name: "TabBar"
  }
</script>

<style scoped>
    #tab-bar {
        display: flex;
        background-color: #f6f6f6;
        left: 0;
        right: 0;
        bottom: 0;
        position: fixed;
        box-shadow: 0 -3px 1px rgba(8, 100, 59, 0.08);
    }
</style>

TabBarItem.vue

<template>
    <div class="tab-bar-item" @click="itemClick">
        <slot v-if="!isActive" name="item-icon"></slot>
        <slot v-else name="item-icon-active"></slot>
        <!--        <slot :class="{name:isActive}" name="item-name"></slot> class会失效因为会被替换
                    v-if  虽然可以但建议还是用div封一下
-->
        <div :style="activeStyle">
            <slot name="item-name"></slot>
        </div>
    </div>
</template>

<script>
  export default {
    name: "TabBarItem",
    data() {
      return {
      }
    },
    props:{
      path:{
        default:'',
        type:String
      },
      activeColor:{
        default: '#00645A',
        type: String
      }
    },
    computed:{
      isActive(){
        return this.$route.path.indexOf(this.path) >=0
      },
      activeStyle(){
        return this.isActive?{color:this.activeColor}:{}
      }
    },
    methods: {
      itemClick(){
        if (!this.isActive)
        this.$router.replace(this.path)
      },
    }
  }
</script>

<style scoped>
    .tab-bar-item {
        flex: 1;
        text-align: center;
        height: 49px;
        margin-top: 3px;
        vertical-align: middle;
    }

    .tab-bar-item img {
        width: 20px;
        height: 20px;
    }
</style>

MainTabBar.vue

<template>
    <div>
        <tab-bar>
            <tab-bar-item path="/home" active-color="red">
                <img slot="item-icon" src="@/assets/img/tabbar/home.svg" alt="">
                <img slot="item-icon-active" src="@/assets/img/tabbar/home-active.svg" alt="">
                <div slot="item-name">Home</div>
            </tab-bar-item>
            <tab-bar-item path="/advance">
                <img slot="item-icon" src="@/assets/img/tabbar/advance.svg" alt="">
                <div slot="item-name">Advance</div>
            </tab-bar-item>
            <tab-bar-item path="/settings">
                <img alt="" slot="item-icon" src="@/assets/img/tabbar/settings.svg">
                <div slot="item-name">Settings</div>
            </tab-bar-item>
            <tab-bar-item path="/profile">
                <img slot="item-icon" src="@/assets/img/tabbar/profile.svg" alt="">
                <div slot="item-name">Profile</div>
            </tab-bar-item>
        </tab-bar>
    </div>
</template>

<script>
  import TabBar from "@/components/tabbar/TabBar"
  import TabBarItem from "@/components/tabbar/TabBarItem";
  export default {
    name: "MainTabBar",
    components:{
      TabBarItem,
      TabBar
    }
  }
</script>

<style scoped>

</style>

配置router

import Vue from 'vue'
import VueRouter from 'vue-router'
// import Home from "../views/Home";
// import About from "../views/About";
// import User from "../views/User";

Vue.use(VueRouter)
//路由懒加载
const Home=()=>import('../views/home/Home')
const About=()=>import('../views/About')
const Profile=()=>import('../views/profile/Profile')
const Advance=()=>import('../views/advance/Advance')
const Settings=()=>import('../views/settings/Settings')

const routes = [
  {
    path:'',
    redirect:'/home'
  },
  {
    path:'/home',
    component:Home,
    meta:{
      title:'首页'
    }
  },
  {
    path:'/about',
    component:About,
    meta:{
      title:'关于'
    }
  },
  {
    path:'/profile',
    component:Profile,
    meta:{
      title:'我的'
    }
  },
  {
    path:'/advance',
    component:Advance,
    meta:{
      title:'高级'
    }
  },
  {
    path:'/settings',
    component:Settings,
    meta:{
      title:'设置'
    }
  },
]

const router = new VueRouter({
  routes,
  mode:'history',
  linkActiveClass:''
})

router.beforeEach((to,from,next)=>{
  document.title=to.meta.title
  next()
})

export default router

app.vue

<template>
    <div id="app">
        <router-view></router-view>
       <MainTabBar></MainTabBar>
    </div>
</template>

<script>
  import MainTabBar from "./components/MainTabBar";

  export default {
    name: 'APP',
    components: {
      MainTabBar
    },
    data() {
      return {}
    },
    methods: {
    }
  }
</script>

<style>
    @import "./assets/css/base.css";


</style>

问题与填坑

Cannot read property ‘xx’ of undefined

  • 80%是你的变量名是不是少了个 符 号 什 么 的 , 例 如 符号什么的,例如 route

vue-devtools 安装

Vue Devtools_5.3.4_chrome扩展插件下载_极简插件 (zzzmh.cn)

Chrome 新版edge直接下载 打开开发者模式 拖进来安装就行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值