600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > Vue3.x全家桶之Vue组件化开发(二)

Vue3.x全家桶之Vue组件化开发(二)

时间:2020-02-16 01:52:54

相关推荐

Vue3.x全家桶之Vue组件化开发(二)

✍灬更新说明

更新时间:-1-03更新内容: Vue2.x 更新至 Vue3.xDemo与文件、截图演示齐全保留了Coderwhy老师Vue2.x的精华部分,添加Vue3.x的内容在Vue2.x的基础上进行Vue3.x知识点的补全,所以不需具备Vue2.x的基础,可直接进行学习 阅前提示: 希望您有HTML、CSS、JavaScript的基础知识熟悉JavaScript 的 ES6 语法 更新章节 目前从Vue2.x到Vue3.x更新了基础指令、组件化开发部分,后面会逐渐更新专栏后续章节的3.x 支撑平台 Windows10WebStrom

✍、目录脑图

🔥Vue🔥

Vue3

1、组件化开发

如果将一整个应用的所有处理逻辑都放在一起,处理起来就会变得非常复杂,而且不利于后续的迭代和扩展。如果将一整个应用拆分成一个个页面,一个个页面内部又拆分成一个个独立的组件,每个组件完成属于自己内部独立的功能,组件汇集成页面,页面汇集成应用。那么整个应用的管理和维护则变得非常清晰。

1.1、组件化开发概念

我们来看下面的代码:

<body><div><header>头部</header><header>身体</header><header>尾部</header></div><div id="app"></div><script src="../js/vue.js"></script><script>// 1.创建Vue的实例对象const app = Vue.createApp({data(){return {msg: '你好Vue3!'}}}).mount('#app');</script></body>

如果我们要复用上述代码,那么我们只能复制 div 里面的代码,然后达到复用的效果,这样就太麻烦了,所以组件化开发就可以解决此类问题。

1.2、组件化开发步骤

注册组件:调用ponent()方法注册组件使用组件:在 Vue 实例的作用范围内使用组件

<body><div id="app"><!--调用全局注册组件--><button-counter></button-counter><button-counter></button-counter><button-counter></button-counter></div><script src="../js/vue.js"></script><script>// 1.创建Vue的实例对象const app = Vue.createApp({data(){return {msg: '你好,Vue3!'}}});// 2.定义一个组件(全局组件)ponent('button-counter',{data() {return {count: 0}},template: `<button @click="count++">你点击了{{count}}次</button>`})// 3. 挂载vue实例app.mount('#app');</script></body>

1.3、组件的命名规范

1.3.1、短横线式kebab-case

引用短横线式命名的组件,调用时<my-component-name></my-component-name>

ponent('my-component-name',{data(){return {}},template: ``})

1.3.2、驼峰式PascalCase

引用驼峰式命名的组件,调用时<my-component-name></my-component-name><MyComponentName></MyComponentName>都可以

但是我们直接在DOM(即非字符串的模板)中使用时只有短横线法是有效的。在后面CLI调用两种方法都可以

ponent('My-Component-Name',{data(){return {}},template: ``})

1.4、全局组件和局部组件

全局组件:在整个Vue实例中都可以被调用

局部组件:只能在当前组件中被使用

1.4.1、全局组件

如下代码,我们在 app 下注册了一个全局组件,这意味着该组件可以在app实例内部任意地方使用:我们可以在 app 实例下使用,也可以在 home 实例下使用,也可以在 message 实例下使用。

<body><div id="app"><div id="home"><span>首页</span><button-counter></button-counter></div><div id="message"><span>消息</span><button-counter></button-counter></div><!--调用全局注册组件--><button-counter></button-counter></div><script src="../js/vue.js"></script><script>// 1.创建Vue的实例对象const app = Vue.createApp({data(){return {msg: '你好,Vue3!'}}});// 2.定义一个组件(全局组件)ponent('button-counter',{data() {return {count: 0}},template: `<button @click="count++">你点击了{{count}}次</button>`})// 3. 挂载vue实例app.mount('#app');</script></body>

当然我们也可以设置多个全局组件,代码如下:

<body><div id="app"><div id="home"><span>首页</span><button-counter></button-counter></div><div id="message"><span>消息</span><button-counter></button-counter><!--调用第二个注册组件--><lk-box></lk-box></div><!--调用全局注册组件--><button-counter></button-counter></div><script src="../js/vue.js"></script><script>// 1.创建Vue的实例对象const app = Vue.createApp({data(){return {msg: '你好,Vue3!'}}});// 2.定义一个组件(全局组件)ponent('button-counter',{data() {return {count: 0}},template: `<button @click="count++">你点击了{{count}}次</button>`})// 定义第二个全局组件ponent('lk-box',{template: `<div style="width: 200px;height: 200px;background-color:pink;">盒子组件</div>`})// 3. 挂载vue实例app.mount('#app');</script></body>

