Vue2知识点

Vue

插值表达式:Vue的一种模板语法

作用:利用 表达式 进行插值渲染

语法:{{ 表达式 }}

注意点:

  1. 使用的数据要存在

  2. 支持的是表达式,不是语句 if for

  3. 不能在标签属性中使用 {{ }}

  4. data里面的数据是响应式数据

v-html : 设置元素的innerHTML <div v-html="msg"></div>

v-show 通过css当中display :none来隐藏 适用于频繁操作

v-if 根据判断条件 控制元素的创建与移除 (条件渲染)

v-else必须配合v-if使用

​ <p v-if="score>=90">成绩评定A:奖励电脑一台</p>

​ <p v-else-if="score>=70">成绩评定B:奖励周末郊游</p>

​ <p v-else-if="score>=60">成绩评定C:奖励零食礼包</p>

​ <p v-else>成绩评定D:惩罚一周不能玩手机</p>

v-on:事件名 = '内联语句' v-on: 可以替换为@

<button @click = 'count=count-2'>-</button>

<button v-on:mouseenter = 'count = count+2'>+</button>

methods:{ } 提供处理逻辑函数

​ methods中的所有函数,this指向都指当前实例

v-bind:src(标签属性)

简写 :src

<img v-bind:src="imgUrl" v-bind:title="msg" alt="">

<img :src="imgUrl" :title="msg" alt="">

操作class

<div :class="{类名1:布尔值,类名2:布尔值}"></div>如果布尔值为true就添加上去,false就不添加

<div :class="[类名1,类名2]"></div> 数组当中的所有类都会添加到盒子上去

v-for="(item,index) in list"

<li v-for="(item,index) in list">水果{{item}}-{{index}}</li>

一个参数括号可以省略

​ <li v-for="item in list">水果{{item}}-</li>

v-for中的key:

给列表项添加唯一标识,便于Vue进行列表项的正确排序使用

v-for的行为会尝试原地修改元素

v-model="变量" 实现双向数据绑定

   <div id="app">
      <h3>v-model修饰符 .trim .number</h3>
      姓名:<input v-model.trim="username" type="text"><br>
      年纪:<input v-model.number="age" type="text"><br>
  ​
      
      <h3>@事件名.stop     →  阻止冒泡</h3>
      <div @click="fatherFn" class="father">
        <div @click.stop="sonFn" class="son">儿子</div>
      </div>
  ​
      <h3>@事件名.prevent  →  阻止默认行为</h3>
      <a @click.prevent href="http://www.baidu.com">阻止默认行为</a>
    </div>

计算属性: computed:{}计算属性:有缓存的,一旦算出结果,就会立刻缓存。下一次读取 直接读取缓存就行了 性能特别高

​ 1.计算属性的默认简写,只能读取不能修改computed:{

计算属性名(){一段代码逻辑

}

return 结果

}

​ 2.计算属性的完整写法

  
   computed: {   fullName:{
            get(){
              return this.firstName+this.lastName
            },
            set(value){ //value传入到fullName的值
             console.log(value);
             this.firstName = value.slice(0,1)
             this.lastName = value.slice(1)
            }
          }
        },

​ set(修改的值)

watch监视器:监视数据变化,执行一些逻辑变化和异步操作

​ 1.简单写法

watch:{

数据属性名(newValue,oldValue){

一些业务逻辑

}

}

or

watch:{

'对象属性名'(newValue,oldValue){

一些业务逻辑

}

}

​ 2.完整写法

  
  watch:{
      数据属性名:{
          deep:true, //深度监视
          immediate:true //进入页面立刻执行一次handler方法
          handler(newValue){
              console.log(newValue)
          }
      }
  }

deep:true 对复杂数据类型进行深度监视immediate: true, // 立刻执行,一进入页面handler就立刻执行一次

生命周期八大函数

  
   // 1. 创建阶段(准备数据)
        beforeCreate () {
          console.log('beforeCreate 响应式数据准备好之前', this.count)
        },
        created () {
          console.log('created 响应式数据准备好之后', this.count)
          // this.数据名 = 请求回来的数据
          // 可以开始发送初始化渲染的请求了
        },
  ​
        // 2. 挂载阶段(渲染模板)
        beforeMount () {
          console.log('beforeMount 模板渲染之前', document.querySelector('h3').innerHTML)
        },
        mounted () {
          console.log('mounted 模板渲染之后', document.querySelector('h3').innerHTML)
          // 可以开始操作dom了
        },
  ​
        // 3. 更新阶段(修改数据 → 更新视图)
        beforeUpdate () {
          console.log('beforeUpdate 数据修改了,视图还没更新', document.querySelector('span').innerHTML)
        },
        updated () {
          console.log('updated 数据修改了,视图已经更新', document.querySelector('span').innerHTML)
        },
  ​
        // 4. 卸载阶段
        beforeDestroy () {
          console.log('beforeDestroy, 卸载前')
          console.log('清除掉一些Vue以外的资源占用,定时器,延时器...')
        },
        destroyed () {
          console.log('destroyed,卸载后')
        }

scoped

1.style中的样式 默认是作用到全局的

2.加上scoped可以让样式变成局部样式

组件都应该有独立的样式,推荐加scoped(原理)


scoped原理:

1.给当前组件模板的所有元素,都会添加上一个自定义属性

data-v-hash值

data-v-5f6a9d56 用于区分开不通的组件

2.css选择器后面,被自动处理,添加上了属性选择器

div[data-v-5f6a9d56]

工程化状态下data就是一个函数

// data() {

// console.log('函数执行了')

// return {

// count: 100,

// }

// },

组件通信

