详解Vue组件系统

news2024/11/15 1:28:57

Vue渲染的两大基础方式

new 一个Vue的实例

这个我们一般会使用在挂载根节点这一初始化操作上

new Vue({
  el: '#app'
})

复制

注册组件并使用

通过Vue.component()去注册一个组件,你就可以全局地使用它了,具体体现在每个被new的 Vue

实例/注册组件, 的template选项属性或者对应的DOM模板中,去直接使用

注册组件

全局注册

例如,放在通过new创建的Vue实例当中

Vue.component('my-component', {
  template: '<p>我是被全局注册的组件</p>'
})
/*
  Vue.component(组件名称[字符串], 组件对象)
*/

new Vue({
  el: '#app',
  template: '<my-component></my-component>'
})

复制

demo:

又例如,放在另外一个组件中

Vue.component('my-component', {
  template: '<p>我是被全局注册的组件</p>'
})

Vue.component('other-component', {
  template: '<div>我是另一个全局组件:<my-component></my-component></div>'
})

new Vue({
  el: '#app',
  template: '<other-component></other-component>'
})

复制

局部注册

const child = {
  template: '<p>我是局部注册的组件</p>'
}
/*
   通过components选项属性进行局部注册:
   components: {
    组件名称[字符串]: 组件对象
  }
*/
new Vue({
  el: '#app',  template: '<my-component></my-component>',
  components: {
    'my-component': child
  }
})

复制

demo:

通过组件组合(嵌套),构建大型的应用:

const child = {
  template: '<p>我是child组件</p>'
}

const father = {
  template: '<p>我是father组件,我包含了:<child-component></child-component></p>',
  components: {
    'child-component': child
  }
}

const grandFather = {
  template: '<p>我是grandFather组件,我包含了:<father-component></father-component></p>',
  components: {
    'father-component': father
  }
}
new Vue({
  el: '#app',
  template: '<my-component></my-component>',
  components: {
    'my-component': grandFather
  }
})

复制

demo:

通过new创建Vue实例,  全局注册组件,局部注册组件三者的使用频率(场景)

1.new  Vue(),  尽管在Vue官方文档上在相当多的例子中使用到了创建Vue实例这个操作,实际上它的使用可能并没有你想象的那么平凡,在很多时候,它可能就只在挂载根实例的时候使用到

【这段话给写react框架的人看】

对 new Vue()做个最简单的描述!:在使用上类似于ReactDOM.render()...对,就是那个一开始你撸文档的时候觉得好像很重要,但最后发现在整个APP中就只使用了一次的那个顶层API ....

2.全局注册组件的使用也不太频繁,首先来说,如果大量使用全局注册的话,当然容易产生组件的命名冲突,这就意味着你在构建大型组件的时候,你不应该选择用全局注册构建具体的细颗粒度的组件(实际上即使是小型应用也不推荐啦~~~)

那么全局注册组件会在哪里使用到呢?

2.1 有许多可全局复用的公共UI组件,你可能希望通过Vue.component({ ...})的方式全局注册它

2.2 可以很简单地添加第三方UI框架

【对比】大凡使用过一些UI框架的人,都知道一般情况下,使用这些UI组件的方式就是为元素添加类,像这样:

<div class='UI框架中定义的类名'></div>

复制

而在Vue中,你可以通过直接使用组件名称去使用,就和react相关的UI框架一样

3.大多数时候我们通过组件组合的方式构建页面的时候,运用的是局部注册,就像上文所提及的那样

【注意点】

1.注册组件必须发生在根实例初始化前

2.data是函数!

Vue中的props数据流

【写给react学习者们看的】这跟react中设计非常类似,连名称都相同,所以学过react的同学看这里应该会很轻松吧~~

这里要用到Vue的一个选项属性——props;

通过在注册组件中声明需要使用的props,然后通过props中与模板中传入的对应的属性名,去取用这传入的值

例子:

model部分:

Vue.component('my-component', {
  props: ['name', 'birthTime'],
  template: '<p>我叫:{{name}} 我出生于:{{birthTime}}</p>',
  created: function () {
    console.log('在created钩子函数中被调用')
    console.log('我叫:', this.name)
    console.log('我出生于:', this.birthTime)
  }
})

new Vue({
  el: '#app'
})

复制

HTML部分:

<div id='app'>
   <my-component name="彭湖湾" birth-time="1997 -06 - 06"></my-component>
<div id='app'>

