Vue组件化初体验

认识组件

组件化开发:

​ 将一个完成的页面,划分成一个个的组件,最终由一个个组件来完成整个页面你的开发,这个过程就是组件化开发

优势:复用!!

​ 也就是说组件只需要写一次,然后,用到组件的地方,只需要拿过去用就可以了

组成

一个组件由三部分组成:HTML+CSS+JS

两种注册组件的方式:

​ 1 全局组件

​ 2 局部组件

注册全局组件:

​ 第一个参数:表示组件名称

​ 第二个参数:是一个配置对象,这个配置对象类似于 Vue 构造函数中使用的配置对象

​ 也就是说:在 Vue 构造函数中能够使用的配置项,几乎都可以用在组件中

template 作用:指定组件模板

​ 注意:只能出现一个根节点

通过 data 指定组件中用到的数据

​ 注意:组件中的数据data,是一个方法,通过方法的返回对象来作为组件数据

我的第一个全局组件

<!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>
  </head>

  <body>
    <div id="app">
      <!-- 使用组件: -->
      <hello></hello> <hello></hello> <hello></hello> <hello></hello>
    </div>

    <script src="./vue.js"></script>
    <script>
      /* 
        两种注册组件的方式:
        1 全局组件
        2 局部组件

        一个组件由三部分组成:HTML+CSS+JS
      */

      // 注册全局组件:
      // 第一个参数:表示组件名称
      // 第二个参数:是一个配置对象,这个配置对象类似于 Vue 构造函数中使用的配置对象
      //  也就是说:在 Vue 构造函数中能够使用的配置项,几乎都可以用在组件中
      Vue.component('hello', {
        // template 作用:指定组件模板
        // 注意:只能出现一个根节点
        template: `
          <div>
            <h1>这是我的第一个组件 - Hello 组件</h1>
            <span @click="fn">{{ msg }}</span>
          </div>
        `,

        // 通过 data 指定组件中用到的数据
        // 注意:组件中的数据data,是一个方法,通过方法的返回对象来作为组件数据
        data() {
          return {
            msg: 'Vue Component data'
          }
        },

        // 钩子函数
        created() {
          console.log('created 钩子函数执行了')
        },
        // 方法
        methods: {
          fn() {
            console.log('方法执行了')
          }
        }
      })
      const vm = new Vue({
        el: '#app',
        data: {}
      })
    </script>
  </body>
</html>

注册局部组件

​ 通过配置项 components: {}

//html
 <div id="app">
      <item></item>
      <wang></wang>
  </div>
  
  //js
   Vue.component('wang',{
      template:`
      <p>我是王婷,<ting></ting></p>
      `,
      components:{
        //组件名称
        ting:{
          //组件模板
          template:`
            <span>我最美,{{wt}}</span>
          `,
          //组件使用的数据
          data(){
            return {
              wt:'确认过眼神,这是真的'
            }
          }
        }
      }
    });
    const vm = new Vue({
      el: '#app',
      data: {
        msg:''
      },
      components:{
        item:{
          template:`
            <p>我是一个局部的组件:{{chang}}</p>
          `,
          data(){
            return{
              chang:'验证通过'
            }
          }
        }
      }
    })
两中组件的区别

​ 1.全局组件可以在任意的地方使用

​ 2.局部组件只能在所属组件的 模板 中使用

使用组件

组件使用中的细节点:

表单/ul/ol等中指定的标签

产生的原因:在tbody中只能使用tr标签,使用其他的标签,浏览器解析的时候,会把其他标签作为table外部标签解析出去,结构就会发生分离,ul和ol也是如此

使用is解决问题,例如tr标签

//html
<table>
    <tbody>
        <tr is=chang></chang>
    </tbody>
</table>

//js

Vue.component('chang', {
    template: `
        <tr>
            <td>常杰</td>
            <td>李想</td>
            <td>老丁</td>
        </tr>
    `
});

使用ref属性获取dom对象

//html
  <div id="app">

    <table>
      <tbody>
        <tr is=chang>
          </chang>
        
      </tbody>
    </table>
  </div>

//js
Vue.component('chang', {
      template: `
        <tr>
          <td ref='hello' @click="handler">{{current}}</td>
        </tr>
      `,
      data(){
        return{
          current:'常杰'
        }
      },
      methods:{
        handler(){
          // alert('你触发了点击事件');
          console.log(this.$refs.hello)
        }
      }
    });
组件通讯:

​ 组件是一个独立且封闭的个体,也就是说:默认情况下,组件只能使用自身的数据,而不能使用外部的数据

​ 但是,在组件化开发过程中,两个组件通讯( 也就是两个组件使用对方法的数据 ),是很常见的问题。

​ 这种问题就需要通过 组件通讯 机制来解决

组件通讯的三种情况:

