vue2-语法笔记

370 阅读8分钟

vue.config.js配置

vue.config.js 文件 里可以设置 端口 和 是否执行后 直接打开网页

// vue脚手架项目 - 默认的配置文件名
// webpack+node环境 - 导出配置对象
module.exports = {
  devServer: {
    port: 8080,  // 端口号
    open: true,  // 浏览器自动打开
    // 配置反向代理   :解决 跨域问题
    proxy: {
     // 
     // 当请求地址中包含 /api 的时候 会触发 代理 机制 
     //  ps:一个项目可以同时 代理多个服务器  根据实际需求来。
    '/api': {
      target: 'http://localhost:3000/', // 要代理的服务器地址 
      changeOrigin: true, //是否跨越: 只有这个值为true的情况下 才表示开启跨域 才可以让本地服务器代理我们发出请求 
        // 重写路径   //  如果是不想要 /pi的时候才重写
        // 重新路由前:  localhost:8888/api/login  => www.baidu.com/api/login
        // 重新路由后:  localhost:8888/api/login => www.baidu.com/login
        pathRewrite: {        如果我不想要  /api 就把它替换掉 
            '^/api': '' 
        }  
      }
    }
  },
  lintOnSave: false  //关闭eslint检查   有咩有没用的变量
}

vue指令

v-bind 动态绑定 标签的 属性

<template>
  <div> 
   <a v-bind:href="url">百度</a>
   <img :src="scrurl" />  // 或者直接 : 就行
  </div>
</template>
<script>
export default {
  // 1. 变量在data函数 return 的对象上
 data() {
  return {
    //...其他     
    url: "http://www.baidu.com",  // http://是必须要写的
    scrurl:"图片路径"
    //...其他 
    };
  },
};
</script>

动态变量拼接 字符串

<img :href="'http://'+URL"></img>    //字符串用  '' 单引号  包裹

v-on 事件绑定

<template>
  <div>
    <p>您购买的商品数量:{{ num }}</p>
    <!-- v-on:事件名="少量的js代码" -->
    <button v-on:click="num+=1">+1</button>
      
    <!-- v-on:事件名="方法名" -->
    <button v-on:click="btn">+1</button>
      
    <!-- 简写 @事件名="方法名"" -->
    <button @click="fn(5)">+5</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      num: 1,
    };
  },
  //方法 都写在 methods 里面    methods跟data是平级的
  //this 指向 当前vue实例对象    最大的 {}    [export default {}这个大扩号]
  methods: {
    btn() {
      this.num++;
    },
    fn(num1) {
      this.num = this.num + num1;
    },
  },
};
</script>

v-on 事件对象

目标: vue事件处理函数中, 拿到事件对象

  • 语法:

    • 无传参, 通过形参直接接收
    • 传参, 通过 $event 指代事件对象传给事件处理函数
<template>
  <div>
    <!-- 不传值  直接写方法名就行 -->
    <a @click="one" href="http://www.baidu.com">百度</a>
    <hr />
         <!-- 传参, 通过$event指代事件对象传给事件处理函数 -->
    <a @click="two(10, $event)" href="http://www.taobao.com" target="blank">淘宝</a>
  </div>
</template><script>
export default {
  methods: {
    one(e) {
      // 阻止默认行为
      e.preventDefault();
      console.log(e);
      console.log(this);
    },
    two(num, e) {
      e.preventDefault();
    },
  },
};
</script>

v-on修饰符

目的: 在事件后面.修饰符名 - 给事件带来更强大的功能

  • 语法:

    • @事件名.修饰符="methods里函数"

      • .stop - 阻止事件冒泡
      • .prevent - 阻止默认行为
      • .once - 程序运行期间, 只触发一次事件处理函数
      <button @click.stop="btn">.stop阻止事件冒泡</button>
      <button @click.prevent="btn">.prevent阻止默认行为</button>
      <button @click.once="btn">.once 只触发一次 事件处理函数</button>
       //可以连写,及 阻止事件冒泡 又 阻止默认行为
      <button @click.stop.prevent="btn">可以连写,及 阻止事件冒泡 又 阻止默认行为</button>

<button @click.stop.prevent="btn"> 可以连写,及 阻止事件冒泡 又 阻止默认行为

v-on按键修饰符

目标: 给键盘事件, 添加修饰符, 增强能力

  • 语法:

    • @keyup.enter - 监测回车按键
    • @keyup.esc - 监测返回按键
    • @keyup.ASCII码 - 监测对应的按键
    <!--   @事件名.按键名="函数名"  -->
    <input @keyup.backspace="fn1" type="text" />
    <!--  @事件名.按键的ASCII码="函数名"-->
    <input @keyup.13="fn1" type="text" />

监听 ctrl+s同时按下

<input @keyup.ctrl.s="KeyUpfn" />

更多修饰符参考: cn.vuejs.org/v2/guide/ev…

v-model 表单 value值 的设置和修改

<template>
  <div>
      <!-- 文本框 和 密码框  -->
    <span>姓名: </span>
    <input type="text" v-model="uname" />
    <br>
    <!-- 下拉菜单 v-model 要绑定在select上 -->
    <!--  v-model的变量值 = 选中的 option的 value值 -->
    <span>来自: </span>
    <select v-model="form">
      <option value="长沙">长沙</option>
      <option value="深圳">深圳</option>
      <option value="平江">平江</option>
    </select>
    <br />
    <span>爱好:</span>
    <!-- 遇到复选框, v-model的变量值-->
    <!-- 变量值:不是一个数组 -关联的是 复选框 的 checked属性  true或者false   -->
    <!--  ps:选中一个  所有绑定同一个的  都会被选中;-->
    <!--  变量值:是一个数组 -关联的是 复选框的value属性 ps: 每选中一个,会依次push进数组  -->
    <label><input type="checkbox" v-model="aihao" value="吃饭" />吃饭</label>
    <label><input type="checkbox" v-model="aihao" value="睡觉" />睡觉</label>
    <label><input type="checkbox" v-model="aihao" value="敲代码" />敲代码</label>
    <br />
    <!-- 单选按钮: v-model-->
    <input type="radio" value="女" v-model="gender" name="sxe" />女
    <input type="radio" value="男" v-model="gender" name="sxe" />男
    <br>
    <span>个人爱好: </span><br>
    <textarea v-model="wenben" ></textarea>
  </div>
