Vue 的基本知识 组件
因为 组件是可复用的 Vue 实例 ,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等
一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝,如果 Vue 没有这条规则,一个组件实例会影响到其它所有实例
每个组件必须只有一个根元素
一. 组件切换
组件使用 is
属性
动态组件配合使用 keep-alive,保持这些组件的状态
<template>
<a href="" @click.prevent="conName='login'">登录</a>
<a href="" @click.prevent="conName='register'">注册</a>
// component 是一个占位符,:is 属性可用来指定要展示的组件的名称
<component :is="comName"></component>
</template>
comName
可以是:
- 已注册组件的名字
- 一个组件的选项对象
template:"<div>hello</div>"
注意: 常规 HTML 元素使用 is 属性 :这些元素将被视为组件
- 注意这里的
is="todo-item" attribute
。这种做法在使用 DOM 模板时是十分必要的,因为在<ul>
元素内只有<li>
元素会被看作有效内容,若在<ul>
元素内部使用<todo-item></todo-item>
会被作为无效的内容提升到外部,并导致最终渲染结果出错。
这样做实现的效果与<todo-item>
相同, 可以避开一些潜在的浏览器解析错误 。
- 但如果我们从以下来源使用模板的话,这条限制是 不存在 的:
- 字符串 (例如:template: '...')
- 单文件组件 (.vue)
<script type="text/x-template">
组件切换动画,使用 mode
属性
<template>
//通过 mode 属性,设置组件切换时候的 模式
<transition mode="out-in">
<component :is="comName"></component>
</transition>
</template>
<script>
new Vue({
el: 'xx',
data: {
comName: 'v-a'
},
components: {
'v-a': {
template: '<div>Component A</div>'
},
'v-b': {
template: '<div>Component B</div>'
}
}
})
</script>
二、组件注册
全局注册这些非常通用的基础组件,在应用入口文件 (比如 src/main.js) 中全局导入基础组件的示例代码:
三、组件的属性和 prop
class 属性
在一个自定义组件上使用 class 属性时,这些 class 将被添加到该组件的根元素上面,该元素上已经存在的 class 不会被覆盖
<my-component class="baz boo"></my-component>
<my-component :class="{ active: isActive}"></my-component>
- 禁用 attribute 继承
不希望组件的根元素继承 attribute,你可以在组件的选项中设置
inheritAttrs: false
,但不会影响style
和class
的绑定。<script> Vue.component('my',{ inheritAttrs: false }) </script>
有了
inheritAttrs: false
和$attrs
,你就可以手动决定这些 attribute 会被赋予哪个元素<script> vue.component('base-input' , { inheritAttrs: false, props: ['label' , 'value'], template:` <label> <input v-bind="$attrs" v-bind:value="value" v-on:input="$emit('input', $event.target.value) > </label> ` }) </script>
- 禁用 attribute 继承
ref 属性
注意: $refs 只会在组件渲染完成之后生效,并且它们不是响应式的 。这仅作为一个用于直接操作子组件的“逃生舱”,应该避免在 模板 或 计算属性 中访问 $refs
- 若给标签绑定 ref='xxx'属性,使用 this.$refs.xxx 获取原生的 jsDOM 对象,ref 属性值不能重名
- 若是给组件绑定 ref 属性,那么 this.$refs.xxx 获取的是当前的组件对象,可使用该组件的 data 和 methods 等中的数据、方法
- 特殊情况 :
$nextTick()
是在 DOM 更新循环结束之后执行回调函数,在修改数据之后使用此方法,在回调中获取到更新之后的 DOM<script> mounted(){ this.$nextTick(function(){ this.$refs.input.focus(); }) } </script>
组件的 Prop
- 以字符串数组形式列出的 prop:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
- 以字符串数组形式列出的 prop:
2. 以对象形式列出 prop:
```
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
```
传入一个对象的所有 property:
使用不带参数的 v-bind (取代 v-bind:prop-name)
Prop 验证
这些 prop 会在一个组件实例创建之前进行验证,所以实例的 property (如 data、computed 等) 在 default
或 validator
函数中是不可用的
<script>
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;
},
},
},
},
});
</script>
type 可以是下列原生构造函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
type 还可以是一个自定义的构造函数 ,并且通过 instanceof
来进行检查确认
<script>
function Persion(firstName, lastName){
this.firstName = firstName;
this.lastName = lastName;
}
//可以使用 Persion 老验证 author prop 的值是否通过 new Persion 创建的
Vue.component('blog-post',{
props:{
author:Persion
}
})
</script>
四、render 函数、组件修饰符
1. render 函数
vue 实例的 components
可用 render
函数代替,但是 render 会把 el 指定的容器中,所有的内容都清空覆盖,所以不要把路由的 router-view 和 router-link 直接写到 el 所控制的元素中
- .native 修饰符
在一个组件的根元素上监听一个原生事件 .native 将原生事件绑定到组件
- .sync 修饰符
对一个 prop 进行“双向绑定” .sync 修饰符
五、异步组件
以一个工厂函数的方式定义组件
一个推荐的做法是将异步组件和 webpack 的 code-splitting 功能 一起配合使用:
<script>
Vue.component('async-webpack-example', function(resolve){
//这个特殊的 "require" 语法将告诉 webpack
// 自动将你的构建代码切割成多个包,这些包会通过 AJAX 请求加载
require(['./my-async-component'], resolve);
})
</script>
也可以在工厂函数中返回一个 Promise,所以把 webpack 2 和 ES2015 语法加在一起,我们可以这样使用动态导入:
<script>
Vue.component(
'async-webpack-example',
// 这个动态导入会返回一个 “promise”对象
()=> import('./my-async-component')
)
</script>
局部注册 的时候,你也可以直接提供一个返回 Promise 的函数:
<script>
new Vue({
components:{
'my-component':()=> import('./my-async-component')
}
})
</script>
- 异步组件工厂函数也可以返回一个如下格式的对象来处理加载状态:
const AsyncComponent = ()=>({ // 需要加载的组件(应该是一个 Promise 对象) component: import('./MyComponent.vue'), // 异步组件加载时使用的组件 loading: LoadingComponent, // 加载失败时使用的组件 error: ErrorComponent, // 展示加载时组件的延时时间。默认 200ms delay:200, //如果提供了超时时间且加载也超时了 // 则使用加载失败时使用的组件。默认值:Infinity timeout: 3000 })
组件之间的循环引用
A 依赖 B,但是 B 又依赖 A,但是 A 又依赖 B,如此往复。这变成了一个循环,不知道如何不经过其中一个组件而完全解析出另一个组件。为了解决这个问题,我们需要给模块系统一个点,在那里“A 反正 是需要 B 的,但是我们不需要先解析 B
把 <tree-folder>
组件设为了那个点。我们知道那个产生悖论的子组件是 <tree-folder-contents>
组件,所以我们会 等到生命周期钩子 beforeCreate 时去注册它 :
<script>
new Vue({
beforeCreate: function () {
this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default
}
})
</script>
或者,在本地注册组件的时候,你可以使用 webpack 的异步 import:
<script>
new VUe({
components: {
TreeFolderContents: () => import('./tree-folder-contents.vue')
}
})
</script>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: Vue 组件的生命周期
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论