二、 Vue组件化编程
2.1 组件化模式与传统方式编写应用做对比:
传统方式编写应用
依赖关系混乱,不好维护:例如:比如需要引入js1,js2,js3,但是js3需要用到js1、2的方法,所以js1、2要先引入,而js2的使用需要用到js1中的方法,也就是js1需要在js2前面引用,三个我们还能分清先后顺序,可是多了就会混乱。
2.代码复用率不高
两个html,相同的底部和顶部结构,先创建html1,在创建html2时,是复制了html1的,不属于代码复用;此时,如果将相同结构包装在同一段html结构中,用innerHTML来实现代码复用,这一块我也不太明白,死记硬背吧,就是封装相同的也不好用!
使用组件化方式优势:
模块 庞大js 模块指js模块
模块化 a.js b,js 庞大js给拆了 按照模块化的标准拆开
组件 :不同的功能点不同的组件
2.2 非单文件组件
小tips 使用函数式和对象式改变数据的方法 :
作用:通过分析函数式和对象式的区别,才能知道组件中创建组件时里面的data数据使用函数式的原因了。同一个data数据,但是调用时有不同的实例 互相不会干扰
// 使用对象式和函数式的区别// let data = {// a: 1,// b: 2// }function data () {return {a: 1,b: 2}}// 使用对象式一变都变 根据栈堆存放// const x1 = data// const x2 = data// 当你使用函数来返回对象时,每次调用该函数都会创建一个新的对象实例。即使这些对象具有相同的结构和初始值,//它们也是不同的对象,存储在内存的不同位置。因此,修改一个对象不会影响其他通过该函数创建的对象。const x1 = data()const x2 = data()
> > Vue中使用组件的三大步骤:
> > 一、定义组件(创建组件)
> > 二、注册组件
> > 三、使用组件(写组件标签) 一、如何定义一个组件?
> > 使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,
> 但也有点区别:
> > 1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
> > 2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。
> > 备注:使用template可以配置组件结构。
> > 二、如何注册组件?
> > 1.局部注册:靠new Vue的时候传入components选项
> > 2.全局注册:靠Vue.component('组件名',组件)
> > 三、编写组件标签:<school></school>
下面这段代码主要是讲述组件标签三步骤,以及创建局部组件和全局组件。主要式在注册那一块的差别!
<!--准备好一个容器 --><div id="root"><h1>{{msg}}</h1><hr><!-- 第三步:编写组件标签 --><school></school><hr><!-- 第三步:编写组件标签 --><student></student><!-- 实现复用 并且因为data函数实现改变数据互不干扰--><school></school><student></student></div><!-- 第二个容器使用第一个容器的组件 --><div id="root2"><school></school>组件标签<hello></hello></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。// 第一步:创建school组件const school = Vue.extend({// el: '#root',//组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器template: `<div><h2>学校名称:{{schoolName}}</h2><h2>学校地址:{{address}}</h2><button @click='showName'>点我提示学校名</button></div>`,data () {return {schoolName: '尚硅谷',address: '北京昌平',}}, methods: {showName () {alert(this.schoolName)}},})//第一步:创建student组件const student = Vue.extend({template: `<div><h2>学生姓名:{{studentName}}</h2><h2>学生年龄:{{age}}</h2></div>`,data () {return {studentName: '张三',age: 18}}})//第一步:创建hello组件const hello = Vue.extend({template: `<div> <h2>你好啊!{{name}}</h2></div>`,data () {return {name: 'Tom'}}})//第二步:全局注册组件 组件的名字 组件的位置Vue.component('hello', hello)//第二步:注册组件(局部注册)new Vue({el: '#root',data: {msg: '你好啊'},components: {school,student}})new Vue({el: '#root2',components: {school}})// // 使用对象式和函数式的区别// // let data = {// // a: 1,// // b: 2// // }// function data () {// return {// a: 1,// b: 2// }// }// // 使用对象式一变都变 根据栈堆存放// // const x1 = data// // const x2 = data// // 当你使用函数来返回对象时,每次调用该函数都会创建一个新的对象实例。即使这些对象具有相同的结构和初始值,//它们也是不同的对象,存储在内存的不同位置。因此,修改一个对象不会影响其他通过该函数创建的对象。// const x1 = data()// const x2 = data()</script>
几个注意点
<!--准备好一个容器 --><div id="root"><h2>{{msg}}</h2><!-- 双标签 --><!-- <school></school> --><!-- <School></School> --><!-- <my-school></my-school> --><!-- <MySchool></MySchool> --><!-- 单标签 自闭合 但是写多个只展示一个 所以建议在脚手架中适合用--><school /><school /><school /></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。//定义组件const s = Vue.extend({// 也可以组件注册为一个名字 Vue开发工具展示的一个名字 也就是给Vue开发工具那一块特意起了一个名字name: 'atguigu',template: `<div><h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> </div>`,data () {return {name: '尚硅谷',address: '北京'}}})// // 使用extend定义组件简写// const s = {// // 也可以组件注册为一个名字 Vue开发工具展示的一个名字 也就是给Vue开发工具那一块特意起了一个名字// name: 'atguigu',// template: `// <div>// <h2>学校名称:{{name}}</h2> // <h2>学校地址:{{address}}</h2> // </div>// `,// data () {// return {// name: '尚硅谷',// address: '北京'// }// }// }new Vue({el: '#root',data: {msg: '欢迎学习Vue!'},components: {// 第一种写法(首字母小写):school 但是Vue开发工具会默认转化为首字母大写school: s// 组件名字跟组件标签要相同// School: s// 'my-school': s// 在脚手架中才可以接收大驼峰命名// MySchool: s}})</script>
总结:
几个注意点:1.关于组件名:一个单词组成:第一种写法(首字母小写):school第二种写法(首字母大写):School多个单词组成:第一种写法(kebab-case命名):my-school第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)备注:(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。(2).可以使用name配置项指定组件在开发者工具中呈现的名字。2.关于组件标签:第一种写法:<school></school>第二种写法:<school/>备注:不用使用脚手架时,<school/>会导致后续组件不能渲染。3.一个简写方式:const school = Vue.extend(options) 可简写为:const school = options
一人之下 万人之上组件app
补充知识:在模板容器中没有写代码的原理 根据生命周期原理图,给换成内部模板了
<!--准备好一个容器 --><div id="root"></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。// 定义school的子组件studentconst student = Vue.extend({name: 'student',template: `<div><h2>学生姓名:{{name}}</h2> <h2>学生年龄:{{age}}</h2> </div>`,data () {return {name: '尚硅谷',age: 18}}})//定义school组件const school = Vue.extend({name: 'school',template: `<div><h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <student></student></div>`,data () {return {name: '尚硅谷',address: '北京'}},components: {student}})//定义hello组件const hello = Vue.extend({template: `<h1>{{msg}}</h1>`,data () {return {msg: '欢迎来到尚硅谷学习!'}}})//定义app组件 相当于所有组建的管理者 也就是vm委派给app 让app管理所有组件const app = Vue.extend({template: `<div> <hello></hello><school></school></div>`,components: {school,hello}})//创建vmnew Vue({template: `<app></app>`,el: '#root',//注册组件(局部)components: {app}})</script>
VueComponent
总结来说:就是VueComponent就是类比于Vue,所以有实例对象vc 在组件中this指向VueComponent实例对象,也就是组件
下面案例中涉及到组件嵌套
总结
关于VueComponent:1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。2.我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)。3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!4.关于this指向:(1).组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】(2).new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。Vue的实例对象,以后简称vm。
<!--准备好一个容器 --><div id="root"><school></school><hello></hello></div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。//定义school组件const school = Vue.extend({name: 'school',template: `<div><h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showName">点我提示学校名</button></div>`,data () {return {name: '尚硅谷',address: '北京'}}, methods: {showName () {// 组件中的this指向VueComponent,并且里面有Vue中的很多相似数据,进行了数据代理console.log('showName', this)}}})const test = Vue.extend({template: `<span>哈哈哈</span>`})// 定义hello组件const hello = Vue.extend({template: `<div><h2>{{msg}}</h2><test></test></div>`,data () {return {msg: '你好啊'}}, components: { test }})// 对应的VueComponent不是同一个// school.a = 100// console.log('@', school.a)// console.log('#', hello.a)// console.log('@', school)// console.log('#', hello)const vm = new Vue({el: '#root',components: {school, hello}});</script>
补充知识:上文提到望函数中添加属性,直接函数点属性名即可,因为函数在某种意义上也是对象。
<SCRIPT>function aa () {console.log('哈哈哈')}aa.b = 100// 函数本身也是一个对象console.log(aa.b);</SCRIPT>
一个重要的内置关系
1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
<div id="root"><!-- 相当于new Component() --><school></school></div><script>Vue.prototype.x = 99//定义school组件const school = Vue.extend({template: `<div><h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <button @click='showX'>点我发现我能get到Vue原型对象中的x</button></div>`,data () {return {name: '尚硅谷',address: '北京'}}, methods: {showX () {console.log(this.x)}}})//创建一个vmconst vm = new Vue({el: '#root',data: {msg: '你好'}, components: {school}})// 不要写VueComponent 死记硬背:我们通过调用extend函数返回值VueComponent并且赋值给school //这里按理说应该是true 但是不知道怎么回事是falseconsole.log(school.prototype._proto_ === Vue.prototype)/* function Demo () {this.a = 1this.b = 2}//创建一个Demo的实例对象const d = new Demo()console.log(Demo.prototype) //显示原型属性console.log(d.__proto__) //隐式原型属性// 指向同一个原型对象console.log(Demo.prototype === d.__proto__)//程序员通过显示原型属性操作原型对象,追加一个x属性,值为99Demo.prototype.x = 99// console.log('@', d._proto_.x)// 缩写console.log, ('@', d.x)// 表示关于demo函数的实例对象console.log(d); */</script>
组件实例对象小型vm 他的data必须写成函数式,也不能配置el
2.3 单文件组件
首先是xxx.vue,不能直接展示在浏览器,需要编译成js文件
方法1:使用webpack
方法2:使用脚手架
命名类似于组件名称 推荐首字母大写或者大驼峰命名
安装插件 识别vue文件
vue文件的编写依据组件的定义:实现应用中局部功能的代码和资源的集合,包括html结构、css样式、js交互
书写顺序
在②中通过如下将组件引入:
现成代码见资源