Vue 组件:基础概念
组件实际上是可以复用的 Vue 实例,它们与 new Vue 接收相同的选项,例如 data
、 computed
、 methods
以及生命周期钩子等。
何谓复用?如果把页面看作是组件的容器,那么导航栏、搜索框其实都是可以复用的组件。作为对可重用代码的封装,它们自身具有独立的数据和逻辑。
前端组件化的核心思路就是将一个巨大复杂的东西 拆分
成若干个小东西(组件),这些组件可以自由组合、替换和删除,同时不影响整个应用的运行,这就是组件化开发。
组件化的好处是:
- 提高开发效率
- 方便重复使用,简化调试步骤,方便单元测试
- 提升整个项目的可维护性,方便团队成员的协同开发
- 高内聚(功能必须是完整的)、低耦合(解耦业务逻辑和数据)
2.创建组件
2.1 全局组件
全局组件在 new Vue
之前创建,创建之后可用于所有根实例的模板中。
2.x 之前全局组件的创建过程如下:
let obj = Vue.extend({/*option*/}) // 创建组件构造器对象 Vue.component(TagName,obj) // 注册组件
2.x 之后语法糖的写法如下:
Vue.component("TagName",{/*option*/}) // 同时创建并注册组件
2.2 局部组件
更多的是创建局部组件,让其只能在当前所处的 Vue 实例的模板中使用。
var obj = {/*option*/} const app = new Vue({ el:'#app', components:{ "aaa":obj } })
如果需要创建父子组件,那么可以这样写:
var son = {/*option*/} var parent = { template:``, components:{ "bbb":son } } const app = new Vue({ el:'#app', components:{ "aaa":parent } })something
之后在 dom 中书写
,会发现父子组件都可以渲染,但是单独书写
则无法渲染子组件,这是因为子组件是在父组件中注册的,因此它只能在父组件的模板中使用。
2.3 模板抽离
上面的
something
可以单独抽离出来放在一个有 id 的
中,之后直接 #id
引用该模板即可。
// 改写如下: var parent = { template:`something
`, components:{ bbb:"#temp" } }something
注意: 每个组件都必须有且仅能有一个根元素
,这意味着组件所有的内容必须包裹在一个最外层元素中。
3. 组件的命名
组件创建后,直接在 dom 中书写组件名即可使用组件。但是组件的命名有一定的规则。
定义组件名的方式有两种:
(1)使用 kebab-case(字母全小写+连字符),例如:
Vue.component('my-component', { /*option*/ })
使用时也必须是 kebab-case。即
,否则会报错。
(2)使用 PascalCase(驼峰式),例如:
Vue.component('MyComponent',{/* option*/})
如果是在 父组件模板
(模板没有抽离到 HTML 中的)中使用,则允许 kebab-case 和 PascalCase 两种方式,即
或者
都是允许的;但是如果直接在 DOM (非字符串的模板)中,则只能使用相应的 kebab-case,否则会报错。
我们来看一个例子:
上图中我们创建了父子组件,其中子组件采用 PascalCase 命名,之后在父组件模板中引用子组件时,发现不管是 kebab-case 命名还是 PascalCase 命名都是可以成功渲染的。
再来看第二张图:
我们创建了 HisCpn
组件,之后直接在 DOM 中引用(没有转换为 kebab-case 命名),结果报错了;同样的,我们创建了 cpn1
和 MyCpn
父子组件,之后直接在 DOM 中引用,发现转换为 kebab-case 命名
的子组件可以正常渲染,而仍然采用 PascalCase 命名的子组件则报错了,因为前面我们说过:如果直接在 DOM (非字符串的模板)中,则只能使用相应的 kebab-case,否则会报错。
4. 为什么组件的 data
必须是函数?
另外还有一个需要注意的地方是,根实例的 data
是对象,但是组件的 data
却是函数。
这是因为组件是可复用的,每次使用一次
就会创建一个组件实例,如果定义组件时 data
依然返回的是对象,那么一个组件数据的更改将会同步影响到其它组件,因为
它们共享一个 data
对象
。如下图所示,我们只操作了一个组件,但三个组件数据都同步改变:
相反,如果 data
是函数,那么每次函数执行时都会开辟新的内存空间,创建并返回一个新的对象副本,这使得
每个实例都有自己的 data
对象
,实例互相之间不影响。如下图所示: