一,vue组件
1,什么是vue组件
下图摘自官网,其底层主要是利用封装的思想,将一个大的组件拆分成多个小组件,并且在一个组件中一定会有一个vm的root根组件。组件和组件之间可以嵌套,组件就是实现应用中局部功能代码和资源的集合。代码主要有一些css,html,js等组成;资源主要由MP3,MP4,zip压缩包等文件组成。这样就可以实现代码复用,简化项目编码,提高开发和运行效率。
2,单文件组件和非单文件组件
二者区别
非单文件组件:顾名思义就是不是单文件组件,表示一个文件中包含有n个单文件组件。类似于一个大文件还没有拆分成多个小文件,全部聚集在一起,这样子的代码不容易维护,耦合性高,并且复用性低。
单文件组件:就是说一个组件中只包含一个文件。利用封装的思想,将组件从一个全部挤在一起的大型组件抽取出来,拆分成多个小组件,实现代码复用性高,更加的容易维护。
3,非单组件的基本使用
在创建组件时,和之前最原始的创建组件方式稍有不同,如果不是root根组件,其内部不需要el关键字,由于最终这个组件都是被vm的根节点所管理,因此其内部不需要el关键字,并且通过Vue.extend来创建非根组件,其内部的data元素只能用函数式,不能用对象式。其代码实现如下
<body> <!-- 一个容器,可以用于接收vue实力传来的数据,然后展示 --> <div id="root"> <!-- 3,编写组件标签 --> <school></school> <student></student> </div> <script type="text/javascript" > Vue.config.productionTip = false //阻止vue启动时提示生产提示 const school = Vue.extend({ //1,创建school组件 template:` <div> <h1>学校名称是:{{schoolName}}</h1> <h1>学校地址是:{{address}}</h1> </div> `, data(){ //数据只能以函数式返回,不能以对象式 return { schoolName:'北京大学', address:'北京' } } }) const student = Vue.extend({ //1,创建student组件 template:` <div> <h1>学生姓名是:{{studentName}}</h1> <h1>学生年龄是:{{age}}</h1> </div> `, data(){ //数据只能以函数式返回,不能以对象式 return { studentName:'zhs', age:18 } } }) //2,注册组件,局部注册 new Vue({ el:'#root', components:{ //用于注册组件 school, student } }) </script> </body>
总结来说就分三步,第一步就是创建组件,第二步就是注册组件,第三步就是编写组件标签到对应的父组件中。
如果组件需要变成全局组件,那么其第二步的注册组件的方式需要改成如下,这样这个book就变成了全局组件,在所有的vm中都可以使用。
Vue.component('book',book);
4,vue组件命名规范
4.1,一个单词组成
在官方文档中,如果是一个单词组成,那么可以直接使用这个单词作为组件名,也可以将这个单词的首字母大写之后再作为组件名。
Components:{ school:'school; //方式一 School:'school'; //方式二 }
4.2,多个单词组成
如果组件名是由多个单词组成的,那么可以全部让字母小写,单词与单词之间用一个 - 连接,也可以让多个单词直接使用大写字母拼接在一起(需要使用脚手架)。
Components:{ `my-school`:'school; MySchool:'school' }
并且在命名组件时,尽可能回避html中已有的元素名称,如h1,H1等都不行。并且在注册组件时,也可以简写将这个Vue.extend({}) 直接写成 {},如下。
const s = {...}
虽然说这里并没有显示的调用这个 Vue.extend,但是底层源码是会将这个补上的。
5,组件与组件间的嵌套
如上面的school组件,是一个root根组件下面的子组件,现在又想在school组件中再注册一个组件,形成一个嵌套组件,那么其代码实现如下。这里暂时还没有用到脚手架,因此在定义这个组件时,这个幼儿园组件要在这个定义在这个学校的前面,然后需要将子组件注册到这个父组件中,要和谁嵌套就将谁注册哪个组件中。
//定义一个幼儿园组件,实现和学校的嵌套,这里使用简洁式,省去Vue.extend const kindergarten = { template:` <div> <h1>学校名称是:{{schoolName}}</h1> <h1>学校地址是:{{address}}</h1> </div> `, data(){ return { schoolName:'幼儿园', address:'深圳南山区' } } } //1,创建组件 const school = Vue.extend({ template:` <div> <h1>学校名称是:{{schoolName}}</h1> <h1>学校地址是:{{address}}</h1> <kindergarten></kindergarten> </div> `, data(){ return { schoolName:'北京大学', address:'北京' } }, components:{ kindergarten,kindergarten } })
6,VueComponent组件
上面的这个school的组件的本质,其就是一个名为Component的构造函数,由Vue.extend所生成。即在写school这个标签时,vue解析时就会创建这个school的组件的实例对象,即执行如下语句
new VueComponent(options)
在每次调用Vue.extend的时候,返回的都是一个全新的组件对象。其实这个用java理解也很简单,就是
const a1 = new A(); const a2 = new A();
而且通过vue的源码也可以发现,每次这个sub对象都是一个全新的对象,因此也可以得知每次返回的都是
Vue.extend = function (extendOptions) { var Sub = function VueComponent(options) { this._init(options); }; return Sub; }
VueComponent的实例对象,也可以简称为vc,也可以称为组件实例对象。并且在这个vue的实例对象vm中,管理着一个个vc对象。
7,Vue和VueComponent的关系
7.1,Vue的原型对象
在引入这个vue.js之后,那么这个Vue组件的对象就有了,并且一定会有一个属性名为prototype,这个值就是对应的原型对象 ,那么这个vue原型对象上面的所有的这个函数,都可以被实例对象直接使用了。
而这个直接引入js所生成的vue对象,这个对象中有一个显示属性prototype,而直接通过new关键字构造出来的实例对象也一定会有一个隐式的__property__属性,那么这个实例对象也会通过这个属性直接指向这个Vue的原型对象。
由于这个实例的隐式属性,永远指向自己的缔造者的对象。这个原型对象也是一个对象,那么肯定也会指向自己的缔造者的对象,因此可知这个vue的原型对象的这个隐式属性 __property__,指向的就是自己的制造者对象Object。
7.2,VueComponent的原型对象
这个VueComponent和这个Vue的底层逻辑是一样的,VueComponent这个组件的原型对象一定会有一个显示属性prototype,该值一定是指向他的缔造者对象;这个VueComponent的实例对象也一定会有一个隐式属性 __property__,该值指向的肯定也是他的缔造者对象,即VueComponent的原型对象。
但是这个VueComponent的原型对象和vue的原型对象不一样,vue的原型对象是直接指向Object对象,但是这个VueComponent的原型对象实例加了一层,是将这个VueComponent的原型对象的隐式属性指向Vue的原型对象,(21:57),
通过上图可知,VueComponent这个组件就是想让这个Vue的原型对象做一个兜底,首先去VueComponent的原型对象上找东西,没有的话再去VueComponent的原型对象上找东西,没有的话再去Vue的原型对象上找东西,没有的话再交给Object的原型对象。
这样做的好处就是:让组件实例对象(vc)也可以访问到Vue原型上的属性和方法。
VueComponent.prototype.__proto__ === Vue.prototype
而这个组件实例对象可以近似的认为就是一个小的vm,就是一个vue的实例对象,不同点就是这个vc不能用这个el属性,并且他的data只能写函数式,不能用对象式。
8,单文件组件
上面说了这么多都是在聊多文件组件,接下来主要理解一下什么时单文件组件。在此之前,需要先安装一个插件,这个推荐使用 vetur 这个插件。在安装完插件之后,新建一个.vue的文件,然后其结构主要如下,分别是由组件的结构,组件交互相关的代码(数据,方法等等),组件的样式等组成。在安装了这个插件之后,直接< + 回车就可以出现以下代码。
<template> <div> <!-- 组件的结构 --> </div> </template> <script> export default { //组件交互相关的代码 } </script> <style> /* 组件的样式 */ </style>
8.1,单文件组件的实现
那么接下来就根据这个多文件中的school和Student的这两组件,用这个单文件实现一下,主要会定义一些Student.vue,School.vue,App.vue,main.js,index.js,其步骤如下:
1,School.vue
这里主要编写一些关于学校的样式,数据结构个数据交互
<template> <div> <h1>学校姓名是:{{name}}</h1> <h1>学校地址是:{{address}}</h1> </div> </template> <script> export default { name:'School', data(){ return{ name:'', age:'' } } } </script> <style> </style>
2,Student.vue
<template> <div> <h1>学生姓名是:{{name}}</h1> <h1>学生年龄是:{{age}}</h1> </div> </template> <script> export default { name:'Student', data(){ return{ name:'', age:'' } } } </script> <style> </style>
3,App.vue
这个组件主要是作为一个汇总组件,将其他的所有的子组件汇总到该组件中。
<template> <div> <!-- 引入组件 --> <School></School> <Student></Student> </div> </template> <script> //引入组件 import School from './School.vue' import Student from './School.vue' export default { name:'App', components:{ School, Student } } </script> <style> </style>
4,main.js
import App from './App' new Vue({ el:'#root', template:`<App></App>`, components:{ App } })
5,index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue的单文件组件学习</title> </head> <body> <!-- 根组件 --> <div id="root"></div> <!-- 先引入vue.js --> <script type="text/javascript" src="../js/vue.js"></script> <!-- 再引入main.js --> <script type="text/javascript" src="./main.js"></script> </body> </html>
这样就就完全的通过单文件代替多文件的编写了,当然这里只是一个初步的示例,具体的还得安装脚手架来跑通整个流程
<title>Vue的单文件组件学习</title>
这样就就完全的通过单文件代替多文件的编写了,当然这里只是一个初步的示例,具体的还得安装脚手架来跑通整个流程