</template>
<script>
export default {
  data() {
    return {
      uname: "shanpeng",
      form: "长沙",
      aihao: [],
      gender: "",
      wenben:''
    };
  },
};
</script>

v-model修饰符

<!-- v-model.trim 修饰符: 去除首尾空白字符 -->
用户名: <input type="text" v-model.trim="str" /> <br />
    
<!-- v-model.number 修饰符: 以paseFloat转成数字类型   ps:如果输入的不是数字就还是字符串-->
密码: <input type="password" v-model.number="num" /> <br />
    
<!-- v-model.lazy  修饰符: 在change时触发而非input时 -->
<!-- ps:就是等失去焦点时再提交,而不是每次输入时就提交 -->
个人信息: <textarea v-model.lazy="xinxi"></textarea>

v-html 和 v-text

<template>
  <div>
    <!--  v-html和  v-text 都会覆盖 标签里面原本的内容 -->
    <!--  v-html: 能识别 html标签-->
    <p v-html="str">1</p>
    <!--  v-text: 不能识别html标签-->
    <p v-text="str">2</p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      str: "<span>我叫单鹏</span>",
    };
  },
};
</script>

v-show 和 v-if

<!-- v-show或v-if,给变量赋值 true/false, 显示/隐藏 -->
<!-- v-show隐藏:采用 dispaly:none // 切换 -->
<p v-show="biu1">Hello Vue</p>
<!-- v-if 隐藏: 采用从DOM树直接移除 // 移除 -->
<p v-if="biu2">Hellow ShanPeng</p>

v-if 可以配合 v-if else 或者 v-else

判断条件 可以不一样 相互不影响

    <div v-if="flag">1</div>
    <div v-else-if="num > 3">2</div>
    <div v-else>3</div>
<script>
export default {
  data() {
    return {
      flag: false,
      num: 5,
    };
  },
};
</script>

v-for 遍历

<template>
  <div>
    <tr>
      <!-- v-for 把一组数据, 渲染成一组DOM -->
      <!-- 口诀: 让谁循环生成, v-for就写谁身上 -->
      <!-- 遍历数组 v-for="值 in 数组名" -->
      <!-- item:数组的每一项   index:每一项的索引 -->
     <td v-for="(item, index) in arr" :key="index">{{ value---index }}</td>
    </tr>
      
    <!-- v-for 遍历数组中的每个对象 -->
    <tr v-for="value in stuArr" :key="value.id">
      <td>{{ value.id }}</td>
      <td>{{ value.name }}</td>
      <td>{{ value.sex }}</td>
      <td>{{ value.hobby }}</td>
    </tr>
    <ul>
      <!-- 变量一个对象 v-for="(value, key) in 对象名" -->
      <li v-for="(value, keys) in tObj" :key="value">
        {{ keys }}-----{{ value }}
      </li>
    </ul>
      
    <!-- v-for遍历整数(了解) - 从1开始一直到变量的值  v-for="num in 变量名" -->
    <p>序号</p>
    <div v-for="num in count" :key="num">{{ num }}</div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      arr: ["小明", "小欢欢", "大黄"],
      stuArr: [
        {
          id: 1001,
          name: "孙悟空",
          sex: "男",
          hobby: "吃桃子",
        },
        {
          id: 1002,
          name: "猪八戒",
          sex: "男",
          hobby: "背媳妇",
        },
      ],
      tObj: {
        name: "小黑",
        age: 18,
        class: "1期",
      },
      count: 5,
    };
  },
};
</script>
<style>
th,
td {
  padding: 0 10px;
}
</style>

v-for 更新监测

情况1: 数组翻转 reverse 可以改变数组的方法 v-for可以监测到

情况2: 数组截取 slice 不能改变数组的 v-for监测不到

情况3: 更新值 直接重新赋值 v-for可以监测到

数组非变更方法, 返回新数组, 就不会导致v-for更新, 可采用覆盖数组或this.$set()

<template>
  <div>
    <!-- v-for 更新监听 -->
    <ul>
      <li v-for="value in arr" :key="value">{{ value }}</li>
    </ul>
    <button @click="re">反转数组</button>
    <button @click="del2">删除数组前两个</button>
    <button @click="gaibian">改变第一个值</button>
  </div>
</template><script>
export default {
  data() {
    return {
      arr: [1, 2, 3, 4, 5, 6, 7],
    };
  },
  methods: {
    re() {
      // 能改变 数组本身 的方法   可以让 v-for 更新
      this.arr.reverse();
    },
    del2() {
      // 2. 数组 slice方法 不会造成 v-for 更新
      //  this.arr.slice(0, 3)
      // 不会改变数组本身的方法 都不会造成 v-for 更新
      // 解决v-for 更新-  直接覆盖 原始数组
     this.arr = this.arr.slice(0, 3);
    },
    gaibian() {
      // 3. 更新某个值的时候, v-for是监测不到的
      // this.arr[0]=1000;   页面上是不会改变的
      // 解决 this.$set()
      // 参数:  1.更新的目标结构(数组) 2.更新位置 3.更新值
      this.$set(this.arr, 0, 1000);  // 相当于 原生js的  arr[0]=1000
    },
  },
};
</script><style>
</style>

动态设置 class 属性

