再探VUE(复习)| 青训营

82 阅读4分钟

复习笔记

时隔一年,再次学习VUE,发现了很多曾经没有留意到的用法,原来组件间通信除了常规的那几种,竟然还有好多我不知道的写法,原来v-model也能用来通信,真的收获了很多,正所谓温故而知新呐

1.项目用到的插件

  • 进度条 NProgress
  • 轮播图 Swiper
  • 二维码 qrcode
  • 懒加载 vue-lazyload
  • 防抖节流 lodash
  • 随机ID nanoid
  • 表单验证 vee-validate

2.自定义插件plugins

里面可以封装的内容

  • Vue.prototype.$bus 任何组件都可用
  • Vue.directive
  • Vue.component

MyPlugins.js

// VUE插件一定暴露一个对象
let myPlugins = {};
// 插件必须使用install进行定义
myPlugins.install=function(Vue,options){
第一个参数是Vue注入
第二个参数是传入的内容
举例:封装一个自定义指令(变大写)

    Vue.directive(options.name,(element,attrs)=>{
        element.innerHTML = attrs.value.toUpperCase()
    })
}
export default myPlugins;

main.js

import myPlugins from '@/plugins/myPlugins'
安装插件
vue.use(myPlugins,{
    name:'upper'
})

组件中使用

<template>
	<div>
		<h1 v-upper="msg"></h1>
	</div>
</template>
<script>
data(){
	return {
		msg:'abc'
		}
	}
</script>

3.路由懒加载

优化性能,按需引入,当使用到目标路由时,才加载对应的组件

routes.js

{
path:"/home",
component:()=>import('@/pages/home'),
meta:{},
children:{}
}

4.项目打包

npm run build

  • 打包后的js文件会被压缩加密,因此看不懂哪里报错

  • 在打包后的disk中的js文件中,会发现多了一些map后缀的文件

  • map后缀文件的作用就是:如果运行时报错,可以精准的知道是哪行哪列报错

  • 但是当项目准备打包时,已经不需要知道报错的信息了,因此打包后,把map文件删了

可以在Vue.config.json中配置,不产生map文件

productionSourceMap:false

后台通用系统

知识点归纳

1.组件间的通信方式(6种)

第一种:props

适用场景:父子组件通信

注意事项

  • 如果父组件给子组件传递数据(函数):本质其实是子组件给父组件传递数据
  • 如果父组件给子组件传递数据(非函数):本质就是父组件给子组件传递数据

书写方式:3种

  • ["todos"]

  • {type:Array}

  • {type:Array, default:[ ] }

路由的props

书写形式 :布尔值,对象,函数

第二种:自定义事件

适用场景:子组件给父组件传递数据

$on$emit

第三种:全局事件总线$bus

适用场景:万能

vue.prototype.$bus = this;

第四种:pubsub-js

适用场景:万能

在React框架中使用比较多。(发布与订阅)

第五种:Vuex

适用场景:万能

数据仓库

第六种:插槽slot

适用场景:父子组件通信(一般结构)

  • 默认插槽
  • 具名插槽
  • 作用域插槽
第七种:V-Model
第八种:sync修饰符

相当于给对应的组件绑定了一个update:开头的自定义事件

:ABC.sync ==> update:ABC

第九种:$attrs以及$listeners
第十种:$children以及$parent

2.自定义组件的深入

原生DOM绑定原生事件

<button @click="abc"></button>

原生DOM绑定自定义事件

没有什么意义:因为原生的DOM不能够去调用$emit触发自定义事件

<button @myevent="abc"></button>

组件component绑定原生事件

  • 原生事件会被当作自定义事件来看待

    import Home from '@/page/home'
    <Home @click="abc"></Home>
    当点击组件时,没有任何效果
    
  • 如果想要绑定成原生的事件,就在事件后面.native

  • native就是原生的意思

  • 这个原生的事件,其实是给子组件的根节点绑定了点击事件—利用了事件委派

    import Home from '@/page/home'
    <Home @click.native="abc"></Home>
    当点击组件里的内容时,就会调用abc函数
    

组件component绑定自定义事件

父组件中

import Home from '@/page/home'
<Home @myevent="abc"></Home>
当点击组件里的内容时,就会调用abc函数
methods:{
	abc(params){
	console.log('myevent事件被子组件出发了,携带的参数为',params)
	}
}

