vue 组件

目录

一、创建一个 vue 实例(注意与创建组件区别开来)

二、创建组件

1、创建一个普通的 vue 组件

2、基于 “模块系统” 创建的 “单文件组件”

三、注册组件

1、局部注册

(1)、在 components 对象中局部注册组件

(2)、单文件组件中局部注册组件

2、全局注册

(1)、使用 Vue.component() 函数全局注册组件

(2)、单文件组件中全局注册组件

四、组件与插槽配合使用

五、组件的继承与混合

六、组件之间的通信

七、组件是如何更新的?(vue 的响应式原理 / 双向绑定原理)

八、vue 组件实例的生命周期

1、vue 生命周期的三个阶段

(1)、创建阶段

(2)、更新阶段

(3)、销毁阶段

2、Vue 父子组件的生命周期顺序

(1)、加载渲染过程 

(2)、子组件更新过程

(3)、父组件更新过程

(4)、销毁过程

3、缓存组件相关的两个特有的生命周期

(1)、activated

(2)、deactivated

4、捕获后代组件错误的特有的生命周期——errorCaptured


通常一个应用会以一棵嵌套的组件树的形式来组织。

一、创建一个 vue 实例(注意与创建组件区别开来)

var vm = new Vue({
    el: "#app",
    data() {
        return {
            
        }
    },
})

一个 vue 实例 与 一个 vue 组件的区别:

  • 一个 vue 组件的 data 选项必须是一个函数,以防污染全局变量,这样每个组件都可以维护一份被返回对象的独立的拷贝。
  • 一个 vue 实例 的 data 选项可以是一个对象。

二、创建组件

1、创建一个普通的 vue 组件

一个普通的 vue 组件,其实就是一个对象。

const componentA = {
  data () {
    return {
      msg: 'hello world'
    }
  },
  template: '<h3>{{msg}}</h3>'
}

2、基于 “模块系统” 创建的 “单文件组件

文件扩展名为 .vue 的文件都是 vue 的 “单文件组件”。

<template>
  <div>hello {{str}}</div>
</template>

<script>
export default {
  data () {
    return {
      str: 'world'
    }
  }
}
</script>

<style></style>

三、注册组件

组件的注册类型:全局注册 和 局部注册。

1、局部注册

局部注册组件的方式:

  • 在 components 对象中局部注册要用到的组件
  • 基于模块系统局部注册组件

(1)、在 components 对象中局部注册组件

<div id="app">
  <component-a></component-a>
</div>

<script>
  // 创建一个局部组件
  const componentA = {
    data () {
      return {
        msg: 'hellow'
      }
    },
    template: '<h3>{{msg}}</h3>'
  }

  // 创建 Vue 的一个根实例
  new Vue({
    el:'#app',
    data: {},
    components:{
      'component-a': componentA
    }
  })
</script>

(2)、单文件组件中局部注册组件

基于模块系统定义一个 componentA 组件:

<template>
  {{str}}
</template>

<script>
export default {
  name: 'componentA',
  data () {
    str: 'hello'
  }
}
</script>

局部注册并使用 componentA 组件: 

<template>
  <component-a></component-a>
</template>

<script>
import componentA from '../src/components/componentA.vue'

export default {
  components:{
    componentA
  }
}
</script>

【注意】

  • 局部注册的组件在其子组件中不可用。

2、全局注册

全局注册组件的方式:

  • 使用 Vue.component() 函数全局注册组件。
  • 模块系统中全局注册组件。

全局注册的组件,可以在 Vue 根实例和所有的子组件中使用。

(1)、使用 Vue.component() 函数全局注册组件

// 假设有一个组件 componentA,将 componentA 组件全局注册为 button-counter 组件
​const componentA = {
  data () {
    return {
      msg: 'hello world'
    }
  },
  template: '<h3>{{msg}}</h3>'
}

Vue.component("button-counter", componentA)

等价于:

Vue.component("button-counter", {
  data () {
    return {
      msg: 'hello world'
    }
  },
  template: '<h3>{{msg}}</h3>'
})

(2)、单文件组件中全局注册组件

可能你的许多组件只是包裹了一个输入框或按钮之类的元素,是相对通用的。我们有时候会把它们称为基础组件,它们会在各个组件中被频繁的用到。

所以会导致很多组件里都会有一个包含基础组件的长列表。例如:

<template>
  <base-input
    v-model="searchText"
    @keydown.enter="search"
  />
  <base-button @click="search">
    <base-icon name="search"/>
  </base-button>
</template>

<script>
import BaseButton from './BaseButton.vue'
import BaseIcon from './BaseIcon.vue'
import BaseInput from './BaseInput.vue'

export default {
  components: {
    BaseButton,
    BaseIcon,
    BaseInput
  }
}
</script>

如果你恰好使用了 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
        .split('/')
        .pop()
        .replace(/\.\w+$/, '')
    )
  )

  // 全局注册组件
  Vue.component(
    componentName,
    // 如果这个组件选项是通过 `export default` 导出的,
    // 那么就会优先使用 `.default`,
    // 否则回退到使用模块的根。
    componentConfig.default || componentConfig
  )
})

【拓展】

require.context() 是 webpack 的 一个 api。它用来获取一个特定的上下文,主要用来实现自动化导入模块,以优化从一个文件夹引入很多模块的情况——它会遍历文件夹中的指定文件,然后自动导入,而不需要每次显式的调用 import 导入模块了。

【典例】

引入所有的 api(存放接口):


