form最后一节。
组件基础
- 组件的复用: data必须是函数
- 组织
- 通过Prop向子组件传递data
- 单个根元素
- 通过event向父组件发送消息: 使用事件抛出一个value, 在组件上用v-model
- 动态组件
- 解析DOM模版时的⚠️.
深入组件
- 组件注册
- Prop
- 自定义事件: this.$emit('my-event')用kebab-case做事件名称
- 插槽
- 异步组件
- 处理边界情况
例子:
组件是可复用的 Vue 实例,且带有一个名字,如Vue.component("名字", {data..., template...})。
因为组件是可复用的 Vue 实例,所以它们与 new Vue
接收相同的选项,例如 data
、computed
、watch
、methods
以及生命周期钩子等。仅有的例外是像 el
这样根实例特有的选项。
组件可以复用
无限次反复用比如上例。多次使用<button-counter>, 每用一个,就创建了一个实例。
data是函数
和新建Vue中的data:{...}不一样,
data必须是函数 data: function(){...}, 这是为了每个实例可以维护一份被返回对象的独立的拷贝。
即data不是共用的数据集合,它是函数,是方法,通过它每个实例都会得到各自的结果。
组件的组织
为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册
Vue.component
全局注册:- 全局注册的组件可以用在其被注册之后的任何 (通过
new Vue
) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
(深入看: 组件注册)
通过 Prop 向子组件传递数据
Prop 是你可以在组件上注册的一些自定义特性。
当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。
可以使用 v-bind
来动态传递 prop。这在你一开始不清楚要渲染的具体内容,比如从一个 API 获取博文列表的时候,是非常有用的。
单个根元素
每个组件只能有一个根元素,一般使用<div>包裹其他元素。
否则会报错 every component must have a single root element
⚠️:
JavaScript 的模板字符串来让多行的模板更易读。
它们在 IE 下并没有被支持,所以如果你需要在不编译的情况下支持 IE,请使用折行转义字符取而代之。
什么是模版字符串:
- 模板字符串使用反引号 (` `) 来代替普通字符串中的用双引号和单引号。
- 模板字符串可以包含特定语法(
${expression}
)的占位符。
在多行string每行结尾加上反斜杠
var htmlSTring = "<div>
This is a string.
</div>";
通过事件向父级组件发送消息
案例: https://codepen.io/chentianwei411/pen/EeXxrM/?editors=1010
分析:
在我们开发 <blog-post>
组件时,它的一些功能可能要求和父级组件进行沟通。
例如我们可能会引入一个可访问性的功能来放大博文的字号,同时让页面的其它部分保持默认的字号。
注意:⚠️:
1.自定义组件可以使用v-for,但必须配合使用v-bind:key
2. 任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要用 props
:
<my-component
|
- 明确组件数据的来源能够使组件在其他场合重复使用。
3. vm.$emit( eventName, […args] ) (点击看api)关于事件的实例方法。
-
触发当前实例上的事件。附加参数都会传给监听器回调。
- 监听器指v-on,用于监听当前实例上的自定义事件,事件可以由vm.$emit触发,
参数的上传:
第一种:
在监听v-on:enlarge-text用$event获得上传的参数:v-on:enlarge-text="postFontSize += $event"
第二种:
如果这个事件处理函数是一个方法,那么这个值将会作为第一个参数传入这个方法:
1. v-on:enlarge-text='onEnLargeText'
2.在nev Vue中添加这个方法:
methods: {
onEnlargeText: function(enlargeAmount) {
this.postFontSize += enlargeAmount
}
}
在组件上用v-model
点击:查看(详细分析了数据的流入和流出。及相关。)
还需要⚠️:
- v-model本身就是一个利用叫value的prop和叫input的自定义事件。所以完全可以自己写,因为单选,复选等类型的输入控键,会使用value特性用于其他目的。
- 2.2—+新增了组件的model选项,可以自定义v-model的prop和event 其实就是语法糖。
自己写的案例分析:https://codepen.io/chentianwei411/pen/bxozoj?editors=1010
在DOM上增加了一个组件实例<custom-input>,
- 这个组件把外部数据传给 prop特性value,
- 有一个监听v-on:input事件。
关于$event.target.value
$event是触发的事件,这里是在输入框输入的动作。
$event.target是这个动作作用在哪个元素上。target特性返回被事件激活的元素。这里是输入框input元素。
value指的是input中的value特性,即输入的内容。
el的作用
定位html中的元素,这个元素将作为Vue实例的挂载目标。在实例挂载后,元素可以用vm.$el访问。
template: Vue实例的模版
模版会替换被挂载的html元素,被挂载的元素的内容会被忽略,除非使用inline-template。
<custom-input v-bind:value="searchText"
这是把初始化中的data属性中的数据对象和自定义的prop特性value绑定在一切。
<custom-input v-on:input='searchText = $event'
用于监听input事件,并对参数进行回调处理。
template中的 <input v-bind:value='value'>
把Props: ['value']特性的值赋予了<input>的value属性。
template中的<input v-on:input=''"$emit('input', $event.target.value)">
- 当监听到input这个原生HTML事件时, 执行$emit这个实例方法。
- $emit会触发当前Vue实例上的自定义事件input(自定义,自定义,自定义),并传递参数。
- Vue实例上的监听器v-on会监听到这个触发事件input,并执行预期的行为。这里是执行一条inline statement。
动态组件
根据‘组件名称’来动态的调用组件。类似动态调用方法。把动态和异步组件读完。
<component v-bind:is='组件名称'></component>
用法:
渲染‘元组件’为动态组件。 根据v-bind:is的值,来决定渲染哪个组件。
这里是根据vm的计算属性中currentTabComponent函数来得到组件的名称
内置的组件:
component
Props:
- is - string | ComponentDefinition | ComponentConstructor
- inline-template -boolean
在动态组件上使用keep-alive缓存组件
keep-alive
目的:提高效能:保留组件状态,或避免重新渲染。
Props:
- include -字符串或正则表达式。 匹配的组件会被cache
- exclude -字符串或正则表达式。任何匹配的组件都不会被cache
用法:
- <keep-alive>包裹动态组件</keep-alive>, cache不活动的组件,不会销魂它们。
- <keep-alive>是一个抽象组件,不会渲染DOM元素,不会出现在父组件链中。
钩子函数:(这两个钩子,放到声明组件的代码中)
- activated: 在keep-alive组件激活时用
- deactivated: 在keep-alive组件停用时调用。
Vue.component('tab-home', {
template: '<div>Home component</div>',
activated() {
console.log('activatsse')
},
deactivated() {
console.log('deactivated')
}
})
异步组件
以工厂函数的方式定义组件。
Vue.component('async', function(resolve, reject) { ... })
这个工厂函数会异步解析你的组件定义。
在这个组件被渲染的时候,会触发该工厂函数,并把结构缓存起来供未来重新渲染。
工厂函数?
当一个函数返回一个对象时,并且函数没有使用new关键字,这个函数称为factory function。
不是类,也不是构造函数。
个人理解:工厂函数好像就是类,传递不同的参数,返回不同的对象,但对象们的结构都一样,只是值不一样。
将异步组件和 webpack 的 code-splitting 功能一起配合使用,未学习这个功能!!
Promise是什么?(点击看api)
new Promise( function(resolve, reject) {...} );
一个代表了异步操作最终完成或者失败的对象。大多数人都在使用由其他函数创建并返回的promise.
promise本质上是一个绑定了回调的对象.
- 通过.then形式添加回调函数。
- 通过多次调用.then,可以添加多个回调函数。
- 链式调用,凉鞋执行多个异步操作。
诞生的原因猜测: 传统的多重回调的写法是各地狱,因此改用promise的方式。
回调地狱:崩溃的写发和读发:
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
现代的方法:
doSomething().then(function(result) { return doSomethingElse(result); }) .then(function(newResult) { return doThirdThing(newResult); }) .then(function(finalResult) { console.log('Got the final result: ' + finalResult); }) .catch(failureCallback);
或者使用箭头函数:
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
解析 DOM 模板时的注意事项
部分HTML标签限制子元素的标签类型,
如<ul>内部只能是<li>,如果在里面用自定义组件则会渲染报错。
可以使用is特性,进行变通:
<table>
<tr is="blog-post-row"></tr>
</table>
但是⚠️,如果从以下来源使用模板的话,这条限制是不存在的:
- 字符串 (例如:
template: '...'
)✅ - 单文件组件 (
.vue
) <script type="text/x-template"> 一种定义模版的方式。
Class用在组件上
- 可以直接在组件上写:
<my-component class="baz boo"></my-component>
- 也可以用在v-bind:class='{active: isActive}'上面, 当isActive是true时,类加上active.
<p class="foo bar active">Hi</p>
|
深入Prop
camelCase vs kebab-case
当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>
当定义组件的时候,即在JavaScript中用:props: ['postTitle']
Prop类型:
- 使用字符串数组的形式:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
- 使用对象形式
- 使用对象形式:可以进行Prop的验证。
- type, 可以是任意类型null, 多个类型的任意一种(用数组)
- required: true/false 是否必须填
- default,默认可以是值,也可以是function(){ return ...}
- 自定义验证函数validator。
- 用到这个组件的时候,传入的数据必须符合prop指定的类型,并通过验证,否则browser的控制台会提示❌)
props: {
title: null, #type可以任意类型,用null
likes: [Number, String, Date] #可以设置多个类型。
isPublished: Boolean,
commentIds: Array,
author: [Object, Function, Symbol]
}
传递静态或者动态Prop类型:
需要用 v-bind, 即使数据是静态的,仍然需要 v-bind来告诉Vue:
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>
或者用一个变量进行动态赋值:
<blog-post v-bind:comment-ids="post.commentIds"></blog-post>
⚠️(即使加了双括号v-bind="xxxxx"这还是Javascript表达式)
可以传递任何类型的值给一个prop。每个类型略有使用区别:
- 传入一个数字:
- 传入一个boolean:
- 传入一个数组:
- 传入一个对象:
- 传入一个对象的所有属性。绑定对象名即可 v-bind:prop-name
父子组件中的prop的数据流动是单向的:
但⚠️: JS中对象和数组是通过引用传入的。所以对这两种类型的prop, 在子组件中改变这个对象或数组,改变的是对象和数组本身,因此会影响到父组件的状态。
非Prop的特性
一个非prop特性是:传向一个组件的特性,但该组件没有对应prop定义的特性。
这个非prop特性会添加到这个组件的根元素上。
因为组件的作者不能遇见组件会被什么场景使用,为了让组件更加的灵活,所以组件可以接受任意的特性。
禁用特性继承
Vue.component('my-component', {
inheritAttrs: false,
// ...
})
自定义事件
推荐你始终使用 kebab-case 的事件名。
自定义组件v-model: 见上面用v-model案例,或者api。其实就是语法糖。
将原生事件绑定到组件
在v-on上使用修饰符.native,它用于监听根元素上的原生事件。
base-input v-on:focus.native="onFocus"></base-input>
案例:
JS对象知识点)知识点:
A Javascript object is a collection of named values
Object是mutable: 对象的地址是引用的,不是by value。
JS variable是by value的。
Object.assign(target, ..sources) 返回一个新的Object,他的值包括target和sources,但不重复同时value会被覆盖。
vm.$listeners,
一个对象,包含父作用域(不含.native)的事件监听器。
可以通过v-on='$listeners'把它传入内部组件,即父组件上的事件监听,也可以用在内部组件中了。
vm.$attrs(没有案例,不是很懂。)
类型: { |key: string|: string}
详细: 包含了父作用域中不作为prop被识别获取的特性绑定。可以用v-bind='$attrs'传入内部组件。
inheritAttrs选项和$attrs是一对儿。同时出现在2.4版本
选项/其他 #inheritAttrs
类型 boolean
详细:当写包裹一个目标元素(或另一个组件)的组件时,目标元素(或子组件)不能得到父作用域的非prop特性绑定。这些特性会‘回退’并且作为普通的HTML特性应用在目标元素上(或子组件的根元素上)。
去掉这个默认行为,可以使用inheritAttrs: false。不继承属性。
想要继承特性绑定,可以使用v-bind='$attrs',把这些特性绑定到目标元素(非根元素)上。
inheritAttrs选项和$attrs是一对儿。同时出现在2.4版本
.sync修饰符 没有看。
<solt>插槽
通过slot可以分发内容。
Vue自定义的标签。见:插槽。
理解: 组件实例内的html会被保留。
<solt> 有一个特别的name特性,用于和其他solt区别
- 可以在父组件的<template>元素上使用slot的name特性
- 可以直接用在一个普通的元素上
插槽的默认内容
可以为solt提供默认内容。如在template中有一个插槽

- 如果组件实例内没有html内容就会使用插槽默认内容。
- 如果父组件为这个插槽提供了内容,默认的内容就会被替换掉。
编译作用域 (不是很理解,没有案例)
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
可以在组件实例中的插槽位置加上{{xxx}},来插入数据。
但不能访问url, 这属于<navigation-link>
的作用域。
- 父组件模板的所有东西都会在父级作用域内编译;
- 子组件模板的所有东西都会在子级作用域内编译。
作用域slot
处理边界情况(特殊情况)
访问元素/组件
有些特定情况,需要触及另一个组件实例内部,或手动操作DOM元素。
访问子组件实例/子元素
除了prop,event, 有时需要在JS中直接访问一个子组件。
方法:
通过使用ref特性,为子组件/元素赋予一个id引用。
例如,这个子组件:
<base-input ref="username"></base-input>
然后,用this.$refs.username来访问组件实例。
注意⚠️: ref的使用是非响应式的,并且只会在组件渲染完成后生效。所以尽量避免使用它。
可以用于从父组件聚焦一个输入框:
<input ref="input">
然后添加一个方法:
methods: {
// 用来从父级组件聚焦输入框
focus: function () {
this.$refs.input.focus()
}
}