复制

demo:

你在注册组件的时候通过props选项声明了要取用的多个prop:

props: ['name', 'birthTime'],

复制

然后在模板中通过属性传值的方式进行数据的注入:

<my-component name="彭湖湾" birth-time="1997 -06 - 06"></my-component>

复制

再然后我们就可以在注册组件的模板中使用到props选项中声明的值了:

template: '<p>我叫:{{name}} 我出生于:{{birthTime}}</p>'

复制

这里要注意几个点:

props取值的方式

1.如果是在注册组件的模板内部,直接通过prop的名称取就OK了,例如

template: '<p>我叫:{{name}} 我出生于:{{birthTime}}</p>'

复制

2.如果在注册组件的其他地方,用this.prop的方式取用,例如

console.log('我叫:', this.name)

复制

props内写的是驼峰命名法,为什么在HTML(模板)中又用了短横线命名法?

(camelCased VS kebab-case)

首先我们知道,Vue组件的模板可以放在两个地方:

1. Vue组件的template选项属性中,作为模板字符串

2.放在index.html中,作为HTML

这里的问题在于,HTML特性是不区分大小写的

所以在Vue注册组件中通用的驼峰命名法,显然不适用于HTML中的Vue模板,所以

在HTML中写入props属性,必须写短横线命名法(就是把原来props属性中的每个prop大写换成小写,并且在前面加个“-”)

总结:

1.在template选项属性中,可以写驼峰命名法,也可以写短横线命名法

2.在HTML(模板)中,只能写短横线命名法,不能写驼峰

下面我就来证明以上两点:

对1

Vue.component('my-component', {
  props: ['name', 'birthTime'],
  template: '<p>我叫:{{name}} 我出生于:{{birthTime}}</p>',
  created: function () {
    console.log('在created钩子函数中被调用')
    console.log('我叫:', this.name)
    console.log('我出生于:', this.birthTime)
  }
})

复制

new Vue({
  el: '#app',
  template: '<my-component name="彭湖湾" birthTime="1997 -06 - 06"></my-component>'
})

复制

demo:

name和birthTime都正常显示,这说明在template模板字符串中,是可以写驼峰的

(请注意到一点:name既符合驼峰写法也符合短横线写法,而birthTime只符合驼峰写法)

JS部分

Vue.component('my-component', {
  props: ['name', 'birthTime'],
  template: '<p>我叫:{{name}} 我出生于:{{birthTime}}</p>',
  created: function () {
    console.log('在created钩子函数中被调用')
    console.log('我叫:', this.name)
    console.log('我出生于:', this.birthTime)
  }
})

 

new Vue({
  el: '#app'
})

复制

HTML(模板)部分

<div id='app'>
       <my-component name="彭湖湾" birthTime="1997 -06 - 06"></my-component>
</div>

复制

demo:

这里有个有趣的现象:name对应的值可以正常地显示,但!birthTime不能

这是因为上文提到的:

name既符合驼峰写法也符合短横线写法,而birthTime只符合驼峰写法,不符合HTML要求的短横线写法

使用v-bind的必要性:props不绑定的前提下,只能被作为字符串解析

Vue.component('my-component', {
  props: ['number'],
  template: '<p>检测number的类型</p>',
  created: function () {
    console.log(typeof this.number)
  }
})

new Vue({
  el: '#app',
  template: '<my-component number="1"></my-component>'
})

复制

demo:

number被检测为字符串,这表明在不加v-bind绑定的情况下,props接受到的都是字符串,(注:如果被作为javacript,”1“会被解析为Number的1,而” ‘1’ “才会被解析为String的1)

没错,仅仅这一点就会让我们非常为难,所以,我们需要使用v-bind:

当使用v-bind的时候,在模板中props将会被作为javascript解析

Vue.component('my-component', {
  props: ['number'],
  template: '<p>检测number的类型</p>',
  created: function () {
    console.log(typeof this.number)
  }
})

new Vue({
  el: '#app',
  template: '<my-component v-bind:number="1"></my-component>'
})

复制

demo:

这可能拓展我们对v-bind的认知

1.用v-bind一般是为了做数据的动态绑定

2.有时v-bind并不为了实现点1,只是纯粹为了让字符串内的内容被当作JS解析罢了

Vue的自定义事件

自定义事件是我非常喜欢的Vue的一大特性!!! 看文档的第一眼我就对它情有独钟(虽然那一天离现在也就几天而已的时间。。。)

