组件从注册方式分为全局组件和局部组件。

从功能类型又可以分为偏视图表现的(presentational)和偏逻辑的(动态生成dom),推荐在前者中使用模板,在后者中使用 JSX 或渲染函数动态生成组件模板内容,整体来说表现类的组件远远多于逻辑类组件。

定义组件

注意:组件名最好使用全小写加短横线,即使用 kebab-case (短横线分隔命名) 定义一个组件,在引用这个自定义元素时必须使用 kebab-case,

当使用 PascalCase (首字母大写命名) 定义一个组件时,在引用这个自定义元素时两种命名法都可以使用。

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

也就是说 <my-component-name><MyComponentName> 都是可接受的。

全局组件

Vue.component,在new根实例之前定义全局组件

// 定义一个名为 button-counter 的全局组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
}) //使用:
<button-counter></button-counter>

显示如图:

局部组件

全局注册的组件,对于不再使用的,仍会被包含在最终的构建结果中,造成了用户下载的 JavaScript 的增加。

局部组件有几种注册形式,这里介绍单文件组件。

一个单文件组件即一个vue文件,包含template(必须有),script,style三部分。

JavaScript、CSS 分离成独立的文件然后做到热重载和预编译。

<!-- my-component.vue -->
<template>
<div>This will be pre-compiled</div>
</template>
<script src="./my-component.js"></script>
<style src="./my-component.css"></style>

每个组件必须有且只有一个根元素,使用组件时,组件上的特性也会被添加到根元素上,根元素上已有的同名特性会被覆盖,除了class和style,会与组件的class和style合并,详细见下面“引用第三方库”

//com.vue
<template lang="html">
<div>//只能有一个html标签包裹里面所有节点
   child component{{name}}
</div>
</template>
<script>
export default{    
    data(){//data部分必须是个函数,返回一个对象。当前组件可以使用data里面的数据
      return {
       name:'com'
       }
    }
  }
</script>
<style lang="css">
</style>

引入单文件组件

//App.vue引入组件
import HelloWorld from "./components/HelloWorld";
//在页面实例注入组件
components: {
HelloWorld
},
//在页面使用组件
<HelloWorld />
//或者
<HelloWorld><HelloWorld />

组件的定义方法,参考边界处理之模板定义的替代品

关于template标签。

组件的复用

每用一次组件,就会有一个它的新实例被创建,每个组件都会各自独立维护自身属性,互相独立不干扰,这也是为什么组件的data是一个函数的原因:

<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>

父子组件

上面例子中,App.vue的components上挂载了HelloWorld这个组件,其中App.vue是父组件,HelloWorld是子组件。

父子组件通信

props(参考博文):父组件传递消息给子组件,为单向的,意味着应该在一个子组件内部改变 prop。

每次父组件发生更新时,子组件中所有的 prop 都将会刷新为最新的。

//子组件中声明props
<template lang="html">
<div>
年龄:{{age}}
</div>
</template>
<script>
export default{
props:['age'],
data(){
return{
name:'com'
}
}
}
</script> //组件特性传入prop值
<com age="19"></com>//静态数据
<com :age="ageNum"></com>//动态数据:变量

两种常见的试图改变一个 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 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。

$emit:子组件向父组件传递数据

使用.sync修饰符双向绑定父子组件的变量

比如下面,点击子组件的这个按钮(子组件接收的用户行为),触发组件自定义的patch事件,执行patch事件的处理函数msg3,改变父组件的ageNum数据

在子组件中代码:

<button type="button" name="button" @click="$emit('patch',[参数])">发送到父组件</button>

父组件中代码:

<com :age="ageNum" @patch='msg3'></com>//动态数据:变量
export default{
    data(){
      return {
        ageNum: 18
      }
    },
methods: {
  msg3(params){
     //params:子组件传递过来的参数 
    this.ageNum++
  }
 }
}

