Skip to content

一个页面通常包括三个部分:HTML(骨架)、CSS(样式)、JS(交互)

传统开发方式:写某些通用的功能写在一个JS文件内,在多个也页面中进行使用,不通用的需要单独再写一个文件;弊端就是当引用页面过多,不利于维护,牵一发而动全身

组件化开发方式:页面上有很多功能,如搜索栏、轮播图、热点……,把这些功能都独立成一个个的组件,每个组件都有自己单独的JS、CSS、HTML片段……,当某个页面需要某个功能时引入组件即可

组件应用

html
<div id="app">
    <!-- 使用组件 -->
    <user-list></user-list>
</div>
<script>
    // 创建一个局部组件 可以把Vue.extend()部分省略只留{...}部分
    const userList = Vue.extend({
        // 骨架
        template: `
            <ul>
                <li v-for="(user, index) in users" :key="index">
                    {{user.name}} - {{user.age}}
                </li>
            </ul>
        `,
        // 数据
        data() {
            return {
                users: [
                    { name: "张三", age: 18 },
                    { name: "李四", age: 19 },
                    { name: "王五", age: 20 }
                ]
            }
        },
        // 交互
        methods(){
            // ...
        },
        
    })
    
    // Vue 实例
    const vm = new Vue({
        el: "#app",
        // 注册组件(局部)
        components: {
            // 组件的名字: 变量名
            'userList': userList
        }
    })
</script>

组件的创建和创建Vue实例基本相同,不同的是组件没有 el 属性,data 属性只能使用 function 形式返回数据

在注册的时候组件名和变量名一样的话只用写一个就行,如:components: { userList }

对于组件可使用的命名方式:

  • 全部小写
  • 首字母大写后面都小写
  • 短横线分隔命名法(kebab-case)
  • 驼峰命名法(CamelCase)只允许在脚手架环境下使用
  • 首字母小写的驼峰方式,使用时需在单词分割处使用 - 方式访问

Vue会自动将组件名转换为 kebab-case,因此无论使用驼峰命名还是短横线分隔命名,Vue都能正确解析组件

全局组件:

javascript
// [创建|注册]一个全局组件
Vue.component('myComponent', [组件对象|对象名]);

组件嵌套

在哪里注册的组件就只能在哪里使用

javascript
// 注册组件bx
const bx = {
    template: `
        <div>
            <h1>我是内层组件</h1>
        </div>
    `
    // ...
}

// 注册组件tx
const tx = {
    template: `
        <div>
            <h1>我是外层组件</h1>
            <bx></bx>
        </div>
    `,
    // 注册子组件
    components: {
        bx: bx
    }
    // ...
}

// Vue 实例
const vm = new Vue({
    el: "#app",
    components: {
        tx: tx
    }
})

实例无法使用bx的组件因为bx只在tx内注册所以只有tx可以使用

vm & vc

vm:new Vue({...}); 实例:Vue

vc:Vue.extend({...}); 实例:VueComponent

两个的关系如同兄弟一般,但vc有的vm一定有,vm有的vc不一定有

Vue原型对象

原型对象在JavaScript中是一个重要的概念,它是实现原型继承的基础,在Vue中,原型对象用于定义Vue实例的方法和属性

每个实例都有一个隐式的原型属性 __proto__,指向构造函数的显式的原型属性 prototype

在JS中,每个函数(包括构造函数)都有一个 prototype 属性,而每个对象都有一个 __proto__ 属性

javascript
function Test() { };
const test = new Test();
// 以下数据式对等的
console.log(test.__proto__ == Test.prototype);

原型对象:原型对象是一个普通的对象,包含了一些共享的属性和方法。当创建一个新对象时,它会继承其构造函数的原型对象上的属性和方法。

原型链:每个JavaScript对象都有一个原型对象,通过原型链连接起来。当尝试访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript会沿着原型链向上查找,直到找到对应的属性或方法为止。

原型链的最顶层如果不改动的的情况下是 ObjectObjectprototype 是它自身, __proto__ 为null

单文件组件

概念:一个组件一个文件(xxx.vue)。这是Vue框架规定的,游览器无法直接打开识别。需要Vue进行编译成正常三件套,文件命名规范和组件命名规范相同

文件内容通常包括三块:

html
<template>HTML骨架</template>
<script>JavaScript交互</script>
<style>CSS样式</style>

脚手架

因为游览器不支持ES6的模块化开发,所以编写的vue文件需要使用脚手架(CLI)进行编译成正常的游览器可以识别的HTML文件进行访问

安装脚手架

首先确保安装了Node.js版本建议在V10以上,其次更新以下npm的源

bash
# 查看当前 npm 的源
npm config get registry

