本篇目录:

  • 组件名
  • 组件注册
    • 全局注册

      • 基础组件的自动化全局注册
    • 局部注册
      • 在模块系统中局部注册
  • Prop
    • 单向数据流
    • Prop验证
      • 类型检查
    • 非Prop特性
      • 替换/合并已有的特性
      • 禁用特性继承
  • 自定义事件
    • 事件名
    • 自定义组件的v-model
    • 将原生事件绑定到组件
    • .sync修饰符
  • 小结

组件名

1、使用 kebab-case

Vue.component('my-component-name', { /* ... */ })

2、使用 PascalCase

Vue.component('MyComponentName', { /* ... */ })

当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说 和 都是可接受的。

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

组件注册

全局注册

Vue.component('my-component-name', {
// ... 选项 ...
})

注册之后可以用在任何新创建的Vue跟实例(new Vue)的模版中。

基础组件的自动化全局注册

我们往往有很多功能单一的基础组件,而这些组件有经常会被各个功能组件频繁的用到。这就会导致每个功能组件都会有一长串基础组件的长列表。

如果使用了 webpack (或在内部使用了 webpack 的 Vue CLI 3+),则可以通过require.context来全局注册这些常用的基础组件。

import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase' const requireComponent = require.context(
// 其组件目录的相对路径
'./components',
// 是否查询其子目录
false,
// 匹配基础组件文件名的正则表达式
/Base[A-Z]\w+\.(vue|js)$/
) requireComponent.keys().forEach(fileName => {
// 获取组件配置
const componentConfig = requireComponent(fileName) // 获取组件的 PascalCase 命名
const componentName = upperFirst(
camelCase(
// 剥去文件名开头的 `./` 和结尾的扩展名
fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
)
) // 全局注册组件
Vue.component(
componentName,
// 如果这个组件选项是通过 `export default` 导出的,
// 那么就会优先使用 `.default`,
// 否则回退到使用模块的根。
componentConfig.default || componentConfig
)
})

全局注册的行为必须在根 Vue 实例 (通过 new Vue) 创建之前发生

局部注册

先通过一个普通的对象来定义组件,然后在需要使用的地方通过属性-值的方式来进行定义引入。

var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ } new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})

由于是局部注册,所以只在引入的模块内有效,如果希望在子组件中使用,同样需要定义引入。

var ComponentA = { /* ... */ }

var ComponentB = {
components: {
'component-a': ComponentA
},
// ...
} 或者通过 Babel 和 webpack 使用 ES2015 模块 import ComponentA from './ComponentA.vue' export default {
components: {
ComponentA
},
// ...
}

在模块系统中局部注册

在上面例子中通过ES2015的模块方式进行了组件的局部注册。我们可以将所有的组件放到同一个目录下,每一个都在一个单独的文件中,然后在需要使用的地方通过模块引入的方式进行局部注册即可。

Prop

1、HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。所以在使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名

Vue.component('blog-post', {
// 在 JavaScript 中是 camelCase 的
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
}) <!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>

使用字符串模版时则不存在上述限制

2、可以通过直接赋值的方式来给prop传递一个静态的值,也可以通过v-bind的方式来动态传入prop的值。

3、几种数据类型的传入方式

1.数字类型
<!-- 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:likes="42"></blog-post> <!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:likes="post.likes"></blog-post> 2.布尔值类型
<!-- 包含该 prop 没有值的情况在内,都意味着 `true`。-->
<blog-post is-published></blog-post> <!-- 即便 `false` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:is-published="false"></blog-post> <!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:is-published="post.isPublished"></blog-post> 3.数组类型
<!-- 即便数组是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post> <!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:comment-ids="post.commentIds"></blog-post> 4.对象类型
<!-- 即便对象是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post
v-bind:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
></blog-post> <!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:author="post.author"></blog-post> 5.对象的全部属性
post: {
id: 1,
title: 'My Journey with Vue'
}
<blog-post v-bind="post"></blog-post> 等价于 <blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>

单向数据流

通过prop传递数据是的父子组件之间形成了单向下行绑定,父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。

两种在子组件中试图改变一个prop的情况:

1、这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:

props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}

2、这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:

props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}

注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。

Prop验证

我们可以为Prop提供一个验证来保证传入的数据类型的有效性。为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。

Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})

