Vue 2.0 组件相关知识

203 阅读4分钟

组件部分


组件注册

  • 组件名的大小写问题

    定义组件名的方式有两种:

    使用kebab-case ==> Vue.component('my-component-name', { /* ... */ }),在使用短横线分隔命名定义组件时,引用这个自定义组件也必须使用 kebab-case 的形式

    使用PascalCase => Vue.component('MyComponentName', { /* ... */ }),在使用首字母大写命名定义组件时,在引用这个自定义组件时,两种形式都可以使用

    注意: 直接在 DOM(即非字符串的模板)中使用时只有 kebab-case 是有效的

  • 在模块系统中局部注册注意点

    如果在使用诸如 Babel 和 webpack 的模块系统,推荐创建一个 components 目录来存放组件

Prop

  • Prop 传值注意事项

    如果 prop 传递的是一个静态的字符串值,那么可以直接使用 title="My journey with Vue" 的形式来传递这个字符串值

    如果 prop 传递的是动态参数、数字、布尔值等等非字符串值,那么必须要通过 v-bind 进行动态的赋值

    如果 prop 想要将一个对象的所有属性都传入,可以直接使用不带参数v-bind 进行传入

  • 单向数据流

    所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 能影响子组件,但是子组件不能影响父级组件的状态

    不应该在子组件内部改变 prop,有两种常见的变更 prop 的方式:

    1. 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用,这个情况下可以使用一个本地的 data property 来接收 prop 用作其初始值

    2. 这个 prop 以一种原始的值传入且需要进行转换,这个情况下可以使用这个 prop 来定义一个计算属性供子组件进行修改使用

  • Prop 验证

    prop 会在一个组件实例创建之前进行验证,所以实例的 property(如 datacomputed等)在 defaultvalidator 函数中是不可用的

  • 类型检查重点

    type 可以是 StringNumberBooleanArrayObjectDateFunctionSymbol 这八个原生构造函数中的一个

    也可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认

  • 替换/合并已有的 Attribute 注意点

    classstyle 属性值会将值合并起来,但是对于绝大多数 attribute 来说,从外部提供给组件的值会替换掉组件内部设置好的值

自定义事件

  • 事件名注意点

    事件名不存在任何自动化的大小写转换,因此建议始终使用 kebab-case 形式的事件名

  • 自定义组件的v-model

    组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是如单选框、复选框等类型的输入控件可能会将 value 属性用于不同的目的。 model 选项可以用来避免这样的冲突。

    Vue.component('base-checkbox', { 
        model: { 
            prop: 'checked', 
            event: 'change' 
        }, 
        props: { 
            checked: Boolean 
        }, 
        template: ` 
            <input 
                type="checkbox" 
                v-bind:checked="checked" 
                v-on:change="$emit('change', $event.target.checked)" 
            > 
          ` 
        })
    
  • 原生事件绑定到组件的注意点

    可以使用 v-on.native修饰符来让一个组件的根元素上直接监听一个原生事件

    在一些特殊情况,如重构了一个根节点为 label 标签的 base-input 组件时,.native 监听器会静默失败,但是不会产生任何报错,但是 原生事件的处理函数可能不会如预期调用,针对这种情况,可以使用 $listeners 属性来包含作用在这个组件上的所有监听器,它是一个对象,例如

    { 
        focus: function (event) { /* ... */ } 
        input: function (value) { /* ... */ }, 
    }
    
  • .sync 修饰符的注意点

    .sync 是一个语法糖,用于父组件监听子组件更新某个 props 的请求的缩写语法

    带有 .sync 修饰符的 v-bind 不能表达式一起使用,而是只能提供想要绑定的属性名

    v-bind.sync 用在一个字面量的对象上,是无法正常工作的

