Vue基础 中
一、组件
1.组件概述
组件化:封装的思想,把页面上可重用的部分封装为组件,从而方便项目的开发和维护
组件:是可复用的Vue实例,封装标签、样式和JS代码
2.vue组件——创建
每个组件都是一个独立的个体, 代码里体现为一个独立的.vue文件
-
组件内template只能有一个根标签
-
组件内data必须是一个函数, 独立作用域
3.vue组件——全局注册使用
全局入口在main.js, 在new Vue之上注册
//main.js引入
import Vue from 'vue'
import 组件对象 from 'vue文件路径'//引入组件文件对象
Vue.component("组件名", 组件对象)// 组件名开头大写驼峰(推荐)
使用如:<PannelC></PannelC>//把这个自定义标签当做组件解析
4.vue组件——局部注册使用
//App.vue中引入, 注册, 使用
import 组件对象 from 'vue文件路径'
export default {
components: {
"组件名": 组件对象
}
}
- 组件使用总结
①(创建)封装html+css+vue到独立的.vue文件中
②(引入注册)组件文件 => 得到组件配置对象
③(使用)当前页面当做标签使用
5.vue组件——scoped作用
作用:解决多个组件样式名相同,冲突问题
加上scoped属性,即自动给标签添加data-v-hash值属性
二、组件通信
1.父向子 props
例如: App.vue(父) MyProduct.vue(子)
步骤:
- 创建组件components/MyProductvue - 准备标签
- 组件内在props定义变量, 用于接收外部传入的值(props属性名建议都小写,因为标签里的属性只能小写/把变量驼峰转成-连接)
- App.vue中引入注册组件, 使用时, 传入具体数据给组件显示
注意:在vue中需要遵循单向数据流原则:从父到子的数据流向。
props的值不能重新赋值, 对象引用关系属性值改变, 互相影响
2.子向父
步骤
- 父: @自定义事件名=“父methods函数”
- 子: this.$emit(“自定义事件名”, 传值) - 执行父methods里函数代码
总结:父自定义事件和方法, 等待子组件触发事件给方法传值
3.跨组件通信 EventBus
//EventBus/index.js- 定义事件总线bus对象
import Vue from 'vue'
const bus = new Vue()
export default bus
//List.vue注册事件
eventBus.$on('事件名', 函数体)
//Myp.vue
import eventBus from '../EventBus'
eventBus.$emit('事件名', 值)
总结: 空的Vue对象, 只负责 $on注册事件, $emit触发事件, 一定要确保 $on先执行
三、组件生命周期
- 生命周期:一组件从创建到销毁的整个过程
- 生命周期函数(钩子函数):vue 框架内置函数,随着组件的生命周期,自动按次序执行
- 分类:初始化、挂载、更新、销毁
- 当页面第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子函数
beforeCreate() {
// 1. 创建前,在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
console.log("beforeCreate --- data初始化之前");
console.log(this.msg); // undefined
},
created() {
// 2. 创建后,在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer), 属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
console.log("created --- data初始化以后");
console.log(this.msg); // "我是变量"
},
beforeMount() {
// 3. 挂载前,在挂载开始之前被调用:相关的 render 函数首次被调用。
console.log("beforeMount --- vue的虚拟DOM, 挂载到真实的网页之前");
// console.log(document.getElementById("myUl").children[1].innerHTML); // 报错
},
mounted() {
// 4. 挂载后,el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。
console.log("mounted --- vue的虚拟DOM, 挂载到真实的网页上 ");
console.log(document.getElementById("myUl").children[1].innerHTML);
},
beforeUpdate() {
// 5. 更新前,数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
console.log("beforeUpdate --- 数据更新, 页面更新前");
// 比如点击新增数组元素, vue会触发此生命周期函数, 但是此时页面并未更新, 所以获取不到新增的li标签
// console.log(document.getElementById("myUl").children[4].innerHTML); // 报错
},
updated() {
// 6. 更新后,由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
console.log("updated --- 数据更新, 页面更新后");
console.log(document.getElementById("myUl").children[4].innerHTML);
},
activated() {
//keep-alive 组件激活时调用。该钩子在服务器端渲染期间不被调用。
}
deactivated() {
//keep-alive 组件停用时调用。该钩子在服务器端渲染期间不被调用。
},
beforeDestroy() {
// 7. 销毁前,实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用。
console.log("beforeDestroy --- 实例销毁之前调用");
},
destroyed() {
// 8. 销毁后,Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
// (清空一些本地变量 / 全局变量 / 销毁当前组件的eventBus事件, 引用的全局事件)
console.log("destroyed --- 销毁完成");
},
errorCaptured() {
//(2.5.0+ 新增)当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
}
四、高级用法 $ nextTick和$refs
目标: 利用 ref 和 $refs 可以用于获取 dom 元素, 或者组件实例
//ref定义值
<h1 ref="myH1">1. ref获取原生dom</h1>
//通过$refs.值 来获取dom或组件对象
this.$refs.myH1.innerHTML = "改内容了";
<p ref="a">数字: {{ count }}</p>
//dom是异步更新的, $nextTick可以等待dom更新后触发此方法
this.$nextTick(() => {
console.log("DOM更新后触发$nextTick函数");
console.log(this.$refs.a.innerHTML); // 1
})
五、动态组件、插槽
1.动态组件
动态组件:多个组件使用同一个挂载点,并动态切换,这就是动态组件
//vue内置component组件, 配合is属性, 设置要显示的组件名字
<component :is="comName"></component>
2.组件缓存
目标:组件切换会导致组件被频繁销毁和重新创建, 性能不高
补充生命周期:
-
activated - 激活
-
deactivated - 失去激活状态
判断缓存的组件是出现还是消失
<keep-alive>
<!-- vue内置的组件component, -->
<component :is="comName"></component>
</keep-alive>
- keep-alive可以提高组件的性能, 内部包裹的标签不会被销毁和重新创建, 触发激活和非激活的生命周期方法
3.组件插槽
vue提供组件插槽能力, 允许开发者在封装组件时,把不确定的部分定义为插槽。
用于实现组件的内容分发, 通过 slot 标签, 可以接收到写在组件标签内的内容。
步骤:
1. 组件内用<slot></slot>占位
2. 使用组件时<Pannel></Pannel>夹着的地方, 传入标签替换slot
//Pannel.vue
<slot></slot>
//UserSlot.vue - 使用组件
<Pannel>
<img src="../assets/mm.gif" alt="" />
<span>我是文字哦</span>
</Pannel>
import Pannel from "./Pannel";
export default {
components: {
Pannel,
},
};
4.具名插槽
目标:当一个组件内有2处以上需要外部传入标签的地方。
slot的name属性起插槽名, 使用组件时, template配合#插槽名传入具体标签
//Pannel2.vue - 留下具名slot
<div class="container" v-show="isShow">
<slot name="one"></slot>
<slot name="two"></slot>
</div>
//UseSlot2.vue - v-slot可以简化成#
<Pannel2>
<template v-slot:one>
<img src="../assets/mm.gif" alt="" />
</template>
<template v-slot:two>
<span>我是文字哦</span>
</template>
</Pannel2>
5.作用域插槽
目标: 子组件里值, 在给插槽赋值时在父组件环境下使用
步骤:
- 创建组件, 准备slot, 在slot上绑定属性和子组件值
- 使用组件, 传入自定义标签, 用template和v-slot=“自定义变量名”
- 自定义变量名会自动绑定slot上所有属性, 就可以使用子组件内值, 并替换slot位置
六、自定义指令
目标:获取标签,扩展额外的功能
1.定义方式
①局部注册和使用
<template>
<div>
<input type="text" v-focus />
</div>
</template>
<script>
export default {
// 局部注册
directives: {
focus: { // 自定义指令名
inserted(el){ // 固定配置项 - 当指令插入到标签自动触发此函数
el.focus()
}
},
},
};
</script>
②全局注册
//main.js文件 v-fofo指令
Vue.directive("fofo", {
inserted(el){//dom插入到页面上执行这个函数
el.focus()
}
})
2.接值
//main.js
Vue.directive("color", {
inserted(el, binding){ // 插入时触发此函数
el.style.color = binding.value;
},
update(el, binding){ // 更新时触发此函数
el.style.color = binding.value;
}
})
//Direct.vue
<p v-color="theColor" @click="changeColor">使用v-color指令控制颜色, 点击变蓝</p>
<script>
data() {
return {
theColor: "red",
};
},
methods: {
changeColor() {
this.theColor = 'blue';
},
},
</script>