computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
运用场景:
- 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
- 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
在哪个生命周期内调用异步请求?
可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。但是本人推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
- 能更快获取到服务端数据,减少页面 loading 时间;
- ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
Vue 组件间通信有哪几种方式?
Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你回答出越多方法当然越加分,表明你对 Vue 掌握的越熟练。Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。
(1)props / $emit
适用 父子组件通信
这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。
在父页面 引用组件1、传值给父页面(data) 2、监听子页面的事件 (@confirm)
<dialog-cj ref="dialogCj" :data="cjForm" @confirm="query()"></dialog-cj>
在子页面
1、获取父页面传值的数据
export default {
props: {
data:Object
},
data(){......},
watch: {
data() {
this.formData = {...this.data}
},
},
2、子页面的变化数据传值给 父页面
<el-button @click="callback" type="primary">保存</el-button>
callback(data) {
this.$emit('confirm', data)
},
(2)ref
与 $parent / $children
适用 父子组件通信
ref
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例$parent
/$children
:访问父 / 子实例
<div id="app">
<h2>父组件中的数据{{user}}</h2>
<button @click="FatherBtn">父组件按钮</button>
<son-com></son-com>
</div>
<script>
//定义子组件
let SonCom = ({
template:`
<button @click = "handleClick">子组件按钮</button>
`,
methods:{
handleClick(){
//在子组件中找到父组件的方法并执行
this.$parent.ChangeUser();
}
}
})
let vm = new Vue({
el:"#app",
data:{
user:{
name:"joe"
}
},
components:{
SonCom,
},
methods:{
FatherBtn(){
console.log(this.$children[0]);
},
//定义添加属性的方法
ChangeUser() {
this.user = Object.assign({},this.user,{age:19});
}
}
})
</script>
1、 @click="FatherBtn"父组件按钮 可以通过
this.$children[0]//获取到子组件的数据
2、 @click="handleClick"子组件按钮 可以通过
this.$parent.user//获取父组件 改变父组件的值
(3)EventBus ($emit / $on)
适用于 父子、隔代、兄弟组件通信
这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。
1、父页面打开子页面
<el-button @click="openAddDialog()" type="text">新增</el-button>
......
<dialog-add ref="addDialog" @confirm="onConfirm" :data="tableRowData"></dialog-add>
<dialog-edit ref="editDialog" @confirm="onConfirm" :data="tableRowData"></dialog-edit>
openAddDialog() {
this.$refs.addDialog.$emit('open')
或者
this.$refs.editDialog.$emit('open')
},
2、在子页面
<el-dialog title="新增数据" :visible.sync="dialogVisible" width="480px" :close-on-click-modal="false">
<el-form :key="id" :model="editForm"
......
created() {
this.$on('open', () => {
this.id++
this.dialogVisible = true
})
},
(4)$attrs
/$listeners
适用于 隔代组件通信
$attrs
:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过v-bind="$attrs"
传入内部组件。通常配合 inheritAttrs 选项一起使用。$listeners
:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过v-on="$listeners"
传入内部组件
代码实例
A.vue
<template>
<div id="app">
<!-- 此处监听了事件,可以在C组件中直接触发 -->
<b-child
nameToB="nameToB"
nameToC="nameToC"
@buttonClick="buttonClick"
>
</b-child>
</div>
</template>
<script> import BChild from "./B.vue";
export default {
data() {
return {};
},
components: { BChild },
methods: {
buttonClick() {
console.log("buttonClick...");
}
}
}; </script>
B.vue
<template>
<div>
<h1>B组件</h1>
<p>name: {{nameToB}}</p>
<p>$attrs: {{$attrs}}</p>
<hr>
<!-- C组件中能直接触发buttonClick的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 -->
<!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
<c-child v-bind="$attrs" v-on="$listeners"></c-child>
</div>
</template>
<script> import CChild from './C.vue';
export default {
props: ['nameToB'],
components: { CChild },
data () {
return {};
},
// inheritAttrs: false,
}; </script>
C.vue
<template>
<div>
<h1>C组件</h1>
<p>name: {{nameToC}}</p>
<p>$attrs: {{$attrs}}</p>
<button @click="buttonClick">点击C按钮</button>
</div>
</template>
<script> export default {
// inheritAttrs: false,
props: ['nameToC'],
data () {
return {};
},
methods: {
buttonClick(){
this.$emit('buttonClick');
}
}
}; </script>
最终,通过B实现了A与C的通信
B组件只是作为中间枢纽站进行转折
通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的)
C组件中能直接触发buttonClick的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性
(5)provide / inject
适用于 隔代组件通信
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
(6)Vuex 适用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
vue-router 路由模式有几种?
vue-router 有 3 种路由模式:hash、history、abstract,对应的源码如下所示:
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
复制代码
其中,3 种路由模式的说明如下:
- hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;
- history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
- abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。