vue 组件
组件的出现就是为了实现两个目标
1. 降低整体复杂度,提升代码的可读性和可维护性( 从一个粗粒度划分为细粒度,复杂度自然就降低了 )
2. 提升局部的可重复性( 粒度降细一点,复杂度就降低了,同时重用性也就变的更好 )
一个组件就是页面中的一个区域
有时候为了重复使用,一个 icon 图标就可以做成一个重复使用的组件,
也有时候不是为了重复使用,是为了降低复杂度,把网页分细一点,便于维护管理,
页面上任何一个区域只要觉得认为合适就把它做成一个组件
一、创建组件
创建组件在 vue 里面特别特别的简单就是一个对象,这个对象就是一个配置对象,该对象跟 Vue( {} ) 实例配置的对象几乎是一样的
<div id="app"></div> <script src="https://20180416-1252237835.cos.ap-beijing.myqcloud.com/common/vue.min.js"></script> <script> var myButton = { // 一个按钮组件 } var vm = new Vue({ template: ``, }); vm.$mount("#app"); </script>
组件配置对象 和 vue的配置对象 有几个差别
1. 没有 el 配置( el 必须是在 vue 的构造函数里面配置,表示 vue 生成的 dom 挂载到哪 )
2. data 配置必须是一个函数,该函数会返回一个对象,对象里面提供各种各样数据
3. 由于没有 el,虚拟 DOM 树必须定义在 template 或 render 中
组件的写法和 new Vue({}) 实例的配置实际上是查不多的(就是一点点的区别),下面写一个 myButton 按钮组件
var myButton = { data(){ return { count: 0, } }, template: `<button v-on:click="count++">当前点击了{{count}}次</button>`, } var vm = new Vue({ template: ``, }); vm.$mount("#app");
创建一个组件后,还不知道组件往哪个位置放,还要去注册一个组件,注册之后才能使用
二、注册组件
有两种注册方式
1. 全局注册
2. 局部注册
1、全局注册组件
全局注册一个组件后,整个应用中任何地方使用该组件,在vue实例里可以用,在别组件里也可以用
全局注册组件极其简单,
构造函数 Vue 里面提供了一个静态方法 Vue.component() (conponent是组件的意思)
Vue.component() 方法有两个参数
1. 组件名称( 将来在模板中使用组件时的名称,这才是组件名称,跟 var myButton = {} 这个变量名称无关 )
2. 组件配置对象
Vue.component(组件名称, 组件配置对象);
当然,也可以直接把组件配置对象,直接写到第二个参数的位置也是一样的
Vue.component("组件名称", { data(){ return { count: 0, } }, template: `<button v-on:click="count++">当前点击了 {{count}} 次</button>`, });
在vue实例模板里面,使用全局注册的组件
1. 创建 myButton 组件
2. 全局注册组件
3. 在模板里使用组件,写一个 <MyButton/> 元素(叫做虚拟dom节点)
<div id="app"></div> <script src="https://20180416-1252237835.cos.ap-beijing.myqcloud.com/common/vue.min.js"></script> <script> // 1.创建按钮组件 var myButton = { data() { return { count: 0, } }, template: `<button v-on:click="count++">当前点击了 {{count}} 次</button>`, } Vue.component("MyButton", myButton); // 2.全局注册组件 var vm = new Vue({ template: `<div> <MyButton/> <!-- 3.使用组件,写上组件的名称"MyButton" --> <MyButton/> <MyButton/> </div>`, }); vm.$mount("#app"); </script>
组件的重用,
可以写多个 <MyButton/>,每个组件之间的数据是相互独立的,组件之间的数据不会相互影响
这就是为什么组件的 data配置 要一个函数,
配置为一个函数后,每个组件要得到数据,都要调用这个 data(){} 函数,调用一次该函数就返回的 return {},就是一个新的对象了,
这样避免了组件之间的数据相互影响,因为同一个组件有不同的实例,每个实例有自己的数据,
一个按钮就是一个实例,三个按钮就有三个实例,每个实例要有自己的数据,保障每个组件之间的数据是相互独立的
如果使用同一个 obj 对象,这种方式不好,导致点击按钮所有的数据是一致的
var obj = { count:0, }; var myButton = { name: "button", data(){ return obj; // 数据使用的是同一个对象 }, template: `<button v-on:click="count++">当前点击了 {{count}} 次</ button>`, } Vue.component("my-button", myButton); var vm = new Vue({ name:"App", template: `<div> <my-button/> <my-button/> <my-button/> </div>`, }); vm.$mount("#app");
梳理总结
1. var myButton = {} 创建组件
2. Vue.component("my-button", myButton) 全局注册组件
3. 然后就可以到处使用了 <div> <my-button /> </div> ,使用的组件会生成一个对应的虚拟节点
全局注册并不好,
有些组件并不是通用的,要到处都要用,可能只是在某一个地方用一下,
如果全局注册,将来构建工具打包,影响打包结果,让打包结果变大
2、局部注册组件
需要在 vue 配置对象里面,配置一个 components 属性(单词比全局多了一个字母 s)
1. 局部注册的组件,只能在这个 vue 实例里使用
2. 创建组件时用大驼峰命名,注册时属性名和属性值相同,可以使用速写属性
<div id="app"></div> <script src="https://20180416-1252237835.cos.ap-beijing.myqcloud.com/common/vue.min.js"></script> <script> // 1.创建组件,创建组件时变量名用大驼峰命名 var MyButton = { data() { return { count: 0, } }, template: `<button v-on:click="count++">当前点击了 {{count}} 次</button>`, } var vm = new Vue({ components:{ MyButton, // 2.局部注册组件(属性名和属性值名相同,可以用速写属性) }, template: `<div> <MyButton/> <!-- 3.使用组件,写上组件的名称"Mybutton" --> <MyButton/> <MyButton/> </div>`, }); vm.$mount("#app"); </script>
三、应用组件
组件是在模板中使用的,写组件要注意几个点
1、组件必须要有结束
组件可以写结束标记 <MyButton></MyButton>
也可以用自结束 <my-Button/>
这种是错误的 <my-comp>
2、组件的命名
components: { 注册组件的名字 : 组件对象的变量名 }
注册组件的时候,有两种命名方法
1. 短横线命名法 "my-button" ,官方建议的命名方法( 短横线命名必须要用引号引起来 )
2. 大驼峰命名法 MyButton ,每个单词首字母大写,袁老师推荐大驼峰命名法,因为比较灵活
组件名字使用大驼峰命名 MyButton 在模板里使用组件的时候
1. 既可以写短横线 <my-button/>
2. 又可以写大驼峰 <MyButton/> 两种都支持
new Vue({ components: { MyButton, }, template: `<div> <my-button/> <MyButton></MyButton> <div>`, });
袁老师甚至再推荐,
在模板 template 里使用组件的的时候,也尽量使用大驼峰 <MyButton/> ,
因为有些组件的名字可能只有一个单词 button 没有短横杠了,容易和 html 元素的名字 <button></button> 造成冲突
总结
创建组件:一个对象,跟 vue 配置差不多
注册组件:局部注册就是 components,组件名使用大驼峰
应用组件:在模板中直接使用就完事了
四、组件树
这样说吧,
我们在做一个项目或做一个 vue 应用,百分之九十的时间都是在开发组件,把组件开发的差不多了,项目基本就完成了
大组件里面包含子组件,于是就形成一个组件树,最顶层是 vue 实例,
组件实例里面包含一些别的组件,别的组件里面又包含一些组件,有些组件可能会重用,比如组件3可能在不同的地方使用
五、向组件传递数据
大部分组件需要完成自身功能,都需要一些额外信息,
传递数据的方式有很多种,很常见的一种是使用 props 组件属性(component props)
写一个标题组件 Title,使用该组件的时候需要告诉标题的内容是什么
1. 这就需要在使用 Title 组件时候向组件传递数据,我们管传递的数据叫组件的属性
2. props: ["headline", "paragraph"] props 配置的值是一个数组,数组里面每一项都是属性的名称
<div id="app"></div> <script src="https://20180416-1252237835.cos.ap-beijing.myqcloud.com/common/vue.min.js"></script> <script> var MyButton = { data(){ return { count: 0, }; }, template: `<button v-on:click="count++">当前点击了{{count}}次</button>`, } // Title组件 var Title = { props: ["headline", "paragraph"],// 3.也就是说props属性的内容也会被注入到实例里面 template: `<div> <h2>{{headline}}</h2> <!-- 2.这里使用属性传过来的数据 --> <p>{{paragraph}}</p> </div>`, } var vm = new Vue({ el: "#app", components: { MyButton, Title, }, template: `<div> <MyButton/> <MyButton/> <!-- 1.跟元素属性的道理是一样的, 横向比较一下,用a元素要告诉它url超连接不就是href属性吗? 使用Title组件也一样,要告诉它标题的内容是什么,可以通过headline属性的方式告诉它 --> <Title headline="标题一" paragraph="简介文字"/> <Title headline="标题二" paragraph="简介文字简介文字"/> <Title headline="标题三" paragraph="简介文字简介文字简介文字"/> </div>`, }); // briefIntroduction作者简介 </script>
props 属性也可以在模板中 {{ 属性名 }} 使用,
也就是说属性的的内容也会被注入到实例里面,就像上节课说的 data、methed,还有以后会学的 computed,
每个组件也是实例,跟 vue 实例差不过,其实可以把 vue 实例看做是一个特殊的组件实例
在 vue 实例里面,可能会用到各种各样的组件,可能会传递一些属性(组件1和组件2)
这些组件里面(组件1和组件2),又可能会用的别的组件,传递一些属性(组件3),
形成一个组件树,而且数据从上到下往下流动的,父组件往子组件里面传数据,子组件再往子组件里面传数据,从上往下流动这叫做单向数据流
注意:组件中属性 props 是只读的,绝对不能更改,这叫做单向数据流
什么是单项数据流?
在组件里面是绝对绝对不能去改 props 属性配置的,可以改自己的 data 配置,data 是自己的数据可以去改,
但是 props属性配置是被别人给的数据,组件自己是没有权利改动的
vue 设计上的一个哲学,谁的数据谁负责,别人是没法控制,向组件传递数据,我们把传递的数据叫做组件的属性
ps:
XMind思维导图
六、工程结构
整个项目里可能会涉及到很多的组件,我们需要用合理的方式来组织我们的代码结构
|- htdocs
|- index.html
|- src 把所有的代码都放到src文件夹里面
|- main.js 入口模块文件,一开始就运行这个js,在index.html里面引用它
|- App.js vue的根组件
|- vue.browser.js es6模块化的vue.js文件
|- components
|- MyButton.js
index.html
页面里面就提供一个 div#app 元素,然后用模块化的方式引入 mian.js 入口文件
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>index.html</title> </head> <body> <div id="app"></div> <script src="./src/main.js" type="module"></script> </body> </html>
src/mian.js
入口模块文件就做一件事创建 vue 实例
import Vue from './vue.browser.js'; // 导入vue文件得到的是Vue构造函数 import App from './App.js'; // 导入一个组件对象 // 入口这个地方通常不用写template模板、data数据这些的配置,它就做一件事,渲染一个根组件,剩下的事情都交给根组件去完成 // 怎么渲染这个组件呢?可以用render渲染,rander别的地方不怎么写,就这个地方习惯上会写 new Vue({ render(h){ return h(App); // 这句的意思是,组件也是虚拟节点,渲染一个虚拟节点。虚拟节点的类型不是一个普通的元素而是一个组件,就是这么简单 }, }).$mount('#app');
上面 render(h){ return h(App); } 的写法相当于
1. 先局部注册组件,2. 然后在模板里使用组件
import Vue from './vue.browser.js'; import App from './App.js'; new Vue({ components: { App, // 1.注册 }, template: `<App/>` // 2.使用 }).$mount('#app');
还可以写的更加简单一点(用箭头函数)
也就是 main.js 什么都不做,就写一个简单的启动功能就行了,剩下的渲染、有什么模板、有什么数据,都交给 App.js 去完成
import Vue from './vue.browser.js'; import App from './App.js'; new Vue({ render: (h) => h(App), }).$mount('#app');
src/App.js
vue根组件,整个页面上所有的东西都交给这个根组件来渲染,这个根组件里面要用到别的组件
import MyButton from './components/MyButton.js'; // 导入按钮组件 // 根组件的模板 const template = `<div> <h1>App组件</h1> <!-- 这个组件里面有可能会用到别的组件 --> <MyButton/> <MyButton/> </div>`; export default { // 根组件发本质就是一个对象,导出一个对象 components:{ MyButton, // 局部注册按钮组件 }, template, }
src/components/MyButton.js
const template = `<button v-on:click="count++">当前点击 {{count}} 次</button>`; // 模板写在外面便于管理 export default { // 导入按钮组件 data(){ return { count: 0, }; }, template, };
一个页面有很多不同开发者,不同的开发者去写不同的组件,最后一整合一个页面就出来了
七、vue知识体系
这节课多了一个 props 属性
1. 组件也是实例和vue实例差不多,其实可以把vue实例看做一个特殊的组件实例
2. props属性的内容也会被注入到实例里面,就像上节课说data、methods、computed,被注入到实例里面去
|- vue知识体系
|- 模板
| |- 内容 有变化的地方写大胡子mustache语法 {{ js表达式 }}
| |- 指令 主要写在元素的属性位置,是带有一些功能性的,比如循环生成元素,判断是否要生成元素等
| |- v-bind v-bind: 属性名 = "js表达式"
| |- v-for 循环数组,注意绑定key值
| |- v-on 绑定事件,指令参数是事件名click,简写为@click
|
|- 配置
|- data 和界面相关的数据
|- computed
|- methods 常见的操作方法,跟功能相关的方法
|- template 配置模板
|- render 渲染方法,用于生成vnode
|- el 挂载的目标元素
|- components 局部注册组件
|- props 声明组件的属性