Vue组件详解

文章目录

  • 1. 概述
  • 2. 全局注册
  • 3. 以插件方式定义全局组件
  • 4. 局部注册(单文件组件)
    • 自定义局部组件中的生命周期函数执行顺序
  • 5. 捕获子组件中的错误信息
  • 6. 动态组件


1. 概述

组件 (Component) 是 Vue.js 最强大的功能之一,它是html、css、js等的一个聚合体,封装性和隔离性非常强。

组件化:

  • 将一个具备完整功能的项目的一部分分割多处使用
  • 加快项目的进度
  • 可以进行项目的复用

组件注册分为:

全局注册和局部注册

2. 全局注册

语法:

Vue.component('组件名称', { }),第1个参数是标签名称,第2个参数是一个选项对象。全局组件注册后,任何vue实例都可以用。

组件注意事项:

  1. 构造 Vue 实例时传入的各种选项大多数都可以在组件里使用(el 不能使用),只有一个例外:data必须是函数,同时这个函数要求返回一个对象 ,保证数据唯一性,防止对象发生污染。
  2. 组件模板必须是单个根元素(html标签)
  3. 组件模板的内容可以是模板字符串
Vue.component('HelloWorld', {
  data: function(){
    return {
      msg: 'HelloWorld'
    }
  },
  template: '<div>{{msg}}div>'
});

注意:模板字符串的写法在 Vue 工程化中已被废弃。

应用:

现在我们想要自定义一个标签,并且显示“我是一个标题”的内容,该怎么做呢?

<div id="app">
    <mytitle>mytitle>
div>

<script>
    // 全局组件  一次定义,随时使用
    // 一但声明完成,就可以在所有的组件中直接使用,无需引入和注册
    Vue.component('mytitle', {
        // render 用于直接生成虚拟dom(生成标签)
        // 在工程化中,render中可以直接写jsx,在引入一个babel可以写jsx语法(js的增强版本)
        render(h) {
            // h(生成的标签名称,标签中有哪些属性(没有属性就是null),子元素是什么)
            let vnode = h('h3', { attrs: { name: 'abc', style: 'color:red' } }, '我是一个标签')
            return vnode
        }
    })
    const vm = new Vue({
        el: '#app',
        data: {
        }
    })
script>

Vue组件详解_第1张图片

注意:

  1. 自定义的标签,在vue项目中,称为组件
  2. 没有使用工程化时,我们使用浏览器解析标签,所以定义组件不能使用单标签写法,否则组件无法被多次执行,因为但标签写法浏览器在解析的时候觉得有问题,不再执行后续标签。而在工程化中,我们会使用很多包来编译html标签,单标签写法是允许的

使用模板字符串生成组件,并且实现组件嵌套:

<div id="app">
    <mytitle>mytitle>
div>