父组件向子组件传值

  
   <div class="app" style="border: 3px solid #000; margin: 10px">
      我是APP组件
      <!-- 1.给父组件标签,添加属性方式 赋值 -->
      <Son :title="myTitle"></Son>
    </div>
    
    
    
    <script>
  import Son from './components/Son.vue'
  export default {
    name: 'App',
    data() {
      return {
        myTitle: '学前端,就来黑马程序员',
      }
    },
    components: {
      Son,
    },
  }
  </script>
  
  <template>
    <div class="son" style="border:3px solid #000;margin:10px">
      <!-- 3.直接使用props的值 -->
      我是Son组件 {{title}}
    </div>
  </template>
  ​
  <script>
  export default {
    name: 'Son-Child',
    // 2.通过props来接受
    props:['title']
  }
  </script>
  ​
  <style>
  ​
  </style>

子组件向父组件传值

  
  <template>
    <div class="son" style="border: 3px solid #000; margin: 10px">
      我是Son组件 {{ title }}
      <button @click="changeFn">修改title</button>
    </div>
  </template>
  ​
  <script>
  export default {
    name: 'Son-Child',
    props: ['title'],
    methods: {
      changeFn() {
        // 1.通过this.$emit() 向父组件发送通知
        this.$emit('changTitle','传智教育')
      },
    },
  }
  </script>
  ​
  <style>
  </style>
  
  <template>
    <div class="app" style="border: 3px solid #000; margin: 10px">
      我是APP组件
      <!-- 2.父组件对子组件的消息进行监听 -->
      <Son :title="myTitle" @changTitle="handleChange"></Son>
    </div>
  </template>
  ​
  <script>
  import Son from './components/Son.vue'
  export default {
    name: 'App',
    data() {
      return {
        myTitle: '学前端,就来黑马程序员',
      }
    },
    components: {
      Son,
    },
    methods: {
      // 3.提供处理函数,提供逻辑
      handleChange(newTitle) {
        this.myTitle = newTitle
      },
    },
  }
  </script>
  ​
  <style>
  </style>

props完整写法

  
  <template>
    <div class="base-progress">
      <div class="inner" :style="{ width: w + '%' }">
        <span>{{ w }}%</span>
      </div>
    </div>
  </template>
  ​
  <script>
  export default {
    // 1.基础写法(类型校验)
    // props: {
    //   w: Number,
    // },
  ​
    // 2.完整写法(类型、默认值、非空、自定义校验)
    props: {
      w: {
        type: Number,
        required: true,
        default: 0,//默认值
        validator(val) {
          // console.log(val)
          if (val >= 100 || val <= 0) {
            console.error('传入的范围必须是0-100之间')
            return false
          } else {
            return true
          }
        },
      },
    },
  }
  </script>
  ​
  <style scoped>
  .base-progress {
    height: 26px;
    width: 400px;
    border-radius: 15px;
    background-color: #272425;
    border: 3px solid #272425;
    box-sizing: border-box;
    margin-bottom: 30px;
  }
  .inner {
    position: relative;
    background: #379bff;
    border-radius: 15px;
    height: 25px;
    box-sizing: border-box;
    left: -3px;
    top: -2px;
  }
  .inner span {
    position: absolute;
    right: 0;
    top: 26px;
  }
  </style>

validator固定的 验证器

单项数据流

prop的数据是外部传过来的不能直接修改,

要遵循单项数据流

even bus事件总线(相当于一个桥梁)

作用:非父子通信,进行简易消息传递(复杂->Vuex)

1.创建一个都可以访问到的事件总线(空vue实例 -->utils/EvenBus.js)

  
  import Vue from 'vue'
  ​
  const Bus  =  new Vue()
  ​
  export default Bus

2.组件接收方,监听Bus实例事件

  
  <template>
    <div class="base-a">
      我是A组件(接受方)
      <p>{{msg}}</p>  
    </div>
  </template>
  ​
  <script>
  import Bus from '../utils/EventBus'
  export default {
    data() {
      return {
        msg: '',
      }
    },
    created() {
      Bus.$on('sendMsg', (msg) => {
        // console.log(msg)
        this.msg = msg
      })
    },
  }
  </script>
  ​
  <style scoped>
  .base-a {
    width: 200px;
    height: 200px;
    border: 3px solid #000;
    border-radius: 3px;
    margin: 10px;
  }
  </style>

3.组件发送方,触发Bus实例事件

  
  <template>
    <div class="base-b">
      <div>我是B组件(发布方)</div>
      <button @click="sendMsgFn">发送消息</button>
    </div>
  </template>
  ​
  <script>
  import Bus from '../utils/EventBus'
  export default {
    methods: {
      sendMsgFn() {
        Bus.$emit('sendMsg', '今天天气不错,适合旅游')
      },
    },
  }
  </script>
  ​
  <style scoped>
  .base-b {
    width: 200px;
    height: 200px;
    border: 3px solid #000;
    border-radius: 3px;
    margin: 10px;
  }
  </style>

非父子通信provide&inject

provide&inject作用:跨层级共享数据

1.父组件provide提供数据

  
  <template>
    <div class="app">
      我是APP组件
      <button @click="change">修改数据</button>
      <SonA></SonA>
      <SonB></SonB>
    </div>
  </template>
  ​
  <script>
  import SonA from './components/SonA.vue'
  import SonB from './components/SonB.vue'
  export default {
    provide() {
      return {
        // 简单类型 是非响应式的
        color: this.color,
        // 复杂类型 是响应式的-推荐
        userInfo: this.userInfo,
      }
    },
    data() {
      return {
        color: 'pink',
        userInfo: {
          name: 'zs',
          age: 18,
        },
      }
    },
    methods: {
      change() {
        this.color = 'red'
        this.userInfo.name = 'ls'
      },
    },
    components: {
      SonA,
      SonB,
    },
  }
  </script>
  ​
  <style>
  .app {
    border: 3px solid #000;
    border-radius: 6px;
    margin: 10px;
  }
  </style>