子组件中

当点击子组件中的这个按钮时,就会触发父组件绑定的myevent这个自定义事件

$emit(’父组件自定义事件名称‘ ,参数)

<button @click="$emit('myevent','33112')"></button>

上述案例的执行结果

myevent事件被子组件出发了,携带的参数为33112

3.V-Model的深入

v-model也可以进行父子间的通信,实现数据的同步

实现方式:使用v-model结合原生的@input事件

  • 原生DOM中有事件oninput,它经常结合表单元素一起使用,当表单元素发生变化时就会触发一次回调
  • 在VUE2中,可以通过:value和input实现v-model的功能

父组件中

以下的两种写法都是等价的

  • 第一种写法: V-model实现 (简化写法)
import Home from '@/page/home'
<Home v-model="msg"></Home> 

data(){
	return:{
		msg:'中国'
	}
}
  • 第二种写法: 自定义事件 + : value实现
import Home from '@/page/home'
<Home :value="msg" @myevent="msg = $event" ></Home>

data(){
	return:{
		msg:'中国'
	}
}

子组件中

<input type="text" :value="value" @input="$emit('myevent',$event.target.value)" > </input> 

接受父亲通过props传进来的value
props:['value']   

4.sync修饰符的深入

可以实现父子组件的数据同步

以下使用sync和不用sync的效果相同

  • 当不使用sync修饰符时

父组件中

父组件通过props将abc传给子组件,还绑定了自定义事件myUpdateMoney

自定义事件myUpdateMoney的操作abc=$event的意思是把子组件传回来的值赋值给abc

现在的值为{{abc}}
<Home :abc="abc" @myUpdateMoney="abc=$event"></Home>

   data() {
    return {
        abc:120
     }
   },

子组件中

    <button @click="$emit('myUpdateMoney',abc-1)">花钱</button>

	props: ['abc']
  • 当使用sync修饰符时

    • 给对应的组件传递props

    • 给对应的组件绑定了一个update:开头的自定义事件

    • 举例 的效果就是

      • 给Home组件传递props为abc
      • 给Home组件绑定了一个名为update:abc自定义事件

父组件中

 现在的值为{{abc}}
 <Home :abc.sync="abc"></Home>

   data() {
    return {
        abc:120
     }
   },

子组件中

    <button @click="$emit('update:abc',abc-1)">花钱</button>

	props: ['abc']

5.$attrs$listeners属性

$attrs
  • 介绍:$attrs是组件身上的一个属性

  • 作用:用于接受父组件传过来,但未被子组件接收的的props

  • 注意:当子组件接收了某个props后,它将从$attrs中消失

  • 使用场景:可以通过V-bind结合$attrs一键绑定所有传过来的数据

  • 举例:封装一个element的按钮,让其可以带有提示效果

    • 思路1:因为a标签的title具有提示的效果,所以可以在button外面包一个a便签,写上title

    • 思路2:因为是封装,所以要增强其复用性,不能把属性写死,因此要父组件传过来绑定

    • 思路3:这里的v-bind不能用:代替,要写全

      父组件

         <MyButton title="提示按钮" type="success" icon="el-icon-delete" size="mini"></MyButton>
      

      子组件

      <template>
        <div>
          <a :title="title">
            <el-button v-bind="$attrs"></el-button>
          </a>
        </div>
      </template>
       
      <script>
      export default{
         name:'',
        props: ['title'],
      }
      </script>
      
$listeners
  • 介绍:$listeners是组件身上的一个属性

  • 作用:用于获取父组件给子组件传递的自定义事件

  • 使用场景:可以通过v-on结合$listeners触发父组件的自定义事件

  • 举例:父组件给子组件绑定一个事件,当点击子组件时,触发父组件绑定的这个事件

    • 思路1:要通过v-on绑定listeners,以触发父组件的自定义事件

    • 思路2:v-on不能简写为@

      父组件

      注意这里的click是自定义事件

       <MyButton type="success"  size="mini" @click="alert('触发')"></MyButton>
      

      子组件

       <el-button v-on="$listeners"></el-button>
      

6.$children$parent属性