# 切换到淘宝镜像源
npm config set registry https://registry.npmmirror.com

# 切换到腾讯镜像源
npm config set registry http://mirrors.cloud.tencent.com/npm/

# 恢复默认的 npm 官方镜像源
npm config set registry https://registry.npmjs.org

安装Vue CLI(全局安装)

bash
# 全局安装命令
npm install -g @vue/cli

# 创建项目
vue create 项目名

# 编译程序并将编译内容放入内置服务器
npm run serve

项目结构

image-20240421175413910

脚手架配置文件官方文档:vue.config.js配置文档

样式编写

在组件中有局部样式(在style标签中加入scoped属性)和全局样式两种,一般来说在 App.vue 中编写全局样式,在子组件中编写局部样式

如果在子组件中编写全局样式,那么如果选择器重名的话会导致一方不会按照自己的样式渲染,规则就是:组件AB都有一个 .clazz 的类名,并已写好样式,如果采用全局样式的话先引入组件A那么就会使用A组件中的样式,先引入组件B那么就会使用B组件中的样式

自定义事件

通过自定义事件来实现组件之间的通信。自定义事件是一种用于组件之间传递信息的机制。可以在一个组件中触发(emit)一个事件,然后在另一个组件中监听(listen)这个事件,并在事件被触发时执行相应的逻辑

父组件:

vue
<template>
  <div>
    <!-- 自定义事件 event -->
    <User @event="contentUser"></User>
  </div>
</template>

<script>
import User from "./components/User.vue";
export default {
  components: {User},
  methods: {
    // 事件触发
    contentUser(a,b,c) {console.log(a,b,c);}
  }
};
</script>

子组件:

vue
<template>
  <div class="s1">
    <button @click="contentuser">输出用户信息</button>
  </div>
</template>

<script>
export default {
  data(){
      return {a: 1, b: 2, c: 3}
  },
  methods: {
    // 事件被执行调用,并传递一些数据(多个)
    contentuser() {this.$emit("event", this.a, this.b, this.c);}
  }
};
</script>

再以上例子中子组件自定义了一个事件 event ,父组件通过 @event 进行绑定并指定回调函数 contentUser ,当子组件触发 event 事件时,父组件就会执行 contentUser 方法

父组件绑定事件 —— 子组件触发事件

使用 refs 绑定

子组件不变,父组件可以使用 this.$refs 进行绑定属性来达到相同的效果

vue
<template>
  <div>
    <User ref="user"></User>
  </div>
</template>

<script>
import User from "./components/User.vue";
export default {
  mounted() {
    // 给 ref="user" 绑定 event 事件,并给事件绑定回调函数 this.contentUser
    this.$refs.user.$on("event", this.contentUser);
  },
  methods: {
    contentUser() {console.log(666);}
  },
  components: {User}
};
</script>

解绑

javascript
// 解绑单个
this.$off("event");
// 解绑多个
this.$off(["event1", "event2"]);
// 全部解绑
this.$off();

在哪里子组件中进行解绑

vue
<button @click="untying">解绑事件</button>
......
untying() {
  // 解绑单个
  this.$off("event");
}

全局事件总线

原理:创建一个全局的VC对象,所有组件共享这一个VC对象,本质上就是使用原型对象(prototype)

javascript
let extend = Vue.extend({});
Vue.prototype.x = new extend;

在 main.js 文件中进行为 vue 扩展一个属性 x 用来进行全局传参数

javascript
// 子组件
this.x.$emit("event", 666);

// 父组件(mounted) - 绑定一个事件
this.x.$on("event", this.contentUser);

消息的订阅与发布机制

安装:npm install pubsub-js

在 main.js 中全局导入

javascript
import pubsub from 'pubsub-js'
Vue.use(pubsub)

订阅

javascript
this.pid = pubsub.subscribe('data', (msg, data) => {
  console.log(data)
});

msg:消息的名字

data:消息的具体数据

当销毁组件时需要取消订阅不然

javascript
pubsub.subscribe(this.pid)

发布

javascript
pubsub.publish('data', '333')

data:发布的名字

333:要被发送的消息

往data中发送一个消息,然后所有订阅data的都会接收到消息

组件嵌套

在一个组件标签中嵌套另一个标签或者组件可以使用 <Clock/> 来接受,如下:

vue
<!-- Test组件 -->
<template>
	<span>我是一个组件</span>
	<Clock />
</template>

<!-- 父组件使用 -->
<template>
	<span>我是父组件</span>
	<Text>
    	<span>我嵌套在了Text组件中</span>
    </Text>
</template>

一般 <Clock/> 只需要写一个就够用了,不管在父组件中嵌套多少个标签,一个 <Clock/> 就可以全部展示