• 426 vue组件


    组件 (重难点)

    一、组件

    • 组件可以看作是一些可复用的ui模块
      • 小到一个标签 : <div>哈哈</div>
      • 大到一个页面 :<div><div><div><div><div></div></div></div></div></div>
    • 一个组件对应 一个实例
    • 组件 === Vue实例 == new Vue ( options )
    • 官网 : 组件是可复用的 Vue 实例

    二、组件化开发

    • 概念 :将一个完整的页面,抽离成一个个独立的组件,最终,通过这一个个独立组件完成整个的页面(项目)的功能
    • 组件化开发的优势/作用 : 复用

    三、组件的基本使用

    先注册, 再使用

    • Vue 中的两种注册组件的方法

      1.全局注册

      2.局部注册

    全局组件在所有的vue实例中都可以使用
    局部组件在所有的当前实例中可以使用
    
    • 注册全局组件 - 基本使用
    /**
     * 第一个参数 : 组件名
     * 第二个参数 : 是一个配置对象, 该配置对象与 Vue 实例的配置对象几乎完全相同
     *             也就是说,vue实例中用到的配置项,和组件中的配置项几乎相同
     */
    Vue.component('child', {
      template: `<h1 class="red">这是child组件</h1>`
    })
    
    • 注意点
      • 注册全局组件也是放到 vm 实例之前
      • 模板只能有一个根节点
      • 组件的配置项和 vue 实例 的配置项一样 (如:data、methods、filters、watch、computed、钩子函数等)
      • 组件的 data 是一个函数 , 并且返回一个对象
    // 演示为什么vue在组件中的数据采用函数,而不是对象
    // 原因 : 只想让组件复用,不想让数据复用
    var Component = function() {}
    // 使用对象
    Component.prototype.data = {
        demo: 123
    }
    // 使用函数
    Component.prototype.data = function() {
        return {
            demo: 111
        }
    }
    var component1 = new Component()
    var component2 = new Component()
    component1.data().demo = '8888'
    console.log(component2.data().demo) // 456
    
    • 使用组件
      • 当标签一样使用 <child></child>

    demo:组件介绍.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <!-- 
         组件
         1. 先注册
            - 全局组件: 所有的vue实例里都可以使用
            - 局部组件: 只能在当前注册组件的vue实例里使用
         2. 再使用 : 当成一个标签来使用
       -->
    
        <div id="app">
            <one></one>
        </div>
        <script src="./vue.js"></script>
        <script>
            //注册全局组件
            // 参数1 : 组件的名称
            // 参数2 : 组件的配置项 methods watch data computed 钩子函数
            Vue.component('one', {
                template: `<div>我是注册的组件</div>`,
                computed: {},
                watch: {},
                methods: {}
            })
    
            const vm = new Vue({
                el: '#app',
                data: {}
            })
        </script>
    </body>
    
    </html>
    

    demo:组件的注意事项.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
            <two></two>
        </div>
        <script src="./vue.js"></script>
        <script>
            /**
             * 1. 注册组件要在 vm 实例之前注册
             * 2. template 只能有一个根节点
             * 3. 组件里的data 是一个函数  ,不是一个对象 (下次来讲为什么)
             */
    
    
            // 注册组件
            Vue.component('two', {
                template: `
                    <div>
                        <h1>我是h1</h1>
                        <h1>我也是h1 {{ msg }}</h1>
                    </div>
                `,
                data() {
                    return {
                        msg: '123'
                    }
                }
            })
    
    
            const vm = new Vue({
                el: '#app',
                data: {}
            })
        </script>
    </body>
    
    </html>
    

    image-20200318170844578


    02-为啥【组件的】data是一 个函数,而不是一个对象.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <script>
            // 组件 : 复用
            // 我们想让组件复用,但是不想让组件里的数据复用
    
            // 组件构造函数
            const Component = function() {}
    
            // 原型上加数据
            // Component.prototype.data = {
            //   msg: '傻春'
            // }
    
            // 调用函数,返回对象,随便调用者怎么修改,都不影响原型上原来的函数里的对象
            Component.prototype.data = function() {
                return {
                    msg: '傻春'
                }
            }
    
            // 实例化
            const c1 = new Component()
            const c2 = new Component()
    
            // 修改c1的数据
            c1.data().msg = '小傻春'
    
            // 获取c2 的数据
            console.log(c2.data().msg)
        </script>
    </body>
    
    </html>
    

    四、改造 TodoMVC 成 组件化结构

    • 下载模板 todomvc-at-template + 安装css依赖包 + 清除不要的 + 安装 vue + 实例化
    • 创建一个 components 文件夹
    • 具体步骤
      • 创建一个 todo-head.js
      • 导入 <script src="./components/todo-head.js"></script>
      • 使用 <todo-head></todo-head>

    五、组件通讯 (介绍)

    导入 : 演示子组件访问父组件数据,发现报错 【父组件访问子组件数据,也报错】

    • 组件是一个独立、封闭的个体
      • 也就是说 : 默认情况下,组件中的数据, 只能在组件内部使用,无法直接在组件外部使用
      • 可以将 vue 实例看做一个组件
    • 对于组件之间需要相互使用彼此的情况,应该使用 组件通讯 机制来解决
    • 组件通讯的三种情况 :
      1. 父组件将数据传递给子组件(父 -> 子)
      2. 子组件将数据传递给父组件 (子 => 父)
      3. 非父子组件(兄弟组件)

    03-组件的通信.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
            <!-- 报错 -->
            <div>{{ cmsg }}</div>
            <child></child>
        </div>
    
        <script src="./vue.js"></script>
        <script>
            /**/
            /**
              * 组件是独立 封闭的个体, 
                组件之间是不能直接访问数据的 , 根据组件之间的通信机制去访问
    
                通信机制
                1. 父组件把数据传递给子组件 (父传子)
                2. 子组件把数据传递给父组件 (子传父)
                3. 非父子之间传递数据 (兄弟级)
            */
    
            // 注册组件
            // 子组件 【因为child嵌套在 id="app"的组件内,所以child是子组件,id="app"的组件是父组件。】
            Vue.component('child', {
                template: `
                    // 报错
                    <div> 子组件 : {{ pmsg }}  </div>
                `,
                data() {
                    return {
                        cmsg: '子组件的信息'
                    }
                }
            })
    
            // 父组件
            const vm = new Vue({
                el: '#app',
                data: {
                    pmsg: '父组件的数据'
                }
            })
        </script>
    </body>
    
    </html>
    

    六、父 ==> 子 (重点) 两步

    1. 通过属性, 父组件将要传递的数据,传递给子组件
    <child :msg="pmsg"></child>
    
    1. 子组件通过 props 配置项,来指定要接收的数据
    props: ['msg']
    
    // 以后使用
    - 组件内 :  msg
    - 事件中 : this.msg
    
    

    完善 TodoMVC => 完成 传值 + 渲染列表页


    04-父传子.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <!-- 
            父传子 父组件的数据传递给子组件,
            父传子 (两步)
            1. 通过属性, 父组件把数据传递给子组件  :msg='pmsg'
            2. 子组件通过props配置项, 指定一下要接收过来的数据 props:['msg']
    	-->
    
        <div id="app">
            <!-- 第一步 : 通过属性, 父组件把数据传递给子组件 -->
            <child :msg="pmsg"></child>
        </div>
    
        <script src="./vue.js"></script>
        <script>
            // 子组件
            Vue.component('child', {
                template: `
                  	<div> 子组件 : {{ msg }} </div>
                 `,
                // 第二步 : 子组件通过 props 配置项 指定一下要接收过来的数据
                props: ['msg']
            })
    
            // 父组件
            const vm = new Vue({
                el: '#app',
                data: {
                    pmsg: '父组件里的信息'
                }
            })
        </script>
    </body>
    
    </html>
    
    

    七、子 ==> 父 (重点) 三步 【子传父】

    1. 父组件中提供一个方法
    pfn(arg) {
        console.log('父组件中接受到子组件传递过来的数据:', arg)
     }
    
    
    1. 通过自定义事件, 父组件将这个方法传递给子组件
    // 自定义事件 <child @fn="pfn"></child>
    
    
    1. 子组件调用这个方法( 触发父组件中传递过来的自定义事件 )
    // 在钩子函数里演示也可以,自己调用
     created() {
        // 调用父组件中的方法 pfn
        // 注意:通过 $emit 方法来触发事件 fn
        // 第一个参数:表示要触发的自定义事件名称,也就是 @fn
        // 第二个参数:表示要传递给父组件的数据
        this.$emit('fn', 'child msg')
    }
    
    

    完善 TodoMVC => 完成 传值 +添加+ 删除+修改数据+清除完成


    05-子传父.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <!-- 
            子传父  子组件把数据传递给父组件  
            三步骤
            1. 父组件准备一个方法  pfn(){}
            2. 通过自定义事件, 把方法传递给子组件  @fn='pfn'
            3. 子组件通过$emit触发事件,等同于调用了这个方法
               this.$emit('fn','子组件里的数据')
          -->
        <div id="app">
            <!-- 第二步 : 通过自定义事件 把方法传递给子组件  -->
            <!-- 例子  @click="f" -->
            <child @fn="pfn"></child>
        </div>
    
        <script src="./vue.js"></script>
        <script>
            // 子组件
            Vue.component('child', {
                template: `
                    <div> 子组件 :  </div>
                `,
                created() {
                    // 例子
                    // @click='f'
                    // 1. 点击 => 调用f
                    // 2. 手动触发 this.$emit('click') => f
    
                    // 第三步 : 子组件里面触发这个事件,就调用了pfn
                    // $emit(参数1:事件  参数2..:传递的数据)
                    this.$emit('fn', '我是子组件里的数据')
                }
            })
    
            const vm = new Vue({
                el: '#app',
                data: {},
                // 第一步: 父组件准备好一个方法
                methods: {
                    pfn(res) {
                        console.log('pfn调用了:', res)
                    }
                }
            })
        </script>
    </body>
    
    </html>
    
    

    八、目前为止保存属性的地方

    1. data
    2. 计算属性
    3. props

    九、单向数据流(组件与组件之间)

    所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定

    父级 prop 的更新会向下流动到子组件中,但是反过来则不行。

    这样会防止从子组件意外改变父级组件的状态【数据】,从而导致你的应用的数据流向难以理解。

    【如果不是这样,多个子组件共用一个父组件的数据时,一个子组件改变了父组件的数据,其他子组件的数据也会同时改变。】

    • 透漏三个问题 :
      • 组件与组件之间是单向数据流 【父组件与子组件、不同的组件之间。】
      • 父级 prop 的更新会向下流动到子组件中
      • 子组件不允许修改父组件传过来的prop数据
    1. 单向数据流 (组件与组件之间)
    2. 双向数据绑定 (V <==> M 同一个组件 ) v-model
    3. 面试: vue 是单向还是双向的 ? 单向
    
    

    十、props 的特点 : 只读

    • 演示验证 props 只读
      • 传的是简单类型 : 修改会报错
      • 传的复杂类型 (地址) : 修改不会报错,是因为地址没有变 ,测试 obj={}立马报错 【修改对象中的数据,不会修改对象的地址,但是修改对象的地址就报错。】
    • 修改父组件传给子组件的数据

    思路 : 把接收过来的数据,保存到 data 中一个临时值 (适用在该组件接收数据只会在当前组件内使用)

      Vue.component('child', {
            template: `
          <div>子组件 {{ cmsg }} </div>
           `,
            data() {
              return {
                cmsg: this.msg
              }
            },
            props: ['msg'],
            created() {
              this.cmsg = 666
            }
          })
    
    

    完善TodoMVC => 修改状态 + 修改任务


    08-prop的只读特性.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
            <child :msg="pmsg" :obj="pobj"></child>
        </div>
        
        <script src="./vue.js"></script>
        <script>
            Vue.component('child', {
                template: `
                    <div> 子组件 : {{ obj.name }}</div>
                `,
                props: ['msg', 'obj'],
                mounted() {
                    // 修改传递过来的数据
                    // 1. 基本类型
                    // this.msg = 666
    
                    //2. 只是改了对象里的内容 ,并没有改地址
                    // this.obj.name = '春春'   虽然这不会报错,但是也不要这么改
                    this.obj = {}
                }
            })
    
            const vm = new Vue({
                el: '#app',
                data: {
                    pmsg: '父的信息',
                    pobj: {
                        name: '张三'
                    }
                }
            })
        </script>
    </body>
    
    </html>
    
    

    image-20200319093751232

    image-20200319094145727

    image-20200319094559203


    十一、prop 的大小写

    • 官 : HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。

      • html 的标签和 属性 都是一样,忽略大小写
      • <H1 TITLE="哈哈">我是h1</H1>
    • 官 : 这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名不好使了

      • <child :cMsg="pmsg"></child> 会报警告,父传子也接收不到了
      • 原因是 : 接收的属性是:cMsg, 因为忽略大小写,已为 : cmsg
      • 所以已经准备要读取的 是 cmsg 的值,否则要报警告
        You should probably use "c-msg" instead of "cMsg".
    • 方式 1 : 全用小写,不要使用驼峰命名 (不推荐)

      • 接收 : cmsg
      • props/读取 :cmsg
    • 方式 2 官 : 需要使用其等价的 kebab-case (短横线分隔命名) 命名: (推荐)

      • 接收 : :c-msg='pmsg'
      • props/读取 : cMsg / this.cMsg
    • 大小写在 父传子和 子传父中的应用 (都是要 带 - 的)

      • 父传子 : :c-msg ==> cMsg 改驼峰 - 因为props
      • 子传父 : @todo-head = 'pAddTodo' ==> this.$emit('todo-head') 不改驼峰
    • 完善 TodoMVC : 底部隐藏+剩余完成数+清除完成

      • 计算属性 : 已知值(todoList 在 根组件) ==> 得到一个新值(子组件里使用)
      • 父 => 子通讯
    • 番外篇 : 方法当属性传、传过来的带:得到的原型


    09-prop的大小写问题.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <!-- 
           原因 :
           1. cMsg  => cmsg
    
           解决办法 :
           1. 用小写 (不推荐)
           2. 短横线分割符
             赋值   :c-msg='pMsg'
                   -删除掉 -后面的首字母变大写 cMsg
             指定 : cMsg
             使用 : cMsg
         -->
        <div id="app">
            <child :c-msg="pMsg" :c-user-name="pUserName"></child>
        </div>
    
        <script src="./vue.js"></script>
        <script>
            Vue.component('child', {
                template: `
                    <div> 子组件 : {{ cMsg }}  {{ cUserName }} </div>
                 `,
                props: ['cMsg', 'cUserName']
            })
    
            const vm = new Vue({
                el: '#app',
                data: {
                    pMsg: '父的信息',
                    pUserName: '大傻春'
                }
            })
        </script>
    </body>
    
    </html>
    
    

    10-大小写问题应用在子传父和父传子.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <!-- 
           (prop的)大小写问题 【是prop的大小写问题,不是事件的。 】
           1. 父传子  c-msg  =>  cMsg
           2. 子传父 @add-todo   ==> add-todo
         -->
        <div id="app">
            <child :c-msg="pMsg" @add-todo="pAddTodo"></child>
        </div>
    
        <script src="./vue.js"></script>
        <script>
            Vue.component('child', {
                template: `
                    <div> 子组件 :  {{ cMsg }} </div>
                `,
                props: ['cMsg'],
                created() {
                    this.$emit('add-todo')
                }
            })
    
            const vm = new Vue({
                el: '#app',
                data: {
                    pMsg: '父的信息'
                },
                methods: {
                    pAddTodo() {
                        console.log('哈哈')
                    }
                }
            })
        </script>
    </body>
    
    </html>
    
    

    十二、prop的类型问题

    11-prop的类型问题.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <!-- 
           1. 通过prop 赋值的时候, 如果直接赋值一个静态值, 不管是什么值, 都是字符串类型
            'abc' / '123' / 'true'
           2. 在 属性前面加一个冒号“:” , 可以获取它的真实类型
    
            总结 :
            1.  :msg='pmsg/动态值/data里属性'  
            2.  :msg='固定值/静态值' : 读取固定值的真实类型,赋值给 msg    
    
         -->
    
        <div id="app">
            <child :msg="true" :na="name"></child>
        </div>
    
        <script src="./vue.js"></script>
        <script>
            Vue.component('child', {
                template: `
                <div> 子组件 :  </div>
               `,
                props: ['msg'],
                created() {
                    // 'abc'  => 字符串
                    // '123'  => '123' 字符串
                    console.warn(this.msg, typeof this.msg)
                }
            })
    
            const vm = new Vue({
                el: '#app',
                data: {
                    name: 123
                }
            })
        </script>
    </body>
    
    </html>
    
    

    十三、prop类型的校验问题

    12-类型的校验问题.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
            <!-- 大傻春拿去用了 -->
            <!-- <child :msg="111"></child> -->
            <child></child>
        </div>
        <script src="./vue.js"></script>
        <script>
            // 这个组件是我注册的
            Vue.component('child', {
                template: `
            		<div> 子组件 :  </div>
              	`,
                props: ['msg'],
    
                // props: {
                //   msg: Number // 仅仅是告诉其类型
                // },
                // props: {
                //     msg: {
                //         type: Number, // 类型
                //         default: 100 // 默认值
                //     }
                // },
                created() {
                    // 我希望的是一个数字类型
                    console.log(this.msg, typeof this.msg)
                    console.log(this.msg + 10) // 133
                }
            })
    
            const vm = new Vue({
                el: '#app',
                data: {}
            })
        </script>
    </body>
    
    </html>
    
    <!-- 
       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
          }
        }
      }
    })
     -->
    
    

    十四、非父子之间通讯 ( 组件 => 组件 ) (重点)

    需求 : 组件 jack ===> 恁弄啥哩 ===> 组件 rose

    • 是通过 事件总线 (event bus 公交车) 的机制 来实现的
    • 事件总线 : 实际上就是一个 空Vue实例
    • 可以实现任意两个组件之间的通讯,而不管两个组件到底有什么样的层级关系
    • 看图
    • 示例 :
    // 第一步 : 事件总线
    var bus = new Vue()
    
    // 第二步 : 发送数据   可在点击事件里 触发事件
    // 参数1 : 唯一标识  参数2:参数
    bus.$emit('todo', '恁弄啥哩?')
    
    // 第三步 : 接收数据    可在 created 里 注册事件
    bus.$on('todo', arg => {
      console.log('接收过来的', arg)
    })
    
    

    image-20200319193728198


    01-非父子之间的通信.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <!-- 
           需求 : jack 给 rose 发一条消息 : i jump u jump
    
           非父子之间的通信通过 事件总线 (event bus ) 来实现
           1. 创建事件总线 (bus) : 一个空的vue实例   const bus = new Vue()
           2. 发送数据 触发事件: bus.$emit('jump', XXXX) 【通过事件总线触发事件】
           3. 接收数据 注册事件: bus.$on('jump, res => { })  【通过事件总线注册事件】
         -->
    
        <div id="app">
            <div>
                <div>
                    <div>
                        <div>
                            <jack></jack>
                        </div>
                    </div>
                </div>
            </div>
    
            <div>
                <div>
                    <div>
                        <rose></rose>
                    </div>
                </div>
            </div>
        </div>
    
        <script src="./vue.js"></script>
        <script>
            //1. 事件总线
            const bus = new Vue()
    
            // jack
            Vue.component('jack', {
                template: `<div @click='send'>jack 组件</div>`,
                methods: {
                    send() {
                        console.log('send');
                        //2. 发送数据, 触发事件, 发送 一起 jump 的话
                        // 【代码的执行顺序:先注册,后触发,和传统的DOM操作一样。这个jump事件,只有点击上面的div时,才触发。下面注册的jump,是created阶段就触发。】
                        bus.$emit('jump', 'i jump u jump')
                    }
                }
            })
    
            // rose
            Vue.component('rose', {
                template: `<div>rose 组件</div>`,
                created() {
                    //3. 接收数据 注册事件 【代码的执行顺序:先注册,后触发,和传统的DOM操作一样。回调函数只有触发jump事件时才会执行。】
                    console.log('on')
                    bus.$on('jump', res => {
                        console.log('rose接收到的:', res)
                    })
                }
            })
    
            const vm = new Vue({
                el: '#app',
                data: {}
            })
        </script>
    </body>
    
    </html>
    
    

    十五、开关灯案例

    02-案例:开关灯(模板).html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>Document</title>
        <style>
            /* 容器 */
            
            .container {
                 150px;
            }
            /* 灯 */
            
            .light {
                 100px;
                height: 100px;
                border-radius: 50%;
                text-align: center;
                line-height: 100px;
                margin: 0 auto;
                color: #fff;
                background-color: #000;
            }
            /* 开灯 */
            
            .turn-on {
                background-color: #ff0;
                color: #000;
            }
            /* 灯座 */
            
            .bottom {
                 150px;
                height: 50px;
                margin-top: 10px;
                line-height: 50px;
                text-align: center;
                color: #fff;
                background-color: #000;
            }
        </style>
    </head>
    
    <body>
        <div id="app" class="container">
            <light-head></light-head>
            <light-bottom></light-bottom>
        </div>
        <script src="./vue.js"></script>
        <script>
            //1. 事件总线
            const bus = new Vue()
    
            // 创建灯泡组件
            Vue.component('light-head', {
                // 通过控制台查看,当isLight为true时, div的class同时有 light、turn-on
                template: `<div class="light" :class="{ 'turn-on' : isLight }">我是一盏灯</div>`,
                data() {
                    return {
                        isLight: false
                    }
                },
                created() {
                    //3. 接收数据 注册事件
                    bus.$on('light', res => {
                        console.log(res)
                        this.isLight = res
                    })
                }
            })
    
            // 创建灯座组件
            Vue.component('light-bottom', {
                template: `
                    <div class="bottom">
                        <button @click='turnOn'>开</button>
                        <button @click='turnOff'>关</button>
                    </div>
                `,
                methods: {
                    // 开灯
                    turnOn() {
                        // 2. 发送数据  触发事件
                        bus.$emit('light', true)
                    },
                    // 关灯
                    turnOff() {
                        //3. 发送数据 触发事件
                        bus.$emit('light', false)
                    }
                }
            })
    
            var vm = new Vue({
                el: '#app',
                data: {}
            })
        </script>
    </body>
    
    </html>
    
    

    十四、注册局部组件

    • 局部组件只能在当前vue实例中使用
    • 示例 :
    // 注册局部组件:
    components: {
      // child表示组件名称
      // 值为配置对象,与 Vue.component 中的第二个参数相同
      // 注意:子组件child属于 Vue实例,因此,只能在 Vue实例的模板中使用
      child: {
        template: `
                <div>这是局部组件 child</div>
              `
      }
    }
    
    

    03-注册局部组件.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <div id="app">
            <child></child>
            <one></one>
            <two></two>
        </div>
    
        <script src="./vue.js"></script>
        <script>
            //  全局组件
            //  Vue.component('child',{
            //   template:`
            //   <div> 子组件 :  </div>
            //    `
            //  })
            // Vue.filter()
    
            // 局部组件
            const child = {
                template: `<div>我是局部组件child: {{ msg }}</div>`,
                data() {
                    return {
                        msg: '哈哈'
                    }
                }
            }
    
            const one = {
                template: `<div>one</div>`
            }
            
            const two = {
                template: `<div>two</div>`
            }
    
            const vm = new Vue({
                el: '#app',
                data: {},
                filters: {},
                // 局部组件
                components: {
                    child,
                    one,
                    two
                }
            })
        </script>
    </body>
    
    </html>
    
    

    十六、refs:获取组件、DOM元素,父组件获取子组件的数据

    • 说明 : vm.$refs 一个对象, 持有已注册过 ref 的所有子组件 ( HTML 元素)

    • 使用 :

      1. 注册
      // $refs = {  div : div元素, child:child组件 }
      // 标签 【相当于给标签、组件 添加id。】
      <div ref="div">哈哈</div>
      // 组件 
      <child ref="child"></child>
      
      
      1. 注意 : mounted 中使用
      // mounted 操作DOM
      * this.$refs.div
      * this.$refs.child
      
      
      
    • 注意点 : 如果获取的是一个子组件,那么通过 ref 就能获取到子组件中的 datamethods

      this.$refs.child.num
      this.$refs.child.fn
      
      
    • 场景 : 一般在第三方的组件中, 可能会用到这个功能

    • 示例 :

    // 组件
     <div ref="div">哈哈</div>
     <child ref="child"></child>
    // js
     mounted() {
        console.log(this.$refs.div)
        console.log(this.$refs.child.fn)
      }
    
    

    04-refs.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
    </head>
    
    <body>
        <!-- 
            以后在一些第三方框架里, 获取dom元素/获取组件,可以用 refs 
            1. refs: 对象格式 (键值对) 收集了一些 注册过 ref 的元素/组件
            2. 凡是注册过 ref 的元素或者组件都会被refs收录
            3. 通过 this.$refs  
    
            总结 : 
            1. refs 可以获取 dom元素/组件
            2. 怎么获取 
             - 注册: ref='c' 【给一个唯一的属性值,类似于id。】
             - 获取: this.$refs 
        -->
    
        <div id="app">
            <!-- 
                refs
                {
                    d : <div ref='d'>我是div</div>   
                    p : <p ref='p'>我是p标签</p>
                }
            -->
    
            <!-- 【相当于给标签、组件 添加id。】 -->
            <div ref="d">我是div</div>
            <p ref="p">我是p标签</p>
            <child ref="c"></child>
        </div>
    
        <script src="./vue.js"></script>
        <script>
            // 组件 看做是一个个可复用的ui模块
            // 组件的本质: vue 实例
            Vue.component('child', {
                template: `<div> 子组件 :  </div>`,
                data() {
                    return {
                        cmsg: '子组件里的数据'
                    }
                }
            })
    
            const vm = new Vue({
                el: '#app',
                data: {},
                created() {},
                mounted() {
                    console.log(this.$refs) // {d: div, p: p, c: VueComponent}
    
                    // 可以通过 this.$refs 获取dom元素/组件
                    console.log(this.$refs.d) // <div>我是div</div>
                    console.log(this.$refs.d.innerHTML) // 我是div
    
                    // 也可以获取组件里的数据
                    // 父组件可以通过 refs 拿到子组件里的数据
                    console.log(this.$refs.c)
                    console.log(this.$refs.c.cmsg) // 子组件里的数据
                }
            })
        </script>
    </body>
    
    </html>
    
    
  • 相关阅读:
    父div的透明度不影响子div透明度
    vue-组件命名
    前端页面优化技巧
    Webstorm添加新建.vue文件功能并支持高亮vue语法和es6语法
    防止被坑
    vue安装教程总结
    vue找错
    前段进阶之路
    VM4061 layui.js:2 Layui hint: form is not a valid module
    三月十一号
  • 原文地址:https://www.cnblogs.com/jianjie/p/12605563.html
Copyright © 2020-2023  润新知