Skip to main content

react-vue

react

vue和react的区别

1. react中有对状态管理做进一步封装吗

React Context API: React自身提供了一种状态管理的方式,称为Context API。你可以使用React.createContext来创建一个上下文对象,将状态存储在该上下文中,并使用Provider和Consumer组件来在组件层次结构中传递状态。尽管Context API可以用于简单的状态管理,但它不如Redux或Mobx那么强大。

2. react中在父组件中如何获取子组件的方法

  1. 使用React.createRef()创建引用: 首先,在父组件中使用React.createRef()来创建一个引用对象,然后将该引用对象分配给子组件的ref属性。这样,你就可以在父组件中访问子组件的实例。
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.childRef = React.createRef();
}

// 通过引用访问子组件的方法
someMethodInParent() {
this.childRef.current.someMethodInChild();
}

render() {
return <ChildComponent ref={this.childRef} />;
}
}
  1. hooks: 父组件:
export function TemplateModal(props: TemplateModalProps) {
const formRef: any = useRef()
// 调用子组件的方法
const handleCancel = () => {
formRef.current.resetForm()
onClose()
cleanModalCache()
}

return (
<AddForm ref={formRef} formValues={formValues} />
)
}
export function AddForm(props: AddFormPrps, ref: any) {
const [form] = Form.useForm()

// 暴露组件的方法
useImperativeHandle(ref, () => ({
submitForm: () => {
const values = form.getFieldsValue()
return values
},
resetForm: () => {
form.resetFields()
}
}))

return (
<Form
form={form}
name='template_form'
layout='inline'
onFinish={onFinish}
initialValues={{ title: '', type: 0, tags: [] }}
/>
)
}

const WrappedAddForm = forwardRef(AddForm)

export default WrappedAddForm

3.react函数组件和类组件处理重复渲染有什么区别?

  • shouldComponentUpdate return true 就会render
  • isEqual 返回 true 时,不会触发 render

4.方法什么时候写在父组件中,什么时候写在子组件中?

决定将方法写在父组件还是子组件中取决于方法的用途以及数据的流动方式。以下是一些指导原则,可以帮助你决定应该在哪里编写方法:

  1. 方法写在父组件中的情况:
  • 数据传递和状态提升:如果方法需要在多个子组件之间共享并且操作共享的数据(状态提升),通常将方法定义在最近共享状态的父组件中。这有助于保持数据的一致性和可维护性。
  • 跨组件通信:如果你需要在不直接嵌套的组件之间传递数据或触发操作,将方法定义在更高级别的父组件中,然后通过props将它们传递给相关子组件。
  • 处理全局状态:如果你使用状态管理工具(如Redux)来管理全局状态,通常会在顶层容器组件中定义方法,以便在整个应用程序中共享和操作全局状态。
  1. 方法写在子组件中的情况:
  • 组件内部逻辑:如果方法的逻辑仅涉及到特定子组件内部的操作,将方法定义在子组件中,以保持组件的封装性和独立性。
  • 重用性:如果方法仅在一个子组件内部使用,并且不会在其他地方复用,将其定义在子组件内是有意义的。这可以使组件更加自包含,易于维护。
  • 事件处理:通常,处理与子组件直接相关的事件(例如按钮点击或表单提交)的方法应该位于子组件中。这有助于将逻辑封装在子组件内部,使其更具可读性。

5.react中组件传值

  1. 使用props(属性)来在组件之间传递值。Props是一种将数据从父组件传递到子组件的机制
  2. Context API:React的Context API允许你在整个组件树中共享数据,而不必手动将props一层一层传递。它特别适用于跨层级的数据共享
  3. Redux
  4. HOC(高阶组件):高阶组件是一个接受一个组件并返回一个新组件的函数。你可以使用高阶组件来包装子组件,并将额外的功能或数据传递给子组件。

6.虚拟dom的理解

见doc

7.redux的工作流程

见doc

vue

讲一下 vue 的生命周期

lifecycle-vue3-vue2区别

讲一下 computed 与 watch 的区别

watch

computed-data区别