<template>
  <div>
    <!-- 给标签 class属性动态赋值  -->
    <!-- :class="{类名:布尔值}" -->  也可以通过判断
    <div :class="{bianse:bool}" @click="bian">我可以变颜色</div>
  </div>
</template>
<script>
export default {
  data() {
    return{
      bool:true
    }
  },
  methods: {
    bian(){
      this.bool=!this.bool
    }
  }
};
</script>
<style>
  .bianse{
    color: red;
  }
</style>

动态 style 属性

<template>
  <div>
    <!-- 动态style属性 -->
    <!-- :style="{css属性:属性值}" -->
    <div :style="{backgroundColor:color}">biubiu</div>
  </div>
</template>
<script>
export default {
 data () {
   return {
     color:'blue'
   }
 }
}
</script>
<style>
</style>

自定义指令

全局定义

语法

在 main.js 入口 文件里 引入 Vue 之后

Vue.directive("指令名",{
  // 初始化时执行
    inserted(el,binding){
       //el: 当前使用 该指令的 标签
      //指令功能代码.....
    },
    // update方法-指令对象数据/标签更新时,此方法执行
   update(el, binding) { // binding是自己任意取的名字
     // binding.value  :是   v-指令="的值"
    el.style.color = binding.value
  },
    // 该钩子函数会在当前指令作用的组件 更新数据完毕之后 执行
    componentUpdated(el, binding){
      el.style.color = binding.value
    }
})

实例

// 创建一个全家"自定义指令",让输入框自定聚焦
Vue.directive("focus-g", {
  inserted(el) {
    el.focus()
  }
})
// 自定义修改字体颜色的  指令
Vue.directive('strColor', {
  // inser方法 - 指令所在标签,被插入到网页上触发(一次)
  inserted(el, binding) {
    el.style.color = binding.value
  },
  // update方法-指令对象数据/标签更新时,此方法执行
  update(el, binding) {
    el.style.color = binding.value
  }
})

使用自定义的 指令

语法 <h1 v-指令名="值">内容</h1>

      <h1 v-colorG="'red'">我是谁?</h1>    'red': 固定的值
      <h1 v-colorG="color">我是谁?</h1>    color:变量
       <input type="text" v-focus />   //页面打开自定聚焦

局部

export default {
  //  局部 自定义 指令
  directives: {
    colorG: {
      inserted(el, binding) {
        el.style.color = binding.value;
      },
      // update方法-指令对象数据/标签更新时,此方法执行
      update(el, binding) {
        console.log(binding);
        el.style.color = binding.value;
      },
    },
    focus: {
      inserted(el) { 
        el.focus();
      },
    },
  },
};

