文章目录
- 一、针对简历提到的知识点
- 一、Vue相关知识点
-
-
- 1.vue3和vue2区别
- 1-1.父子组件生命周期执行顺序
- 1-2.**引伸:最早可以执行异步操作的地方created**
- 2.爷传孙的几种方式
- 3.Vue中组件通信的方式
- 4.computed与watch方法区别
- 5.插槽有哪些?
- 6.Vue2中的data()为啥是函数不是对象
- 7.bable底层是如何将es6语法转为被低版本浏览器识别的?
- 8.webpack编译流程
- 8-1.vite相较于webpack做了哪些提升?
- 8-2.Vite 用Rollup 进行打包,那webpack用什么打包的?
- 8-3.如何配置Webpack以实现模块打包、代码分割、热更新等功能?
- 9.**vue3中hooks**
- 10.vue2中的this指什么?
- 11.Vue 常用的修饰符有哪些应用场景
- 12.事件委托原理与优缺点
- 13.vuex和pinia区别
- **13-1.vuex核心概念**
- **13-2.Vuex 底层实现的简化概述:**
- 13-3.全局获取 Vuex 的数据
- **13-4.Vuex 中commit与dispach区别:**
- 15.keep-alive的使用场景跟原理分析
- 16.按钮权限你是怎么实现的?如何封装自定义指令与注册使用?
- 17.自定义指令如何封装与使用。
- 18.如何实现多语言,当其他人修改权限不动配置文件需要怎么做?
- 19.前端如何自动化部署?
- 19-1.Jenkins 进行构建的基本步骤?
- 20.vue可以实现响应式原理,为什么react不可以?
- 21.vue.delete和delete有什么区别?
- 22.router和route的区别?
- 23.对于一个大型的项目后期加入国际化该怎么快速改造?
- 二、 js知识点
-
- js运行机制
- 1.闭包
- 2.垃圾回收机制
- 3.v8垃圾回收机制
- 引申1:js数据类型有哪些?怎么判断
- 4.堆和栈,队列
- 5.js的数据结构有哪些
- 6.数组常用的方法:
- 引伸1:如何将数组中的第二个元素和第四个元素调换位置
- 引伸2:数组去重的几种实现方法:
- 引伸3:for,foreach,map区别
- 引伸4:for, for...in, for...of, forEach,map....数组的循环遍历方法中,哪些可以中断循环哪些不可以?
- 引伸5:如何保留小数点2位?
- 7.原型和原型链
- 8.浅拷贝深拷贝
- 10.虚拟DOM,谈谈对vdom的理解。
- 11.模板编译
- 12.js事件循环
- 13.this.$nextTick的原理
- 14.v-if和v-for优先级
- 15.作用域与作用域链
- 16.点击空白地方隐藏弹框的几种实现方式
- 17.http状态码:
- 18.浏览器以及工作原理,一个url在浏览器输入到呈现出来发生了什么?
- 19.前端中 JS 发起的请求可以暂停吗?
- 19.在网页加载过程中,如果某些外部链接(例如外部脚本、样式表、图片或其他资源)加载缓慢或完全阻塞怎么办?
- 20.call ,apply和bind方法
- 21.ES6新特性
- 22.script标签上有什么属性?
- 三、ts相关知识
- 四、 性能优化
- 五、计算机网络安全
- 六、网络请求
- 七、css
- 八、代码规范
- 九、工程化与工具
- 十、uniapp相关
- 十、微信小程序原生开发相关
- 十一、Electron相关知识
- 十二、项目难点亮点
- 流程视图
- 案例片段
- 十三,关于游戏开发相关问题
-
- 技术基础
- 1. **请描述一下你对HTML5、CSS3和JavaScript的理解。**
- 2. **你如何优化网页性能?具体有哪些方法?**
- 3. **解释一下什么是WebGL,它在游戏开发中的作用是什么?**
- 4. **你是如何处理跨浏览器兼容性问题的?**
- 5. **请解释一下事件冒泡和事件捕获的区别。**
- 游戏开发知识
- 6. **你在游戏开发中使用过哪些框架或库?**
- 7. **你能解释一下游戏循环的概念吗?它是如何工作的?**
- 8. **你是如何处理游戏中的碰撞检测的?**
- 9. **如何实现一个简单的动画系统?**
- 前端框架与工具
- 10. **你熟悉哪些前端框架?**
- 11. **你是如何管理状态的?**
- 12. **你能谈谈Webpack的工作原理吗?如何进行配置优化?**
- 性能与用户体验
- 13. **在游戏开发中,你是如何保证流畅的游戏体验的?**
- 14. **你如何测试你的代码以确保其质量和性能?**
- 15. **对于移动设备上的游戏,你如何处理触摸事件?**
- 设计模式与架构
- 16. **你能举出一些常用的设计模式,并说明它们在游戏开发中的应用吗?**
- 17. **你是如何组织大型项目的文件结构和代码架构的?**
- 项目经验
- 18. **请分享一个你参与过的最复杂的前端项目。**
- 19. **在过去的项目中,你是如何解决遇到的最大挑战的?**
- 20. **你有没有为现有项目引入新技术的经历?结果如何?**
- 团队合作与沟通
- 21. **当团队成员之间出现分歧时,你是如何处理的?**
- 22. **你是如何与后端开发人员协作的?**
-
前端目前主流框架:vue,react,angular
前端各大主流UI库:设计风格和适用场景如下
Element UI:适用于Vue开发PC端项目,以其清晰的文档和友好的UI受到开发者欢迎。
Ant Design(阿里蚂蚁金服团队):在React中地位显著,提供大量高质量的React组件,适用于企业级中后台产品设计。针对vue和react有2个版本(ant-design-vue,AntDesign of React)
antd。是Ant Design的React实现版本,专注于提供高质量的React组件库。Ant Design(AntD)
常用的: import Antd from ‘ant-design-vue’;
Vant:轻巧可靠的移动UI组件库,适用于Vue2和Vue3,支持微信和支付宝小程序
AntDesignMobile:AntDesign的移动版本,适用于移动终端产品的研发,提供40+基本组件。
Bootstrap:适用于快速构建响应式布局和网格系统,虽然主要用于Web开发,但其Flexbox布局和丰富的资源使其在前端开发中仍有一定影响力。
Vuetify:渐进式JavaScript框架|Vue.js,微服务架构,采用Material Design风格,适用于需要避免界面同质化的项目。
一、针对简历提到的知识点
1.Prettier 统一格式化代码,
2. ESLint(检测代码)、
3.Stylelint (格式化样式),
4.commitint等代码校验规范提升代码质量。
提交代码(提交前会自动执行 lint:lint-staged 命令)
pnpm commit
江门防呆喷码系统:
该项目难点:要求一个月完成项目上线,并且没有产品原型,需出差去现场对接。需要同时开发PC端和APP。
亮点:项目封装了强大的 ProTable 组件,在一定程度上提高开发效率。另外本项目还封装了一些常用组件、Hooks、指令、动态路由、按钮级别权限控制等功能。
项目功能 🔨
*使用 Vue3.4 + TypeScript 开发,单文件组件<script setup>
*采用 Vite5 作为项目开发、打包工具(配置 gzip/brotli 打包、tsx 语法、跨域代理…)
*使用 Pinia 替代 Vuex,轻量、简单、易用,集成 Pinia 持久化插件
*使用 TypeScript 对 Axios 整个二次封装(请求拦截、取消、常用请求封装…)
*基于 Element 二次封装 ProTable 组件,表格页面全部为配置项 Columns
*支持 Element 组件大小切换、多主题布局、暗黑模式、i18n 国际化
*使用 VueRouter 配置动态路由权限拦截、路由懒加载,支持页面按钮权限控制
*使用 KeepAlive 对页面进行缓存,支持多级嵌套路由缓存
*常用自定义指令开发(权限、复制、水印、拖拽、节流、防抖、长按…)
*使用 Prettier 统一格式化代码,集成 ESLint、Stylelint 代码校验规范
*使用 husky、lint-staged、commitlint、czg、cz-git 规范提交信息
一、Vue相关知识点
1.vue3和vue2区别
1.生命周期
2.多根节点
3.组合式API(composition api)
4.异步组件
5.响应式原理
6.teleport
7.虚拟DOM
8.事件缓存
9.differ算法优化
10.打包优化
11.ts支持
https://www.cnblogs.com/caix-1987/p/17290009.html1.响应式原理+性能提升:
vue2中是通过Object.defineProperty实现响应式,而Vue3用proxy替代了vue2的响应式原理。原因是vue2中对于添加未声明的属性(不可枚举)或直接修改数组(默认只能监听对象的属性变化,而不是数组元素的变化)是不会触发视图更新的。
Vue 2.x 使用了基于 Object.defineProperty 的响应式系统,它在初始化阶段遍历对象的属性,通过
Object.defineProperty 将每个属性转化为 getter 和 setter,以便在属性访问和修改时触发更新。
对数组的每个元素进行观察会带来性能开销,尤其是在大型数组上。所以在 Vue 2 中,对数组使用
push、pop、shift、unshift、splice
等方法都会触发页面更新。然而,直接通过索引修改数组元素的值是不会触发更新的。其他的通过Vue.set(vm.someObject,
‘newProperty’, ‘New Value’); Vue 3
在内部实现上进行了大量优化,提高了渲染速度和性能,同时降低了内存占用。这得益于对响应式系统的改进,如使用 Proxy 替代
Object.defineProperty
来实现响应式,并采用了静态提升技术以提高渲染性能。此外,还新增了对最长递归子序列的计算,用于最小化修改偏移量的计算,以及对静态标记的支持。
引伸:Vue 的双向绑定的底层原理是什么?
简单点的说就是通过object.defineproperty()去劫持各个属性。object.defineproperty进行数据劫持再结合观察者模式,即发布订阅来实现数据双向绑定,这也是vue2的响
应式原理,而vue3增加了一种响应式实现方法数组和对象使用的proxy,而基础数据类型还是用object.defineproperty实现。
响应式原理底层的实现逻辑是什么:1响应式转化,当vue组件实例创建时候,它会遍历data对象所有的属性值,
对于每个属性用vue.defineproperty()将其转化为getter和settet.当浏览器读取的时候会触发getter,当设置对象属性新值的时候setter会被调用,还会触发通知所有订阅了改属性变化的watcher.它接受到通知后,会重新计算视图需要更新的部分,进而引发dom更新。2.setup语法糖: vue3采用了组合式 API ,为了使用组合式API,我们需要一个入口,在vue3组件中,称之为setup语法糖。注意:在setup()中定义的数据,方法都需要 return 返回出去,不然会报错。
引伸:setup放组件标签里面和外面有区别吗?
1当 setup 函数放在组件标签内时,因为 setup 在组件实例创建之后执行。上下文: setup 函数内可以访问到组件实例,this
指向组件实例。 2将 setup 函数放在export default外时,需要显式导入 defineComponent
并使用它包裹组件配置。import { defineComponent } from ‘vue’; 3写在script标签上,setup
函数的上下文(this)不指向组件实例,而是被封装到响应式上下文中。3.编码方式的改变:组合api Vue 3 引入了 组合式Composition API,它允许开发者更好地组织和复用逻辑代码,从而提高代码的可维护性。精确地按需引入,减少打包体积
1.ref 用于创建一个响应式对象,通常用于包装基本类型的数据。
2.reactive 用于创建一个响应式对象,可以包含嵌套的属性(引用数据)。
3.computed 用于创建计算属性,可以基于响应式数据进行计算。
4.watch 用于监听响应式数据的变化,并执行回调函数。
5.toRefs 用于将响应式对象转换为包含 ref 的普通对象,方便在解构时使用。
toRefs 函数用于将响应式对象转换为包含 ref 的普通对象。这在解构响应式对象时非常有用,因为解构会使响应式对象的属性失去响应式特性。使用 toRefs 可以保留解构后的对象属性的响应性。6.watchEffect 用于创建一个响应式数据的副作用,类似于 watch,但不需要指定要监听的具体属性。
7.onMounted、onUnmounted、onUpdated 等,用于处理组件的生命周期。
8.provide 和 inject
9.directive自定义指令(后台管理系统按钮控制)
10.mixin混入
11.createApp()创建一个应用实例,类似new vue()
12.nextTick()4.TypeScript支持:
Vue 3 对于 TypeScript 提供了一致的完整类型定义,这对于大型项目尤其有用,因为它可以帮助确保代码的一致性和错误检测。
vue2从版本2.4.0开始,它也增加了对 TypeScript 的支持。
5.新特性的支持: Vue 3 支持更多的新特性,如片段 (Fragments多根节点)、Teleport(传送门)、Suspense
(占位符),双向绑定数据请阅读官网的ref,reactive,torefs等 ,这些都是为了提供更好的开发体验和更高的灵活性。1.vue3可以实现多根节点Fragments不受限制,vue2是单根节点,外面只能用一个div嵌套
2.Teleport 允许你在 DOM 树的其他部分渲染组件的内容。这在处理模态框、弹出框等需要在 DOM 结构中移动的场景时非常有用。比如将弹框传送到body下面
3.Suspense 允许你在异步组件加载过程中提供一个占位符,直到异步组件加载完成。这对于处理异步组件的加载状态非常有用。6.生命周期钩子的变化: Vue 3 的生命周期钩子有所调整,例如
beforeCreate
现在是在setup
函数中执行的,而不是像 Vue 2 那样在实例初始化和数据观测之前。此外,还有activated
和deactivated
两个新的生命周期钩子,它们分别在组件激活和解. Vue3 在组合式API(Composition
API,下面展开)中使用生命周期钩子时需要先引入,而 Vue2 在选项API(Options API)中可以直接调用生命周期钩子,如下所示。
setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地去定义。
1-1.父子组件生命周期执行顺序
创建阶段(Mounting)
父组件 的 beforeCreate 钩子触发。
父组件 的 created 钩子触发。
父组件 的 beforeMount 钩子触发。
子组件 的 beforeCreate 钩子触发。
子组件 的 created 钩子触发。
子组件 的 beforeMount 钩子触发。
子组件 的 mounted 钩子触发。
父组件 的 mounted 钩子触发。
在这个阶段,Vue 首先初始化父组件,然后是子组件。每个组件在其挂载到 DOM 之前都会经历一系列的生命周期钩子。
更新阶段(Updating)
当数据变化引起视图更新时,更新过程会按照以下顺序进行:
父组件 的 beforeUpdate 钩子触发。
子组件 的 beforeUpdate 钩子触发。
子组件 的 updated 钩子触发。
父组件 的 updated 钩子触发。
在这个阶段,Vue 首先检测父组件的变化,然后检查子组件是否有需要更新的地方。只有当子组件的状态依赖于父组件的数据时,才会触发子组件的更新钩子。
当组件被销毁时,销毁过程会按照以下顺序进行:
子组件 的 beforeDestroy 钩子触发。
子组件 的 destroyed 钩子触发。
父组件 的 beforeDestroy 钩子触发。
父组件 的 destroyed 钩子触发。
在销毁阶段,Vue 先销毁子组件,然后再销毁父组件。这确保了所有子组件资源都被正确清理之后,父组件才被销毁。
1-2.引伸:最早可以执行异步操作的地方created
created 是用于在组件创建后立即请求数据的好地方,特别是在你需要数据来初始化组件的情况下。
mounted 更适合于那些需要确保DOM 已经准备好的情况,或者你想让用户界面首先展示出来的情形。
watch 适用于当数据依赖于其他状态或参数变化时,可以保证数据总是最新的。
1.created 钩子 适用场景:如果你需要在组件实例创建完成后立即发起一个异步请求来获取数据,并且这个数据对于初始化组件是必要的,那么可以在 created
钩子中发起请求。 优点: 组件还未挂载到 DOM 上,因此不会触发不必要的重渲染。
如果你需要在组件创建时就准备好数据,这通常是最早可以执行异步操作的地方。
2. mounted 钩子 适用场景:当你需要确保组件已经挂载到 DOM 上后再发起请求(例如,依赖于 DOM 元素的操作),或者你希望用户界面先显示出来再加载数据时,应该使用 mounted 钩子。 优点: 组件已经完全挂载,可以访问真实的 DOM
节点。 用户界面已经呈现给用户,可能提供更好的用户体验(如显示加载指示器)
2.爷传孙的几种方式
在Vue中,可以通过爷(祖父)组件将数据传递给孙子组件的方式通常有两种:通过中央事件总线(Event Bus) 和 通过Provide / Inject。
- 通过中央事件总线(Event Bus):
使用场景: 适用于简单的、非常频繁的通信。当组件层级较深,或者需要在不直接关联的组件之间传递数据时,事件总线是一种简便的方式。
全局性: 事件总线是一个全局实例,可以在整个应用中使用,但这也可能导致不同组件之间的耦合性增加。
2. 通过Provide / Inject
使用场景: 适用于需要在组件层级中传递数据,而且通常用于祖父-父亲-子孙这样的层级结构。provide 和 inject 提供了更直接的组件层级关系,适用于复杂的数据传递场景。
局部性: Provide / Inject 的数据传递是局部的,只在 provide 组件的子孙组件中可用。这有助于减少全局状态,使得组件之间的通信更加可控。
3.Vue中组件通信的方式
Vue.js 中组件间通信可以通过多种方式实现,以下是 Vue 组件通信的六种常见方法:
Props 和 Events:
- Props:父组件向子组件传递数据的主要方式。在子组件中声明props选项以接收来自父组件的数据。
<!-- 父组件 --> <child-component :my-prop="parentData"></child-component> <!-- 子组件 --> <script> export default { props: ['myProp'], // ... } </script>
- Events (自定义事件):子组件通过
$emit
触发事件向父组件发送信息。<!-- 子组件内部 --> <button @click="notifyParent">点击通知</button> <script> export default { methods: { notifyParent() { this.$emit('update-data', someValue); } }, // ... } </script> <!-- 父组件 --> <child-component :my-prop="parentData" @update-data="handleUpdate"></child-component>
$refs:
- 当需要直接操作子组件实例或调用其方法时,可以使用
ref
在父组件中引用子组件,并通过$refs
访问它。<!-- 子组件 --> <child-component ref="childRef"></child-component> <!-- 父组件 --> <script> export default { mounted() { this.$refs.childRef.someMethod(); }, // ... } </script>
Vuex:
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。当多个组件之间存在复杂的共享状态时,可以通过创建和操作 Vuex store 来进行跨组件通信。
Event Bus(全局事件总线):
- 创建一个全局事件总线(通常是新建一个 Vue 实例),允许任何组件通过触发和监听该实例上的事件来进行通信。
provide/inject:
- Vue 提供了一种依赖注入的方式,允许祖先组件将数据提供给任意深度的后代组件,而无需在每一个层级手动传递。这对一些大型应用中组织多级嵌套组件间的通信非常有用。
a t t r s / attrs/ attrs/listeners:
$attrs
包含了父作用域中未绑定到 prop 的特性绑定(attribute bindings)。$listeners
包含了父作用域中的所有 v-on 事件监听器。这两个属性可以帮助实现非父子组件之间的通信,通常用于封装组件库的情况。总结起来,在实际开发中,应根据项目需求和组件间的关系选择合适的通信方式。对于简单场景,通常首选 Props 和
Events;对于复杂状态管理,则可能需要引入 Vuex;对于不相关的组件间通信或者跨层级通信,可以考虑 Event Bus 或
provide/inject。
4.computed与watch方法区别
computed 计算属性: 用途: 用于根据其他响应式数据的变化计算出一个新的值,并将其缓存,只有依赖的响应式数据发生变化时,计算属性才会重新计算。
watch 观察者: 用途: 用于观察指定的数据,当数据发生变化时执行一些操作。可以用于执行异步操作、复杂逻辑等。 语法: 定义在 watch 对象内,可以监听一个数据或多个数据。 在选择使用 computed 还是 watch
时,主要取决于你的需求。如果需要根据其他数据计算得到一个新值,而且希望这个值具有缓存特性,可以使用
computed。如果需要在数据变化时执行一些操作,比如异步请求或者复杂的逻辑,可以使用 watch。通常情况下,computed
更适合处理派生数据(派生数据是指从应用状态或其他数据中计算而来的新数据),而 watch 更适合处理副作用。
5.插槽有哪些?
在Vue.js中,插槽(Slot)是一种用于在父组件中向子组件传递内容的机制。插槽则是用来传递 DOM 结构或片段。插槽允许你定义一个占位符,然后在父组件中填充具体的内容。Vue.js提供了多种类型的插槽,包括默认插槽、具名插槽、作用域插槽等。以下是一些常见的插槽类型:
<!-- ChildComponent.vue -->
<template>
<div>
<h2>子组件内容</h2>
<slot name="header"></slot> <!-- 具名插槽 -->
<slot></slot> <!-- 默认插槽 -->
<slot :data="childData"></slot> <!-- 作用域插槽 -->
</div>
</template>
插槽的意义如下:
1可复用性: 插槽允许你在父组件中注入内容,这使得组件更加灵活和可复用。通过插槽,你可以在不修改组件内部代码的情况下,向组件中插入不同的内容,从而实现更广泛的复用性。
2布局控制: 插槽允许父组件控制子组件的布局和结构。父组件可以通过插槽来定制子组件的外观,使得组件在不同的上下文中可以以不同的方式呈现。
3灵活的组件结构:
插槽允许你在组件内定义占位符,并在父组件中填充内容。这种机制使得组件可以更容易地适应不同的设计和布局需求,从而提高了组件的灵活性。4分发内容:
插槽允许子组件向父组件分发内容,这在某些情况下非常有用。例如,子组件可以通过插槽将某些状态信息传递给父组件,使得父组件能够动态地响应子组件的状态变化。5提高可维护性:
插槽可以提高代码的可维护性。通过将组件的结构和样式分离,你可以更容易地理解和维护代码,因为父组件和子组件的关注点得到了清晰的分离。
6.Vue2中的data()为啥是函数不是对象
在 Vue 2 中,data 选项为函数而不是对象的设计是为了解决组件实例之间数据共享的问题。Vue的组件是可以复用的,当多个组件实例共享同一个对象时,如果该对象是引用类型,就可能导致一个组件的状态变化影响到其他组件。
使用函数返回一个新的对象,确保每个组件实例都拥有独立的数据对象。这样,每个组件实例的 data 都是一个独立的数据作用域,不会互相影响。当组件实例被创建时,data 函数会被调用,返回一个新的数据对象。
总之:如果是对象的话,当对象是引用类型数据他是一个地址指向另一个指针,当数据发送改变的时候会影响整个对象的属性发生改变。而作为一个函数被其他对象调用的时候,就各自有自己的作用域,修改属性不会对其他地方造成影响
7.bable底层是如何将es6语法转为被低版本浏览器识别的?
安装 Babel 及其相关的插件和预设,以及 Babel Loader:
npm install --save-dev @babel/core @babel/preset-env babel-loader
这里 @babel/core 是 Babel 的核心库,@babel/preset-env 是一个智能预设,可以根据你指定的目标环境自动决定需要转译的 JavaScript 特性。babel-loader 是 Webpack 专用的 loader。
Babel 是一个用于 JavaScript 编译的工具,它的主要功能之一是将 ECMAScript 2015+(ES6+)的代码转换为向后兼容的 JavaScript 代码,以便在低版本的浏览器中运行。Babel 实现这个转换的方式主要涉及两个步骤:
解析(Parsing): Babel 使用解析器来将输入的 ES6+ 代码解析成抽象语法树(Abstract Syntax
Tree,AST)。AST 是一种树状结构,它以 JavaScript 代码的形式表示代码的抽象结构,方便后续的处理。转换(Transformation): 一旦得到 AST,Babel 将应用一系列的转换插件,这些插件会遍历并修改 AST,将 ES6+
语法转换为更低版本的 JavaScript
语法。每个插件通常专注于一个特定的语法转换。例如,有插件用于将箭头函数转换为普通函数、将模板字符串转换为普通字符串等。Babel
的转换过程并非一成不变的,它是高度可配置的,可以根据用户的需求启用或禁用不同的插件。这使得开发者可以根据项目的特定要求进行定制化的转换。最终,Babel 将修改后的 AST 转回为 JavaScript
代码。这个生成的代码就是经过转换后的、适用于低版本浏览器的代码。这整个过程使得开发者能够使用最新的 ECMAScript
特性,同时确保其代码能够在不同浏览器中运行,提高了代码的可移植性和兼容性。
8.webpack编译流程
- Webpack是一个模块打包工具,它会递归地解析依赖关系,并将所有模块打包成一个或多个bundle。
- 通过配置entry、output、loader和plugin,可以自定义打包过程。
- 优化方法包括:使用Tree Shaking去除未使用的代码,使用Code Splitting进行代码分割,启用Source Maps进行调试,以及使用缓存提高构建速度。
Webpack 的编译流程可以简化概括为以下几个关键步骤:
初始化参数 (Initialization)
- 读取配置:Webpack 从配置文件(如
webpack.config.js
)和命令行参数中读取配置信息,并将其合并为最终的配置对象。- 参数校验与补充:使用诸如
yargs
等工具处理CLI参数,形成完整的配置项,包括入口(entry)、输出(output)、loader、插件等。初始化编译器 (Compiler Creation)
- 根据上一步骤得到的配置,初始化一个
Compiler
对象,加载所有配件的插件,这个对象是整个编译过程的核心,负责管理和协调编译任务。- 注册插件:加载所有配置的插件,并调用插件的
apply
方法,这样插件就能监听并参与到编译生命周期的各个阶段。编译阶段 (Compilation)
- 寻找入口文件:根据配置中的
entry
属性,Webpack 找到应用程序的入口点。- 构建模块依赖图:从入口文件开始,Webpack 递归地解析每个模块及其导入的依赖关系,形成一个模块依赖图。
- 模块转换:通过Loader,对每个模块的内容进行转换,如将ES6模块转换为CommonJS模块,将SCSS转换为CSS,将TypeScript转换为JavaScript等。
- 优化和处理资源:Webpack在编译过程中,会执行一系列优化操作,例如Tree Shaking、Scope Hoisting等,并且通过插件和loader处理图片、字体等资源。
输出阶段 (Output)
- 分割代码块(Chunks):根据模块的依赖关系和SplitChunksPlugin等配置,将模块分组成多个代码块(chunks),比如common
chunk、vendor chunk等。- 生成最终资源:Webpack依据输出配置,将每个chunk转换成单独的文件(如bundle.js),同时生成manifest数据以便按需加载和映射关系。
- 写入文件系统:将编译后的资源输出到指定的文件夹中。
后期处理与插件介入
- 在整个编译过程中,插件可在不同阶段的生命周期钩子中执行自定义操作,比如清理旧文件、生成资产清单、注入环境变量等。
总的来说,Webpack
的编译流程是一个高度可定制的过程,通过处理模块依赖关系、转化和合并资源,最终输出满足项目需求的静态资源。在整个过程中,Webpack 强大的插件系统使得开发者能够灵活地扩展和优化构建过程。
loader和plugin区别
loader主要用于转换某些类型的模块,它是一个转换器。如将ES6模块转换为CommonJS模块
plugin是插件,它是对webpack本身的扩展,是一个扩展器.比如打包优化,文件压缩等等.
8-1.Webpack是怎么进行代码压缩的,为什么比较慢?与vite有啥区别**
Webpack 使用 UglifyJS(或 Terser,取决于配置)来进行代码压缩。UglifyJS/Terser 是一个用于
JavaScript 的压缩和混淆工具,它能够删除不必要的空格、重复的代码、缩短变量名等,从而减小文件体积。下面是一些
UglifyJS/Terser 进行代码压缩的主要步骤:
1.解析(Parsing): 将源代码解析成抽象语法树(AST),这是一个树状结构,表示代码的抽象结构。2转换(Transformation): 对 AST 进行变换和优化,包括删除不必要的代码、简化表达式、提取公共部分等。
3生成(Code Generation): 将经过转换的 AST 重新生成为压缩后的 JavaScript 代码。
4混淆(Obfuscation): 可选的步骤,通过缩短变量名、替换字符串等方式,增加代码的难以阅读性。
代码压缩在 Webpack 中默认是开启的,但为什么有时候会感到比较慢呢?
原因可能包括:
代码量较大: 如果项目中有大量的 JavaScript代码需要压缩,这个过程就会相对耗时。尤其是在大型项目中,压缩工具需要处理大量的文件和代码。
混淆操作: 如果开启了混淆,混淆操作可能会引入更多的计算,导致压缩时间增加。
启用 Source Map: 如果开启了 Source Map 选项,UglifyJS/Terser
会生成与源代码映射的文件,以便调试。生成 Source Map 也需要一定的时间。CPU 资源: 压缩操作是 CPU 密集型任务,如果计算机的 CPU 资源较为有限,也可能导致压缩速度较慢。
为了提高代码压缩的速度,可以考虑以下几个方面:
使用 Terser 替代 UglifyJS(Webpack 5 默认使用 Terser)。 调整压缩配置,去除不必要的混淆操作。
确保代码结构简洁,避免不必要的冗余代码。 在开发环境中尽量减少对代码的压缩,以提高构建速度。
8-2.那Terser 有什么性能上的提升呢?
Terser 是一个用于 JavaScript 的压缩工具,是 UglifyJS 的一个分支,用于进行代码压缩和混淆。相较于
UglifyJS,Terser 在性能和功能上进行了一些改进,带来了一些性能上的提升和优势:更好的压缩率: Terser 在压缩算法方面进行了改进,对代码进行更优化的压缩,可能会产生更小的文件体积。
更快的压缩速度: Terser 在处理大型项目时可能比 UglifyJS 更快,这对于大型前端应用的构建过程是一个显著的优势。
ES6+ 支持: Terser 更好地支持 ECMAScript 2015+(ES6+)语法,包括箭头函数、模板字符串等新特性,使得现代
JavaScript 项目能够更好地受益于代码压缩。维护性: Terser 是在 UglifyJS 的基础上进行的改进,它会继续受到维护和更新,确保在将来能够适应新的 ECMAScript
标准和代码规范。Tree-shaking 的优化: Terser 在 Tree-shaking
方面进行了优化,能够更好地消除未使用的代码,减小最终生成文件的体积。总体而言,Terser 提供了更先进的压缩算法、更好的性能、更好的 ES6+ 支持和更好的维护性。在现代前端开发中,Terser
是一个被广泛使用的压缩工具,特别适用于构建过程中的代码压缩和优化。
8-3.那vite相对于webpack做了哪些性能上的提升呢
以下是 Vite 相对于 Webpack 在性能上的一些优势:
1.快速的冷启动: Vite 使用了一种基于浏览器原生 ES 模块(ESM)的开发服务器,称为 “esbuild”,Vite 利用了原生 ES 模块导入的功能,能够实现极速的冷启动和热更新速度,非常适合开发阶段使用。由于只需编译和处理当前正在编辑的文件,而不是整个项目,因此启动时间更短。
2.HMR(热模块替换)性能优化: Vite 利用了浏览器原生的模块系统,支持 HMR 的性能更好。在开发环境下,Vite 不需要像
Webpack 那样通过 WebSocket 将更新的模块推送到客户端,而是直接使用浏览器的 ESM 特性,实现更快的 HMR
8-1.vite相较于webpack做了哪些提升?
启动速度与冷启动时间:
Vite 使用原生 ES 模块导入(ESM)来提供快速的冷启动时间。它通过浏览器原生支持的 ESM 动态导入特性,使得开发服务器几乎可以立即启动。
Vite 在开发模式下不需要打包整个应用,而是按需编译单个模块,这大大减少了启动时间和热更新的时间。
Webpack 在启动时需要对整个项目进行打包,这可能需要几秒到几十秒不等,尤其是在大型项目中。
Webpack 的 HMR(Hot Module Replacement)虽然可以实现模块热替换,但在某些情况下配置和性能可能不如 Vite。热更新(HMR)效率:
- Vite 实现了高效的模块热更新,当开发者修改代码后,它只重新编译改动的部分,并通过HMR机制通知浏览器加载变更的模块,而非整体刷新页面或重新编译整个应用。
- 相比之下,Webpack 的 HMR 也会尽量做到局部更新,但在大型项目中,尤其是在处理大量依赖关系时,其热更新速度可能会慢于 Vite。
构建速度:
Vite 在生产构建时使用 Rollup 进行打包。Rollup 是一个专注于库打包的工具,生成的代码通常更小且更高效。
Vite 的构建速度相对较快,特别是在处理大型项目时。
Webpack 的构建速度取决于项目的复杂性和配置。对于大型项目,尤其是那些包含大量依赖和复杂配置的项目,构建时间可能会较长。
Webpack 提供了多种优化手段(如 Tree Shaking、代码分割等),但这些优化有时会增加构建时间。预构建依赖:
- Vite 利用了
esbuild
进行快速的预构建处理,esbuild
由 Go 语言编写,具有非常高的编译性能,能够迅速解析和转换依赖模块。- 而 Webpack 依赖 JavaScript 编写的插件体系,虽然功能强大但速度相对较慢,特别是在项目规模较大时。
开发体验:
- Vite 提供了更流畅的开发体验,减少了等待构建时间,使开发者能够更快地看到代码更改的效果。
- 因为 Vite 不依赖 CommonJS 规范,它鼓励使用 ESM 进行模块导入,进一步优化了现代浏览器的加载性能。
配置复杂度:
- Vite 的配置文件相比于 Webpack 更加简洁,尤其是对于小型到中型项目,开箱即用的体验更好。Vite 的配置文件 vite.config.js 通常比 Webpack 的配置文件更简洁;Webpack 的配置较为复杂,特别是对于复杂的项目。你需要手动配置各种插件和加载器,以满足特定的需求
打包效率:
- Vite 在生产环境中依然可以选择使用 Rollup 进行最终的优化打包,Rollup 作为 tree-shaking 性能优秀的打包器,结合 Vite 的开发环境特性,使得整体项目的构建效率也有所提高。
总结起来
Vite 适合追求快速开发体验和简化配置的项目,特别是对于中小型项目或需要快速迭代的项目。
Webpack 适合需要高度定制化和复杂配置的大型项目,以及需要广泛生态系统支持的项目。Webpack 经过多年的发展,已经非常稳定,被广泛应用于各种规模的项目中
拓展:
ESM 提供了更现代、更灵活的模块化方式,支持静态导入/导出、动态导入、严格模式、作用域隔离、循环依赖处理等特性。这些特性使得 JavaScript 代码更加模块化、可维护和高效。随着浏览器和 Node.js 对 ESM 支持的不断增强,ESM 已经成为现代 JavaScript 开发的标准模块系统。
8-2.Vite 用Rollup 进行打包,那webpack用什么打包的?
Webpack 是一个模块打包器,它将应用程序中的所有资源(如
JavaScript、CSS、图片等)视为模块,并通过依赖关系图将这些模块打包成一个或多个最终的输出文件(通常是 JavaScript
文件)。Webpack 本身就是一个完整的构建工具,它负责处理以下任务:
- 模块解析:识别不同类型的资源(如 JS、CSS、图片等),并将其转换为模块。
- 依赖分析:构建模块之间的依赖关系图。
- 代码转换:使用各种加载器(Loaders)对模块进行转换,例如将 TypeScript 转换为 JavaScript,将 SCSS 转换为 CSS 等。
- 代码优化:通过插件(Plugins)进行代码优化,如 Tree Shaking、代码分割、压缩等。
- 输出:将处理后的代码打包成最终的输出文件。
Webpack 的核心是其运行时(Runtime),它负责在运行时动态加载和执行模块。Webpack
使用自己的内部机制来处理上述所有步骤,不需要依赖其他外部打包工具。Vite
Vite 在开发模式下利用浏览器原生支持的 ES 模块(ESM)特性,提供快速的冷启动和热更新体验。但在生产构建时,Vite 使用
Rollup 进行打包。Rollup 是一个专注于库打包的工具,具有以下特点:
- Tree Shaking:Rollup 可以自动移除未使用的代码,生成更小的包。
- 代码分割:Rollup 支持代码分割,可以将代码分割成多个小包,按需加载。
- 优化:Rollup 提供了多种优化手段,如代码压缩、变量名优化等。
Vite 的生产构建过程如下:
- 模块解析:Vite 仍然使用 ESM 来解析模块。
- 依赖分析:Vite 构建依赖关系图。
- 代码转换:Vite 使用 Rollup 插件系统来处理代码转换,类似于 Webpack 的加载器。
- 代码优化:Rollup 提供了丰富的插件来优化代码,包括 Tree Shaking、代码分割、压缩等。
- 输出:Rollup 将处理后的代码打包成最终的输出文件。
总结
- Webpack:使用自己的内部机制进行模块解析、依赖分析、代码转换、代码优化和输出。它是一个完整的构建工具,不依赖于其他打包工具。尽管它依赖于插件比如插件(Plugins)进行代码优化,来扩展功能,但这些插件是 Webpack 生态系统的一部分,而不是独立的打包工具。
- Vite:在开发模式下利用浏览器原生的 ESM 特性,在生产构建时使用 Rollup 进行打包。Vite 通过 Rollup 的插件系统来处理代码转换和优化。
8-3.如何配置Webpack以实现模块打包、代码分割、热更新等功能?
在Webpack中配置模块打包、代码分割和热更新(Hot Module Replacement, HMR)是一项常见的任务。以下是一个基本的配置示例,演示如何设置这些功能:
1. 模块打包
Webpack的核心功能就是将模块进行打包,这是默认行为,只需要正确配置入口(entry)和出口(output)即可。
//webpack.config.js
const path = require(‘path’);module.exports = { // 入口文件 entry: ‘./src/index.js’,
// 输出目录及文件名 output: {
filename: ‘[name].bundle.js’,
path: path.resolve(__dirname, ‘dist’), }, };2. 代码分割
为了实现按需加载和代码分割,可以使用
import()
动态导入语句,并结合SplitChunksPlugin或optimization配置项来提取公共chunk。
// webpack.config.js
const path = require(‘path’); const
HtmlWebpackPlugin = require(‘html-webpack-plugin’);
// 用于生成HTML并自动插入分割后的JS chunkmodule.exports = { // … 其他配置不变 …
optimization: {
splitChunks: {
// 配置chunks分割策略
chunks: ‘all’, // 所有同步和异步chunk都进行分割
minSize: 30000, // 分割的最小大小
maxSize: 0, // 不限制最大大小
minChunks: 1, // 最少被引用次数
maxAsyncRequests: 5, // 最大异步请求次数
maxInitialRequests: 3, // 最大初始化请求次数
automaticNameDelimiter: ‘~’, // 连接符
name: true, // 自动生成名称
cacheGroups: { // 定义缓存组
vendors: { // 第三方库
test: /[\/]node_modules[\/]/,
priority: -10,
},
default: { // 默认缓存组
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
}, },plugins: [
new HtmlWebpackPlugin({
template: ‘./src/index.html’, // HTML模板路径
inject: ‘body’, // JS注入位置,默认为head
}), ], };
````
3. 热更新(HMR)
要在开发环境下启用Hot Module
Replacement,你需要在webpack-dev-server的配置中开启HMR,并确保你的项目支持HMR。对于大部分基于React/Vue/Angular等现代前端框架的应用,通常需要额外的loader或plugin配合。HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); module.exports = { // ... 其他配置不变 ... // 开发服务器配置 devServer: { hot: true, // 开启HMR contentBase: path.join(__dirname, 'dist'), // 服务静态资源的目录 publicPath: '/', // 设置构建后文件的访问路径 compress: true, // 是否启用gzip压缩 port: 3000, // 设置服务器端口 }, // 使用webpack.HotModuleReplacementPlugin插件 plugins: [ new HtmlWebpackPlugin({ /* ... */ }), // 保持不变 new webpack.HotModuleReplacementPlugin(), // 启用HMR插件 ], // 如果你使用的是TypeScript或Babel,还需要确保它们的配置支持HMR module: { rules: [ // TypeScript支持HMR { test: /\.tsx?$/, use: [ 'babel-loader', { loader: 'ts-loader', options: { transpileOnly: true, experimentalWatchApi: true, hmr: true, // 如果支持的话 }, }, ], exclude: /node_modules/, }, // Babel也需要相应的插件支持HMR { test: /\.js$/, exclude: /node_modules/, use: ['babel-loader'], }, ], }, }; // 对于React应用,还需在入口文件或者根组件中添加对HMR的支持 if (module.hot) { module.hot.accept(); } ``` 请注意以上只是一个简化的示例,实际配置可能需要根据项目的具体需求进行调整。同时,有些特性可能随着Webpack版本升级而有所变化,请参考最新的官方文档进行配置。
9.vue3中hooks
主要让功能模块细分,项目维护性更高。hooks是封装了一些函数方法,以供外部调用。
1.在Vue 3中,引入了Composition API(组合式API),它提供了一种使用函数而不是基于对象的方式来组织组件的状态、计算属性和方法。虽然Vue并没有直接采用React中的“hooks”概念,但Composition
API的设计思想与React Hooks有异曲同工之妙。Vue 3的Composition API提供了以下几个关键功能,它们可以被视为Vue中的“hooks”:
1.setup() 函数:
2.响应式API: reactive() 和 ref():用来创建响应式数据,分别对应于对象和基本类型的值。 readonly():创建只读响应式对象
3.computed(),watch()
4.生命周期钩子调用:
2自定义Hooks: 虽然Vue 3不直接支持自定义Hook的概念,但是由于Composition API的灵活性,开发者可以封装一些可复用的功能模块,通过组合不同的API函数来达到类似React Hooks的效果。
10.vue2中的this指什么?
在Vue 2中,this关键字的含义取决于它在哪个上下文中被引用。在Vue组件内部,this通常指代当前组件实例对象。
1.在methods、生命周期钩子函数和计算属性(computed):在这些地方使用this时,它指向当前Vue组件实例。
2.在模板表达式中: Vue模板中的表达式也会隐式地绑定到当前组件实例的上下文中,因此在模板内可以直接使用this访问数据和方法。
{ { this.message }}
< button @click=“this.showMessage”>Show Message< /button> < /div>
3.在不被Vue管理的上下文中: 如果在一个常规的JavaScript函数中使用this,或者在事件处理程序、定时器回调等非Vue上下文中直接使用,this可能不会指向Vue组件实例。在这种情况下,你可能需要通过箭头函数来保持this上下文,或者使用.bind(this)等方式确保this指向正确。
总结来说,在Vue
2的组件内部大部分情况下,this都是用来引用当前组件实例的,但在某些原生JavaScript环境中需特别注意this的指向问题。
引申:vue3中setup如何写类似与vue2中的this?
在Vue3中应尽量避免模拟Vue 2中的this行为,转而采用Composition API提供的更加直观和模块化的编程方式。尽管不推荐直接依赖,但在某些情况下可以通过getCurrentInstance()获取组件实例。
在Vue 3的Composition API(组合式API)中,由于setup()函数是一个纯函数,并且没有实例上下文,因此不能直接使用this关键字来访问组件实例。非要写的话就写在标签内部?
11.Vue 常用的修饰符有哪些应用场景
Vue.js 中的修饰符主要用于增强或修改组件绑定行为、事件处理等。以下是一些常用的Vue修饰符及其应用场景:比如控制事件传播、格式化用户输入以及父子组件间的数据同步等。
打个比方caputer:冒泡是从里往外冒,捕获是从外往里捕。
当捕获存在时,先从外到里的捕获,剩下的从里到外的冒泡输出。
1.事件修饰符:
1.stop:阻止事件冒泡,用于防止事件向上级元素传播。
< div @click=“parentClick”>
< button @click.stop=“childClick”>点击我不会触发父级事件
< /div>
2.prevent:阻止默认行为,如表单提交、链接跳转等。
3.capture:添加事件监听器时使用捕获阶段。
4.once:只触发一次事件处理器。
5.键盘事件修饰符:keyCode:监听特定键盘按下 .right:右键
.enter:当用户按下回车键时触发事件。
.space、.arrowLeft、.arrowRight 等:根据特定按键触发事件。
6.self :将事件绑定在自身身上,相当于阻止事件冒泡
8.passive:事件的默认行为为立即执行,无需等待事件 回调执行完毕
打个比方caputer:冒泡是从里往外冒,捕获是从外往里捕。
当捕获存在时,先从外到里的捕获,剩下的从里到外的冒泡输出。
2.表单修饰符:
1.lazy:在 v-model 绑定的 input 元素上使用时,会在 change 事件而非 input 事件发生时更新数据,即懒惰更新(仅在失去焦点后更新)。
< input v-model.lazy=“message” type=“text”>
2.number:自动将用户的输入转换为数值类型,对于需要数字输入的情况非常有用
< input v-model.number=“age” type=“number”>
3.trim:自动过滤用户输入两端的空白字符
< input v-model.trim=“username” type=“text”>
4.动态指令修饰符:
.sync:在子组件中同步一个 prop 的变化到父组件,适用于 Vue 2.x(Vue 3 使用 v-model:propName 或 modelValue/update:modelValue 配对来代替)。
12.事件委托原理与优缺点
事件委托,也称为事件代理,在JavaScript编程中是一种技术,用于处理DOM元素的事件绑定。它的基本原理是利用事件冒泡(event bubbling)特性:当一个事件在DOM树中的某个元素上触发时,该事件会向上逐级传播到其所有祖先元素。
具体实现方式例如:假设有一个包含多个<li>
元素的<ul>
列表
使用事件委托,则只需在<ul>
元素上绑定一个click事件监听器,当用户点击任意一个<li>
时,该事件会冒泡到<ul>
,然后在<ul>
的事件处理器中通过event.target
确定是哪个<li>
被点击。
优点:- 减少内存消耗,避免对大量元素进行独立事件绑定。- 对于动态添加或删除的子元素,无需重新绑定或移除事件处理器。- 简化代码,特别是对于大型或动态生成的DOM结构。
缺点:- 需要额外的逻辑来判断事件源,对于某些复杂的交互场景可能会增加处理难度。- 对于不支持冒泡的事件类型,无法使用事件委托机制。
13.vuex和pinia区别
Vuex 和 Pinia 是 Vue.js 生态系统中两个不同的状态管理库,虽然它们的目标都是为了在大型单页应用中更好地管理和组织状态,但是它们在设计理念、API 设计、易用性以及与 Vue.js 版本的适配上有所不同。以下是两者的主要区别:
架构设计
- Vuex 采用集中式的设计,有一个单一的状态树,并强调严格的 mutation 机制来更新状态。状态变更必须通过提交 mutation 或 dispatching actions 来完成。
- Pinia 更偏向于去中心化设计,允许分布式的状态存储,每个 store 拥有自己的状态和操作逻辑,没有强制的 mutation 概念,可以直接修改状态,但仍然建议通过 actions 来执行副作用操作。
API 设计
- Vuex 使用 mutations、actions、getters 以及 modules 这些概念,其中 mutations 用于更改状态,actions 包含异步逻辑,getters 提供计算属性式的状态读取。
- Pinia 提供 state、getters 和 actions,省去了 mutations,直接在 actions 内部同步或异步地修改 state。同时没有模块化概念,而是通过定义多个 store 来分割状态。
易用性和复杂性
- Vuex 功能强大,适合大型和复杂的项目,但由于其严格的流程和较多的概念,对于小型项目或新手开发者可能会显得较为复杂。
- Pinia 设计上更为简洁和直观,易于快速上手,特别是在 Vue 3 组合式 API 中,Pinia 的 API 更加契合现代 Vue 开发体验,其体积也相对较小。
TypeScript 支持
- Vuex 在 Vue 2 中支持 TypeScript 需要配合额外的插件,而在 Vue 3 中已经改进了对 TypeScript 的支持。
- Pinia 从设计之初就完全拥抱 TypeScript,内置了对类型系统的优秀支持,无需额外配置即可获得很好的类型提示和检查。
与 Vue.js 版本的关联
- Vuex 是早期 Vue.js(尤其是 Vue 2)项目的标准状态管理解决方案,而随着 Vue 3 的推出,Vue 团队推出了 Pinia 作为 Vuex 的进化版,专为 Vue 3 打造,具有更好的性能和集成性。
综上所述,Pinia 可以视为 Vuex 的现代化替代品,尤其是在 Vue 3 中,Pinia 凭借其简洁的 API 和对 Vue 3
新特性的良好兼容性,成为官方推荐的状态管理库。但在实际项目中,具体选择哪个库还需要根据项目的规模、团队的技术栈和长期维护计划来考虑。
13-1.vuex核心概念
State: State 是 Vuex 中的单一状态树,它是整个应用程序的唯一数据源。所有状态都保存在这个对象中,可以通过
this.$store.state 在组件中访问。
Getter:
Getter 类似于 Vue 组件中的计算属性(computed)。用于从 state 中派生出一些状态, 比如对列表进行过滤、统计等。 可以在 store 中定义
getter 来处理复杂的状态逻辑,类似于 Vue 组件内的计算属性。 Mutation:
Mutation 是更改 Vuex store 中 state 的唯一方法。每个 mutation 都有一个字符串类型的事件类型 (type) 和 一个 回调函数
(handler),这个回调函数就是我们实际进行状态更改的地方。 Mutations 必须是同步事务。
Action:
Actions 类似于 mutations,不同之处在于: Action 提交的是 mutation,而不是直接变更状态。
Action可以包含任意异步操作。 Actions 通过 context.commit 方法来提交 mutation。
Module: 当应用变得非常大时,将所有的状态逻辑放在一个地方会使 store 文件变得臃肿难以维护。Vuex 允许我们将 store分割成模块(modules),每个模块拥有自己的 state、mutation、action、getter,甚至是嵌套子模块。
13-2.Vuex 底层实现的简化概述:
创建 Store 当你创建一个新的 Vuex store 时,Vuex 会将你提供的 state 对象转换为一个响应式的对象。这主要是通过 Vue 的 Vue.observable() 方法(在 Vue 2 中是通过
Object.defineProperty 或 ES6 的 Proxy)来完成的,使得 state 中的数据变化能够被侦测到。响应式系统 当 state 发生变化时,所有依赖于该 state 的组件都会自动更新。这是因为 Vuex 使用了 Vue 的响应式机制,任何对 state 的访问或修改都受到 Vue 的监控。
单一状态树 Vuex 强调使用单一的状态树,即整个应用程序只有一个 store 实例,所有的状态都被存储在这个单一的对象中。这种方式使得状态更加易于追踪和调试。
Getter 计算属性 Getter 类似于 Vue 组件中的计算属性,它们的结果会被缓存起来,只有在其依赖的状态发生变化时才会重新计算。这有助于提高性能,尤其是在处理复杂的状态逻辑时。
Mutation 提交更改 为了保证状态变更的可预测性,Vuex 要求所有的状态更改都必须通过提交 mutation 来进行。每个 mutation 都有一个字符串类型的事件类型和一个回调函数,这个回调函数接收 state 作为第一个参数。mutation
必须是同步的,这样可以确保每次状态变化都是可追踪的Action 处理异步操作 Action 类似于 mutation,但不同的是它们不会直接改变状态,而是用来处理一些复杂的逻辑,特别是包含异步操作的情况。Actions
可以包含任意的异步操作,并最终提交一个或多个 mutation 来更改状态。 综上所述,Vuex 是通过利用 Vue
的响应式系统以及一套严格的状态管理模式来实现全局状态管理的。它的设计模式和实现细节旨在让状态管理变得简单而强大,同时保持状态变化的可预测性和易调试性。
。。。。。。。。。。
13-3.全局获取 Vuex 的数据
为了全局获取 Vuex 的数据,你可以通过以下几种方式来实现:
- 使用 mapState 辅助函数
mapState 是一个 Vuex 提供的辅助函数,可以帮助我们将 store 中的状态映射到计算属性中。你可以在组件的 computed 属性中使用它。
2.直接从 this.$store.state 获取- 使用 getters 如果你需要对状态进行一些处理后才返回,或者想要缓存某些计算结果,你应该使用 getters。getters 可以看作是 store 的计算属性。
通常推荐的方式是使用 mapState 和 getters,因为它们提供了更好的结构化和可维护性。对于简单的状态读取,直接从 this.$store.state 获取也是可行的,但尽量避免这种方式,特别是在大型项目中。- 插槽作用域(Scoped Slots) 对于更复杂的场景,比如列表项中的状态,可以考虑使用插槽作用域来传递数据给子组件。
- 全局混入 (Global Mixin) 如果你想让所有的组件都能访问某个特定的状态,可以创建一个全局混入,并将该状态添加到每个组件的计算属性中。 Vue.mixin({
computed: {
globalState() {
return this.$store.state.globalState;
} } });
13-4.Vuex 中commit与dispach区别:
- 如果你需要立即并且同步地更改状态,你应该使用
commit
来提交一个 mutation。- 如果你需要执行一些可能涉及异步操作的任务,或者需要在一个地方处理多个状态变更,你应该使用
dispatch
来触发一个 action。
commit
和dispatch
是 Vuex(Vue.js
的状态管理库)中用于不同目的的两个方法,它们的主要区别在于它们各自操作的对象和使用场景。Commit
- 操作对象: Mutations
- 同步性: 只能执行同步任务。这意味着当一个 mutation 被提交时,Vuex store 的 state 立即被更新。
- 用途: 用于直接改变 Vuex store 中的状态。是修改状态的唯一方式。
- 调用方式: 使用
store.commit('mutationName', payload)
来提交一个 mutation,其中mutationName
是你想要触发的 mutation 的名称,payload
是可选的参数,用来传递给
mutation 的数据。- 复杂逻辑: 不适用于复杂的业务逻辑或异步操作,因为必须保持同步且简单。
Dispatch
- 操作对象: Actions
- 同步性: 可以执行异步任务。Actions 允许包含任何异步逻辑,并且可以处理更复杂的流程,包括多个异步请求。
- 用途: 通常用于启动异步操作或处理可能需要多次变更状态的复杂逻辑。在 actions 中,你可以根据异步操作的结果调用 commit 来提交 mutations,从而改变状态。
- 调用方式: 使用
store.dispatch('actionName', payload)
来触发一个 action,其中actionName
是你想要触发的 action 的名称,payload
是可选的参数,用来传递给 action 的数据。- 复杂逻辑: 非常适合处理复杂的业务逻辑、异步操作以及组合多个 mutations 或其他 actions。
两者都是 Vuex 工作流中的重要组成部分,理解它们的区别对于有效地管理和维护应用的状态至关重要。
15.keep-alive的使用场景跟原理分析
内置组件,用于在组件切换时缓存组件实例
使用场景 1表单页面:当用户在多个表单之间切换时,使用 keep-alive 可以保持表单的状态,避免用户输入的数据丢失。 2列表页和详情页:在列表页和详情页之间切换时,使用 keep-alive 可以保持详情页的状态,减少重新加载数据的时间。
3多标签页:在多标签页的应用中,每个标签页的内容可能需要保持状态,使用 keep-alive 可以确保切换标签页时内容不会被重置。
4复杂组件:对于初始化成本较高的复杂组件,使用 keep-alive 可以避免每次切换时都重新初始化,提高性能。
缓存机制: keep-alive 会将包裹的组件实例缓存起来,而不是销毁它们。这些缓存的组件实例存储在一个内部的对象中。 当组件再次被激活时,keep-alive 会从缓存中取出对应的组件实例,而不是重新创建一个新的实例。
生命周期钩子: keep-alive 提供了两个额外的生命周期钩子:activated 和 deactivated。
activated 钩子在组件被激活时调用。
deactivated 钩子在组件被停用时调用。
缓存策略: keep-alive 支持通过 include 和 exclude 属性来控制哪些组件需要缓存。 include 属性可以是一个字符串、正则表达式或数组,用于指定需要缓存的组件名称。
exclude属性可以是一个字符串、正则表达式或数组,用于指定不需要缓存的组件名称。
<template>
<div id="app">
<button @click="currentComponent = 'ComponentA'">Show Component A</button>
<button @click="currentComponent = 'ComponentB'">Show Component B</button>
<keep-alive include="ComponentA">
<component :is="currentComponent"></component>
</keep-alive>
</div>
</template>
<script>
import ComponentA from './components/ComponentA.vue';
import ComponentB from './components/ComponentB.vue';
export default {
components: {
ComponentA,
ComponentB
},
data() {
return {
currentComponent: 'ComponentA'
};
}
};
</script>
16.按钮权限你是怎么实现的?如何封装自定义指令与注册使用?
1.函数方式。本质上就是通过v-if,只不过是通过一个统一的权限判断方法hasPermission: < a-button v-if=“hasPermission([‘20000’, ‘2000010’])” color=“error” class=“mx-4”>
拥有[20000,2000010]code可见 < /a-button>
2.组件方式 除了通过函数方式使用,也可以使用组件方式,Vue vben admin提供了一个Authority组件,使用示例如下: 使用Authority包裹需要权限控制的按钮即可,该按钮需要的权限码通过value属性传入,接下来看看Authority组件的实现。同样还是使用hasPermission方法,如果当前用户存在按钮需要的权限码时就原封不动渲染Authority包裹的内容,否则就啥也不渲染。
3.指令方式。使用示例如下: < a-button v-auth=“‘1000’” type=“primary” class=“mx-4”> 拥有code [‘1000’]权限可见 < /a-button>
17.自定义指令如何封装与使用。
在 Vue.js 中,自定义指令允许你封装并复用 DOM 操作行为。下面是如何在 Vue 中定义和使用自定义指令的步骤:
在 Vue 中,通过全局或局部方式注册自定义指令:
全局注册:
// 在你的 main.js 或类似的入口文件中
import Vue from 'vue';
Vue.directive('my-directive', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el, binding, vnode) {
// ……这里可以对 el(DOM 元素)进行操作
console.log('指令已插入,元素是:', el);
// binding.value 是传递给指令的值
if (binding.value) {
el.style.color = binding.value;
}
},
// 当被绑定的元素所在模板更新时……
update: function (el, binding) {
// ……这里可以对 el(DOM 元素)进行操作
},
// 当被绑定的元素所在组件的 VNode 更新时……
componentUpdated: function (el, binding) {
// ……这里可以对 el(DOM 元素)进行操作
},
// 当被绑定的元素卸载时……
unbind: function (el) {
// ……这里可以对 el(DOM 元素)进行操作
}
});
局部注册:
export default {
directives: {
'my-directive': {
// 相同的钩子函数定义
inserted(el, binding, vnode) {
// ...
},
// ...
}
}
}
使用自定义指令
在模板中使用自定义指令,格式为
v-my-directive="expression"
,其中expression
是传递给指令的值:
html <div v-my-directive="'red'">指令生效后的颜色将会变为红色</div>
在这个例子中,当
v-my-directive
指令被插入或更新时,它会把该元素的颜色设为传递的表达式值(这里是'red'
)。钩子函数说明
inserted
:当元素被插入到 DOM 中时触发。update
:当包含指令的组件视图更新时触发,此时指令的值可能发生了变化。componentUpdated
:当包含指令的组件完成更新后触发,此时 DOM 已经更新完毕。unbind
:当指令与元素解绑时触发,即组件销毁时。根据实际需求,你可以在相应的钩子函数中编写相应的行为逻辑。
18.如何实现多语言,当其他人修改权限不动配置文件需要怎么做?
实现多语言功能时,为了避免每次添加或修改权限时频繁改动配置文件,可以通过设计合理的架构和数据库支持来实现动态管理多语言内容和权限。以下是一种可能的方法:
数据库驱动的多语言支持:
- 创建一个多语言表结构,包含字段如
key
(用于标识文本键)、language_code
(用于表示语言代码,如’en’、‘zh-CN’)、value
(对应语言的文本值)。- 当需要添加或修改权限相关的多语言文案时,管理员可以直接通过后台管理系统操作数据库,而不是修改配置文件。
动态权限管理:
- 使用角色权限模型,将权限规则存储在数据库中,而不是硬编码在配置文件中。
- 创建角色表、权限表以及角色权限关联表,通过数据库记录和查询来判断用户的角色和对应的权限集合。
- 开发后台管理系统,允许管理员在无需接触配置文件的情况下,增删改查权限规则和分配给不同角色。
应用层面实现:
- 在应用程序中,当需要获取权限相关的多语言文本时,根据当前用户的语言设置查询数据库获取对应语言的内容。
- 在进行权限验证时,