vue3 中组件通信都有哪些方式?

Vue 3 中有多种方式可以实现组件通信,根据需求和场景的不同,你可以选择适合的方式:

  1. Props/自定义属性:通过将数据作为属性传递给子组件,父组件可以向子组件传递数据。这是一种父子组件通信的常见方式。子组件通过props接收来自父组件的数据。

  2. 自定义事件:父组件可以通过自定义事件向子组件发送消息。子组件可以使用$emit触发事件,而父组件可以监听这些事件并执行相应的操作。

  3. $emit/$on:除了父子组件之间的通信,Vue 3 允许在任意两个组件之间建立通信渠道。你可以使用$emit$on来建立自定义事件通信,而不仅仅限于父子组件之间。

  4. Provide/Inject:使用provideinject可以在父组件和子组件之间建立一种"祖先-后代"关系的通信。祖先组件通过provide提供数据,而后代组件通过inject访问这些数据。

  5. 全局事件总线:你可以创建一个全局事件总线(Event Bus)来进行组件通信。这是一种非常灵活的方式,但也容易导致混乱。在Vue 3中,你可以使用mitt库来创建事件总线。

  6. Vuex:对于大型应用或需要跨组件通信的情况,可以使用Vuex来管理全局状态。Vuex提供了一种集中式的状态管理方法,允许多个组件共享和修改同一状态。

  7. Router参数:如果你的应用使用Vue Router,你可以通过路由参数来进行组件通信。参数可以通过路由链接传递,也可以通过路由的queryparams属性传递。

  8. 事件总线库:除了Vue自带的通信方式,还有一些第三方事件总线库,如mitttiny-emitter等,可以用来处理组件通信。

选择合适的通信方式取决于你的应用的需求和架构。对于简单的父子组件通信,props和自定义事件通常足够了。对于复杂的状态管理和跨组件通信,考虑使用Vuex或其他全局状态管理方法。

讲一下 vue3 相比 vue2 有哪些提升

Vue 3 相较于 Vue 2 带来了一系列的改进和性能提升,以下是一些 Vue 3 相比 Vue 2 的主要提升:

  1. 性能优化

    • 更快的渲染性能:Vue 3 使用了优化的虚拟DOM算法,提供更快的渲染性能。
    • 更小的包大小:Vue 3 的核心库采用了更好的模块化和Tree-Shaking支持,使得最终的包大小更小。
    • 懒加载:Vue 3 支持组件的懒加载,只在需要时才加载组件,减少初始加载时间。
  2. Composition API:Vue 3 引入了 Composition API,它允许组织代码更加灵活,使组件更易维护和理解。它也更容易共享和重用逻辑。

  3. Teleport 和 Suspense:Vue 3 引入了 Teleport 组件,使得更容易在组件树中移动DOM元素。同时,Suspense 组件使异步数据加载更加简单,可通过等待异步数据渲染组件,而不需要复杂的逻辑。

  4. 响应式系统升级:Vue 3 使用 Proxy 来替代 Object.defineProperty,这带来了更好的性能和更丰富的反应性特性。它还提供了更好的 TypeScript 支持。

  5. 更好的TypeScript支持:Vue 3 提供了更强大的 TypeScript 类型支持,包括组件类型、响应式类型等。

  6. 多根节点支持:Vue 3 放宽了单文件组件的模板要求,允许多个根节点,使得组织复杂的布局更加容易。

  7. Fragments:Vue 3 引入了 Fragments,可以更容易地组合多个元素而不需要额外的包装。

  8. 自定义渲染器:Vue 3 的架构更加灵活,支持自定义渲染器,可以用于构建不仅限于DOM的渲染目标,例如NativeScript、Weex等。

  9. 更好的TypeScript支持:Vue 3 提供了更强大的 TypeScript 类型支持,包括组件类型、响应式类型等。

总之,Vue 3 带来了许多性能优化、更好的开发体验和更灵活的编程模型。它在响应性、可组合性、性能和开发工具等方面都有显著的改进,使得开发者能够更高效地构建现代的Web应用。当然,在迁移到 Vue 3 时,开发者需要了解新特性和API,以充分利用这些优势。

