likes
comments
collection
share

Vue2深入必知的几点

作者站长头像
站长
· 阅读数 24

Vue2深入学习

异步组件

  • Vue.component()注册组件时,接收两个参数
  • 第一个是组件名,如果是在字符串模板或组件中使用,建议大驼峰。如果是在DOM中直接使用,建议全小写短横线连接
  • 第二个是组件定义。可以是一个对象或一个函数

Vue2深入必知的几点

  • 观察源码实现可知,传入的组件定义被缓存在options.components中。
  • 当定义为对象时,会继承this.options._base对象的值
  • 当为函数时,是原样保存,同时该组件被识别为异步组件

Vue2深入必知的几点

  • 观察文档描述可知,该函数默认接收resolve和reject两个参数,我们可以在函数中根据自己的方式返回组件的定义

特别注意

  1. Vue.component提供了异步渲染组件的功能,当收到的组件定义参数是函数时,会在需要渲染时去调用该函数。
  2. webpack提供了动态导入模块的功能,当使用import()时,会将该语句编译为webpack.require.e()函数。
  3. 在编译时,webpack遇到import()编译为webpack.require.e()函数,
  4. 在运行时,Vue将异步组件函数调用获取到webpack.require.e的调用结果,同时会给e传入参数resolve,reject。而webpack.require.e内部是动态创建script标签,将分割为单独chunk的模块的url作为src,动态插入页面,监听脚本加载事件,当加载完毕则执行参数resolve将获取到的结果传入。
  5. 因此我们在使用import时必须放在函数中,这样异步组件才能获取到import的结果。

Prop和Emit