ref与$refs
  • 在vue组件中,可以通过ref获取到子组件的数据与方法

  • 在html标签中用ref,在script的方法中用$refs

    <button @click="reduce(100)">点我儿子涨工资</button>
    <Son ref="abc"></Son>
    
    <script>
    export default{
      methods: {
        reduce(salary){
         this.$refs.abc.money+=salary; 
        }
      },
    }
    </script>
    
$children
  • $children是组件实例身上的一个属性,可以获取当前组件的所有子组件

  • 进而可以操作所有子组件身上的数据与方法,实现父子组件间通信

  • this.$children会将所有的子组件存放在一个数组中

  • 尽量别用下标来获取子组件,因为不知道第0项是不是想要的那个子组件

    <button @click="reduceAll(100)">点我孩子都涨工资</button>
    <Son></Son>
    <Daughter></Daughter>
    
    <script>
    export default{
      methods: {
        reduceAll(salary){
         this.$children.forEach(item=>{
             item.money+=salary
         })
        }
      },
    }
    </script>
    
$parent
  • $parent是组件实例身上的一个属性,可以获取当前组件的父组件
  • 进而可以操作父组件身上的数据与方法,实现父子组件间通信
<button @click="reduce(100)">点我爸爸涨工资</button>

<script>
export default{
  methods: {
    reduce(salary){
     this.$parent.money+=salary
    }
  },
}
</script>

7.混入mixin复习

主要放置组件重复的 JS 业务逻辑,以便于复用

myMixin.js

export default{
  methods: {
    add(a,b){
      return a+b
    }
  }
}

使用该JS业务逻辑的组件

<script>
import myMixin from '@/mixin/myMixin.js'
export default{
  mixins:[myMixin]
}
</script>

8.插槽的复习

  • 默认插槽
  • 具名插槽
  • 作用域插槽:子组件的数据来自于父组件,子组件决定不了自身的外观与结构

父组件

  • slot-scope用于接受子组件回传的数据,{todo,$index}是对应数据的解构
  • 返回的数据中,会以slot-scope="todo"的值为key,值为返回的item对象
  • 举例:{"todo",{id:1,text:'A'} } 因此数据要通过todo.todo.text获取
  • 子组件无权决定结构和外观,都由父组件决定
<template>
  <div>   
	<Children :todos="todos">
      <template slot-scope="{todo,$index}">
        <span :style="color='red'">{{todo.todo.text}}</span>
      </template>
    </Children>
  </div>
</template>
<script>
export default {
  data(){
      return {
          todos:[
          {
             id:1,text:'A'
          },
          {
             id:2,text:'B'
          }]
      }
  }
};
</script>

子组件

  • : todo和: $index是将子组件的数据回传给父组件 这就是作用域插槽的语法格式
  • todos为父组件通过props传递过来的数据
<template>
  <div>
    <ul>
      <li v-for="(item, index) in todos" :key="index">
        <slot :todo="item" :$index="index"></slot>
      </li>
    </ul>
  </div>
</template>
 
<script>
export default {
  props: ["todos"],
};
</script>

总结

在进一步学习Vue之后,我对其API有了更深入的理解,并发现了一些之前未曾注意到的用法和特性。通过学习,我掌握了更多的Vue技巧和最佳实践,进一步提升了自己的开发技能。

我发现Vue的生命周期钩子函数可以帮助我更好地理解组件的创建、更新和销毁过程,以及在不同阶段执行相应的操作,如在created钩子中进行数据初始化,在mounted钩子中进行DOM操作等。我也学到了如何合理使用计算属性和监听属性,以优化性能并简化代码。我还学习了如何使用Vue的动画过渡和动态组件,为我的应用程序增添了一些视觉效果和交互性。我也开始尝试使用Vue的异步组件和路由懒加载来优化应用的加载速度,提升用户体验。

通过深入学习Vue,我意识到组件间通信的重要性,并发现了更多灵活的通信方式,如使用事件总线、Vuex状态管理和provide/inject等。这些方法为我解决了组件间的数据传递和共享状态的问题。熟悉了Vue之后,我感到更加自信和有能力去构建复杂的前端应用程序。我也开始更多地关注Vue社区中的最新动态和开发技术,以不断提升自己,并与其他开发者分享我的经验和知识。

我将继续努力学习和探索Vue的更多特性和用法,以不断提升自己在前端开发领域的能力。