Vue
Vue
组件通信
import { useState } from "react";
import "./styles.css";
function One({ count, setCount }) {
return (
<div style={{ border: "1px solid red" }}>
<h2>Conponent One</h2>
<button onClick={() => setCount(count + 1)}>Click</button>
<div>{count}</div>
</div>
);
}
function Two({ count, setCount }) {
return (
<div style={{ border: "1px solid red" }}>
<h2>Conponent Two</h2>
<button onClick={() => setCount(count + 1)}>Click</button>
<div>{count}</div>
</div>
);
}
export default function App() {
const [count, setCount] = useState(0);
return (
<div className="App">
<One count={count} setCount={(c) => setCount(c)} />
<Two count={count} setCount={(c) => setCount(c)} />
</div>
);
}
数据响应式原理 vue2 & vue3
VUE2
- 核心API-Object.defineProperty
- 如何实现响应式
- Object.defineProperty的一些缺点(VUE3.0启用 Proxy)
- Proxy存在兼容性问题
- 且无法polyfill
- IE11等无法使用
- Proxy存在兼容性问题
// 触发更新视图
function updateView() {
console.log('视图更新')
}
// 重新定义数组原型
const oldArrayProperty = Array.prototype
// Object.create 方法
// --->创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
const arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
arrProto[methodName] = function () {
updateView() // 触发视图更新
oldArrayProperty[methodName].call(this, ...arguments)
// Array.prototype.push.call(this, ...arguments)
}
})
// 重新定义属性,监听起来
function defineReactive(target, key, value) {
// 深度监听
observer(value)
// 核心 API
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
if (newValue !== value) {
// 深度监听
observer(newValue)
// 设置新值
// 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
value = newValue
// 触发更新视图
updateView()
}
}
})
}
// 监听对象属性
function observer(target) {
if (typeof target !== 'object' || target === null) {
// 不是对象或数组
return target
}
// 1.0 不可以这样处理 array 的方法 --->污染全局的 Array 原型
// Array.prototype.push = function () {
// updateView()
// ...
// }
// 2.0 只能通过copy代理一份
if (Array.isArray(target)) {
target.__proto__ = arrProto
}
// 重新定义各个属性(for in 也可以遍历数组)
for (let key in target) {
defineReactive(target, key, target[key])
}
}
// 准备数据
const data = {
name: 'zhangsan',
age: 20,
info: {
address: '北京' // 需要深度监听
},
nums: [10, 20, 30]
}
// 监听数据
observer(data)
// 测试
// data.name = 'lisi'
// data.age = 21
// // console.log('age', data.age)
// data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set
// delete data.name // 删除属性,监听不到 —— 所有已 Vue.delete
// data.info.address = '上海' // 深度监听
data.nums.push(4) // 监听数组
- 几个缺点
- Object.defineProperty缺点
- 深度监听,需要
递归到底
,一次性计算量大 - 无法监听
新增/删除
属性(需使用 Vue.set
Vue.delete
) - 无法
监听
原生数组
,需要特殊处理
- 深度监听,需要
- Object.defineProperty缺点
VUE3—Proxy响应式
- vue3如何用proxy实现响应式
深度监听
通过get
来获取—只有触发get才会又,触发其他的方法set,deleteProperty不会- 你获取到那一层,才会触发那一层的响应式(递归)
- obj.a 只到a,obj.a.b 不会触发 obj.a.b.c
- 不像
definePropery
的深度监听,一开始就已经深度递归了.一次性递归所有层级
- 你获取到那一层,才会触发那一层的响应式(递归)
- proxy的缺点
- 无法兼容IE,无法polyfill Polyfill 是一块代码(通常是 Web 上的 JavaScript),用来为旧浏览器提供它没有原生支持的较新的功能
// 创建一个响应式对象的函数
function reactive(target) {
// 定义处理程序对象,用于拦截对目标对象的操作
const handlers = {
// 当访问对象属性时触发此方法
get: (obj, prop) => {
console.log(`获取 ${prop} 的值`); // 打印获取的属性名
// 使用 Reflect.get 来执行默认的属性获取操作,并返回结果
return Reflect.get(obj, prop);
},
// 当设置对象属性时触发此方法
set: (obj, prop, value) => {
console.log(`设置 ${prop} 的新值为 ${value}`); // 打印设置的属性名和新值
// 使用 Reflect.set 来执行默认的属性设置操作,并返回操作是否成功的结果
const result = Reflect.set(obj, prop, value);
trigger(); // 触发副作用函数重新运行
return result;
}
};
// 返回一个带有自定义处理程序的新代理对象
return new Proxy(target, handlers);
}
// 触发副作用函数
let activeEffect; // 存储当前活跃的副作用函数
// 注册并立即执行副作用函数
function effect(fn) {
activeEffect = fn; // 设置当前活跃的副作用函数
fn(); // 立即执行副作用函数
activeEffect = null; // 清除当前活跃的副作用函数
}
// 当数据改变时调用此函数以触发副作用
function trigger() {
if (activeEffect) {
// 如果存在当前活跃的副作用函数,则重新执行它
activeEffect();
}
}
// 使用示例
const state = reactive({ count: 0 }); // 创建一个响应式对象
effect(() => {
// 注册一个副作用函数,该函数依赖于 state.count
console.log(`count 的值是:${state.count}`); // 每当 state.count 变化时,这个函数会被重新执行
});
// 修改状态,这将触发副作用函数重新运行
state.count++; // 这里会打印 "设置 count 的新值为 1" 和 "count 的值是:1"
- reactive 函数:
接收一个普通对象 target 并返回一个通过 Proxy 包装过的响应式对象。
- handlers 对象包含两个关键方法:
get 和 set,它们分别在访问和修改对象属性时被触发。 Reflect.get 和 Reflect.set 方法用来执行默认的对象操作行为,同时允许我们拦截这些操作并添加额外逻辑(如日志记录)。
- effect 函数:
接收一个副作用函数 fn 并立即执行它。 在执行副作用函数之前,先将其存储在 activeEffect 变量中,这样可以在数据变化时重新执行该副作用函数。 副作用函数通常包含一些依赖于响应式数据的操作,比如读取 state.count。
- trigger 函数:
在 set 处理器中调用,用于触发副作用函数重新运行。 如果当前有活跃的副作用函数(即 activeEffect 不为 null),则重新执行该副作用函数。
创建了一个响应式对象 state,其中包含一个属性 count。 使用 effect 注册了一个副作用函数,该函数依赖于 state.count 的值。 每次修改 state.count 的值时,都会触发副作用函数重新执行,从而实现响应式更新。
Vue2 & Vue3 区别
options API生命周期 —>类似vue2的生命周期
- 销毁生命周期名称修改
- beforeDestroy 改为
beforeUnmout
- destroy 改为
unmouted
- beforeDestroy 改为
- 其余沿用vue2生命周期不变
- 在vue2中仍然可以用使用原有的date,methods,props等方法不变
setUp中的新生命周期定义
setup
等同于beforeCreate
& Create
与老的几乎一致,同时新老都可以使用
虚拟dom , diff
- diff即对比的意思,是一个广泛的概念,如linux diff命令,git diff等
- 两个js对象 也可以做diff ,如
https://github.com/cujojs/jiff
- 两颗树做diff,如 这里的 vdom diff
Vue 和 react 的区别
从思想、生态、语法、数据、通信、diff等角度自己总结一下吧。
v-model
的原理
请阐述一下 v-model 本质上不过是语法糖,可以用 v-model 指令在表单 input、textarea
及 select
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。 v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text 和 textarea 元素使用 value 属性和 input 事件;
- checkbox 和 radio 使用 checked 属性和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
原理:
v-model只不过是一个语法糖而已,真正的实现靠的还是
- v-bind:绑定响应式数据
- 触发oninput 事件并传递数据
<input v-bind:value="searchText"
v-on:input="searchText=$event.target.value"/>
在 react/vue 中数组是否可以以在数组中的次序为 key
不可,key应为唯一标示,在数组变更时插入或删除后,index无法确保始终
指向对应的序列
vue 生命周期&父子组件生命周期
执行顺序:父组件先创建,然后子组件创建;子组件先挂载,然后父组件挂载,
即
父beforeCreate-> 父create -> 子beforeCreate-> 子created -> 子mounted -> 父mounted
子组件挂载完成后,父组件还未挂载。所以组件数据回显的时候,在父组件mounted
中获取api的数据,子组件的mounted
是拿不到的。
补充单一组件钩子执行顺序
activated deactivated=⇒ keep-alive的生命周期
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- activated
- deactivated
- beforeDestroy
- destroyed
- errorCaptured
computed和methods有什么区别
- computed有缓存—返回计算的值给到视图View
- data
不变
则不会
重新计算
- data
- watch监听引用类型,拿不到oldVal
- array,object,function引用类型,拿不到 oldVal 。
- 因为指针相同,此时已经指向了新的 val
请阐述keep-alive组件的作用和原理
VUEX
- state
- getters
- action 异步操作
- mutation 原子级别的多个操作(同步)
在组件中使用
- dispatch
- commit
- mapState
- mapGetters
- mapActions
- mapMutations