先展示代码和demo:

Vue.component('button-counter', {
  template: '<button v-on:click="increment">{{counter}}</button>',
  data: function () {
    return {
      counter: 0
    }
  },

  methods: {
    increment: function () {
      this.counter += 1
      this.$emit('increment-event')
    }
  }
}) 

new Vue({
  el: '#app',
  data: {
    totalCounter: 0
  },

  methods: {
    total_increment: function () {
      this.totalCounter += 1
    }
  }
})

复制

模板HTML部分:

<div id='app'>
      <button>{{ totalCounter }}</button>
      </br>
      <button-counter v-on:increment-event='total_increment'></button-counter>
      <button-counter v-on:increment-event='total_increment'></button-counter>
</div>

复制

demo:

下面两个按钮是两个相同的子组件,并和上面那个按钮共同组成了父组件。

当点击任意一个子组件的按钮,使其加1,都会使得父组件+1,最终:父组件的数值 = 子组件的数值之和

点击下方左边button

 点击下方右边button

自定义事件的原理

通过$emit(event)触发一个自定义事件

然后通过$on(event,callback) 去执行对应的callback(回调函数)

(两个event是字符串,且必须名称相同)

但$on不能在父组件中监听子组件抛出的事件,所以我们要做到这一点,可以在父组件的模板中使用到子组件的时候,直接用v-on绑定 (和$on作用效果一致) 就像上面那样:

<button-counter v-on:increment-event='total_increment'></button-counter>

复制

这样一来,自定义事件的雏形就变得和原生事件一样了

即使这样,上面的代码可能还是有些难理解,我认为比较重要的是这一段:

increment: function () {
      this.counter += 1
      this.$emit('increment-event')
  }

复制

因为我们对于事件的运用主要是:利用事件和函数绑定,从而在事件触发的时候能执行相印的函数

所以! 对于自定义事件,我们要解决的问题就是,“这个事件在什么时候被触发” 在上面的代码中,触发事件的时间(执行 this.$emit('increment-event')的时间)

就恰恰是执行this.counter += 1 的时候

自定义事件的作用

对此,我主要从两点阐述我的观点:(非官方文档内容,自己思考的,觉得不对的可以指出):

自定义事件的作用1 ——“重新定义”了事件监听机制的范围

MDN是这样描述DOM事件的:“DOM事件被发送以通知代码已发生的有趣的事。每个事件都由基于Event接口的一个对象表示”

在我看来:当你使用事件的时候,你可能试图做这样一件事情: 在某一个特定的时间节点(或场景)做某个操作,例如调用一个函数。 而定位这个“时间节点”或“场景”的,就是事件。而我们对事件最喜欢做的事情,就是把事件和某个函数给绑定起来

但我们可能一直都忽略了一个认知:我们认知范围内的事件,好像只有原生事件呀?例如click(点击),focus(聚焦),keydown(按键)

我们认知内的事件,难道只有这些个固定的范围吗?点击是事件,按下键盘按钮是事件。那么,我们能不能人为地定义一个事件呢? 例如上面的,我们通过代码处理,让"某个数据增加1"也作为一个事件,从而去触发一个函数呢?

这,就是自定义事件的目的和魅力

自定义事件的作用2 ——使得父子组件权责明确

就让我们看一下 父组件的这个模板吧,在这里,我们发现:

1.父组件不知道子组件究竟做了什么(increment事件触发前的处理),同时也无需关心

2.父组件只要完成它的任务:在increment事件触发的时候执行对应的函数就足够了

对子组件反是

所以,从这个角度上说,自定义事件使得父子组件“权责明确”

【注意】官方文档的示例可能容易制造这样一种错觉:自定义事件是以原生事件(如click)为基础的,但实际上并不是这样。

虽然自定义事件和原生事件息息相关,但自定义事件并不以原生事件的触发为基础的

Slot的使用

当你试图使用slot的时候,你可能试图做这样一件事情:

用父组件动态地控制子组件的显示的内容

Vue.component('son-component', {
  template: '<div><slot></slot></div>'
})

new Vue({
  el: '#app'
})

复制

模板HTML:

<div id='app'>
      <p>这是slot的内容</p>
      <son-component>
        <p>你好,我是slot</p>
      </son-component>
</div>

复制

demo:

【写给react的同学看的】你可以把slot看作是个3.0版本的props.children

通俗的理解