2.子孙组件inject取值使用

  
  <template>
    <div class="grandSon">
      我是GrandSon
      {{ color }} -{{ userInfo.name }} -{{ userInfo.age }}
    </div>
  </template>
  ​
  <script>
  export default {
    inject: ['color', 'userInfo'],
  }
  </script>
  ​
  <style>
  .grandSon {
    border: 3px solid #000;
    border-radius: 6px;
    margin: 10px;
    height: 100px;
  }
  </style>

v-model原理

v-model本质上是一个语法糖 是value和input事件的和写

作用提供数据的双向绑定

  <template>
    <div class="app">
      <input type="text" v-model="msg1" />
      <br />
      <!-- v-model的底层其实就是:value和 @input的简写 -->
      <input type="text" :value="msg2" @input="msg2 = $event.target.value" />
    <!-- $event 参数可以访问原生事件对象 -->
    </div>
  </template>
  ​
  <script>
  export default {
    data() {
      return {
        msg1: '',
        msg2: '',
      }
    },
  }
  </script>
  ​
  <style>
  </style>

v-model简化代码

1.双向绑定数据

2.子组件中props通过value接收,事件触发input

prop属性名固定value

  
  <template>
      <div>
          <!-- 我们不能直接使用v-model去绑定 -->
          <!-- 我们使用change事件监听 -->
          <select :value="value" @change="handleChange">
              <option value="1">北京</option>
              <option value="2">上海</option>
              <option value="3">广州</option>
              <option value="4">深圳</option>
          </select>
      </div>
  </template>
  <script>
  export default {
      name: 'VueBaseSelect',
      props: {
          // 我们直接使用value去接收父组件中的selectId
          value: String
      },
      methods: {
          handleChange(e) {
              // 子传父 用到this.$emit()
              this.$emit("input", e.target.value)
          }
      }
  };
  </script>
  <style scoped></style>
  ​

3.父组件 v-model给组件直接绑数据

  
  <template>
    <div>
      <!-- 我们使用v-model v-model的原理采用了
       :value + @input="?=$event.target.value" -->
      <BaseSelect v-model="selectId"></BaseSelect>
    </div>
  </template>
  <script>
  import BaseSelect from "./components/BaseSelect.vue"
  export default {
    name: 'VueApp',
    data() {
      return {
        selectId: '2',
      };
    },
    components: {
      BaseSelect
    }
  };
  </script>
  <style scoped></style>
  ​

.sync修饰符

作用:可以使用子组件与父组件的双向数据绑定,简化代码

特点:prop属性名,可以自定义,非v-model的固定value

1.父组件属性传参

:isShow.sync="isShow 等价于 :isShow+@updata:isShow

  
  <template>
    <div class="app">
      <button @click="openDialog">退出按钮</button>
      <!-- isShow.sync  => :isShow="isShow" @update:isShow="isShow=$event" -->
      <BaseDialog :isShow.sync="isShow"></BaseDialog>
    </div>
  </template>
  ​
  <script>
  import BaseDialog from './components/BaseDialog.vue'
  export default {
    data() {
      return {
        isShow: false,
      }
    },
    methods: {
      openDialog() {
        this.isShow = true
        // console.log(document.querySelectorAll('.box')); 
      },
    },
    components: {
      BaseDialog,
    },
  }
  </script>
  ​
  <style>
  </style>
  ​

2.子组件向父组件传递值

this.$emit('update:isShow',false)

  
  <template>
    <div class="base-dialog-wrap" v-show="isShow">
      <div class="base-dialog">
        <div class="title">
          <h3>温馨提示:</h3>
          <button class="close" @click="closeDialog">x</button>
        </div>
        <div class="content">
          <p>你确认要退出本系统么?</p>
        </div>
        <div class="footer">
          <button>确认</button>
          <button>取消</button>
        </div>
      </div>
    </div>
  </template>
  ​
  <script>
  export default {
    props: {
      isShow: Boolean,
    },
    methods:{
      closeDialog(){
        this.$emit('update:isShow',false)
      }
    }
  }
  </script>
  ​
  <style scoped>
  .base-dialog-wrap {
    width: 300px;
    height: 200px;
    box-shadow: 2px 2px 2px 2px #ccc;
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    padding: 0 10px;
  }
  .base-dialog .title {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 2px solid #000;
  }
  .base-dialog .content {
    margin-top: 38px;
  }
  .base-dialog .title .close {
    width: 20px;
    height: 20px;
    cursor: pointer;
    line-height: 10px;
  }
  .footer {
    display: flex;
    justify-content: flex-end;
    margin-top: 26px;
  }
  .footer button {
    width: 80px;
    height: 40px;
  }
  .footer button:nth-child(1) {
    margin-right: 10px;
    cursor: pointer;
  }
  </style>

利用ref和$refs获取DOM元素标签

1.给目标标签-添加ref属性(也可以获取组件)

  
   <div class="base-chart-box" ref="baseChartBox">子组件</div>