1.Vue2和vue3的tree shaking的区别

Tree Shaking 是一种用于优化前端 JavaScript 应用程序大小的技术,它可以通过删除未使用的代码来减小最终的打包文件大小。以下是 Vue.js 2 和 Vue.js 3 在 Tree Shaking 方面的主要区别:

  1. 模块系统的不同:
  • Vue.js 2 使用的是 CommonJS 模块系统。CommonJS 模块的导入和导出方式不够静态,这意味着 Webpack 在尝试进行 Tree Shaking 时可能会遇到一些困难。因此,Vue.js 2 的Tree Shaking 支持并不是特别好。
  • Vue.js 3 使用的是 ES 模块系统,这是一种更适合静态分析的模块系统。这使得 Vue.js 3 更容易进行 Tree Shaking,因为工具可以更容易地分析和确定哪些代码未被使用。
  1. Composition API:
  • Vue.js 3 引入了 Composition API,它使得代码更容易组织和重用。这有助于提高 Tree Shaking 的效果,因为你可以更精确地控制哪些组合函数和逻辑被包含在最终的捆绑文件中。 懒加载:

  • Vue.js 3 提供了更好的支持懒加载(Lazy Loading)组件的机制,这可以进一步提高 Tree Shaking 的效果。你可以延迟加载只有在需要时才加载的组件,而不是将所有组件一次性打包到主文件中。

讲一下 vue3 相比 vue2 ,它在 diff 算法上做了哪些优化?

Vue 3 在 diff 算法方面进行了一些优化,以提高性能和减少不必要的渲染。以下是一些 Vue 3 在 diff 算法上的改进:

  1. 静态树提升(Static Tree Hoisting):Vue 3 引入了静态树提升,通过标记和提升静态树中的不变节点,减少了虚拟DOM的生成和比对成本。这可以减少不必要的重渲染,提高性能。

  2. Fragments 和 Teleport:Vue 3 引入了Fragments和Teleport,允许在不创建额外的DOM层级的情况下,更轻松地组合和渲染组件。这可以减少虚拟DOM树的复杂性,减少渲染开销。

  3. Diff 策略优化:Vue 3 改进了虚拟DOM的比对策略,减少了不必要的节点比对。Vue 3的diff算法在处理动态节点时更高效,而Vue 2中使用的双指针算法可能会在某些情况下执行更多的比对操作。

  4. Fragment 和 Teleport 的内置支持:Vue 3 为Fragments和Teleport提供了内置支持,而在Vue 2中,它们通常需要使用插件或其他解决方案来实现。这意味着更少的外部依赖和更高的性能。

  5. 新的编译器(Compiler):Vue 3 使用了新的编译器,它生成更优化的渲染函数,进一步减少了运行时开销。

  6. Tree Shaking 支持:Vue 3 更好地支持Tree Shaking,使开发者能够在应用中只包含实际使用的代码,而不包括未使用的组件和功能。

这些优化使Vue 3在性能方面更加出色,特别是在处理大型、复杂的页面和组件树时。 Vue 3的diff算法和渲染引擎都经过重新设计和改进,以提供更好的性能和更高的效率。这使得Vue 3成为了一个更强大的前端框架,适用于各种类型的应用程序。

讲一下 vue3 的 diff 算法吧

