组件高级(面试)
7.传值
7.1父组件通过属性给子组件传值: 子组件的props接受数据
在页面模板中 使用变量名:属性 data 计算属性(重点)
//注意属性传值是单向的
7.2子组件通过调用父组件的方法给父组件传值:子组件的自定义事件中,用$emit触发事件调用父组件方法给父组件传值 (重点)
因为通过属性传值是单向的,有时候我们需要子组件的data 数据需要交给父组件使用: 通过在子组件上定义自定义事件,在子组件中通过$emit 来触发事件;子组件的事件被触发并传参,事件处理函数可以接收到子组件的数据;事件绑定的事件处理函数在父节点上,故可在事件处理函数中用到子组件的数据值来修改父节点的数据。
//父组件中:
<my-search @myevent="handleEvent"></my-search>
//myevent是事子组件的自定义事件
//handleEvent是绑定的父组件的方法
子组件中:
在任意业务中触发事件:this.$emit("myevent","要给父组件传的数据")
7. 3多层组件传值:attrs(了解)
在不用状态管理vuex的时候,如何让GrandFather与Son通信,我们可以用可以emit一层一层的传递:会显得冗余。 vue2.4之后,提出listeners ,可以实现跨级组件通信。 attrs官网解说:属性传递
在组件中绑定 可以把当前组件的自定义属性和方法传给子元素使用:
one组件:<two v-bind:xx="100" v-on:twoEvent="fn"></two>
two组件中:<three v-bind="$attrs" v-on="$listeners"></three>
three组件:可以访问two的 属性和触发事件: {{this.$attrs.xx}} this.$emit("twoEvent",20)
7. 4 root、refs(了解)
这些功能都是有劣势或危险的场景的,官方建议我们尽量避开它们,但是高级点的面试题中会出现
$root: 访问根组件vm对象,所有的子组件都可以将这个实例作为一个全局 store 来访问或使用,现在有更好的技术vuex代替。
$parent:访问父组件对象,直接操作父组件的data数据,不需要再使用属性传值,但是容易出现渲染混乱之后只渲染一个的情况
$children:访问子组件对象数组,不能保证顺序,也不是响应式的
$refs:只会在组件渲染完成之后生效,并且它们不是响应式的。你应该避免在模板或计算属性中访问 $refs。
//在组件或者原生元素绑定ref属性(类似于id):
<myinput ref="myInput1"></myinput>
<input ref="myInput2"></input>
//在父组件中可以通过 this.$refs访问到它:
methods: {
focus: function () {
this.$refs.myInput2.focus()
}
}
7. 5中央事件总线bus(了解) event bus
通过创建一个新的vm对象,专门统一注册事件,供所有组件共同操作,达到所有组件随意隔代传值的效果
//vue-bus.js文件
const install = function (Vue) {
const Bus = new Vue({
methods: {
emit(event, ...args) {
this.$emit(event, ...args);
},
on(event, callback) {
this.$on(event, callback);
},
off(event, callback) {
this.$off(event, callback);
}
}
});
Vue.prototype.$bus=Bus;//由于这个新的vm放在与界面绑定的那个vm的原型上,因此页面上的所有组件都能通过this.$bus访问这个新vm对象
};
export default install;
//main.js文件
import VueBus from './vue-bus'
Vue.use(VueBus);
//组件文件中:
任意业务中都可以通过调用来绑定事件,触发事件并传值,和销毁事件
this.$bus.on(event,callback)
this.$bus.off(event,callback)
this.$bus.emit(event, ...args)
示例:
组件1:
this.$bus.on('changedFormObject',(val) =>{
//接受并处理传过来的值:val
this.msg = val;
});
组件2:
this.$bus.emit('changedFormObject',this.inputValue);//把组件2的data中的给inputValue值传给组件1
7.6Vue 依赖注入 - Provide/Inject(重点)
通常情况下,父组件向孙组件传递数据,可以采用父子props
层层传递,也可以使用bus
和Vuex
直接交互。在Vue2.2.0之后,Vue还提供了provide/inject
选项
官网不建议在应用中直接使用该办法,理由很直接:他怕你"管不好"
//爷爷
<template>
<div>
<p>{{ title }}</p>
<son></son>
</div>
</template>
<script>
import Son from "./son"
export default {
name: 'Father',
components: { Son },
// provide选项提供变量
provide: {
message: 'provided by father'
},
data () {
return {
title: '父组件'
}
},
methods: { ... }
}
</script>
//爸爸
<template>
<div>
<p>{{ title }}</p>
<grand-son></grand-son>
</div>
</template>
<script>
import grandSon from "./grandSon "
export default {
name: "Son",
components: { grandSon },
data () {
return {
title: '子组件'
}
},
};
</script>
//孙子
<template>
<div>
<p>message:{{ message }}</p>
</div>
</template>
<script>
export default {
name: "GrandSon",
inject: [ "message" ],
data () {
return {
title: '孙组件'
}
},
methods: { ... }
};
</script>
- 7仓库vuex(重点)
后面讲
8.动态组件
有的时候,我们希望页面中的某个地方,在不同组件之间进行动态切换,这时候除了条件渲染,还可以使用动态组件
component 标签的 is属性语法:is后跟组件的变量名决定使用哪个组件来渲染
==>
注意: is是组件名 :is是data中的变量中保存的组件名
<template>
<div id="app">
<button @click="fn">xx</button>
<components :is="com"></components>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import HelloWorld2 from './components/HelloWorld2.vue'
export default {
name: 'app',
data(){return{
flag:true,
com:"HelloWorld",
}},
components: {
HelloWorld,"HelloWorld2"
},
methods:{
fn(){
this.flag=!this.flag
this.com=this.flag?"HelloWorld":"HelloWorld2"
}
}
}
</script>
9.缓存组件keep-alive(还有两个钩子函数active()和deactive()没写)
缓存组件keep-alive 动态组件的切换,切换后是不会缓存之前被切换掉的组件的,每次切换新组件的时候,Vue 都创建了一个新的组件对象。 有时候我们希望在A组件时用户做了一些操作,切换B组件时做了一些操作,当切回A组件时希望记住A的操作,不要重新创建A组件,keep-alive可以缓存动态切换的组件
//include表示a,b组件被缓存,其他组件不缓存,exclude代表除了xx组件其他的组件缓存
匹配首先检查组件自身的 name 选项,匿名组件不能被匹配。
<template>
<div id="app">
<button @click="fn">xx</button>
<keep-alive>
<components :is="com"></components>
</keep-alive>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import HelloWorld2 from './components/HelloWorld2.vue'
export default {
name: 'app',
data(){return{
flag:true,
com:"HelloWorld",
}},
components: {
HelloWorld,"HelloWorld2"
},
methods:{
fn(){
this.flag=!this.flag
this.com=this.flag?"HelloWorld":"HelloWorld2"
}
}
}
</script>
10.异步组件(高薪意向)
vue开发过程中,我们会做出特别多特别多的组件,包括login,header,footer,main等等。这样使整个网站看起来就十分的庞大,当我们在打开网页的时候,突然一下子把这些所有的组件加载上来,这么多的请求全部同时开始请求,势必会造成网页打开很慢,使客户得到的是非常差劲的体验。
SPA: single page app
在单页应用中,如果没有用懒加载,运用webpack打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多,延时过长,不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时
异步加载并缓存组件:
1、 异步加载组件:用不到的组件不会加载,因此网页打开速度会很快,当你用到这个组件的时候,才会通过异步请求进行加载;
官方解释:Vue允许将组件定义为一个异步解析(加载)组件定义的工厂函数,即Vue只在实际需要渲染组件时,才会触发调用工厂函数,并且将结果缓存起来,用于将来再次渲染。
2、 组件缓存起来:通过异步加载的组件会缓存起来,当你下一次再用到这个组件时,丝毫不会有任何的疑迟,组件很快会从缓存中加载出来。
作业:import和require的区别
方法一:通过webpack2.0的按需加载
//1 全局:
Vue.component('component-name',function(resolve){
//require 语法告诉 webpack自动将编译后的代码分割成不同的块
//这些块将通过 Ajax 请求自动下载
require(['./my-async-componnet'],resolve)
})
//注册全局组件名,但只有一个名字,没有实体,相当于空的
//当需要这个组件时,调用上面的工厂函数,触发webpack的异步加载模块方法
//然后异步请求一个模块,请求成功后,这个模块内容即为组件实体部分,并对应地方渲染,加载内容也缓存下来。
//2 局部
new Vue({
components: {
'component-name':function(resolve) {
require(['./my-component'], resolve)
}
}
})
方法二:通过webpack2+es2015返回一个promise(主流 )
//1 全局:
Vue.component('component-name',
()=> import('./my-async-componnet')//这个 `import` 函数会返回一个 `Promise` 对象,不要问我为什么,人家webpack这样设计的。
)
//2 局部:
new Vue({
components: {
'component-name': () => import('./my-async-componnet')//这个 `import` 函数会返回一个 `Promise` 对象。
}
})
方法三:高级异步组件(可以处理加载状态) 常常用在路由中使用,vue 2.3.0+ 新增终极解决方案,要求路由2.4.0+
//工厂对象可以返回一个如下对象,对象里面的一些配置参数
const AsyncComponent = () => ({
// 需要加载的组件 (这个 `import` 函数会返回一个 `Promise` 对象。)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
示例:
<template>
<div id="app">
<button @click="fn">xx</button>
<keep-alive>
<components :is="com"></components>
</keep-alive>
<!-- <components :is="com"></components> -->
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'app',
data() {
return {
flag: true,
com: "HelloWorld",
}
},
components: {
HelloWorld,
// HelloWorld2,
// "HelloWorld2":() => import('./components/HelloWorld2.vue')
"HelloWorld2": () => ({
// 需要加载的组件 (是一个 `Promise` 对象)
component: import('./components/HelloWorld2.vue'),
// 异步组件加载时使用的组件
loading: import('./components/Palcebox.vue'),
// 加载失败时使用的组件
error: import('./components/Palcebox.vue'),
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
},
methods: {
fn() {
this.flag = !this.flag
this.com = this.flag ? "HelloWorld" : "HelloWorld2"
}
}
}
</script>
<style>
</style>