Prop

  • Prop是组件的一个概念,用于组件外部给组件内传递值
  • 可以传递任意类型的值给prop变量
  • 组件内定义prop时,可以定义每个prop变量的type,required,default,validator
  • 传递属性时,如果组件内部没有对应的prop定义,则属性会被添加到组件的根元素上
  • 一般的属性传入组件后会覆盖掉根元素的同名属性值,但是class和style,Vue特殊处理进行合并
  • 如果不希望根元素继承这些非prop的属性,则在组件的选项对象中将inheritAttrs设置为false即可
  • Vue会将非prop的属性存放在$attr对象上,我们可以通过$attrs结合v-bind将这些属性绑定到组件内的任意标签上
  • 特别注意

  • prop是单向数据流,传递给组件的prop属性值,会在父组件中更新后自动立即刷新子组件的值
  • 子组件修改prop的值,并不会自动刷新父组件,除非传入的prop是一个对象,由于引用传递的原因会导致父组件也被更新,vue会爆出警告,但不会阻止这种行为
  • 最佳实践,如果子组件仅需要使用prop初始化,则可以声明一个data,将prop作为初始值
  • 最佳实践,如果子组件需要使用转换后的prop,则可以声明一个计算属性
  • 最佳实践,如果子组件需要修改prop,则最好通过emit告知父组件,让父组件去修改,保证数据更新来源的一致性
  • Emit

  • Emit是用于触发自定义事件的
  • Vue通过@或者v-on指令来绑定一个自定义事件,并在其被触发时调用回调函数
  • 也就是说使用v-on绑定的事件名,必须通过emit来手动触发,如果我们想绑定原生的事件,并且让DOM自动触发,就要通过.native修饰符来说明
  • 通过声明为原生的事件名,传入组件后,默认绑定在根元素上,如果根元素没有相应的事件,就会导致事件无法触发。
  • Vue会将所有绑定在组件上的事件,以及监听器存放在$listeners对象上,我们可以通过$listeners结合v-on将这些监听器绑定到组件内的任意标签上
  • v-model前边的案例演示了默认绑定value,并且监听input事件,我们在自定义组件中,只要声明value——prop,emit——input即可
  • 为了可读性,我们可以通过组件定义时model选项来声明v-model的的prop和event,则外部组件通过v-model双向绑定时,就会监听相应的prop和event
  • 与v-model相同功能的另一种实现是,绑定prop时使用修饰符.sync即可,相比于v-model其书写简单,组件内只要触发update:变量名即可,父组件也不用写监听器和回调函数
  • 在Vue2中子组件内部不需要配置emits选项来声明自定义事件,直接使用$emit去触发就行
  • 插槽

  • Slot是用于在父组件传递渲染内容到子组件的一种方式
  • 在vue2.6之后将具名插槽slot和作用域插槽slot-scope废除了,采用了新的v-slot
  • 具名插槽slot,在子组件中定义多个slot插槽,通过name来区分不同的插槽,在父组件中通过slot来指定内容的分发
  • 作用域插槽,在子组件中将子组件的某些属性通过v-bind绑定到slot标签上,父组件中通过slot-scope获取子组件传出的属性对象(组件内部会将所有的属性绑定到一个对象上传出)
  • 作用域插槽的原理是将传入的插槽内容放在一个函数内,将子组件绑定的prop作为一个对象的属性传入该函数,因此slot-scope的值是一个可作为函数参数的任意类型的值
  • v-slot的缩写为#,动态插槽名v-slot:[name]即可
  • 动态组件

  • 动态组件通过component组件配合is属性来动态渲染,但是切换渲染组件后,之前的内容不会被保留,所以在外层包一层keep-alive组件即可缓存
  • 异步组件在注册组件时,不传递组件的选项对象,而是传递require或者import语句来动态导入选项对象
  • 异步组件需要配合构建工具来使用,如webpack可以实现代码分割
  • 异步组件可以是一个工厂函数,返回一个新的对象,该对象基本结构如下所示,特别注意异步组件工厂函数的写法()=>({})
  • const AsyncComponent = () => ({
        // 需要加载的组件 (应该是一个 `Promise` 对象)
        component: import('./MyComponent.vue'),
        // 异步组件加载时使用的组件
        loading: LoadingComponent,
        // 加载失败时使用的组件
        error: ErrorComponent,
        // 展示加载时组件的延时时间。默认值是 200 (毫秒)
        delay: 200,
        // 如果提供了超时时间且组件加载也超时了,
        // 则使用加载失败时使用的组件。默认值是:`Infinity`
        timeout: 3000
    })
    

    边界情况

    • 后代组件通过$root获取根实例数据
    • 后代组件通过$parent获取父实例数据
    • 组件通过$refs获取根实例数据(ref不是响应式数据,只有子组件渲染完毕才会生效,因此在模板和计算属性中不要使用,否则获取不到数据)
    • 依赖注入通过provide和inject来获取祖先传下来的数据,相比于root,这种方式父组件只暴露少量数据,相比于root,这种方式父组件只暴露少量数据,相比于root,这种方式父组件只暴露少量数据,相比于parent,可以跨越任意多的层级去获取数据

    事件侦听器的一些注意事项

    • $emit是用来触发自定义事件的,我们一般是在子组件中通过v-on来自动监听自定义事件
    • 有时候需要手动去监听组件的某些事件,而不是在组件标签上通过v-on自动监听,此时就会用到on,on,on,once,$off这三个方法
    • 比如,我们在组件内引入第三方组件时,想要在父组件挂载后去实例化三方组件,在父组件卸载前销毁三方组件
    • 第一种方式:在父组件的mounted钩子函数内实例化组件,并保存该组件的引用,在beforeDestroy钩子函数中销毁该组件
    • 第二种方式:在父组件的mounted钩子函数内实例化组件,调用$once去手动监听hook:beforeDestroy事件,在回调中销毁该组件,相比第一种方式有两个好处,不用保存三方组件的引用,挂载和卸载的逻辑在一块,容易阅读

    典型边界问题

    • 父子组件互相引用,模板解析时发现互相引用,找不到入口,就会报组件未定义。当然如果父组件是全局注册的则Vue可以自动解开这个循环,如果都是局部注册,则需要我们去异步引入子组件
    • 在组件标签中加入inline-template属性,则组件标签之间的内容不会被解析为slot,而是取代组件内的template
    • 在template的根标签中加入v-once则只进行一次渲染并缓存,之后使用缓存来解析,一般不用,相比于提升的那点时间,对人造成的困惑更难以接受
    • 对于数组和对象的变更检测,有时候可能不满足我们的需求,但是99%的情况都能满足,最好是检查是否使用正确。如果确定使用正确也无法满足需要手动强制更新时可调用$forceUpdate来强制更新
    • 注意强制更新只会影响当前组件实例和其直接的子组件实例,更深层次的组件不会被影响,因为递归渲染子组件是Vue主动检测父组件发生变化后才会执行的操作,我们手动更新其父组件并未发生变化