Vue学习之组件基础学习
目录
- 组件开发
- 一、组件的结构与注册
- 1.1 组件的data必须是函数,且必须返回一个实例对象(对象内部保存着数据)
- 1.2 组件的组织
- 1.3 局部组件与全局组件
- “我的附庸的附庸不是我的附庸”
- 1.4 组件间的样式冲突问题
- 样式穿透
- 二、动态组件
- 2.1 keep-alive
- 2.1.1 keep-alive的生命周期
- 2.1.2 keep-alive的include和exclude属性
- 2.1.3 组件注册名称和组件声明name的区别
- 三、Props(重要,父传子)
- 3.1 传递静、动态props
- 3.2 Props验证
- 四、自定义事件(子传父)
- 五、异步组件
- 加载与错误状态
组件开发
vue是一个支持组件化开发
的前端框架。组件化开发指的是根据封装
的思想,把页面上可重用的UI结构封装为组件,从而方便项目的开发和维护。vue 中规定组件的后缀名是.vue
。
每个.vue 组件都由3部分构成,分别是:①template
:组件的ui模板结构;②script
:组件的JavaScript行为;③style
:组件的样式。
//.vue文件模板
<template><!-- 只能有一个根节点,即最外层只有一个div盒子 --><div></div>
</template>
<script>export default{data(){ return { } },methods: { },computed: { },//...}
</script>
<style scoped>
/* 默认语法是css,也可以进行更改:<style lang="less"> */
</style>
注意:组件是可复用的 Vue 实例,所以它们与
new Vue
接收相同的选项,例如data
、computed
、watch
、methods
以及生命周期钩子
等。仅有的例外是像el
这样根实例特有的选项。
一、组件的结构与注册
1.1 组件的data必须是函数,且必须返回一个实例对象(对象内部保存着数据)
我们容易发现上述vue文件的 data
并不是像我们之前遇到过的那样直接提供一个对象,现在变成了一个函数。因为组件不可以访问Vue实例中的数据,所以组件应该有保存自己数据的地方。所以一个组件的 data
选项必须是一个函数,这样每个实例就可以维护一份被返回对象的独立的拷贝(即复用多次的同一个组件之间相互独立互不影响)。否则,如果我们在多次复用某个组件时,其中一个变化则会影响到其他实例的变化。
1.2 组件的组织
通常一个应用会以一棵嵌套的组件树的形式来组织:
其中最上面的节点就是我们所有组件的根组件,然后在根组件App.vue中使用其他组件:
<template><div id="app"><h2>App.vue组件</h2><!-- 3、以标签的形式使用注册的组件 --><ComponentA></ComponentA><ComponentB></ComponentB></div>
</template><script>
//根组件使用组件的三个步骤:
//1、使用import语法导入需要的组件
import ComponentA from '@/components/a.vue'
import ComponentB from '@/components/b.vue'export default {//2、使用components节点注册组件components: {//ComponentA: ComponentA的缩写ComponentA,ComponentB}
}
</script>
1.3 局部组件与全局组件
上面使用组件的第二个步骤中“使用components节点注册组件”中在组件的 components
节点下注册的组件是私有子组件,是局部的。也就是说,在其他组件中无法使用。例如:在组件A的components节点下注册了组件F,则组件F只能用在组件A中,不能被用在组件C中。要想在其他组件中使用的话,因此通过 Vue.component()
方法注册全局组件。全局组件注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中。
import ComponentA from '@/components/a.vue'
//第一个参数是组件的名称,第二个参数则是导入的需要全局注册的组件
//定义组件名的方式有两种:①kebab-case:短横线分隔命名和②PascalCase:首字母大写命名
Vue.component('my-component-name', ComponentA)
//等效于 Vue.component('MyComponentName', ComponentA)
“我的附庸的附庸不是我的附庸”
例如,在根组件App.vue下注册了A和B两个组件,又在A下注册了组件C,可以发现直接在App.vue中使用 <C></C>
将无法编译,即无法使用子组件的子组件。也正如局部组件一样,组件C只能在A中使用,无法在其他组件中使用。
1.4 组件间的样式冲突问题
默认情况下,写在.vue组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。导致组件之间样式冲突的根本原因是:在SPA中,所有组件的DOM结构,都是基于唯一的index.html页面进行呈现的,而每个组件中的样式都会影响整个index.html页面中的DOM元素。举个例子,比如父组件和子组件中都有着p标签,此时如果给父组件的p标签加上样式,则会导致子组件的p标签也会发生变化,这就是样式冲突问题。
解决办法:为每个组件分配唯一的自定义属性,在编写组件样式时,通过属性选择器来控制样式的作用域。这种自定义属性名前面加上 data-v-
,所有data-v-*属性都存放在 dataset
中,获取/赋值都需要通过dataset。
<!-- 根组件/父组件 -->
<template><div id="app" data-v-tip><h2 data-v-tip>App根组件</h2><p data-v-tip>根组件</p></div>
</template>
<style lang="less">p[data-v-tip] {color: red,}
</style>
<!-- 子组件 -->
<template><div data-v-tip2><p data-v-tip2>子组件</p></div>
</template>
上面这种人工给每个组件分配自定义属性的方式较为繁琐,因此,为了提高开发效率和开发体验,vue为 style
节点提供了 scoped
属性,从而防止组件之间的样式冲突问题。style节点的 scoped
属性,用来自动为每个组件分配唯一的“自定义属性”,并自动为当前组件的 DOM标签和 style 样式应用这个自定义属性,防止组件的样式冲突问题。
<style lang="less" scoped>/* ... */
</style>
样式穿透
如果给当前组件的style节点添加了scoped 属性,则当前组件的样式对其子组件是不生效的。如果想让某些样式对子组件生效,可以使用 /deep/
深度选择器。
/* 根组件/父组件 */
<style lang="less" scoped>
p {font-size: 15px;color: #e81410;
}
/* 给子组件中类名为b的p元素进行样式穿透 */
/* vue2写法 */
/deep/ .b {color: #e81410;
}
/* vue3写法 */
:deep(.b) {color: #e81410;
}
</style>
注意:
/deep/
是vue2.x中实现样式穿透的方案,在vue3.x中推荐使用:deep()
替代/deep/。
二、动态组件
可以通过 Vue 的 <component>
元素加一个特殊的 is
属性来实现在一个多标签的界面里进行不同组件间的动态切换。vue提供了一个内置的 <component>
组件,专门用来实现动态组件的渲染。
<template><div id="app"><h2>Vue动态组件</h2><hr /><!-- 渲染一个“元组件”为动态组件。依 is 的值,来决定哪个组件被渲染。 --><component :is="itemName"></component><button @click="itemName='A'">切换A页面</button><button @click="itemName='B'">切换B页面</button><button @click="itemName='C'">切换C页面</button></div>
</template>
<script>
import A from '@/components/a.vue'
import B from '@/components/b.vue'
import C from '@/components/c.vue'
export default {name: 'App',data(){return {itemName: 'A'}},components: {A,B,C}
}
</script>
2.1 keep-alive
之前在一个多标签的界面中使用 is
属性来切换不同的组件,但是我们可以发现:当在这些组件之间切换的时候,切换之后前一个组件会被销毁,导致我们在切换回上一个组件时会重新渲染该组件(即在该组件操作之后先切换到其他组件在切换回该组件时,这些操作不会被保存)。重新创建动态组件的行为通常是非常有用的,但是在某些情况下,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,可以用一个 <keep-alive>
元素将其动态组件包裹起来。使用该元素包裹后在进行组件间的切换时,上一个组件将处于失活(inactive
)状态,而不会被销毁,从而达到保存该组件的状态的效果。
<template><div id="app"><h2>Vue动态组件</h2><hr /><!-- keep-alive的使用 --><keep-alive><component :is="itemName"></component></keep-alive><button @click="itemName='A'">切换A页面</button><button @click="itemName='B'">切换B页面</button><button @click="itemName='C'">切换C页面</button></div>
</template>
注意:这个
<keep-alive>
要求被切换到的组件都有自己的名字,不论是通过组件的name
选项还是局部/全局注册。
2.1.1 keep-alive的生命周期
当组件被缓存(失活)时,会自动触发组件的 deactivated
生命周期函数;当组件被激活时,会自动触发组件的 activated
生命周期函数。当组件第一次被创建的时候,既会执行 created
生命周期,也会执行 activated
生命周期;当组件被激活的时候,只会触发 activated
生命周期,不再触发created
,因为组件没有被重新创建。
注意:只有
<keep-alive>
包裹的的组件才有deactivated
和activated
生命周期!
2.1.2 keep-alive的include和exclude属性
include
属性用来指定只有名称匹配的组件会被缓存,多个组件名之间使用英文的逗号分隔。没有include属性的话默认所有组件在切换后都会被缓存。
<keep-alive include="A,B"><component :is="itemName"></component>
</keep-alive>
exclude
属性用来指定哪些组件不需要被缓存,多个组件名之间使用英文的逗号分隔。没有exclude属性的话默认所有组件在切换后也都会被缓存,且include属性和exclude属性不能同时使用
。
<keep-alive exclude="C"><component :is="itemName"></component>
</keep-alive>
2.1.3 组件注册名称和组件声明name的区别
// App.vue 如果在“声明组件”的时候,没有为组件指定 name名称,则组件的名称默认就是注册时候的名称components: {A,B,C}
// c.vue 声明组件name名称
export default {// 当提供了name属性之后,组件的名称,就是name属性的值name: 'myC'
}
组件的“注册名称”的主要应用场景是以标签的形式把注册好的组件渲染和使用到页面结构之中;组件声明时候的“name”名称的主要应用场景是结合 <keep-alive>
标签实现组件缓存功能以及在调试工具中看到组件的 name名称。
三、Props(重要,父传子)
3.1 传递静、动态props
为了提高组件的复用性,在封装通用组件的时候,组件的DOM结构、Style样式要尽量复用且组件中要展示的数据,尽量由组件的使用者提供。为了方便使用者为组件提供要展示的数据,于是就引入了 props
。props
是组件的自定义属性,组件的使用者可以通过props把数据传递到子组件内部,供子组件内部进行使用(即子组件利用props来接收父组件传递过来的数据),合理地使用props可以极大的提高组件的复用性!另外,可以给 props
传入一个静态的值以及可以通过 v-bind
动态赋值。以一个计数的Count组件:Count.vue为例:
<template>
<div><!-- <p>count的值是: {{ count }}}</p> --><!-- <button @click="count += 1">+1</button> --><!-- 缺点:不同场景下多次使用该组件时可能会有不同的初始值,但data中的count已经固定了初始值改进:使用props --><!-- <p>count的值是: {{ init }}</p> --><!-- <button @click="init += 1">+1</button> --><!-- 缺点:props是只读的,因此直接更改props会报错 改进:要想修改props中的值,可以把props中的值转存到data中,因为data中的数据是可读可写的--><p>count的值是: {{ count }}</p><button @click="count += 1">+1</button><!-- 传递静态props --><p>致橡树的作者:{{author}}</p>
</div>
</template>
<script>
export default {//props中的数据,可以直接在模板结构中被使用props: ['init','author'],data(){return {// count: 0//把init的值转存到data中//console.log(this) this指向vue实例,this下有init和countcount: this.init}},
}
</script>
在a.vue文件中使用count组件,并动态赋值props:
<!-- a.vue -->
<template><div><!-- 传递动态props:使用v-bind属性绑定的形式,为组件动态绑定 props 的值--><Count :init="3"></Count></div>
</template>
注意:1、一个组件默认可以拥有任意数量的 prop,任何值(数字,布尔值,数组,对象以及一个对象的所有属性等)都可以传递给任何 prop。能够在组件实例中访问这个值,就像访问 data 中的值一样。
2、props是只读的,不能直接修改 props的值,否则终端会报错!
3、如果父组件给子组件传递了未声明的props属性,则这些属性会被忽略,无法被子组件使用。
3.2 Props验证
Props验证用来在封装组件时对外界传递过来的props数据进行合法性的校验,从而防止数据不合法。使用 对象类型
的props 节点而不是一个字符串数组,可以对每个prop进行数据类型的校验。其中键是prop属性,值是类型。如果传递的值的类型与prop的类型不合法,则会在浏览器的console调试面板中会提示警告信息: Invalid prop
。
对象类型的props节点提供了多种数据验证方案,如①基础的类型检查;②多个可能的类型;③必填项校验;④属性默认值;⑤自定义验证函数等。
export default {props: {//1、基础的类型检查:可以直接为组件的prop属性指定基础的校验类型,从而防止组件的使用者为其绑定错误类型的数据。//type类型可以是String、Number、Boolean、Array、Object、Date、Function、Symbol,propA: String,//2、多个可能的类型:如果某个prop属性值的类型不唯一,此时可以通过数组的形式为其指定多个可能的类型。propB: [String,Number],//3、如果组件的某个prop属性是必填项,必须让组件的使用者为其传递属性的值。propC: {type: String,required: true //当前属性的值是必填项,没有指定则会在终端进行警告提示(开发环境下)},//4、在封装组件时,可以为某个prop属性指定默认值。//带有默认值的数字propD: {type: Number,default: 100 //如果没有指定propD的值,则propD属性的默认值为100},// 带有默认值的对象propE: {type: Object,default() { // 对象或数组默认值必须从一个工厂函数获取return { message: 'hello' }}},//5、为prop属性指定自定义的验证函数,从而对prop属性的值进行更加精确的控制propF: {validator(value) {return ['success', 'warning', 'danger'].includes(value) // 这个值必须匹配下列字符串中的一个}}}
}
注意:在①基础的类型检查中,
null
和undefined
会通过任何类型验证。另外,prop 会在一个组件实例创建之前进行验证,所以实例的 property (如 data、computed 等) 在default
或validator
函数中是不可用的。
四、自定义事件(子传父)
像常见的click、键盘事件等都是js内置的事件,都是在html元素上使用的;而自定义事件则是区别于这些内置的事件且自定义事件是只能应用在组件上。**它与 props
同样用于组件通信,但是props是 父传子
,而自定义事件则是 子传父
**。使用自定义事件的三个步骤是:在封装组件时,要先声明自定义事件再触发,然后在使用组件时再监听自定义事件。
开发者为自定义组件封装的自定义事件,必须事先在 emits
节点中声明;在emits 节点下声明的自定义事件,可以通过 this.$emit('自定义事件的名称')
方法进行触发(在调用 this.$emit()
方法触发自定义事件时,可以通过第2个参数为自定义事件传参);在使用自定义的组件时,可以通过v-on的形式监听自定义事件。
五、异步组件
在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。Vue 提供了 defineAsyncComponent
方法来实现此功能:
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() => {return new Promise((resolve, reject) => {// ...从服务器获取组件resolve(/* 获取到的组件 */)})
})
// ... 像使用其他一般组件一样使用 `AsyncComp`
defineAsyncComponent
方法接收一个返回Promise的加载函数。这个 Promise 的 resolve 回调方法应该在从服务器获得组件定义时调用。你也可以调用 reject(reason) 表明加载失败。而ES模块动态导入
也会返回一个 Promise,所以多数情况下我们会将它和defineAsyncComponent 搭配使用。
import { defineAsyncComponent } from 'vue'const AsyncComp = defineAsyncComponent(() =>import('./components/MyComponent.vue')
)
最后得到的 AsyncComp 是一个外层包装过的组件,仅在页面需要它渲染时才会调用加载内部实际组件的函数。它会将接收到的 props 和插槽传给内部组件,所以你可以使用这个异步的包装组件无缝地替换原始组件,同时实现延迟加载。与普通组件一样,异步组件可以使用 app.component()
全局注册,也可以在局部注册组件时使用 defineAsyncComponent
。异步组件可以搭配内置的 <Suspense>
组件一起使用。
加载与错误状态
异步操作不可避免地会涉及到加载和错误状态,因此 defineAsyncComponent()
也支持在高级选项中处理这些状态:
const AsyncComp = defineAsyncComponent({// 加载函数loader: () => import('./xxx.vue'),// 加载异步组件时使用的组件loadingComponent: LoadingComponent,// 展示加载组件前的延迟时间,默认为 200msdelay: 200,// 加载失败后展示的组件errorComponent: ErrorComponent,// 如果提供了一个 timeout 时间限制,并超时了,则也会显示这里配置的报错组件,默认值是:Infinitytimeout: 3000
})
如果提供了一个加载组件,它将在内部组件加载时先行显示。如果提供了一个报错组件,则它会在加载器函数返回的Promise抛错时被渲染。你还可以指定一个超时时间,在请求耗时超过指定时间时也会渲染报错组件。
Vue学习之组件基础学习相关推荐
- 医用计算机基础学习心得,计算机基础学习心得.doc
计算机基础学习心得 计算机基础学习心得一:计算机基础学习心得 当今社会,到处充满机会与挑战.知识是我们面对这一切的筹码,而计算机知识则更为重要.如何在本科函授这样一种形式下,学好计算机课程,这是摆在我 ...
- 学习笔记-零基础学习人工智能(0)
学习笔记-零基础学习人工智能(0) 背景 规划 背景 作为物理专业的大龄青年,由于兴趣爱好想学习下人工智能.主要感兴趣的方向是对抗样本生成.自己也做了一些了解,但是发现千头万绪,不懂的东西太多.为了梳 ...
- Python学习教程(Python学习路线_Python基础学习教程_Python视频教程):初学者新手怎样快速入门Python
Python学习教程(Python学习路线_Python基础学习教程_Python视频教程):初学者新手怎样快速入门Python? 人生苦短,我用Python!!!短短几个字,现在在各大学习类平台随处 ...
- Vue框架的入门基础学习
当创建新的vue项目时,遇到无法加载文件 E:\node\node_global\vue.ps1,因为在此系统上禁止运行脚本的错误,以下为解决办法. vue中文文档 菜鸟教程:Vue.js 该笔记只是 ...
- 大数据生态系统组件基础学习
这是学习大数据这一整套各种组件MySQL,hive,spark,mapreduce等等的一些基础语法,日常更新,有不对的地方欢迎指正,资料也是自己收集来的,若有侵权,联系我立马删. MySQL (一) ...
- Scala学习(一)--Scala基础学习
Scala基础学习 摘要: 在篇主要内容:如何把Scala当做工业级的便携计算器使用,如何用Scala处理数字以及其他算术操作.在这个过程中,我们将介绍一系列重要的Scala概念和惯用法.同时你还将学 ...
- 全栈学习之CSS基础学习
CSS基础学习 1. CSS导入方式 行内样式 内部样式 外部样式 2. 三种基本选择器 2.1 标签选择器 2.2 类选择器 2.3 id选择器 2.4 关联 3. 层次选择器 3.1 后代选择器 ...
- ①小白学习Linux之基础学习★
Linux基础学习√ 1.centos6和7的变化 1.1.文件系统 1.2.防火墙.内核版本.默认数据库 1.3.时间同步,修改时区,修改语言 1.4.主机名 1.5.网络服务管理 1.6.网络设置 ...
- Vue第二部分(1):组件基础学习
在大型应用开发的时候,页面可以划分成很多部分.往往不同的页面,也会有相同的部分.例如可能会有相同的头部导航. 但是如果每个页面都独自开发,这无疑增加了我们开发的成本.所以为了提高代码复用,我们会把页面 ...
最新文章
- 是时候开始使用JavaScript严格模式了怎样启用javascri
- Xcode10 闪退问题
- Bit-Z 关于交易隐藏及下线说明
- 理解webpack原理,手写一个100行的webpack
- 1021. Deepest Root (25)
- matlab五子棋_应用 | 五子棋游戏——没人能在我的程序里打败我
- php写带分页的留言板,php中分页程序之基于留言板详解_PHP教程
- 工科数学分析无穷级数总结
- 识别JVM –比预期的要复杂
- 嵌入式实训-day1
- php 图片印章_php工具型代码之印章抠图
- MySQL小黑框怎么打开_打开你的小黑框命令行,来跟我一起嗨嗨嗨
- android https详解,如何使用HTTPS和HTTP来解析Android中的JSON数据?
- DevExpress小结(简略)
- 神奇的机器人评课_《机器人》教学反思
- Docker系列(八)Docker的CS模式、守护进程的配置和操作
- java 分布式同步锁_java编程进阶之路:回归锁的本质,探索分布式锁之源头
- 32位程序如何在64位系统上运行_32位支持:使用 GCC 交叉编译 | Linux 中国
- python3.6+Appium实现手机微信自动回复
- 汽车抛负载瞬态7637-5A/5B测试,您不知道的都在这里