Vue 组件化开发
组件化开发
基本概念
在最开始的时候,已经大概的聊了聊Vue
是单页面开发,用户总是在一个页面上进行操作,看到的不同内容也是由不同组件构成的。
通过用户的操作,Vue
将会向用户展示某些组件,也会隐藏某些组件。
一个Vue的项目就是一个Vue的实例对象。而用户看到的页面则是Vue.component的实例对象。
对于一些复用性高的内容,我们也可以将它封装成一个单独的组件,如导航栏、搜索框、版权信息等等。
所以说组件是Vue
的核心、但是本章节不会讨论Vue
如何实现组件的显示、隐藏,而是聊一聊如何使用组件。
认识组件
根组件
被挂载管理的元素块就是一个根组件。在此根组件中可以嵌套多个子组件,根组件一般来说一个就够了。
<body>
<div id="app">
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el:"#app",
})
</script>
</body>
子组件
子组件即是被嵌套在根组件中的组件,子组件可复用,并且数据都是相互独立的。
子组件可以拥有根组件所拥有的任意的属性,如data/methods/computed/watch/filter
。
需要注意一点:组件名字如果有多个单词构成,应当写成驼峰形式(个人推荐)。
并且在模板中进行引用时,当以-进行分割。
<body>
<div id="app">
<!-- 使用全局组件 -->
<cpn-header></cpn-header>
<cpn-header></cpn-header>
</div>
<script src="./vue.js"></script>
<script>
// 定义组件名字与模板
Vue.component("cpnHeader", {
template: "<div><span>这是一个头部组件</span></div>",
})
const app = new Vue({
el: "#app",
})
</script>
</body>
组件声明
全局子组件
全局子组件会自动进行注册,任何Vue
的实例都能进行使用。
使用Vue.component("name",{})
对其进行声明并注册。
<body>
<div id="app">
<!-- 使用全局组件 -->
<cpn-header></cpn-header>
<cpn-header></cpn-header>
</div>
<script src="./vue.js"></script>
<script>
// 定义组件名字与模板
Vue.component("cpnHeader", {
template: "<div><span>这是一个头部组件</span></div>",
})
const app = new Vue({
el: "#app",
})
</script>
</body>
局部子组件
局部子组件在Vue
实例下使用components
进行注册。它仅供当前实例使用,如下所示:
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<script src="./vue.js"></script>
<script>
// 定义组件
const cpn = {
template:"<div><mark>HELLO,WORLD</mark></div>",
}
const app = new Vue({
el:"#app",
components:{ // 进行注册
cpn, //es6语法
}
})
</script>
</body>
组件模板
根标签
每一个组件对象都应该具有template
属性,它应当是一个HTML
字符串。
并且,要拥有一个根标签在下面,否则将会抛出警告信息:
当发生这样的警告信息,你应该检查一下你的子组件模板,并给他套上根标签,如下所示:
// 定义组件
const cpn = {
template: `
<div>
<div>
HELLO,VUE
</div>
<div>
HELLO,WORLD
</div>
</div>
`,
}
抽离写法
如果在定义组件时在template
属性中写HTML
代码,是不太友好的,你可以将模板抽离出来。
- 使用script标签,并添加type="text/x-template"的属性
- 使用template标签
如下所示,使用<template>
标签配合id
属性将其作为子组件模板:
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<div>
HELLO,VUE
</div>
<div>
HELLO,WORLD
</div>
</div>
</template>
<script src="./vue.js"></script>
<script>
const cpn = {
template: "#cpn",
}
const app = new Vue({
el: "#app",
components: {
cpn,
}
})
</script>
</body>
错误示范
如果你书写了一个DOM
可识别的标签,则是错误的操作,如下所示我使用了main
标签来定义组件模板,很显然DOM
认识它,就会自己先一步渲染它再交由Vue
进行处理,这会导致我们的组件模板会多一次渲染。
<body>
<div id="app">
<cpn-header></cpn-header>
<cpn-header></cpn-header>
</div>
<!--子组件模板-->
<main id="cpn-header-template">
<div>
<span>这是一个头部组件</span>
</div>
</main>
<script src="./vue.js"></script>
<script>
var cpnHeader = {
template: "#cpn-header-template",
}
const app = new Vue({
el: "#app",
components: { // Vue实例内部进行注册
cpnHeader,
}
})
</script>
</body>
子组件的data
上面已经说过,子组件可以拥有data/methods/computed/watch/filter
等对象。
但是需要注意的是子组件的data
必须是一个函数,且必须返回一个Object
。
这是因为每个子组件是相互独立的,如果data
是一个对象,那么获取所有的子组件数据都会引用同一个Object
。
所以子组件中的data
必须是一个函数,因为函数调用时会重新申请内存,返回一个全新的Object
。
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<p>{{childrenMessage}}</p>
</div>
</template>
<script src="./vue.js"></script>
<script>
const cpn = {
template: "#cpn",
data() {
return {
childrenMessage: "子组件数据",
}
}
}
const app = new Vue({
el: "#app",
components: {
cpn,
}
})
</script>
</body>
组件通信
通信意义
组件之间的通信是十分有必要的,例如当Vue
项目启动后,父组件获取到了一些数据,它将如何把这些数据分发到子组件上。
再比如,子组件上产生了一个新数据,它将如何把该数据交由父组件?
props
父组件向子组件传递数据时,则通过props
进行传递。
接收值在父组件模板中采用 - 分割命名,在props中采用驼峰式,在子组件模板中进行使用时采用与props中同样的命名方式
这是因为 - 分割命名方式不是Js的合法变量名,如果props中采用 - 来进行命名,子组件模板在使用时将查询不到该变量名
具体操作步骤如下所示:
<body>
<!--根组件模板-->
<div id="app">
<father></father>
</div>
<!--子组件模板-->
<template id="son">
<div>
<!-- ④ 子组件现在已经可以正常渲染出该值了,使用接收的变量名即可-->
<p>{{childerRecv}}</p>
</div>
</template>
<!--父组件模板-->
<template id="father">
<div>
<!--② 子组件通过v-bind,将父组件中的值存储到props中,需要注意的是再模板中应该使用 - 进行多单词分割-->
<son :childer-recv="fatherMessage"></son>
</div>
</template>
<script src="./vue.js"></script>
<script>
const son = {
template: "#son",
// ③ 现在子组件中已经用childerRecv这个变量名接收到父组件中传递过来的值了,需要注意的是接收时使用驼峰式命名进行接收,否则模板中不会渲染
props: ["childerRecv",]
}
const father = {
template: "#father",
components: {
son,
},
data() {
return {
// ① 父组件的作用域内能够接收到该值了
fatherMessage: {id: 1, name: "yunya", age: 18},
}
}
}
const app = new Vue({
el: "#app",
components: {
father,
}
})
</script>
</body>
上面这个是三层组件嵌套,可能看起来有点绕。我这里有个双层嵌套的,看起来比较简单:
<body>
<div id="app">
<!-- 接收: - 分割命名 -->
<cpn :recv-msg="sendMsg"></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn-template">
<!-- 使用: 与props同样的命名方式 -->
<div><span>接收到的信息:{{recvMsg}}</span></div>
</template>
<script src="./vue.js"></script>
<script>
var cpn = {
// props:使用驼峰式命名,为了子组件模板中能够使用而不产生报错
props: ["recvMsg",],
template: "#cpn-template",
data: function () {
return {}
}
}
const app = new Vue({
el: "#app",
data: {
sendMsg: {
id: 1,
name: "admin",
}
},
components: { // Vue实例内部进行注册
cpn,
}
})
</script>
</body>
画张图,让你更容易理解:
props数据验证
在上述例子中,父组件从后端获取的数据传递到子组件时没由进行任何验证就直接渲染了,这可能导致子组件渲染错误。
所以在子组件接收父组件数据时进行验证是十分必要的流程。
我们可以发现,上述例子的props
接收是一个array
,如果要使用验证,则接收要用Object
,如下示例:
验证项目 | 描述 |
---|---|
type | 一个Array,允许的类型 |
required | 一个Boolen,是否必须传递 |
default | 任意类型 |
validator | 一个Function,返回需要验证的数据字段 |
<body>
<div id="app">
<cpn :recv-msg="sendMsg"></cpn>
</div>
<!-- 模板 -->
<template id="cpn-template">
<div><span>接收到的信息:{{recvMsg}}</span></div>
</template>
<script src="./vue.js"></script>
<script>
var cpn = {
props: { // props是一个Object
recvMsg: { // 参数验证是一个Object
// 允许的类型
type: [Object, Array],
// 是否是必须传递
required: true,
// 如果没有传递的默认值
default() {
return "默认值";
},
// 验证,当验证失败后,会在调试台显示错误
validator(v) {
// v就是父组件传递过来的数据
return v.id;
},
},
},
template: "#cpn-template",
data: function () {
return {}
}
}
const app = new Vue({
el: "#app",
data: {
sendMsg: {
// id: 1,
name: "admin",
}
},
components: { // Vue实例内部进行注册
cpn,
}
})
</script>
</body>
由于父组件中传递的数据不具有id
字段,所以控制台抛出异常,但是不会影响正常渲染:
$emit
当子组件中发生某一个事件,我们可以使用父组件对其进行处理。
使用$emit
进行自定义事件,由父组件进行处理,示例如下:
我们使用子组件定义了一个加法的运算,但是结果却是在父组件中显示,需要子组件将计算结果发送给父组件。
如果自定义事件的单词有多个,则在Js中采用驼峰形式,在html中采用 - 分割形式
<body>
<div id="app">
<!-- 父组件监听add事件,并且交由fatherAdd进行处理-->
<cpn @add="fatherAdd"></cpn>
结果:{{showResult}}
<!-- 结果显示在父组件 但是计算确是在子组件 -->
</div>
<!-- 子组件模板 -->
<template id="cpn-template">
<div>
<input type="text" v-model.number="n1"> + <input type="text" v-model.number="n2">
<button @click="sum">计算</button>
</div>
</template>
<script src="./vue.js"></script>
<script>
var cpn = {
template: "#cpn-template",
data() {
return {
n1: 0,
n2: 0,
}
},
methods: {
sum() {
let sonResult = this.n1 + this.n2;
// 自定义了一个add事件,由父组件进行监听。并且传递了一个值
this.$emit("add", sonResult);
}
}
}
const app = new Vue({
el: "#app",
components: { // Vue实例内部进行注册
cpn,
},
data: {
showResult: 0,
},
methods: {
fatherAdd(v) {
this.showResult = v;
}
}
})
</script>
</body>
还是画一张图梳理一下流程,这其实都是固定的用法:
.sync
如果子组件的数据来自于父组件,当子组件中的数据发生改变时我们也想让父组件中的数据发生同样的改变。
则可以使用.sync
修饰符(尽量少用,会破坏单一性),如下所示:
<body>
<div id="app">
<span>父组件的值:{{num}}</span>
<cpn :son-num.sync="num"></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn-template">
<div>
<span>子组件的值:{{sonNum}}</span>
<p><input type="text" @keyup="changeValue" v-model="newValue" placeholder="输入新的值"></p>
</div>
</template>
<script src="./vue.js"></script>
<script>
var cpn = {
props: ["sonNum",],
template: "#cpn-template",
data: function () {
return {
newValue: this.sonNum,
}
},
methods: {
changeValue() {
this.$emit("update:sonNum", this.newValue)
}
}
}
const app = new Vue({
el: "#app",
data: {
num: 100,
},
components: { // Vue实例内部进行注册
cpn,
},
})
</script>
</body>
流程图如下:
组件访问
$children
有的时候我们想直接通过父组件拿到子组件这个对象,调用其下面的某一个方法,可以使用$children
属性完成操作。
如下所示,父组件想调用子组件中的show()
方法:
一个父组件可能有多个子组件,所以该属性$children是一个Array
<body>
<div id="app">
<button @click="btn">父组件调用子组件方法</button>
<cpn></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn-template">
<div>
<span>{{msg}}</span>
</div>
</template>
<script src="./vue.js"></script>
<script>
var cpn = {
template: "#cpn-template",
data: function () {
return {
msg: "子组件show未调用",
}
},
methods: {
show() {
this.msg = "子组件show已调用";
}
}
}
const app = new Vue({
el: "#app",
components: { // Vue实例内部进行注册
cpn,
},
methods: {
btn() {
// 取出第0个子组件,进行调用其下方法
this.$children[0].show();
}
}
})
</script>
</body>
$refs
上述的访问方法并不常用,因为父组件非常依赖索引值来访问子组件。
使用$refs
来访问子组件就方便的多,我们需要给子组件取一个名字,再用父组件进行调用,这个是非常常用的手段。
<body>
<div id="app">
<button @click="btn">父组件调用子组件方法</button>
<!-- 取名字 -->
<cpn ref="nbcpn"></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn-template">
<div>
<span>{{msg}}</span>
</div>
</template>
<script src="./vue.js"></script>
<script>
var cpn = {
template: "#cpn-template",
data: function () {
return {
msg: "子组件show未调用",
}
},
methods: {
show() {
this.msg = "子组件show已调用";
}
}
}
const app = new Vue({
el: "#app",
components: { // Vue实例内部进行注册
cpn,
},
methods: {
btn() {
// 根据取的名字进行调用
this.$refs.nbcpn.show();
}
}
})
</script>
</body>
$parent
如果在子组件中想拿到父组件的对象,使用$parent
即可,如果存在多层嵌套,它只会拿自己上一层。
一个子组件,在一块被挂载区域中只有一个父组件
<body>
<div id="app">
{{msg}}
<cpn></cpn>
</div>
<!-- 子组件模板 -->
<template id="cpn-template">
<div>
<button @click="btn">子组件调用父组件方法</button>
</div>
</template>
<script src="./vue.js"></script>
<script>
var cpn = {
template: "#cpn-template",
data: function () {
return {}
},
methods: {
btn() {
this.$parent.show();
}
}
}
const app = new Vue({
el: "#app",
data: {
msg: "父组件show未调用",
},
components: { // Vue实例内部进行注册
cpn,
},
methods: {
show() {
// 取出自己的父组件,进行调用其下方法
this.msg = "父组件show已调用";
}
}
})
</script>
</body>
$root
如果存在三级或以上嵌套,可以直接使用$root
来访问根组件。与$parent
使用相同,但是它是具体的一个对象,而并非Array
,所以这里不再做演示。
组件的动态切换
使用:is
属性,可以让我们动态使用不同的组件。
如下,定义了一个input
框和文本域两个组件,当我们点击不同按钮,它就会选择不同的组件。
<body>
<div id="app">
<div :is="choice"></div>
<input type="radio" v-model="choice" value="inputCPN">文本框
<input type="radio" v-model="choice" value="textareaCPN">文本域
</div>
<script src="./vue.js"></script>
<script>
const inputCPN = {
template: "<div><input type='text'/></div>",
}
const textareaCPN = {
template: "<div><textarea></textarea></div>",
}
const app = new Vue({
el: "#app",
data: {
choice: "inputCPN",
},
components: {
inputCPN, textareaCPN,
},
})
</script>
</body>
组件的钩子函数
一个Vue
的项目就是由不同的组件构成,不管是局部注册也好,全局注册也罢,Vue
官方都给你提供了一些钩子函数供你调用,如下图所示:
钩子函数 | 描述 |
---|---|
beforeCreate | 创建Vue实例之前调用 |
created | 创建Vue实例成功后调用(可以在此处发送异步请求后端数据) |
beforeMount | 渲染DOM之前调用 |
mounted | 渲染DOM之后调用 |
beforeUpdate | 重新渲染之前调用(数据更新等操作时,控制DOM重新渲染) |
updated | 重新渲染完成之后调用 |
beforeDestroy | 销毁之前调用 |
destroyed | 销毁之后调用 |
如下所示,定义这几个钩子函数:
<div id="app">
<p>{{msg}}</p>
</div>
<script src="./vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data:{
msg:"HELLO,VUE",
},
beforeCreate() {
console.log("创建Vue实例之前...");
},
created() {
console.log("创建Vue实例成功...");
},
beforeMount() {
console.log("准备渲染DOM...");
},
mounted() {
console.log("渲染DOM完成...");
},
beforeUpdate() {
console.log("准备重新渲染DOM...");
},
updated() {
console.log("重新渲染DOM完成");
},
// 后两个暂时体会不到
beforeDestroy() {
console.log("准备销毁当前实例");
},
destroyed() {
console.log("当前实例销毁完成...");
}
})
</script>
</body>
Vue 组件化开发的更多相关文章
- 二、vue组件化开发(轻松入门vue)
轻松入门vue系列 Vue组件化开发 五.组件化开发 1. 组件注册 组件命名规范 组件注册注意事项 全局组件注册 局部组件注册 2. Vue调试工具下载 3. 组件间数据交互 父组件向子组件传值 p ...
- vue组件化开发实践
前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了一下的内容.首先会对使用Vue进行开发的一些前期需要的技术储备进行简单 ...
- Vue组件化开发
Vue的组件化 组件化是Vue的精髓,Vue就是由一个一个的组件构成的.Vue的组件化设计到的内容又非常多,当在面试时,被问到:谈一下你对Vue组件化的理解.这时候又有可能无从下手,因此在这里阐释一下 ...
- day69:Vue:组件化开发&Vue-Router&Vue-client
目录 组件化开发 1.什么是组件? 2.局部组件 3.全局组件 4.父组件向子组件传值 5.子组件往父组件传值 6.平行组件传值 Vue-Router的使用 Vue自动化工具:Vue-Client 组 ...
- Vue 组件化开发之插槽
插槽的作用 相信看过前一篇组件化开发后,你对组件化开发有了新的认识. 插槽是干什么的呢?它其实是配合组件一起使用的,让一个组件能够更加的灵活多变,如下图所示,你可以将组件当作一块电脑主板,将插槽当作主 ...
- 06Vue.js快速入门-Vue组件化开发
组件其实就是一个拥有样式.动画.js逻辑.HTML结构的综合块.前端组件化确实让大的前端团队更高效的开发前端项目.而作为前端比较流行的框架之一,Vue的组件和也做的非常彻底,而且有自己的特色.尤其是她 ...
- Vue 组件化开发的思想体现
现实中的组件化思想化思想体现 标准(同一的标准) 分治(多人同时开发) 重用(重复利用) 组合(可以组合使用) 编程中的组件化思想 组件化规范:Web Components 我们希望尽可能多的重用代码 ...
- webpack(8)vue组件化开发的演变过程
前言 真实项目开发过程中,我们都是使用组件化的去开发vue的项目,但是组件化的思想又是如何来的呢?下面就从开始讲解演变过程 演变过程1.0 一般情况下vue都是单页面开发,所以项目中只会有一个inde ...
- vue组件化开发-vuex状态管理库
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.Vuex 也集成到 Vue 的官方调试工具 ...
随机推荐
- go 结构体与方法
go 结构体与方法 go 结构体相当于 python 中类的概念,结构体用来定义复杂的数据结构,存储很多相同的字段属性 结构体的定义 1.结构体的定义以及简单实用 package main imp ...
- vue-awesome-swiper ---移动端h5 swiper 和 tab 栏选项联动效果实现
很久之前做小程序时有个类似每日优鲜里储值卡充值界面里的 卡轮播和价格tab栏联动效果,当时觉得新鲜做出来之后也没当回事.直到今天又遇到了一个类似的功能,所以想着总结经验. 实现效果如下图: 图解:点击 ...
- Linux命令的执行
为什么在提示符下命令可以被执行呢? 执行命令过程 输入命令后回车,提请shell程序找到键入命令所对应的可执行程序或代码,并由其分析后提交给内核分配资源将其运行起来 shell本身也是一个程序,只不过 ...
- 数据恢复软件推荐-easyrecovery绿色破解版(附注册码)免费下载
easyrecovery破解版专注于PC端存储数据的抢救恢复,软件的整体界面风格和360杀毒有些许相似,没有看起来像牛皮藓的杂乱广告,只有六个功能按键,对应你所遇到的数据丢失状况级别,点击最为适合的功 ...
- JUC---13各种锁
一.公平锁与非公平锁 公平锁:加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得 非公平锁:加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待 非公平锁性能比公平锁高5~10倍 ...
- ThreeJS系列2_effect插件集简介( 3d, vr等 )
ThreeJS系列2_effect插件集简介( 3d, vr等 ) ThreeJS 官方案例中有一些 js库 可以替代 render 将场景中的物质变换为其他效果的物质 目录 ThreeJS系列2_e ...
- SDOI征途--斜率优化
题目描述 给定长为 n 的数列 a, 要求划分成 m 段,使得方差最小, 输出方差\(*m^2\) 题解 斜率优化好题 准备部分 设第 i 段长为 \(len_i\) 先考虑方差(\(S^2\))的式 ...
- STM32入门系列-复位程序
已经对启动文有了大致了解,再来看看系统在复位过程中做了哪些工作.复位程序如下: 1 ; Reset handler 2 3 Reset_Handler PROC 4 5 EXPORT Reset_Ha ...
- Miller-Rabin 素数检验算法
算法简介 Miller-Rabin算法,这是一个很高效的判断质数的方法,可以在用\(O(logn)\) 的复杂度快速判断一个数是否是质数.它运用了费马小定理和二次探测定理这两个筛质数效率极高的方法. ...
- DP百题练(一)
目录 DP百题练(一) 线性 DP 简述 Arithmetic Progressions [ZJOI2006]物流运输 LG1095 守望者的逃离 LG1103 书本整理 CH5102 移动服务 LG ...