Vue的nextTick、bus通信、插槽
nextTick
uodate => 只要视图更新都触发
为了单独针对某个数据变化导致的视图更新写单独的逻辑,应该用nextTick
vue的视图更新是异步的,数据变化不会马上导致视图更新
nextTick(回调函数),回调函数在视图更新后触发
触底
const App = {
template: `
<div>
<button @click='fn'>修改str</button>
<ul ref='ul'>
<li v-for='d in count'>{{d}}</li>
</ul>
</div>
`,
data() {
return {
count: 0
}
},
methods: {
fn() {
// 数据变化会导致视图变化.视图变化完成之后,就会触发nextTick
this.count++;
// 这里只会在count变化导致的视图更新后触发.
this.$nextTick(() => {
let oUl = this.$refs.ul;
oUl.scrollTop = oUl.scrollHeight - oUl.clientHeight;
});
}
}
// 每次视图更新后,都设置触底.
// updated() {
// let oUl = this.$refs.ul;
// oUl.scrollTop = oUl.scrollHeight - oUl.clientHeight;
// }
}
const vm = new Vue({
el: '#app',
components: { App },
template: '<App />'
})
render
代替template选项
// template => 字符串视图 => 优点是直观.缺点是需要编译.
// render => 跳过编译字符串template,直接生成虚拟节点 => 优点是性能好,不用编译,确定是模板不直观
直接render函数虚拟节点
createElement(标签名,属性列表,子节点列表)
createElement(组件配置项)
render(createElement) {
return createElement('div', { attrs: { id: 'app' } }, [
createElement('h3', null, 'App组件')
]);
}
new Vue({
render: h => h(App),
}).$mount('#app');
bus组件通信
我们在比较复杂布局情况下,可以通过一个空的vue实例来充当中间组件
给这个中间组件绑定自定义事件,通过这个中间组件触发自定义事件
这个中间组件就充当 了任意两个组件的数据运输工具
vm.$on
=> 添加一个自定义事件
vm.$emit
=> 触发一个自定义事件
vm.off
=> 解绑自定义事件
在created里给bus实例添加自定义事件
const bus = new Vue();
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='fn'>传递count给item</button>
</div>
`,
data() {
return { count: 666 }
},
methods: {
fn() {
bus.$emit('getcount', this.count);
}
}
}
const item = {
template: `
<div>
<h3>item组件---{{num}}</h3>
</div>
`,
data() {
return { num: 0 }
},
methods: {
getCount(count) {
this.num = count
}
},
created() {
bus.$on('getcount', this.getCount);
}
}
const App = {
template: `
<div id='app'>
<h3>App组件</h3>
<box />
<item />
</div>
`,
components: { box, item }
}
new Vue({
render: h => h(App),
}).$mount('#app');
父子组件生命周期
// 父beforeCreate
// 父created
// 父beforeMount
// 子beforeCreate
// 子created
// 子beforeMount
// 子mounted
// 父mounted
面试题:
父子组件的生命周期钩子顺序.
为什么顺序是这样的. => 函数执行顺序回答.父组件的视图编译就是一个函数从上往下执行.
能不能修改这个顺序.(4父4子) => 通过v-if实现.父组件挂载后再编译子组件.即在mouted里面让flag变为true
插槽
默认插槽
- 组件挂载处要写双标签
- 双标签里面的写要插入的标签
- 组件里面需要插入标签的地方写上
<slot />
const box = {
template: `
<div>
<h3>{{title}}</h3>
<p>大家都好</p>
<slot />
</div>
`,
props: ['title']
}
const App = {
template: `
<div>
<box title='你好'>
<button>按钮111</button>
<button>按钮222</button>
</box>
<box title='他好'>
<span>span</span>
</box>
<box title='我好'>
<a href='#'>百度一下</a>
</box>
</div>
`,
components: { box }
}
具名插槽
- 组件挂载处要写双标签
- 双标签里面写要插入的标签,并在里面添加slot属性,值为组件内需要插入位置的slot里name的值
- 组件里slot 须写上
name='里面写名字'
const box = {
template: `
<div>
<slot name='a' />
<h3>{{title}}</h3>
<slot name='span' />
<slot name='btn2' />
<p>大家都好</p>
<slot name='btn1' />
</div>
`,
props: ['title']
}
const App = {
template: `
<div>
<box title='你好'>
<button slot='btn1'>按钮1111</button>
<button slot='btn2'>按钮2222</button>
</box>
<box title='他好'>
<template slot='span'>
<span>span</span>
<span>span</span>
</template>
</box>
<box title='我好'>
<a href='#' slot='a'>百度一下</a>
</box>
</div>
`,
components: { box }
}
作用域插槽
当插槽内容包含子组件数据时,就得用作用域插槽
因为当,作用域内有子组件数据,但是默认却在父组件上找数据
可以通过v-slot指令,这样组件内部就构成了一个作用域
而插槽内部的数据,会优先在作用域内查找
v-slot的值,默认是一个对象,这个对象的名字可以任意书写,这个对象能获取到slot组件上所有绑定的数据
- 组件里的slot 需要绑定数据,如
<slot :name='name' a='100' />
- 组件挂载处双标签
- 并有属性
v-slot='slotProps'
,slotProps任意名字,是一个对象,对象里面有绑定的所有数据(此时获得的是默认插槽绑定的数据)
const box1 = {
template: `
<div>
<table border='1' cellspacing='10' cellpadding='10'>
<tr v-for='({name,age,sex,nation}) in arr'>
<td>{{name}}</td>
<td>{{age}}</td>
<td>{{sex}}</td>
<slot :nation='nation' a='100' c='200' />
</tr>
</table>
</div>
`,
data() {
return {
arr: [
{
name: '幂幂',
age: 32,
sex: '女',
nation: '汉'
}, {
name: '超越',
age: 23,
sex: '女',
nation: '苗'
}, {
name: '德刚',
age: 65,
sex: '男',
nation: '蒙古'
}
]
}
}
}
const App = {
template: `
<div>
<box1></box1>
<!--
<box1 v-slot='slotProps'>
<td>{{slotProps.nation}}--{{slotProps}}</td>
</box1>
-->
<box1 v-slot='{nation}'>
<td>{{nation}}</td>
</box1>
</div>
`,
components: { box1 }
}
作用域插槽+具名插槽
因为直接使用v-slot,则默认只能获取到默认插槽绑定的数据
如果是具名插槽,则需要在template包裹需要插入的标签,并添加属性 v-slot:插槽名
const box1 = {
template: `
<div>
<table border='1' cellspacing='10' cellpadding='10'>
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<slot name='c'></slot>
</tr>
</thead>
<tr v-for='({name,age,sex,nation,marryed}) in arr'>
<td>{{name}}</td>
<td>{{age}}</td>
<td>{{sex}}</td>
<slot name='a' :marryed='marryed' />
<slot name='b' :nation='nation'/>
</tr>
</table>
</div>
`,
data() {
return {
arr: [
{
name: '幂幂',
age: 32,
sex: '女',
nation: '汉',
marryed: '已离异'
}, {
name: '超越',
age: 23,
sex: '女',
nation: '苗',
marryed: '未婚'
}, {
name: '德刚',
age: 65,
sex: '男',
nation: '蒙古',
marryed: '已婚'
}
]
}
}
}
const App = {
template: `
<div>
<box1>
<th slot='c'>婚姻状况</th>
<template v-slot:a='{marryed}' slot='a'>
<td>{{marryed}}</td>
</template>
</box1>
<box1>
<th slot='c'>民族</th>
<template v-slot:b='{nation}' slot='b'>
<td>{{nation}}</td>
</template>
</box1>
</div>
`,
components: { box1 }
}