作者:新洋之家140 | 来源:互联网 | 2023-10-14 16:47
文章目录Vue的组件化一、认识组件化1.什么是组件化2.组件化思想二、注册组件1.组件的使用步骤三、组件其他补充1.全局组件和局部组件2.父组件和子组件3.组件的语法糖写法4.组件
文章目录
- Vue的组件化
- 一、认识组件化
- 二、注册组件
- 三、组件其他补充
- 1. 全局组件和局部组件
- 2. 父组件和子组件
- 3. 组件的语法糖写法
- 4. 组件模板抽离的写法
- 四、组件数据存放
- 1. 组件能访问vue中的数据吗
- 2. 组件中的data为什么必须是函数
- 五、父子组件通信
- 1. 父传子props
- 2. props的驼峰命名
- 3. 子传父(自定义事件)
- 4. 数据双向绑定案例
- 5. 父访子-\$children-\$refs
- 6. 子访父-\$parent-\$root
- 六、插槽slot
- 1. 为什么要使用插槽
- 2. 插槽的封装和基本使用
- 3. 具名插槽
- 4. 作用域插槽
Vue的组件化
一、认识组件化
1. 什么是组件化
- 如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
- 但如果,我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。
- 我们将一个完整的页面分成很多个组件。
- 每个组件都用于实现页面的一个功能块。
- 而每一个组件又可以进行细分。
2. 组件化思想
- 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。
- 任何的应用都会被抽象成一颗组件树。
- 组件化思想的应用:
- 有了组件化的思想,我们在之后的开发中就要充分的利用它。
- 尽可能的将页面拆分成一个个小的、可复用的组件。
- 这样让我们的代码更加方便组织和管理,并且扩展性也更强。
二、注册组件
1. 组件的使用步骤
- 创建组件构造器
- Vue.extends()
- 调用Vue.extend()创建的是一个组件构造器
- 通常在创建组件构造器时,传入template代表我们自定义组件的模板
- 该模板就是在使用到组件的地方,要显示的HTML代码
- 事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础
- 注册组件
- Vue.component()
- 调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称
- p所以需要传递两个参数:1、注册组件的标签名 2、组件构造器
- 使用组件
- 组件必须挂载在某个Vue实例下,否则它不会生效
<div id&#61;"app"><hello-vue>hello-vue><hello-vue>hello-vue><hello-vue>hello-vue><hello-vue>hello-vue>div><script src&#61;"../js/vue.js">script><script>const cpnC &#61; Vue.extend({template:&#96;hello vue
&#96;})Vue.component(&#39;hello-vue&#39;,cpnC)const app &#61; new Vue({el: &#39;#app&#39;,data: {msg: &#39;hello vue!&#39;}})script>
三、组件其他补充
1. 全局组件和局部组件
上述方式是全局组件&#xff0c;可以在多个vue实例中使用。&#xff08;实际开发一般从头到尾就一个vue实例&#xff09;
局部组件注册&#xff1a;
const app &#61; new Vue({el: &#39;#app&#39;,data: {msg: &#39;hello vue!&#39;},components:{cpn:cpnC}
})
2. 父组件和子组件
<div id&#61;"app"><cpn1>cpn1>div><script src&#61;"../js/vue.js">script><script>const cpnC2 &#61; Vue.extend({template: &#96;hello vue2
&#96;})const cpnC1 &#61; Vue.extend({template: &#96;hello vue
&#96;,components:{cpn2:cpnC2}})const app &#61; new Vue({el: &#39;#app&#39;,data: {msg: &#39;hello vue!&#39;},components: {cpn1: cpnC1}})script>
3. 组件的语法糖写法
省去extend
Vue.component(&#39;hello-vue&#39;,{template:&#96;hello vue
&#96;
})
const app &#61; new Vue({el: &#39;#app&#39;,data: {msg: &#39;hello vue!&#39;},components:{cpn:{template:&#96;hello vue
&#96;}}
})
4. 组件模板抽离的写法
在
标签中写&#xff0c;两种方式&#xff1a;
然后使用id选择器放到模板后面
四、组件数据存放
1. 组件能访问vue中的数据吗
- 组件无法访问vue实例中的数据
- 组件的数据存放在组件中的data里&#xff0c;数据为函数格式&#xff0c;返回一个值
2. 组件中的data为什么必须是函数
当我们封装好一个组件之后&#xff0c;在html使用组件实例&#xff0c;例如封装计数器组件&#xff0c;我们创建三个组件实例&#xff0c;每个组件实例都有数值的改变&#xff0c;如果组件中的data是对象或者数组形式&#xff0c;其中一个组件实例的数字改变&#xff0c;其他组件的数值也会跟着改变&#xff0c;因为组件在使用data中的数据时&#xff0c;三个组件的内存地址都会指向同一个data中的内存地址。而使用函数进行返回值&#xff0c;函数每次返回数据都会开辟一个新的内存地址&#xff0c;这就使得每一个组件指向的data中的数据都是不同的内存地址&#xff0c;这样其中一个计数器数值增加&#xff0c;其他计数器数值才不会跟着增加
五、父子组件通信
- 在上一个小节中&#xff0c;我们提到了子组件是不能引用父组件或者Vue实例的数据的。
- 但是&#xff0c;在开发中&#xff0c;往往一些数据确实需要从上层传递到下层&#xff1a;
- 比如在一个页面中&#xff0c;我们从服务器请求到了很多的数据。
- 其中一部分数据&#xff0c;并非是我们整个页面的大组件来展示的&#xff0c;而是需要下面的子组件进行展示。
- 这个时候&#xff0c;并不会让子组件再次发送一个网络请求&#xff0c;而是直接让大组件(父组件)将数据传递给小组件(子组件)。
1. 父传子props
<body><div id&#61;"app"><cpn :cmovies&#61;"movies">cpn>div><script type&#61;"text/x-template" id&#61;"cpn"><ul><li v-for&#61;"item in cmovies">{{item}}</li></ul>script><script src&#61;"../js/vue.js">script><script>const cpn &#61; {template: &#39;#cpn&#39;,data() {return {}},props: [&#39;cmovies&#39;]}const app &#61; new Vue({el: &#39;#app&#39;,data: {msg: &#39;hello vue!&#39;,movies: [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;]},components: {cpn}})script>
- 在子组件中使用props属性给要传的数据命名
- 在子组件模板中直接使用命名后的数据命
- 在html中引用组件时使用v-bind绑定父组件的数据
- props有两种写法&#xff1a;
-
上面的数组写法&#xff0c;一般不用
-
对象写法
-
对象写法可验证的数据类型
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
- 自定义类型
-
props: {propA: Number,propB: [String, Number],propC: {type: String,required: true},prorD: {type: Number,default: 100},propE: {type: Object,default() {return {msg: &#39;hello&#39;}},},propF: {validator(value) {return [&#39;success&#39;, &#39;warning&#39;, &#39;danger&#39;].indexOf(value) !&#61;&#61; -1}}}
2. props的驼峰命名
在子组件中我们习惯使用驼峰命名&#xff0c;但是在html中绑定时&#xff0c;html无法识别驼峰命名&#xff0c;因此会报错&#xff0c;此时我们要么不使用驼峰命名&#xff0c;要么在html使用该名字时将驼峰命名改为用-
连接&#xff0c;即cMovies
---->c-movies
3. 子传父(自定义事件)
<div id&#61;"app"><cpn &#64;itemclick&#61;"nameclick">cpn>
div>
<template id&#61;"cpn"><div><button &#64;click&#61;"cnameClick(name)" v-for&#61;"name in names">{{name.name}}button>div>
template>
<script src&#61;"../js/vue.js">script>
<script>const cpn &#61; {template: &#39;#cpn&#39;,data() {return {names: [{id: 1, name: &#39;老子&#39;},{id: 2, name: &#39;孟子&#39;},{id: 3, name: &#39;孔子&#39;},{id: 4, name: &#39;荀子&#39;},]}},methods: {cnameClick(name){console.log(name,&#39;c&#39;)this.$emit(&#39;itemclick&#39;,name)},}}const app &#61; new Vue({el: &#39;#app&#39;,data: {msg: &#39;hello vue!&#39;},components: {cpn},methods: {nameclick(name){console.log(name)}}})
script>
4. 数据双向绑定案例
<body><div id&#61;"app"><cpn :number1&#61;"num1" :number2&#61;"num2" &#64;num1change&#61;"num1change" &#64;num2change&#61;"num2change">cpn>div><template id&#61;"cpn"><div>props:{{number1}}data:{{dnumber1}}<input type&#61;"text" :value&#61;"dnumber1" &#64;input&#61;"num1input">props:{{number2}}data:{{dnumber2}}<input type&#61;"text" :value&#61;"dnumber2" &#64;input&#61;"num2input">div>template><script src&#61;"../js/vue.js">script><script>const app &#61; new Vue({el: &#39;#app&#39;,data: {num1: 1,num2: 0},components: {cpn: {template: &#39;#cpn&#39;,props: {number1: Number,number2: Number,},data(){return {dnumber1 : this.number1,dnumber2 : this.number2,}},methods:{num1input(event){this.dnumber1 &#61; event.target.valuethis.$emit(&#39;num1change&#39;,this.dnumber1)this.dnumber2 &#61; this.dnumber1 * 100this.$emit(&#39;num2change&#39;,this.dnumber2)},num2input(event){this.dnumber2 &#61; event.target.valuethis.$emit(&#39;num2change&#39;,this.dnumber2)this.dnumber1 &#61; this.dnumber2 / 100this.$emit(&#39;num1change&#39;,this.dnumber1)}}}},methods: {num1change(num){this.num1 &#61; parseInt(num)},num2change(num){this.num2 &#61; parseInt(num)}}})script>
body>
需求&#xff1a; 子组件的输入框中显示父组件中定义的num1
&#xff0c;num2
&#xff0c;更改子组件输入框中的内容&#xff0c;父组件的num1
&#xff0c;num2
跟着改变
思路&#xff1a;
- 用父传子
props
把数据传给子组件&#xff0c;然后在输入框中使用v-model
双向绑定父传过来的数据 - 不可行
- 因为官方规定&#xff0c;父组件传过来的数据只能有父组件更改&#xff0c;否则造成混乱&#xff0c;因此报错
- 用父传子
props
把数据传给子组件&#xff0c;子组件在data
中将传过来的数据用另一个变量number1
&#xff0c;number2
保存&#xff0c;在子组件输入框中更改时&#xff0c;将数据保存到number1
&#xff0c;number2
中&#xff0c;然后使用自定义事件&#xff0c;将更改后的数据传入给父组件&#xff0c;父组件在自己内部将子组件传过来的数据给原来的num1
,num2
- 可行
- 这种双向绑定的方法就是v-model的原理
- 完整代码在上方
5. 父访子-$children-$refs
当父组件要访问子组件中的方法或者属性时&#xff0c;可以使用以下两种方法
- $children
- 写法&#xff1a;
this.$children
- 作用&#xff1a;拿到所有子组件实例&#xff0c;返回一个数组
- 由于不能固定拿到某个组件&#xff0c;使用很少
- $refs
- 写法&#xff1a;
this.$refs.xxxx
,在子组件实例中要加上ref&#61;"xxxx"
属性 - 作用&#xff1a;拿到对应名字的组件实例&#xff0c;返回一个对象
- 使用多
6. 子访父-$parent-$root
使用很少&#xff0c;且不建议使用&#xff0c;耦合度高
写法与父访子类似&#xff0c;两个的区别&#xff0c;字面意思可看出
六、插槽slot
1. 为什么要使用插槽
生活中插槽目的是让我们原来的设备进行扩展性
vue中组件的插槽也是为了让封装的组件更加有扩展性&#xff0c;让使用者可以决定组件内部一些内容到底展示什么
2. 插槽的封装和基本使用
封装&#xff1a;
- 在组件中定义一个
标签即可 - 标签中可以写上默认要放的东西
使用&#xff1a;
- 在组件实例标签中写的所有内容会替换
标签 - 如果不写则显示默认内容
3. 具名插槽
上述方法只能将组件实例标签中的所有内容替换给所有的
标签
假设我们定义了3个插槽&#xff0c;而我们只想替换中间那个插槽那我们就要给中间那个插槽命名
写法&#xff1a;
4. 作用域插槽
一句话总结&#xff1a;父组件替换插槽的标签&#xff0c;但是内容由子组件来提供。
个人理解&#xff1a;子组件定义数据&#xff0c;父组件决定表现形式
需求&#xff1a;
- 子组件中包括一组数据&#xff0c;比如&#xff1a;pLanguages: [‘Javascript’, ‘Python’, ‘Swift’, ‘Go’, ‘C&#43;&#43;’]
- 需要在多个界面进行展示&#xff1a;
- 某些界面是以水平方向一一展示的
- 某些界面是以列表形式展示的
- 某些界面直接展示一个数组
- 内容在子组件&#xff0c;希望父组件告诉我们如何展示&#xff0c;怎么办呢&#xff1f;
写法&#xff1a;
表现形式
需求&#xff1a;
- 子组件中包括一组数据&#xff0c;比如&#xff1a;pLanguages: [‘Javascript’, ‘Python’, ‘Swift’, ‘Go’, ‘C&#43;&#43;’]
- 需要在多个界面进行展示&#xff1a;
- 某些界面是以水平方向一一展示的
- 某些界面是以列表形式展示的
- 某些界面直接展示一个数组
- 内容在子组件&#xff0c;希望父组件告诉我们如何展示&#xff0c;怎么办呢&#xff1f;
写法&#xff1a;
参考&#xff1a;vue从入门到精通