Vue

umwelt2021-08-22frontendvue

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等无法使用
// 触发更新视图
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.setVue.delete)
      • 无法监听原生数组,需要特殊处理

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
  • 其余沿用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、textareaselect 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。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的生命周期

  1. beforeCreate
  2. created
  3. beforeMount
  4. mounted
  5. beforeUpdate
  6. updated
  7. activated
  8. deactivated
  9. beforeDestroy
  10. destroyed
  11. errorCaptured

computed和methods有什么区别

  • computed有缓存—返回计算的值给到视图View
    • data不变不会重新计算
  • watch监听引用类型,拿不到oldVal
    • array,object,function引用类型,拿不到 oldVal 。
    • 因为指针相同,此时已经指向了新的 val

请阐述keep-alive组件的作用和原理

VUEX

  • state
  • getters
  • action 异步操作
  • mutation 原子级别的多个操作(同步)

在组件中使用

  • dispatch
  • commit
  • mapState
  • mapGetters
  • mapActions
  • mapMutations
Last Updated 2/21/2025, 12:31:21 AM