​ 1 父组件 传递数据给 子组件

​ 2 子组件 传递数据给 父组件

​ 3 非父子组件(兄弟组件)

父 -->子通讯:

​ 父组件: Vue实例

​ 子组件: child组件

步骤:

​ 1 在子组件标签中添加要传递的属性

  <child msg="123"></child> msg就是要传递的属性
  <child :msg="parentMsg"></child> 表示传递动态数据给子组件

​ 2 在子组件中通过 props 显示指定要接受的属性

​ props: ['msg']

​ 3 此时,就可以在子组件中直接使用 msg 这个数据了

<!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>
      .parent {
        background-color: hotpink;
        padding: 50px;
      }

      .child {
        background-color: #daa520;
        height: 100px;
      }
    </style>
  </head>

  <body>
    <div id="app" class="parent">
      父组件:{{ parentMsg }}
      <!-- 渲染子组件: -->
      <!-- <child msg="123"></child> -->
      <!-- <child msg="parentMsg"></child> -->

      <!-- 表示将父组件中的数据 parentMsg 传递给子组件 -->
      <child :msg="parentMsg"></child>
    </div>

    <script src="./vue.js"></script>
    <script>
      // 注册子组件
      Vue.component('child', {
        template: `
          <div class="child">
            <p>这是子组件 - {{ msg }}</p>
          </div>
        `,

        // 组件中通过 props 配置项,来显示指定要接受的数据
        props: ['msg']
      })

      const vm = new Vue({
        el: '#app',
        data: {
          parentMsg: '这是父组件中的数据'
        }
      })
    </script>
  </body>
</html>

子 -->父通讯

·思路:父组件提供一个方法,让子组件调用,子组件调用方法的时候将数据作为参数传递,这样,父组件就拿到子组件中传递过来的数据了

​ function parent(data) {

​ console.log('子组件中传递过来的数据:', data)

​ }

​ 子组件调用方法: parent( '要传递给父组件的数据' )

实现子组件给父组件传值的过程

​ 1.首先给父组件注册一个方法,同时在data设置一个变量准备接收子组件传出的值

​ 2.给子组件的标签上绑定自定义事件,并把方法作为事件函数传入

​ 3.在子组件中设置模板。在模板中设置事件,触发子组件的方法,在子组件的方法中,获取到传入的自定义事件,并且触发这个事件,传递参数

​ 4.子组件触发这个方法,并把数据作为方法的参数传出

​ 5.在父组件中处理触发事件的操作

//html
  <div id="app">

    我儿子给我发的信息:{{msg}}
    <!-- 2.给子组件的标签上绑定自定义事件,并把方法作为事件函数传入 -->
    <son @pmsg="fn"></son>
  </div>
  
  //js
  Vue.component('son',{
      // 3.在子组件中设置模板。在模板中设置事件,触发子组件的方法,在子组件的方法中,获取到传入的自定义事件,并且触发这个事件,传递参数
      template:`
        <p>我要给老爸发信息
          <button @click="send">发送信息</button>
        </p>
      `,
      // 4.子组件触发这个方法,并把数据作为方法的参数传出
      methods:{
        send(){
          this.$emit('pmsg',"老爸,我资金有点....")
        }
      }
    });

    const vm = new Vue({
      el: '#app',
      data: {
        msg:'没啥用,就占个位'
      },
      // 1.首先给父组件注册一个方法,同时在data设置一个变量准备接收子组件传出的值
      methods:{
        // 5.在父组件中处理触发事件的操作
        fn(data){
          this.msg = data
        }
      }
    })
小案例:

计数器实现的步骤

  • 1.首先创建一个子组件count
  • 2.在组件中创建模板,在模板中绑定事件handler,插入数据num
  • 3.在组件中创建模板中调用的方法handler
  • 4.在模板中设置数据num
  • 5.在组件标签上注册事件change,并绑定handlerChange方法(非常重要)
  • 6.在组件标签上添加属性ref为one/two(非常重要)
  • 7.在Vue实例中,添加方法handlerChange,并通过this.$refs.绑定的名称获取数据
  • 8.获取到子组件的数据进行操作
<!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>
</head>

