一. 组件注册
以下代码使用Babel 和 webpack 的模块系统
1.通过vue.component 全局注册
- 全局注册的组件可以用在任何新建的 vue根实例(new Vue)模板中。
- 全局组件在各自内部也都可以相互使用
main.js
import componentA from './componentA
Vue.component('组件名', componentA)
当你有多个全局组件需要注册时,可以这样写
新建一个configurations.js
import demoA drom './demo1';
import demoB drom './demo1';
import demoC drom './demo1';
function deploy(Vue) {
Vue.component('demoA', demoA);
Vue.component('demoB', demoA);
Vue.component('demoC', demoA);
}
export default deploy;
main.js
...
import deploy from './configurations';
Vue.use(deploy )
2.局部注册
- 全局注册的缺点: 如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。
- 局部注册的组件在其子组件中不可用
...
import componentA from "./componetA;
export default {
data() {
return {};
},
components: { componentA },
}
3.基础组件的自动化全局注册
- 有时组件只是包裹了一个输入框或按钮之类的元素,是相对通用的基础组件。基础组件如果使用局部注册,会非常麻烦。
- 推荐使用require.context 只全局注册这些非常通用的基础组件
- 例子:main.js为应用入口文件,组件使用PascalCase命名法
main.js
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
// 这两个方法用来实现PascalCase命名
const requireComponent = required.context(
// 基础组件的相对路径
'./basecomponent',
// 是否查询子目录
false,
// 匹配文件目录的正则
/BaseA+\.(vue|js)$/
)
requireComponent.keys().forEach(
fileName => {
// 获取组件配置
const componentConfig = requireComponent(fileName);
// 获取组件的 PascalCase 命名
const componentName = upperFirst(
camelCase(
// 获取和目录深度无关的文件名
fileName
.split('/')
.pop()
.replace(/\.\w+$/, '')
// 全局注册组件
Vue.component(
componentName,
// 如果这个组件选项是通过 `export default` 导出的,
// 那么就会优先使用 `.default`,
// 否则回退到使用模块的根。
componentConfig.default || componentConfig
)
)
)
})
二. Prop
1.Prop的类型
- 基础例子
props: ['a', 'b', 'c']
- 如果你希望每个prop都有指定的类型,可以这么写
props: {
a: String,
b: Number,
c: Boolean
}
在它们遇到错误的类型时会从浏览器的 JavaScript 控制台提示用户
2. 传递静态或动态 Prop
- 传递静态的 prop
<son-component title="My journey with Vue"></son-component>
- 传递动态的 prop
<son-component v-bind:title="My journey with Vue"></son-component>
3. 传入一个对象的所有属性
- 当你想把一个对象的所有属性作为 prop 传入时 你可以使用不带参数的 v-bind (取代 v-bind:prop-name)
例子:给定一个对象 postMsg
postMsg: {
title: 'test',
name: 'psot'
}
下面的模板
<son-component v-bind="postMsg"></son-component>
等同于
<son-component
v-bind:title="postMsg.title"
v-bind:name="postMsg.name"
>
</son-component>
注意:上面两种写法子组件都要使用props接收数据
4. prop的单向数据流
官网原话:所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
- 不应该在一个子组件内部改变 prop,如果你这样做了,Vue 会在浏览器的控制台中发出警告
- 这里有两种常见的试图改变一个 prop 的情形:
- 子组件接下来希望将prop数据作为一个本地数据来使用,此时最好定义一个本地的 data 属性并将这个 prop 用作其初始值
props: ['a', 'b'],
data() {
return {
sonData: this.a;
}
}
- 这个 prop 以一种原始的值传入且需要进行转换,此时 最好使用这个 prop 的值来定义一个计算属性
props: ['size'],
compouted: {
twoBei() {
return this.size.trim().toLowerCase();
}
}
5. prop的验证
给prop指定类型,或者一些特定需求。这在开发一个会被别人用到的组件时尤其有帮助。
官网例子
export default {
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
}
}
}
}
关于类型检查
type 可以是下列原生构造函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
你也可以使用自定义构造函数,此时 通过 instanceof 来进行检查确认
6. 非 Prop 的特性
- 非 prop 特性是指传向一个组件,但是该组件并没有相应 prop 定义的特性 ($attrs)
- 替换/合并已有的特性
- 现有一个子组件 base-input, 模板如下
<input type="date" class="form-control">
- 调用 base-input 时,添加了一个类名并传入一个type属性
<base-input
class="style1"
type="text"
> </base-input>
备注:对于绝大多数特性来说,从外部提供给组件的值会替换掉组件内部设置好的值。class和style除外,这两个属性的值会合并,而type属性会变成 text。
- 禁用特性继承
- 当你不希望组件的根元素继承特性,你可以在组件的选项中设置 inheritAttrs: false。
- 当设置inheritAttrs: true(默认)时,子组件的顶层标签元素中会渲染出父组件传递过来的属性
- 当设置inheritAttrs: false时,子组件的顶层标签元素中不会渲染出父组件传递过来的属性
- inheritAttrs为true或者false 都不会影响 $attrs
- inheritAttrs: false 选项不会影响 style 和 class 的绑定。
链接
三. 自定义事件
1. 在组件上使用v-mdel
- 在html标签上使用 v-mdel
<input v-model="mydata">
等价于
<input :value="mydata" @input="value = $enent.target.value">
$enent.target.value指input输入框的值
- 在组件上使用v-mdel
自定义的input组件
<template>
<input :value="value" @input="$emit('input', $event.target.value)">
</template>
<script>
export default {
props: ['value'],
data() {
return {};
},
};
</script>
调用组件时
<custom-input
v-model="searchText"
></custom-input>
或者
<custom-input
:value="searchText"
@:input="searchText = $event"
></custom-input>
2. 自定义组件的 v-model
一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件
- 注意:单选框、复选框 等类型的输入控件可能会将 value 特性用于不同的目的
- 解决方法:可以在定义组件时使用model 选项来避免这样的冲突:
自定义单选框组件 base-checkbox
<template>
<input
type="checkbox",
:checked="checked",
@change="$emit("change", $event.target.checked)"
>
</template>
export default {
name: "base-checkbox",
model: {
prop: "checked",
event: "change"
},
props: {
checked: Boolean
},
}
调用组件时
<base-checkbox v-model="lovingVue"> </base-checkbox>
注意: lovingVue的值会传如 名为cheched 的prop,base-checkbox组件的checked发生变化触发change事件时,lovingVue会更新。
3. 将原生事件绑定到组件
- 在一个组件的根元素上直接监听一个原生事件,使用 v-on的.native修饰符
- .native修饰符适用于 组件的根元素支持所监听的原生事件的情况
<base-input @focus.native="onFoucus"> </base-input>
如果base-input组件的根元素不是 input,focus事件就不会触发
- $listeners 属性
- $listeners是一个对象,其中包含了作用在这个组件上的所有监听器
{
focus: (event)=>{},
input: (value)=>{},
......
}
- v-on=" l i s t e n e r s " 效 果 和 v − b i n d = " listeners" 效果和 v-bind=" listeners"效果和v−bind="attrs"相似,他可以将所有的事件监听器指向组件的某个特定的子元素
- 结合 $listeners 和计算属性,创建一个可以配合 v-model 工作的基础输入框组件
<template>
<label>
{{title}}
<input v-bind="$attrs" :value="value" v-on="inputListeners">
</label>
<template/>
export default {
inheritAttrs: false, // 禁用特性继承
props: ["label", "value"],
computed: {
inputListeners() {
return {
Object.assign{
{},
this.$listeners,
{
// 由于调用组件时传入了input事件,此时需要覆写input监听器的行为
input: (event) => { this.$emit("input", event.target.value) }
}
}
}
}
}
}
现在 base-input 组件是一个完全透明的包裹器了,也就是说它可以完全像一个普通的 元素一样使用了:所有跟它相同的特性和监听器的都可以工作。
4. .sync 修饰符
- 使用场景: 当需要对一个 prop 进行“双向绑定”,我们推荐使用 update:myPropName 的模式触发事件
在一个包含 title prop 的假设的组件中,我们可以用以下方法表达对其赋新值的意图
this.$emit('update:title', newTitle)
父组件可以监听 update:title 事件并根据需要更新一个本地的数据属性
<text-document
:title="doc.title"
@update:title="doc.title = $event"
></text-document>
.sync 修饰符的写法
<text-document v-bind:title.sync="doc.title"></text-document>
注意:
- :title.sync的写法不能和表达式一起使用(例如 :title.sync=“doc.title + 1”)
- v-bind.sync = "{ a: 1 }"这种写法也不支持,只支持绑定一个data的属性