vm.$attrs 属性和vm.$listeners属性(单向传值,查阅博文:实例属性和方法

常用在跨级组件中,祖孙组件通信。

组件上使用v-model指令

查阅博文:v-model

组件上的原生事件

在一个组件的根元素上直接监听一个原生事件,使用 v-on.native 修饰符。

<base-input v-on:focus.native="onFocus"></base-input>

插槽和作用域插槽

查阅博文:slot

引用第三方库的组件

组件可以接受任意的特性,而这些特性会被添加到这个组件的根元素上。

例如,你通过一个 Bootstrap 插件使用了一个第三方的 <bootstrap-date-input> 组件,然后这个 data-date-picker="activated" 特性就会自动添加到 <bootstrap-date-input> 这个组件的根元素上:

<bootstrap-date-input
  data-date-picker="activated"
  class="date-picker-theme-dark"
></bootstrap-date-input>

该组件根元素为:

<input type="date" class="form-control">

替换/合并根元素已有特性

对于绝大多数特性来说,从外部提供给组件的值会替换掉组件内部设置好的值。所以如果组件上有 type="text" ,input的 type="date" 就会被替换掉并被破坏!classstyle 特性会把两边的值会被合并起来,最终的值:form-control date-picker-theme-dark

最终,input为:

<input
type="date"
data-date-picker="activated"
  class="form-control date-picker-theme-dark">

禁用特性继承

如果你希望组件的根元素继承特性,你可以在组件的选项中设置 inheritAttrs: false,该选项不会影响 styleclass 的绑定。适合配合实例的 $attrs 属性使用。

组件嵌套在有嵌套规则的标签里

有些 HTML 元素,诸如 <ul><ol><table><select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li><tr><option>,只能出现在其它某些特定的元素内部。

//这个自定义组件 <blog-post-row> 会被作为无效的内容提升到外部
<table>
<blog-post-row></blog-post-row>
</table>

通过is特性解决:

<table>
<tr is="blog-post-row"></tr>
</table>

动态组件

使用vue内置标签<component>,通过添加is特性来切换不同的组件。

比如:在一个多标签的界面中切换不同的组件(这个标签的位置渲染为那个组件)

<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

在上述示例中,currentTabComponent 可以是

  • 已注册组件的名字
  • 一个组件的选项对象(推荐)

当想保持组件被切换时的状态,以避免反复重渲染导致的性能问题。

比如下例,posts的第二个文章被选中,切换到 Archive 标签,然后再切换回 Posts,是不会继续展示之前选择的文章的。

需求:组件实例能够在它们第一次被创建的时候缓存下来。

方案:<keep-alive> 元素将动态组件包裹起来。

参考博文:vue内置标签

异步组件

在需要的时候才从服务器加载一个模块,Vue 提供一个工厂函数,resolve 回调会在从服务器得到组件定义的时候被调用。

Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {//setTimeout 是为了演示用的,如何获取组件取决于业务。
// 向 `resolve` 回调传递组件定义
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})

一个推荐的做法是将异步组件和 webpack 的 code-splitting 功能一起配合使用:

Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 `require` 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})

也可以在工厂函数中返回一个 Promise:

Vue.component(
'async-webpack-example',
// 这个 `import` 函数会返回一个 `Promise` 对象。
() => import('./my-async-component')
)

当使用局部注册的时候,你也可以直接提供一个返回 Promise 的函数:

new Vue({
// ...
components: {
'my-component': () => import('./my-async-component')
}
})

处理加载状态:

const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})

Vue Router 的路由组件中使用上述语法的话,必须使用 Vue Router 2.4.0+ 版本。

组件值之处理边界情况

介绍组件使用中遇到的一些问题:父子孙组件的访问,组件循环或者互相引用,模板其他的定义方式,强制更新(数组对象的变更检测注意事项,或者可能依赖了非响应式状态),在根元素使用v-once创建静态组件。

基础组件一次性引入

推荐创建一个 components 目录管理单文件组件。

针对基础组件的长列表的改善:

import BaseButton from './BaseButton.vue'
import BaseIcon from './BaseIcon.vue'
import BaseInput from './BaseInput.vue'
export default {
components: {
BaseButton,
BaseIcon,
BaseInput
}
}

使用webpack(或在内部使用了 webpack 的 Vue CLI 3+)的前提下,使用 require.context 全局注册这些基础组件。