在父组件模板中使用子组件的时候,如果在子组件里面嵌套了HTML的内容,它会以”props“的方式传递给子组件的模板,并被子组件中的slot接受,例如:

【一个不太专业的说法】

<son-component>
   <p>你好,我是slot</p>
</son-component>

复制

等同于

<son-component  slot = '<p>你好,我是slot</p>'></son-component>

复制

具名slot

为了使增强slot的用法,使父组件能够更加灵活地控制子组件,Vue引入了具名slot

通过name属性,可以把在父组件中控制子组件同时渲染不同的slot

Vue.component('son-component', {
  template: '<div><slot name="h1"></slot><slot name="button"></slot><slot name="a"></slot></div>'
})

new Vue({
  el: '#app'
})

复制

HTML模板部分:

<div id='app'>

      <son-component>

        <h1 slot='h1' >我是标题</h1>

        <button slot='button'>我是按钮</button>

        <a href='#' slot='a'>我是链接</a>

      </son-component>

 </div>

复制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/690740.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是信号槽机制,如何实现,有什么用?(Qt面试题)

1. 什么是信号槽机制&#xff1f; 信号槽机制&#xff08;Signal-Slot mechanism&#xff09;是一种在软件开发中常用的设计模式&#xff0c;用于实现对象间的通信和事件处理。该机制最初由Qt框架引入并广泛应用&#xff0c;后来也被其他编程框架和库所采用。 信号槽机制通过定…

这样做,轻松拿捏阻焊桥!

PCB表面的一层漆&#xff0c;称为阻焊油墨&#xff0c;也就是PCB线路板阻焊油墨。阻焊油墨是PCB线路板中非常常见、也是主要使用的油墨&#xff0c;一般90%都是绿色&#xff0c;但也有杂色油墨&#xff1a;红色、蓝色、黑色、白色、黄色等。 阻焊油墨的作用就是绝缘&#xff0…

postman持续集成-Jenkins手动构建

Jenkins启动 在jenkins.war文件所在的目录输入cmd打开终端输入: java -jar jenkins.war启动服务,启动后终端的窗口不要关闭 在浏览器地址栏输入:localhost:8080 准备工作 打开已完成并测试无误的postman项目脚本,再次执行测试 导出测试用例集和测试环境两个文件,注意全部…

【换根DP】CF1324F

Maximum White Subtree - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题意&#xff1a; 思路&#xff1a; 先去树形DP求出DP值&#xff0c;这很好求 设dp[u]为以u为根的子树中白-黑的最大值 初始化就是&#xff1a;如果u本身是黑&#xff0c;那dp[u]-1&#xff0c;否则dp…

K8s(Kubernetes)学习(三):pod概念及相关操作

1 什么是 Pod 摘取官网: https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/#working-with-pods 1.1 简介 Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。Pod&#xff08;就像在鲸鱼荚或者豌豆荚中&#xff09;是一组&#xff08;一个或多个&…

【Java面试题】Java基础——面向对象

文章目录 重载和重写的区别★★★Java的三大特性请说明一下Super关键字的作用&#xff1f;static关键字的作用&#xff1f;final关键字的作用&#xff1f;super关键字和this关键字的作用&#xff1f;面向对象的三大特性★★★成员变量和局部变量的区别&#xff1f;Java能实现多…

JMeter请求头添加删除方法(解决请求头类型冲突)

JMeter请求头添加删除方法&#xff08;解决请求头类型冲突&#xff09; 1. 为什么会有冲突 请求头的Content-Type类型在做上传和请求图片地址是&#xff0c;请求头类型是不一样的 请求图片地址&#xff1a;Content-Type: image/jpeg 一般的Restful接口&#xff1a;Content-Ty…

Linux使用第三方库链接的使用方式——静态式

目录 二.第三方库为静态库时&#xff1a; 方法1&#xff1a; 两个窗口去分别模拟两个窗口公司A(客户端)&#xff0c;公司B(服务端)的视角案例实现&#xff1a; 方法2——优化&#xff1a;该方法在上述方法1的第10步后开始进行&#xff1a; 这里强调一个问题&#xff1a; 今天…

linux上虚拟机vmware-workstation离线安装详细教程

linux上虚拟机vmware-workstation详细教程 一、VMWare基本介绍二、VMWare下载2.1 查看本地系统信息2.2 选择及下载合适的版本 三、VMWare安装3.1 安装依赖库3.2 vmware安装3.3 验证安装3.4 异常及解决方案3.4.1 Failed to start SYSV3.4.2 GLib does not have GSettings suppor…