2.恰当时机,通过this.$refs.xxx,获取目标标签(mounted(){} //页面渲染完成)

  
  mounted(){
      console.log(this.$refs.baseChartBox)
  }

document.querySelector 会查找项目中所有的元素

Vue异步更新的

$nextTick:等DOM更新完后才会执行方法里面的函数体

语法this.$nextTick(函数体)

定义指令**

1.全局注册指令(在main.js)

  
  // // 1. 全局注册指令
  // Vue.directive('focus', { //focus指令名
  //   // inserted 会在 指令所在的元素,被插入到页面中时触发
  //   inserted (el) {
  //     // el 就是指令所绑定的元素
  //     // console.log(el);
  //     el.focus()
  //   }
  // })
  ​

在标签使用v-focus 就可以获取焦点了

2.局部注册指令

  
  // 2. 局部注册指令
    directives: {
      // 指令名:指令的配置项
      focus: {
        inserted (el) {
          el.focus()
        }
      }
    }

指令的值

binding.value 就是指令的值

inserted 提供的是元素被添加到页面中时的逻辑

  
  <template>
    <div>
      <h1 v-color="color1">指令的值1测试</h1>
      <h1 v-color="color2">指令的值2测试</h1>
    </div>
  </template>
  ​
  <script>
  export default {
    data () {
      return {
        color1: 'red',
        color2: 'orange'
      }
    },
    directives: {
      color: {
        // 1. inserted 提供的是元素被添加到页面中时的逻辑
        inserted (el, binding) {
          // console.log(el, binding.value);
          // binding.value 就是指令的值
          el.style.color = binding.value
        },
        // 2. update 指令的值修改的时候触发,提供值变化后,dom更新的逻辑
        update (el, binding) {
          console.log('指令的值修改了');
          el.style.color = binding.value
        }
      }
    }
  }
  </script>
  ​
  <style>
  ​
  </style>

*v-loading**

默认插槽:让组件内部自定义

1.组件内需要定制的部分,改用<slot></slot>

  
  <template>
    <div class="dialog">
      <div class="dialog-header">
        <h3>友情提示</h3>
        <span class="close">✖️</span>
      </div>
  ​
      <div class="dialog-content">
        <!-- 1. 在需要定制的位置,使用slot占位 -->
        <slot></slot>
      </div>
      <div class="dialog-footer">
        <button>取消</button>
        <button>确认</button>
      </div>
    </div>
  </template>

2.使用组件时在 <MyDialog>

​ <div>你确认要删除么</div>

​ </MyDialog>传入结构替换slot

  
  <template>
    <div>
      <!-- 2. 在使用组件时,组件标签内填入内容 -->
      <MyDialog>
        <div>你确认要删除么</div>
      </MyDialog>
  ​
      <MyDialog>
        <p>你确认要退出么</p>
      </MyDialog>
    </div>
  </template>

插槽-默认值 -后备内容

插槽后备内容:封装组件时可以为预留的<slot>插槽提供默认内容

1.往slot标签内部,编写内容,可以作为后备内容(默认值)

<slot>我是默认的文本内容 </slot>

  
  <template>
    <div class="dialog">
      <div class="dialog-header">
        <h3>友情提示</h3>
        <span class="close">✖️</span>
      </div>
  ​
      <div class="dialog-content">
        <!-- 往slot标签内部,编写内容,可以作为后备内容(默认值) -->
        <slot>
          我是默认的文本内容
        </slot>
      </div>
      <div class="dialog-footer">
        <button>取消</button>
        <button>确认</button>
      </div>
    </div>
  </template>
  ​
  
  <template>
    <div>
      <MyDialog></MyDialog>  //触发插槽默认值
  ​
      <MyDialog>
        你确认要退出么
      </MyDialog>
    </div>
  </template>

具名插槽

1.多个slot使用name属性区分名字

<slot name="名字"></slot>

  
  <template>
    <div class="dialog">
      <div class="dialog-header">
        <!-- 一旦插槽起了名字,就是具名插槽,只支持定向分发 -->
        <slot name="head"></slot>
      </div>
  ​
      <div class="dialog-content">
        <slot name="content"></slot>
      </div>
      <div class="dialog-footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </template>

2.template配合v-slot:名字,来分发对应的标签,v-slot:名字简写#插槽名

  
  <template>
    <div>
      <MyDialog>
        <!-- 需要通过template标签包裹需要分发的结构,包成一个整体 -->
        <template v-slot:head>
          <div>我是大标题</div>
        </template>
        
        <template v-slot:content>
          <div>我是内容</div>
        </template>
  ​
        <template #footer>
          <button>取消</button>
          <button>确认</button>
        </template>
      </MyDialog>
    </div>
  </template>

插槽作用域

  1. 给slot标签,添加属性的方式传值

  <slot :row="item" msg="测试文本"></slot>


  1. 将所有的属性,添加到一个对象中

     <!-- 2. 将所有的属性,添加到一个对象中 -->
              <!-- 
                 {
                   row: { id: 2, name: '孙大明', age: 19 },
                   msg: '测试文本'
                 }
    
    
    
    1. 通过template #插槽名="变量名" 接收

      <template>
        <div>
          <MyTable :data="list">
            <!-- 3. 通过template #插槽名="变量名" 接收 -->
            <template #default="obj">
              <button @click="del(obj.row.id)">
                删除
              </button>
            </template>
          </MyTable>
          
          <MyTable :data="list2">
            <template #default="{ row }">
              <button @click="show(row)">查看</button>
            </template>
          </MyTable>
        </div>
      </template>
      
      
      

路由

1.什么是单页面应用程序? 所有功能都在一个html页面

2.单页面的优缺点? 优点:按需更新性能高,开发效率好。缺点:学习成本,首屏加载慢,不利于SEO

3.应用场景:系统类网站、内部网站、

vue当中的路由就是路径和组件的映射关系

VueRouter的使用(5+2)v2对应VueRouter3.x Vuex3.x v3对应VueRouter4.x Vuex4.x**

5个基础步骤:

1.下载VueRouter模块到当前工程,版本3.6.5

yarn add vue-router@3.6.5

2.在main.js中引入

import VueRouter from 'vue-router'


3.安装注册

Vue.use(VueRouter) // VueRouter插件初始化


4.创建路由对象

const router = new VueRouter({
  // routes 路由规则们
  // route  一条路由规则 { path: 路径, component: 组件 }
  routes: [
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/friend', component: Friend },
  ]
})


5.将路由对象创建到new Vue实例当中去,建立关联

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


2个核心步骤

1.创建views目录,在main.js中配置路由规则

const router = new VueRouter({
  // routes 路由规则们
  // route  一条路由规则 { path: 路径, component: 组件 }
  routes: [
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/friend', component: Friend },
  ]
})


2.配置导航,配置路由出口

<template>
  <div>
    <div class="footer_wrap">
      <a href="#/find">发现音乐</a>
      <a href="#/my">我的音乐</a>
      <a href="#/friend">朋友</a>
    </div>
    <div class="top">
      <!-- 路由出口 → 匹配的组件所展示的位置 -->
      <router-view></router-view>
    </div>
  </div>
</template>


<!-- 路由出口 → 匹配的组件所展示的位置 -->

<router-view></router-view>

告诉组件名字是多个单词

<template>
  <div>
    <p>发现音乐</p>
    <p>发现音乐</p>
    <p>发现音乐</p>
    <p>发现音乐</p>
  </div>
</template>

<script>
export default {
  name: 'FindMusic'//告诉组件名字是多个单词
}
</script>

<style>

</style>


组件分类:(页面组件&复用组件)便于维护

页面组件-views文件夹=>配合路由,页面展示

复用组件-components文件夹=>封装复用

路由的封装抽离

1.src新建文件夹router当中新建index.js

@相当与src

import Find from '@/views/Find'
import My from '@/views/My'
import Friend from '@/views/Friend'
//@类似src
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化

// 创建了一个路由对象
const router = new VueRouter({
  // routes 路由规则们
  // route  一条路由规则 { path: 路径, component: 组件 }
  routes: [
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/friend', component: Friend },
  ]
})

export default router


2.在main.js当中导入

import Vue from 'vue'
import App from './App.vue'
import router from './router/index'

Vue.config.productionTip = false

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



使用vue-router当中的router-link(取代a标签)

1.router-link必须配置to属性,本质还是a标签,to无需#

<template>
  <div>
    <div class="footer_wrap">
      <router-link to="/find">发现音乐</router-link>
      <router-link to="/my">我的音乐</router-link>
      <router-link to="/friend">朋友</router-link>
    </div>
    <div class="top">
      <!-- 路由出口 → 匹配的组件所展示的位置 -->
      <router-view></router-view>
    </div>
  </div>
</template>


2.能高亮,默认就会提供高亮类名,可以直接设置高亮样式(自带激活时的类名)

声明式导航-两个类名

router-link会自动给当前导航添加两给高亮类名

1.router-link-exact-active 精确匹配

to="/my" 仅可以匹配 /my

2.router-link-active 模糊匹配(用的多)

to="/my"可以匹配 /my /my/a /my/b .......

自定义匹配的名字

1.在router的index.js中link自定义高亮类名

在router外面

import Find from '@/views/Find'
import My from '@/views/My'
import Friend from '@/views/Friend'

import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化

// 创建了一个路由对象
const router = new VueRouter({
  // routes 路由规则们
  // route  一条路由规则 { path: 路径, component: 组件 }
  routes: [
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/friend', component: Friend },
  ],
  // link自定义高亮类名
  linkActiveClass: 'active', // 配置模糊匹配的类名
  linkExactActiveClass: 'exact-active' // 配置精确匹配的类名
})

export default router


声明式导航-跳转传参

目标:在跳转时可以进行传参

1.查询参数传参(适合多个参数传参)

​ 1.1语法格式 to="/path?参数名=值"

  <router-link to="/search?key=黑马程序员">黑马程序员</router-link>


​ 1.2.对应页面组件接收传递过来的值 $route.query.参数名

<template>
  <div class="search">
    <p>搜索关键字: {{ $route.query.key }} </p>
    <p>搜索结果: </p>
    <ul>
      <li>.............</li>
      <li>.............</li>
      <li>.............</li>
      <li>.............</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'MyFriend',
  created () {
    // 在created中,获取路由参数
    // this.$route.query.参数名 获取
    console.log(this.$route.query.key);
  }
}
</script>