以下代码放在应用入口文件 (比如 src/main.js) 中全局导入基础组件

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学习之组件的更多相关文章

  1. vue学习之四组件系统

    vue.js既然是框架,那就不能只是简单的完成数据模板引擎的任务,它还提供了页面布局的功能.本文详细介绍使用vue.js进行页面布局的强大工具,vue.js组件系统. 一.Vue.js组件系统 每一个 ...

  2. Vue学习之--------组件的基本使用(非单文件组件)(代码实现)(2022/7/22)

    文章目录 1.为啥要使用组件 2.基本使用 3.代码实例 4.测试效果 5.注意点 1.为啥要使用组件 好用啊.像堆积木一样 2.基本使用 Vue中使用组件的三大步骤: 一.定义组件(创建组件) 二. ...

  3. Vue学习之组件切换及父子组件小结(八)

    一.组件切换: 1.v-if与v-else方式: <!DOCTYPE html> <html lang="en"> <head> <met ...

  4. Vue学习笔记-组件通信-子传父(自定义事件)

    props用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中.我们应该如何处理呢?这个时候,我们需要使用自定义事件来完成.什么时候需要自定义事件呢?当子组件需要向父组件传递 ...

  5. Vue学习之--------组件嵌套以及VueComponent的讲解(代码实现)(2022/7/23)

    欢迎加入刚建立的社区:http://t.csdn.cn/Q52km 加入社区的好处: 1.专栏更加明确.便于学习 2.覆盖的知识点更多.便于发散学习 3.大家共同学习进步 3.不定时的发现金红包(不多 ...

  6. Vue 学习笔记 — 组件初始化

    简书 在vue中有3个概念很容易搞混,data,computed,props,特别是我们这些原后端开发人员. new Vue({ el: "#x", data: { id: 1 } ...

  7. vue学习笔记——组件的优化

    Vue 应用性能优化指南 1 给组件定义name,然后在同级目录新建index文件: import Count from './count.vue' export Count; 2 优化大数据的列表 ...

  8. 40.VUE学习之--组件之间的数据传参父组件向子组件里传参,props的使用实例操作

    父组件向子组件里传参,props的使用实例 <!DOCTYPE html> <html> <head> <meta charset="utf-8&q ...

  9. 43.VUE学习之--组件之使用.sync修饰符与computed计算属性超简单的实现美团购物车原理

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

随机推荐

  1. Jenkins之Job建立-运行本地脚本

    新建一个自由风格的项目,运行本地脚本 1.点击菜单栏中的“新任务” 2.进入该页面后输入一个项目名称,然后选择“构建一个自由风格的软件项目”,滑动到最底端,点击ok(在左下角) 3.进入下图页面后 “ ...

  2. 日志级别的选择:Debug、Info、Warn、Error

    日志信息分类 1.等级由低到高:debug<info<warn<Error: 2.区别: debug 级别最低,可以随意的使用于任何觉得有利于在调试时更详细的了解系统运行状态的东东: ...

  3. Eclipse中的快捷键

    Ctrl+1:快捷修复(数字 1 不是字母 l) 将鼠标悬停到出错区域,按 Ctrl+1,出现快捷修复的菜单, 按上下方向键选择一种修复方式即可. 也可以将光标移动到出错区域,按 F2 + Enter ...

  4. 多数据库有序GUID

    背景 常见的一种数据库设计是使用连续的整数为做主键,当新的数据插入到数据库时,由数据库自动生成.但这种设计不一定适合所有场景. 随着越来越多的使用Nhibernate.EntityFramework等 ...

  5. Linux内存管理 (26)内存相关工具

    1. vmstat 参照<Linux CPU占用率监控工具小结-vmstat> 2. memstat memstat可以通过sudo apt install memstat安装,安装包括两 ...

  6. System.Diagnostics.Process 测试案例

    1.System.Diagnostics.Process 执行exe文件 创建项目,编译成功后,然后把要运行的exe文件拷贝到该项目的运行工作目录下即可,代码如下: using System; usi ...

  7. 使用Node.js搭建数据爬虫crawler

    0. 通用爬虫框架包括: (1) 将爬取url加入队列,并获取指定url的前端资源(crawler爬虫框架主要使用Crawler类进行抓取网页) (2)解析前端资源,获取指定所需字段的值,即获取有价值 ...

  8. 为什么String被设计为不可变?是否真的不可变?

    1 对象不可变定义 不可变对象是指对象的状态在被初始化以后,在整个对象的生命周期内,不可改变. 2 如何不可变 通常情况下,在java中通过以下步骤实现不可变 对于属性不提供设值方法 所有的属性定义为 ...

  9. 数据降维之多维缩放MDS(Multiple Dimensional Scaling)

    网上看到关于数据降维的文章不少,介绍MDS的却极少,遂决定写一写. 考虑一个这样的问题.我们有n个样本,每个样本维度为m.我们的目标是用不同的新的k维向量(k<<m)替代原来的n个m维向量 ...

  10. vhdl when else

    在VHDL中,IF...THEN...ELSE是顺序语句,只能出现在行为描述中(进程体或者子程序中):而WHEN...ELSE是并行语句,可以直接出现在结构体中,但却不能出现在行为描述中.WHEN.. ...