Go to comments

vue 组件

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

1. 降低整体复杂度,提升代码的可读性和可维护性( 从一个粗粒度划分为细粒度,复杂度自然就降低了 )

2. 提升局部的可重复性( 粒度降细一点,复杂度就降低了,同时重用性也就变的更好 )


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

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

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

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


一、创建组件

创建组件在 vue 里面特别特别的简单就是一个对象,这个对象就是一个配置对象,该对象跟  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,虚拟 DOM 树必须定义在 template 或 render 中


组件的写法和  new Vue({} 实例的配置实际上是查不多的(就是一点点的区别),下面写一个 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 里面提供了一个静态方法 Vue.component() (conponent是组件的意思)


Vue.component() 方法有两个参数

1. 组件名称( 将来在模板中使用组件时的名称,这才是组件名称,跟  var myButton = {}  这个变量名称无关 )

2. 组件配置对象

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


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

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


在vue实例模板里面,使用全局注册的组件

1. 创建 myButton 组件

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/>
      <MyButton/>
    </div>`,
  });

  vm.$mount("#app");

</script>


组件的重用,

可以写多个 <MyButton/>,每个组件之间的数据是相互独立的,组件之间的数据不会相互影响


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

配置为一个函数后,每个组件要得到数据,都要调用这个 data(){} 函数,调用一次该函数就返回的 return {},就是一个新的对象了,

这样避免了组件之间的数据相互影响,因为同一个组件有不同的实例,每个实例有自己的数据,

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


如果使用同一个 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 配置对象里面,配置一个 components 属性(单词比全局多了一个字母 s)

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

2. 创建组件时用大驼峰命名,注册时属性名和属性值相同,可以使用速写属性

<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>  

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

这种是错误的  <my-comp> 


2、组件的命名

 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 实例,

组件实例里面包含一些别的组件,别的组件里面又包含一些组件,有些组件可能会重用,比如组件3可能在不同的地方使用

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


五、向组件传递数据

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

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


写一个标题组件 Title,使用该组件的时候需要告诉标题的内容是什么

1. 这就需要在使用 Title 组件时候向组件传递数据,我们管传递的数据叫组件的属性

2.  props: ["headline", "paragraph"]  props 配置的值是一个数组,数组里面每一项都是属性的名称

<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/>
      <!-- 
      1.跟元素属性的道理是一样的,
        横向比较一下,用a元素要告诉它url超连接不就是href属性吗?
        使用Title组件也一样,要告诉它标题的内容是什么,可以通过headline属性的方式告诉它
      -->
      <Title headline="标题一" paragraph="简介文字"/>
      <Title headline="标题二" paragraph="简介文字简介文字"/>
      <Title headline="标题三" paragraph="简介文字简介文字简介文字"/>
    </div>`,
  });

// briefIntroduction作者简介
</script>


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

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

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


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

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

形成一个组件树,而且数据从上到下往下流动的,父组件往子组件里面传数据,子组件再往子组件里面传数据,从上往下流动这叫做单向数据流

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


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


什么是单项数据流

在组件里面是绝对绝对不能去改 props 属性配置的,可以改自己的 data 配置,data 是自己的数据可以去改,

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


vue 设计上的一个哲学,谁的数据谁负责,别人是没法控制,向组件传递数据,我们把传递的数据叫做组件的属性


ps:

XMind思维导图


六、工程结构

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


|- htdocs

   |- index.html 

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

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

      |- App.js   vue的根组件

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

      |- components

         |- MyButton.js


index.html

页面里面就提供一个 div#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 实例

import Vue from './vue.browser.js'; // 导入vue文件得到的是Vue构造函数
import App from './App.js'; // 导入一个组件对象

// 入口这个地方通常不用写template模板、data数据这些的配置,它就做一件事,渲染一个根组件,剩下的事情都交给根组件去完成
// 怎么渲染这个组件呢?可以用render渲染,rander别的地方不怎么写,就这个地方习惯上会写
new Vue({
  render(h){
    return h(App); // 这句的意思是,组件也是虚拟节点,渲染一个虚拟节点。虚拟节点的类型不是一个普通的元素而是一个组件,就是这么简单
  },
}).$mount('#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');


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

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

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

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


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 和界面相关的数据

        |- computed

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

        |- template 配置模板

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

        |- el  挂载的目标元素

        |- components  局部注册组件

        |- props  声明组件的属性



Leave a comment 0 Comments.

Leave a Reply

换一张