父组件中添加 scoped 属性会影响子组件吗
答:不会;在父组件中添加scoped之后,在父组件中书写子组件的样式是无效果的。去掉scoped之后,样式可以覆盖。但这样会污染全局样式。解决方案:使用 /deep/ 深度选择器
v-if 和 v-show 区别
答:v-if是按条件动态的增删DOM,v-show是设置display的block或none
v-if 有更高的切换开销,v-show 有更高的出示渲染开销。因此如果频繁的切换开销 v-show 使用 v-show 比较好,如果在运行时条件较少改变使用 v-if 比较好。
route 和 router 区别
答:route 是“路由信息对象”,包括path,params,hash,query,fullPath, matched,name等路由信息。
router 是“路由实例”对象包括了路由的跳转方法,钩子函数等。
vue.js的两个核心是什么?
答:数据驱动、组件系统
vue几种常用的指令
答:v-for 、 v-show、v-if 、v-else、v-bind、v-on
vue常用的修饰符?
答:
.number:输入框限制输入数字
.prevent: 提交事件不再重载页面;
.stop: 阻止单击事件冒泡;
.self: 当事件发生在该元素本身而不是子元素的时候会触发;.
capture: 事件侦听,事件发生的时候会调用
v-on 可以绑定多个方法吗?
答:可以
vue中 key 值的作用?
答:key的作用就是在更新组件时判断两个节点是否相同。相同就复用,不相同就删除旧的创建新的。在渲染简单的无状态组件时,如果不添加key组件默认都是就地复用,状态默认绑定的是位置,不会删除添加节点,只是改变列表项中的文本值,要知道节点操作是十分耗费性能的。而添加了key之后,状态根据key属性的值绑定相应的数组元素。当对比内容不一致时,就会认为是两个节点,会先删除掉旧节点,然后添加新节点。可以的作用就是为了高效的更新虚拟DOM。
若数组索引index作为key,当向数组中指定位插入一个元素后,对应着后面的虚拟DOM的key值全部更新了,这个时候还是做了不必要的更新,就想没有加key一样。
直接给一个数组赋值,Vue能检测到吗?
官方文档 检测变化的注意事项
答:由于JavaScript 的限制,Vue 不能检测到一下数组的变动
· 当你利用索引直接设置一个数组项的时候,例如:vm.items[index] = newValue;
· 当你修改数组的长度时,例如:vm.items.length = newLength;
解决方案
解决第一个问题
Vue.set(vm.items, indexOfItem, newValue)
// vm.$set,Vue.set的一个别名
vm.$set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
解决第二个问题
// Array.prototype.splice
vm.items.splice(newLength)
computed和watch区别和应用场景
答:
computed
1.支持缓存,只有当依赖数据发生变化时,才会重新计算。
2.不支持异步,当computed 内有异步操作时,无法监听数据的变化。
3.computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
4.如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
5.如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
watch
1.不支持缓存,数据变,直接会触发相应的操作;
2.watch支持异步;
3.当一个属性发生变化时,需要执行对应的操作;一对多;
4.监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,immediate:组件加载立即触发回调函数执行,deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用
Vue的父组件和子组件生命周期钩子函数执行顺序
答:Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:
- 加载渲染过程
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted - 子组件更新过程
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated - 父组件更新过程
父 beforeUpdate -> 父 updated - 销毁过程
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
在那个生命周期内调用异步请求
答:可在钩子函数 created,beforeMounted,mounted 中进行,因为在这三个钩子函数之后,data 已经创建,可以将服务端端返回的数据进行赋值。但是本人推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点
· 能更快获取到服务端数据,减少页面 loading 时间
· ssr 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
在什么阶段可以调用DOM
答: 在钩子函数 mounted 被调用前,Vue 已经将编译好的模板挂载到页面上,所以在 mounted 中可以访问操作 DOM。
父组件能监听子组件生命周期吗
答:可以
比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现:
// Parent.vue
<Child @mounted="doSomething"/>
// Child.vue
mounted() {
this.$emit("mounted");
}
以上需要手动通过 $emit 触发父组件的事件,更简单的方式可以在父组件引用子组件时通过 @hook 来监听即可,如下所示:
// Parent.vue
<Child @hook:mounted="doSomething" ></Child>
doSomething() {
console.log('父组件监听到 mounted 钩子函数 ...');
},
// Child.vue
mounted(){
console.log('子组件触发 mounted 钩子函数 ...');
},
// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...
当然 @hook 方法不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以监听。
谈谈你对 keep-alive 的了解?
答:keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:
· 一般结合路由和动态组件一起使用,用于缓存组件;
· 提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
· 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。
https://www.jianshu.com/p/a548446ecafe
为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?
答:
// data
data() {
return {
message: "子组件",
childName:this.name
}
}
// new Vue
let vm = new Vue({
// 挂载元素
el: '#app',
// 实例vm的数据
data: {
name: '张三',
age: 18
}
})
因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。
v-bind 和 v-model 区别?
-v-bind 是数据绑定,没有双向绑定效果,但不一定在表单组件上使用,任何有效元素上都可以使用。
-v-model 是双向数据绑定,基本上只用在表单元素上。
-当 v-bind 和 v-model 同时用在一个元素上时,他们各自的作用没变,但v-model优先级更高。
v-model原理
答:我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
· text 和 textarea 元素使用 value 属性和 input 事件;
· checkbox 和 radio 使用 checked 属性和 change 事件;
· select 字段将 value 作为 prop 并将 change 作为事件。
v-model会把它关联的响应式数据(如info.message),动态地绑定到表单元素的value属性上,然后监听表单元素的input事件:当v-model绑定的响应数据发生变化时,表单元素的value值也会同步变化;当表单元素接受用户的输入时,input事件会触发,input的回调逻辑会把表单元素value最新值同步赋值给v-model绑定的响应式数据。
以 input 表单元素为例:
<input v-model='something'>
相当于
<input v-bind:value="something" v-on:input="something = $event.target.value">
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示
父组件:
<ModelChild v-model="message"></ModelChild>
子组件:
<div>{{value}}</div>
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小红')
},
},
Vue2 响应式原理
效果:当视图模型(VM)中的数据模型(M)发生改变时, 视图(V)就会进行更新
实现方式:
new Vue的时候在Vue 的构造函数中会获取到 data 然后吧data注入到Vue 的实例中,这样在调用data中的数据时就可以直接使用this调用而不是this.data调用。
然后调用Observer方法传入data,在Observer 的构造方法中 遍历data,对每一个data成员使用Object.defineProperty 进行数据劫持,重写每个成员的getter 和setter方法,每一个data成员都会有一个Dep 实例,在首次render时会触发每个data成员的getter方法, 在getter方法中收集依赖,添加Watcher,这样在修改data成员值的时候会调用setter 方法,setter 方法会比较传入传入进来的值和旧值是否一样,若不一样则会调用dep实例的 notify() 方法去通知当前修改数据的所有观察者(watcher)去执行update()方法,updata方法中执行回调函数去更新视图
自己实现了一个简单版的响应式原理:
https://github.com/dingruibobo/minivue/tree/main/%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86%E6%A8%A1%E6%8B%9F
vue2双向数据绑定原理
效果:数据改 , 视图更;视图改, 数据更
实现:使用 v-model
实现机制:v-model会把它关联的响应式数据(如info.message),动态地绑定到表单元素的value属性上,然后监听表单元素的input事件:当v-model绑定的响应数据发生变化时,表单元素的value值也会同步变化;当表单元素接受用户的输入时,input事件会触发,input的回调逻辑会把表单元素value最新值同步赋值给v-model绑定的响应式数据。
vue2是如何监听数组的变化的
我们知道通过Object.defineProperty() 劫持数组设置getter 和 setter 后,调用数组的push,splice,pop等方法改变数组时并不会触发setter,这样就造成使用上述方法时,页面上不能及时体现。也就是数组的变化不是响应的。
但是在实际开发中,对响应式数组使用push、splice、pop等方法改变数组时,页面会及时体现这种变化,那么vue中是如何实现的呢?
vue重写了数组的七个方法:push, pop, shift, unshift, splice, sort, reverse;重写方法时除了将数组方法名对应的原始方法执行了一遍并返回执行结果外,还执行 dep.notify() 将当前数组的变更通知给订阅者,这样但使用重写后到的方法改变数组的时候,数组订阅者会将变化更新到页面中。重写完7个方法后,我们还需要将这些方法应用到数组上,所以在Observer 构造函数中,会先去判断当前劫持的data数据是不是数组,如果是数组,则会判断浏览器是否支持proto属性,若浏览器支持proto 属性,则直接将当前data数据的proto 指向重写后的数组方法对象,若浏览器不支持proto 则遍历重写后数组方法对象直接定义到当前data数据上;
这样在调用 pop, push 等方法的时候,重写后的方法会手动出发通知该数组的所有依赖进行更新
参考链接:https://www.cnblogs.com/ming1025/p/13082822.html
源码解析链接:https://github.com/dingruibobo/learnVue/tree/master/vue-src/core/observer
Vue.nextTick(callback)
官方文档异步更新队列
可能你还没有注意到,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。
例如,当你设置 vm.someData = 'new value',该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。多数情况我们不需要关心这个过程,但是如果你想基于更新后的 DOM 状态来做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员使用“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们必须要这么做。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用。例如:
<div id="example">{{message}}</div>
var vm = new Vue({
el: '#example',
data: {
message: '123'
}
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
})
在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上:
Vue.component('example', {
template: '<span>{{ message }}</span>',
data: function () {
return {
message: '未更新'
}
},
methods: {
updateMessage: function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '已更新'
})
}
}
})
因为 $nextTick()
返回一个 Promise
对象,所以你可以使用新的 ES2017 async/await 语法完成相同的事情:
methods: {
updateMessage: async function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
await this.$nextTick()
console.log(this.$el.textContent) // => '已更新'
}
}
vue函数式组件
函数是组件
无状态
无法实例化
内部没有任何生命周期处理函数
轻量,渲染性能高,适合只依赖于外部数据传递而变化的组件(展示组件,无逻辑和状态修改)
在template标签里标明functional
只接受props值
不需要script标签
父组件
<template>
<div>
<List :items="['Wonderwoman', 'Ironman']" :item-click="item => (clicked =
item)" />
<p>Clicked hero: {{ clicked }}</p>
</div>
</template>
<script>
import List from './List'
export default {
name: "App",
data: () => ({ clicked: "" }),
components: { List }
};
</script>
List.vue 函数式组件
<template functional>
<div>
<p v-for="(item,index) in props.items" :key="index" @click="props.itemClick(item)" />
</div>
</template>
vue作用域插槽
作用域插槽就是子组件可以给父组件传参,父组件决定怎么展示,作用域插槽给了子组件将数据返给父组件的能力,子组件一样可以复用,同时父组件也可以重新组织内容和样式
常用于多级列表展示,https://segmentfault.com/a/1190000015938629
简述vuex有哪几个模块,存取方式都有哪些?
state:定义了应用程序的数据,可以设置默认的初始状态。
getters:允许组件从 store 中获取数据 。
mutations:是唯一更改 store 中状态的方法,且必须是同步函数。但不可以直接调用mutation,必须使用commit函数来告诉Vuex更新存储并提交更改。
actions:执行异步操作来存取状态,但也不可以直接调用action,必须使用dispatch函数来执行。
通过dispatch来调用actions中的方法。当actions调用commit的方法来触发mutations里面的方法修改数据
存值: this.store.state.xx
Vuex也提供了一些辅助工具,如mapStates,mapGetters,mapMutations,mapActions,从而来减少繁杂的工作量
用法:
...mapGetters(['realname','money_us'])
映射关系
mapState > computed
mapGetters > computed
mapMutations > methods
mapActions > methods