【Vue】详解Vue组件系统
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>
demo:
【注意事项】
1.实际上,我觉得我这篇文章的语言有些过于罗嗦(其实很为难,因为说多了怕罗嗦——”太长不看“),说少了又怕不能完整地表达自己的意思,这是我权衡后的所做的结果
2.文中很多只为知识点服务,跟实际的项目构建存在很大差异,例如我把很多模板都放在template选项中,而实际上我们会使用Vue单文件组件来实现这些
【完】
【Vue】详解Vue组件系统的更多相关文章
- Unity Jobsystem 详解实体组件系统ECS
原文摘选自Unity Jobsystem 详解实体组件系统ECS 简介 随着ECS的加入,Unity基本上改变了软件开发方面的大部分方法.ECS的加入预示着OOP方法的结束.随着实体组件系统ECS的到 ...
- 详解Vue中的computed和watch
作者:小土豆 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.cn/user/2436173500265335 1. 前言 作为一名Vue ...
- 详解Vue.js 技术
本文主要从8个章节详解vue技术揭秘,小编觉得挺有用的,分享给大家. 为了把 Vue.js 的源码讲明白,课程设计成由浅入深,分为核心.编译.扩展.生态四个方面去讲,并拆成了八个章节,如下: 准备工作 ...
- 详解vue的数据binding原理
自从angular火了以后,各种mv*框架喷涌而出,angular虽然比较火,但是他的坑还是蛮多的,还有许多性能问题被人们吐槽.比如坑爹的脏检查机制,数据binding是受人喜爱的,脏检查就有点…性能 ...
- 详解Vue 方法与事件处理器
本篇文章主要介绍了详解Vue 方法与事件处理器 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 方法与事件处理器 方法处理器 可以用 v-on 指令监听 DOM 事件 ...
- 详解vue 路由跳转四种方式 (带参数)
详解vue 路由跳转四种方式 (带参数):https://www.jb51.net/article/160401.htm 1. router-link ? 1 2 3 4 5 6 7 8 9 10 ...
- 详解Vue 如何监听Array的变化
详解Vue 如何监听Array的变化:https://www.jb51.net/article/162584.htm
- Angular6 学习笔记——组件详解之组件通讯
angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...
- [转]详解C#组件开发的来龙去脉
C#组件开发首先要了解组件的功能,以及组件为什么会存在.在Visual Studio .NET环境下,将会有新形式的C#组件开发. 组件的功能 微软即将发布的 Visual Studio .NET 将 ...
- Tomcat详解及SNS系统的部署实现
Tomcat详解及SNS系统的部署实现 http://jungege.blog.51cto.com/4102814/1409290
随机推荐
- hdu5803
hdu5803 题意 给出四个整数 A B C D,问有多少个四元组 (a, b, c, d) 使 a + c > b + d 且 a + d >= b + c ,0 <= a &l ...
- JVM点滴
JVM java拥有GC,为什么还会内存泄漏? 理解什么是内存泄漏: Java中的内存泄露,广义并通俗的说,就是:不再会被使用的对象的内存不能被回收,就是内存泄露. Java为了简化编程工作,对于不再 ...
- git rebase -i命令修改commit历史
[TOC] 修改commit历史的前提 修改历史的提交是可能有风险的,是否有风险取决于commit是否已经推送远程分支,未推送,无风险,如果已推送,就千万不要修改commit了. 修改commit历史 ...
- 在WIN SERVER 2016上安装DOCKER(带过坑)
目录 1 概要 1 1.1 主要优势 1 2 在Windows Server上部署Docker 2 概要 博客使用Word发博,发布后,排版会出现很多问题,敬请谅解 ...
- PHP:phpMyAdmin如何解决本地导入文件(数据库)为2M的限制
经验地址:http://jingyan.baidu.com/article/e75057f2a2288eebc91a89b7.html 当我们从别人那里导出数据库在本地导入时,因为数据库文件大于2M而 ...
- Windows定时关机
用shutdown命令.开始菜单>运行,输入shutdown -s -t 7200 (两个小时之后关机)at 12:00 shutdown -s (12:00关机) 其他设置:shutdown ...
- Hibernate_Validator学习分享
1. Hibernate Validator介绍 1.1 背景 在任何时候,当你要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情.应用程序必须通过某种手段来确保输入进来的数据从 ...
- django favicon配置
其实网站加一个图标,在/static/images/里面放置favicon.ico 1. 直接url里修改 from django.views.generic.base import Redirect ...
- API 接口规范
整体规范建议采用RESTful 方式来实施. 1. 协议 API与用户的通信协议,总是使用HTTPs协议,确保交互数据的传输安全. 2. 域名 应该尽量将API部署在专用域名之下. https://a ...
- module.exports与exports,export和export default
还在为module.exports.exports.export和export default,import和require区别与联系发愁吗,这一篇基本就够了! 一.首先搞清楚一个基本问题: modu ...