当然我们全局组件之间可以相互使用,使用方式如下:我们在定义第二个全局组件,若向使用第一个全局组件,只需要将第一个全局组件的名称标签写入模板template中即可

// 定义第二个全局组件ponent('lk-box',{template: `<div style="width: 200px;height: 200px;background-color:pink;">盒子组件<button-counter></button-counter></div>`})

1.4.2、局部组件🔥

如下代码,局部组件是使用一个常量来接收,我们将此局部组件通过components可以将其挂载在app实例中,这样我们在 app 实例里面就可以使用了

<body><div id="app"><lk-count></lk-count></div><script src="../js/vue.js"></script><script>// 注册一个局部组件const Counter = {data() {return {count: 0}},template: `<button @click="count++">你点击了{{count}}次</button>`}// 创建Vue的实例对象const app = Vue.createApp({data(){return {msg: '你好,Vue3!'}},// 组件选项components: {'lk-count': Counter}});// 挂载vue实例app.mount('#app');</script></body>

当然我们可以定义多个局部组件,然后通过components可以将其挂载在app实例中:

<body><div id="app"><lk-count></lk-count><cc-count></cc-count></div><script src="../js/vue.js"></script><script>// 注册一个局部组件const Counter = {data() {return {count: 0}},template: `<button @click="count++">你点击了{{count}}次</button>`}// 注册第二个局部组件const Box = {template: `<div style="width: 200px;height: 200px;background-color:pink;">盒子组件</div>`}// 创建Vue的实例对象const app = Vue.createApp({data(){return {msg: '你好,Vue3!'}},// 组件选项components: {'lk-count': Counter,'cc-count': Box}});// 挂载vue实例app.mount('#app');</script></body>

若我们想要局部组件之间相互使用,不能像全局组件那样在注册时向模板template写入名称标签,比如在注册组件时通过components将其挂载在想使用的实例中,然后再如全局组件那样向模板template写入名称标签:

// 注册第二个局部组件const Box = {components: {'lk-count': Counter,},template: `<div style="width: 200px;height: 200px;background-color:pink;">盒子组件<lk-count></lk-count></div>`}

1.4.3、总结

全局组件:在整个Vue实例中都可以被调用,若想要全局组件之间相互使用,只需将想使用全局组件的名称写入template

局部组件:只能在当前组件中被使用,若想在其他组件中使用,必须使用components将其挂载在想使用的组件中,然后再如全局组件那样向模板template写入名称标签

我们之后其实对局部组件用的更多一些。

1.5、组件标签化

template模块写法不够清晰,如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰

Vue 提供了两种方案来定义HTML模板内容

使用 < script > 标签使用 < template > 标签

1.5.1、使用script标签

下面代码是我们之前注册局部组件的代码,我们可以看到templates里面有一个 button 按钮

<body><div id="app"><lk-count></lk-count></div><script src="../js/vue.js"></script><script>// 注册一个局部组件const Counter = {data() {return {count: 0}},template: `<button @click="count++">你点击了{{count}}次</button>`}// 创建Vue的实例对象const app = Vue.createApp({data(){return {msg: '你好,Vue3!'}},// 组件选项components: {'lk-count': Counter,}});// 挂载vue实例app.mount('#app');</script></body>

我们使用 script 标签将其抽离

<body><div id="app"><lk-count></lk-count></div><!--1.script标签, 注意:类型必须是text/template--><script type="text/x-template" id="mycount"><button @click="count++">你点击了{{count}}次</button></script><script src="../js/vue.js"></script><script>// 注册一个局部组件const Counter = {data() {return {count: 0}},// 使用#idtemplate: '#mycount'}// 创建Vue的实例对象const app = Vue.createApp({data(){return {msg: '你好,Vue3!'}},// 组件选项components: {'lk-count': Counter,}});// 挂载vue实例app.mount('#app');</script></body>

1.5.2、使用template标签🔥

我们使用 template 标签将其抽离

<body><div id="app"><lk-count></lk-count></div><template id="mycount"><button @click="count++">你点击了{{count}}次</button></template><script src="../js/vue.js"></script><script>// 注册一个局部组件const Counter = {data() {return {count: 0}},template: '#mycount'}// 创建Vue的实例对象const app = Vue.createApp({data(){return {msg: '你好,Vue3!'}},// 组件选项components: {'lk-count': Counter,}});// 挂载vue实例app.mount('#app');</script></body>