Vue 3 的虚拟DOM和diff算法在渲染组件时经历了一系列的步骤。以下是Vue 3的diff算法的基本流程:

  1. 数据变更触发:当组件的数据发生变化,或者触发了setState等操作,Vue 3会标记组件为"dirty",表示需要重新渲染。

  2. 生成新虚拟DOM:Vue 3会生成新的虚拟DOM树,这是一个内存中的虚拟表示,用于描述要渲染的实际DOM结构。新虚拟DOM是基于组件的状态和数据生成的。

  3. Diff算法比对:Vue 3会将新虚拟DOM与旧虚拟DOM进行比对,以找出差异。这个比对过程是一个深度递归的过程,从根节点开始逐层比对。

  4. Patch(打补丁):在比对的过程中,Vue 3会标记出需要进行实际DOM操作的节点,这些标记包括新增节点、删除节点、替换节点和更新节点的操作。这些操作被称为"打补丁",Vue 3会将这些补丁操作记录下来。

  5. 批量更新:Vue 3会批量处理补丁操作,而不是立即执行。这有助于减少DOM操作的次数,提高性能。

  6. 实际DOM更新:在批量处理完补丁操作后,Vue 3会将这些操作应用于实际的DOM树,使其与新虚拟DOM树保持一致。这包括插入新节点、删除不需要的节点、更新已有节点的属性等。

  7. 更新组件状态:Vue 3会更新组件的状态,确保与新虚拟DOM树保持一致,以便下一次渲染时可以使用新状态生成新虚拟DOM。

  8. 生命周期钩子触发:在更新完成后,Vue 3会触发适当的生命周期钩子,如beforeUpdateupdated,以便开发者可以执行一些附加的操作。

总的来说,Vue 3的diff算法通过虚拟DOM比对,找出需要变更的部分,然后批量更新实际的DOM,以保持UI与数据的一致性。这个算法在性能上进行了多项优化,例如静态树提升、PatchFlag、缓存策略等,以提供更快的渲染速度和更高的效率。这也使Vue 3成为一个强大的前端框架,适用于各种规模的应用。

vuex 刷新后数据会丢失,除了把数据放本地存储外,你还知道其他什么方法吗?

保持 Vuex 数据在页面刷新后不丢失的方法之一是将数据保存在本地存储中(如LocalStorage或SessionStorage)。这是一种常见的方法,但还有其他一些方法可以考虑:

  1. 使用持久化插件:Vuex支持使用一些第三方插件来实现数据的持久化。例如,vuex-persistedstate插件可以帮助将Vuex状态保存在本地存储中,并在页面刷新后恢复状态。

  2. 使用Cookie:你可以使用Cookie来存储一些小型的状态数据,尤其是用户会话信息。Cookies在浏览器中是持久的,但容量有限。

  3. 服务端渲染:如果你的应用是服务端渲染(SSR)的,那么Vuex的状态通常在服务端和客户端之间共享,不容易丢失。

  4. 使用IndexedDB:IndexedDB是浏览器的数据库系统,可以用来存储大量数据。虽然它比LocalStorage复杂,但可以存储大量数据并支持离线操作。

  5. 自定义持久化方案:你可以实现自定义的持久化方案,将Vuex状态保存在服务器上或其他外部存储中。这需要更多的工作,但可以满足特定需求。

无论采用哪种方法,都需要考虑数据的安全性和隐私,特别是敏感信息。还要谨慎处理数据的版本控制,以避免与缓存和更新相关的问题。根据你的应用需求和架构,选择最合适的方法来保存Vuex数据以保证数据在页面刷新后不丢失。

2.vue3 怎么实现懒加载?

懒加载的好处:懒加载后,当用户首次访问某个路由时,相关组件会在那个时刻加载,而不会在应用程序启动时加载,从而提高初始加载速度。

配置路由:首先,你需要使用 Vue Router 配置你的路由。确保你已经安装了 Vue Router。

import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
component: () => import('./views/Home.vue') // 异步加载 Home 组件
},
{
path: '/about',
component: () => import('./views/About.vue') // 异步加载 About 组件
},
// 其他路由配置
]
});

export default router;

Vue2 为什么要对数组的常用方法进行重写?

Vue 2 之所以对数组的常用方法进行重写,主要是为了实现数据的响应性,以便在数据发生变化时能够自动触发视图的更新。这是 Vue 的核心特性之一,即数据驱动视图,通过建立数据和视图之间的关联,实现了双向绑定。

Vue 2 使用了一个名为"响应性系统"的机制,它依赖于数据的 getter 和 setter 方法,以便在访问或修改数据时能够捕获这些操作,从而实现依赖追踪和自动更新视图。在这个系统中,数组的常用方法(如 push、pop、shift、unshift、splice、sort、reverse)需要被重写,以便在执行这些操作时,能够通知 Vue 哪些数据发生了变化。

