文章目录
什么是组件化 ❓
组件化目的 📇
组件使用3步骤 🕒
全局组件和局部组件 🍓
父子组件 🎨
注册组件语法糖 🍬
组件模板抽离的写法 ✍🏼
为什么组件data必须是函数 ❓
父子组件通信 父传子props ✉️
父子组件通信 props驼峰标识 🛑
父子组件通信 子传父自定义事件 🥳
什么是组件化 ❓
组件化是指解耦复杂系统时将多个功能模块拆分、重组的过程,有多种属性、状态反映其内部特性。
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:
组件化目的 📇
为了解耦:把复杂系统拆分成多个组件,分离组件边界和责任,便于独立升级和维护。
组件使用3步骤 🕒
♦️ 创建组件构造器
♦️ 注册组件
♦️ 使用组件
调用 Vue.extend() 插件的是一个组件构造器,通常在创建组件构造器时,传入 template 代表自定义组件的模板,这个模板就是要显示的 HTML 代码,事实上,这种写法实际上基本不使用,但这是基础
调用 Vue.component() 是将刚创建的组件构造器注册成为一个组件,并且给它起一个组件标签名,所以传递了两个参数,1.注册组件的标签名2.组件构造器
组件必须挂载在某个 Vue 实例下,否则是不会生效的
<body> <div id="app"> <!-- 3.使用组件 --> <hello></hello> <hello></hello> </div> <script src="./vue.js"></script> <script> // 1.插件组件构造器 const hello = Vue.extend({ template: `<div> <h1> Hello World </h1> </div>` }) // 2.注册组件 Vue.component('hello',hello) const vm = new Vue({ el: '#app' }) </script> </body>
全局组件和局部组件 🍓
全局注册
全局注册是通过 Vue.component 注册,它可以被所有 vue 实例使用
<body> <div id="app"> <!-- 3.使用组件 --> <hello></hello> <hello></hello> </div> <div id="app1"> <hello></hello> </div> <script src="./vue.js"></script> <script> // 1.插件组件构造器 const hello = Vue.extend({ template: `<div> <h1> Hello World </h1> </div>` }) // 2.注册组件(全局注册,意味着可以在多个vue实例下使用) Vue.component('hello',hello) const vm = new Vue({ el: '#app' }) const vm1 = new Vue({ el:'#app1' }) </script> </body>
局部注册
局部注册实在某个vue实例内部,通过配置项 components 配置,局部组件只能在注册它的 vue 实例下才可以使用
<body> <div id="app"> <!-- 3.使用组件 --> <hello></hello> <hello></hello> </div> <div id="app1"> <hello></hello> </div> <script src="./vue.js"></script> <script> // 1.插件组件构造器 const hello = Vue.extend({ template: `<div> <h1> Hello World </h1> </div>` }) // 2.注册组件(全局注册,意味着可以在多个vue实例下使用) const vm = new Vue({ el: '#app', // 局部注册,只能在当前 vue 实例下使用 components:{ hello:hello } }) const vm1 = new Vue({ el:'#app1' }) </script> </body>
父子组件 🎨
组件和组件之间存在关系,最常见的就是父子组件的关系
<body> <div id="app"> <c2></c2> </div> <script src="./vue.js"></script> <script> // 创建第一个组件 const c1 = Vue.extend({ template: ` <div> <p>我是p1</p> </div> ` }) // 创建第二个组件 const c2 = Vue.extend({ template: ` <div> <p>我是p2</p> <c1></c1> <c1></c1> </div> `, components: { c1: c1 } }) new Vue({ el: '#app', data: { message: 'Hello World' }, components: { c2: c2 } }) </script> </body>
注意如果在vue实例下写标签 c1 是不正确的,因为我们既没有全局注册 c1 组件,也没有在 vue 实例中进行注册,而是在 c2 组件内部进行了注册,作用域在 c2 组件,所以可以在 c2 的模板中使用,这一点需要注意
注册组件语法糖 🍬
Vue为简化书写组件的过程,提供了注册的语法糖
省去了 Vue.extend() 这个步骤,而是直接使用一个对象来代替
全局注册组件语法糖,直接使用 Vue.component ,第一个配置组件名,第二个是template模板,底层还是extend的形式
<body> <div id="app"> <hello></hello> </div> <script src="./vue.js"></script> <script> // 全局注册,语法糖,底层还是 extend Vue.component('hello',{ template:` <div> <h1>Hello World</h1> </div> ` }) new Vue({ el: '#app' }) </script> </body>
局部注册组件语法糖,使用 components 配置项,组件名后跟模板,省去Vue.extend()
<body> <div id="app"> <hello></hello> </div> <script src="./vue.js"></script> <script> new Vue({ el: '#app', components:{ hello:{ template:` <div> <h1>Hello World</h1> </div> ` } } }) </script> </body>
组件模板抽离的写法 ✍🏼
<body> <div id="app"> <hello></hello> <world></world> </div> <!-- 1. 模板在script标签中写,但是类型必须是 text/x-template 类型 --> <script type="text/x-template" id="hello"> <div> <h2>Hello Vue</h2> </div> </script> <!-- 2. 使用template标签 --> <template id="world"> <div> <h2>Hello World</h2> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> Vue.component('hello',{ template:'#hello' }) Vue.component('World',{ template:'#world' }) new Vue({ el: '#app', }) </script> </body>
为什么组件data必须是函数 ❓
因为以函数形式返回,当多次调用组件,每个组件都会有自己的一份data,彼此互不影响
组件时一个单独模块的封装,这个模块有属于自居的 HTML 模板,也应该有自己的数据 data……
组件是不可以直接访问 Vue 实例的data的
Vue组件应该有自己保存数据的地方
组件对象也有一个 data 属性,也可以有 method 等属性
只是这个 data 属性必须是一个函数
而且这个函数返回一个对象,对象内部保存着数据
<body> <div id="app"> <hello></hello> </div> <template id="hello"> <div> <h2>{{hello}}</h2> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> Vue.component('hello', { template: '#hello', data() { return { hello: 'Hello World,Hello Vue.js' } } }) new Vue({ el: '#app', }) </script> </body>
这个计数器案例就很好的解释了,为什么vue组件但会data要使用函数的形式
<body> <div id="app"> <counter></counter> <counter></counter> <counter></counter> </div> <template id="counter"> <div> <button @click="sub">-</button> {{count}} <button @click="add">+</button> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> Vue.component('counter', { template: '#counter', data() { return { count: 0 } }, methods: { add() { this.count++; }, sub() { this.count--; } }, }) new Vue({ el: '#app', }) </script> </body>
如果我们调用多次组件去操作的是一个对象,那就会引起连锁反应,像这样
<body> <div id="app"> <counter></counter> <counter></counter> <counter></counter> </div> <template id="counter"> <div> <button @click="sub">-</button> {{obj.count}} <button @click="add">+</button> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const obj = { count: 0 } Vue.component('counter', { template: '#counter', data() { return { obj } }, methods: { add() { this.obj.count++; }, sub() { this.obj.count--; } }, }) new Vue({ el: '#app', }) </script> </body>
父子组件通信 父传子props ✉️
实际开发,往往一些数据需要从上层传递到下层
Vue中通过 props 向子组件传递数据,通过事件向父组件发送消息
<body> <div id="app"> <child :cmessage="message" :cmovies="movies"></child> </div> <template id="child"> <div> <p>我是子组件</p> <p>{{cmessage}}</p> <ul> <li v-for="item in cmovies">{{item}}</li> </ul> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const child = { template: '#child', // 数组写法 // props: ['cmessage'] // 对象写法,还可以指定类型, props: { cmessage: { // 类型 type: String, // 默认值 default: '哈哈', // 必须传值,否则报错 required: true }, cmovies: { type: Array, default() { return []; } } } } new Vue({ el: '#app', data: { message: '你好啊,子组件', movies: ['天气之子', '你的名字', '千与千寻'] }, components: { child } }) </script> </body>
父子组件通信 props驼峰标识 🛑
在未使用单文件组件,即以.vue为后缀的文件时,可能会遇到父组件传子组件时驼峰命名的问题,你可以使用-来解决
<body> <div id="app"> <npm :c-npm="person"></npm> </div> <template id="npm"> <div> <p>{{cNpm.width}}</p> <p>{{cNpm.height}}</p> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const npm = { template: '#npm', props: { cNpm: { type: Object } } } new Vue({ el: '#app', components: { npm }, data: { person: { height: 1.8, width: 1.5 } } }) </script> </body>
父子组件通信 子传父自定义事件 🥳
当子组件需要向父组件传递时,就要用到自定义事件了,之前学习的 v-on 不仅可以用于监听 DOM 事件,也可以监听组件间的自定义事件
在子组件使用 $emit()来触发事件,在父组件中使用 v-on 来监听子组件事件
<body> <!-- 父组件模板 --> <div id="app"> <!-- 监听自定义事件,接收到事件触发后调用aa函数 --> <hello @cclick="aa"></hello> </div> <!-- 子组件模板 --> <template id="zi"> <div> <button v-for="item in shoppings" @click="btnClick(item.name)">{{item.name}}</button> </div> </template> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> // 子组件 const hello = { template: '#zi', data() { return { shoppings: [ { id: 'a', name: '热门推荐' }, { id: 'b', name: '家用电器' }, { id: 'c', name: '数码办公' } ] } }, methods: { btnClick(name) { // 自定义事件 cclick this.$emit('cclick', name) } }, } // 父组件 new Vue({ el: '#app', components: { hello }, methods: { aa(name) { console.log(name); } } }) </script> </body>