在created中,获取路由参数 this.$route.query.参数名 获取

2.1动态路由传参(传单个参数比较方便)

​ 1.配置动态路由

/path/:参数名? /path/:参数名,表示必须传,如果不传参也希望匹配可以加一个可选符?

import Home from '@/views/Home'
import Search from '@/views/Search'
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化

// 创建了一个路由对象
const router = new VueRouter({
  routes: [
    { path: '/home', component: Home },
    { path: '/search/:words', component: Search }//动态路由传参
  ]
})

export default router


​ 2.配置导航链接 to="/path/参数值"‘

<template>
  <div class="home">
    <div class="logo-box"></div>
    <div class="search-box">
      <input type="text">
      <button>搜索一下</button>
    </div>
    <div class="hot-link">
      热门搜索:
      <router-link to="/search/黑马程序员">黑马程序员</router-link>
      <router-link to="/search/前端培训">前端培训</router-link>
      <router-link to="/search/如何成为前端大牛">如何成为前端大牛</router-link>
    </div>
  </div>
</template>


​ 3.对应页面组件接收传递过来的值 $route.params.参数名

<template>
  <div class="search">
    <p>搜索关键字: {{ $route.params.words }} </p>
    <p>搜索结果: </p>
    <ul>
      <li>.............</li>
      <li>.............</li>
      <li>.............</li>
      <li>.............</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'MyFriend',
  created () {
    // 在created中,获取路由参数
    // this.$route.query.参数名 获取查询参数
    // this.$route.params.参数名 获取动态路由参数
    console.log(this.$route.params.words);
  }
}


