Vue2基础-组件

207 阅读5分钟

写在前面:

1.本文是个人课上学习内容的总结和梳理,主要知识点来自于“开课吧”线上直播课程,以及Vue官方文档。

2.目前尚处于Vue乃至前端入门阶段,因此很多内容理解的不是很透彻,文章更多是用来学习记录而非干货分享。

因此,建议如果需要解决项目问题,还是去看一下vue官方文档(一手资料)或者其他大佬写的文档。

组件

真实的业务场景中,我们的网页是被分成很多个模块的,比如导航栏,内容区,状态栏等等,同时我们开发的逻辑也是模块化组件化去耦合,因此我们需要以一种嵌套的组件树的形式来组织我们的视图和代码:

组件树

组件注册

我们可以先看一个例子,观察一下什么是组件注册。

const Foo = {
    template:`
    <div>this is component-Foo</div>
    `
}

Vue.component("Bar",{
    components:{
        Foo,
    },
    data(){
        return{
            msg: "this is component-Bar"
        }
    },
    template:`
    <div>{{msg}}
        <Foo></Foo>
    </div>
    `
});

const app = new Vue({
    el:"#app",
    data(){
        return{
            count : 100
        }
    },
    template:`
        <div>
            {{count}}<br>
            <Bar></Bar>
        </div>
    `,
})

通过上述例子,我们可以看到:

  • 通过vue.component的方式注册的组件被称为全局组件,这一类之组件只要组成后,可以在任何位置使用。声明语法为:

    vue.component('component-name',{
    	//options API
    })
    
  • 通过创建函数对象,并在其他组件內通过components:{component-a,component-b,component-c}的方式注册的,被称为局部注册,仅在该声明的组件內生效。声明语法为:

    const Foo = {
      //options API...
    };
    
    vue.component('Bar',{
    	components:{ Foo },
      // other options API...
    })
    

通过props传参

如果我们想要在调用一个组件的时候,通过传入某些参数来控制组件內渲染的视图时,我们可以在注册声明组件的时候,声明一个props参数来实现这个需求:

Vue.component("Foo",{
  props:['msg','count'],
  template:`
		<div>
			{{msg}},{{count}}
		</div>
	`
})

然后在组件外部调用该组件的时候,可以通过上述制定一个attribute来传递进去:

<Foo msg="msg1" count="one"></Foo>
<Foo msg="msg2" count="two"></Foo>
<Foo msg="msg3" count="three"></Foo>

通过emit和v-on监听子组件事件

如果我们想要通过监听子组件的事件来更改父组件视图的话,比如:当点击子组件的button按钮的时候,父组件上显示的数字翻倍。这时就可以用到emit和v-on了。

其中emit是子组件用来向父组件传递事件的。

Vue.component('Foo',{
  data(){
    return{}
  },
  methods:{
    handleClick(){
      this.$emit('double',2);
      this.$emit('treble',3);
    }
  }
  template:`
		<div><button v-on:click="handleClick">send msg to fatherComponent</button></div>
	`
})

这样当点击子组件的按钮的时候,就会触发handleClicck的方法,然后在该方法內通过$emit发送了两个自定义事件和参数。父组件通过V-on监听对应事件,当该事件触发时执行对应的业务逻辑。

      const app = new Vue({
        methods: {
          handleDouble(value) {
            console.log("翻倍了:" , this.count * value);
          },
          handleTreble(value) {
            console.log("三倍:", this.count * value);
          },
        },
        template: `
            <div>
                <Foo @double="handleDouble" @treble="handleTreblee"></Foo>
            </div>
        `,
        data() {
          return { count:1 };
        },
      });

v-model语法糖实现自定义事件侦听

当我们在进行父级和子级通信的时候,是需要一方抛出一个自定义事件,然后另一方需要先接受到对应的事件,然后在进行处理逻辑,这样是比较麻烦的。因此v-model语法糖可以给我提供一种方式,不需要再抛出事件/监听事件。

首先,在需要发送消息的一方,需要定义model:

