课程链接
传统方式编写应用:
组件:实现应用中局部功能代码和资源的集合
当应用中的js 都以模块来编写的,那这个应用就是一个模块化的应用
当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用。
一个文件中包含有n个组件
Vue中使用组件的三大步骤:
一、定义组件(创建组件)
二、注册组件
三、使用组件(写组件标签)
一、如何定义一个组件?
使用Vue.extend(options)
创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
区别如下:
1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。
备注:使用template可以配置组件结构。
二、如何注册组件?
1.局部注册:靠new Vue的时候传入components选项
2.全局注册:靠Vue.component(‘组件名’,组件)
三、编写组件标签:
<div id="root">
<school>school>
<hr>
<student>student>
<hr>
<hello>hello>
<hr>
div>
<div id="root2">
<hello>hello>
div>
body>
<script>
Vue.config.productionTip=false
// 1创建school组件
const school = Vue.extend({
template:`
学校名称:{{schoolName}}
学校地址:{{address}}
`,
data(){
return {schoolName:'1+1',address:'武汉'}
},methods: {
showMsg(){
alert(this.schoolName)
}
},
})
// 1创建student组件
const student = Vue.extend({
template:`
学生名称:{{studentName}}
学生年龄:{{age}}
`,
data(){
return {studentName:'brisa',age:18,}
}
})
// 1创建hello组件
const hello = Vue.extend({
template:`
你好{{name}}
`,
data(){
return {name:'ali'}
}
})
// 2注册全局组件
Vue.component('hello',hello)
new Vue({
el:"#root",
data:{
msg:'lllll',
},
components:{
// 2注册组件 - 局部
school:school,
student,//简写
}
})
new Vue({
el:"#root2",
})
script>
几个注意点:
1.关于组件名:
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成:
第一种写法(kebab-case命名):my-school
第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
备注:
(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。
2.关于组件标签:
第一种写法:
第二种写法:
备注:不用使用脚手架时,
会导致后续组件不能渲染。
3.一个简写方式:
const school = Vue.extend(options)
可简写为:const school = options
<div id="root">
<h1>{{msg}}h1>
<school>school>
<school />
div>
body>
<script>
Vue.config.productionTip=false
// 定义组件
/* const school = Vue.extend({
name:'atguigu',
template:`
学校名:{{schoolName}}
学校地址:{{schoolAddress}}
`,
data(){
return {schoolName:'1+1',schoolAddress:'wuhan'}
}
}) */
//简写
const school = ({//不写vue.extend,在vm的components里也会被自动解析,这就是可以简写的原因(如果不简写,则是依靠自己的声明完成解析)
name:'atguigu',
template:`
学校名:{{schoolName}}
学校地址:{{schoolAddress}}
`,
data(){
return {schoolName:'1+1',schoolAddress:'wuhan'}
}
})
new Vue({
el:"#root",
data:{
msg:'欢迎学习vue',
},components:{
// 1个单词组成 - 纯小写 或者 首字母大写(与开发者工具呼应)
// 多个单词组成 - 全部小写用-分隔 或者 首字母都大写《结合脚手架使用>
school,//组件名,简写
}
})
script>
嵌套,最后容器中什么都没有写
<div id="root">
div>
body>
<script>
Vue.config.productionTip=false
// 1创建student组件
const student = Vue.extend({
template:`
学生名称:{{studentName}}
学生年龄:{{age}}
`,
data(){
return {studentName:'brisa',age:18,}
}
})
// 1创建school组件
const school = Vue.extend({
template:`
学校名称:{{schoolName}}
学校地址:{{address}}
`,
data(){
return {schoolName:'1+1',address:'武汉'}
},components:{
student//school中嵌套student,要使用的话,就在这里的template加student
}
})
const hello = Vue.extend({
template:'hello,{{name}}哇
',
data(){return {name:'brisa'}}
})
const api = Vue.extend({//使用一个api管理所有的组件
template:`
`,components:{
school,
hello
}
})
new Vue({
template:` `,//在这里使用api,不在容器中去写
el:"#root",
data:{
},
components:{
api//vm就只调用这个api即可
}
})
script>
关于VueComponent:
1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
2.我们只需要写
或
,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">
div>
body>
<script>
Vue.config.productionTip = false;
// 1创建school组件
const school = Vue.extend({
template: `
学校名称:{{schoolName}}
学校地址:{{address}}
`,
data() {
return { schoolName: "1+1", address: "武汉" };
},
methods: {
showName() {
alert(this.schoolName);
console.log(this); //this--->vc(VueComponent)的school实例对象
},
},
});
const hello = Vue.extend({
template: `
你好,{{name}}
`,
data() {
return { name: "brisa" };
},
});
console.log(school === hello); //false
console.log(school);
/* ƒ VueComponent(options) {
this._init(options);
} */
const api = Vue.extend({
template: `
`,
components: {
school,
hello,
},
});
const vm = new Vue({
template: ` `,
el: "#root",
data: {},
components: {
api,
},
});
console.log(vm);
script>
显示原型属性:prototype
隐式原型属性:__proto__
指向的对象称为原型对象
1.一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
一个文件中只包含有1个组件
<template>
页面模板
template>
<script>
export default {
data() {
return {}
},
methods: {},
computed: {},
components: {}
}
script>
<style>
样式定义
style>
<template>
<div>
<school>school>
<Student>Student>
div>
template>
<script>
// 引入组件
import School from "./School";
import Student from "./Student";
export default {
name: "App",
components: {
School,
Student,
},
};
script>
1、第一步(仅第一次执行):全局安装@vue/cli。
npm config set registry https://registry.npm.taobao.org
vue inspect > output.js
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件
.....
或
this.$refs.xxx
功能:让组件接收外部传过来的数据
传递数据:
接收数据:
第一种方式(只接收):props:['name']
第二种方式(限制类型):props:{name:String}
第三种方式(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String, //类型
required:true, //必要性
default:'老王' //默认值
}
}
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步定义混合:
{
data(){....},
methods:{....}
....
}
第二步使用混入:
全局混入:Vue.mixin(xxx)
局部混入:mixins:['xxx']
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
定义插件:
对象.install = function (Vue, options) {
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}
使用插件:Vue.use()
题外话:
vue脚手架不直接支持less,用 npm i less-loader@7
下载安装依赖
这个@7是限制版本
组件化编码流程:
(1).拆分静态组件:组件要按照功能点
拆分,命名
不要与html元素冲突。
(2).实现动态组件:考虑好数据的存放位置
,数据是一个组件在用,还是一些组件在用:
1).一个组件在用:放在组件自身即可。
2). 一些组件在用:放在他们共同的父组件上(状态提升)。
(3).实现交互:从绑定事件开始。
props适用于:
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信(要求父先给子一个函数)
使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。
存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
浏览器端通过 Window.sessionStorage
和 Window.localStorage
属性来实现本地存储机制。
相关API:
xxxxxStorage.setItem('key', 'value');
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
xxxxxStorage.getItem('person');
该方法接受一个键名作为参数,返回键名对应的值。
xxxxxStorage.removeItem('key');
该方法接受一个键名作为参数,并把该键名从存储中删除。
xxxxxStorage.clear()
该方法会清空存储中的所有数据。
备注:
xxxxxStorage.getItem(xxx)
如果xxx对应的value获取不到,那么getItem的返回值是null
。JSON.parse(null)
的结果依然是null
。一种组件间通信的方式,适用于:子组件 ===> 父组件
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
绑定自定义事件:
第一种方式,在父组件中:
或
第二种方式,在父组件中:
<Demo ref="demo"/>
......
mounted(){
this.$refs.xxx.$on('atguigu',this.test)
}
若想让自定义事件只能触发一次,可以使用once
修饰符,或$once
方法。
触发自定义事件:this.$emit('atguigu',数据)
解绑自定义事件this.$off('atguigu')
组件上也可以绑定原生DOM事件,需要使用native
修饰符。
注意:通过this.$refs.xxx.$on('atguigu',回调)
绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
一种组件间通信的方式,适用于任意组件间通信。
安装全局事件总线:
new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
......
})
使用事件总线:
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
methods(){
demo(data){......}
}
......
mounted() {
this.$bus.$on('xxxx',this.demo)
}
提供数据:this.$bus.$emit('xxxx',数据)
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
一种组件间通信的方式,适用于任意组件间通信。
使用步骤:
安装pubsub:npm i pubsub-js
引入: import pubsub from 'pubsub-js'
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){
demo(data){......}
}
......
mounted() {
this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
}
提供数据:pubsub.publish('xxx',数据)
最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)
去取消订阅。
this.$nextTick(回调函数)