const files = require.context('.', false, /\.js$/)
const modules = {}

function getFileName (name) {
  if (name.indexOf('-')) {
    const _name = name.toLowerCase().split('-')
    const len = _name.length
    for (let i = 1; i < len; i++) {
      _name[i] = _name[i].substring(0, 1).toUpperCase() + _name[i].substring(1)
    }
    return _name.join('')
  } else {
    return name
  }
}

files.keys().forEach(key => {
  if (key === './index.js') { return }
  const _key = key.replace(/(\.\/|\.js)/g, '')
  const humpName = getHumpName(_key)
  modules[humpName] = files(key)
})

export default modules

额外的补充——上述代码的核心算法是:遍历树形结构,将其转化为一个对象的形式输出。 

【注意】

  • 全局注册的行为,必须在根 Vue 实例 (通过 new Vue) 创建之前发生。
  • 非必要不使用全局注册组件——全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。

四、组件与插槽配合使用

详情请戳这里:vue 插槽 slot

五、组件的继承与混合

请戳这里:vue 组件的继承与混合

六、组件之间的通信

请戳这里:vue 组件之间的通信

七、组件是如何更新的?(vue 的响应式原理 / 双向绑定原理)

vue 核心——数据驱动 DOM(尽量避免直接操作 DOM)。

数据的来源:

  • 来自父元素的属性;
  • 来自组件自身的状态 data;
  • 来自状态管理器,如 vuex,vue.observable。

状态(data)与属性(props)

  • 状态是组件自身的数据,状态的改变未必触发更新;
  • 属性是来自父组件的数据,属性的改变也未必触发更新。

data 触发更新条件:数据写在 data 里,并 return 出去,同时,必须在 template 中有使用。

props 触发更新条件:若以对象接收,可以且必须在 template 中有使用;若以数组接收,还得在data 中转接一下,才能且必须在 template 中有使用。

vue 响应式更新:

vue 在实例化的时候,使用 Object.definePropery() 方法Proxy 构造函数,对 data 进行 getter 和 setter 的处理。在组件渲染时,若用到 data 里的某个数据,这个数据就会被依赖收集进 watcher 里。当数据更新,如果这个数据在 watcher 里,就会收到通知并更新,否则不会更新——vue 采用“数据劫持”+“观察者模式(发布者-订阅者模式)”相结合的方式实现了双向绑定——vue 的响应式原理

computed 和 watch 请戳此链接:vue 的计算属性(computed)和侦听器(watch)

八、vue 组件实例的生命周期

截止此次更新,vue 共有 11 个钩子函数:

vue 生命周期(钩子函数)执行时机
beforeCreate初始化数据
created数据初始化完成
beforeMount准备挂载数据
mounted数据挂载完毕,DOM 加载完成
beforeUpdate更新前的状态
updated更新完成的状态
activated被 keep-alive 缓存的组件激活时调用
deactivated被 keep-alive 缓存的组件失活时调用
beforeDestroy在实例销毁之前调用,实例仍然完全可用
destroyed在Vue 实例销毁后调用,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁
errorCaptured在捕获一个来自后代组件的错误时被调用

1、vue 生命周期的三个阶段

  • 创建阶段
  • 更新阶段
  • 销毁阶段

(1)、创建阶段

beforeCreate:初始化事件和生命周期

created:数据初始化完成,包括数据观测、属性、侦听器的配置等。

beforeMount:将模板编译到虚拟DOM上。

rander——生成虚拟DOM

mounted:处理“异步请求”、“操作DOM”、“定时器”等,但是在mounted中,vue不承诺子组件也会挂载到真实DOM上,为确保子组件已经挂载到真实DOM上,常常需要用this.$nextTick()在回调中对DOM进行一些操作。

【拓展】

vue获取后端数据放在created还是mounted方法里面?

(2)、更新阶段

beforeUpdate:移除已经添加的事件监听器等,不可以更改依赖数据(响应式数据),否则会造成死循环。

rander——生成虚拟DOM

updated:可以操作DOM,添加的事件监听器等,但是vue不承诺子组件也会挂载到真实DOM上,为确保子组件已经挂载到真实DOM上,常常需要用this.$nextTick()在回调中对DOM进行一些操作。另外,不可以更改依赖数据,否则会造成死循环。

(3)、销毁阶段

beforeDestroy:移除已经添加的事件监听器,计时器等。

destroyed:移除已经添加的事件监听器,计时器等。

2、Vue 父子组件的生命周期顺序

(1)、加载渲染过程 

父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

(2)、子组件更新过程

父beforeUpdate->子beforeUpdate->子updated->父updated 

(3)、父组件更新过程

父beforeUpdate->父updated

(4)、销毁过程

父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

3、缓存组件相关的两个特有的生命周期

(1)、activated

被 keep-alive 缓存的组件激活时调用。

(2)、deactivated

被 keep-alive 缓存的组件失活时调用。

4、捕获后代组件错误的特有的生命周期——errorCaptured

errorCaptured:在捕获一个来自后代组件的错误时被调用。

此钩子会收到三个参数:

  • 错误对象
  • 发生错误的组件实例
  • 一个包含错误来源信息的字符串。

此钩子可以返回 false 以阻止该错误继续向上传播。以防止该组件可能会进入一个无限的渲染循环。

【拓展】Vue 的错误处理机制——errorCaptured 与 config.errorHandler

浅出Vue 错误处理机制errorCaptured、errorHandler

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值