注册组件,使用组件(项目中都是写component中)
为什么用组件:组件可以复用,每一个组件都是独立的,模板,数据,css样式互不影响
-
全局注册组件component:请勿滥用全局组件inx 因为 全局组件会造成组件名污染
-
vue2:Vue.component("组件名", { } ) 全局组件
-
vue3:app.component(“组件名字”, { } ) 全局组件
-
vue3: 局部注册组件components
-
<div id="app">
<my-header></my-header>
</div>
</html>
<script src="./lib/vue.global.js"></script>
<script>
let app = Vue.createApp({
data () {
return {
}
},
components: { // 局部组件的注册
// 组件的名字:对象形式的配置项
'my-header':{
template:`<div>{
{msg}}</div>`,
data() {
return {
msg:"子组件"
}
},
}
}
})
app.mount("#app")
</script>
组件传参:解决组件之间数据无法共享(单向数据流原则)
-
vue中遵循是单向数据流,数据的流向是父到子,父组件的数据变化了,props中的数据就会立马的更新
-
props:父传子(只读属性):父组件需要在子组件身上通过自定义属性传值,子组件内部通过props接收值(props 校验)
-
父中子组件 v-bind(自定义属性)绑定属性样式,属性值就是父的数据传递给子组件
-
在子中定义一个 props来接收场地过来的属性名
-
接收的props上的所有的数据,都可以直接在子组件的模板上使用,也可以使用在选项中(记得带this)即:methods,watch,computed
-
eg::: 1.父组件传递 (也可以自定义属性传值 :xiaoming="") <ZiCom xiaoming="我是传递的数据a" xiaohong='我是传递的第二个b'/> 2.子组件设置接收 props,直接使用 export default // 1.使用props定义接收参数 props:["xiaoming","xiaohong"] }
props验证:
-
<!-- 父传子 --> <div id="app"> { {fMsg}} <!-- 子组件 --> <my-kun :title="fMsg" :hei="list[0].title" :num="list[0].price"></my-kun> </div> </html> <script src="./lib/vue.global.js"></script> <script> let app = Vue.createApp({ data() { return { fMsg: '父的数据在此', list: [{ title: '小黑子', price: 5 }, { title: '坤坤', price: 5 }, { title: '凡凡', price: 5 }] } } }) app.component('myKun', { template: `<div> { {zMsg}}-----{ {title}}-----{ {hei}}-----{ {num}} </div>`, // props: ['title','hei','num'], props: { 'title': [String], 'hei': String, // 自定义类型校验函数 'num': { validator(value) { // The value must match one of these strings return ['你好', '不好',5].includes(value) } }, }, data() { return { zMsg: '爱坤才会赢' } } }) app.mount('#app') </script>
-
-
emits:子传父(监听事件):父组件需要在子组件身上绑定自定义事件,子组件内部通过$emit触发事件并传值(可以 emits 效验)
- 父中子组件绑定事件@myclick="fuMethod"
- 父中methods中使用 fuMethod( val ){ } 方法。用于接收子传递过来的数据,做处理
- 而 事件名 myclick则要去子组件中 emits中接收,绑定方法,然后使用(或者直接使用) 然后两种方式使用:
第一种: emits:['myClick'], //接收 methods:{ ziMethod(){ this.$emit('myclick', this.zMsg) } }, html中使用: @click="ziMethods"
第二种:接收后,直接html中使用 <button @click="$emit('someEvent')">click me</button> - - 父中接收,methods中的fuMethod方法做接收的处理。 处理完后根据:根据单向数据流原则,也自动更新视图
- 父组件
<template> <div> fufuf <!-- 3.绑定自定义事件 --> <!-- <ZiCom @自定义事件=“函数”/> --> <ZiCom @xiaoming="demo"/> </div> </template> <script> import ZiCom from "./ZiCom.vue" export default { components:{ ZiCom }, methods:{ // 4定义函数 并且接收自定义事件上面的数据 demo(val){ console.log(val) } } } </script>
子组件
-
<template> <div> zizi <!-- 1.逆向传值必须必须必须要使用事件来触发 --> <button @click="fun()">点我把数据给父组件</button> </div> </template> <script> export default { data(){ return { zitext:"我是子组件的数据么么哒!!!!!" } }, methods:{ fun(){ // 2.自定义事件 携带我们子组件的数据 // this.$emit("给自定义事件起个名字",你传递的数据) this.$emit("xiaoming",{a:this.zitext}) } } } </script>
-
<!-- 父传子 --> <div id="app"> { {fMsg}} <!-- 子组件 --> <my-kun @myclick="fuMethod"></my-kun> </div> </html> <script src="./lib/vue.global.js"></script> <script> let app = Vue.createApp({ data() { return { fMsg: '父的数据在此', list: [{ title: '小黑子', price: 5 }, { title: '坤坤', price: 5 }, { title: '凡凡', price: 5 }] } }, methods: { fuMethod(msg) { console.log(111); this.fMsg = msg } } }) app.component('myKun', { template: `<div> { {zMsg}} <button @click="ziMethod">哈哈</button> </div>`, // 直接写template中 // <button @click="$emit('someEvent')">click me</button> // emits:["myclick"], emits: { // click: null, // 校验 submit 事件 'myclick': (payload) => { if (payload) { return true } else { console.warn('忘记传参了') return false } } }, data() { return { zMsg: '爱坤才会赢' } }, methods: { ziMethod() { this.$emit('myclick', this.zMsg) } } }) app.mount('#app') </script>
-
兄弟组件传值:
先实例化一个公共的通信对象(中央事件总裁)eventBus.js,
其中一个兄弟组件通过$emit去发送触发事件 ,
一个组件通过$on接收监听事件
vue3:中没有明确的兄弟组件传值的方案,可以使用状态提升
(找到这两个组件共同的父级组件,然后通过父与子之间的传值实现)
//utils文件夹下:eventBus.js
import {createApp} from 'vue'
// 实例化通信对象,导出
export default createApp()
跨组件传值:方式一:依赖注入:外层组件通过 provide 选项传值,内层组件通过inject接收值:
不推荐使用依赖注入:因为数据追踪比较困难,不知道是哪一个层级声明了这个或者不知道哪一层级或若干个层级使用了。如果数据在多个组件都需要使用到,可以使用 vueX 来进行状态管理。如果只是子组件想要使用父组件上的数据,可直接通过 props 来让父组件给子组件传值。
跨组件传值:方式二:vuex状态管理工具也可实现。单向数据流
一个vue文件 provide只能写一个(data同级),写多个只有最后一个生效,所以,其他方法,
计算属性也是如此,自上而下执行代码,只有最后一个生效
特殊属性ref $parent $root
-
ref
用于注册元素或子组件的引用。进行基本的页面dom操作1.在标签的dom之上使用ref=“随便起个名字”
2.this.$refs.你的那个名字即可找到指定元素
放在标签上:获取的是所在标签的dom节点 。
<template>
<div>
<!-- 1.绑定 -->
<h1 ref="wangcai">找到我</h1>
<button @click="fun()">点我修改上面的内容</button>
</div>
</template>
<script>
export default {
methods:{
fun(){
// 2.找到他
this.$refs.wangcai.style.color="red";
}
}
}
</script>
放到组件上,获取子组件的方法属性。使用方法时记得方法要加()调用。不要重复打印
<template>
<div>
<h1>我是home</h1>
<!-- 1.把ref绑定到组件身上 -->
<Rc ref="com"/>
<button @click="fun()">点我</button>
</div>
</template>
<script>
import Rc from "@/components/refcom.vue"
export default {
components:{
Rc
},
methods:{
fun(){
// 2.把ref绑定到组件身上父组件就可以得到子组件的所有属性和方法
console.log(this.$refs.com)
}
}
}
</script>
$parent:写子组件内可以获取父组件的方法属性,有就获取,没有则为 null
$root:写子组件内可以获取 根组件的方法属性,有就获取,没有就是它自己
$set:This.$set("你要给谁添加","x添加的key","你要添加的val")
在vue中 数据改变试图不变怎么解决?
<template>
<div>
<h2>数据变试图不会改变</h2>
<h3>{
{obj.age}}</h3>
<button @click="funb()">点我</button>
</div>
</template>
<script>
// 在vue2.0中 数据的双向绑定 是基于数据劫持与发布者订阅者模式的
// 其中数据劫持是通过Object.defineProperty()这个方法来拦截劫持data中的数据的 因为有了这个方法
// 所以数据改变试图也会更新
// 但是 Object.defineProperty()有个问题 他是会监听初始化的数据 如果中途给数组或者对象
// 添加新属性的时候 Object.defineProperty() 就不会监听到 不会监听到就没有数据劫持 没有
// 数据劫持就没有双向绑定 没有双向绑定就没有数据变试图变
export default {
methods:{
funb(){
// this.obj.age=18
// console.log(this.obj.age)
this.$set(this.obj,"age",18)
}
},
data(){
return {
obj:{
name:"xixi"
}
}
}
}
</script>
透传(属性):Vue3:透传属性。透传又分为自动透传绑定,和手动绑定两种。
什么是透传属性? 透传属性指组件在使用的时候,作用在组件上的属性,会被向下流动,绑定到组件内的标签中。
透传:父组件传递数据给子组件时,子组件接收父组件传递的属性 $attrs
透传作用:::
- attribute继承:子继承父:eg:class,id,style,属性,事件等头可以透传到组件内的标签中。
- 对class和style合并
v-on监听继承
- 禁用透传属性是否继承属性: inheritAttrs: false
- 多根节点的继承:子组件使用props接收父组件传递的属性
内置组件
<KeepAlive> 缓存组件:用于包裹动态切换组件
<KeepAlive> 包裹动态组件时,会缓存不活跃的组件实例,而不是销毁它们。避免重复渲染组件(性能优化)
keep-alive和include,exclude
activated deactivated
<component :is='coms[index]' /> 动态组件:判断渲染哪个组件
is:特殊Attribute用于绑定[动态组件]
//动态组件:包裹挂载点即可
<keep-alive>
<component :is="Com"></component>
</keep-alive>
//路由:包裹路由出口即可
<keep-alive>
<router-view/>
</keep-alive>
//属性:如果 我把两个都写了 excloud的优先级大于incloude
include 你要缓存谁
exclude 你不想缓存谁
<keep-alive exclude="Db,xxxxx,cccc">
<!-- 设置动态组件的挂载点 -->
<component :is="com"></component>
</keep-alive>
#### 钩子函数
activated:在进入到被kepp-alive管理的组件时候触发
deactivated:在离开被kepp-alive管理的组件时候触发
替代 `mounted`挂载后 和 `unmounted`销毁后。
<template>
<div>
aaaaaaaa
<input type="text" />
</div>
</template>
<script>
export default {
activated() {
console.log("进入到了被keep-alive管理的组件中了");
},
deactivated() {
console.log("离开到了被keep-alive管理的组件中了");
},
};
</script>
动画组件 <Transiton />:里边只能放一个标签或者组件(使用vue官网查看)
name=" " 自定义动画类名前缀,没有就默认
mode="out-in" 设置动画顺序,in-out,out-in
一共有 6 个应用于进入与离开过渡效果的 CSS class
<Transition>
<p v-if="show">hello</p>
</Transition>
/* 下面我们会解释这些 class 是做什么的 */
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
插槽 <slot /> 组件: 自定义组件里内容 (借用一个链接详细讲解)
如果在组件的开关标签中插入内容 默认是不显示的(组件是一个完整的独立的个体 如果没有特殊设置 那么没有办法向组件中插入内容) slot用来混合父组件与子组件自己的模板
默认插槽 <slot />
具名插槽 <slot name = "" />
插件plugins + 自定义指令directive
定义插件:export default { install(app){ } }
应用插件:app.use(插件) use方法在执行的时候会自动寻找插件的 install 方法并执行该方法,完成插件的应用
局部指令:通过directives: { 插件名:{ 钩子函数 } } 选项来定义,只能在当前组件中使用
指令的功能:取决指令钩子的钩子函数 created,mouted,updated......
eg: 当一个 input 元素被 Vue 插入到 DOM 中后,它会被自动聚焦
全局指令: 全局指令 v-指令名使用,通过app.directive( 'cals' { })方法来定义,可以在任何组件中使用,指令的功能:取决指令钩子的钩子函数 created,mouted,updated......
使用 v-自动以指令的名字
-
//a export default{ install(app){ app.directive('focus',{ // 绑定元素的父组件 // 及他自己的所有子节点都挂载完成后调用 mounted(el, binding, vnode, prevVnode) { el.focus(); //聚焦 }, }) } }
// b export default{ install(app){ app.directive('cal',{ // 绑定元素的父组件 // 及他自己的所有子节点都挂载完成后调用 mounted(el, binding, vnode, prevVnode) { el.value = el.value * 10 + '没钱' // 加钱 }, }) } }
<template> <div class="childa"> <!-- <h1 ref="title">{ { count }}</h1> --> childa组件 <input v-focus v-calc type="text" :value="4">哎哎哎啊 <input type="text" v-sss :value="6">哎哎哎啊 <h1 ref="title">{ { count }}</h1> <button @click="handleClick">传值</button> </div> </template> <script> export default { data() { return{ p:'123', count:3 } }, methods:{ handleClick(){ this.count++, // console.log(this.$refs.title.innerHTML); this.$nextTick(()=>{ console.log(this.$refs.title.innerHTML); }) } }, 局部自定义指令 directives: { //自定义局部指令,只能在当前组将中使用 // 在模板中启用 v-focus focus:{ // 在绑定元素的父组件 // 及他自己的所有子节点都挂载完成后调用 mounted(el, binding, vnode, prevVnode) { console.log(el); el.focus() } }, sss:{ // 在绑定元素的父组件 mounted(el, binding, vnode, prevVnode) { el.value = el.value * 7+'元' } } } } </script>
-
mixins :混入
作用:抽离多个组件的公共代码(选项),避免代码冗余
使用 :混入的公共代码会出现在组件的选项位置
全局混入不建议,会造成全局污染,用下边的局部混入吧
import导入mixins路径,混入:data同级自动混入,可以直接使用mixins里的方法属性了
export default{
// 能写在这里的都是组件已知选项 watch filter directives components data
methods:{
saveIndex(i){
this.index = i
}
},
computed:{
calc(){
return 2**10
}
}
}
<footer>
<span @click="saveIndex(index)" v-for="item, index in btns" :key="index">{
{ item }}</span>
</footer>
// 导入mixins
import mixins from './mixins/index';
// 应用混入
mixins: [mixins],
methods: {
// 这个可以省略了。
// saveIndex(i) {
// this.index = i
// }
},
两个核心API:(不常用)
$forceUpdate():让组件强制更新(极端更新不了情况,自己写错了)
$nextTick():可以让特定代码在组件的 下次更新周期到 之后再执行
<button @click="handleClick">传值</button>
methods:{
handleClick(){
this.count++,
// console.log(this.$refs.title.innerHTML);
this.$nextTick(()=>{
console.log(this.$refs.title.innerHTML);
})
}
},