插槽

  • 插槽语法注意点

    自2.6.0开始,Vue为具名插槽作用域插槽引入了新的统一语法,即 v-slot 指令来取代 slotslot-scope 这两个已经被废弃但未被移除且仍在文档中的属性

  • 插槽使用注意点

    如果子组件template没有包含任何一个 slot 元素,那么父组件起始标签和结束标签之间的任何内容都会被抛弃

    在插槽中使用数据时,如 {{ user.name }},该插槽可以访问实例 property,但不能访问子组件上的属性等,即不能访问子组件的作用域,父级模板里的所有内容都是在父级作用域中编译的,子模板里的所有内容都是在子作用域中编译的

  • 具名插槽注意点

    可以在 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称(<template v-slot:name>),如:

    <template v-slot:header> 
        <h1>Here might be a page title</h1>
    </template>
    

    任何没有被包裹在带有 v-slottemplate 中的内容会被视为默认插槽的内容

    注意: v-slot 只能添加在 <template>(只有在被提供的内容只有默认插槽时,才可以直接将 v-slot 用在组件上)

    v-slot: 可以进行缩写,可以替换为字符 #,前提是该缩写只有在有参数的情况下才可以使用

  • 作用域插槽注意点

    作用域插槽的作用为:让父组件的插槽内容能够访问子组件才有的数据

    绑定在 <slot> 元素上的 attribute 被称为插槽 prop,如

    <span> 
        <slot v-bind:user="user"> 
            {{ user.lastName }} 
        </slot>
    </span>
    

    在父组件中可以使用带值的 v-slot 来定义子组件提供的插槽 prop 的名字,如

    <template v-slot:default="slotProps"> 
        {{ slotProps.user.firstName }} 
    </template>
    

    注意: 这里的 slotProps 可以任意指定

  • 解构插槽 prop

    作用域插槽的工作原理:将插槽内容包裹在一个拥有单个参数的函数里

    因此,v-slot 的值可以是任何能够作为函数定义中的参数的JavaScript表达式,在支持的环境下(单文件组件或现代浏览器),也可以使用 ES2015 解构来传入具体的插槽 prop

  • 其他插槽使用示例

    插槽 prop 允许将插槽转换为可复用的模板,这些模板可以基于输入的 prop 渲染出不同的内容,示例如下:

    子组件:
    <ul> 
        <li 
            v-for="todo in filteredTodos"
            v-bind:key="todo.id" 
        >
            <!--
                我们为每个 todo 准备了一个插槽, 
                将 `todo` 对象作为一个插槽的 prop 传入。
            --> 
            <slot name="todo" v-bind:todo="todo">
                <!-- 后备内容 --> 
                {{ todo.text }}
            </slot> 
        </li> 
    </ul>
    
    父组件:
    <todo-list v-bind:todos="todos">
        <template v-slot:todo="{ todo }">
            <span v-if="todo.isComplete"></span> 
            {{ todo.text }}
        </template> 
    </todo-list>
    

动态组件&异步组件

  • 动态组件注意点

    组件在进行切换时,都是一个销毁再创建的过程

    如果希望组件在进行切换时,能够在第一次被创建后进行缓存,可以使用 <keep-alive> 元素将动态组件包裹起来,如

    <keep-alive> 
        <component></component> 
    </keep-alive>
    

    注意: <keep-alive> 要求被切换的组件都有自己的名字,不管是通过组件的 name 选项还是局部/全局注册

  • 异步组件知识点

    Vue 可以通过一个工厂函数的方式定义组件,工厂函数会异步解析组件的定义。 Vue 只有在这个组件需要被渲染的时候才会触发这个工厂函数,并且会把结果缓存起来供未来重渲染,如

    Vue.component('async-example', function (resolve, reject) { 
        setTimeout(function () { 
        // 向 `resolve` 回调传递组件定义 re
        solve({ 
            template: '<div>I am async!</div>' 
        }) 
      }, 1000)
     })
    

处理边界情况

  • 访问根实例

    在每一个 new Vue 实例的子组件中,子组件可以通过 $root 属性来对根组件进行访问

  • **访问父级组件实例

    子组件可以通过 $parent 来访问父组件的实例

    注意: 子组件触达父级组件会让应用更难调试和理解,尤其是在变更了父级组件的数据的时候,之后再回看子组件的时候,会很难找到源数据是从哪里来的,所以不推荐这种做法

  • 访问子组件实例或子元素

    父组件可以通过 $ref 属性为子组件赋予一个 ID 的引用

    使用时可以通过 this.$refs.name 来使用这个子组件

    refv-for 一起使用的时候,父组件得到的 ref 是一个包含了对应数据源的这些子组件的数组

    注意: $refs 只会在组件渲染完成之后生效,并且它们不是响应式的

  • 依赖注入知识点

    provide 选项可以使父组件指定想要提供给后代组件的数据/方法。provide 使用方法如下所示:

    provide() {
        return {
            name: this.name
            sayHi: this.sayHi
        }
    }
    

    任何后代组件(包括深层的后代组件)中,可以使用 inject 选项来接收父组件指定的属性/方法

    可以把依赖注入看作一部分“大范围有效的 prop”,除了:

      1. 祖先组件不需要知道哪些后代组件使用了它提供的 property
      2. 后代组件不需要知道被注入的 property 来自哪里
      
    

    注意: 这样的方式和使用 $root 一样是不够好的,会将应用程序中的组件与它们当前的组织形式耦合起来,换而言之就是在将组件使用到其他地方时,也应该有这样的文件结构,更好的解决方案应用是使用 vuex

  • 程序化的事件侦听器

    事件可以被 v-on 侦听,$emit 可以用来触发事件

    1. 通过 $on(eventName, eventHandler) 侦听一个事件
    2. 通过 $once(eventName, eventHandler) 一次性侦听一个事件
    3. 通过 $off(eventName, eventHandler) 停止侦听一个事件
  • 内联模板知识点

    inline-template 这个属性出现在一个子组件上时,这个组件将会用里面的内容作为模板,而不是将其作为被分发的内容,内联模板需要定义在 Vue 所属的 DOM 元素内

    注意: 最佳实践是在组件内优先选择 template 选项或 .vue 文件里的 template 元素来定义模板

  • 控制更新知识点

    在需要手动强制更新时,可以通过 $forceUpdate 来进行操作

    如果一个组件中包含了大量的静态内容,可以在根元素上添加 v-once 属性来确保这些内容只计算一次就被缓存起来