组件化编程

空~2022年10月14日
  • Vue
大约 7 分钟

组件化编程

  1. 理解: 用来实现局部(特定)功能效果的代码集合(html/css/js/image…..);
  2. 为什么: 一个界面的功能很复杂;
  3. 作用: 复用编码, 简化项目编码, 提高运行效率;

image-20211015222005670

image-20211015222120118

非单文件组件

一个文件中包含有 n 个组件

Vue 中使用组件的三大步骤:

  1. 定义组件(创建组件)
  2. 注册组件
  3. 使用组件(写组件标签)

准备好一个容器

<div id="root">
  <h2>学校名称:{{name}}</h2>
  <h2>学校地址:{{address}}</h2>
  <hr />
  <h2>学生姓名:{{name}}</h2>
  <h2>学生年龄:{{age}}</h2>
</div>

如何定义一个组件?

使用 Vue.extend(options) 创建,其中 optionsnew Vue(options) 时传入的那个 options 几乎一样,但也有点区别;

区别如下:

  1. el 不要写,为什么? ——— 最终所有的组件都要经过一个 vm 的管理,由 vm 中的 el 决定服务哪个容器。

  2. data 必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。

  3. 使用 template 属性可以配置组件结构,会替换掉真实 DOM。

// 创建 school 组件
const school = Vue.extend({
  // el: "#root", 组件不需要 el 标签, 最终所有标签都会被一个 vm 管理
  // data写成函数式
  data() {
    return {
      name: "学校",
      address: "深圳",
    };
  },
});

// 创建 student 组件
const student = Vue.extend({
  data() {
    return {
      name: "小明",
      age: "北京",
    };
  },
});

如何注册组件?

  1. 局部注册:靠 new Vue 的时候传入 components 选项

  2. 全局注册:靠 Vue.component('组件名',组件)

组件在使用时还需要一个基本的 HTML 结构, 可以使用 template 在组件内定义.

template 必须要有一个根 DOM 节点.

局部注册

添加 template

// 创建 school 组件
const school = Vue.extend({
  // el: "#root", 组件不需要 el 标签, 最终所有标签都会被一个 vm 管理
  // data 写成函数式
  // 使用 template 属性可以配置组件结构
  template: `
        <div>
        <h2>学校名称:{{ name }}</h2>
        <h2>学校地址:{{ address }}</h2>
        </div>`,
  data() {
    return {
      name: "学校",
      address: "深圳",
    };
  },
});

// 创建 student 组件
const student = Vue.extend({
  template: `
      <div>
      <h2>学生姓名:{{ name }}</h2>
      <h2>学生年龄:{{ age }}</h2>
      </div>`,
  data() {
    return {
      name: "小明",
      age: "北京",
    };
  },
});

创建 vm, vm 需要在最后创建, 否则无法加载组件

// 创建 vm
new Vue({
  el: "#root",
  // 注册组件 局部注册
  components: {
    // 驼峰命名法会导致组件加载不到
    myschool: school,
    mystudent: student,
  },
});

修改 root 容器

<div id="root">
  <myschool></myschool>
  <hr />
  <mystudent></mystudent>
</div>

全局注册

编写一个容器

<div id="root2">
  <hello></hello>
</div>

创建一个新组件

const hello = Vue.extend({
  template: `
    <h1>hello</h1>
    `,
});

进行全局注册

// 全局注册
Vue.component("hello", hello);

创建一个新的 vm 绑定容器, 不注册局部组件

new Vue({
  el: "#root2",
});

提示

关于组件名:

  1. 一个单词组成:

    第一种写法(首字母小写):school

    第二种写法(首字母大写):School

  2. 多个单词组成:

    第一种写法(kebab-case 命名):"my-school" (需要引号)

    第二种写法(CamelCase 命名):MySchool (需要 Vue 脚手架支持)

备注:

  1. 组件名尽可能回避 HTML 中已有的元素名称,例如:h2、H2 都不行。

  2. 可以使用 name 配置项指定组件在开发者工具中呈现的名字。

关于组件标签:

第一种写法:<school></school>

第二种写法:<school/>

备注:

不用使用脚手架时,<school/> 会导致后续组件不能渲染。

一个简写方式:

const school = Vue.extend(options) 可简写为:const school = options

嵌套组件

简单的组件嵌套

<body>
  <div id="root">
    <school />
  </div>
</body>
<script>
  // 创建 student 组件
  const student = {
    template: `
          <div>
          <h2>学生名称:{{ name }}</h2>
          <h2>学生年龄:{{ age }}</h2>
          </div>`,

    data() {
      return {
        name: "小明",
        age: 18,
      };
    },
  };
  // 创建 school 组件
  const school = {
    template: `
          <div>
          <h2>学校名称:{{ name }}</h2>
          <h2>学校地址:{{ address }}</h2>
          <student/>
          </div>`,

    data() {
      return {
        name: "学校",
        address: "深圳",
      };
    },
    components: {
      student,
    },
  };

  // 创建 vm
  new Vue({
    el: "#root",
    // 注册组件 局部注册
    components: {
      school,
    },
  });
</script>

一般会定义一个 app 父组件管理所有子组件.

// 添加一个 app 组件来管理其他组件
const app = {
  template: `
    <div>
    <school/>
    <hello/>
    </div>`,
  components: {
    school,
    hello,
  },
};