<script>
    // 定义的组件,它的元素必须要有一个顶层元素进行包裹,否则报错
    Vue.component('mytitle', {
        data() {
            return {
                title: '我是一个标题'
            }
        },
        template: `

{{title}}

aaaa

`
, // 组件中可以写方法 methods: { setTitle() { this.title = Date.now() } } }) // 又一个全局组件,可以嵌套 Vue.component('subtitle', { template: `

我是一个子标题

`
}) const vm = new Vue({ el: '#app', data: { } })
script>

Vue组件详解_第2张图片

注意:定义的组件,它的元素必须要有一个顶层元素进行包裹,否则报错

3. 以插件方式定义全局组件

这种方式,可以使得组件的使用更加灵活,当需要使用全局组件时,就通过插件插入,不用时,就移除。

<div id="app">
    
    <mytitle>mytitle>
div>

<script>
    const myTitleCmp = Vue => {
        Vue.component('mytitle', {
            data() {
                return {
                    title: '我是一个标题'
                }
            },
            template: `

{{title}}

aaaa

`
, methods: { setTitle() { this.title = Date.now() } } }) Vue.component('subtitle', { template: `

我是一个子标题

`
}) } // 通过插件的方式定义组件 Vue.use(myTitleCmp) const vm = new Vue({ el: '#app', data: { } })
script>

Vue组件详解_第3张图片

4. 局部注册(单文件组件)

<div id="app">
    <child>child>
div>

<script>
    // 创建局部组件,它就是一个对象
    // 局部组件,在创建完成后,如果你要给别人使用,一定要在配置中进行对应的配置
    const child = {
        data() {
            return {
                title: '我是一个标题'
            }
        },				
        template: `
			

{{title}}

我是一个自定义的局部组件

`
, } const vm = new Vue({ el: '#app', data: { }, // 局部组件配置 components: { // key就是在使用时的标签名称 // value就是对应的局部组件对象 // child: child // 简写 child } })
script>

Vue组件详解_第4张图片

自定义局部组件中的生命周期函数执行顺序

初始化阶段生命周期中,父组件和子组件的执行顺序:

<div id="app">
    <h3>{{title}}h3>
    <child>child>
div>

<script>
    const child = {
        data() {
            return {
                title: '我是一个标题'
            }
        },				
        template: `
			

{{title}}

我是一个自定义的局部组件

`
, beforeCreate() { console.log('child -- beforeCreate') }, created() { console.log('child -- created') }, beforeMount() { console.log('child -- beforeMount') }, mounted() { console.log('child -- mounted') } } const vm = new Vue({ el: '#app', data: { title: 'aaaa', }, // 局部组件配置 components: { child, }, beforeCreate() { console.log('parent -- beforeCreate') }, created() { console.log('parent -- created') }, beforeMount() { console.log('parent -- beforeMount') }, mounted() { console.log('parent -- mounted') setTimeout(() => { this.$destroy() }, 1000) } })
script>

Vue组件详解_第5张图片

更新阶段生命周期,父组件和子组件的执行顺序:

<div id="app">
    <h3>{{title}}h3>
    <button @click="setTitle">修改button>
    <child>child>
div>

<script>
    const child = {
        data() {
            return {
                title: '我是一个标题'
            }
        },				
        template: `
			

{{title}}

我是一个自定义的局部组件

`
, beforeUpdate() { console.log('child -- beforeUpdate') }, updated() { console.log('child -- updated') } } const vm = new Vue({ el: '#app', data: { title: 'aaaa', }, // 局部组件配置 components: { child }, beforeUpdate() { console.log('parent -- beforeUpdate') }, updated() { console.log('parent -- updated') }, methods: { setTitle() { this.title = Date.now() } } })
script>

Vue组件详解_第6张图片

注意:上面的代码中,子组件的更新阶段函数并没有执行,说明子组件没有被更新。原因是 Vue 会进行数据劫持,劫持完后的watch监听器会监听哪些组件发生了更新,哪个组件更新就改变哪个组件,所以父组件更新并不会引起子组件的更新,而在 react 中,父组件更新,父组件下的所有子组件都会发生更新。

那么如果父组件和子组件同时更新,更新阶段的生命周期函数的执行顺序是怎样的呢?

<div id="app">
    <button @click="setTitle">修改button>
    <child :ptitle="title">child>
div>

<script>
    const child = {
        // 父通过自定义属性向子传数据
        props: ['ptitle'],
        data() {
            return {
                title: '我是一个标题'
            }
        },				
        template: `
			

{{title}}---{{ptitle}}

`
, beforeUpdate() { console.log('child -- beforeUpdate') }, updated() { console.log('child -- updated') } } const vm = new Vue({ el: '#app', data: { title: 'aaaa', }, // 局部组件配置 components: { child }, beforeUpdate() { console.log('parent -- beforeUpdate') }, updated() { console.log('parent -- updated') }, methods: { setTitle() { this.title = Date.now() } } })
script>

Vue组件详解_第7张图片

销毁阶段生命周期,父组件和子组件的执行顺序:

<div id="app">
    <child>child>
div>

<script>
    const child = {
        data() {
            return {
                title: '我是一个标题'
            }
        },				
        template: `
			

{{title}}

`
, beforeDestroy() { console.log('child -- beforeDestroy') }, destroyed() { console.log('child -- destroyed') } } const vm = new Vue({ el: '#app', data: { title: 'aaaa', }, // 局部组件配置 components: { child }, mounted() { console.log('parent -- mounted') setTimeout(() => { this.$destroy() }, 1000) }, beforeDestroy() { console.log('parent -- beforeDestroy') }, destroyed() { console.log('parent -- destroyed') } })
script>

Vue组件详解_第8张图片

两兄弟元素,生命周期函数的执行顺序:

<div id="app">
    
    <child2>child2>
    <child>child>
div>

<script>
    const child = {
        data() {
            return {
                title: '我是一个标题'
            }
        },				
        template: `
			

{{title}}

`
, beforeCreate() { console.log('child -- beforeCreate') }, created() { console.log('child -- created') } } const child2 = { template: `
child2
`
, created() { console.log('child2 --- created') } } const vm = new Vue({ el: '#app', data: { title: 'aaaa', }, // 局部组件配置 components: { child, child2 } })
script>

Vue组件详解_第9张图片

5. 捕获子组件中的错误信息

<div id="app">
    <template v-if="!error">
        <child>child>
    template>
    
    <template v-else>
        <div>网站异常div>
    template>
div>

<script>

    // 对于警告也要进行捕获
    Vue.config.warnHandler = function (err, vm, info) {
        // console.log(err, vm, info)
        // 它只是一个警告,可以记录,也可以不记录,只要不在生产控制台中显示出来就可以
    }

    const child = {
        name: 'child',
        template: `

我是一个子组件 --- {{title}}

`
, created() { a } } // 在vue中有一个生命周期,它可以收集子孙组件中的错误信息,进行捕获 const vm = new Vue({ el: '#app', components: { child }, data: { error: false }, // 子孙组件错误的捕获,它要返回一个布尔类型,如果为false,则不向上传递错误了 // err具体的错误 此信息是最为关键 // vm发生错误的虚拟dom对象(哪一个组件错的) // info 相关提示信息 errorCaptured(err, vm, info) { // console.log(err, vm, info) // 得到错误信息,进行网络请求,发给服务器端,让他记录下面,然后我们在统一来处理问题 // fetch 可以通过ajax发送错误信息到后端,将错误信息统一记录下来,按照权重修复对应bug // 生产环境中,有时候报错,可能会有安全隐患(暴露服务器地址) this.error = true return false } })
script>

Vue组件详解_第10张图片

注意:

  1. 在 vue 中存在 errorCaptured 生命周期,它可以收集子孙组件中的错误信息,进行捕获。
  2. 得到错误信息,进行网络请求,发给服务器端,让服务器收集记录,然后我们按照权重修复对应 bug。
  3. 生产环境中,有时候报错,可能会有安全隐患(暴露服务器地址)。
  4. 警告,可以记录,也可以不记录,只要不在生产控制台中显示出来就可以。

6. 动态组件

概述:

通过使用保留的 元素,动态地绑定到它的is特性,我们让多个组件可以使用同一个挂载点,并动态切换。

应用:

<div id="app">
    
    <div>
        <button @click="cmp='login'">登录button>
        <button @click="cmp='reg'">注册button>
    div>
    <hr>
    
    <component :is="cmp">component>
div>

<script>
    const reg = {
        name: 'reg',
        data() {
            return {
                username: ''
            }
        },
        template: `
			

注册


`
} const login = { name: 'login', data() { return { username: '' } }, template: `

登录


`
} const vm = new Vue({ el: '#app', components: { reg, login }, data: { // cmp的值 ,作为动态组件中的is属性所用,所以它的值只能是components配置中的属性(reg或login) cmp: 'reg' } })
script>

Vue组件详解_第11张图片

缓存组件:

在上述代码中,当我们在登录组件和注册组件中分别执行生命周期函数created时,我们发现,每一次切换 tab 都会销毁切换前组件,新创建切换后组件。

<div id="app">
    <div>
        <button @click="cmp='login'">登录button>
        <button @click="cmp='reg'">注册button>
    div>
    <hr>
    <component :is="cmp">component>

div>

<script>
    const reg = {
        name: 'reg',
        data() {
            return {
                username: ''
            }
        },
        template: `
			

注册


`
, created() { console.log('reg --- created') } } const login = { name: 'login', data() { return { username: '' } }, template: `

登录


`
, created() { console.log('login --- created') } } const vm = new Vue({ el: '#app', components: { reg, login }, data: { // cmp的值 ,作为动态组件中的is属性所用,所以它的值只能是components配置中的属性(reg或login) cmp: 'reg' } })
script>

Vue组件详解_第12张图片

这意味着,如果我们在输入框中输入信息,则一切换组件,信息就没了。这时我们需要用到缓存组件。

<div id="app">
    
    <div>
        <button @click="cmp='login'">登录button>
        <button @click="cmp='reg'">注册button>
    div>
    <hr>
    
    
    
    
    
    <keep-alive>
        <component :is="cmp">component>
    keep-alive>

div>

<script>
    const reg = {
        name: 'reg',
        data() {
            return {
                username: ''
            }
        },
        template: `
			

注册


`
, created() { console.log('reg --- created') }, // 下面这两种生命周期钩子,只有标签中有keep-alive才有用 activated() { console.log('reg --- activated') }, deactivated() { console.log('reg --- deactivated') } } const login = { name: 'login', data() { return { username: '' } }, template: `

登录


`
, created() { console.log('login --- created') }, activated() { console.log('login --- activated') }, deactivated() { console.log('login --- deactivated') } } const vm = new Vue({ el: '#app', components: { reg, login }, data: { // cmp的值 ,作为动态组件中的is属性所用,所以它的值只能是components配置中的属性(reg或login) cmp: 'reg' } })
script>

Vue组件详解_第13张图片

注意:

  1. 缓存组件中的元素并没有被销毁,而是处于未被激活状态,tab 切换时的性能得到很大的提升
  2. 缓存组件带来的问题是,组件的初始化阶段生命周期函数只执行一次
  3. Vue 提供了 activated 和 deactivated 两个生命周期钩子函数用来判断组件处于激活状态还是未激活状态。在缓存组件中,虽然初始化生命周期钩子函数只在创建组件时执行一次,但是如果有了这两个钩子函数,我们的代码就可以通过这两个函数进行捕获
  4. activated 和 deactivated 只有标签中有keep-alive才有用
  5. keep-alive 标签有两个属性,include 和 exclude。

你可能感兴趣的:(Vue,vue.js,前端,javascript)