Vue.component('Sender',{
  props:['visible'],
  data(){return{}},
  model:{
    event:"close",
    prop:"visible"
  },
  methods:{
    handleClose(){
      this.$emit("close", false)
    }
  },
  template: `
		<div v-if="visible">
      this is sender, and its state is {{visible}}
      <button @click="handleClose">close</button>
    </div>
		`,
})

当子组件点击close按钮的时候,回调用handleClose()方法,这时会向父组件发送一个("close", false),这两个值代表什么东西,等我们看完父组件的代码之后再说:

const app = new Vue({
  template:`
		<div>
    	this is receiver,this button can show Sender-component:
      <button @click="handleShowSender">show</button>
      <Sender v-model="showSender" ></Sender>
    </div>
	`,
  data(){
    return{
      showSender:false
    }
  },
  methods:{
    handleShowSender(){
      this.showSender = true
    }
  }
})

上述代码v-model="controlSenderr"中的controlSender对应的值对应着子组件定义的model.prop:"visible"参数,当触发handleShowSender函数时,会将showSender的值修改为true,对应的会传递给子组件的visible使之变成true

刚才留着没有解释的子组件发来的("close", false)这个close其实就是model.event定义的这个事件,与之对应的就是父组件的v-modle:"showSender"这个事件,false就是传过来的value值,赋值给showSender

这样就实现了不用抛出事件,监听事件,再处理逻辑这样一套繁琐的操作。

sync实现自定义事件侦听

Vue还给我们提供了一种类似v-model的方法,也就是.sync修饰符,用法类似,但是我个人更倾向于使用这种方式,因为起名字这个问题永远困扰着程序员,而.sync这种方式个人认为表意更加清晰明确。

以下是.sync修饰符的使用方式,我们还是先看子组件:

Vue.component("Sender", {
  components: {},
  props: ["visible"],
  data() {
  	return {};
  	},
	methods: {
		handleClose() {
			this.$emit("update:visible", false);
			},
		},
	template: `
		<div v-if="visible"> 
			state of Sender is {{visible}}
			<button @click="handleClose">close</button>
		</div>
          `,

可以看到,我们同样是需要定义一个props:'visible'这样才可以控制子组件模块的显示和隐藏;同样的,我们也是在组件内添加了一个close按钮,当点击按钮的时候会触发handleClose的函数方法;不同的是,该方法$emit发送的却改变了,由之前的"close"改成了"update:visible",前面的update:是这种方式的固定语法,后面跟着的就是需要修改的对应props,这样就表意十分明确,一眼可以认出来。

对应的,父组件需要进行如下操作:

const app = new Vue({
    el: "#app",
    template: `
        <div>
            <button @click="handleShowSender">showSender</button>
            <Foo :visible.sync="isVisible" ></Foo>
        </div>
    `,
    data() {
      return {
        isVisible: false,
      };
    },
    methods: {
        handleShowSender() {
        this.isVisible = true;
      },
    },
  });

父组件通过:visible:sync监听来自自组件的自定义事件,当接收到自定义事件之后,会讲传过来的第二个参数赋值给后面的isVisible响应式数据。

slot插槽实现组件自定义模版与替换

我们可以在子组件里面使用slot插槽,来预设一些模版内容;父组件可以按需加在,如果父组件想要更改模版内容,可以执行在template标签內加上加上自己需要的内容。否则就会渲染出来预设的内容。

Vue.component("Foo", {
    components: {},
    data() {
      return {};
    },

    template: `
        <div> 
          <slot name="header" test1="123" test2="234">header in slot</slot>
          <slot name="main">main in slot</slot>
          <slot name="footer">footer in slot</slot>
        </div>
      `,

在子组件预设slot插件的时候,可以通过name字段区分各个slot,并且也可以预设一些参数,父组件可以直接调用:

const app = new Vue({
    el: "#app",
    template: `
        <div>
            <Foo>
              <template #header="{test1}">
                {{test1}}
              </template>

              <template #main>
                main in template
              </template>

              <template #footer>
              </template>
            </Foo>
        </div>
    `,
    data() {
      return {};
    },
  });

父组件通过#slotName="{propName}"的方式读取对应名称的slot插槽并使用预设的参数。