编程:“上学时如果遇到自己,我会更早的成为我?”

作者&#xff1a;小傅哥 博客&#xff1a;https://bugstack.cn 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 如果当年我遇到自己&#xff0c;我会更早的成为我&#xff1f; 你觉得一瞬间的技术能力提升&#xff0c;是必须经历过过吃苦或者…

Python安装教程(初学者很实用)

一、Python环境搭建 1、下载Python 进入Python官网下载安装包 https://www.python.org/ 2、选择合适的版本&#xff0c;点击下载 3、安装Python 双击安装软件 等待安装完成 出现【setup was successful】&#xff0c;表示安装成功 4、检验是否安装成功 通过【winr】调出…

llama.cpp LLM模型 windows cpu安装部署

参考&#xff1a; https://www.listera.top/ji-xu-zhe-teng-xia-chinese-llama-alpaca/ https://blog.csdn.net/qq_38238956/article/details/130113599 cmake windows安装参考&#xff1a;https://blog.csdn.net/weixin_42357472/article/details/131314105 llama.cpp下载编…

DAY30:回溯算法(五)分割回文串+复原IP地址(注意复盘,经典的分割子集问题)

文章目录 131.分割回文串思路伪代码重要问题1&#xff1a;如何通过startIndex判断已经切到结束了重要问题2&#xff1a;为什么[startIndex,i]能够表示当前遍历的子串substr的用法 std::string的成员函数std::string完整版debug测试**Line 4: Char 27: error: expected unqualif…

MySQL------事务并发问题和解决办法

一、事务并发问题 1. 脏读 含义&#xff1a;在事务过程中&#xff0c;A事务还未提交&#xff0c;B事务就读到了A事务未提交的数据。 一个事务正在对一条记录做修、改&#xff0c;在这个事务完成并提交前&#xff0c;这条记录的数据就处于不一致状态&#xff1b;这时&#xff0c…

PHP 文心千帆API接口对接

一&#xff1a;API 调用流程简介 创建一个智能云应用。根据实际需求创建智能云应用。创建成功后&#xff0c;获取AppID、API Key、Secret Key 等信息。API 授权。对应用的 AppID 进行授权。获取接口访问凭证 access_token 。根据第1步获取的 API Key 和 Secret Key &#xff0c…

外观模式(Facade)

定义 外观是一种结构型设计模式&#xff0c;能为程序库、框架或其他复杂类提供一个简单的接口。 前言 1. 问题 假设你必须在代码中使用某个复杂的库或框架中的众多对象。正常情况下&#xff0c;你需要负责所有对象的初始化工作、管理其依赖关系并按正确的顺序执行方法等。 …

centos 手动下载包安装elasticsearch8.7.0, 并设置密码访问

访问下载网站 Elasticsearch 8.7.0 | Elastic 进入centos , 创建目录es mkdir /opt/es cd /opt/es wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.7.0-linux-x86_64.tar.gz 3、解压目录 es870 tar -zxvf elasticsearch-8.7.0-linux-x86_64.tar…

二叉树的堂兄弟节点——深度优先搜索+广度优先搜索

二叉树的堂兄弟节点 题目描述 在二叉树中&#xff0c;根节点位于深度 0 处&#xff0c;每个深度为 k 的节点的子节点位于深度 k1 处。 如果二叉树的两个节点深度相同&#xff0c;但 父节点不同 &#xff0c;则它们是一对堂兄弟节点。 我们给出了具有唯一值的二叉树的根节点 …

实现分布式事务的新标杆:RocketMQ的全面解析与应用指南

在分布式系统中&#xff0c;实现事务的一致性和可靠性是一项重要的挑战。本文将详细介绍如何利用 RocketMQ 的半消息机制来实现分布式事务&#xff0c;并提供具体的代码示例和最佳实践。 1. 引言 在分布式系统中&#xff0c;事务处理是一项复杂而关键的任务。传统的 ACID 事务…

基于SpringCloud微服务毕业论文管理系统设计与实现

一、概述 1.1 课题背景及意义 随着学校不断扩大和学生人数的猛增,关于各类教学信息也越来越多。毕业论文的管理也成为了不可避免的一道关卡,学生需要及时获取论文相关进度,学校的管理者要求能方便对论文进行处理。基于这些需求,开发一个实用的微服务管理系统,以满足双方…