注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的属性 (如 data、computed 等) 在 default 或 validator 函数中是不可用的。

类型检查

type 可以是下列原生构造函数中的一个:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

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

function Person (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
} Vue.component('blog-post', {
props: {
author: Person
}
})

非Prop特性

替换/合并已有的特性

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

禁用特性继承

如果你不希望组件的根元素继承特性,你可以在组件的选项中设置 inheritAttrs: false

注意 inheritAttrs: false 选项不会影响 styleclass 的绑定。

自定义事件

事件名

不同于组件和 prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。不同于组件和 prop,事件名不会被用作一个 JavaScript 变量名或属性名,所以就没有理由使用 camelCase 或 PascalCase 了。并且 v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的)。所以,始终使用 kebab-case 的事件名

自定义组件的v-model

可以通过model选项将v-model的value特性用于不同的目的。

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)"
>
`
}) <base-checkbox v-model="lovingVue"></base-checkbox>

这里的 lovingVue 的值将会传入这个名为 checked 的 prop。同时当 <base-checkbox> 触发一个 change 事件并附带一个新的值的时候,这个 lovingVue 的属性将会被更新。

将原生事件绑定到组件

可能有很多次想要在一个组件的根元素上直接监听一个原生事件。这时,你可以使用 v-on 的 .native 修饰符来进行绑定。但是如果是一个类似原生元素的组件时,可能组件内部已经被重构了,此时会.native失效。

为了解决这个问题,Vue 提供了一个 $listeners 属性,它是一个对象,里面包含了作用在这个组件上的所有监听器。有了这个 $listeners 属性,你就可以配合 v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素。

Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` 将所有的对象合并为一个新对象
return Object.assign({},
// 我们从父级添加所有的监听器
this.$listeners,
// 然后我们添加自定义监听器,
// 或覆写一些监听器的行为
{
// 这里确保组件配合 `v-model` 的工作
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
</label>
`
})

.sync修饰符

在前面我们知道,prop是单行向下的数据流方式,子组件不能直接修改prop的值,我们推荐通过update:myPropName 的模式触发事件取而代之。

//父组件监听事件
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document> //子组件触发事件
this.$emit('update:title', newTitle)

我们为这种模式提供一个缩写,即 .sync 修饰符:

<text-document v-bind:title.sync="doc.title"></text-document>

注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’” 是无效的)。取而代之的是,你只能提供你想要绑定的属性名,类似 v-model。

当我们用一个对象同时设置多个 prop 的时候,也可以将这个 .sync 修饰符和 v-bind 配合使用:

<text-document v-bind.sync="doc"></text-document>

这样会把 doc 对象中的每一个属性 (如 title) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on 监听器。

将 v-bind.sync 用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”,是无法正常工作的,因为在解析一个像这样的复杂表达式的时候,有很多边缘情况需要考虑。


小结

本次主要围绕组件注册和组件的Prop,以及组件的自定义事件相关内容进行了梳理。我们知道,在Vue.js的开发过程中,我们会面临各种各样的组件相关的处理,组件也是我们开发过程中十分重要的部分,所以关于Vue.js组件相关的内容我们分两次来进行整理。

浅尝Vue.js组件(一)的更多相关文章

  1. 浅尝Vue.js组件(二)

    本篇目录: 插槽(Slot) 插槽内容 作用域 具名插槽 作用域插槽 独占默认插槽的缩写语法 解构插槽Prop 使用场景举例 动态插槽名 具名插槽缩写 动态组件&keep-alive 异步组件 ...

  2. 浅谈Vue.js

    作为一名Vue.js的忠实用户,我想有必要写点文章来歌颂这一门美好的语言了,我给它的总体评价是“简单却不失优雅,小巧而不乏大匠”,下面将围绕这句话给大家介绍Vue.js,希望能够激发你对Vue.js的 ...

  3. 包学会之浅入浅出Vue.js:结业篇(转)

    蔡述雄,现腾讯用户体验设计部QQ空间高级UI工程师.智图图片优化系统首席工程师,曾参与<众妙之门>书籍的翻译工作.目前专注前端图片优化与新技术的探研. 在第一篇<包学会之浅入浅出Vu ...

  4. 包学会之浅入浅出Vue.js:升学篇

    包学会之浅入浅出Vue.js:升学篇 蔡述雄,现腾讯用户体验设计部QQ空间高级UI工程师.智图图片优化系统首席工程师,曾参与<众妙之门>书籍的翻译工作.目前专注前端图片优化与新技术的探研. ...

  5. 包学会之浅入浅出Vue.js:开学篇(转)

    包学会之浅入浅出Vue.js:开学篇 蔡述雄,现腾讯用户体验设计部QQ空间高级UI工程师.智图图片优化系统首席工程师,曾参与<众妙之门>书籍的翻译工作.目前专注前端图片优化与新技术的探研. ...

  6. 包学会之浅入浅出Vue.js:结业篇

    在第一篇<包学会之浅入浅出Vue.js:开学篇>和上一篇<包学会之浅入浅出Vue.js:升学篇>的学习中,我们首先了解了Vue环境的搭建以及两个重要思想——路由和组件的学习,通 ...

  7. vue.js组件化开发实践

    前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了下面的内容.借油开车. 组件化 需求一到,接就是怎么实现,技术选型自然 ...

  8. 如何理解vue.js组件的作用域是独立的

    vue.js组件的作用域是独立,可以从以下三个方面理解: 1.父组件模板在父组件作用域内编译,父组件模板的数据用父组件内data数据:2.子组件模板在子组件作用域内编译,子组件模板的数据用子组件内da ...

  9. VUE.JS组件化

    VUE.JS组件化 前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了下面的内容.借油开车. 组件化 需求一到,接就是怎 ...

随机推荐

  1. Ng第八课:神经网络表述(Neural Networks: Representation)

    8.1  非线性假设 8.2  神经元和大脑 8.3  模型表示 1 8.4  模型表示 2 8.5  特征和直观理解 1 8.6  样本和直观理解 II 8.7  多类分类 8.1  非线性假设 无 ...

  2. kepware http接口 c语言 ruby

    读取某变量的值 require 'uri' require 'net/http' url = URI("http://127.0.0.1:39321/iotgateway/read?ids= ...

  3. concurrent.future

    concurrent.future module provides a high-level interface for asynchronously executing callables. Bas ...

  4. 第80讲:List的泛型分析以及::类和Nil对象

    今天我们学习一下scala中的列表,List. 通过源码,我们可以发现,List类型是协变的,所以我们可以把Int类型的List赋值给Any型的List. 我们可以看到,List定义下有3个比较重要的 ...

  5. POJ3666 线性dp_离散化_贪心

    POJ3666 线性dp_离散化_贪心 就DP而言这个题不算难,但是难就难在贪心,还有离散化的思想上 题目大意:n个土堆,问你最少移动多少单位的图,可以使得这n个土堆变成单调的 dp[i][j]表示前 ...

  6. unigui1404在delphi10.2.2安装

    unigui1404在delphi10.2.2安装 UNIGUI1404不能直接在DELPHI10.2.2下面编译安装,在10.2.1下面是可以的. 这里讲下怎样安装的方法: 1)执行FMSoft_u ...

  7. 2015年 10月最新苹果IOS上架App Store商店步骤

    1.1.前期工作 首先你需要有一个苹果的开发者帐号,一个Mac系统. 如果没有帐号可以在打开http://developer.apple.com/申请加入苹果的开发者计划.支付99美元每年,怎么申请网 ...

  8. WINDOWS CLUSTER -- 时间不同步导致的群集问题

    故障描述,重启服务器后,发现该重启节点未成功加入到Windows群集中,导致该节点上的Alwayson服务也受影响处于“正在解析”状态,尝试重启cluster服务,发现无效,查看windows日志,发 ...

  9. 通过 NewLife.XCode 迁移任意现有数据库到任意数据库

    通过 NewLife.XCode 迁移任意现有数据库到任意数据库(附分表分库方法) 本文背景是将其他系统的数据库迁移到另一个数据库(仅需 20 行代码),也可以作为项目迁移用,生成自己系统的专属实体代 ...

  10. ASP.Net MVC OA项目笔记<五>

    1.1.1  抽象工厂封装数据操作类实例创建,然后DBSession调用抽象工厂,修改DBSession CZBK.ItcastOA.DALFactory数据会话层调数据层不能直接new,要封装一下解 ...