Vue路由-重定向

问题:网站打开,url默认/路径,未匹配到组件是,会出现空白

说明:重定向->匹配path后,强制跳转path的路径

语法:{ path:匹配路径 ,redirect :重定向的路径 }

import Home from '@/views/Home'
import Search from '@/views/Search'
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化

// 创建了一个路由对象
const router = new VueRouter({
  routes: [
    { path: '/', redirect: '/home' },//页面默认跳转到/home
    { path: '/home', component: Home },
    { path: '/search/:words?', component: Search }
  ]
})

export default router


Vue路由 -404

作用:在找不到匹配时,给个提示页面

位置:配在路由最后

语法:在router文件当中index.js当中配置path:"*"(任意路径)-前面不匹配就最后这个

import Home from '@/views/Home'
import Search from '@/views/Search'
import NotFound from '@/views/NotFound'
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化

// 创建了一个路由对象
const router = new VueRouter({
  routes: [
    { path: '/', redirect: '/home' },
    { path: '/home', component: Home },
    { path: '/search/:words?', component: Search },
    { path: '*', component: NotFound }
  ]
})

export default router


在views当中创建一个NotFound.vue 组件

<template>
  <div>
    <h1>404 Not Found</h1>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>


Vue模式设置

hash路由(默认) 例如http://localhost:8081/search#/search

history路由(常用)例如http://localhost:8081/search/search

模式切换 在路由对象里面添加 mode:"history" 一旦采用history,地址栏就没有#,需要后台配置访问规则

import Home from '@/views/Home'
import Search from '@/views/Search'
import NotFound from '@/views/NotFound'
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化

// 创建了一个路由对象
const router = new VueRouter({
  // 注意:一旦采用了 history 模式,地址栏就没有 #,需要后台配置访问规则
  mode: 'history', //路由转化为history模式
  routes: [
    { path: '/', redirect: '/home' },
    { path: '/home', component: Home },
    { name: 'search', path: '/search/:words?', component: Search },
    { path: '*', component: NotFound }
  ]
})

export default router


编程式导航-2种路由的切换

1.path跳转(1)简写 this.$router.push('路由路径')

​ (2)完整写法 this.$router.push({path:'路由路径'})

      // 1. 通过路径的方式跳转
      // (1) this.$router.push('路由路径') [简写]
      // this.$router.push('/search')

      // (2) this.$router.push({     [完整写法]
      //         path: '路由路径' 
      //     })
      // this.$router.push({
      //   path: '/search'
      // })


2.name 命名跳转(适合path路径长的场景)

​ 给页面加一个名字 name:"名字"

import Home from '@/views/Home'
import Search from '@/views/Search'
import NotFound from '@/views/NotFound'
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化

// 创建了一个路由对象
const router = new VueRouter({
  // 注意:一旦采用了 history 模式,地址栏就没有 #,需要后台配置访问规则
  mode: 'history',
  routes: [
    { path: '/', redirect: '/home' },
    { path: '/home', component: Home },
    { name: 'search', path: '/search/:words?', component: Search },
    { path: '*', component: NotFound }
  ]
})

export default router


this.$router.push({ name:'名字'})

// 2. 通过命名路由的方式跳转 (需要给路由起名字) 适合长路径
      //    this.$router.push({
      //        name: '路由名'
      //    })


3.1.path路径跳转查询参数传参(query传参)

(1.1)简写 this.$router.push('路由路径?参数名=参数值')

  // 1. 通过路径的方式跳转
      // (1) this.$router.push('路由路径') [简写]
      //     this.$router.push('路由路径?参数名=参数值')
      // this.$router.push('/search')
      // this.$router.push(`/search?key=${this.inpValue}`)
      // 


(1.2)完整写法 this.$router.push({path:'路由路径', query:{参数名:参数值,参数名:参数值}})

  // (2) this.$router.push({     [完整写法] 更适合传参
      //         path: '路由路径'
      //         query: {
      //            参数名: 参数值,
      //            参数名: 参数值
      //         }
      //     })
      // this.$router.push({
      //   path: '/search',
      //   query: {
      //     key: this.inpValue
      //   }
      // })
      // this.$router.push({
      //   path: `/search/${this.inpValue}`
      // })


​ (2)获取参数 $route.query.参数名

在created中,获取路由参数
    // this.$route.query.参数名 获取查询参数


3.2path动态路由跳转传参

(1)简写 this.$router.push('/路径名称/参数值')

this.$router.push(`/search/${this.inpValue}`)


