Go to comments

vue 组件

组件的出现就是为了实现两个目标

1. 降低整体复杂度,提升代码的可读性和可维护性

2. 提升局部的可重复性,比如 loading 效果


一个组件就是页面中的一个区域

有时候为了重复使用,一个 icon 图标就可以做成一个重复使用的组件,

也有时候不是为了重复使用,是为了降低复杂度,把网页分细一点,便于维护管理,

页面上任何一个区域只要觉得认为合适就把它做成一个组件


一、创建组件

创建组件在 vue 里面特别特别的简单就是一个对象,

这个对象就是一个组件配置对象,跟  new Vue( { } )  配置配置的对象几乎是一 样的,就有一点点的区别

<div id="app"></div>

<script src="https://20180416-1252237835.cos.ap-beijing.myqcloud.com/common/vue.min.js"></script>
<script>

  var myButton = { // 一个按钮组件

  }

  var vm = new Vue({
    template: ``,
  });

  vm.$mount("#app");

</script>


组件配置对象和 vue 配置对象的区别

1. 组件没有 el 配置

   el 必须是在 vue 的构造函数里面配置,表示 vue 生成的 dom 挂载到哪

2. 组件里面的 data 配置必须是一个函数,该函数会返回一个对象

3. 由于没有 el,组件的模板必须配置在 template 或 render 中


下面写一个 myButton 按钮组件

var myButton = {
  data(){
    return {
      count: 0,
    }
  },
  template: `<button v-on:click="count++">当前点击了{{count}}次</button>`,
}


var vm = new Vue({
  template: ``,	
});


vm.$mount("#app");


创建一个组件后,

还不知道组件往哪个位置放,还要去注册一个组件,注册之后才能使用


二、注册组件

有两种注册方式

1. 全局注册

2. 局部注册


1、全局注册

全局注册一个组件后,整个应用中任何地方使用该组件,在vue实例里可以用,在别组件里也可以用


全局注册组件极其简单,

构造函数 Vue 里面提供了一个静态方法 component(conponent 是组件的意思),这个方法有两个参数

1. 组件名称,名称是一个字符串

2. 组件配置对象

Vue.component("组件名称", 组件配置对象);


当然,也可以直接把组件配置对象,放到到第二个参数的位置也是一样的,

Vue.component("组件名称", {
  data(){
    return {
      count: 0,
    }
  },
  template: `<button v-on:click="count++">当前点击了 {{count}} 次</button>`,
});


组件的名称是 MyButton,

将来在模板中使用组件时的名称,这 MyButton 是组件名称,跟这个  var myButton = {}  组件配置对象的变量名称无关


使用全局注册的组件

1. 创建按钮组件

2. 全局注册组件,现在能够用这个组件了