1.6、组件数据存放

问题:组件可以访问Vue实例数据吗?

结论:组件不能直接访问Vue实例中的 data

组件是一个单独功能模块的封装:

这个模块有属于自己的 HTML 模板,也应该有属于自己的数据 data

组件自己的数据存放在哪呢?

组件对象也有一个 data 属性(也可以有 methods 属性)

只是这个 data 属性必须是一个函数

而且这个函数返回一个对象,对象内部保存着数据

// 注册一个局部组件const Counter = {data() {return {count: 0}},template: `<button @click="count++">你点击了{{count}}次</button>`,methods: {}}

为什么 data 在组件中必须是一个函数呢?

我这里来写一个简单介绍:我们设立两个对象,虽然都是空对象,但是两者不相等。

let obj1 = {};let obj2 = {};console.log(obj1 === obj2); // false

上述代码在内存中的示意图如下:

我们创建一个对象

let obj = {name: '秦晓'}let p1 = obj;let p2 = obj;let p3 = obj;console.log(p1,p2,p3);console.log(p1 === p2);// true

let obj = {name: '秦晓'}let p1 = obj;let p2 = obj;let p3 = obj;// 我们修改p1的namep1.name = '大林';console.log(p1,p2,p3);console.log(p1 === p2);

从上述例子中就可以看出,如果我们在使用对象的时候,很容易造成值拷贝。现在我们来回答一下为什么 data 在组件中必须是一个函数呢?

首先,如果不算是一个函数,Vue 直接就会报错其次,原因是在于 Vue 让每个组件对象都返回一个新的对象,因为如果是同一个对象,组件在多次使用后会相互影响

1.7、组件通信props

组件通信的常用方式有4种:

props自定义事件消息订阅与发布vuex

在上一个小节中,我们提到了子组件是不能引用父组件或者Vue实例的数据的

但是,在开发中,往往一些数据确实需要从上层传递到下层

比如在一个页面中,我们从服务器请求到了很多的数据其中的一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)

组件中,使用选项props来声明需要从父级接收到的数据(properties)

props的值有两种方式:

方式一:字符串数组,数组中的字符串就是传递时的名称方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。

1.7.1、props传数组

我们先来看方式一:先看如下代码,我们使用局部组件展示数据

<body><div id="app"><lk-box></lk-box></div><template id="box"><div><h1>技能掌握</h1><li>Web</li><li>Python</li><li>Java</li></div></template><script src="../js/vue.js"></script><script>// 注册一个局部组件const Box = {template: '#box'};// 创建Vue的实例对象const app = Vue.createApp({data(){return {}},components: {'lk-box': Box}});// 挂载vue实例app.mount('#app');</script></body>

如果我们的数据是从服务器端返回的,那么我们就需要动态绑定数据,代码如下:

<body><div id="app"><!--3.使用组件,v-bind动态绑定--><lk-box :brand="msg1" :colleges="msg2"></lk-box></div><template id="box"><div><h1>{{brand}}</h1><ul><li v-for="item in colleges">{{item}}</li></ul></div></template><script src="../js/vue.js"></script><script>// 1.注册一个局部组件(子组件)const Box = {// 子组件接收数据props: ['brand','colleges'],template: '#box'};// 2.将子组件在父组件里面注册const app = Vue.createApp({data(){return {msg1: '技能掌握',msg2: ['Web','Python','Java']}},components: {'lk-box': Box}});// 挂载vue实例app.mount('#app');</script></body>

1.7.2、props传对象

在前面,我们的props选项是使用一个数组除了数组之外,我们也可以使用对象,当需要对props进行类型等验证时,就需要对象写法了

1、类型限制

我们可以在 props 里面限制父组件给子组件传递的数据类型

<body><!--父组件模板--><div id="app"><cpn :cmessage="message" :cmovies="movies"></cpn></div><!--子组件模板--><template id="cpn"><div><h1>{{cmovies}}}</h1><h1>{{cmessage}}</h1></div></template><script src="../js/vue.js"></script><script>// 父传子: propsconst cpn = {template: '#cpn',props: {// 1.类型限制cmovies: Array,// 限制父组件传的是数组类型cmessage: String,// 限制父组件传的是字符串类型}}// root组件,我们当作父组件const app = Vue.createApp({data(){return {message: '你好啊',movies: ['海王', '海贼王', '海尔兄弟']}},components: {//对象字面量增强写法的属性增强写法cpn}});// 挂载vue实例app.mount('#app');</script></body>

