Vue组件化编程
一、对组件的理解
-
什么是组件?
组件的定义:
实现应用中局部【组件要拆的尽量细致】功能代码【对于前端来说,就是css+html+js】和资源【mp3、mp4、ttf、.zip...】的集合【组件是一个集合】
-
与传统方式编程相比,组件化编程有什么优势?
依赖关系不混乱、好维护、代码复用率高。
组件化编程体现了封装的思想
一个应用只有一个vm,但一个vm会管理多个组件
组件可以产生嵌套
模块与组件、模块化与组件化
模块:可以将一个js拆分成多个js,前端中的模块指的就是js中的模块
- 理解: 向外提供特定功能的 js 程序, 一个模块就是一个 js 文件
- 为什么: js 文件很多很复杂
- 作用: 复用 js, 简化 js 的编写, 提高 js 运行效率【js的模块化:解决js依赖关系混乱,js引入变量或函数冲突】
组件
- 理解: 用来实现局部(特定)功能效果的代码集合(html/css/js/image…..)
- 为什么: 一个界面的功能很复杂
- 作用: 复用编码, 简化项目编码, 提高运行效率
模块化:形容词
当应用中的 js 都以模块来编写的, 那这个应用就是一个模块化的应用【将应用中的js按照模块化的标准进行了拆分】。
组件化:形容词
当应用中的功能都是多组件的方式来编写的, 那这个应用就是一个组件化的应用【应用中按照功能点进行了拆分出了不同的组件】。
注:一个应用可以既是模块化的也是组件化的,模块化是针对js的,说的是应用中针对js按照模块化标准进行了拆分;组件化是针对不同的功能进行拆分,两个词并不冲突
二、Vue中的组件
Vue中如何定义/创建一个组件
Vue中有两种组件的编写形式:非单文件组件和单文件组件
1. 非单文件组件
1.1 基本使用
一个文件中包含n个组件
注意的点:
- 组件名要尽量语义化
- 配置对象参数用于控制组件的相关内容,该配置对象参数可传入的配置项与创建vm时传入的配置对象的配置项几乎是相同的。
- 组件的配置对象参数中不可传入el【组件就是一块砖,哪里需要哪里搬】
- 组件的配置对象参数中data配置项必须是函数式写法【组件会被多个页面引用,使用对象式写法会导致多个页面引用同一个data对象,一个页面的数据改变,另一个页面的数据也会变;使用函数式写法每次都会返回一个新的data对象,一个页面的数据改变,不会影响到其他引用该组件的页面】
- components中的key对应组件名
- 非单文件组件样式不能跟着组件走,就是样式不能定义在组件中
Vue中使用组件的三大步骤:
一、定义组件(创建组件)
二、注册组件
三、使用组件(写组件标签)
一、如何定义一个组件?
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
区别如下:
1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。简单说就是el是与具体哪个元素绑定,绑定了就无法复用了,所以不能写。
2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。
备注:使用template可以配置组件结构。
二、如何注册组件?
1.局部注册:靠new Vue的时候传入components选项
2.全局注册:靠Vue.component('组件名',组件)
三、编写组件标签:
<school></school>
<body>
<!-- 准备好一个容器-->
<div id="root">
<hello></hello>
<hr>
<h1>{{msg}}</h1>
<hr>
<!-- 第三步:编写组件标签 -->
<school></school>
<hr>
<!-- 第三步:编写组件标签 -->
<student></student>
</div>
<div id="root2">
<hello></hello>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
//第一步:创建school组件
const school = Vue.extend({
template:`
<div class="demo">
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">点我提示学校名</button>
</div>
`,
// el:'#root', //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。
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)
//创建vm
new Vue({
el:'#root',
data:{
msg:'你好啊!'
},
//第二步:注册组件(局部注册)
components:{
school,
student
}
})
new Vue({
el:'#root2',
})
</script>
1.2 组件的几个注意点
几个注意点:
1.关于组件名:
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成:
第一种写法(kebab-case命名):my-school
第二种写法(CamelCase【大驼峰】命名):MySchool (需要Vue脚手架支持,html中直接引入vue.js的情况下是不支持这种命名的)
注:脚手架:用webpack搭建出来一整套的工作流
备注:
(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
(2).可以在创建组件时使用name配置项指定组件在开发者工具中呈现的名字【常用在第三方组件库或大型项目开发中】。如果创建组件时未指定name配置项,Vue开发者工具中显示的组件名就是注册组件时用的组件名
2.关于组件标签:
第一种写法:<school></school>
第二种写法:<school/>【自闭合】(需要Vue脚手架支持,虽然单个的自闭合标签不会报错)
备注:不使用脚手架时,<school/>会导致后续组件不能渲染。
3.一个简写方式:
const school = Vue.extend(options) 可简写为:const school = options【即使简写,在使用components配置项注册组件时会进行判断,如果是简写Vue会帮我们调用Vue.extend】
注:vue.js源码中也是可以打debugger的
1.3 组件的嵌套
创建组件的顺序很重要,子组件要创建在父组件之前
页面中的组件嵌套就体现了组件之间的父子关系,上面的组件是下面组件的子组件
开发中会定义一个名为app的组件,用于管理应用中所有的组件【一人之下,万人之上】
<body>
<!-- 准备好一个容器-->
<div id="root">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
//定义student组件
const 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:'北京'
}
},
//注册组件(局部):体现嵌套关系需要在父组件中注册子组件,并在父组件的template中使用子组件标签
components:{
student
}
})
//定义hello组件
const hello = Vue.extend({
template:`<h1>{{msg}}</h1>`,
data(){
return {
msg:'欢迎来到尚硅谷学习!'
}
}
})
//定义app组件
const app = Vue.extend({
template:`
<div>
<hello></hello>
<school></school>
</div>
`,
components:{
school,
hello
}
})
//创建vm
new Vue({
template:'<app></app>',
el:'#root',
//注册组件(局部)
components:{app}
})
</script>
疑问:template中什么时候不用包div,什么时候需要包div?
1.4 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)的options配置中:
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。
5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。
Vue的实例对象,以后简称vm。
6.vm管理其他的vc是如何体现的?
vm/vc的身上有一个$children属性,该属性中维护了vm/vc的子组件列表
7.vm和vc能够划等号吗?
不能,vm能够通过el配置项决定为哪个容器服务,vc却不行;vm的data配置项可以是对象可以是函数,vc的data配置项只能是函数,防止一个组件被多次使用的时候存在数据的引用关系。
new Vue/定义组件传入的配置对象的配置项顺序是没有要求的,通常顺序是template、data、components ...
注:原理性的知识提前讲,API【属性和方法】现用现讲
1.5 一个重要的内置关系
原型基础知识
<script type="text/javascript">
// 定义一个构造函数Demo
function Demo(){
this.a = 1
this.b = 2
}
// 创建一个Demo的实例对象
const d = new Demo()
// 只要是函数,身上就有一个prototype属性,显式原型属性
console.log(Demo.prototype)
// 构造函数缔造出来的实例对象,身上有一个__proto__属性,隐式原型属性
console.log(d.__proto__)
// 显式原型属性和隐式原型属性指向了同一个原型对象
console.log(Demo.prototype === d.__proto__)
// 程序员通过显式原型属性操作原型对象,追加了一个x属性,值为99
Demo.prototype.x = 99
// 可以通过隐式原型对象拿到刚刚追加的属性
console.log(d.__proto__.x)
// 这样也可以拿到,d身上没有x,默认就会从__proto__找x【自身没有,就按照隐式原型链查找】
console.log(d.x)
</script>
查看Vue构造函数身上的属性和方法
console.dir(Vue)
Vue原型对象的所有属性和方法,Vue的实例对象都能用
只要是函数,身上就有prototype属性
只要是对象,身上就有__proto__属性
实例的隐式原型属性永远指向自己缔造者【实例的构造函数】的原型对象
1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
Vue让VueComponent原型对象的隐式原型属性指向了Vue的原型对象,也就是说,VueComponent原型对象的原型对象就是Vue的原型对象
2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
谁缔造了这个原型对象,就是看看这个原型对象是什么类型的
2. 单文件组件
一个文件中只包含1个组件,文件和组件是一一对应的
.vue文件直接交给浏览器,是不能运行的,所以必须将.vue处理加工为.js,浏览器才能够识别
将.vue处理加工成.js的两种方式:webpack和官方提供的脚手架
webpack:自己用Loader和plugin完成整个编译的流程,自己搭建一个工作流
官方提供的脚手架:Vue团队通过webpack的方式打造完的一套工作流
单文件组件文件名的规则和非单文件组件命名的规则相同
一个标准组件的构成:html + css + js
对应的,Vue提供了三个标签,<template></template>、<style></style>、<script></script>
分别负责编写组件的结构、组件的样式、组件交互相关的代码(数据、方法等等)
要求<template></template>中必须要有一个根元素
VScode 对Vue编码提供支持的插件【不装没有标签提示】:vetur 作者 Pine Wu,安装后要重启VScode
ES6模块化
暴露方式:
-
分别暴露:export,常用于暴露多个对象
暴露:
// mixin.js export const x = { methods:{ showName(){ alert(this.name) } } }
引入:
import { x } from '../mixin.js'
-
统一暴露:export {}
-
默认暴露:export default,最常用,默认暴露单个对象的情况下引入的方式最简单
暴露:
export default { name: 'School', data(){ return { name: 'sgg', address: 'bj' } } }
引入:
import School from './components/School'
name属性最好与文件名保持一致,如果不写,默认组件名为注册时指定的名字
入口文件 main.js在不同的脚手架中命名可能不同,main/index/app.js
使用单文件组件的整个过程
-
创建School组件和Student组件
<template> <div class="demo"> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showName">点我提示学校名</button> </div> </template> <script> export default { name:'School', data(){ return { name:'尚硅谷', address:'北京昌平' } }, methods: { showName(){ alert(this.name) } }, } </script> <style> .demo{ background-color: orange; } </style>
<template> <div> <h2>学生姓名:{{name}}</h2> <h2>学生年龄:{{age}}</h2> </div> </template> <script> export default { name:'Student', data(){ return { name:'张三', age:18 } } } </script>
-
创建App.vue,用于汇总所有组件
<template> <div> <School></School> <Student></Student> </div> </template> <script> //引入组件 import School from './School.vue' import Student from './Student.vue' export default { name:'App', components:{ School, Student } } </script>
-
创建main.js入口文件,用于创建Vue实例并指明要服务的容器
import App from './App.vue' new Vue({ el:'#root', template:`<App></App>`, components:{App}, })
-
创建index.html,包含了容器
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>练习一下单文件组件的语法</title> </head> <body> <!-- 准备一个容器 --> <div id="root"></div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript" src="./main.js"></script> </body> </html>
直接运行是会报错的
浏览器不能直接支持ES6的模块化语法
标签:Vue,name,对象,编程,2x,js,组件,data From: https://www.cnblogs.com/wzzzj/p/18045287