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。
图示文字还原:
传入配置:
new Vue({data:{name:'尚硅谷',address:'宏福科技园'}})Vue内部把配置的data拷贝,存放至实例私有属性:
vm._data = {name:'尚硅谷',address:'宏福科技园'}Vue调用
Object.defineProperty(),在vm实例上增加name、address代理属性vm.name 的getter:内部读取
vm._data.namevm.name 的setter:内部修改
vm._data.name = 新值
模板中
{{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"
✅补充细节
.self和.stop区别:.stop:阻止冒泡;不管是谁触发,阻止事件向上传递。.self:只有点击元素本身才触发;不会阻止冒泡,只是过滤触发条件。
事件传参:
@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 } }✅补充
计算属性缓存:依赖不变,多次读取get只执行1次;methods每次渲染都会执行。(面试高频)
计算属性不能做异步,get里面写异步,return拿不到结果。
简写只有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】总结
基本数据类型:可监听,新值旧值不同。
监听对象:默认只监听对象引用变化;开启
deep:true可以监听对象内部属性;新旧值是同一个对象引用。监听对象内的对象属性:开启deep,新旧值仍然同一个引用。
监听数组:
push/pop/shift/unshift/splice/sort/reverse会触发监听;直接下标修改不触发;数组修改时新值旧值指向同一个数组。
Vue2监听是基于引用对比,对象数组深度变化,newValue === oldValue。
✅补充:开启deep之后依然拿不到对象修改前旧值;不要在watch回调中修改正在监听的数据,容易死循环。
computed 和 watch 完整对比
| computed | watch | |
|---|---|---|
| 作用 | 数据推导计算 | 监听数据变化做副作用 |
| 缓存 | ✅有缓存,依赖不变不执行 | ❌无缓存,数据变化即执行 |
| 返回值 | 必须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
- v‑if
v-if="表达式" v-else-if="表达式" v-else适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除销毁。
注意:
v-if可以和v‑else‑if、v‑else一起使用,但要求结构不能被“打断”。可以搭配
<template>,template不会生成真实DOM,只能配合v‑if,不能配合v‑show。
- v‑show
v-show="表达式"适用于:切换频率较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用css
display:none隐藏掉。
备注:使用v‑if的时,元素可能无法获取到;而使用v‑show一定可以获取到DOM。
✅补充:v‑if销毁重建组件,组件生命周期created/mounted会反复执行。
10 v‑for 中 key的作用 —— diff算法
key是虚拟节点唯一标识;diff对比新旧虚拟DOM的key。
key相同:对比节点内容;内容复用就复用真实DOM;内容变了就更新DOM。
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对比:
key=004 在旧虚拟DOM找不到 →新建li节点渲染老刘
key=001、002、003在旧虚拟DOM找到完全一样key →直接复用原来完整DOM节点(包括input输入框),只移动DOM位置。
✅没有错乱,DOM复用效率最高。
【3】总结面试题
react、vue中的key有什么作用?
key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行新虚拟DOM与旧虚拟DOM的差异比较。
对比规则:
旧虚拟DOM中找到了与新虚拟DOM相同key:
虚拟DOM内容没变,直接复用之前真实DOM。
虚拟DOM内容变了,则生成新的真实DOM,替换页面旧DOM。
旧虚拟DOM未找到相同key:创建新真实DOM渲染页面。
用index作为key潜在问题:
逆序增删,产生不必要DOM更新,效率低。
如果包含输入类DOM,会出现界面渲染错误。
开发如何选key:
✅优先用每条数据的唯一标识id、手机号等。
如果只是简单展示列表,不做逆序增删删除,用index没问题。
✅补充坑点
不要把key写成Math.random(),每次渲染key全部变化,完全失去diff复用效果。
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。
vue会监视data中所有层次的数据。
监测对象数据:
通过setter实现监视;new Vue时传入的对象才会被劫持。
对象后期追加属性,默认不是响应式。
需要新增响应式属性:
Vue.set(target, propName, value)/vm.$set(target, propName, value)
监测数组数据:
Vue重写数组7个变更方法:push pop shift unshift splice sort reverse调用原生数组方法修改数据
重新解析模板更新页面。
❗注意:
通过数组下标直接修改元素,不会触发响应式更新!
Vue.set()/vm.$set()不能给vm、vm根data对象添加属性。
✅补充扩展面试点
非变异数组方法:
filter map reduce返回新数组,需要手动覆盖原数组:this.list = this.list.filter(...)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收集
valueradio:必须配置
value,v‑model收集valuecheckbox:
没有value:收集checked布尔 true/false
有value:data初始是数组,则收集一组value;非数组收集布尔checked
v‑model三个修饰符:
.lazy:失去焦点再收集数据.number:输入字符串转为有效的数字.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}}⚠️注意:
Vue3已经彻底移除过滤器API;
局部过滤器优先级高于同名全局过滤器;
过滤器只做页面展示格式化,复杂业务逻辑写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)总结
局部指令:
directives:{指令名:回调/配置对象}全局指令:
Vue.directive(指令名,配置对象/回调)钩子:
bind:指令绑定到元素
inserted:元素插入页面
update:模板更新
使用指令加
v‑,定义不加。多单词指令使用kebab‑case短横线命名。
16 生命周期函数
生命周期钩子:Vue实例在不同阶段自动执行的回调函数,this指向vm实例。
| 钩子 | 执行时机 | 常用操作 |
|---|---|---|
beforeCreate | 数据代理、数据监测创建之前 | 几乎不用,拿不到data/methods |
created | 数据代理监测创建完毕 | 最早访问data/methods;发起初始ajax请求 |
beforeMount | 虚拟DOM生成,页面挂载之前 | 页面还是原始模板 |
mounted | DOM挂载完成,页面渲染完毕 | 操作DOM、开启定时器、订阅消息 |
beforeUpdate | 数据更新,页面重新渲染之前 | 数据是新,页面DOM旧 |
updated | 数据更新,DOM渲染完成 | 数据页面同步 |
beforeDestroy | 实例销毁之前 | 清除定时器、解绑事件、取消订阅、解绑$bus事件 |
destroyed | 实例完全销毁 | 组件全部销毁,响应式失效 |
✅重点考点
created拿不到真实DOM,$el还不存在,不要操作DOM。beforeDestroy一定要清理定时器、事件、$bus监听,否则内存泄漏。
17 VueComponent()构造函数
组件本质是
VueComponent构造函数,由Vue.extend生成。写
<school/>标签,Vue自动执行new VueComponent(options)。每一次
Vue.extend()返回全新VueComponent构造函数,组件之间构造函数相互独立。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>合并规则:
data/methods:组件自身优先级高于混入。同名方法,组件覆盖混入。
生命周期钩子:混入钩子和组件钩子全部执行,混入先执行,组件后执行。
③ 全局混入 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:模板绑定
<!--父组件--> <Student @atguigu="getStudentName"/><!--子组件--> <button @click="send">提交</button> <script> export default { methods:{ send(){ this.$emit('atguigu','子组件数据') } } } </script>- 写法2:ref + $on(mounted中动态绑定,灵活)
<!--父组件--> <Student ref="student"/>mounted() { this.$refs.student.$on('atguigu',this.getStudentName) }【3】任意组件通信 全局事件总线 $bus
原理:让所有组件可以访问同一个事件中心对象,o n 监听事件, on监听事件,on监听事件,emit触发事件。
- main.js安装事件总线
new Vue({ el:'#app', render: h => h(App), beforeCreate() { Vue.prototype.$bus = this //安装全局事件总线 }, })- 接收组件,绑定监听 $on
mounted(){ this.$bus.$on('hello',(data)=>{ console.log('收到数据',data) }) }, beforeDestroy(){ this.$bus.$off('hello') //销毁组件解绑事件,防止内存泄漏!! }- 发送组件触发事件 $emit
methods:{ send(){ this.$bus.$emit('hello',666) } }✅Vue2全套组件通信补充(原笔记缺失)
父传子:
props子传父:自定义事件
$emit;ref直接调用子组件方法(不推荐)兄弟、任意组件:事件总线
$bus;Vuex状态管理隔代祖后代:
$attrs/$listeners;provide / inject插槽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。