news 2026/10/2 7:18:25

Vue 2 完整学习笔记

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue 2 完整学习笔记

Vue 2 完整学习笔记

1 Vue.config.productionTip = false

阻止vue启动时生成生产提示。

2 容器与vue实例的关系

容器与Vue实例需要一一对应

【1】多个容器对应一个vue实例,只对第一个有效

<!-- 准备好一个容器 --> <div class="root"> <h1>Hello, {{name}} 1</h1> </div> <div class="root"> <h1>Hello, {{name}} 2</h1> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 //创建Vue实例 new Vue({ el:'.root', //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。 data:{ //data中用于存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象。 name:'尚硅谷' } }) </script>

渲染结果:第二个容器不会被Vue解析,直接显示{{name}}原始字符串。

【2】多个vue实例对应一个容器 报错

<!-- 准备好一个容器 --> <div id="root"> <h1>Hello, {{name}}, {{address}}</h1> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 //创建Vue实例 new Vue({ el:'#root', data:{ name:'尚硅谷' } }) new Vue({ el:'#root', data:{ address:'北京昌平' } }) </script>

⚠️注意:一个DOM容器只能交给一个Vue实例接管。

✅补充el两种写法

//写法1:配置el属性 new Vue({el:'#root'}) //写法2:$mount,更加灵活,后期手动挂载 const vm = new Vue({data:{}}) vm.$mount('#root')

容器内可以写原生html,也可以写vue插值语法;容器的outerHTML会被Vue模板替换。

3 数据代理

通过vm对象代理_data对象的数据,对vm读写属性,本质读写的是_data。

图示文字还原:

  1. 传入配置:new Vue({data:{name:'尚硅谷',address:'宏福科技园'}})

  2. Vue内部把配置的data拷贝,存放至实例私有属性:vm._data = {name:'尚硅谷',address:'宏福科技园'}

  3. Vue调用Object.defineProperty(),在vm实例上增加name、address代理属性

    • vm.name 的getter:内部读取vm._data.name

    • vm.name 的setter:内部修改vm._data.name = 新值

  4. 模板中{{name}}访问vm.name,经过代理,最终读写_data内真实数据。

  • Vue中的数据代理:通过vm对象来代理**data(_data)**对象中属性的操作(读/写)

  • 好处:更加方便的操作data中的数据

  • 基本原理:
    通过Object.defineProperty()把data对象中所有属性添加到vm上。
    为每一个添加到vm上的属性,都指定一个getter/setter。
    在getter/setter内部去操作(读/写)data中对应的属性。

const vm = new Vue({ el:'#root', data:{ name:'尚硅谷', address:'宏福科技园' } })

vm._data 就是配置的data,vm上的name/address是代理属性。

⚠️易错:Object.defineProperty是ES5 API,无法监听对象新增属性,不能监听数组下标修改,这也是Vue2响应式痛点根源。

4 事件修饰符

<!-- Vue中的事件修饰符: 1.prevent:阻止默认事件(常用); 2.stop:阻止事件冒泡(常用); 3.once:事件只触发一次(常用); 4.capture:使用事件的捕获模式; 5.self:只有event.target是当前操作的元素时才触发事件; 6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕; --> <div id="root"> <h2>欢迎来到{{name}}学习</h2> <!-- 阻止默认事件(常用) --> <a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a> <!-- 阻止事件冒泡(常用) --> <div class="demo1" @click="showInfo"> <button @click.stop="showInfo">点我提示信息</button> </div> </div>

修饰符可以链式写法:@click.stop.prevent="xxx"

✅补充细节

  1. .self和.stop区别:

    • .stop:阻止冒泡;不管是谁触发,阻止事件向上传递。

    • .self:只有点击元素本身才触发;不会阻止冒泡,只是过滤触发条件。

  2. 事件传参:@click="fn($event,arg1)",$event代表原生事件对象。

5 计算属性computed

基于依赖的响应式数据进行计算,自带缓存,依赖不变,多次读取不会重复执行get。