3. 在模板里使用组件,写一个 <MyButton/> 元素(叫做虚拟dom节点

<div id="app"></div>

<script src="https://20180416-1252237835.cos.ap-beijing.myqcloud.com/common/vue.min.js"></script>
<script>

  // 1.创建按钮组件
  var myButton = {
    data() {
      return {
        count: 0,
      }
    },
    template: `<button v-on:click="count++">当前点击了 {{count}} 次</button>`,
  }

  Vue.component("MyButton", myButton); // 2.全局注册组件

  var vm = new Vue({
    template: `<div>
      <MyButton/> <!-- 3.使用组件 -->
      <MyButton/>
      <MyButton/>
    </div>`,
  });

  vm.$mount("#app");

</script>


组件的重用,

可以写多个 <MyButton/>,点击后每个的按钮数字是独立增加的,

每个组件之间的数据是相互独立的,组件之间的数据不会相互影响


这就是为什么组件的 data 配置为一个函数,

1. 配置为一个函数后,每个组件要得到数据,都要调用这个 data(){} 函数,

    调用一次函数返回的 return {},就是一个新的对象了,

2. 这样避免了组件之间的数据相互影响,因为每一个组件就是一个单独的实例,每个实例有自己的数据,

3. 一个按钮就是一个实例,三个按钮就有三个实例,每个实例要有自己的数据,保障每个组件之间的数据是相互独立的


如果 data 使用同一个 obj 对象,导致点击按钮后所有的数据是一致的

var obj = {
  count:0,
};

var myButton = {
  name: "button",
  data(){
    return obj; // 使用的是同一个对象
  },
  template: `<button v-on:click="count++">当前点击了 {{count}} 次</ button>`,
}

Vue.component("my-button", myButton);

var vm = new Vue({
  name:"App",
  template: `<div>
    <my-button/>
    <my-button/>
    <my-button/>
  </div>`,
});

vm.$mount("#app");


梳理总结

1. 创建组件 var myButton = {}

2. 全局注册组件 Vue.component("my-button", myButton)

3. 然后就可以到处使用组件了 <div><my-button /></div>,使用的组件会生成一个对应的虚拟节点


全局注册并不好,

有些组件并不是通用的,可能只是在某一个地方用一下,

如果全局注册,将来构建工具打包,可能会影响打包结果让打包结果变大


2、局部注册

比如在 vue 实例里面用 MyButton 组件

1. 需要在vue的配置对象里面,配置一个 components 属性(单词比全局多了一个字母 s)

    表示局部注册的组件,只能在这个 vue 实例里使用

2. 属性名就是组件名,创建组件时用大驼峰命名,

    属性值是组件配置对象的名称,

3. 注册的属性名和属性值相同,可以使用速写属性

<div id="app"></div>

<script src="https://20180416-1252237835.cos.ap-beijing.myqcloud.com/common/vue.min.js"></script>
<script>

  // 1.创建组件,创建组件时变量名用大驼峰命名
  var MyButton = {
    data() {
      return {
        count: 0,
      }
    },
    template: `<button v-on:click="count++">当前点击了 {{count}} 次</button>`,
  }

  var vm = new Vue({
    components:{
      MyButton, // 2.局部注册组件(属性名和属性值名相同,可以用速写属性)
    },
    template: `<div>
      <MyButton/> <!-- 3.使用组件,写上组件的名称"Mybutton" -->
      <MyButton/>
      <MyButton/>
    </div>`,
  });

  vm.$mount("#app");

</script>


三、应用组件

组件是在模板中使用的,写组件要注意几个点


组件必须要有结束

1. 写结束标记 <MyButton></MyButton>

2. 也可以用自结束 <my-Button/>

3. 没有结束是错误的  <my-comp> 


组件的命子

 components: { "注册组件的名字" : 组件对象的变量名 } 


注册组件的时候有两种命名方式

1. 短横线命名法 "my-button",官方建议的命名方法(短横线命名必须要用引号引起来)

2. 大驼峰命名法 MyButton,每个单词首字母大写,袁老师推荐大驼峰命名法,因为比较灵活


组件名字使用大驼峰命名 MyButton 之后,在模板里使用组件的时候

1. 既可以写短横线 <my-button/> 

2. 又可以写大驼峰 <MyButton/> 两种都支持

new Vue({
  components: {
    MyButton,
  },
  template: `<div>
    <my-button/>
    <MyButton></MyButton>
  <div>`,
});


袁老师甚至再推荐,

在模板 template 里使用组件的的时候,也尽量使用大驼峰 <MyButton/>,

因为有些组件的名字可能只有一个单词 button 没有短横杠了,容易和 html 元素的名字 <button></button> 造成冲突


总结

创建组件:就是一个配置对象,跟 vue 配置对象差不多

注册组件:局部注册就是 components,组件名使用大驼峰

应用组件:在模板中直接使用就完事了


四、组件树

我们做一个 vue 应用的项目,百分之九十的时间都是在开发组件,把组件开发的差不多了,项目基本就完成了


一个组件创建好后,往往会在各种地方使用它。它可能出现在 vue 实例中,也可能出现在其他组件中。于是就形成一个组件树


组件树的结构,

最顶层是 vue 实例,

vue 实例里面包含一些别的组件,比如包含“组件1”和“组件2

这些别的组件里面又包含一些别的组件,比如“组件1”里面又包含两个“组件3

有些组件可能会重用,比如“组件3”可能在不同的地方使用

2020-02-18-11-13-19.png


五、向组件传递数据

大部分组件需要完成自身功能,都需要一些额外信息,

比如一个头像组件,需要告诉它头像的地址,这就需要在使用头像组件时候,向组件传递数据,

传递数据的方式有很多种,很常见的一种是使用组件属性 component props


示例,

向组件传递数据,我们把传递的数据叫做组件的属性

1. 父亲组件使用 Title 组件时,要传递标题的内容是什么,

2. Title 组件要声明组件的属性,

    props 配置的值是一个数组,数组里面每一项就是一个属性的名称

3. 父组件传递标题内容,传递的内容叫属性

<div id="app"></div>

<script src="https://20180416-1252237835.cos.ap-beijing.myqcloud.com/common/vue.min.js"></script>
<script>

  var MyButton = {
    data(){
      return {
        count: 0,
      };
    },
    template: `<button v-on:click="count++">当前点击了{{count}}次</button>`,
  }


  // Title组件
  var Title = {
    props: ["headline", "paragraph"],// 3.也就是说props属性的内容也会被注入到实例里面(组件实例)
    template: `<div>
      <h2>{{headline}}</h2> <!-- 2.模板大胡子语法里面是父组件传递过来的属性 -->
      <p>{{paragraph}}</p>
    </div>`,
  }


  var vm = new Vue({
    el: "#app",
    components: {
      MyButton,
      Title,
    },
    template: `<div>
      <MyButton/>
      <MyButton/>
      <Title headline="首页" paragraph="轮播图"/>
      <Title headline="文章" paragraph="学习笔记的列表"/>
      <Title headline="关于我" paragraph="我是远远的地方"/>
      <!-- 
      1.向Tite组件传递一个属性就跟元素属性的道理是一样的,
        横向比较一下,用a元素要告诉它url超连接不就是href属性吗? 
        这里用Title组件也一样,要告诉它标题的内容是什么,可以通过headline属性的方式告诉它
      -->
    </div>`,
  });

// briefIntroduction作者简介
</script>


props 属性也可以在模板中  {{ 属性名 }}  使用,

也就是说 props 属性的的内容也会被注入到实例里面,就像上节课说的 data、methods,还有以后会学的 computed,

每个组件也是实例,跟 vue 实例差不过,可以把 vue 实例看做是一个特殊的组件实例


注意:组件中属性 props 是只读的,绝对不能更改,这叫做单向数据流


在 vue 实例里面可能会用到各种各样的组件(组件1和组件2),可能会传递一些属性,

这些组件里面(组件1和组件2),又可能会用的别的组件(组件3),又可能会传递一些属性,

形成一个组件树,而且数据从上到下,往下流动的,

通过 props 属性,父组件往子组件里面传数据,子组件再往子组件里面传数据,从上往下流动,这叫做单向数据流

2020-02-18-11-13-19.png


什么是单项数据流

就是在组件内部绝对绝对不能去改属性 props ,

可以改自己的 data 配置,data 是自己的数据可以去改,

属性 props 是别人给的数据,组件自己是没有权利改的

比如,属性 props 的数据 headline,是 vue 实例给的数据,组件自己没有权利更改


注意,

如果 Title 组件有 data 配置,

vue 实例里面也有 data 配置,

data 配置的数据只能组件自己控制,

写在哪个组件里面的 data,哪个组件自己控制,别的组件是控制不了的,

这是 vue 设计上的一个哲学,谁的数据谁负责,别人没法控制


ps:

XMind 思维导图


六、工程结构

整个项目里可能会涉及到很多的组件,我们需要用合理的方式来组织我们的代码结构


|- htdocs

   |- index.html 

   |- src  把所有的代码都放到src文件夹里面

      |- main.js  入口模块文件,一开始就运行这个js,它作为 es6 模块在index.html里面引用它

      |- App.js   vue的根组件

      |- vue.browser.js  es6模块化的vue.js文件

      |- components

         |- MyButton.js


index.html

页面上就提供一个 #app 元素,然后用模块化的方式引用入口文件 mian.js 

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>index.html</title>
</head>
<body>

  <div id="app"></div>
  <script src="./src/main.js" type="module"></script>

</body>
</html>


src/mian.js

入口模块文件就做一件事创建 vue 实例

1. 配置对象这里 new Vue({}) 通常不用写 template 模板、data 数据这些的配置,

    它就做一件事,渲染一个根组件,剩下的事情都交给根组件去完成 

2. 怎么渲染根组件呢?可以用 render 渲染,rander 别的地方不怎么写,就这个地方习惯上会写

import Vue from './vue.browser.js'; // 导入vue文件得到的是Vue构造函数(这是es6模块化的js)
import App from './App.js'; // 导入根组件的组件对象

new Vue({
  render(h){
    return h(App); // 渲染导入的组件对象
  },
}).$mount('#app');


之前页面上引用 vue.js 得到一个会污染全局变量的构造函数,

vue.browser.js 这个不会污染全局变量


 return h(App) 

渲染一个虚拟节点,组件也是虚拟节点,

渲染虚拟节点的类型不是一个普通的元素,而是一个组件,就是这么简单


 render(h){ return h(App); }  这句写法相当于

1. 先局部注册组件

2. 然后在模板里使用组件

import Vue from './vue.browser.js';
import App from './App.js';

new Vue({
  components: {
    App, // 1.注册
  },
  template: `<App/>` // 2.使用
}).$mount('#app');


还可以用箭头函数,写的更加简单一点

import Vue from './vue.browser.js';
import App from './App.js';

new Vue({
  render: (h) => h(App),
}).$mount('#app');


也就是 main.js 什么都不做,

就写一个简单的启动功能就行了,剩下的渲染、有什么模板、有什么数据,都交给 App.js 去完成


src/App.js

vue 根组件,整个页面上所有的东西都交给根组件来渲染,根组件里要用到别的组件

import MyButton from './components/MyButton.js'; // 导入按钮组件

// 根组件的模板
const template = `<div>
    <h1>App组件</h1>
    <MyButton/> <!-- 根组件里面有可能会用到别的组件 -->
    <MyButton/>
  </div>`;

// 根组件发本质就是一个对象,导出一个对象
export default {
  components:{
    MyButton, // 局部注册按钮组件
  },
  template,
}


src/components/MyButton.js

按钮组件

const template = `<button v-on:click="count++">当前点击 {{count}} 次</button>`; // 为了便于管理模板写在外面

export default { // 导出按钮组件
  data(){
    return {
      count: 0,
    };
  },
  template,
};


每一个模块就是一个组件,

有自己的模板,有自己的配置对象,就写好一个按钮组件了,

一个页面有很多不同开发者,不同的开发者去写不同的组件,最后一整合一个页面就出来了


七、vue知识体系

这节课多了一个 props 属性

1. 组件也是实例和vue实例差不多,其实可以把vue实例看做一个特殊的组件实例

2. props属性的内容也会被注入到实例里面,就像上节课说data、methods、computed,被注入到实例里面去


|- vue知识体系 

   |- 模板

   |    |- 内容  有变化的地方写大胡子mustache语法 {{ js表达式 }} 

   |    |- 指令  主要写在元素的属性位置,是带有一些功能性的,比如循环生成元素,判断是否要生成元素等

   |       |- v-bind v-bind: 属性名 = "js表达式"

   |       |- v-for  循环数组,注意绑定key值

   |       |- v-on  绑定事件,指令参数是事件名click,简写为@click

   |

   |- 配置

        |- data 和界面相关的数据

        |- methods  常见的操作方法,跟功能相关的方法

        |- template 配置模板

        |- render  渲染方法,用于生成vnode

        |- el  挂载的目标元素

        |- components  局部注册组件

        |- props  声明组件的属性



Leave a comment 0 Comments.

Leave a Reply

换一张