类型一般支持:String、Number、Boolean、Array、Object、Date、Function、Symbol、自定义类型

2、默认值和必传值

type: 限制的类型default: 如果没有传值,给一个默认值注意:类型是对象或者数组时, 默认值必须是一个函数required: 必须的,即意味着这个值是必须要传递的,不传就报错

<body><!--父组件模板--><div id="app"><cpn :cmessage="message" :cmovies="movies"></cpn></div><!--子组件模板--><template id="cpn"><div><h1>{{cmovies}}}</h1><h1>{{cmessage}}</h1></div></template><script src="../js/vue.js"></script><script>// 父传子: propsconst cpn = {template: '#cpn',props: {// 2.提供一些默认值, 以及必传值cmessage: {type: String, // 类型限制为 Stringdefault: 'aaaaaaaa', // 如果没有传值,则给一个默认值required: true // required 必须的,即意味着这个值是必须要传递的,不传就报错},// 类型是对象或者数组时, 默认值必须是一个函数cmovies: {type: Array,default() {return []}}}}// root组件,我们当作父组件const app = Vue.createApp({data(){return {message: '你好啊',movies: ['海王', '海贼王', '海尔兄弟']}},components: {//对象字面量增强写法的属性增强写法cpn}});// 挂载vue实例app.mount('#app');</script></body>

3、props驼峰标识

当我们 props 里面的属性是驼峰写法的时,在传入值时需要进行-连接