<body>
  <div id="app">
      <!-- 5.在组件标签上注册事件change,并绑定handlerChange方法(非常重要) -->
      <!-- 6.在组件标签上添加属性ref为one/two(非常重要) -->
    <count ref="one" @change="handlerChange"></count>
    <count ref="two" @change="handlerChange"></count>

    <div>{{total}}</div>
  </div>
  <script src="./vue.js"></script>
  <script>
    // 1.首先创建一个子组件count
    Vue.component('count',{
      // 2.在组件中创建模板,在模板中绑定事件handler,插入数据num
      template:`
        <div @click="handler">{{num}}</div>
      `,
      // 4.在模板中设置数据num
      data(){
        return {
          num: 0
        }
      },
      // 3.在组件中创建模板中调用的方法handler
      methods:{
        handler(){
          this.num++;
          this.$emit("change")
        }
      }
    });
    const vm = new Vue({
      el: '#app',
      data: {
        total: 0
      },
      // 7.在Vue实例中,添加方法handlerChange,并通过this.$refs.绑定的名称获取数据
      methods:{
        handlerChange(){
          // console.log("handlerChange执行了")
          // console.log(this.$refs.one.num)
          // console.log(this.$refs.two.num)

          // 8.获取到子组件的数据进行操作
          this.total = this.$refs.one.num + this.$refs.two.num
        }
      }
    })
  </script>
</body>

</html>
其他通讯(非父子)

思路:

1 创建一个事件总线(空的Vue实例 bus)

​ 2 哪个组件要接收数据,就注册事件

​ bus.$on(事件名称, () => {})

​ 3 哪个组件要传递数据,就触发事件

​ bus.$emit(事件名称, 要传递的数据)

注意: 注册和触发事件的 bus 是同一个,事件名称也要相同

实际上,不管两个组件是什么关系,都可以通过 bus 方式来实现通讯!

步骤:

​ 1.首先创建一个 空的vue实例的对象 bus

​ 2.然后创建两个组件,一个临时用于接收 (wang) ,一个临时用于发送数据 (chang)

​ 3.给两个组件设置好模板内容

​ 4. 组件chang设置方法,在方法中通过bus.$emit获取事件,并设置触发事件的参数

​ 5.在组件wang的钩子函数created中声明创建事件listen,并设置获取到参数后的处理

​ 注意点:虽然用不到vm但是,必须声明创建这个实例,只有指定了vue的边界,代码才能生效

//html
    <div id="app">
      <chang></chang>
      <wang></wang>
    </div>
    
//js
 // 空Vue实例
      // 事件总线
      // 1.首先创建一个 空的vue实例的对象 即事件总线bus
      let bus = new Vue();
      // 2.然后创建两个组件,一个临时用于接收 (wang) ,一个临时用于发送数据 (chang)
      Vue.component('chang',{
        // 3.给两个组件设置好模板内容
        template:`
        <p>我是常杰,我想对王婷说:<button @click="fn">加密发送</button></p>
        `,
        // 4. 组件chang设置方法,在方法中通过bus.$emit获取事件,并设置触发事件的参数
        methods:{
          fn(){
            bus.$emit('listen','那天我一直在等你')
          }
        }
      });
      Vue.component('wang',{
        template:`
        <p>我是王婷,常杰对我说:{{msg}}</p>
        `,
        data(){
          return {
            msg:'接收信息中,请等待...'
          }
        },
        // 5.在组件wang的钩子函数created中声明创建事件listen,并设置获取到参数后的处理
        created(){
          bus.$on('listen',data=>{
            this.msg = data
          })
        }
      });
    //没有用到数据,但是必须声明
      const vm = new Vue({
        el:"#app",
        data:{
        }
      });

细节补充

props的属性

​ props:是只读的,在组件中使用的时候,只能读取,不能修改props的值( 赋值

)如果修改这个值,会报错!!!

​ 如果props是一个引用类型的数据,不能直接赋值,但是,可以修改引用类型中某个属性的值。

单向数据流:

​ 是说组件之间数据流动方向是从 父组件流动到 子组件

​ 父组件可以通过 props 将数据传递给子组件,并且,当父组件中的这个数据改变

后,这个改变后的数据会自动的再流动到子组件中。也就是说:子组件会自动接收到最

新的props数据,并且自动更新

​ 总结:

​ 1.props是只读的属性,只能读取props的值,而不能直接修改props的值

​ 如果是引用类型(对象、数组),可以直接修改对象中某个属性的值,但是,如果要求很严谨,也不应该直接修改对象中某个属性的值!!!

​ 2.单项数据流: 两个组件之间的数据流动方向,方向是: 父 -> 子

命名

HTML 标签或属性都是不区分大小写的(大小写不敏感的)

​ 不管你写的是大写字母的标签名或属性名,还是小写的,最终,都会被转化为小写字母

​ 同样的,在 Vue 中,给组件传递属性的时候,如果属性名中包含大写字母,在解析的时候,也会被转化为小写字母,再作为属性传递给组件

​ 如何给props命名:

​ 1 使用纯小写字母

​ <hello :childmsg="parentMsg"></hello>

​ props: ['childmsg']

​ 2 传递属性的时候,使用短横线连接多个单词,在子组件中通过 驼峰命名法 来接收这个数据(规范)

​ 传递(短横线连接) <hello :child-msg="parentMsg"></hello>

​ 接收(驼峰命名法) props: ['childMsg']

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容