Vue 中 computed 和 watch 有什么区别?
计算属性 computed:
(1)支持缓存,只有依赖数据发生变化时,才会重新进行计算函数;
(2)计算属性内不支持异步操作;
(3)计算属性的函数中都有一个 get(默认具有,获取计算属性)和 set(手动添加,设置计算属性)方法;
(4)计算属性是自动监听依赖值的变化,从而动态返回内容。
侦听属性 watch:
(1)不支持缓存,只要数据发生变化,就会执行侦听函数;
(2)侦听属性内支持异步操作;
(3)侦听属性的值可以是一个对象,接收 handler 回调,deep,immediate 三个属性;
(3)监听是一个过程,在监听的值变化时,可以触发一个回调,并做一些其他事情。
vue是如何实现响应式数据的呢?(响应式数据原理)
Vue2: Object.defineProperty
重新定义 data
中所有的属性, Object.defineProperty
可以使数据的获取与设置增加一个拦截的功能,拦截属性的获取,进行依赖收集。拦截属性的更新操作,进行通知。
具体的过程:首先Vue使用 initData
初始化用户传入的参数,然后使用 new Observer
对数据进行观测,如果数据是一个对象类型就会调用 this.walk(value)
对对象进行处理,内部使用 defineeReactive
循环对象属性定义响应式变化,核心就是使用 Object.defineProperty
重新定义数据。
写过自定义指令吗 原理是什么
指令本质上是装饰器,是 vue 对 HTML 元素的扩展,给 HTML 元素增加自定义功能。vue 编译 DOM 时,会找到指令对象,执行指令的相关方法。
自定义指令有五个生命周期(也叫钩子函数),分别是 bind、inserted、update、componentUpdated、unbind
1. bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
2. inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
3. update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。
4. componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
5. unbind:只调用一次,指令与元素解绑时调用。
原理
1.在生成 ast 语法树时,遇到指令会给当前元素添加 directives 属性
2.通过 genDirectives 生成指令代码
3.在 patch 前将指令的钩子提取到 cbs 中,在 patch 过程中调用对应的钩子
4.当执行指令对应钩子函数时,调用对应指令定义的方法
如果让你从零开始写一个vuex,说说你的思路
思路分析
这个题目很有难度,首先思考vuex
解决的问题:存储用户全局状态并提供管理状态API。
vuex
需求分析- 如何实现这些需求
回答范例
- 官方说
vuex
是一个状态管理模式和库,并确保这些状态以可预期的方式变更。可见要实现一个vuex
- 要实现一个
Store
存储全局状态 - 要提供修改状态所需API:
commit(type, payload), dispatch(type, payload)
- 实现
Store
时,可以定义Store
类,构造函数接收选项options
,设置属性state
对外暴露状态,提供commit
和dispatch
修改属性state
。这里需要设置state
为响应式对象,同时将Store
定义为一个Vue
插件 commit(type, payload)
方法中可以获取用户传入mutations
并执行它,这样可以按用户提供的方法修改状态。dispatch(type, payload)
类似,但需要注意它可能是异步的,需要返回一个Promise
给用户以处理异步结果
实践
Store
的实现:
class Store {
constructor(options) {
this.state = reactive(options.state)
this.options = options
}
commit(type, payload) {
this.options.mutations[type].call(this, this.state, payload)
}
}
vuex简易版
/**
* 1 实现插件,挂载$store
* 2 实现store
*/
let Vue;
class Store {
constructor(options) {
// state响应式处理
// 外部访问: this.$store.state.***
// 第一种写法
// this.state = new Vue({
// data: options.state
// })
// 第二种写法:防止外界直接接触内部vue实例,防止外部强行变更
this._vm = new Vue({
data: {
$$state: options.state
}
})
this._mutations = options.mutations
this._actions = options.actions
this.getters = {
}
options.getters && this.handleGetters(options.getters)
this.commit = this.commit.bind(this)
this.dispatch = this.dispatch.bind(this)
}
get state () {
return this._vm._data.$$state
}
set state (val) {
return new Error('Please use replaceState to reset state')
}
handleGetters (getters) {
Object.keys(getters).map(key => {
Object.defineProperty(this.getters, key, {
get: () => getters[key](this.state)
})
})
}
commit (type, payload) {
let entry = this._mutations[type]
if (!entry) {
return new Error(`${
type} is not defined`)
}
entry(this.state, payload)
}
dispatch (type, payload) {
let entry = this._actions[type]
if (!entry) {
return new Error(`${
type} is not defined`)
}
entry(this, payload)
}
}
const install = (_Vue) => {
Vue = _Vue
Vue.mixin({
beforeCreate () {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store
}
},
})
}
export default {
Store, install }
验证方式
import Vue from 'vue'
import Vuex from './vuex'
// this.$store
Vue.use(Vuex)
export default new Vuex.Store({
state: {
counter: 0
},
mutations: {