具体来说,Vue 2 对数组的重写包括以下关键步骤:

  1. 劫持数组方法:Vue 2 重写了数组的常用方法,例如 pushpopshift 等,以捕获这些方法的执行。这些重写的方法会先执行原始的数组操作,然后通知 Vue 数据已变更。

  2. 派发通知:当数组方法执行时,Vue 2 会派发通知,告诉依赖于该数组的视图需要更新。这使得 Vue 能够自动追踪依赖关系,确保视图与数据的同步。

  3. 深度观察:Vue 2 还支持深度观察数组内部元素的变化,即当数组元素是对象时,修改对象属性也会触发视图更新。

虽然这种重写数组方法的方式在 Vue 2 中实现了响应式数据,但它也存在一些局限性,如无法捕获直接通过索引修改数组元素的操作。因此,Vue 3 引入了 Proxy-based 响应性系统,可以更灵活地处理数组和对象的变化,无需对数组方法进行重写。这使得 Vue 3 在数组方面的性能和功能上有一些改进。

4.vue的双向数据绑定的原理

vue的双向数据绑定的原理

4.vuex

未总结

vue为什么data属性是一个函数而不是一个对象

  1. 数据隔离:每个 Vue 组件实例需要维护自己的数据状态,如果 data 是一个对象,那么不同组件实例共享相同的数据对象,这可能导致状态污染和不可预测的行为。通过将 data 定义为一个函数,每个组件实例都会返回一个独立的数据对象,从而保持了数据的隔离。
  2. 初始化数据:data 函数返回的数据对象会在组件实例化时被深度复制,以确保每个组件实例都有自己的初始数据副本。

Vue 组件是可以复用的,如果多个组件实例都共享一个数据对象,那么在一个组件中修改数据可能会影响到其他组件,这是不合理的。通过将 data 定义为一个函数,你可以确保每个组件都有自己的数据副本,从而实现组件的复用性。

5.vue的生命周期有哪些,data里面的数据初始化是在哪个阶段

vue的生命周期有哪些

表面上看

  • 数据初始化通常发生在 created 钩子中。在这个阶段,组件实例已经创建,数据已经初始化,但模板编译和挂载阶段还未开始。你可以在 created 钩子中访问和操作组件的 data 数据。
  • 在 Vue.js 3.x 中,对应的初始化数据的阶段也在 setup 函数中,通常在 onBeforeMount 钩子之前。

源码的阶段看: 例子

在构建vnode前执行,

# 此时调用reactive--createReactiveObject创建一个代理对象并返回:方便以后触发track
start响应式=>setup() color:chartreuse

--1:组件挂载前 onBeforeMount-->

# 开始调用code-->vnode
=vnode-构建:start-->调用Ast生成的render函数
vnode-构建:b-->createBaseVNode返回值

# 递归调用patch构建之后
--2:组件挂载后 onMounted-->

6.vue的自定义指令用过没

在生成 ast 语法树时,遇到指令会给当前元素添加 directives 属性

通过 genDirectives 生成指令代码

在 patch 前,将指令的钩子提取到 cbs 中,在 patch 过程中调用对应的钩子

当执行 cbs 对应的钩子时,调用对应指令定义方法

vue 3 的自定义指令是通过 Vue 3 的插件系统来实现的,其源码原理涉及到 Vue 3 的编译器和运行时的协作。以下是自定义指令的源码原理概述:

  1. 自定义指令的注册:
    • 开发者使用 app.directive 方法来注册自定义指令。这个方法将自定义指令的名称和一个包含钩子函数的对象作为参数。
  2. 编译器:
    • 在模板编译阶段,编译器会识别和处理模板中的自定义指令。当编译器遇到使用自定义指令的元素,它会生成相应的渲染函数代码。
  3. 渲染函数:
    • 生成的渲染函数代码包括指令的处理逻辑。这个逻辑负责在运行时应用指令到元素上。
  4. 运行时:
    • 在组件实例化并挂载到 DOM 之后,Vue 3 运行时会执行渲染函数代码。当渲染函数执行时,它会检查元素是否有绑定了自定义指令,然后执行相应的钩子函数。
  5. 钩子函数:
    • 自定义指令对象中的钩子函数(如 mounted、beforeUpdate、unmounted 等)会在合适的时机被调用,以实现自定义指令的具体行为。例如,mounted 钩子会在元素被挂载到 DOM 后执行,用于设置元素的初始状态。