点击按钮 让颜色随机变化

 methods: {
    changColor() {
      this.color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(
        Math.random() * 255
      )},${Math.floor(Math.random() * 255)})`;
    },
  },

vue 过滤器 filter

定义全局过滤器

main.js文件 里定义

// Vue.filter('过滤器名字',函数体)   itme:就是需要过滤的值
Vue.filter('fanzhuan', itme=> itme.split('').reverse().join())

定义局部过滤器

在当前 vue文件中 定义filters

filters: {
1.过滤器名字: () => {return "返回处理后的值"},
2.过滤器名字(过滤的值){ return "返回处理后的值"}            //val  就是过滤的值
}
export default {
  // ........
  // filters:{'过滤器名字':函数体}
  filters: {
    // 保留 小数点后两位             itme和val都指  过滤的值
    biu: (item) => itme.toFixed(2),
    toLower(val) {
      //  小写字母 转为 大写
      return val.toUpperCase();
    },
},
 // ........
};

使用过滤器

可同时使用多个过滤器, 或者给过滤器传参

// 插值表达式
<p>{{ msg | fanzhuan }}</p>
// v-bind表达式
<p :title="msg | bianda">提示信息</p>
<template>
  <div>
    {{ msg1 | biu | cheng }}
    {{ msg2 | str("-") }}
  </div>
</template>
<script>
export default {
  data() {
    return {
      msg1: 1.23456789,
      msg2: [1, 2, 3, 4, 5],
    };
  },
  filters: {
    // 保留 小数点后两位
    cheng: (itme) => itme * 2,
    biu: (itme) => itme.toFixed(2),
    str(val, i) {
      //  数组转 字符串
      return val.join(i);
    },
  },
};
</script>

vue计算属性  computed

一个数据, 依赖另外一些数据计算而来的结果

export default {
  computed: {
     "计算属性名" () {
        return "值"
     }
  }
}
<template>
  <div>
    <p>{{ num }}</p>
  </div>
</template>
<script>
export default {
  data(){
    return {
      a: 10,
      b: 20
    }
  },
  computed: {
    num(){
      return this.a + this.b
    }
  }
}
</script>

注意: 计算属性也是vue数据变量, 所以不要和data里重名, 用法和data相同

vue计算属性-完整写法

get: 当计算属性 所依赖的 变量发生变化时, 计算属性也跟着变化

set: 当修改计算属性时, 可以改变其他值

computed: {
    "属性名": {
        get() {
            return "值"
        } ,       
        set(){  
            
        }
    }
}

vue侦听器 watch

可以侦听data/computed属性值改变

watch: {
      // newVal 最新的内容  oldVal  上一次的内容(旧的)
    "被侦听的属性名(变量名)" (newVal, oldVal){
    }
}
<template>
  <div>
    <input type="text" v-model="name" />
  </div>
</template>
<script>
export default {
  data() {
    return {
      name: "",
    };
  },
  watch: {
    // newVal 最新的内容  oldVal  上一次的内容(旧的)
    name(newVal, oldVal) {
      console.log(newVal, oldVal);
    },
  },
};
</script>

侦听器的 深度侦听 和 立即执行

深度侦听: deep: true 立即执行: immediate: true 就是打开页面一上来就执行一次

watch: {
    "要侦听的属性名": {
        immediate: true, // 立即执行
        deep: true, // 深度侦听复杂类型内变化
        handler (newVal, oldVal) { 
        }
    }
}
<template>
  <div>
    <input type="text" v-model="user.name">
    <input type="text" v-model="user.age">
  </div>
</template>
<script>
export default {
  data(){
    return {
      user: {
        name: "",
        age: 0
      }
    }
  },
  // 目标: 侦听对象
  /*
  语法:
    watch: {
      变量名 (newVal, oldVal){
        // 变量名对应值改变这里自动触发
      },
      变量名: {
        handler(newVal, oldVal){
        },
        deep: true, // 深度侦听(对象里面层的值改变)
        immediate: true // 立即侦听(网页打开handler执行一次)
      }
    }
  */
  watch: {
    user: {
        // handler 里有 最新的值,和旧的值
      handler(newVal, oldVal){
        // user里的对象
        console.log(newVal, oldVal);
      },
      deep: true,  //  深度监听  
      immediate: true, // 该回调将会在侦听开始之后被立即调用 
    }
  }
}
</script>

vue组件

全局 - 注册使用

在入口文件:一般是 main.js里, 在new Vue之上注册

import Vue from 'vue'
// 引入 组件
import 组件对象 from 'vue文件路径'
Vue.component("组件标签名", 组件对象)
// 目标: 全局注册 (一处定义到处使用)
// 1. 创建组件 - 文件名.vue
// 2. 引入组件
import Pannel from './components/Pannel'
// 3. 全局 - 注册组件
/*
  语法: 
  Vue.component("组件标签名", 组件对象)
*/
Vue.component("PannelG", Pannel)

在任意Vue文件中 template 标签 里用

<PannelG></PannelG>   //双标签 都可以
​
<PannelG/>  //单标签  都可以
​
<pannel-g></pannel-g>全部小写  -   //都可以
<组件标签名></组件标签名>

在 components文件中统一注册全局组件

提供注册入口src/component/index.js

// 该 文件 负责所有的公共的组件的全局注册
import 组件名 from './组件路径'
import xxx from './xxx'
/*
* Vue.use(对象) 如果是个 对象的话 ,会调用 对象里面的 install方法  参数是 Vue的实例
* 
* 这里为什么vue会在调用install函数时,把Vue构造函数作为实参传给我们?
* 就是为了保证 你用的 Vue构造函数 ,和我的Vue构造函数 是 同一个
* 就是保证  Vue构造函数  一致
*
*   还有一个好处是 :  
    我想开放一个  基于 vue 的 第三方包插件,并且发布到npm上
    vue插件的作者不需要安装vue依赖包,减少插件的代码体积
*/
export default{
    install(Vue){
        // 自己的代码.......
        // 还可以用来做很多事,不只是 全局注册组件
        /*
        * 这里是用来注册全局组件
        */
        Vue.component('组件名',组件名)
        Vue.component('xxx',xxx)
    }
}

在main.js里面注册

import Component from '@/components'Vue.use(Component)

Vue.use() 方法

Vue.use() 方法内部会判断 传入的实参 是一个 函数 还是 一个 对象

如果是函数,则会立即调用,并且传入Vue构造函数实参

如果是对象,则会立即调用对象.install(Vue)

局部 -注册使用

在 使用这个组件的 vue文件中 引入

<script>
// 引用组件
import 组件对象 from 'vue文件路径'
export default {
      // 注册组件
    components: {
      "组件标签名": 组件对象
    }
}
</script>

vue组件-里 style标签的 scoped作用

解决多个组件样式名相同, 冲突问题

如果不使用 scoped,那么当前组件css样式很有可能影响其他组件

<style scoped>
</style>

组件name属性使用

可以用组件的name属性值, 来注册组件名字

<template>
  <div>
      <p>我是一个Com组件</p>
  </div>
</template><script>
export default {
    name: "ComNameHaHa" // 注册时可以定义自己的名字
}
</script>

组件通行 父向子-props

父组件

<template>
  <div>
  <product title="超好吃的烧鹅饭" price="16" intro="每次都送一瓶饮料" ></Product>
  </div>
</template>
<script>
    //引用 组件
import product from "./components/MyProduct.vue";
export default {
  // 注册组件
  components: {
    product,
  },
  data() {
    return {
      str: "开业大酬宾, 全场5折",
    };
  },
};
</script>

子组件

<template>
  <div class="my-product">
    // 使用props定义的变量
    <h3>标题:{{ title }}</h3>
    <p>价格: {{ price }}元</p>
    <p>{{ intro }}</p>
  </div>
</template>
<script>
export default {
// 在 props:[] 里  定义 变量  //接收 父组件 传过来的值
  props: ["title", "price", "intro"],
  // .....
};
</script>
<style>

父向子- props 配合 循环

<template>
  <div>
    <product v-for="obj in list" :key="obj.id"
      :title="obj.proname"
      :price="obj.proprice"
      :intro="obj.info"></product>
  </div>
</template>
<script>
// 目标: 循环使用组件-分别传入数据
// 1. 创建组件
// 2. 引入组件
import product from "./components/MyProduct.vue";
export default {
// 3. 注册组件
      components: {
    product,
  },
  data() {
    return {
      list: [
    { id: 1, proname: "超级好吃的棒棒糖", proprice: 18.8, info: '开业大酬宾, 全场8折' },
    { id: 2, proname: "超级好吃的大鸡腿", proprice: 34.2, info: '好吃不腻, 快来买啊' },
    { id: 3, proname: "超级无敌的冰激凌", proprice: 14.2, info: '炎热的夏天, 来个冰激凌了' },
],
    };
  },
​
};
</script><style>
</style>

props完整版

  props: {
    item: {
      type: Array, // 传过来的必须是数组
      // type:[String,number]  传过来的必须是 字符串 或者 数字
      default: "内容", //如果没有传值过来 默认显示的值
      // default:()=>{ 如果是数组或者对象,默认的值 必须是函数return的值
      //   return []
      // },
      required: true, // 是必须传的
      validator(value) { // 自定义一个验证规则
        if (value.length >= 2 && value.length <= 5) {
          return true;
        } else {
          console.log("数据源必须在2-5项");
          return false;
        }
      },
    },
  },

组件通信 子向父-$emit

父组件 监听自定义事件

<template>
  <div>
<Child @modification="fn">   //  自定义事件名  必须写在  子组件标签上
{{zuoping}}   // 以 插槽的形式  使用传过来的  参数   
</Child>
  </div>
</template>
<script>
import Child from "子组件路径";
export default {
  components: {
    Child,   // 注册 组件
  },
  data() {
    return {
      zuoping: "", //声明接收变量
    };
  },
  methods: {
    fn(value) {   //vlaue:  就是子组件 传过来的参数
      this.zuoping = value;  // 变量= 传过来的参数
    },
  },
  created() {
    this.fn();  //在 初始化的时候 调用  函数
  },
};
</script>
<style lang="less" scoped>
</style>

子组件

<template>
  <div>
    <slot></slot> // 插槽标签
  </div>
</template>
<script>
export default {
  data() {
    return {
      poem: "天生我材必有用,千金散尽还复来。", // 定义传过去的 变量
    };
  },
  created() {   // 在初始化的时候 传过来
      // 语法  this.$emit("父组件自定义事件名",传过去的变量,.....)
     this.$emit("modification", this.poem);
  },
};
</script>
<style lang="less" scoped>
</style>

如果只传一个值

this.$emit("soom", this.num);

如果只有一个值得话  $event  等于 传过来的值
<dupu @soom="sum += $event" />

sync 修饰符 !!!!

多用于 第三方弹框 如果传一个值 子组件又要改这个值的时候

父组件

<comp :foo.sync="bar"></comp>    

会被扩展为:

<comp :foo="bar" @update:foo="val => bar = val"></comp>

子组件

// this.$emit('update:变量名', 新值)
this.$emit('update:foo', newValue)

组件通信_跨组件传值-EventBus(兄弟组件传值)

新建 EventBus/index.js  (文件夹名和文件名都是随意定义的)- 定义事件总线bus对象(中间商)

import Vue from 'vue'
// 导出空白vue对象
export default new Vue()

传值方

1.引用 中间商 EventBus/index.js 导出的 new Vue 实例

<template>
  <button @click="subFn">加1</button>
</template>
<script>
import eventBus from "./eventBus"; 
// 因为文件名是index.js 所以不用写全 它也会自动找到 
// 如果是其他文件名   就要写全
export default{
  data(){
      return{
          num:1
      }
  },
  methods:{
     subFn() {
      this.num++;
      eventBus.$emit("send", this.num); // 跨组件
    },
  }
}
</script>

接收方

<template>
  <div>
   {{shu}}    
  </div>
</template>
<script>
import eventBus from "./eventBus"; 
// 因为文件名是index.js 所以不用写全 它也会自动找到 
// 如果是其他文件名   就要写全
export default{
  data(){
      return{
          shu:1
      }
  },
  created(){  // 在 created钩子函数中  调用 $on方法
      eventBus.$on("send", (index) => {
       this.shu += index;
    }); 
  },
  destroyed(){  // 在 destroyed钩子函数中 移除 事件
    // 移除所有事件
    eventBus.$off() 
    // 移除这个事件
     eventBus.$off('send')  //事件名
  }  
}
</script>

组件进阶 - 动态组件

  1. 准备被切换的 - UserName.vue / UserInfo.vue 2个组件
  2. 引入到UseDynamic.vue注册
  3. 准备变量来承载要显示的"组件名"
  4. 设置挂载点 标签, 使用is属性来设置要显示哪个组件
  5. 点击按钮 – 修改comName变量里的"组件名"
<template>
  <div>
    <span>
     // 5. 通过点击按钮 修改 ComName的值 来让哪个组件显示
      <button @click="ComName='uname'">账号密码填写</button>
      <button @click="ComName='uinfo'">个人信息填写</button>
    </span>
    <p>下面显示注册组件:</p>
    //4. 挂载点<component>标签   is属性来设置要显示哪个组件
    //  ps:is属性等于哪个组件名就显示哪个组件 
    <component :is="ComName"></component>
  </div>
</template>
<script>
 // 1引入两个组件
import uname from "./UserName.vue";
import uinfo from "./UseInfo.vue";
export default {
  data () {
    return {
    // 3 定义一个变量.默认先等于 uname 组件名
    ComName:'uname'
    }
  },
    //2 注册两个组件
  components: {
    uname,
    uinfo,
  },
}
</script>

组件进阶 - 组件缓存

组件切换会导致组件被频繁销毁和重新创建, 性能不高

使用Vue内置的keep-alive组件, 可以让包裹的组件保存在内存中不被销毁

演示1: 可以先给UserName.vue和UserInfo.vue 注册created和destroyed生命周期事件, 观察创建和销毁过程

演示2: 使用keep-alive内置的vue组件, 让动态组件缓存而不是销毁

语法:

包裹需要缓存的组件标签

Vue内置的keep-alive组件 包起来要频繁切换的组件

02_UseDynamic.vue

    <div style="border: 1px solid red">
      <!-- Vue内置keep-alive组件, 把包起来的组件缓存起来 -->
      <keep-alive>
        <component :is="ComName"></component>
      </keep-alive>
    </div>

属性

<keep-alive max="5">
max属性: 一共只缓存 5个组件, 如果超过了那么就把 最久没有被访问的组件清除缓存
</keep-alive>
​
<keep-alive :include="componentArr">
include: 只缓存指定的组件,不缓存其他的
</keep-alive>
​
<keep-alive :exclude="componentArr">
exclude: 跟includ相反,  不缓存指定的组件,缓存其他的
</keep-alive>

补充生命周期:

  • activated - 激活
  • deactivated - 失去激活状态

总结: keep-alive可以提高组件的性能, 内部包裹的标签不会被销毁和重新创建, 触发激活和非激活的生命周期方法

组件进阶 - 组件插槽 slot

默认插槽

子组件
<template>
  <div id="container">
    <slot>默认显示内容,如果父组件没有插入内容,默认就显示这段话</slot>
  </div>
</template>
父组件
<template>
  <div id="container">
    <div id="app">
      <h3>案例:折叠面板</h3>
        
      <Pannel>在这里写入要显示的代码 
          子组件就会把  <slot></slot>站位标签  替换成 这些代码
      </Pannel>
 
      <Pannel>
        <p>寒雨连江夜入吴,</p>
        <p>平明送客楚山孤。</p>
        <p>洛阳亲友如相问,</p>
        <p>一片冰心在玉壶。</p>
      </Pannel>
        
      <Pannel>
      <img src="./鸣宝1.jpg" alt="">
      <span>大家好,我叫旋涡鸣人</span>
      </Pannel>
    </div>
  </div>
</template><script>
// 1. 引入 子组件
import Pannel from "./chacao/zhedie.vue";
export default {
 // 2. 注册子组件
  components: {
    Pannel,
  },
};
</script>

组件进阶 - 具名插槽

当一个组件内有2处以上需要外部传入标签的地方

传入的标签可以分别派发给不同的slot位置

给 slot 标签添加 name属性 取名

<slot name="title"></slot>
<slot name="content"></slot>

父组件这边使用 template 和 v-solt 属性 传递代码

<Pannel>
  <!-- <template v-slot:子组件slot标签的name名> -->
  <template v-slot:title>
    <h4>芙蓉楼送辛渐</h4>
  </template>
  <!--   v-slot:  可以缩写 # -->
  <template #content>
    <p>寒雨连江夜入吴,</p>
    <p>平明送客楚山孤。</p>
    <p>洛阳亲友如相问,</p>
    <p>一片冰心在玉壶。</p>
  </template>
</Pannel>
 

组件进阶 - 作用域插槽

子组件

<!-- 在 slot标签上  自定义属性名 -->
<!-- :自定义属性名="要传过去的变量" -->
<slot :row="flag">   //也可以传固定的值,  就不用  在前面加 :
  {{ flag}}
</slot>

父组件

 <!-- 使用组件,template配合v-slot="自定义变量名"  -->
 <!-- 自定义变量名是个对象  它接收了 子组件传过来的变量 -->
 <template v-slot="flag1">
   <!-- 使用方法   自定义变量名.子组件自定义属性名-->
   <!-- 如果传过来的是 对象 可以   自定义变量名.子组件自定义属性名.属性   -->
   {{ flag1.row }}
 </template>

采用 解构 对象 简写

        // 直接接受
        <template v-slot="{ flag }">
          <img src="./柯南惊讶.jpg" alt="" />
          <span> {{ flag.name1 }}</span>
        </template>

组件进阶-具名插槽和作用插槽

同时使用 采用连写的方式

<!-- v-solt:具名="作用域自定义名" -->
<template v-slot:content="ge">
  <img src="./柯南惊讶.jpg" alt="" />
  <span> {{ ge.flag.name1 }}</span>
</template>

vue 生命周期

从Vue实例, 创建到销毁的过程 官网文档

阶段方法名方法名
初始化beforeCreatecreated
挂载beforeMountmounted
更新beforeUpdateupdated
销毁beforeDestroydestroyed
<template>
  <div class="box"></div>
</template>
<script>
  export default{
    data(){
      return{}
    },
    methods:{},
    created(){}, // 初始化之后
    mounted(){},  // 挂载之后
    updated(){}, // 更新之后
    destroyed(){},// 销毁之后
  }
</script>
<style scrped>
</style>

refsrefs和nextTick知识

$refs-获取DOM 或者 组件实例

ref 和 this.$refs 可以用于获取 dom 元素

<template>
  <div>
      <p>1. 获取原生DOM元素</p>
     <!--  ref="名"  -->
      <h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1>
      <p>2. 获取组件实例</p>
      <hello-world ref="HelloWorld"></hello-world>
  </div>
</template>
<script>
 import HelloWorld from './HelloWorld'
export default {
    components:{
        HelloWorld
    },
    mounted(){ // 渲染后
        // this.$refs.ref名
        console.log(this.$refs.myH); // h1
        // 通过 $refs获取组件,  可以调用组件里面的方法      获取组件里面的属性
        console.log(this.$refs.HelloWorld) // HelloWorld组件实例
    }
}
</script>
<style>
</style>

$nextTick使用

语法: this.$nextTick(回调函数) //回调函数 一般使用 箭头函数

  methods: {
    fn() {
      this.num++; // 1
      // 解决: this.$nextTick()    
      // 过程: DOM更新完会挨个触发$nextTick里的函数体
      this.$nextTick(() => console.log(this.$refs.a.innerHTML)); // 1
    },
  },

总结: 因为DOM更新(视图渲染)是异步的

如果nexkTick拿不到 可以试试 定时器

setTimeout(function(){},0)   //0 秒就行

router

vue-router使用

学会vue官方提供的vue-router路由系统功能模块使用

vue-router文档

  • 安装
yarn add vue-router
  • main.js里 导入路由
import VueRouter from 'vue-router'
  • main.js里 使用路由插件
// 在vue中,使用使用vue的插件,都需要调用Vue.use()
Vue.use(VueRouter)
  • main.js里 创建路由规则数组
const routes = [
  {
    path: "/find",
    name:'find'
    component: Find
  },
  {
    path: "/my",
    name:'my',
    component: My
  },
  {
    path: "/part",
    path: "part",
    component: () => import('@/views/part'),
  }
]
  • main.js里 创建路由对象 - 传入规则
const router = new VueRouter({
  routes
})
  • main.js里 关联到vue实例
new Vue({
  router
})
  • 组件.vue
<router-view></router-view>

总结: 下载路由模块, 编写对应规则注入到vue实例上, 使用router-view挂载点显示切换的路由

总结2: 一切都围绕着hash值变化为准

vue路由 - 声明式导航

可用全局组件router-link来替代a标签

  1. vue-router提供了一个全局组件 router-link
  2. router-link实质上最终会渲染成a链接 to属性等价于提供 href属性(to无需#)
  3. router-link提供了声明式导航高亮的功能(自带类名)
      <!--  <a href="#/find">发现音乐</a> href=对应 路由路径  a标签  路由路径前要加 #
      <a href="#/my">我的音乐</a>
      <a href="#/part">朋友</a> -->
      <!-- 声明式导航 - 基本使用 -->
      <!-- 本质: vue-router提供的全局组件 "router-link" 替代a标签 -->
      <!-- "router-link" 替代a标签 -->
      <!-- to属性  替代 href 属性   路由路径前不用加#了 -->
      <router-link to="/find">发现音乐</router-link>  <!-- to =对应路由路径    不用加# -->
      <router-link to="/my">我的音乐</router-link>
      <router-link to="/part">朋友</router-link>

点击之后 会自动添加 类名 class: .footer_wrap .router-link-active

通过这个类名 添加高亮样式

<style scoped>
/* 省略了 其他样式 */
.footer_wrap .router-link-active{
  color: white;
  background: black;
}
</style>

router-link自带的2个类名的区别是什么

观察路由嵌套导航的样式

  • router-link-exact-active 类名 (精确匹配) url中hash值路径, 与href属性值完全相同, 设置此类名
  • router-link-active 类名 (模糊匹配) url中hash值, 包含href属性值这个路径

image-20210512102829250.png

声明式导航 - 跳转传参

查询字符串

/path?参数名=值

传参
<template>
 <button @click="$router.push('/home?name=shanpeng&age=18')">跳转路由</button>
</template>

或者

this.$router.push({
     name: "home",
     query:{
       name:'shanpeng',
       age:18
     }
})
接收
<script>
 export default{
     created(){
         console.log(this.$route.query)       
         console.log(this.$route.query.name)
         console.log(this.$route.query.age)
     }
 }
</script>

动态路由传参

    {
      path: 'departments/:name/:id',//y 这里当二级路由的path什么都不写的时候 表示该路由为当前二级路由的默认路由
      component: () => import('@/views/departments'),
      parps:true,// 开启 parps后 可以在路由组件中 通过 props接收  动态路由参数
    }
传参
<router-link to="/part/ShanPeng/001">朋友</router-link>

或者

this.$router.push('/part/ShanPeng/001')
或者
this.$router.push({
    name:'part',
    params:{
        name:'ShanPeng',
        id:"001"
    }
})
接收
console.log(this.$route.params)
console.log(this.$route.params.name)
console.log(this.$route.params.id)

重定向和模式

匹配path后, 强制切换到目标path上

  • 网页打开url默认hash值是/路径
  • redirect是设置要重定向到哪个路由路径

网页默认打开, 匹配路由"/", 强制切换到"/find"上

const routes = [
  {
    path: "/", // 默认hash值路径
    redirect: "/find" // 重定向到/find
    // 浏览器url中#后的路径被改变成/find-重新匹配数组规则
  }
]

总结: 强制重定向后, 还会重新来数组里匹配一次规则

路由 - 404页面

默认给一个404页面

语法: 路由最后, path匹配*(任意路径) – 前面不匹配就命中最后这个, 显示对应组件页面

  1. 创建NotFound页面

    <template>
      <img src="../assets/404.png" alt="">
    </template><script>
    export default {
    ​
    }
    </script><style scoped>
        img{
            width: 100%;
        }
    </style>
    
  2. 在main.js - 修改路由配置

    import NotFound from '@/views/NotFound'const routes = [
      // ...省略了其他配置
      // 404在最后(规则是从前往后逐个比较path)
      {
        path: "/404",
        component: NotFound
      },
      {
        path: "*", // 默认hash值路径
        redirect: "/404" // 重定向到/404
        // 浏览器url中#后的路径被改变成/404-重新匹配数组规则
      }
    ]
    

总结: 如果路由未命中任何规则, 给出一个兜底的404页面

路由跳转传参推荐

使用 路由对象名 name 传参

this.$router.push({
    name: "路由名",
    params: {
        "参数名": 值
    },
//  query: {
//      "参数名": 值
//  },
})
// 对应路由接收   this.$route.params.参数名   取值
// 对应路由接收   this.$route.query.参数名    取值

路由 - 模式设置

目标: 修改路由在地址栏的模式

hash路由例如: http://localhost:8080/#/home //默认是 哈西 模式

history路由例如: http://localhost:8080/home (以后上线需要服务器端支持, 否则找的是文件夹)

模式文档

router/index.js

const router = new VueRouter({
  routes,
  mode: "history" // 打包上线后需要后台支持, 默认模式是hash
})

路由 - 路由嵌套 子路由

{
    path:'/',
    component:login,
    children:[
        {
            path:'my',
            component:()=>import("@/对应路由的路径")
        },
        {
            path:'your',
            component:()=>import("@/对应路由的路径")
        },
        {
            path:'time',
            component:()=>import("@/对应路由的路径")
        },
    ]
}

全局 前置/后置 守卫

目标: 路由跳转之前, 先执行一次前置守卫函数, 判断是否可以正常跳转

在路由对象上使用固定方法beforeEach

在 项目的src文件下 新建 permission.js配置路由守卫

// 场景: 当你要对路由权限判断时
// 语法: router.beforeEach((to, from, next)=>{//路由跳转"之前"先执行这里, 决定是否跳转})
// 参数1:to 要跳转到的路由 (路由对象信息)    目标
// 参数2:from 从哪里跳转的路由 (路由对象信息)  来源
// 参数3:next 函数体 - next()才会让路由正常的跳转切换, next(false)在原地停留, next("强制修改到另一个路由路径上")
import router from '@/router'
import store from '@/store'
import NProgress from 'nprogress' // 引入一份进度条插件
import 'nprogress/nprogress.css' // 引入进度条样式
const whiteRouterList = ['/login', '/404']
​
/**
 *  路由前置守卫
 */
router.beforeEach((to, from, next) => {
  NProgress.start() // 开启进度条
  if (store.getters.token) {
    if (to.path === 'login') next('/')
    else next()
  } else {
    if (whiteRouterList.includes(to.path)) next()
    else next('/login')
  }
  NProgress.done() // 关闭进度条
})
/**
 *  路由后置守卫
 */
router.afterEach((to, from, next) => {
​
})

Router实例 api方法

router.app

 配置了 router 的 Vue 根实例

router.mode

路由使用的 模式

router.currentRoute

 当前路由对应的路由信息对象

router.replace()

跳转路由后不保留历史记录

this.$router.replace()

$route 路由信息对象

$route.path

当前路由页面的  绝对路径

$route.params

当前路由页面的 动态参数

$route.query

当前路由页面的 查询字符串参数

$rout.hash

当前路由的 hash 值 (带 #) ,如果没有 hash 值,则为空字符串

$route.fullPath

完成解析后的 URL,包含查询参数和 hash 的完整路径

$router.resolve 新窗口打开

新窗口打开

vuex 全局状态管理

第一步.下载

yarn add vuex 

第二步: 在main.js中 引入

import Vuex from 'vuex'

第三步:在main.js中 注册

Vue.use(Vuex)// 调用了 vuex中的 一个install方法

第四步: 实例化

const store = new Vuex.Store({...配置项})
const store =new Vuex.Store({
    state:{},
    actions:{},
    mutations:{},
    modules:{},
    getters:{}
})

第五步:在根实例配置 store 选项指向 store 实例对象

new Vue({    //挂载到 vue 实例上
  store,
  el: '#app'
})

vuex基础-state

state是放置所有公共状态的属性,如果你有一个公共状态数据 , 你只需要定义在 state对象中

定义 state

// 初始化vuex对象
const store = new Vuex.Store({
  state: {
    // 管理数据
    count: 0,
    UserInfo:{},
    toKen:'',
  }
})

使用

1.原始形式- 插值表达式

组件中可以使用 this.$store 获取到vuex中的store对象实例,可通过state属性获取count属性, 如下

<div> state的数据:{{ $store.state.count }}</div>

2.计算属性 - 将state属性定义在计算属性中

computed:{
    count()
        return this.$store.state.count 
    }
}
 <div> state的数据:{{ count }}</div>

vuex基础-mutations

state数据的修改只能通过mutations,并且mutations必须是同步更新,目的是形成数据快照

定义mutations 方法

mutations: {
    // 方法里参数 第一个参数是当前store的state属性
    // payload 载荷 运输参数 调用mutaiions的时候 可以传递参数 传递载荷
    addCount (state,payload) {
      state.count += payload
    }
  },

使用mutations 方法

<template>
  <div>
    <button @click="addCount">+1</button>
  </div>
</template>
<script>
export default {
  methods: {
    addCount() {
      // 调用mutation方法, 提交 mutation
      // this.$store.commit(mutation名字)   可以获取到 mutations里面的方法并执行
      // commit 的第二个参数 就是要传递的 载荷 payload 就是传入的参数
      this.$store.commit("addCount",1);
    },
  },
};
</script>

vuex基础-actions

state是存放数据的,mutations是同步更新数据,actions则负责进行异步操作

定义actions

 actions: {
  //  获取异步的数据 context表示当前的store的实例 可以通过 context.state 获取状态 也可以通过context.commit 来提交mutations, 也可以 context.diapatch调用其他的action
    getAsyncCount (context,payload) {
      setTimeout(function(){
        // 一秒钟之后 要给一个数 去修改state
        context.commit('addCount', payload)
      }, 1000)
    }
 } 

调用

 addAsyncCount () {
     this.$store.dispatch('getAsyncCount', 123)
 }

Vuex中的模块化-Modules

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

定义Modules的state状态

const store  = new Vuex.Store({
  modules: {
    user: {
        namespaced: true,  // 开启 命名空间
        state: {
          UserInfo: {},
          token: 1561984891651651651
         }
    },
  })

使用 moudles 里面的state

此时要获取子模块的状态 需要通过 $store.state.模块名称.属性名 来获取

<template>
  <div>
      <div>用户token {{ $store.state.user.token }}</div>
      <div>网站名称 {{ $store.state.setting.name }}</div>
  </div>
</template>

定义 modules里面的 mutations

const store  = new Vuex.Store({
  modules: {
    user: {
        state:{.....},
        mutations:{
            setToKen(state,toKen){
                state.token=toKen
            }
        }
    },
  })

定义 modules里面的 actions

import {async_getToKen} from '@/api/user'
const store  = new Vuex.Store({
  modules: {
    user: {
        state:{.....},
        mutations:{.....},
        actions:{
            async getToKen(context){
              const taken = await async_getToKen()
              context.commit('setToKen',taken)
            }
        }
    },
  })

调用 modules 里面的 actions

<template>
   <div>
     <button @click="login">登录</button>    
   </div>
</template>
<script>
export default{
    data(){
        return{}
    },
    methods{
       login(){
          this.$store.dispatch('user/getToKen')  
       }
    },
    created(){},
    mounted(){}
}
</script>
<style scoped lang="scss">
</style>