(2)完整写法 this.$router.push({

path:'/路径名称/参数值'

})

 this.$router.push({
      //   path: `/search/${this.inpValue}`
      // })


(3)获取参数 $route.params.参数名

// this.$route.params.参数名 获取动态路由参数


4.1name命明查询参数传参(query传参)

(1)

      // 2. 通过命名路由的方式跳转 (需要给路由起名字) 适合长路径
      //    this.$router.push({
      //        name: '路由名'
      //        query: { 参数名: 参数值 },
      //    })


(2)获取参数 $route.query.参数名

 // this.$route.params.参数名 获取动态路由参数


4.2name命名路由传参(动态路由传参)

(1)

      // 2. 通过命名路由的方式跳转 (需要给路由起名字) 适合长路径
      //    this.$router.push({
      //        name: '路由名'
      //        params: { 参数名: 参数值 }
      //    })


(2)获取参数 $route.params.参数名

 // this.$route.params.参数名 获取动态路由参数


$router.back()返回

二级路由

 routes: [
    {
      path: '/', component: Layout,
      redirect: '/article',
      children: [{
        path: '/article', component: Article
      },
      {
        path: '/collect', component: Collect
      }, {
        path: '/like', component: Like
      }, {
        path: '/user', component: User
      }
      ]
    },
    {
      path: '/detail/:id', component: ArticleDetail
    }
  ]


组件名:如果没有配置name属性,才回去找文件名

export default {
  name: 'CollectPage'
}


<keep-alive> </keep-alive>保存缓存

keep-alive是vue内置组件,会缓存不活动的组件实例,而不是销毁他们

他是一个抽象组件:他本身不会渲染成一个DOM元素,也不会出现在父组件链中。

keep-alive的优点:把切换出去的组件保存在内存当中,防止重复渲染DOM

减少加载时间及性能消耗,提高用户体验。

1.include:组件名数组,只有匹配的才会缓存

2.exclude:组件名数组,任何匹配的组件不被缓存

3.max:最多可以缓存多少给组件实例

<template>
  <div class="h5-wrapper">
    <keep-alive :include="['LayoutPage']">
      <router-view></router-view>
    </keep-alive>
    
  </div>
</template>


一旦组件缓存了就不会触发 created ,mounted ,destroyed。但会提供2个函数activated (看到页面)组件激活时触发,deactivated组件失活时触发(离开页面)

自定义创建项目

vue create 项目名称

vuex概述

vuex是一个vue的状态管理工具,状态就是数据。

场景: 1.某个状态 在很多个组件来使用(个人信息)

​ 2.多个组件共同维护一份数据(购物车)

优势: 1.共同维护一份数据,数据集中化管理

​ 2.响应式变化

​ 3.操作简单(vuex提供了一些辅助函数)

如果创建项目的时候没有选择vuex,需要装包

yarn add vuex@3

1.在store文件夹下的index.js插件安装

// 插件安装
Vue.use(Vuex)


2.创建仓库

const store = new Vuex.Store({
  // 通过state提供数据(所有组件共享的数据)
  state: {
    title: '大标题',
    count: 100
  }
})


3.导出给main.js使用

// 导出给main.js使用
export default store


4.在main.js当中导入挂载

import Vue from 'vue'
import App from './App.vue'
import store from '@/store/index'
Vue.config.productionTip = false

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



使用数据

1.获取store

模板中 : {{ $store.state.xxx }}

组件逻辑中: this.$store.state.xxx

  created () {
    console.log(this.$router)// undefined 没有配置路由
    console.log(this.$store.state.count)
  },


js模块中: store.state.xxx

2.通过辅助函数(简化)

1.导入 import { mapState } from 'vuex'

​ 2.数组方式引入

  computed: {
    ...mapState(['count', 'title'])
  },


3.页面直接使用stata里面的数据

<template>
  <div id="app">
    <h1>
      根组件
      - {{title}}
      -{{count}}
      </h1>
    <input type="text">
    <Son1></Son1>
    <hr>
    <Son2></Son2>
  </div>
</template>


修改**数据**

strict:true 可以开启严格模式 (可检查是不是修改了长裤的数据,上线需关闭)

vuex同样遵循单项数据流,组件中不能直接修改仓库里面的数据

mutation

定义mutation对象,对象中放修改state的方法

在1.vuex实例对象里添加mutations方法

// 这里存放的是vuex 的相关代码
import Vue from 'vue'
import Vuex from 'vuex'

// 插件安装
Vue.use(Vuex)
// 创建仓库(空仓库)
const store = new Vuex.Store({
  // 严格模式
  strict: true,
  // 1.通过state提供数据(所有组件共享的数据)
  state: {
    title: '大标题',
    count: 100
  },
  // 2.通过mutation 可以提供修改数据的方法
  mutations: {
    // 所有mutation函数,第一个参数就是state
    addCount (state) {
      state.count += 1
    },
    changeTitle (state) {

    }
  }
})
console.log(store.state.count)
// 导出给main.js使用
export default store



2.组件调用mutation

this.$store.commit('mutation函数')


<template>
  <div class="box">
    <h2>Son1 子组件</h2>
    从vuex中获取的值: <label>{{ $store.state.count }}</label>
    <br />
    <button @click="handleAdd">值 + 1</button>
  </div>
</template>

<script>
export default {
  name: 'Son1Com',
  methods: {
    handleAdd () {
      // 错误代码(vue默认不会执行,检测需要成本)
      // this.$store.state.count++
      // 需要提交调用mutation
      this.$store.commit('addCount')
    }
  }
}
</script>

<style lang="css" scoped>
.box {
  border: 3px solid #ccc;
  width: 400px;
  padding: 10px;
  margin: 20px;
}
h2 {
  margin-top: 10px;
}
</style>



mutation(同步)传递参数(不支持传递多个参数,单可以传递对象,数组)

// 这里存放的是vuex 的相关代码
import Vue from 'vue'
import Vuex from 'vuex'

// 插件安装
Vue.use(Vuex)
// 创建仓库(空仓库)
const store = new Vuex.Store({
  // 严格模式
  strict: true,
  // 1.通过state提供数据(所有组件共享的数据)
  state: {
    title: '大标题',
    count: 100
  },
  // 2.通过mutation 可以提供修改数据的方法
  mutations: {
    // 所有mutation函数,第一个参数就是state
    addCount (state, n) {//n 叫提交载荷
      state.count += n
    },
    changeTitle (state, str) {
      state.title = str
    }
  }
})
console.log(store.state.count)
// 导出给main.js使用
export default store



辅助函数:mapMutation

导入 import { mapMutations} from 'vuex'

mapMutation和mapState很像,他是把位于mutation的方法提出来,映射到组件的methods当中去

1.在组件导入import { mapMutations} from 'vuex'

2.在methods当中做映射

  methods: {
    ...mapMutations(['delCount']),
    del (n) {
      this.$store.commit('delCount', n)
    }
  }


3.组件直接调用函数

    <button @click="delCount(1)">值 - 1</button>
    <button @click="delCount(5)">值 - 5</button>


actions(异步)处理异步操作

1.提供action方法

  // 3.actions 处理异步
  // 注意不能直接操作state ,操作state还是 commit mutation
  actions: {
    // context 上下文(这里没有分模块,可以当初state仓库)
    // context.commit('mutation',额外参数)
    changeCountAsync (context, num) {
      // setTimeout模拟异步,以后大部分场景发请求
      setTimeout(() => {
        context.commit('changeCount', num)
      }, 1000)
    }
  }


2.页面中dispatch调用

this.$store.dispatch('action函数名', 额外参数)

    handleChange () {
      // 调用action
      this.$store.dispatch('changeCountAsync', 666)
    }
    


辅助函数mapActions

作用:把位于action的方法提取出来

1.导入包import { mapActions } from 'vuex'

2.在methods当中做映射

...mapActions(['action函数名']),

<script>
import { mapMutations, mapActions } from 'vuex'
export default {

  name: 'Son2Com',
  methods: {
    ...mapActions(['changeCountAsync']),
    ...mapMutations(['delCount', 'changeTitle']),
    del (n) {
      this.$store.commit('delCount', n)
    }
  }
}
</script>


页面直接调用action函数

<template>
  <div class="box">
    <h2>Son2 子组件</h2>
    从vuex中获取的值:<label>{{ $store.state.count }}</label>
    <br />
    <button @click="delCount(1)">值 - 1</button>
    <button @click="delCount(5)">值 - 5</button>
    <button @click="del(10)">值 - 10</button>
    <button @click="changeCountAsync(999)">一秒后改成999</button>
    <button @click="changeTitle('崔陈志')">改标题</button>
  </div>
</template>

<script>
import { mapMutations, mapActions } from 'vuex'
export default {

  name: 'Son2Com',
  methods: {
    ...mapActions(['changeCountAsync']),
    ...mapMutations(['delCount', 'changeTitle']),
    del (n) {
      this.$store.commit('delCount', n)
    }
  }
}
</script>

<style lang="css" scoped>
.box {
  border: 3px solid #ccc;
  width: 400px;
  padding: 10px;
  margin: 20px;
}
h2 {
  margin-top: 10px;
}
</style>



getters

除了state之外,有时候我们还需要从state中派生处一些状态,这些状态是依赖state,此时我们就会用到getters

1.定义getters

  // 4.getters  类似于计算属性
  getters: {
    // 注意点
    // 1.形参第一参数就是 state
    // 2.必须有返回值,返回值就是getters的值
    filterList (state) {
      return state.list.filter(item => item > 5)
    }
  }


2.访问getters

2.1通过store访问getters

{{$store.getters.filterList}}

    <div>{{$store.getters.filterList}}</div>


2.2通过mapGetters访问

  computed: {
    ...mapGetters(['filterList']),
  },


直接使用 <div>{{filterList}}</div>

vuex的模块化

在store文件下创建modules,模块就写在modules下,例如user.js ,setting.js

1.创建user.js

// user 模块
const state = {
  userInfo: {
    name: 'zs',
    age: 18
  },
  score: 80
}
const mutation = {}
const actions = {}
const getters = {}

export default {
  state,
  mutation,
  actions,
  getters
}



setting.js

// user 模块
const state = {
  them: 'light', // 主题色
  desc: '测试模块demo'
}
const mutation = {}
const actions = {}
const getters = {}

export default {
  state,
  mutation,
  actions,
  getters
}



2.在根模块导入子模块

// 5.moduler 模块
  modules: {
    user,
    setting
  }


3.在组件当中使用子模块的数据

测试访问模块中的state -- 原生 --

$store.state.模块名.xxx 来访问

    <!-- 测试访问模块中的state -- 原生 -- -->
    <div>{{$store.state.user.userInfo.name}}</div>


利用辅助函数mapstate

1...mapState('模块名', ['属性名'])

  computed: {
    ...mapState('user', ['userInfo'])
  },


2.开启命名空间

在模块名的导出里面添加 namespaced: true,

export default {
  namespaced: true, //开启命名空间
  state,
  mutation,
  actions,
  getters
}



3.组件当中使用

<div>{{userInfo}}</div>


**访问模块当中的getters **

  • 原生访问

  • <div>{{$store.getters['user/UpperCaseName']}}</div>

  • 特殊字符不能直接对象.访问 的对象【'属性名'】

// user 模块
const state = {
  userInfo: {
    name: 'zs',
    age: 18
  },
  score: 80
}
const mutation = {}
const actions = {}
const getters = {
// 分模块后state指代子模块的state
  UpperCaseName (state) {
    return state.userInfo.name.toUpperCase()//toUpperCase() 转化为大写
  }

}

export default {
  namespaced: true,
  state,
  mutation,
  actions,
  getters
}



mapGetters辅助函数访问

...mapGetters('模块名', ['属性名'])

 ...mapGetters('user', ['UpperCaseName'])


直接使用

   <div>{{UpperCaseName}}</div>



调用模块当中的mutation

1.直接通过store调用$store.commit('模块名/xxx',额外参数)

2.通过mapMutations映射

2.1默认根级别的映射mapMutation(['xxx'])

2.2子模块的映射 maoMutation('模块名',['xxx']) 需要开启命名空间

调用模块当中的action

1.直接通过store调用 $store.dispath('模块名/xxx',额外参数)

2通过mapActions

2.1默认跟级别的映射 mapActions(['xxx'])

2.2子模块的映射mapActions('模块名',['xxx']) 需要开启命名空间

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值