场景:

  1. 自定义输入验证:你可以创建自定义指令来验证用户的输入,例如,只允许输入特定格式的电话号码或电子邮件地址。
  2. 懒加载图片:创建一个自定义指令,以便在图片进入可视窗口时再加载它们,以提高性能。
  3. 无限滚动:你可以创建一个自定义指令,当用户滚动到页面底部时,自动加载更多内容。

方法1:

<template>
<div>
<input v-limit-length="11" v-model="inputValue" />
</div>
</template>

<script>
export default {
data() {
return {
inputValue: ''
};
},
directives: {
'limit-length': {
// 自定义指令的定义
mounted(el, binding) {
const maxLength = binding.value || 11;

el.addEventListener('input', function () {
if (el.value.length > maxLength) {
el.value = el.value.slice(0, maxLength); // 截断输入值
}
});
}
}
}
};
</script>

方法1: 在<script setup>中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令。在上面的例子中

<script setup>
// 在模板中启用 v-focus
const vFocus = {
mounted: (el) => el.focus()
}
</script>

<template>
<input v-focus />
</template>

方法2:

const app = Vue.createApp({});

app.directive('my-directive', {
beforeMount(el, binding, vnode) {
// 在元素挂载到 DOM 之前执行
// el: 指令绑定的元素
// binding: 包含指令的信息,如传递给指令的参数和修饰符
// vnode: 虚拟节点
},
mounted(el, binding, vnode) {
// 元素挂载到 DOM 之后执行
},
beforeUnmount(el, binding, vnode) {
// 在元素销毁之前执行
},
unmounted(el, binding, vnode) {
// 元素销毁之后执行
}
});

<template>
<div v-my-directive="directiveValue">Custom Directive Example</div>
</template>

Vue 的 nextTick 是怎么实现的?

Vue 的 nextTick 方法用于在 DOM 更新后执行回调函数,它可以用于在 Vue 更新视图后执行一些操作,通常是在下一个事件循环中执行,以确保在当前事件循环内的数据变化已经应用到 DOM 上。

nextTick 的实现原理主要涉及了 JavaScript 中的事件循环机制。下面是 Vue 的 nextTick 如何实现的简要过程:

  1. Vue 使用宏任务(Microtasks)和微任务(Microtasks)的机制,宏任务通常在事件循环中排在微任务之后执行。

  2. 在 Vue 中,nextTick 方法会先检查浏览器是否支持 Promise 对象,如果支持,它会首先使用 Promise 来创建一个微任务。

  3. 如果浏览器不支持 Promise,则会回退到使用 MutationObserver(如果支持)来模拟微任务。MutationObserver 是浏览器提供的用于监视 DOM 变化的机制。

  4. Vue 会将要执行的回调函数添加到微任务队列中,然后等待下一个事件循环执行。

  5. 当浏览器的当前任务(宏任务)执行完毕后,它会去执行微任务队列中的回调函数,这时 nextTick 的回调函数就会被执行。

  6. nextTick 的回调函数中通常包含了需要在 DOM 更新后执行的操作,例如读取更新后的 DOM 元素尺寸或执行一些其他任务。

总之,nextTick 利用了 JavaScript 异步机制,确保回调函数在 DOM 更新后执行,从而可以安全地操作更新后的 DOM。这对于处理 Vue 中的数据更新和视图更新非常有用,特别是在需要进行一些与 DOM 相关的操作时。在不同浏览器和环境中,nextTick 的实现可能会有所不同,但其核心思想是使用微任务或 MutationObserver 来实现 DOM 更新后的回调。