// 创建 vm
new Vue({
  template: `<app/>`,
  el: "#root",
  // vm 只需要管理 app
  components: {
    app,
  },
});

vm 中配置了 template, DOM 容器什么都不用写了.(标准化开发)

<div id="root"></div>

关于 VueComponent

<div id="root">
  <school></school>
  <hello></hello>
</div>

<script type="text/javascript">
  Vue.config.productionTip = false;

  //定义school组件
  const school = Vue.extend({
    name: "school",
    template: `
            <div>
                <h2>学校名称:{{name}}</h2>
                <h2>学校地址:{{address}}</h2>
                <button @click="showName">点我提示学校名</button>
            </div>
        `,
    data() {
      return {
        name: "尚硅谷",
        address: "北京",
      };
    },
    methods: {
      showName() {
        console.log("showName", this);
      },
    },
  });

  const test = Vue.extend({
    template: `<span>atguigu</span>`,
  });

  //定义hello组件
  const hello = Vue.extend({
    template: `
            <div>
                <h2>{{msg}}</h2>
                <test></test>
            </div>
        `,
    data() {
      return {
        msg: "你好啊!",
      };
    },
    components: { test },
  });

  // console.log('@',school)
  // console.log('#',hello)

  //创建vm
  const vm = new Vue({
    el: "#root",
    components: { school, hello },
  });
</script>
  1. school 组件本质是一个名为 VueComponent 的构造函数,且不是程序员定义的,是 Vue.extend 生成的。

  2. 我们只需要写 <school/><school></school>Vue 解析时会帮我们创建 school 组件的实例对象,即 Vue 帮我们执行的:new VueComponent(options)

  3. 特别注意:每次调用 Vue.extend,返回的都是一个全新的 VueComponent!!!!

  4. 关于 this 指向:

    1. 组件配置中:

      data 函数、methods 中的函数、watch 中的函数、computed 中的函数, 它们的 this 均是【VueComponent 实例对象】。

    2. new Vue(options)配置中:

      data 函数、methods 中的函数、watch 中的函数、computed 中的函数, 它们的 this 均是【Vue 实例对象】。

  5. VueComponent 的实例对象,以后简称 vc(也可称之为:组件实例对象)。 Vue 的实例对象,以后简称 vm

内置关系

  1. 一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
  2. 为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue 原型上的属性、方法。
<body>
  <!-- 准备好一个容器-->
  <div id="root">
    <school></school>
  </div>
</body>

<script type="text/javascript">
  Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。
  Vue.prototype.x = 99;

  //定义school组件
  const school = Vue.extend({
    name: "school",
    template: `
            <div>
                <h2>学校名称:{{name}}</h2>
                <h2>学校地址:{{address}}</h2>
                <button @click="showX">点我输出x</button>
            </div>
        `,
    data() {
      return {
        name: "尚硅谷",
        address: "北京",
      };
    },
    methods: {
      showX() {
        console.log(this.x);
      },
    },
  });

  //创建一个vm
  const vm = new Vue({
    el: "#root",
    data: {
      msg: "你好",
    },
    components: { school },
  });

  //定义一个构造函数
  function Demo() {
    this.a = 1;
    this.b = 2;
  }
  //创建一个Demo的实例对象
  const d = new Demo();

  console.log(Demo.prototype); //显示原型属性

  console.log(d.__proto__); //隐式原型属性

  console.log(Demo.prototype === d.__proto__);

  //程序员通过显示原型属性操作原型对象,追加一个x属性,值为99
  Demo.prototype.x = 99;

  console.log("@", d);
</script>

image-20221014182731676

单文件组件

单文件组件后缀名名为 vue 且文件内只支持三种标签.

<template>
  <!--组件结构  -->
</template>

<script>
// 交互
</script>

<style>
/*样式*/
</style>

ES6 复习: 单文件组件需要暴露出来, 才能被引入.

三种暴露方式:

例如:

暴露 school 组件

分别暴露

export const school = Vue.extend(option)

统一暴露

export {school}

默认暴露

export default {option}

引入方式:

分别暴露和统一暴露需要大括号引入

import {school} from './school'

默认暴露不需要

import school from './school'

创建一个 School.vue

<template>
  <!--  -->
  <div class="demo">
    <h2>学校名称:{{ name }}</h2>
    <h2>学校地址:{{ address }}</h2>
    <button @click="showName">点我提示学校名</button>
  </div>
</template>

<script>
export default {
  // 编程规范: name 和 文件名一致
  name: "School",
  data() {
    return {
      name: "尚硅谷",
      address: "北京昌平",
    };
  },
  methods: {
    showName() {
      alert(this.name);
    },
  },
};
</script>

<style>
/**/
.demo {
  background-color: orange;
}
</style>

创建 App.vue

<template>
  <div>
    <School></School>
    <Student></Student>
  </div>
</template>

<script>
//引入组件
import School from "./School.vue";

export default {
  name: "App",
  components: {
    School,
  },
};
</script>

单文件组件里无法创建vm, 需要使用 main.js 创建.

import App from "./App.vue";

new Vue({
  el: "#root",
  template: `<App></App>`,
  components: { App },
});

创建一个容器 index.html.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>练习一下单文件组件的语法</title>
  </head>
  <body>
    <!-- 准备一个容器 -->
    <div id="root"></div>
  </body>
</html>

运行需要脚手架支持.