<div id="app"><!--目前不支持直接写cInfo,驼峰得加 `-` 连接--><cpn :c-info="info" :child-my-message="message" ></cpn></div><template id="cpn"><div><h2>{{cInfo}}</h2><h2>{{childMyMessage}}</h2></div></template><script src="../js/vue.js"></script><script>const cpn = {template: '#cpn',props: {// 驼峰写法cInfocInfo: {//类型是对象或者数组时, 默认值必须是一个函数type: Object,default() {return {}}},childMyMessage: {type: String,default: ''}}}const app = new Vue({el: '#app',data: {info: {name: 'why',age: 18,height: 1.88},message: 'aaaaaa'},components: {cpn}})</script>

1.7.3、子级向父级传递

props用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件去。这个时候,我们需要使用自定义事件来完成

自定义事件的流程:

在子组件中,通过$emit()来发射事件在父组件中,通过v-on来监听子组件事件

我们来看下方代码,我们在子组件中放置一个按钮,在父组件中对子组件进行注册:

<body><!--父组件--><div id="app" style="background-color:blue; width: 300px; height: 300px;padding: 20px;"><lk-box></lk-box></div><!--子组件--><template id="box"><div style="background-color:red;width: 200px;height: 200px;"><button @click="btnClick">点我</button></div></template><script src="../js/vue.js"></script><script>// 子组件const Box = {template: '#box',methods: {btnClick(){alert('点击了');}},};const app = Vue.createApp({data(){return {msg: '你好Vue3!'}},components: {'lk-box': Box},});// 挂载vue实例app.mount('#app');</script></body>

如果我们点击了子组件,希望让父组件知道,那么子组件就要发射事件到父组件:例如如下代码,我们使用子组件发射事件来触发父组件的 boxFunc 函数:

<body><!--父组件--><div id="app" style="background-color:blue; width: 300px; height: 300px;padding: 20px;"><!--父组件监听子组件事件--><lk-box @box-click="boxFunc"></lk-box></div><!--子组件--><template id="box"><div style="background-color:red;width: 200px;height: 200px;"><button @click="btnClick">点我</button></div></template><script src="../js/vue.js"></script><script>// 子组件const Box = {template: '#box',methods: {btnClick(){alert('点击了');// 发射事件// 第一个参数是自定义事件的名称,第二个参数是自定义事件的参数this.$emit('box-click');}},};// 父组件const app = Vue.createApp({data(){return {msg: '你好Vue3!'}},components: {'lk-box': Box},methods: {boxFunc(){console.log('子组件中的按钮发生了点击')}}});// 挂载vue实例app.mount('#app');</script></body>

那如果我们点击子组件按钮,同时还想要传递参数给父组件,代码如下:

我们给$emit()事件传递参数在触发函数中接收参数

<body><!--父组件--><div id="app" style="background-color:blue; width: 300px; height: 300px;padding: 20px;"><lk-box @box-click="boxFunc"></lk-box></div><!--子组件--><template id="box"><div style="background-color:red;width: 200px;height: 200px;"><button @click="btnClick">点我</button></div></template><script src="../js/vue.js"></script><script>// 子组件const Box = {template: '#box',methods: {btnClick(){alert('点击了');// 发射事件// 第一个参数是自定义事件的名称,第二个参数是自定义事件的参数this.$emit('boxClick','秦晓');}},};// 父组件const app = Vue.createApp({data(){return {msg: '你好Vue3!'}},components: {'lk-box': Box},methods: {boxFunc(item){console.log('子组件中的按钮发生了点击');console.log(item);}}});// 挂载vue实例app.mount('#app');</script></body>

如果我们想传递多个参数,我们可以以一个对象或者数组的形式传递,例如我们传递一个对象:

<body><!--父组件--><div id="app" style="background-color:blue; width: 300px; height: 300px;padding: 20px;"><lk-box @box-click="boxFunc"></lk-box></div><!--子组件--><template id="box"><div style="background-color:red;width: 200px;height: 200px;"><button @click="btnClick">点我</button></div></template><script src="../js/vue.js"></script><script>// 子组件const Box = {template: '#box',methods: {btnClick(){alert('点击了');const dataObj = {name: '秦晓',age: 20}// 发射事件this.$emit('boxClick',dataObj);}},};// 父组件const app = Vue.createApp({data(){return {msg: '你好Vue3!'}},components: {'lk-box': Box},methods: {boxFunc(item){console.log('子组件中的按钮发生了点击');console.log(item);}}});// 挂载vue实例app.mount('#app');</script></body>

1.7.4、父子组件相互访问

有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件。

父组件访问子组件:使用$children(Vue3.x已经废弃)$refs子组件访问父组件:使用$parent

1、父组件访问子组件

$refs的使用🔥

$refs和 ref 指令通常是一起使用的首先,我们通过 ref 给某一个子组件绑定一个特定的 ID其次,通过this.$refs.ID就可以访问到该组件了 接着使用this.$refs.ID.xx就可以拿到该组件里面的属性数据了

例如下方代码,我们给子组件使用ref="box1"绑定ID,在父组件里面使用this.$refs.box1就可以拿到该组件,接着使用this.$refs.box1.msg拿到该组件的msg属性数据

<body><!--父组件--><div id="app"><lk-box ref="box1"></lk-box><button @click="getChildComponent">获取子组件</button></div><!--子组件--><template id="box"><div style="background-color:red;width: 200px;height: 200px;"><button @click="btnClick">点我</button></div></template><script src="../js/vue.js"></script><script>// 定义局部组件const Box = {data(){return {msg: '春风十里'}},methods: {btnClick(){alert('点击了按钮')}},template: '#box'};// 父组件const app = Vue.createApp({data(){return {msg: '你好Vue3!'}},components: {'lk-box': Box},methods: {// 获取子组件getChildComponent(){// this.$refs.box1 相当于拿到了子组件// this.$refs.box1.msg 就是拿到了子组件里面的 msg 数据// this.$refs.box1.btnClick 就是拿到了子组件里面的 btnClick 方法console.log(this.$refs.box1.msg);}}});// 挂载vue实例app.mount('#app');</script></body>

2、子组件访问父组件

如果我们想在子组件中直接访问父组件,可以通过$parent,如果想访问根组件,可以通过$root

尽管在 Vue 开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这要做

子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了

因为子组件一般会复用,如果我们将子组件放入另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题

例如如下代码,我们做了三层嵌套,最外层是根组件Vue实例,第二层是div盒子,最里面一层是 button 按钮。我们现在想点击 最里面的 button 来拿到div父组件或者root根组件。

<body><!--父组件--><div id="app"><lk-box></lk-box></div><!--子组件1--><template id="box1"><button @click="btnClick">点击了按钮{{count}}次</button></template><!--子组件2--><template id="box2"><div style="background-color:red; width: 200px; height: 200px"><lk-button></lk-button></div></template><script src="../js/vue.js"></script><script>// 定义局部组件1const LKButton = {data(){return {count: 0}},template: '#box1',methods: {btnClick(){// 子组件访问父组件// this.$parent 可以拿到该组件的父组件,也就是button子组件的父组件div// console.log(this.$parent);this.count++;console.log(this.$parent.count);// 子组件访问根组件// this.$root 可以拿到根组件,也就是vue实例// console.log(this.$root);console.log(this.$root.msg);}}};// 定义局部组件2const LKBox = {data(){return {count: 0}},template: '#box2',components: {'lk-button': LKButton}};// 父组件const app = Vue.createApp({data(){return {msg: '你好Vue3!'}},components: {'lk-box': LKBox},});// 挂载vue实例app.mount('#app');</script></body>

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。