computed:{ fullName:{ //get什么时候调用? //1. 当有人读取fullName时,get就会被调用,且返回值就作为fullName的值 //2. 所依赖的数据发生变化时。 get(){ console.log('get被调用了') return this.firstName + '-' + this.lastName }, //set什么时候调用?当fullName被修改时。 set(value){ console.log('set',value) const arr = value.split('-') this.firstName = arr[0] this.lastName = arr[1] } } }

简写(只需要get,不需要set)

computed:{ fullName(){ return this.firstName + '-' + this.lastName } }

✅补充

  1. 计算属性缓存:依赖不变,多次读取get只执行1次;methods每次渲染都会执行。(面试高频)

  2. 计算属性不能做异步,get里面写异步,return拿不到结果。

  3. 简写只有get没有set,直接修改计算属性控制台抛出警告。

6 侦听属性 watch

监听响应式数据变化,执行回调,适合异步、复杂逻辑。

写法一:配置项内写watch

const vm = new Vue({ el:'#root', data:{ isHot:true, }, watch:{ isHot:{ immediate:true, //初始化时让handler调用一下 //handler什么时候调用?当isHot发生改变时。 handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } } } })

写法二:vm.$watch (代码运行过程中动态添加监听)

vm.$watch('isHot',{ immediate:true, handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } })

【1】深度监视

data:{ isHot:true, numbers:{ a:1, b:1 } }, watch:{ //监视多级结构中某个属性的变化(推荐字符串写法,性能更好) 'numbers.a':{ handler(){ console.log('a被改变了') } }, //监视多级结构中所有属性的变化 numbers:{ deep:true, //开启深度监听 handler(){ console.log('numbers改变了') } } }

【2】简写形式

简写不能写immediate、deep;函数不能写成箭头函数,否则this指向有问题

watch:{ isHot(newVal,oldVal){ console.log(newVal,oldVal) } } //或者 vm.$watch('isHot',function(newVal,oldVal){})

【3】总结

  1. 基本数据类型:可监听,新值旧值不同。

  2. 监听对象:默认只监听对象引用变化;开启deep:true可以监听对象内部属性;新旧值是同一个对象引用。

  3. 监听对象内的对象属性:开启deep,新旧值仍然同一个引用。

  4. 监听数组:push/pop/shift/unshift/splice/sort/reverse会触发监听;直接下标修改不触发;数组修改时新值旧值指向同一个数组。

Vue2监听是基于引用对比,对象数组深度变化,newValue === oldValue。

✅补充:开启deep之后依然拿不到对象修改前旧值;不要在watch回调中修改正在监听的数据,容易死循环。

computed 和 watch 完整对比

computedwatch
作用数据推导计算监听数据变化做副作用
缓存✅有缓存,依赖不变不执行❌无缓存,数据变化即执行
返回值必须return结果不需要return
异步❌不支持异步✅适合异步请求、定时器
使用场景根据现有数据算出新数据数据变化后执行复杂业务逻辑

7 watch 和 computed 的区别

见上面表格。

8 样式绑定

【1】class样式

(1)字符串写法

适用于样式类名不确定,需要动态指定

<div class="basic" :class="mood" @click="changeMood">{{name}}</div>
data:{ name:'尚硅谷', mood:'normal' }, methods: { changeMood(){ this.mood = 'happy' } }
(2)数组写法

适用于绑定的样式的个数不确定,样式名字也不确定

<div class="basic" :class="classArr">{{name}}</div>
data:{ classArr:['atguigu1','atguigu2','atguigu3'] }
(3)对象写法

适用于样式个数和名字都确定的情况下,但动态决定用不用

<div class="basic" :class="classObj">{{name}}</div>
data:{ classObj:{ atguigu1:false, atguigu2:false, } }

【2】style样式

(1)对象写法

属性名使用小驼峰fontSize,不要font-size

<div class="basic" :style="styleObj">{{name}}</div>
data:{ styleObj:{ fontSize: '40px', color:'red', } }
(2)数组写法
<div class="basic" :style="styleArr">{{name}}</div>
data:{ styleArr:[ {fontSize: '40px',color:'blue'}, {backgroundColor:'orange'} ] }

⚠️坑:class动态绑定会和原生class自动合并;style属性必须小驼峰。

9 条件渲染 v-if 和 v-show

  1. v‑if
v-if="表达式" v-else-if="表达式" v-else
  • 适用于:切换频率较低的场景。

  • 特点:不展示的DOM元素直接被移除销毁。

  • 注意:v-if可以和v‑else‑if、v‑else一起使用,但要求结构不能被“打断”。

  • 可以搭配<template>,template不会生成真实DOM,只能配合v‑if,不能配合v‑show。

  1. v‑show
v-show="表达式"
  • 适用于:切换频率较高的场景。

  • 特点:不展示的DOM元素未被移除,仅仅是使用cssdisplay:none隐藏掉。

备注:使用v‑if的时,元素可能无法获取到;而使用v‑show一定可以获取到DOM。

✅补充:v‑if销毁重建组件,组件生命周期created/mounted会反复执行。

10 v‑for 中 key的作用 —— diff算法

key是虚拟节点唯一标识;diff对比新旧虚拟DOM的key。

  1. key相同:对比节点内容;内容复用就复用真实DOM;内容变了就更新DOM。

  2. key不同:直接新建真实DOM。

【1】当用index作为key(图示文字还原)

场景:初始数组[{id:'001',name:'张三'},{id:'002',name:'李四'},{id:'003',name:'王五'}],数组头部插入一条{id:'004',name:'老刘'},key绑定数组index。

旧虚拟DOM:

<li key=0>张三‑18 <input></li> <li key=1>李四‑19 <input></li> <li key=2>王五‑20 <input></li>

新虚拟DOM头部新增老刘:

<li key=0>老刘‑30 <input></li> <li key=1>张三‑18 <input></li> <li key=2>李四‑19 <input></li> <li key=3>王五‑20 <input></li>

diff对比:key是index,新旧key一一匹配。
key=0旧节点是张三,新key=0是老刘 → 修改文本,input输入框DOM直接复用旧的input,里面的残留文本不会清空。

现象:文本全部错位更新,输入框里面内容错乱;DOM做大量不必要修改,性能差。

【2】用唯一标识作为key(id,图示文字还原)

同样头部插入老刘,key绑定唯一id:
旧虚拟DOM key:001 / 002 /003
新虚拟DOM key:004 / 001 /002 /003

diff对比:

  1. key=004 在旧虚拟DOM找不到 →新建li节点渲染老刘

  2. key=001、002、003在旧虚拟DOM找到完全一样key →直接复用原来完整DOM节点(包括input输入框),只移动DOM位置。
    ✅没有错乱,DOM复用效率最高。

【3】总结面试题

react、vue中的key有什么作用?

  1. key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行新虚拟DOM与旧虚拟DOM的差异比较。

  2. 对比规则:

    1. 旧虚拟DOM中找到了与新虚拟DOM相同key:

      • 虚拟DOM内容没变,直接复用之前真实DOM。

      • 虚拟DOM内容变了,则生成新的真实DOM,替换页面旧DOM。

    2. 旧虚拟DOM未找到相同key:创建新真实DOM渲染页面。

  3. 用index作为key潜在问题:

    • 逆序增删,产生不必要DOM更新,效率低。

    • 如果包含输入类DOM,会出现界面渲染错误。

  4. 开发如何选key:

    • ✅优先用每条数据的唯一标识id、手机号等。

    • 如果只是简单展示列表,不做逆序增删删除,用index没问题。

✅补充坑点

  1. 不要把key写成Math.random(),每次渲染key全部变化,完全失去diff复用效果。

  2. v‑for优先级高于v‑if,禁止v‑for和v‑if写同一标签上;过滤数组优先使用computed。

<!-- ❌不推荐 --> <li v‑for="item in list" v‑if="item.show"></li>

11 数据监测(数据劫持)

Vue监视vm._data中数据变化,驱动页面更新。底层Observer+Object.defineProperty。

  1. vue会监视data中所有层次的数据。

  2. 监测对象数据:

    • 通过setter实现监视;new Vue时传入的对象才会被劫持。

    • 对象后期追加属性,默认不是响应式。

    • 需要新增响应式属性:Vue.set(target, propName, value)/vm.$set(target, propName, value)

  3. 监测数组数据:
    Vue重写数组7个变更方法:push pop shift unshift splice sort reverse

    • 调用原生数组方法修改数据

    • 重新解析模板更新页面。

❗注意:

  1. 通过数组下标直接修改元素,不会触发响应式更新!

  2. Vue.set()/vm.$set()不能给vm、vm根data对象添加属性。

✅补充扩展面试点

  1. 非变异数组方法:filter map reduce返回新数组,需要手动覆盖原数组:this.list = this.list.filter(...)

  2. Object.defineProperty只能劫持已存在属性,是Vue2响应式底层缺陷。

12 v‑model收集表单数据

v‑model本质语法糖:value+@input;默认收集value属性。

<div id="root"> <form @submit.prevent="demo"> 账号:<input type="text" v-model="userInfo.account"> <br/> 密码:<input type="password" v-model="userInfo.password"> <br/> 年龄:<input type="number" v-model.number="userInfo.age"> <br/> 性别: 男<input type="radio" name="sex" v-model="userInfo.sex" value="male"> 女<input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/> 爱好: 学习<input type="checkbox" v-model="userInfo.hobby" value="study"> 打游戏<input type="checkbox" v-model="userInfo.hobby" value="game"> 吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat"> <br/> 所属校区 <select v-model="userInfo.city"> <option value="">请选择校区</option> <option value="beijing">北京</option> <option value="shanghai">上海</option> </select> <br/> 其他信息: <textarea v-model="userInfo.other"></textarea> <br/> <input type="checkbox" v-model="userInfo.agree">阅读并接受协议 <button>提交</button> </form> </div>
  • text / password / textarea / select:v‑model收集value

  • radio:必须配置value,v‑model收集value

  • checkbox:

    • 没有value:收集checked布尔 true/false

    • 有value:data初始是数组,则收集一组value;非数组收集布尔checked

v‑model三个修饰符:

  1. .lazy:失去焦点再收集数据

  2. .number:输入字符串转为有效的数字

  3. .trim:输入首尾空格过滤

✅补充原理

<input v‑model="msg"> <!--等价于下面代码--> <input :value="msg" @input="msg=$event.target.value">

自定义组件实现v‑model:子组件接收props:[‘value’],内部this.$emit('input',新值)
checkbox多选场景,data初始必须是空数组hobby:[],否则只会收集布尔值。

13 第三方库网站 BootCDN

可以引入CDN资源;dayjs用于处理日期时间。

14 过滤器 |

过滤器:对模板显示的数据做格式化处理,只在插值语法和v‑bind使用,不能用于v‑model,不会修改原数据。

【1】局部过滤器

<h3>现在是:{{time | timeFormatter}}</h3>
new Vue({ el:'#root', data:{ time:1621561377603 //时间戳 }, filters:{ timeFormatter(value){ return dayjs(value).format('YYYY年MM月DD日 HH:mm:ss') } } })

【2】全局过滤器

Vue.filter('mySlice',function(value){ return value.slice(0,4) })

传参过滤器:第一个参数永远是待过滤数据,后面是传入参数

<h3>现在是:{{time | timeFormatter('YYYY_MM_DD')}}</h3>
filters:{ timeFormatter(value,str='YYYY年MM月DD日 HH:mm:ss'){ return dayjs(value).format(str) } }

多个过滤器链式调用,前一个输出作为后一个输入:

{{time | timeFormatter('YYYY_MM_DD') | mySlice}}

⚠️注意:

  1. Vue3已经彻底移除过滤器API;

  2. 局部过滤器优先级高于同名全局过滤器;

  3. 过滤器只做页面展示格式化,复杂业务逻辑写methods/computed。

15 v‑指令

【1】v‑bind

单向绑定解析表达式,简写:,解析js表达式。

【2】v‑model

双向数据绑定,只用于表单元素。

【3】v‑on

绑定事件,简写@。

【4】v‑text

更新元素文本,会覆盖全部内容,不解析html。

【5】v‑html

解析html字符串,有XSS安全风险,禁止渲染用户输入内容。

【6】v‑cloak

解决网速慢,页面闪现{{xxx}}原始插值。

  • Vue实例接管容器之后,自动删除v‑cloak属性。
[v-cloak]{ display:none; }
<h2 v-cloak>{{name}}</h2>

生效前提:vue是外部js引入;内联script模板v‑cloak无效。

【7】v‑once

只渲染一次;之后数据变化不再更新DOM,做静态内容优化。

<h2 v-once>初始化n值:{{n}}</h2>

【8】v‑pre

跳过该节点编译;节点没有vue语法,用来提升编译速度。

<h2 v-pre>Vue其实很简单</h2>

【9】自定义指令

使用时写v‑xxx,定义指令名不加v‑;命名多个单词使用短横线命名v‑big‑num。

(1)函数式写法

触发时机:①指令初次绑定元素;②模板重新解析更新。

<span v-big="n"></span>
directives:{ big(element,binding){ //element真实DOM,binding指令信息对象,binding.value是绑定的值 element.innerText = binding.value * 10 } }
(2)对象式写法(完整钩子)
directives:{ fbind:{ //指令与元素成功绑定(此时元素还没有插入页面) bind(element,binding){ element.value = binding.value }, //指令所在元素插入页面DOM树 inserted(element,binding){ element.focus() //获取焦点这类DOM操作写这里 }, //模板更新时 update(element,binding){ element.value = binding.value } } }

函数式等价于 bind + update;钩子函数内部this指向window,不要用this访问组件data。

(3)全局指令
Vue.directive('fbind',{ bind(){}, inserted(){}, update(){} })
(4)总结
  1. 局部指令:directives:{指令名:回调/配置对象}

  2. 全局指令:Vue.directive(指令名,配置对象/回调)

  3. 钩子:

    • bind:指令绑定到元素

    • inserted:元素插入页面

    • update:模板更新

  4. 使用指令加v‑,定义不加。多单词指令使用kebab‑case短横线命名。

16 生命周期函数

生命周期钩子:Vue实例在不同阶段自动执行的回调函数,this指向vm实例。

钩子执行时机常用操作
beforeCreate数据代理、数据监测创建之前几乎不用,拿不到data/methods
created数据代理监测创建完毕最早访问data/methods;发起初始ajax请求
beforeMount虚拟DOM生成,页面挂载之前页面还是原始模板
mountedDOM挂载完成,页面渲染完毕操作DOM、开启定时器、订阅消息
beforeUpdate数据更新,页面重新渲染之前数据是新,页面DOM旧
updated数据更新,DOM渲染完成数据页面同步
beforeDestroy实例销毁之前清除定时器、解绑事件、取消订阅、解绑$bus事件
destroyed实例完全销毁组件全部销毁,响应式失效

✅重点考点

  1. created拿不到真实DOM,$el还不存在,不要操作DOM。

  2. beforeDestroy一定要清理定时器、事件、$bus监听,否则内存泄漏。

17 VueComponent()构造函数

  1. 组件本质是VueComponent构造函数,由Vue.extend生成。

  2. 写<school/>标签,Vue自动执行new VueComponent(options)。

  3. 每一次Vue.extend()返回全新VueComponent构造函数,组件之间构造函数相互独立。

  4. this指向:

    • 组件配置中methods/computed/watch函数:this是组件实例vc

    • new Vue配置中:this是Vue实例vm。

18 Vue构造函数与VueComponent构造函数之间的关系

原型链文字还原图示

VueComponent.prototype.__proto__ = Vue.prototype
  • 目的:所有组件实例vc,可以直接访问Vue原型上挂载的方法,例如$on $emit $bus $refs。

  • 不是让VueComponent继承Vue类;只是修改组件构造函数的隐式原型指向Vue.prototype。

19 脚手架

【1】安装

npm i -g @vue/cli

【2】创建项目

vue create project-name cd project-name npm run serve

【3】render()函数

  • vue.js完整版:包含核心 +模板解析器,支持template配置。

  • vue.runtime.xxx.js运行版(脚手架默认引入):没有模板解析器,不能写template配置,必须用render。

main.js

new Vue({ el:'#app', render: h => h(App), //h即createElement函数,创建虚拟VNode })

.vue单文件中的template不是vue解析,是webpack的vue‑loader编译处理。

20 ref属性

  • ref作用:给DOM元素 / 组件打标识,存到this.$refs对象。

  • ref写普通标签:拿到真实DOM。

  • ref写组件标签:拿到组件实例vc。

<template> <div> <h1 v-text="msg" ref="title"></h1> <button ref="btn" @click="showDOM">点我输出DOM</button> <School ref="sch"/> </div> </template> <script> import School from './components/School' export default { name:'App', components:{School}, data() { return { msg:'欢迎学习Vue!' } }, methods: { showDOM(){ console.log(this.$refs.title) //dom元素 console.log(this.$refs.sch) //组件实例vc } }, } </script>

⚠️坑:ref写在v‑for循环上,this.$refs.xxx得到DOM数组。

id是原生DOM属性;ref是vue提供,优先使用ref获取组件实例。

21 mixin混入

把多个组件共用配置抽离为混入对象,复用data、methods、生命周期钩子。

① 创建mixin.js

export const hunhe = { methods: { showName(){ alert(this.name) } }, mounted() { console.log('你好啊!') }, } export const hunhe2 = { data(){ return { x:100, y:200 } } }

② 局部混入

组件中导入,mixins配置数组

<script> import {hunhe,hunhe2} from '../mixin' export default { name:'School', data() { return { name:'尚硅谷',address:'北京' } }, mixins:[hunhe,hunhe2] } </script>

合并规则:

  1. data/methods:组件自身优先级高于混入。同名方法,组件覆盖混入。

  2. 生命周期钩子:混入钩子和组件钩子全部执行,混入先执行,组件后执行。

③ 全局混入 main.js

import {hunhe,hunhe2} from './mixin' Vue.mixin(hunhe) Vue.mixin(hunhe2)

全局混入所有组件、vm实例都会生效,谨慎使用,容易污染全局。

22 plugins插件

插件用来增强Vue;本质对象,必须包含install(Vue,options)方法。

定义插件 plugins.js

export default { install(Vue){ //1. 全局过滤器 Vue.filter('mySlice',function(value){}) //2.全局指令 Vue.directive('fbind',{}) //3.全局混入 Vue.mixin({}) //4.给Vue原型添加实例方法 Vue.prototype.hello = ()=>{alert('你好')} } }

使用插件 main.js

import plugins from './plugins' Vue.use(plugins) //Vue.use(插件,参数1,参数2) 可传参数给install第二个参数

Vue.use()会做去重,同一个插件多次use只会执行一次install。

23 组件间的传值(通信)

【1】父传子 props

props是只读,子组件不要直接修改props;需要修改,复制一份到自己data。
⚠️对象、数组类型的default必须写成函数返回。

三种接收写法

//1.简单数组声明接收 props:['name','age','sex'] //2.接收+类型限制 props:{ name:String, age:Number, sex:String } //3.类型+必填+默认值(最完整) props:{ name:{ type:String, required:true, //是否必填 }, age:{ type:Number, default:99 //默认值 } }

【2】子传父:自定义事件

原理:父组件给子绑定自定义事件,子组件内部this.$emit('事件名',数据)触发。

  1. 写法1:模板绑定
<!--父组件--> <Student @atguigu="getStudentName"/>
<!--子组件--> <button @click="send">提交</button> <script> export default { methods:{ send(){ this.$emit('atguigu','子组件数据') } } } </script>
  1. 写法2:ref + $on(mounted中动态绑定,灵活)
<!--父组件--> <Student ref="student"/>
mounted() { this.$refs.student.$on('atguigu',this.getStudentName) }

【3】任意组件通信 全局事件总线 $bus

原理:让所有组件可以访问同一个事件中心对象,o n 监听事件, on监听事件,on监听事件,emit触发事件。

  1. main.js安装事件总线
new Vue({ el:'#app', render: h => h(App), beforeCreate() { Vue.prototype.$bus = this //安装全局事件总线 }, })
  1. 接收组件,绑定监听 $on
mounted(){ this.$bus.$on('hello',(data)=>{ console.log('收到数据',data) }) }, beforeDestroy(){ this.$bus.$off('hello') //销毁组件解绑事件,防止内存泄漏!! }
  1. 发送组件触发事件 $emit
methods:{ send(){ this.$bus.$emit('hello',666) } }

✅Vue2全套组件通信补充(原笔记缺失)

  1. 父传子:props

  2. 子传父:自定义事件$emit;ref直接调用子组件方法(不推荐)

  3. 兄弟、任意组件:事件总线$bus;Vuex状态管理

  4. 隔代祖后代:$attrs/$listeners;provide / inject

  5. 插槽slot:父向子传递HTML结构;作用域插槽实现子数据,父定义渲染。

【4】插槽

插槽:父组件向子组件传入HTML结构。

(1)默认插槽

子组件定义坑位<slot>默认内容</slot>,父组件标签中间写内容。
子组件Category.vue

<template> <div class="category"> <h3>{{title}}分类</h3> <!--插槽,没有传入内容则渲染默认值--> <slot>我是默认值</slot> </div> </template> <script> export default { props:['title'] } </script>

父组件App.vue

<Category title="美食"> <img src="xxx"/> </Category>
(2)具名插槽:多个插槽,指定名字

子组件

<slot name="center"></slot> <slot name="footer"></slot>

父组件使用:

<Category title="游戏"> <ul slot="center"> <li v-for="g in games">{{g}}</li> </ul> <template v-slot:footer> <div class="foot">内容</div> </template> </Category>

v‑slot:xxx只能写在<template>标签上;简写语法#footer。

(3)作用域插槽

数据在子组件,渲染结构由父组件决定。子组件slot绑定数据,父template接收scope对象。

子组件:

<slot :games="games" msg="hello"></slot>

父组件:

<Category title="游戏"> <template v-slot="atguigu"> <ul> <li v-for="g in atguigu.games">{{g}}</li> </ul> </template> </Category>

Vue2.6+推荐v‑slot="obj",废弃旧语法scope="";默认插槽等价#default。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 7:17:03

wifit3 Driver ABC抽象基类:扩展新芯片必须实现的6个方法

wifit3 Driver ABC抽象基类&#xff1a;扩展新芯片必须实现的6个方法 【免费下载链接】wifit3 Wifite but USB-only & cross-platform. 项目地址: https://gitcode.com/GitHub_Trending/wi/wifit3 wifit3 是一款仅依赖 USB 无线网卡、跨平台的 Wi-Fi 审计工具。它的…

作者头像 李华
网站建设 2026/10/2 7:17:02

Unity接入DeepSeek流式传输实战:从API调用到逐字输出

做 Unity 项目的朋友&#xff0c;最近应该都在聊怎么把大模型塞进游戏里。我折腾了一段时间&#xff0c;把 DeepSeek 的 API 接进了 Unity 工程&#xff0c;并且实现了数据流式传输——就是那种 AI 对话不是干等好几秒然后一次性蹦出来&#xff0c;而是像 ChatGPT 网页版一样逐…

作者头像 李华
网站建设 2026/10/2 7:16:08

多无人机TDOA/FDOA无源定位与EKF跟踪的MATLAB实现

做定位的同学应该都有体会&#xff0c;TDOA&#xff08;到达时间差&#xff09;和FDOA&#xff08;到达频率差&#xff09;这两个词听着很熟&#xff0c;但真正要把两者揉进一个滤波器里&#xff0c;让无人机编队一边飞一边把辐射源的位置和速度持续估计出来&#xff0c;总是会…

作者头像 李华
网站建设 2026/10/2 7:15:51

从零搭建AI工程体系:数据、训练、评估、服务与观测五层实战

1. 从零搭建AI工程能力&#xff0c;到底在搭什么很多人第一次看到“ai-engineering-from-scratch”这个标题&#xff0c;脑子里冒出来的第一个念头是&#xff1a;是不是又要手撸一个Transformer&#xff1f;是不是得先把反向传播推一遍&#xff1f;我一开始也这么想&#xff0c…

作者头像 李华