网站创建需要多少钱,wordpress 企业模板 免费下载,湛江做网站开发,蒙古文政务网站建设工作汇报js 面试题总结 文章目录 js 面试题总结近百道面试题1、实现 子元素 在父元素中垂直居中的方式2、实现 子元素 在父元素中水平 垂直居中的方式3、描述 Keepealive 的作用#xff0c;有哪些钩子函数#xff0c;如何控制组件级存列表?4、请写出判断对象是数组的三个方法5、请说…js 面试题总结 文章目录 js 面试题总结近百道面试题1、实现 子元素 在父元素中垂直居中的方式2、实现 子元素 在父元素中水平 垂直居中的方式3、描述 Keepealive 的作用有哪些钩子函数如何控制组件级存列表?4、请写出判断对象是数组的三个方法5、请说出下面代码的执行结果6、过滤器如何使用? 请写一个数字保两个小数的过滤器上述要求用vue的过滤器实现 7、在vue里面 什么是自定义指令请注册一个全局的自定义指令。8、请说出下面代码的输出结果9、介绍下浅拷贝和深拷贝10、websocket 如何监听心跳11、前端 http 如何转 https12、前端 同步可以调用异步吗异步调异步呢13、视频在安卓可以正常播放在 ios 无法正常播放?14、前端 如何对后台接口进行封装15、promise 原理16、uniapp 如何自动化打包17、微信小程序凭证?18、公众号怎么实现微信支付19、前端 实现支付宝支付20、前端如何识别 后端返回链接 是否是图片21、前端怎么解决页面白屏22、dom树加载机制23、前端页面渲染机制25、介绍下http\https 协议26、get、post怎么传参27、vuex 数据传参导致白屏28、WebSocket 线程过多通常是因为服务器无法在实时处理所有客户端请求所致。想要解决这个问题可以尝试以下几种方法29、在 Canvas 中处理音频通常需要结合 Web Audio API 来实现。Web Audio API 是一个用于处理和控制音频的高级 API它提供了许多函数和接口可以很容易地在 JavaScript 中创建音频模型并控制其行为。30、 在 Webpack 中配置 rem31、Webpack 解决样式兼容问题32、Vite33、vite 的基本使用34、vue 的 object.prototype 为什么监听不到数组 (对象) 里面变化35、那在vue中怎么做到监听数组 (对象) 里面变化呢1、用 $set() 和 Object.assign() 方法可以给对象增加响应式数据2、数组的 pop(),push() 等方法可以实现响应式 主要是因为 Vue 使用了特殊的技巧来拦截数组的变异方法mutation methods。 35、路由 history 原理服务端如何配置?36、关于 Tree.js 的教程以下是一个基本的介绍37、v-model 指令的修饰符38、小程序代码的构成39.小程序页面的组成部分40、微信小程序开发流程41、Vuex:42、Vuex43、浏览器中的Event Loop是什么它有什么作用44、请简述HTML5中nav标签的作用。45、在Vue.js中computed与watch有什么区别46、请简述React中Virtual DOM的作用。47、如何在JavaScript中判断一个变量是否是数组48、简述ES6中let和const关键字的作用及区别。49、在JavaScript中如何遍历一个对象的属性50、请简述React中props与state的区别。51、如果希望在React应用中使用Ajax请求请列出至少两种可以实现该功能的库或插件。52、如何在JavaScript中创建自定义异常53、在JavaScript中如何判断一个变量是否为null或undefined54、如何让一个DOM元素在点击时触发某个事件55、在React中shouldComponentUpdate()生命周期方法的作用是什么56、如何在JavaScript中判断一个变量是否为NaN57、在CSS中请简述盒模型(Box Model)的概念。58、请简述React中的组件生命周期。59、在JavaScript中如何获取当前时间60、在React中请简述Refs的作用。61、什么是闭包请举例说明。62、如何判断一个变量是否为数组类型61、常见的HTTP状态码有哪些请分别说明各自的含义。62、如何检测一个对象是否包含某个属性63、解释一下JavaScript中的事件循环。64、请说明ES6中let和const关键字的区别。65、jQuery中的on()和bind()方法有什么区别66、JavaScript中的typeof操作符会返回哪些值请说明各自的含义。67、请解释一下浏览器的同源策略。68、请解释一下JavaScript中的this关键字。69、请解释一下JavaScript中的原型继承。70、请解释一下CSS中的盒模型。71、什么是小程序72、小程序有哪些优势73、小程序和传统网页开发有何不同74、请说明小程序的页面生命周期及其执行顺序。75、如何实现小程序之间的通信76、什么是uni-app77、uni-app有哪些主要特点78、uni-app的工作原理是什么79、如何在uni-app中进行页面跳转80、uni-app支持哪些第三方插件和组件库81、uni-app的页面生命周期有哪些82、如何进行uni-app之间的页面通信83、uni-app如何实现原生功能调用84、VueX 使用教程安装 Vuex创建一个 store在 main.js 中引入 store在组件中使用 Vuex在组件中使用 actions 和 getters总结 真实实例1、vue的双向绑定原理是什么里面的关键点在哪里2、实现水平垂直居中的方式3、常用伪元素有哪一些4、移动端如何适配不同屏幕尺寸5、本地存储有哪一些他们三者有什么区别6、JS的数据类型如何判断js的数据类型7、说一下ES6的新特性有哪些8、let、const、var三者有什么区别9、数组去重有哪些办法10、说一下深拷贝和浅拷贝如何自己实现一个深拷贝11、Vue的生命周期有哪一些说一下它们每个阶段做什么操作12、组件通讯方式有哪一些13、Vuex有几个属性及作用14、Vue的监听属性和计算属性有什么区别15、说一下防抖和节流。怎么实现16、Vue的导航守卫有哪一些Vue的导航守卫包括以下几种 分模块面试题一、HTML 篇1. 简述一下你对 HTML 语义化的理解2. 标签上 title 与 alt 属性的区别是什么3. iframe的优缺点优点缺点 4. href 与 src 二、CSS 篇1、介绍一下 CSS 的盒子模型2. css 选择器优先级3、垂直居中几种方式4、简明说一下 CSS link 与 import 的区别和用法5、rgba和opacity的透明效果有什么不同6、display:none和visibility:hidden的区别7、position的值 relative和absolute分别是相对于谁进行定位的8、画一条0.5px的直线9、calc, support, media各自的含义及用法10、1rem、1em、1vh、1px各自代表的含义1、rem2、em3、vw/vh4、px 11、画一个三角形 三、HTML / CSS 混合篇1、HTML5、CSS3 里面都新增了那些新特性HTML5CSS3 2、BFC 是什么3、常见兼容性问题 四、JS 篇1、JS 数据类型 ?2、判断一个值是什么类型有哪些方法3、null 和 undefined 的区别4、怎么判断一个变量arr的话是否为数组此题用 typeof 不行5、“ ”、“ ”的区别6、 “eval是做什么的7、箭头函数有哪些特点8、var、let、const 区别9、new操作符具体干了什么呢10、JSON 的了解11、document.write 和 innerHTML 的区别12、ajax过程13、请解释一下 JavaScript 的同源策略14、介绍一下闭包和闭包常用场景15、javascript的内存(垃圾)回收机制16、JavaScript原型原型链 ? 有什么特点1、任何对象都有 proto 隐式原型, 等于 构造函数 的 prototype2、任何函数都有 prototype 显示原型 等于 原型对象(就是一个普通对象包含公共属性)3、对象还具有 constructor 属性指向构造函数Person.prototype.constructor Person4、原型链是依赖于__proto__, 查找一个属性会沿着 proto 原型链向上查找直到找到为止。5、特殊特点 17、用js递归的方式写1到100求和18、事件队列宏任务微任务常见宏任务常见微任务微任务和宏任务的本质区别。 19、async/await改进 20、JavaScript 是单线程的浏览器是多进程的 五、Vue 篇1、谈谈你对MVVM开发模式的理解2、v-if 和 v-show 有什么区别3、说说你对 SPA 单页面的理解它的优缺点分别是什么优点缺点 4、你使用过 Vuex 吗State 基本数据定义了应用状态的数据结构可以在这里设置默认的初始状态。Getter 从基本数据派生的数据允许组件从 Store 中获取数据mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。Mutation 是唯一更改 store 中状态的方法且必须是同步函数。Action 像一个装饰器包裹mutations使之可以异步。用于提交 mutation而不是直接变更状态可以包含任意异步操作。Module 模块化Vuex允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。 总结 5、Class 与 Style 如何动态绑定对象语法数组语法对象语法数组语法 6、怎样理解 Vue 的单向数据流7、computed 和 watch 的区别和运用的场景运用场景 8、直接给一个数组项赋值Vue 能检测到变化吗9、谈谈你对 Vue 生命周期的理解生命周期是什么各个生命周期的作用 10、Vue 的父组件和子组件生命周期钩子函数执行顺序11、父组件可以监听到子组件的生命周期吗12、谈谈你对 keep-alive 的了解13、组件中 data 为什么是一个函数14、v-model 的原理15、Vue 组件间通信有哪几种方式1props / $emit 适用 父子组件通信2ref 与 $parent / $children适用 父子组件通信3EventBus $emit / $on适用于 父子、隔代、兄弟组件通信4$attrs/$listeners适用于 隔代组件通信5provide / inject适用于 隔代组件通信6Vuex适用于 父子、隔代、兄弟组件通信 16、使用过 Vue SSR 吗说说 SSR1服务端渲染的优点2) 服务端渲染的缺点 17、vue-router 路由模式有几种19、能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗1hash 模式的实现原理hash 路由模式的实现主要是基于下面几个特性 2history 模式的实现原理 19、Vue 框架怎么实现对象和数组的监听Vue 主要通过以下 4 个步骤来实现数据双向绑定的 20、Vue 是如何实现数据双向绑定的21、Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 22、虚拟 DOM 的优缺点优点缺点: 23、虚拟 DOM 实现原理24、Vue 中的 key 有什么作用25、你有对 Vue 项目进行哪些优化1代码层面的优化2Webpack 层面的优化3基础的 Web 技术的优化 26、对于 vue3.0 特性你有什么了解的吗1监测机制的改变2模板3对象式的组件声明方式4其它方面的更改 27、响应式原理变化侦测28、Object.defineProperty怎么用 三个参数有什么作用啊29、vue2和vue3的响应式原理都有什么区别呢Object.defindProperty 缺点Proxy就没有这个问题可以监听整个对象的数据变化所以用vue3.0会用Proxy代替definedProperty。 30、Vue的patch diff 算法31、Vue 模板编译原理32、Vue原理总结 六、Webpack 篇1、谈谈你对Webpack的理解Webpack是什么webpack的主要作用如下 2、Webpack的打包过程/打包原理/构建流程3、loader的作用4、有哪些常见的Loader他们是解决什么问题的常用的loader如下 5、plugin的作用6、有哪些常见的Plugin他们是解决什么问题的7、Webpack中Loader和Plugin的区别运行时机使用方式 8、webpack的热更新是如何做到的说明其原理详细 9、如何解决循环依赖问题10、如何提高Webpack构建速度1、代码压缩JS 压缩CSS 压缩HTML 压缩 2、图片压缩3、Tree Shaking4、缩小打包域5、减少 ES6 转为 ES5 的冗余代码6、提取公共代码7、其他 七、性能优化篇1、浏览器缓存优化2、渲染层面性能优化CSS策略DOM策略 3、性能优化六大指标 八、其他杂项篇1、常见的浏览器内核有哪些2、网页前端性能优化的方式有哪些3、网页从输入网址到渲染完成经历了哪些过程4、线程与进程的区别5、HTTP常见的状态码6、图片懒加载7、移动端性能优化8、TCP 传输的三次握手、四次挥手策略三次握手断开一个TCP连接需要“四次挥手” 9、HTTP 和 HTTPS为什么HTTPS安全10、axios和fetch区别对比fetch优势fetch存在问题 九、主观题篇1、你都做过什么项目呢具体聊某一个项目中运用的技术.2、你遇到过比较难的技术问题是你是如何解决的3、常使用的库有哪些常用的前端开发工具开发过什么应用或组件4、除了前端以外还了解什么其它技术么你最最厉害的技能是什么5、对前端开发工程师这个职位是怎么样理解的它的前景会怎么样6、你的优点是什么缺点是什么 新面试题1、flex布局周边属性justify-content用于设置弹性项目在主轴上的对齐方式。它可以接受以下值align-items用于设置弹性项目在交叉轴上的对齐方式。它可以接受以下值align-content用于设置多行弹性容器在交叉轴上的对齐方式。它可以接受以下值4、flex-direction5、flex-wrap6、flex-flow 属性是以下属性的简写属性 2、mixins.extends 混入3、无感登录 怎么实现4、vue 自定义指令都有哪些属性bind指令第一次绑定到元素时调用。inserted被绑定元素插入父节点时调用仅保证父节点存在但不一定已插入文档。update被绑定元素所在的模板更新时调用但可能发生在其子元素更新之前。componentUpdated被绑定元素所在模板完成一次更新周期时调用。unbind指令与元素解绑时调用。 5、keep-alive 中的属性max 超过设置的数值10后如何处理具体处理方式如下 6、vue computed 在哪两个勾子函数中间 打印具体的生命周期顺序如下 7、for in 和 for of的区别遍历的目标不同遍历的值不同遍历顺序不同使用场景不同示例代码说明 8、nextTick 原理nextTick是Vue中的一个异步方法用于在DOM更新后执行回调函数。它的原理如下示例代码 9、axios 中断请求10、部署时候用hash刷新白屏问题为解决哈希刷新导致的白屏问题可以尝试以下方法 11、tab页用路由模式咋实现12、vuex中b.module里面咋接收根目录下面的state13、A跳转B再跳转C返回时怎么直接到A14、前端怎么部署配置15、sass和less用到哪些属性配置啥的SassLess配置方面 Node1、什么是Node.js它与前端开发有什么关系2、Node.js的特点是什么它适用于哪些场景3、如何创建一个基本的HTTP服务器并监听特定端口4、什么是模块Module如何在Node.js中使用模块5、require()函数的作用是什么它如何加载和导出模块6、Node.js中常用的内置模块有7、处理异步操作以避免回调地狱的方式8、包管理器是管理和安装软件包包括库、框架和工具的工具。Node.js中常用的包管理器有9、读取和写入文件的代码示例10、如何处理表单提交数据请简述一下Express框架的用途和优势。Express框架的优势包括 11、如何处理路由请求请介绍一下RESTful API的设计原则。1、使用合适的HTTP方法2、使用语义化的URL路径3、返回合适的HTTP状态码4、使用JSON格式进行数据交互 12、Node.js中的事件驱动编程是什么意思请给出一个具体的例子。13、什么是中间件Middleware它在Node.js中的作用是什么14、要使用Node.js连接和操作数据库一般需要按照以下步骤进行15、关于WebSocket的解释和与HTTP通信的区别如下16、前端常见的解决跨域的方式17、如何通过 vuex 实现登录验证?18、router 和route 区别19、created 生命周期不可以千什么事情20、数据拦截是发生在哪个生命周期的? 什么情况下不会对属性数据拦截要怎么解决?21、css bfc触发BFC的条件包括BFC具有以下特性BFC在实际的页面布局中有多种应用例如 22、什么是圣杯布局23、什么是双飞翼布局24、http 缓存浏览器有以下几种缓存方式 25、http 1.0 和 2.0 的区别26、介绍下 bfsdfs1、广度优先搜索BFS2、深度优先搜索DFS选择使用BFS还是DFS取决于具体的应用场景和问题要求 27、输入网址发生了什么28、websocket 主题29、前端怎么做公共模块的抽离30、组件封装的时候如果在用到别人封装的组件库里面还想再用到他封装之前的方法或者插槽应该怎么用31、自己在mui基础上封装组件怎么让别人在使用自己封装的组件的时候还能调用mui原生的方法和api32、跨一层级的插槽怎么使用在父组件中定义插槽在子组件中接收并使用插槽内容 33、长列表怎么做性能优化34、怎么优化首屏的加载速度35、小程序 怎么做响应式布局36、1rpx37、vue怎么做权限管理创建路由配置文件例如router.js定义需要进行权限控制的路由和对应的权限要求在主组件中编写路由守卫来进行权限判断例如在App.vue中 38、小程序怎么做权限管理39、小程序从A页面到B页面再到C页面 怎么在不刷新页面的情况下返回A页面40、h5页面缓存怎么实现41、h5页面从A页面到B页面再到C页面 怎么在不刷新页面的情况下返回A页面知识点 42、比较两个对象是否相同43、vueX 跟普通的 window注册的全局变量有什么区别44、不考虑写法差异 vueX 比一般设置全局变量的方式好在那里45、请写出下列代码执行结果46、判断基础类型之间的比较47、HTML5新增的inputtype类型有哪些?48、HTML5 input type类型 都有那些49、可以实现选择某个元素的所有奇数项的选择器是?50、下面的执行结果51、下列程序运行结果符合预期的是:52、可以实现只选择与div相邻的p元素的选择器是?53、下列程序符合预期的是54、请编写一个JavaScript 函数 parseQueryString它的用途是把 URL参数解析为一个对象使用示例输出结果 55、decodeURIComponent Electron 面试题11、Electron 怎么使用确保你已经安装了Node.js和npmNode包管理器。你可以在官方网站上下载并按照说明进行安装。打开命令行工具创建一个新的项目文件夹并进入该文件夹在项目文件夹下初始化你的应用程序并创建一个 package.json 文件来管理依赖项安装Electron作为开发依赖项创建一个主进程文件 main.js 这将是你的应用程序的入口点。在这个文件中你可以设置窗口、处理系统事件和与渲染进程通信等创建一个渲染进程文件 index.html 用于定义你的应用程序界面在 package.json 文件中添加一个脚本命令用于运行你的应用程序运行以下命令启动应用程序 1、什么是 Electron2、Electron 是如何工作的3、Electron 应用的主进程和渲染进程有什么区别4、如何与主进程进行通信5、Electron 支持的操作系统是哪些6、如何打包 Electron 应用7、怎样优化 Electron 应用的性能8、如何在 Electron 中使用本地模块Native Modules9、如何处理 Electron 应用程序的主窗口关闭事件10、如何在 Electron 应用中实现自动更新Auto Update功能11、如何在 Electron 应用程序中使用本地存储12、如何创建自定义菜单栏和上下文菜单Context Menu13、如何调试 Electron 应用程序14、如何处理 Electron 应用程序的多窗口通信15、如何处理 Electron 应用的安全性16、如何实现自动更新 Electron 应用程序17、如何调试 Electron 应用 Vue31、Vue 3中的Composition API是什么它与Options API有何不同2、Vue 3中的响应式数据是如何实现的3、Vue 3中的Teleport组件是用来做什么的4、Vue 3中的v-model指令有何变化5、Vue 3中的Suspense组件是用来做什么的6、Vue 3中的组合式API和混入Mixin有何区别7、在Vue 3中如何处理全局状态管理8、Vue 3中的动态组件有何变化9、Vue 3中支持哪些新的生命周期钩子函数10、Vue 3中的模板语法有哪些改进11、如何在Vue 3中处理异步操作12、Vue 3中支持哪种方式进行组件通信13、Vue 3中怎么做页面过渡效果14、Vue 3中的编译器和运行时的区别是什么15、Vue 3中的Teleport是什么它有什么作用16、Vue 3中的Composition API与Options API相比有哪些优势17、Vue 3中的Fragment是什么它的作用是什么18、Vue 3中的v-model指令如何使用它是怎样实现双向数据绑定的19、Vue 3中的Suspense和错误边界是什么它们有什么作用20、Vue 3中的响应式原理是什么它与Vue 2相比有何改进21、Vue 3中的Teleport与Portal有何区别22、Vue 3中的递归组件是什么它在哪些场景下常用23、Vue 3中的Fragments和Portals在性能上有何改进24、Vue 3中的Composition API和Options API有何区别25、Vue 3中的全局状态管理工具是什么它与Vue 2中的Vuex有何区别26、Vue 3中的动画过渡是如何实现的请提供一个简单的示例。27、history 模式和hash模式的区别 优缺点 和分别的用法28、h5页面从A页面到B页面再到C页面 在不刷新页面的情况下返回A页面用上面的哪一种路由模式实现29、vue3中生命周期函数30、Electron 如何与主进程进行通信代码示例31、用const 定义一个对象 这个对象能再增加属性吗33、vue 中样式隔离34、vue 监听属性和计算属性的实现原理35、vue 中数组用push()的方式为什么可以实现响应式36、webpack 链式路由37、websocket 主题38、前端怎么做公共模块的抽离39、什么是js 设计模式40、这三种的区别和优点缺点是什么41、vuex 数据 刷新页面白屏的问题原因42、webpack 链式列表43、webpack 和 vite 的区别 和优缺点 和实现原理44、这两种工具 实现热更新的原理是什么45、vite 和webpack 热更新原理 哪里不同导致了vite 更快46、这两种工具 实现热更新的原理是什么47、JavaScript中对象调用自身的原型方法48、有一个数组数组里面存放着n个promise函数要求这些promise函数顺序执行。1、asyncawait 方法2、递归方法 49、浏览器回流与重绘1、浏览器的渲染过程1.1 生成渲染树注意:1.2 回流1.3 重绘 2、何时发生回流重绘3、浏览器的优化机制4、减少回流和重绘 50、vue中ref属性使用详情介绍1、获取DOM元素的引用2、获取子组件的引用 51、js ||与|、与的区别介绍1.情景展示2.||3.| 52、js面向对象怎么理解一、认识对象。二 、在js中面向对象的编程思想是什么呢三、对象既可以存储数据又可以存储函数的结构。四、用面向对象创建一个人的方法五、继承和多态多态我自己拓展和重写的属性和方法。六、满足继承、封装、多态的特点叫做面向对象。 53、vue父子组件传值之$parent、$children、$refs示例一$parent 子组件获取父组件的数据、调用父组件的方法父组件子组件 示例二$children 父组件获取子组件的数据、调用子组件的方法父组件子组件 示例三$[refs](https://so.csdn.net/so/search?qrefsspm1001.2101.3001.7020) 父组件获取子组件的数据、调用子组件的方法 54、前端发请求阶段如何进行优化:跨域的解决方法1jsonp的认识2cores的认识 55、跨域请求被拦截后怎样判断请求是否到达后端预检请求options 56、ajax中如何实现isonp跨域 说说原生js实现方法57、iframe跨域操作怎么实现的58、考察的是一个表单提交如何防止用户多次提交减少服务器端的压力59、创建10个a标签点击弹出它是第几个60、左固定布局右侧自适应布局61、题目给一个字符串循环输出每一个字符当输出到最后一个字符时再从头开始62、题目给一个数判断该数是否是斐波那契数列里的一项若是则返回第几项若不是则返回-163、js 实现一个斐波那契数列 要求小于 给定的数字n什么是斐波那契数列返回斐波那契数列的第几位的代码如下递归方式迭代方式 64、题目实现链式的加减乘除myCalculator(1).add(12).minus(3).multi(10).div(5).getValue() 2564、题目搜索多个文件夹路径字符串数组例如[/usr/bin, /etc/config]查找是否有公共路径若有则返回公共路径字符串否则返回 null65、题目若a * a b * b c * c则称a,b,c为勾股数找出比 n 小的所有勾股数66、将一串[字符串](https://so.csdn.net/so/search?q字符串spm1001.2101.3001.7020)驼峰化,第一个‘-’后的字母无需转小写为大写其他‘-’后的字母需转小写为大写67、一串字符串当字符[a-zA-Z]重复输出true否则输出false。js 时间复杂度 和空间复杂度v-for 和v-if为什么不能一块儿用什么时候使用闭包64、题目搜索多个文件夹路径字符串数组例如[/usr/bin, /etc/config]查找是否有公共路径若有则返回公共路径字符串否则返回 null65、题目若a * a b * b c * c则称a,b,c为勾股数找出比 n 小的所有勾股数66、将一串[字符串](https://so.csdn.net/so/search?q字符串spm1001.2101.3001.7020)驼峰化,第一个‘-’后的字母无需转小写为大写其他‘-’后的字母需转小写为大写67、一串字符串当字符[a-zA-Z]重复输出true否则输出false。js 时间复杂度 和空间复杂度v-for 和v-if为什么不能一块儿用什么时候使用闭包 近百道面试题
1、实现 子元素 在父元素中垂直居中的方式
有多种方法可以实现子元素在父元素中垂直居中的方式以下是其中几种常用的方法
使用 Flexbox 布局
父元素设置为 flex 容器并使用 align-items 属性将子元素垂直居中。
.parent {display: flex;align-items: center; /* 子元素垂直居中 */
}使用表格布局
将父元素设置为 table子元素设置为 table-cell并使用 vertical-align 属性将子元素垂直居中。
.parent {display: table;
}.child {display: table-cell;vertical-align: middle; /* 子元素垂直居中 */
}使用绝对定位和 transform
使用绝对定位将子元素相对于父元素居中结合 transform 属性进行位移变换。
.parent {position: relative;
}.child {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%); /* 子元素垂直居中 */
}使用 Grid 布局
父元素设置为 grid 容器并使用 place-items 属性将子元素垂直居中。
.parent {display: grid;place-items: center; /* 子元素垂直居中 */
}以上是常用的几种方法选择适合你项目需求和浏览器兼容性的方式来实现子元素在父元素中垂直居中。
2、实现 子元素 在父元素中水平 垂直居中的方式
1.可以使用 Flexbox 布局。以下是一种基本的方法
.parent {display: flex;justify-content: center; /* 子元素水平居中 */align-items: center; /* 子元素垂直居中 */
}以上代码将父元素设置为 flex 容器使用 justify-content 属性将子元素水平居中使用 align-items 属性将子元素垂直居中。2.使用绝对定位和 transform
.parent {position: relative;
}.child {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%); /* 子元素水平垂直居中 */
}3.使用表格布局
将父元素设置为 table子元素设置为 table-cell并使用 vertical-align 和 text-align 属性控制对齐方式。
.parent {display: table;width: 100%;height: 100%;
}.child {display: table-cell;vertical-align: middle; /* 子元素垂直居中 */text-align: center; /* 子元素水平居中 */
}4.使用 Grid 布局
.parent {display: grid;
}.child {justify-self: center; /* 子元素水平居中 */align-self: center; /* 子元素垂直居中 */
}3、描述 Keepealive 的作用有哪些钩子函数如何控制组件级存列表?
KeepAlive 组件是 Vue.js 中的一个内置组件用于缓存和复用组件实例。它可以将动态组件进行缓存在组件切换时保留其状态提高应用性能。
作用
缓存组件状态使用 KeepAlive 包裹的组件在切换时会被缓存保留组件的状态包括数据、DOM 状态以及生命周期状态。复用组件实例每次切换到被 KeepAlive 包裹的组件时并不会重新创建组件实例而是直接使用之前缓存的组件实例。
常用的钩子函数
activated在组件被激活时调用每次切换到该组件时都会触发。可以用来执行组件激活时需要进行的逻辑操作例如数据加载或动画效果的启动。deactivated在组件失活时调用每次从该组件切换出去时都会触发。可以用来执行组件失活时需要进行的清理操作例如取消定时器或停止正在进行的动画。
控制组件级存储
可以通过 keep-alive 的特殊属性 include 和 exclude 控制具体哪些组件需要被缓存以及哪些组件不需要被缓存。
include指定要缓存的组件名称数组只有包含在该数组中的组件才会被缓存。exclude指定不需要缓存的组件名称数组这些组件将不会被缓存。
示例如下
keep-alive :include[ComponentA, ComponentB] :exclude[ComponentC]router-view/router-view
/keep-alive以上代码中只有名为 “ComponentA” 和 “ComponentB” 的组件会被缓存而名为 “ComponentC” 的组件不会被缓存。其他组件则按需创建和销毁。
使用 include 和 exclude 可以灵活地控制组件级别的缓存策略根据具体需求来决定哪些组件需要保持状态并进行复用。
4、请写出判断对象是数组的三个方法
判断对象是否为数组的三个常用方法如下
使用 Array.isArray() 方法
if (Array.isArray(obj)) {// obj 是一个数组
}使用 instanceof 运算符
if (obj instanceof Array) {// obj 是一个数组
}使用 Object.prototype.toString.call() 方法
if (Object.prototype.toString.call(obj) [object Array]) {// obj 是一个数组
}4.通过 constructor 判断
if (obj.constructor Array) {// obj 是一个数组
}
例子
var listInfo [1,2,3];
console.log(this.listInfo) // [1,2,3]
console.log(Array.isArray(this.listInfo)) // true
console.log(this.listInfo instanceof Array) //true
console.log(Object.prototype.toString.call(this.listInfo)) // [object Array]
console.log(this.listInfo.constructor) // ƒ Array() { [native code] }
console.log(this.listInfo.constructor Array); // true注意 instanceof 和 .constructor 必须确保这个数组是 Arry 构造函数创建出来的 否则会报错这些方法都可以判断给定的对象是否为数组。对于这三种方法推荐使用 Array.isArray()因为它是专门用于判断对象是否为数组的方法且在不支持ES5的环境中也能正常工作。
5、请说出下面代码的执行结果
var myObj1 {name: 小王,myAge: this.age,sayName: function(add, front) {console.log(this.name 今年 this.age 在 add 做 front);}
};var heros {name: 小张,age: 20
};
myObj1.sayName.call(heros, 上海, 前端);
myObj1.sayName.apply(heros,[上海,前端]);
myObj1.sayName.bind(heros)(上海,前端);
myObj1.sayName.bind(heros,上海,前端)();
myObj1.sayName.bind(heros,[上海,前端])()小张今年20在上海做前端小张今年20在上海做前端小张今年20在上海做前端小张今年20在上海做前端小张今年20在上海,前端做undefined考察的 下面是对每个调用方法的说明
1、 myObj1.sayName.call(heros, 上海, 前端)使用 call 方法调用 sayName 方法并将 heros 对象作为方法的上下文this绑定同时传递 上海 和 前端 作为参数。 立即执行2 myObj1.sayName.apply(heros, [上海, 前端])使用 apply 方法调用 sayName 方法并将 heros 对象作为方法的上下文this绑定同时以** 数组 **形式传递 [上海, 前端] 作为参数。 立即执行
3、myObj1.sayName.bind(heros)(上海,前端); 将 heros 作为参数 传入 bind 方法然后在后面的调用中又依次传入 上海、前端
4、、 myObj1.sayName.bind(heros, 上海, 前端)()使用 bind 方法创建一个新的函数并将 heros 对象作为方法的上下文this绑定同时传递 上海 和 前端 作为参数。然后立即调用新创建的函数。
5、myObj1.sayName.bind(heros,[上海,前端])() 将heros 作为改变this 指向的值传入再把 [上海,前端] 这个数组作为第一个替换值传入 所以 add ‘上海,前端’ front undefined 知识点在 JavaScript 中call、apply 和 bind 都是用于改变函数的执行上下文即函数内部的 this 指向的方法。它们的主要区别在于参数的传递方式和是否立即执行函数。1. call 方法- 语法function.call(thisArg, arg1, arg2, ...)。- 作用call 方法调用一个函数并将指定的对象作为函数的执行上下文this。可以通过 call 方法实现继承、借用其他对象的方法或更改函数内部的上下文。- 参数- thisArg被绑定到函数的执行上下文this的对象。- arg1, arg2, ...函数调用时所需的参数列表。- 返回值函数的返回结果。2. apply 方法- 语法function.apply(thisArg, [argsArray])。- 作用apply 方法调用一个函数并将指定的对象作为函数的执行上下文this。与 call 方法类似不同之处在于参数的传递方式apply 使用 ·数组·来传递参数。- 参数- thisArg被绑定到函数的执行上下文this的对象。- [argsArray]一个包含函数调用时所需参数的数组。- 返回值函数的返回结果。3. bind 方法- 语法function.bind(thisArg, arg1, arg2, ...)。- 作用bind 方法.创建. 一个新的函数该函数和原来的函数具有相同的函数体但执行上下文this永久地被绑定到 bind 方法的第一个参数指定的对象。- 参数- thisArg被绑定到新函数的执行上下文this的对象。- arg1, arg2, ...在调用新函数时要传递给原函数的固定参数。- 返回值返回一个绑定了执行上下文和固定参数的新函数并不会立即执行。在给定的代码示例中我们使用了 call、apply 和 bind 来改变 myObj1.sayName 函数的执行上下文并传递了额外的参数。- myObj1.sayName.call(heros, 上海, 前端)调用 sayName 函数使其以 heros 对象作为执行上下文this并传递 上海 和 前端 作为参数。结果会打印出类似于 小张今年20在上海做前端 的内容。
- myObj1.sayName.apply(heros, [上海, 前端])类似于 call 方法但是参数通过数组进行传递。
- myObj1.sayName.bind(heros, 上海, 前端)()创建一个新的函数并将 heros 对象作为执行上下文this同时传递 上海 和 前端 作为参数。由于使用了括号 ()新函数会立即执行并打印出相应的内容。需要注意的是在使用 call、apply 和 bind 时要确保参数和参数列表的正确配对并根据具体需求选择合适的方法来改变函数的执行上下文。6、过滤器如何使用? 请写一个数字保两个小数的过滤器
在 JavaScript 中你可以使用过滤器Filter来对数据进行格式化和处理。过滤器通常用于将原始数据转化为需要的形式比如在显示数据时对数字进行格式化。
下面是一个示例代码实现了一个保留两位小数的数字过滤器
// 定义数字保留两位小数的过滤器
function twoDecimalFilter(value) {if (typeof value ! number) return value; // 非数字类型直接返回return value.toFixed(2); // 使用toFixed方法保留两位小数
}// 示例使用
let number1 12.3456;
let number2 3.7;
let string abc;console.log(twoDecimalFilter(number1)); // 输出 12.35
console.log(twoDecimalFilter(number2)); // 输出 3.70
console.log(twoDecimalFilter(string)); // 输出 abc上述代码中twoDecimalFilter 是一个自定义的过滤器函数。它接受一个参数 value判断该值是否为数字类型如果是则使用 toFixed 方法将其保留两位小数并返回如果不是数字则直接返回原值。
在使用过滤器时只需调用过滤器函数并传入要格式化的数据即可。在上面的示例中我们分别对 number1、number2 和 string 进行过滤并输出结果。
请注意在实际开发中可能会使用框架或库来提供更便捷和灵活的过滤器机制比如在 Vue.js 中使用过滤器。以上示例仅演示了一个简单的自定义过滤器的实现方式。
上述要求用vue的过滤器实现
使用 Vue 的过滤器可以更方便地对数据进行格式化和处理。下面是一个使用 Vue 过滤器的示例实现保留两位小数的数字过滤器
html复制代码!-- 在 Vue 模板中定义过滤器 --
div idappp{{ number1 | twoDecimal }}/pp{{ number2 | twoDecimal }}/pp{{ string | twoDecimal }}/p
/divscript srchttps://cdn.jsdelivr.net/npm/vue2.6.14/script
script
// 创建 Vue 实例
new Vue({el: #app,data: {number1: 12.3456,number2: 3.7,string: abc},filters: {twoDecimal(value) {if (typeof value ! number) return value;return value.toFixed(2);}}
});
/script在上述代码中我们创建了一个 Vue 实例并在该实例中定义了一个过滤器 twoDecimal。在模板中通过使用管道操作符 | 将要过滤的数据与过滤器名称相连在输出时会自动应用过滤器。
在这个示例中number1、number2 和 string 是 Vue 实例的数据属性分别代表要过滤的数字和字符串。通过 {{ number | twoDecimal }} 的形式将数值绑定到对应的模板插值处同时应用名为 twoDecimal 的过滤器从而实现保留两位小数的效果。
运行以上代码你将看到页面上输出的数值已按照过滤器的设定进行了格式化。
7、在vue里面 什么是自定义指令请注册一个全局的自定义指令。
在 Vue 中自定义指令Custom Directive是一种扩展 Vue 的能力用于对 DOM 元素进行低层次的操作和交互。通过自定义指令我们可以直接操作 DOM、添加事件监听器、进行样式操作等。
要注册一个全局的自定义指令你可以使用 Vue.directive 方法。下面是一个示例代码演示如何注册一个名为 my-directive 的全局自定义指令
div idappinput v-my-directive /
/divscript srchttps://cdn.jsdelivr.net/npm/vue2.6.14/script
script
// 注册全局自定义指令
Vue.directive(my-directive, {bind: function(el, binding, vnode) {// 指令绑定时的处理逻辑el.style.backgroundColor yellow;el.addEventListener(focus, function() {el.style.color red;});},inserted: function(el, binding, vnode) {// 指令插入到 DOM 中的处理逻辑},update: function(el, binding, vnode, oldVnode) {// 组件更新时的处理逻辑},unbind: function(el, binding, vnode) {// 指令解绑时的处理逻辑el.removeEventListener(focus);}
});// 创建 Vue 实例
new Vue({el: #app,
});
/script在上述代码中我们使用 Vue.directive 方法注册了一个名为 my-directive 的全局自定义指令。该指令包含了几个生命周期钩子函数例如 bind、inserted、update 和 unbind你可以根据需要实现相应的逻辑。
在示例代码中bind 钩子函数用来在指令绑定时设置元素的背景颜色为黄色并添加一个焦点事件监听器当聚焦到输入框时将文字颜色设置为红色。
在 Vue 实例的模板中我们通过 v-my-directive 指令将这个自定义指令应用到了一个 input 元素上。当 Vue 渲染该模板时my-directive 指令就会被触发执行对应的逻辑。
请注意以上示例代码是注册一个全局自定义指令的基本用法你可以根据实际需求进行扩展和修改。
8、请说出下面代码的输出结果
const arr1 [{id:1,name:name1},{id:2name:name2}
]const arr2 [...arr1] // 复制数组浅拷贝
arr1[0].namenamel-undate // 会改变复制后的数组
console.log(arr2[0].name) // const obj {a:aab:bb
}
const obj2 {...obj} // 复制对象形成新对象
obj.aaa-update // 不会影响新对象
console.log(obj.a)
console.log(obj2.a)对于更新后的代码如下
const arr1 [{id: 1,name: name1
}, {id: 2,name: name2
}];const arr2 [...arr1];
arr1[0].name namel-update;
console.log(arr2[0].name);与之前相同arr1是一个包含两个对象元素的数组。通过扩展运算符[...arr1]将arr1数组的元素复制给了新数组arr2。同样地这仍然是浅拷贝意味着修改arr1中的第一个对象的name属性会影响到arr2中的对应元素。因此console.log(arr2[0].name)将输出namel-update。
const obj {a: aa,b: bb
};
const obj2 {...obj};
obj.a aa-update;
console.log(obj.a);
console.log(obj2.a);在这段代码中我们使用了对象展开语法{...obj}来将obj对象进行复制。这种方式会创建一个新的对象并将原始对象中的属性复制到新的对象中。因此obj2是一个独立的对象其属性与原始对象obj相同但值不共享。
由于我们将obj对象的a属性值修改为aa-update所以console.log(obj.a)将输出aa-update。而console.log(obj2.a)将输出原始的属性值aa因为obj2是在修改之前复制的对象。
综上所述使用对象展开语法{...obj}可以实现对象的浅拷贝并且复制后的对象与原始对象的属性值不共享。
9、介绍下浅拷贝和深拷贝
浅拷贝和深拷贝是两种常用的对象或数组复制方法它们在复制过程中处理引用类型数据的方式不同。
浅拷贝 浅拷贝是指创建一个新对象或数组将原始对象或数组的值复制到新对象或数组中。当复制引用类型数据时只复制了引用地址而不是创建新的独立副本。浅拷贝后的新对象或数组与原始对象或数组共享相同的引用值修改其中任何一个的属性或元素会影响到另一个。常见的浅拷贝方式包括扩展运算符[...arr]或{...obj}、Object.assign()等。 深拷贝 深拷贝是指完全复制一个对象或数组及其所有嵌套的对象或数组得到一个全新且独立的副本。这样两个对象/数组互不干扰修改其中一个不会影响另一个。 深拷贝复制了所有层级的数据包括基本类型和引用类型数据递归地遍历复制每个对象或数组的属性或元素。 常见的实现深拷贝的方式包括循环递归、JSON序列化与反序列化、使用第三方库如Lodash或深拷贝工具函数等。 实现深拷贝的方式有多种。以下是几种常见的方法 手动递归遍历 这是一种基本的深拷贝方法通过递归遍历对象或数组的每个属性并创建完全独立的副本。 JSON 序列化和反序列化 这种方法利用 JavaScript 中的 JSON 对象提供的序列化JSON.stringify和反序列化JSON.parse功能将对象转换为字符串再解析为新对象实现深拷贝。但需要注意该方法无法复制函数、正则表达式等特殊对象。 使用第三方库: LodashLodash 是一个流行的 JavaScript 实用工具库其中提供了 _.cloneDeep 方法来进行深拷贝。jQueryjQuery 的 extend 方法可以实现深拷贝例如 $.extend(true, {}, obj)。Immutable.jsImmutable.js 是一个持久化数据结构的库它通过使用树结构和哈希映射实现高效的数据共享。这个库的数据结构天生支持深拷贝。 需要注意的是虽然这些方法都可以实现深拷贝但在处理特定类型的数据或大型对象时可能会存在性能问题。在选择使用哪种方法时请考虑到数据的复杂性、大小和需要拷贝的深度并根据实际情况选择最适合的深拷贝方式。
在选择使用浅拷贝还是深拷贝时需要根据具体需求和数据结构来决定。如果对象/数组的属性值不包含引用类型数据、或者对共享值的修改无影响那么可以使用浅拷贝如果需要完全独立且不受影响地操作复制后的对象/数组那么应该使用深拷贝。
10、websocket 如何监听心跳
WebSocket 是一种基于 HTTP 协议进行双向通信的协议通信的两端可以同时发送和接收数据。在 WebSocket 连接中心跳包用于维持连接的存活状态可以通过定时发送特定格式的数据包来检测连接是否处于正常工作状态。
具体地可以通过在服务器端设置一个定时器在一定时间间隔内发送心跳包。客户端也可以实现类似的逻辑在一定时间内没有收到服务端的消息则认为连接出现了问题需要重新连接或重连。WebSocket 还支持 PING 和 PONG 控制帧可以使用这些帧来检测连接是否存活。当客户端发送 PING 帧时如果服务端成功接收到则会回复一个 PONG 帧客户端可以通过检查是否收到 PONG 帧来判断连接状态。在大部分情况下开启心跳机制是很重要的因为它可以避免因长时间没有通信而导致的连接中断保持 WebSocket 的稳定性和可靠性。
前端在监听 WebSocket 心跳时可以通过在客户端实现定时器来检测连接是否存活。具体地可以在连接建立成功后设置一个定时器定期发送心跳包到服务器并在服务器端回复的限定时间内未收到相应的数据包则认为连接异常需要重新连接或重连。
同时WebSocket 还提供了 onclose 事件和 onerror 事件用于监听连接的关闭和错误。当 WebSocket 断开连接或发生错误时这两个事件都会触发可以在事件回调中实现自定义逻辑比如进行重新连接或提示用户等。需要注意的是在监听 WebSocket 事件时也要考虑到接口文档中相关的限制条件避免违反相关法律法规
11、前端 http 如何转 https
在前端应用中可以通过以下步骤将 HTTP 转换为 HTTPS
获取 SSL 证书获取可信的 SSL 证书是使用 HTTPS 的前提条件。可以通过商业 CA 颁发或自签署证书的方式获得 SSL 证书。
在服务器上部署 SSL 证书一旦 SSL 证书准备好了就需要在 HTTP 服务器如 Nginx、Apache 等上部署证书。这里假设已经正确部署了 SSL 证书。
修改源代码在 Web 应用程序的源代码中需要将所有 http:// 替换为 https://。可以使用全局搜索替换的方法也可以在部分需要保护的地方手动修改。
重定向 HTTP 请求到 HTTPS使用服务器级别的 URL 重定向可以将未加密的请求强制重定向到 HTTPS URL。例如在 Nginx 中可以在 server block 中添加以下配置段:
server { listen 80; server_name example.com; rewrite ^(.*)$ https://$server_name$1 permanent; } 这些配置将启用监听80端口并将所有HTTP流量重定向到匹配的 HTTPS地址。
测试 HTTPS 连接完成前面的步骤后应该测试 Web 应用程序是否正确地使用 HTTPS 实现形式。通常HTTPS 的开发和调试可能会遇到各种问题包括混合内容、证书错误等。
需要注意的是对于某些特殊情况比如不想购买 SSL 证书、只需要加密部分内容等情况也可以使用一些其它方法来实现 HTTPS 化。但无论使用何种方法都应该遵循安全性最佳实践并谨慎处理您的用户数据。
12、前端 同步可以调用异步吗异步调异步呢
前端同步代码是会阻塞 UI 线程的如果在同步 JavaScript 代码中调用异步函数由于异步函数通常需要等待返回结果那么可能会使页面长时间无响应。因此在同步 JavaScript 代码中调用异步函数会导致页面卡死甚至崩溃不可取。
同时在异步 JavaScript 代码中调用异步函数也需要小心。虽然异步函数通常会使用 Promise 或回调方式来处理异步操作并且在执行时不会阻塞 UI 线程但是在多层嵌套异步调用时可能会出现回调地狱问题通过 async / await 方式可以有效解决这个问题。
为了避免上述问题建议在编写JavaScript 代码时谨慎考虑同步和异步调用的方式尽量避免长时间阻塞UI线程并通过适当的方式来处理异步回调嵌套和错误捕获等相关问题。
13、视频在安卓可以正常播放在 ios 无法正常播放?
视频在安卓可以正常播放而在iOS上无法正常播放这可能是由于使用了一种不受 iOS 设备支持的视频编解码格式所致。
iOS 上支持播放的视频编解码格式包括 H.264、HEVC(H.265)、MPEG-4 Part 2 和 Motion JPEG 等。而在 Android 上通常支持更多的视频编解码格式包括 VP9、AV1 等开源编解码格式。因此如果使用了 iOS 不支持的视频编解码格式将导致视频无法在 iOS 设备上正常播放。
为了确保视频能够在所有设备上正常播放建议尽可能使用受广泛支持的视频编解码格式。如果需要使用某些特定编解码格式则需要进行额外处理例如在视频上传时将其转换为不同的格式以适应不同的设备。
同时也可以考虑使用跨平台的视频播放库例如 Video.js 或 Jwplayer 等它们通常会自动检测设备并使用相应的编解码格式进行播放从而最大限度地提高视频的兼容性。
14、前端 如何对后台接口进行封装
在前端进行后台接口封装可以提高代码的可读性、稳定性和重用性同时也能更好地维护后台接口降低与后端的耦合度。以下是一些推荐的封装方式
1。封装 Ajax 请求函数将常见的 Ajax 请求封装成函数在函数内部处理请求、响应错误和数据转换等逻辑以减少冗余代码和复杂度。
2。封装 API 接口对象将后端 API 接口封装到一个对象中每个接口用一个方法表示并统一处理请求和响应。这种方式方便集中管理所有 API 接口易于调用和测试。
3.封装数据模型类将后端的接口数据结构封装为一个前端数据模型类提供相应的方法进行数据操作和业务处理。这种方式可以有效抽象出基础业务逻辑和处理过程并实现数据缓存、数据加工等功能。
4.中间件封装使用中间件框架如 axios-interceptors 等对 HTTP 请求和响应进行拦截和处理实现许多共同的需求例如鉴权、数据缓存和统一错误处理等。
5.Hook 封装使用 React 的 Hook 监听组件生命周期或者自定义 Hook对接口进行处理使 Hooks 像普通函数一样调用接口并且 Hooks 也可以处理接口并返回值。
15、promise 原理
Promise 是 ES6 新增的一种异步编程方式它是一种对象用于表示异步操作的最终完成或失败及其结果值。Promise 的核心理念是将异步操作放入 Promise 中并返回一个代表着该异步操作结果的 Promise 对象以便在异步操作完成时进行处理。
Promise 的基本原理如下
Promise 对象有三种状态pending、fulfilled 和 rejected。初始状态为 pending待执行完成后会变成 fulfilled已解决或 rejected已拒绝。
Promise 对象通过 then 方法注册回调函数当异步操作执行完毕后通过 resolve 方法或 reject 方法确定最终状态并将结果作为参数传给回调函数。
then 方法可以链式调用每个 then 方法中的回调函数都会接受上一个 then 方法中回调函数返回值作为参数。
如果在 Promise 构造函数中发生错误Promise 对象就会立即进入被拒绝rejected状态并且抛出异常。
基本语法
js new Promise((resolve, reject) { // 执行异步操作 if (/* 异步操作成功 */) { resolve(result); // 将结果传递给回调 } else { reject(error); // 将错误信息传递给回调 } }).then(result { // 注册回调函数 // 处理异步操作成功后的结果 }).catch(error { // 捕捉 Promise 抛出的异常 // 处理异步操作失败后的错误 }); 该 Promise 对象会调用一个函数该函数有两个参数resolve 和 reject。执行结果成功时调用 resolve 方法将结果传递给 then 函数回调执行结果失败调用 reject 方法向 catch 函数传递错误信息。then 函数中接收到上一个 promise 函数的执行结果回调处理该结果catch 函数接收前面抛出的错误并进行处理。
以上方式都能有效封装前端与后端之间的接口提升开发效率与维护便利性根据实际项目需求进行选择即可。
16、uniapp 如何自动化打包
UniApp 提供了通过命令行自动化构建打包的方式。具体步骤如下
1.在项目根目录下使用命令行进入到 HBuilderX 安装目录中的 uni-cli/bin 目录下。
2.运行以下命令行安装打包所需依赖
npm install3.在 uniapp 项目目录下修改 package.json 文件的 scripts 字段添加如下脚本代码 “scripts”: { “build:h5”:“cross-env NODE_ENVproduction uni-build --h5” } 4.修改完 package.json 后在命令行运行以下命令 npm run build:h5 此时会在项目根目录的 dist 目录下生成 h5 平台所需的文件。其它平台的操作方法与 h5 平台类似在 scripts 中添加对应的命令即可。
需要注意的是在使用自动化构建打包前需要先进行配置相关打包参数等内容例如 app 配置及 manifest.json 配置等。此外若需要生成离线包则需要在 HBuilderX 的菜单栏中选择 「发行」-「云端打包」登录后按照页面的提示操作即可。
17、微信小程序凭证?
在微信小程序中每个小程序必须拥有自己的 AppID 和 AppSecret这两个参数被称为小程序的凭证用于进行认证和接口调用。
*AppID是一个字符串是小程序的唯一标识符与开发者帐号相关联。 *AppSecret是一个字符串是小程序的加密密钥与App ID相关联。App Secret用来进行小程序后端的身份验证如果泄露此密码可能会导致恶意用户获取您小程序的数据或者搞乱各种API等操作。 AppID 是小程序的唯一标识符可通过微信公众平台申请获得。而 AppSecret 则是用于对接口进行签名的密钥需要开发者在开发者中心中手动设置。
在使用微信小程序的开放接口时需要使用小程序的凭证进行接口调用同时还需要针对不同的接口填写相应的参数。具体接口调用方法可以参考微信小程序开发文档文档中提供了详细的接口说明和调用示例。
18、公众号怎么实现微信支付
要在公众号中实现微信支付需要进行以下步骤
1.在微信商户平台注册账号并进行实名认证获取商户号和密钥等信息。
2.使用商户号和密钥申请微信支付接口的api密钥和证书。
3.在公众号开发者后台中设置支付授权目录和JSAPI支付安全域名并开通JSAPI支付功能。
4.实现支付功能使用微信支付接口的统一下单 API向微信支付服务器发起请求获取预支付交易会话标识prepay_id。
5.客户端调用JSAPI方法拉起微信支付界面用户确认支付后完成支付过程。
具体的步骤和操作细节可以参考微信官方文档中提供的支付接口开发指南以及相关的示例代码和接口文档。在实现支付功能时还需要注意数据加密和处理异常情况等问题。另外建议开发者严格按照微信支付平台的标准接入流程进行开发和测试并在审核前仔细检查相关功能是否符合规范要求。
19、前端 实现支付宝支付
在前端实现支付宝支付需要进行以下步骤
1.在支付宝开发者平台创建应用并进行认证获取应用ID和密钥等信息。
2.采用支付宝提供的SDK或接口将应用与支付宝账号关联。
3.实现支付功能使用支付宝提供的统一收单下单并支付页面接口向支付宝服务器发送下单请求并获得一个唯一的订单号和支付宝跳转链接。
4.将跳转链接进行二维码生成或者页面渲染展示给用户以完成支付过程。
具体的步骤和操作细节可以参考支付宝官方文档中提供的支付接口开发指南以及相关的示例代码、接口文档。在实现支付功能时还需要注意数据加密和处理异常情况等问题。另外建议开发者严格按照支付宝平台的标准流程进行开发和测试并在审核前仔细检查相关功能是否符合规范要求。
20、前端如何识别 后端返回链接 是否是图片
前端可以通过以下方式判断后端返回的链接是否是有效的图片链接
利用JavaScript的Image对象进行预加载如果加载成功则说明该链接是有效的图片链接。
function checkImage(url, success, error) { var img new Image(); img.onload success; img.onerror error; img.src url; } checkImage(‘https://example.com/image.jpg’, function() { console.log(‘Valid image URL’); }, function() { console.log(‘Invalid image URL’); });
2.判断链接是否以常见的图片格式结尾例如.jpg、.png、.gif等。
function isImageUrl(url) {return (url.match(/\.(jpeg|jpg|gif|png)$/) ! null);
}console.log(isImageUrl(‘https://example.com/image.jpg’)); // true 以上两种方式都可以判断链接是否是有效的图片链接同时建议在展示图片前对数据进行合法性检查和类型判断。另外需要注意的是由于网络原因或服务器问题等链接可能会返回404或其他错误码因此就算链接是有效的图片链接也不能100%保证一定能够展示出来请开发者在使用时进行细致的测试和异常处理。
21、前端怎么解决页面白屏
检查网络连接是否正常确保 CDN 或静态资源能够正常加载。
检查代码中是否有语法错误、逻辑错误或其他代码问题可以使用开发者工具进行调试并检查控制台报错信息。
确认浏览器版本是否支持所使用的技术如是否支持 ES6、CSS Grid等。如果不支持建议降级或用 polyfill 处理。
预加载相关资源例如预加载 HTML、CSS、JS 文件。
将关键 CSS 和 JS 文件放在 head 标签中以便在文档加载时就能够渲染。
合理、简化页面结构和样式尽量减少 HTTP 请求次数和响应容量。
使用浏览器工具对页面加载过程进行分析找到具体瓶颈并进行优化。
22、dom树加载机制
DOM文档对象模型树是浏览器解析 HTML 文档后所形成的一种树形结构表示页面中所有 HTML 元素及其层级关系。浏览器在加载页面时会先经过以下阶段
解析 HTML 文件当浏览器请求一个 HTML 页面时它会读取并解析文件内容构建出 DOM 树。
构建 DOM 树解析 HTML 文件时遇到标签、属性和文本节点等元素都会在内存中创建相应的节点对象并按照层级关系构建 DOM 树节点对象具有父子关系DOM 树是由多个节点所组成的树形结构。
生成 Render Tree浏览器在解析 DOM 树时会将 CSS 样式与每个节点除非该节点对应的 CSS 尚未下载结合起来生成一个带有样式信息的 Render 树该树表示了网页的渲染结构。
布局Layout最终的Render树事先排版布局即确定每个节点在屏幕上的准确位置和大小。同时还需要确定节点的颜色、字体、边框、背景色、图像等外观效果以便后续的绘制工作。布局过程会根据盒模型、文档流等特性进行计算。
绘制Painting在已经确定好的节点位置上进行绘制即显示出文本、图片、背景等视觉效果。
以上就是浏览器加载页面时DOM树的加载机制涉及的一系列流程和步骤。
CSS 盒模型Box Model是用于描述和布局 HTML 元素的一种模型。它表示每个元素都被包含在一个矩形框中该框由内容区域、内边距padding、边框border和外边距margin组成。盒模型的组成部分如下1. 内容区域Content- 包含元素的实际内容如文本、图像等。- 通过设置 width 和 height 可以调整内容区域的尺寸。2. 内边距Padding- 内边距是内容区域与边框之间的空白区域。- 可以使用 CSS 的 padding 属性为元素设置内边距。3. 边框Border- 边框是围绕内容区域和内边距的线条或样式。- 可以使用 CSS 的 border 属性来定义边框的样式、宽度和颜色。4. 外边距Margin- 外边距是元素与其周围元素之间的空白区域。- 可以使用 CSS 的 margin 属性设置元素的外边距。在标准盒模型中元素的总宽度计算公式为总宽度 内容区域宽度 左内边距 右内边距 左边框 右边框 左外边距 右外边距。同样总高度的计算也是类似的。CSS 盒模型提供了灵活的布局控制使用不同的属性和值可以实现各种布局效果。可以通过设置盒模型的属性来调整元素的尺寸、间距和边框样式以满足设计需求。23、前端页面渲染机制
前端页面渲染机制主要涉及的是浏览器如何对网页进行解析、构建和渲染的过程。具体来说它通常包括以下几个阶段
构建 DOM 树当浏览器接收到 HTML 页面时会按照标签的层次关系依次解析文件内容并根据标签生成对应的 DOM 元素节点。
解析 CSS 样式在 CSS 阶段中浏览器会处理样式表信息解析出每个元素所对应的应用样式并将其合并组成一个规则列表。
生成 Render 树Render 树是一个包含每个节点和其对应样式属性信息的树形结构与 DOM 树类似。它的每个节点都代表了文档中相应的可视化元素。
布局Layout在 Render 树构建完成后浏览器会进入布局阶段计算出每个节点的位置和尺寸信息并确定它们在屏幕上的呈现方式。
绘制Painting布局完成后浏览器就可以进入绘制阶段将页面内容绘制到屏幕上。
总的来说浏览器在渲染页面时会从 DOM 树、CSS 样式、JavaScript 脚本等多个方面对页面进行处理。在实际应用中为了提高页面的性能还需要注意避免网络请求、资源重复加载等问题。
25、介绍下http\https 协议
HTTPHypertext Transfer Protocol是一种应用层协议它是 Web 技术的基础之一。通过 HTTP 协议客户端和服务端之间可以有效地传输数据和通信。HTTP 协议支持多种请求方法如 GET、POST 等同时还定义了可接受的请求头域以及状态码等规范。
HTTPSHyperText Transfer Protocol Secure是在 HTTP 协议之上添加了加密和认证机制的协议。在 HTTPS 中服务器需要使用 SSL 或 TLS 协议对数据进行加密处理并通过数字证书验证身份保证服务器和客户端之间的通信安全性。
具体来说简单介绍一下 HTTP 和 HTTPS 的区别
安全性HTTP 是明文传输协议不提供任何加密方法。而 HTTPS 利用加密技术保证信息传输过程中的机密性和完整性更为安全。
数据传输方式HTTP 以明文方式传输数据所有数据都可以在网络上被监听和窃取。而 HTTPS 则通过 SSL/TLS 加密来保障数据的安全传输。
默认端口HTTP 默认使用端口号为80HTTPS 默认使用端口号为443。
证书要求HTTPS 涉及到证书的验证和签发在使用 HTTPS 前需要申请SSL证书并且要求使用 HTTPS 的网站必须拥有有效的数字证书。
综上所述HTTP 协议是一种常用的协议在访问普通网站的过程中使用较多而对于数据传输要求更高、安全性更为重要的网站则选择使用 HTTPS 协议。
26、get、post怎么传参
无论是 GET 还是 POST 请求方式都可以通过参数来传递数据。一般来说GET 请求方式适合请求简单的数据而 POST 适合提交复杂数据或者包含文件上传等操作。
GET 请求方式传参 在 GET 请求方式中参数一般写在 URL 的末尾使用 ? 来分隔 URL 和参数多个参数之间使用 符号来连接。例如 http://example.com/path/to/file?key1value1key2value2 在实际代码中可以使用 JavaScript 中的 fetch 函数客户端或者 PHP 中的 $_GET 服务端获取 GET 参数进行数据的传递和获取。
POST 请求方式传参 在 POST 请求方式中参数不会跟在 URL 后面像 GET 方式一样而是作为数据在请求体中进行提交。提交的数据可以是字符串、JSON 对象、FormData 对象等。例如 const data { key1: ‘value1’, key2: ‘value2’ };
fetch(‘http://example.com/path/to/file’, { method: ‘POST’, body: JSON.stringify(data), headers: { ‘Content-Type’: ‘application/json’ } }) 在 PHP 中可以使用 $_POST 服务端获取 POST 参数 或 file_get_contents(‘php://input’) 等方法获取 POST 数据。
需要注意的是GET 方法提交的数据长度有限制且数据会直接暴露在 URL 上不适合传输敏感信息而 POST 方法提交的数据可以存储大量数据也更为安全但是需要服务器进行处理。因此在实际开发中需要根据具体情况合理选择 GET 或 POST 方式来传递参数。
27、vuex 数据传参导致白屏
Vuex 是 Vue.js 的一个状态管理库主要用于 Vue 应用中的数据共享问题。Vuex 存放在集中式存储中的状态是响应式的能够做到在组件间传递和共享数据。
关于Vuex 数据传参导致白屏的问题可能存在以下原因
Vuex 数据传递出现了循环依赖导致系统进入死循环最终导致白屏。可以通过检查数据流向来判断是否有这种情况。
在使用 Vuex 进行数据传递时没有及时调用异步操作需要的命令导致白屏。这种情况下需要检查代码确保异步操作的正确执行。
由于异步操作或其他原因Vuex 中的数据未成功加载但系统却尝试渲染视图导致出现白屏。此时需要确保数据正常加载以后再进行视图渲染。
针对以上问题可以针对具体的情况分别处理。例如在处理循环依赖的问题时可以修改代码结构、减少数据依赖关系等方式来解决在处理异步操作相关问题时需要仔细考虑应该何时调用异步操作并且要编写良好的错误处理机制避免出现无法处理的异常情况对于数据加载问题可以使用 Vue.js 提供的生命周期函数来确保数据加载完成后再进行视图渲染。
28、WebSocket 线程过多通常是因为服务器无法在实时处理所有客户端请求所致。想要解决这个问题可以尝试以下几种方法 调整 WebSocket 同时连接的数量限制我们可以调整 WebSocket 的同时连接数。例如如果服务器只支持 1000 个连接并且正在处理第 1001 个连接则新的连接会被拒绝。通过调整同步连接数的限制可以降低线程并发量和资源消耗。 根据消息的价值和重要性进行数据分类和筛选服务器可以根据不同类型的消息内容将其分配到不同的线程中进行处理以避免单一线程处理太多数据导致负载过大。这可以通过使用消息队列等技术实现。 水平扩展服务器如果以上两种方法无法解决问题则可以使用水平扩展服务器的方法来增加系统的集群节点。这种方法可以通过添加更多服务器同时处理客户端请求来提高服务器的吞吐量和并发性能。
总之处理 WebSocket 过多线程的方法有很多具体取决于您的应用场景和实际需求。
29、在 Canvas 中处理音频通常需要结合 Web Audio API 来实现。Web Audio API 是一个用于处理和控制音频的高级 API它提供了许多函数和接口可以很容易地在 JavaScript 中创建音频模型并控制其行为。
下面是展示如何使用 Web Audio API 在 Canvas 上处理音频的基本步骤 加载音频文件使用 JavaScript 的 Audio 对象来加载音频文件。 创建音频上下文对象创建一个 AudioContext 对象。该对象将被用于从音频文件中读取数据并传递到 Web Audio API。 解码音频数据调用 AudioContext 对象的 decodeAudioData() 方法解码音频数据。 创建音频节点根据需要创建各种类型的音频节点例如 SourceNode、GainNode、DestinationNode 等。节点将构成一个音频处理图谱每个节点都会执行一些特定的音频操作。 将音频节点连接起来使用 connect() 方法将音频节点与其他节点连接起来形成所需的音频处理图谱。由此形成的处理图可以通过简单的组合来实现从音频文件读取数据并对其进行处理。 开始播放音频调用 start() 方法启动音频轨道中的播放或停止音频播放 。
在 Canvas 中处理音频的能力不仅限于上述步骤。实际上 Canvas 有一些内置功能可用于处理和操作音频例如使用图形来控制音频播放、操纵声音的可视化效果和创建有趣的交互式体验。具体取决于您的需求和想要实现的功能。
30、 在 Webpack 中配置 rem
想要在 Webpack 中配置 rem可以采用以下步骤
安装 postcss-loader 和 postcss-pxtorem 插件
在终端中输入以下命令安装相关插件
npm install postcss-loader postcss-pxtorem -D配置 postcss-loader
在 webpack.config.js 文件中的 module.rules 数组中添加 postcss-loader如下所示
module.exports {// ...module: {rules: [// ...{test: /\.css$/,use: [style-loader, css-loader, postcss-loader]}]}
};配置 postcss-pxtorem
在项目根目录创建 postcss.config.js 文件并配置 postcss-pxtorem 插件如下所示
module.exports {plugins: [require(postcss-pxtorem)({rootValue: 75, // 根字体大小propList: [*], // 全部选择器都需要转换mediaQuery: false // 是否开启媒体查询模式})]
};设置 HTML 文档的 font-size
在 index.html 的 head 标签内设置 HTML 文档的 font-size如下所示
!DOCTYPE html
htmlheadmeta charsetutf-8titleWebpack Project/titlemeta nameviewport contentwidthdevice-width,initial-scale1,minimum-scale1,maximum-scale1,user-scalablenostylehtml {font-size: 100px; /* 此处设置为100px与 rootValue 相同 */}/style/headbody!-- ... --/body
/html最后重新运行 Webpack 即可实现在 CSS 中使用 rem。
31、Webpack 解决样式兼容问题
为了解决样式兼容问题可以考虑在 Webpack 中配置 PostCSS 插件 Autoprefixer它可以自动添加浏览器前缀从而简化前端编写 CSS 样式时的兼容性处理工作。
使用 Autoprefixer 需要以下步骤
安装 postcss-loader 和 autoprefixer
执行以下命令来安装所需依赖
npm install postcss-loader autoprefixer -D在 webpack.config.js 配置文件中添加 loader
在 module.rules 数组中添加处理 CSS 文件的 loader连同 Autoprefixer 插件一起使用。下面是示例代码
module.exports {// ...module: {rules: [// ...{test: /\.css$/,use: [style-loader,css-loader,{loader: postcss-loader,options: {plugins: [require(autoprefixer)]}}]}]}
};在 package.json 文件中添加 browerslist 配置
建议将 Autoprefixer 和 Babel 一起配合使用来兼容更多 JavaScript 特性和一些旧版浏览器。将目标浏览器放在项目根目录的 package.json 文件的 browserslist 配置项中。例如
browserslist: [ 1%,last 2 versions
]这条指令的意思是我们需要支持全球占有率 1% 及以上的浏览器并且支持最新的两个主要浏览器版本。
经过上述步骤就完成了 Autoprefixer 的配置。在接下来开发和打包过程中Autoprefixer 将自动解析 CSS并根据你的 browserslist 配置添加相应的前缀从而帮助解决跨浏览器兼容性问题。
32、Vite
Vite 是 Vue.js 创始人尤雨溪开发的一款面向现代浏览器的轻量级前端开发服务器和构建工具。它提供了快速的冷启动内置开箱即用支持 Vue 单文件组件 (SFC) 的 devServer并能够在开发期间基于 ES Module 进行非常快速的模块热更新 (HMR)从而解决了 Webpack 打包重启时间过长的问题。
下面是 Vite 的主要特点 快速冷启动通过 ES Module 的特性在服务启动时只需要编译当前需要的文件而不需要像 Webpack 那样一次性编译整个项目从而提升了冷启动速度。 实时模块热替换基于 Vue 3 的 Reactivity API 实现可以更精准地定位需要替换的模块同时也提供了对 CSS 和自定义模块类型的支持可以加快前端开发的速度。 内置优化插件通过内置的预处理插件以及针对生产环境的打包优化可以帮助用户轻松地实现优化产品代码的目的。 零配置默认配置下就可以直接使用无需进行额外的配置。 可扩展性Vite 提供了 API 和插件体系方便用户根据需求扩展、优化相关功能。
使用 Vite 可以轻松实现项目开发和构建其兼容多种框架 (Vue / React / Preact / LitElement)支持 TypeScript、SCSS、LESS 等多个前端开发技术栈并且提供了快速开发、更佳的开发体验和更快的构建效率。
33、vite 的基本使用
使用 Vite 是非常简单的下面介绍如何使用 Vite 来搭建 Vue.js 应用程序。
安装
首先需要全局安装 vite/cli
npm install -g vite 或者 yarn global add vite创建项目
使用以下命令创建一个新的 Vue 项目
npm init vite-app my-project 或者 yarn create vite-app my-project安装依赖
进入项目目录并安装依赖
cd my-project
npm install 或者 yarn运行项目
执行以下命令运行项目
npm run dev 或者 yarn dev然后打开浏览器访问 http://localhost:3000 即可查看应用程序。
构建项目
使用以下命令构建项目
npm run build 或者 yarn build构建完成后所有打包好的文件都会生成在 dist 目录下。
以上就是使用 Vite 搭建 Vue.js 应用程序的基本流程。同时 Vite 也支持其他前端框架只需按照相应文档进行相应设置即可。
34、vue 的 object.prototype 为什么监听不到数组 (对象) 里面变化
Vue 在实现数据双向绑定时使用了 Object.defineProperty() 方法来为对象上的属性添加 getter 和 setter从而实现数据响应式。
但是Object.defineProperty() 方法只能劫持对象的属性而不能劫持整个对象或数组。这就意味着当你改变数组中的某一个元素时它是不会触发响应式更新的因为 Vue 并没有劫持数组元素本身的 setter。
由于数组是一种比较特殊的对象类型在 JavaScript 中最好使用 Array 类型提供的方法对其进行操作。这些数组的方法可以触发响应式更新例如 push、pop、splice 等等。所以推荐在 Vue 中我们尽量使用这些数组方法来修改数组中的元素从而保证数据响应式。
总结一下Vue 监听不到对象里面的数组变化是因为 Object.defineProperty() 方法只能劫持对象的属性而不能劫持整个对象或数组但是 Vue 提供了一些数组的方法可以触发响应式更新。因此我们在Vue使用中建议使用这些数组方法来修改数组中的元素。
35、那在vue中怎么做到监听数组 (对象) 里面变化呢
在 Vue 中要监听数组内部元素的变化可以使用一个名为 Vue.set 或者 this.$set 的方法来实现。这个方法会将新增的元素转换为响应式数据并触发界面的重新渲染。
Vue.set(obj, propertyName/index, value) 方法接收 3 个参数
obj需要被添加属性的对象或数组propertyName/index需要添加的属性名或索引值value新添加的属性值
例如
// 数组增加元素
this.$set(this.itemList, 1, newItem);// 对象增加属性
this.$set(this.userInfo, name, 张三);除此之外Vue 还提供了一种 Watch 深度监测的方法即开启深度监测。开启后当你改变对象或数组中的属性时Vue 将递归地遍历对象以追踪所有属性并绑定Getter和Setter。这个选项默认关闭由于其性能影响我们不应该在所有属性都需要检测的情况下开启它仅在必要时才考虑使用。开启方式如下
var vm new Vue({data: {users: [{ id: 0, name: Peter }]},watch: {users: {deep: true,handler: function(newUsers, oldUsers) {console.log(users changed!)}}}
})代码中的 { deep: true } 表示开启深度监测handler 函数会在 users 数组发生变化时被调用。
总之使用 Vue.set 和深度监控可以实现数组对象的监听但是开销比较大。建议尽量使用数组提供的方法来代替直接对数组进行操作以获得更好的性能和用户体验。
1、用 $set() 和 Object.assign() 方法可以给对象增加响应式数据
2、数组的 pop(),push() 等方法可以实现响应式 主要是因为 Vue 使用了特殊的技巧来拦截数组的变异方法mutation methods。
Vue 的响应式系统在创建响应式对象时会将数组的原型进行改写并劫持了数组变异方法比如 push()、pop()、shift()、unshift()、splice()、sort() 和 reverse() 等。当调用这些数组方法时Vue 能够观察到数组的变化并通知相关的依赖进行更新。
具体的实现原理如下
当创建一个响应式数组时Vue 会将数组的原型指向一个改写过的数组原型 Array.prototype。改写过的数组原型包含了对数组变异方法的重新定义以使它们能够触发依赖更新。当使用 push() 方法向数组中添加新元素时Vue 能够捕捉到这个变化并在内部执行相应的操作 添加新元素到数组中。将新元素设置为响应式并进行依赖追踪。触发视图更新重新渲染包含该数组的组件。
通过以上步骤Vue 实现了对数组变异方法的拦截与处理使得数组的变化能够被响应式系统检测到并进行相应的更新操作从而实现了数组的响应式。
需要注意的是Vue 的这种拦截和劫持机制只能处理使用变异方法改变的数组数据对于直接通过索引修改数组元素或者改变数组长度的操作Vue 是无法检测到的。为了解决这个问题Vue 提供了一些特殊的方法来处理这类情况比如使用 Vue.set 或者替换整个数组来触发更新等方式。
35、路由 history 原理服务端如何配置?
路由 history 模式通过 HTML5 的 History API 实现它使用 window.history.pushState() 方法动态改变浏览器的当前 url同时也是监听 popstate 事件来更新页面视图。
与 hash 模式不同history 模式可以更改 URL 中的 pathname 和 search 字段而不会在最后添加一个 # 符号。例如对于 URL http://example.com/path/user如果用户点击了链接/update浏览器将会发送一个跳转请求并把显示路径更改为 http://example.com/path/update。
服务端需要根据实际情况进行配置。当在服务端启用历史模式时任何链接都应指向主页。否则当用户在浏览器中输入 URL 时服务器将返回 404 错误这是因为服务端不知道如何处理该 URL。
配置方式分为以下两种
使用服务器框架。如果在使用一些服务器框架例如 Express来构建你的应用程序则需要捕获到所有的路由并渲染 index.html 文件。这个文件将包含您的应用程序当应用程序启动时它将自动渲染正确的组件作为响应。
const express require(express)
const path require(path)const app express()app.use(express.static(path.resolve(__dirname, public)))app.get(*, (req, res) {res.sendFile(path.resolve(__dirname, public, index.html))
})app.listen(3000)配置 Nginx 服务器。另一种是使用 Nginx 或其他支持 Rewrite 规则的 web 服务器来配置。
location / {try_files $uri /index.html;
}以上示例是在 Nginx 配置文件中添加了一个转发规则将所有请求重定向到 index.html 文件并让 Vue Router 接管路由。
总之路由 history 模式通过 HTML5 的 History API 实现它可以更改 URL 的 pathname 和 search字段。服务端需要根据实际情况进行配置比如使用服务器框架或 Nginx 等服务器以捕获到所有的路由并渲染 index.html 文件并让 Vue Router 接管路由。
36、关于 Tree.js 的教程以下是一个基本的介绍
安装和引入 Tree.js 库 首先需要安装 Tree.js 库并将其引用到你的网站上。可以在官方网站下载最新版的库文件或者使用 NPM 安装然后在 HTML 页面中添加引用。
创建渲染器(Renderer) 和场景(Scene) 对象 使用 Tree.js 创建一个 Renderer渲染器对象这个对象将负责把 3D 场景布置在浏览器中。同时创建一个 Scene场景对象作为一个容器把所有需要呈现的 3D 对象都放在其中。
添加相机(Camera) 添加 Camera相机对象即可确定场景视角。将相机放置在一个合适的位置以便捕捉想要呈现的场景。
创建和添加几何图形(Geometry) Tree.js 提供了许多内置几何模型类例如 BoxGeometry立方体几何、SphereGeometry球体几何等您也可以使用自定义几何模型。然后可以使用材质来设置图形的颜色纹理等。
光源和阴影 在 Tree.js 中有不同类型的光源包括环境光、点光源、半球光、方向光、聚光等。在场景中添加适当的光源可以增强图形的真实感并且还可以使用阴影让图形看起来更加立体。
动画和交互 Tree.js 支持各类交互和动画行为例如旋转、移动、缩放、改变颜色等。同时还支持事件处理程序可以监听用户输入、键盘等。
渲染 最后调用 Renderer 对象的 Render 方法即可将整个 3D 场景内部渲染出来。
这里只是对 Tree.js 的一个简单介绍想要深入学习还需要参考相关文档和示例代码。
步骤
Tree.js 是一个基于 WebGL 的 3D 图形开发的 JavaScript 库。它提供了丰富的工具和函数使您可以轻松地创建复杂的动画和交互式 3D 应用程序。下面是 Tree.js 的详细教程
安装和引入 Tree.js
首先在 HTML 文件中引入 Tree.js 库
script srchttps://cdn.rawgit.com/mrdoob/three.js/master/build/three.min.js/script如果您的项目使用 npm 包管理器管理依赖也可以通过以下命令安装 Three.js
npm install three然后在你的项目中以如下方式引用
import * as THREE from three;创建场景
在开始创建 3D 场景之前需要先初始化一个 Scene 对象指定背景颜色等属性。
const scene new THREE.Scene();
scene.background new THREE.Color(0xf4f6f9);添加相机
相机是观察者看到 3D 场景的窗口。在 Three.js 中有许多类型的相机例如透视相机、正交相机等可以根据需求选择一个合适的相机。
const camera new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 5);// 或者使用 OrthographicCamera
const camera new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, 1, 1000);
camera.position.set(0, 0, 5);添加渲染器
渲染器负责通过 WebGL 技术将场景呈现到作为 HTML 元素的画布中。
const renderer new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement); // 将 canvas 元素添加到 document.body 中添加几何体和材质
在 Three.js 中创建 3D 对象需要使用 Geometry(几何体)和Material(材质)它们决定了对象的形状和外观。
// 创建立方体几何体和基本红色材质
const geometry new THREE.BoxGeometry();
const material new THREE.MeshBasicMaterial({ color: 0xff0000 });// 创建网格对象
const cube new THREE.Mesh(geometry, material);// 将网格对象添加到场景中
scene.add(cube);添加光源
当场景变得复杂
37、v-model 指令的修饰符
.nummber 自动将用户的输入值转为数值类型 .trim 自动过滤用户输入的首民空白字料 .Lazy 只有在输入数据结束才会调用不会在输入时调用
38、小程序代码的构成
pages 用来存放所有小程序的页面9 utils 用来存放工具性质的模块 (例如:格式化时间的自定义模块) app.js 小程序项目的入口文件 app.json 小程序项目的全局配置文件 app.wxss 小程序项目的全局样式文件 project.config.json 项目的配置文件 sitemap.json 用来配置小程序及其页面是否允许被微信索引
39.小程序页面的组成部分
小程序官方建议把所有小程序的页面都存放在 pages 目录中以单独的文件夹存在 其中每个页面由 4 个基本文件组成它们分别是: 1、js 文件 (页面的脚本文件存放页面的数据、事件处理函数等) 2、 json 文件(当前页面的配置文件配置窗口的外观、表现等) 3、.wxml 文件(页面的模板结构文件) 4、.wxss 文件(当前页面的样式表文件)
能够使用WXML模板语法染页面结构 wx:if、wx:elif、wx:else、hidden、wx:for、wx:key 能够使用wXSS样式美化页面结构 rpx 尺寸单位、import 样式导入、全局样式和局部样式 能够使用 app.json 对小程序进行全局性配置 pages、window、tabBar、style 能够使用 page,json 对小程序页面进行个性化配置 对单个页面进行个性化配置、就近原则 能够知道如何发起网络数据请求 wx.request() 方法、onLoad()事件
40、微信小程序开发流程
注册小程序账号在微信公众平台注册一个小程序账号注册完成后可以获取到 AppID 和 AppSecret这是后续开发的必备信息。 创建小程序项目使用微信开发者工具创建一个新的小程序项目填写小程序的名称、AppID 等基本信息并选择对应的开发语言小程序支持多种语言开发。 开发页面在微信开发者工具中开发小程序页面包括页面的布局、样式、交互逻辑等。 开发接口在服务器端开发小程序需要的接口比如获取数据、提交表单等功能。 测试和调试开发完成后需要进行测试和调试确保功能正常。 提交审核当开发完成后需要提交审核经过审核后即可发布到小程序平台供用户使用。 公众号网页开发流程 注册公众号账号在微信公众平台注册一个公众号账号注册完成后可以获取到 AppID 和 AppSecret这是后续开发的必备信息。 开发网页在本地开发网页包括页面的布局、样式、交互逻辑等。 接入微信JS-SDK通过引入微信JS-SDK可以在网页中调用微信的API比如获取用户信息、分享等。 进行网页授权通过网页授权可以获取用户的微信身份信息实现个性化的功能。 部署网页将网页部署到服务器上并配置服务器地址、令牌(Token)、消息加密密钥(EncodingAESKey)等信息。 验证服务器配置完成后微信公众平台会向服务器发送一个验证请求需要在服务器中进行验证并返回验证信息。 测试和调试部署完成后需要进行测试和调试确保功能正常。 提交审核当开发完成后需要提交审核经过审核后即可发布到公众号中供用户使用。 总体来说微信小程序开发和公众号网页开发都需要涉及多个方面包括前端、后端、数据库等需要开发者具备一定的技术能力和经验。同时还需要对微信公众平台的规则和限制有一定的了解确保开发的功能符合相关要求。
41、Vuex:
每一个?Vuex?应用的核心就是?store仓库它包含着你的应用中大部分的状态?(state)。 状态管理有5个核心state、getter、mutation、action、module。
State 1、单一状态树定义应用状态的默认初始值页面显示所需的数据从该对象中进行读取。 2、Vuex?使用单一状态树用一个对象就包含了全部的应用层级状态。它便作为一个“唯一数据源”而存在。这也意味着每个应用将仅仅包含一个?store?实例。 3、单一状态树让我们能够直接地定位任一特定的状态片段在调试的过程中也能轻易地取得整个当前应用状态的快照。 4、不可直接对?state?进行更改需要通过?Mutation?方法来更改。 5、由于?Vuex?的状态存储是响应式的从?store?实例中读取状态最简单的方法就是在计算属性中返回某个状态
Getter 1、可以认为是?store?的计算属性对?state?的加工是派生出来的数据。 2、就像?computed?计算属性一样getter?返回的值会根据它的依赖被缓存起来且只有当它的依赖值发生改变才会被重新计算。 3、可以在多组件中共享?getter?函数这样做还可以提高运行效率。 4、在?store?上注册?gettergetter?方法接受以下参数 state,?如果在模块中定义则为模块的局部状态 5、getters,?等同于?store.getters
Mutation 1、Vuex中store数据改变的唯一方法就是mutation 2、通俗的理解mutations?里面装着改变数据的方法集合处理数据逻辑的方法全部放在?mutations?里使数据和视图分离。
Action action?类似于?mutation?不同在于 1、action?提交的是?mutation通过?mutation?来改变?state?而不是直接变更状态。 2、action?可以包含任意异步操作。
Module 1、由于使用单一状态树应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时store?对象就有可能变得相当臃肿。 2、为了解决以上问题Vuex?允许我们将?store?分割成模块module。每个模块拥有自己的?state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
42、Vuex
四大核心属性以及一个子模块管理属性state存放数据Mutations同步修改state里面的数据actions异步修改数据但需要调用Mutations里面的方法修改getters数据过滤器对数据进行整理还有一个modules当数据过多和复杂时将数据用模块化分开在住文件中用modules引用
Leon: 简单回答
store存储变量 mutation发起变量更改记录更改 action发起异步变量更改记录更改 getter计算属性 module模块划分
43、浏览器中的Event Loop是什么它有什么作用
答案Event Loop是浏览器提供的一种机制用于执行JavaScript代码。它主要作用在于监听任务队列将其中挂起的任务分配到可用的线程上去执行。
44、请简述HTML5中nav标签的作用。
答案nav标签用于定义导航栏通常包含一个或多个链接用户通过点击这些链接可以跳转到不同的页面或功能。
45、在Vue.js中computed与watch有什么区别
答案computed属性会在其依赖数据发生改变时自动更新而watch则需要手动监听数据的变化。computed适用于处理复杂的计算并且数据源发生变化时需要经过一些额外的操作而watch则适用于监听某个特定数据的变化并且需要执行一些副作用操作如发送网络请求。
46、请简述React中Virtual DOM的作用。
答案Virtual DOM是React中的一种机制它是一个轻量级的JavaScript对象用于表示真实DOM节点的结构。由于Virtual DOM比真实DOM更容易操作和修改所以React会首先对Virtual DOM进行操作并计算出最小变化集合然后再将这些变化同步到真实DOM节点上从而减少不必要的页面重绘。
47、如何在JavaScript中判断一个变量是否是数组
答案可以使用Array.isArray()方法来判断一个变量是否是数组类型
复制代码var arr [];if (Array.isArray(arr)) {console.log(arr is an array);}48、简述ES6中let和const关键字的作用及区别。
答案let和const都是块级作用域的声明方式用于替代var关键字。它们的主要区别在于const声明的变量必须被初始化并且在后续的操作中不能被重新赋值而let声明的变量则可以被重新赋值。
49、在JavaScript中如何遍历一个对象的属性
答案可以使用for-in循环来遍历一个对象的所有可枚举属性
复制代码var obj {a: 1, b: 2, c: 3};for (var prop in obj) {console.log(prop : obj[prop]);}50、请简述React中props与state的区别。
答案props和state都是React组件中用于存储组件数据的属性但它们的作用略有不同。props通常是由父组件向子组件传递的静态数据子组件无法修改它们只能读取。而state则是组件内部维护的动态数据只能通过setState()方法来修改但这些修改会触发组件的重新渲染。
51、如果希望在React应用中使用Ajax请求请列出至少两种可以实现该功能的库或插件。
答案常用于在React应用中进行Ajax请求的第三方库包括Axios、jQuery和Fetch等。
52、如何在JavaScript中创建自定义异常
答案通过Error对象可以轻松地创建自定义异常可以像下面这样定义并抛出一个名为CustomError的异常
复制代码function CustomError(message) {this.message message;this.name CustomError;}throw new CustomError(Something went wrong);53、在JavaScript中如何判断一个变量是否为null或undefined
答案需要使用“null”运算符来检测一个变量是否为null或undefined代码如下
复制代码var a;if (a null) {console.log(a is null or undefined);}54、如何让一个DOM元素在点击时触发某个事件
答案可以使用addEventListener()方法来在DOM元素上注册事件监听器
复制代码var btn document.querySelector(#myBtn);btn.addEventListener(click, function() {// do something});55、在React中shouldComponentUpdate()生命周期方法的作用是什么
答案shouldComponentUpdate()方法用于控制组件是否需要重新渲染。如果该方法返回false则React将不会重新渲染该组件从而提高页面的性能。
56、如何在JavaScript中判断一个变量是否为NaN
答案可以使用isNaN()函数来判断一个变量是否为NaN
复制代码var num NaN;if (isNaN(num)) {console.log(num is NaN);}57、在CSS中请简述盒模型(Box Model)的概念。
答案CSS中所有元素都被视为一个箱子包含content、padding、border和margin四个部分。盒模型通过这四个部分来定义一个元素的大小、边距和边框等属性。
58、请简述React中的组件生命周期。
答案React中的组件生命周期包括以下几个阶段
Mounting(挂载)组件被创建并插入到DOM中 Updating(更新)组件的state或props发生改变需要重新渲染 Unmounting(卸载)组件从DOM中移除 Error Handling(错误处理)组件在渲染期间发生错误需要进行错误处理。
59、在JavaScript中如何获取当前时间
答案可以使用Date对象来获取当前时间代码如下
复制代码var now new Date();console.log(now.toDateString());60、在React中请简述Refs的作用。
答案Refs是用于在React组件中获取DOM节点或组件实例的方法。它可以通过React.createRef()方法或回调函数的方式来创建并且可以在组件中
61、什么是闭包请举例说明。
答闭包是指函数与其相关引用环境组合的一种特殊情况。简单来说就是一个函数能够访问在函数定义时不在其作用域内的变量。例如
javascript
复制代码
function outerFunction() {let number 10;function innerFunction() {console.log(number);}return innerFunction;
}let closure outerFunction();closure(); // 输出 1062、如何判断一个变量是否为数组类型
答使用Array.isArray()方法。
61、常见的HTTP状态码有哪些请分别说明各自的含义。
答常见的HTTP状态码有以下几个
200 OK 表示请求成功。 201 Created 表示请求成功并创建了新资源。 204 No Content 表示请求成功但无返回内容。 400 Bad Request 表示客户端发送的请求有误。 401 Unauthorized 表示客户端需要身份验证才能访问该资源。 403 Forbidden 表示服务器拒绝提供该资源。 404 Not Found 表示请求的资源不存在。 500 Internal Server Error 表示服务器发生错误。
62、如何检测一个对象是否包含某个属性
答使用in运算符或者Object.hasOwnProperty()方法。
63、解释一下JavaScript中的事件循环。
答事件循环指的是JavaScript在执行代码时处理异步操作的一种机制。当有异步操作时它们会被放到事件队列中等待JavaScript引擎空闲时去处理。事件循环由三个部分组成调用栈、事件队列和微任务队列。当调用栈为空时JavaScript开始处理事件队列中的事件。处理完每一个事件后JavaScript还会执行微任务队列中所有的任务。
64、请说明ES6中let和const关键字的区别。
答let和const都用于声明变量但是它们之间有几个重要的区别
let声明的变量可以修改const声明的变量不能修改。 let声明的变量在同一个作用域内不能重复定义const声明的变量也不能重复定义而且一旦赋值就不能改变。 使用let声明的变量没有定义提升而使用const声明的变量也没有定义提升并且必须在声明的同时就进行赋值。 const声明的变量必须在初始化时赋值否则会抛出异常。
65、jQuery中的on()和bind()方法有什么区别
答bind()方法是jQuery早期的事件绑定方法而on()方法是新版jQuery引入的更强大的事件绑定方法。相比之下on()方法具有以下优势
on()方法支持多个事件同时绑定到同一个元素上而bind()方法只能绑定一个。 on()方法可以为后添加的元素绑定事件而bind()方法不支持这种方式。 on()方法还可以使用命名空间对事件进行管理。
66、JavaScript中的typeof操作符会返回哪些值请说明各自的含义。
答JavaScript中的typeof操作符可以返回以下几种值
“undefined” 表示该变量未定义。 “boolean” 表示该变量是布尔类型。 “number” 表示该变量是数值类型。 “string” 表示该变量是字符串类型。 “object” 表示该变量是对象类型或null类型注意null被认为是一个空对象。 “function” 表示该变量是函数类型。
67、请解释一下浏览器的同源策略。
答浏览器的同源策略指的是一个网页的脚本只能读取来自同一来源的数据。同源是指协议、域名和端口号都相同。如果两个页面的URL有任何一个部分不同就会被视为不同源。同源策略的目的是保护用户隐私和安全。
68、请解释一下JavaScript中的this关键字。
答在JavaScript中this关键字引用的是当前执行代码所处的上下文对象。具体来说this指向的是函数调用时的环境取决于函数的调用方式。如果在全局作用域中使用this则它会指向全局对象window如果在独立的函数中使用this则它会指向全局对象或undefined如果在对象的方法中使用this则它会指向该对象如果使用call、apply或bind方法可以手动设置this的值。
69、请解释一下JavaScript中的原型继承。
答JavaScript中的原型继承是通过原型链实现的一种继承机制。每个JavaScript对象都有一个指向另一个对象的内部链接这个链接就是该对象的原型。如果在对象上访问一个属性或方法时如果该对象本身不存在该属性或方法JavaScript会沿着原型链查找直到找到具有该属性或方法的对象为止。可以使用Object.create()方法来创建一个新对象并将其原型设为某个现有对象。
70、请解释一下CSS中的盒模型。
答CSS中的盒模型指的是元素在文档流中所占据的空间包括元素的内容区域、内边距、边框和外边距等。根据标准盒模型和IE盒模型的区别盒模型可以分为两种不同的模式即标准content-box和IEborder-box盒模型。标准盒模型计算元素尺寸时不包含内边距和边框的宽度而IE盒模型将内边距和边框的宽度纳入了元素的尺寸计算。
71、什么是小程序
答小程序是一种轻量级的应用程序可以在移动设备上直接使用无需下载安装。它结合了Web开发的灵活性和原生应用的性能提供了一种更好的用户体验。
72、小程序有哪些优势
答小程序具有以下优势
无需下载安装用户可以直接打开使用。跨平台支持可以在多个操作系统如iOS、Android上运行。快速加载提供更好的用户体验。提供丰富的API和能力如地理位置、文件操作、相机等。方便的分享和传播可以通过二维码等方式进行推广。
73、小程序和传统网页开发有何不同
答小程序和传统网页开发相比有几个区别
小程序基于特定的框架和开发规范如微信小程序基于WXML、WXSS和JavaScript进行开发。小程序拥有更多原生能力和API如访问硬件设备、发送消息等。小程序有独立的运行环境相比网页更加封闭对域名和资源的访问有一定限制。
74、请说明小程序的页面生命周期及其执行顺序。
答小程序的页面生命周期包括onLoad、onShow、onReady、onHide、onUnload等。执行顺序如下
onLoad页面加载时触发仅执行一次。onShow页面显示时触发每次打开页面都会执行。onReady页面初次渲染完成时触发仅执行一次。onHide页面隐藏时触发当用户切换到其他页面时执行。onUnload页面卸载时触发如返回上一页或小程序关闭。
75、如何实现小程序之间的通信
答小程序之间的通信可以通过以下方式实现
路由跳转使用navigator组件进行页面跳转并传递参数。Storage API使用Storage API在本地存储数据在不同页面之间进行传递。全局变量可以将数据保存在App全局对象中在不同页面之间共享数据。事件监听使用事件总线模式通过触发事件和监听事件来实现页面间的通信。
76、什么是uni-app
答uni-app是一个基于Vue.js的开发框架可以同时构建多个平台的应用程序包括iOS、Android、Web、微信小程序等。通过uni-app开发者可以使用一套代码编写跨平台的应用并具备原生应用的性能和体验。
77、uni-app有哪些主要特点
答uni-app具有以下主要特点 一套代码多端运行开发者只需编写一次代码即可同时发布到多个平台。 原生级性能通过底层的渲染优化和性能调优uni-app实现了原生应用的性能表现。 开发门槛低基于Vue.js进行开发对于熟悉Vue.js的开发者来说上手较快。 生态丰富uni-app拥有庞大的插件市场和活跃的社区支持提供了丰富的插件和组件库。 跨平台支持可以使用同一份代码开发多个平台的应用提高开发效率。 组件化开发采用Vue.js的组件化开发思想可以快速构建复杂的UI界面。 内置原生能力具备访问设备功能、调用原生API等能力。 良好的性能表现通过编译生成原生代码提升应用的运行效率。 大量插件和扩展拥有丰富的插件和扩展库方便开发各种功能和特效。
78、uni-app的工作原理是什么
答uni-app的工作原理可以简述为使用前端技术栈Vue.js Webpack开发页面通过编译器将代码转换为各个平台所需的代码再通过各个平台的运行时环境将代码渲染为可执行的应用。
79、如何在uni-app中进行页面跳转
答在uni-app中可以使用navigateTo、redirectTo、switchTab等API进行页面跳转。同时也可以通过标签结合Vue Router实现页面间的导航。
80、uni-app支持哪些第三方插件和组件库
答uni-app支持大量的第三方插件和组件库包括但不限于以下
vant-weapp基于微信小程序封装的轻量级组件库。uView UI一款基于uni-app的高质量UI框架。uni-uiuni-app官方推荐的组件库提供了丰富的UI组件。Lodash一个JavaScript实用工具库提供了许多常用的函数和方法。axios一个基于Promise的HTTP客户端用于发送异步请求。
81、uni-app的页面生命周期有哪些
答uni-app的页面生命周期包括onLoad、onShow、onReady、onHide、onUnload等。与小程序的页面生命周期类似。
82、如何进行uni-app之间的页面通信
答uni-app之间的页面通信可以采用以下方式
路由跳转使用uni-app提供的路由API进行页面跳转并可传递参数。Vuex状态管理通过Vuex实现不同页面之间的数据共享和通信。Event Bus事件总线创建一个全局的事件总线对象不同页面通过发布-订阅模式进行通信。Storage API使用uni-app提供的Storage API在本地存储数据在不同页面之间进行传递。
83、uni-app如何实现原生功能调用
答uni-app通过封装了一套API来实现原生功能的调用。可以使用uni-app提供的API如拍照、获取地理位置、调用相应等设备能力。也可以使用插件或原生扩展来实现对原生功能的调用。
84、VueX 使用教程 安装 Vuex
需要使用 npm 安装 Vuex 在终端输入如下命令
npm install vuex --save创建一个 store
在项目中引入 Vuex 后在 src 目录下新建一个 vuex 文件夹然后在其中创建一个 index.js 文件代码如下
// 引入 Vue 和 Vuex
import Vue from vue
import Vuex from vuexVue.use(Vuex)// 创建仓库
const store new Vuex.Store({state: {count: 0},mutations: {increment(state) {state.count}},actions: {incrementAsync({ commit }) {setTimeout(() {commit(increment)}, 1000)}},getters: {getCount: state {return state.count}}
})// 导出 store
export default store在这段代码中我们创建了一个名为 store 的 Vuex 实例并编写了 state、mutations、actions、getters 四种选项来管理应用的状态。
state存储应用的数据状态。mutations类似于事件更改 state 中的值是 Vuex 唯一允许修改 state 的方法。actions用于处理异步操作提交 mutation通常在 actions 中进行异步请求获取数据等操作再调用 mutations 中的方法更新 state。actions 可以包含任意异步操作但不能直接操作 state必须通过 mutations 等同步操作来修改状态。getters用于获取数据在组件内部使用 computed 对象计算属性时可以通过 getters 获取 store 中的计算结果。 在 main.js 中引入 store
在 src 目录下找到 main.js 文件将以下代码添加到文件开头
import Vue from vue
import App from ./App.vue
import store from ./vuexnew Vue({el: #app,store,components: { App },template: App/
})这段代码将 store 引入了全局中并在 Vue 实例中注册该 store这样就使整个应用程序都可以访问到 store 中的状态和其他特性。 在组件中使用 Vuex
在组件中使用 Vuex 需要做如下两步操作
引入 mapState、mapMutations、mapActions、mapGetters 等方法将 state、mutations、actions、getters 映射到当前组件中
下面是一个基本的组件示例演示如何在组件中使用 state 和 mutations。
templatediv classcounterh1{{ count }}/h1button clickincrementadd/button/div
/templatescript
import { mapState, mapMutations } from vuexexport default {name: Counter,computed: {...mapState([count])},methods: {...mapMutations([increment])}
}
/script在模板中我们使用 {{ count }} 显示 state 中存储的数据使用 clickincrement 触发 mutations 中的 increment 方法来更新状态。在 scripts 中我们使用 mapState 将 count 映射到 computed 中使用 mapMutations 将 increment 映射到 methods 中。 在组件中使用 actions 和 getters
在组件中使用 actions 和 getters 的方法与上述使用 state 和 mutations 的方法类似在此不再赘述。以下是示例代码
script
import { mapActions, mapGetters } from vuexexport default {name: Counter,computed: {...mapGetters([getCount])},methods: {...mapActions([incrementAsync])}
}
/script在模板中我们使用 {{ getCount }} 显示 getters 中计算的数据使用 clickincrementAsync 触发 actions 中的 incrementAsync 方法来更新状态。在 scripts 中我们使用 mapGetters 将 getCount 映射到 computed 中使用 mapActions 将 incrementAsync 映射到 methods 中。
总结
Vuex 是一个用于 Vue.js 应用程序的状态管理库它允许我们集中式地处理应用程序的所有状态。通过创建和修改 store 实例中的 state、mutations、actions、getters 等选项我们可以方便地管理应用程序的状态并在组件中轻松使用这些状态。
真实实例
1、vue的双向绑定原理是什么里面的关键点在哪里
Vue的双向绑定原理是通过数据劫持和发布-订阅模式实现的。关键点在于通过Object.defineProperty()方法来劫持对象的属性访问当数据发生变化时触发相应的监听器并更新视图。
2、实现水平垂直居中的方式
实现水平垂直居中的方式有多种。其中一种常用的方式是使用Flex布局设置容器为display: flex;justify-content: center;align-items: center;。
3、常用伪元素有哪一些
常用的伪元素有::before和::after它们在元素的内容前后添加虚拟元素可以通过CSS样式进行修饰。
4、移动端如何适配不同屏幕尺寸
移动端适配可以采用响应式布局或者使用媒体查询等方式进行适配。常见的做法是使用百分比、rem单位或者CSS框架如Bootstrap来实现灵活布局和缩放效果。
5、本地存储有哪一些他们三者有什么区别
常见的本地存储方式有Cookie、LocalStorage和SessionStorage。它们的区别在于作用域、存储容量和数据有效期等方面。Cookie存储在客户端浏览器中有大小限制且过期时间可以设置LocalStorage和SessionStorage则是HTML5提供的Web Storage API存储在浏览器中LocalStorage没有过期时间限制且容量较大而SessionStorage在会话结束后自动清除。
6、JS的数据类型如何判断js的数据类型
JS的数据类型包括基本数据类型Number、String、Boolean、Null、Undefined、Symbol和引用数据类型Object。可以使用typeof操作符判断基本数据类型使用instanceof操作符或Object.prototype.toString.call()方法判断引用数据类型。
7、说一下ES6的新特性有哪些
ES6的新特性包括箭头函数、解构赋值、模板字符串、let和const关键字、默认参数、扩展运算符、Promise、类与继承、模块化等。
es7 新特性
1.新增了includes的数组查找方法
使用场景确认数组中有我们需要查找的内容在ES7之前我们接触到的数组方法可以通过filter或者indexOf如果使用filter 可以直接筛选之后输出如果是indexOf则根据返回值来判断是否在当前数组中。如果返回值 0 则证明是有值的如果是负值 则没有包含需要查询的数值。如果查询的数值没有返回undefined但是indexOf的缺陷也很明显没错不能检测NaN那么ES7横空出世的includes正好可以解决这个问题相对于indexOf返回的数字includes返回的是布尔值2.取幂运算符 **
当我们需要将一个数进行乘方时通常使用Math库中的pow()函数。但是ES7中提供了新的幂运算符**使得我们可以更方便地进行乘方运算。语法格式为base ** exponent其中base表示底数exponent表示指数。例如2的3次方可以表示为2 ** 3结果为8。需要注意的是幂运算符的优先级高于加减乘除运算符因此需要使用括号来控制运算顺序。例如(2 3) ** 2的结果为25而2 3 ** 2的结果为17。
8、let、const、var三者有什么区别
三者都用于声明变量但区别在于作用域和可修改性。var声明的变量具有函数作用域而let和const声明的变量具有块级作用域。另外let和var声明的变量可以被修改而const声明的变量是常量不可被修改。
9、数组去重有哪些办法
除了ES6新增的构造函数Set具有天然去重功能和数组迭代外常见的数组去重方法还包括使用indexOf()方法、利用对象属性唯一性等方式进行去重。
10、说一下深拷贝和浅拷贝如何自己实现一个深拷贝
深拷贝是指在内存中完全复制一个对象包括其所有的属性和嵌套对象而浅拷贝只复制对象的引用。实现深拷贝可以通过递归遍历对象的属性并创建新的对象来实现。
11、Vue的生命周期有哪一些说一下它们每个阶段做什么操作
Vue的生命周期包括beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy和destroyed。在beforeCreate阶段Vue实例被初始化数据观测和事件配置之前在created阶段Vue实例完成数据观测、属性和方法的运算在beforeMount阶段模板编译成虚拟DOM之前在mounted阶段虚拟DOM挂载到实际DOM后在beforeUpdate阶段数据更新之前在updated阶段数据更新完成在beforeDestroy阶段开始销毁Vue实例之前在destroyed阶段Vue实例销毁完成。
12、组件通讯方式有哪一些
组件通讯方式包括父子组件通讯props和$emit、兄弟组件通讯通过共同的父组件传递数据或使用事件总线和跨层级通讯使用Vuex或provide/inject。
13、Vuex有几个属性及作用
Vuex有state、getters、mutations、actions和modules等属性。state是存储共享状态的地方getters用于获取state中的数据mutations用于修改state的数据同步操作actions用于处理异步操作或提交mutations而modules可以将store分割成模块化的部分。
14、Vue的监听属性和计算属性有什么区别
Vue的监听属性通过watch选项来实现可监听数据的变化并执行相应的回调函数而计算属性通过computed选项来定义它根据依赖的数据自动计算得出结果计算属性具有缓存机制在依赖数据未改变时直接返回缓存结果。
15、说一下防抖和节流。怎么实现
防抖和节流是解决频繁触发事件时的性能优化手段。防抖是在一定时间内只执行最后一次操作常用的实现方式是使用setTimeout延迟执行节流是在一定时间间隔内只执行一次操作常用的实现方式是使用定时器控制执行频率。
16、Vue的导航守卫有哪一些
Vue的导航守卫包括以下几种 beforeEach在路由切换开始之前调用可以用来进行全局的前置守卫验证。 beforeResolve在导航被确认之前调用可以用来进行延迟加载的路由组件的初始化。 afterEach在每次路由切换完成之后调用可以用来进行全局的后置处理操作。 路由独享的守卫 beforeEnter在单个路由配置中定义只对该路由有效。 组件内的守卫 beforeRouteEnter在进入路由对应的组件前调用可以访问不到组件实例需要使用 next 方法来传递组件实例给回调函数。 beforeRouteUpdate在路由参数发生变化时调用例如从 /user/1 导航到 /user/2。 beforeRouteLeave在离开当前路由时调用可以用来确认是否离开或取消导航。
这些导航守卫可以让你控制路由的跳转行为在路由切换前进行验证、权限控制或其他必要的操作。
分模块面试题
一、HTML 篇
1. 简述一下你对 HTML 语义化的理解
用正确的标签做正确的事情。
html 语义化让页面的内容结构化结构更清晰便于对浏览器、搜索引擎解析即使在没有样式 CSS 情况下也以一种文档格式显示并且是容易阅读的;
搜索引擎的爬虫也依赖于 HTML 标记来确定上下文和各个关键字的权重利于 SEO;
使阅读源代码的人对网站更容易将网站分块便于阅读维护理解。
2. 标签上 title 与 alt 属性的区别是什么
alt 是给搜索引擎识别在图像无法显示时的替代文本
title 是关于元素的注释信息主要是给用户解读。
当鼠标放到文字或是图片上时有 title 文字显示。因为 IE 不标准在 IE 浏览器中 alt 起到了 title 的作用变成文字提示。
在定义 img 对象时将 alt 和 title 属性写全可以保证在各种浏览器中都能正常使用。
3. iframe的优缺点
优点
页面和程序分离,几乎不会受到外界任何js或者css的影响, 便于使用可以通过iframe嵌套通用的页面, 提高代码的重用率, 比如页面的头部样式和底部版权信息重新加载页面时, 不需要重载iframe框架页的内容, 增加页面重载速度.iframe可以解决第三方内容加载缓慢的问题.
解决加载缓慢的第三方内容如图标和广告等的加载问题
Security sandbox
并行加载脚本
缺点
会产生很多页面不容易管理iframe框架的内容无法被搜索引擎捕获, 所以iframe不适用于首页iframe兼容性较差iframe有一定的安全风险iframe会阻塞主页面的Onload事件
6、即时内容为空加载也需要时间
没有语意
4. href 与 src href (Hypertext Reference)指定网络资源的位置从而在当前元素或者当前文档和由当前属性定义的需要的锚点或资源之间定义一个链接或者关系。目的不是为了引用资源而是为了建立联系让当前标签能够链接到目标地址。 src source缩写指向外部资源的位置指向的内容将会应用到文档中当前标签所在位置。 href与src的区别 请求资源类型不同href 指向网络资源所在位置建立和当前元素锚点或当前文档链接之间的联系。在请求 src 资源时会将其指向的资源下载并应用到文档中比如 JavaScript 脚本img 图片 作用结果不同href 用于在当前文档和引用资源之间确立联系src 用于替换当前内容 浏览器解析方式不同当浏览器解析到src 会暂停其他资源的下载和处理直到将该资源加载、编译、执行完毕图片和框架等也如此类似于将所指向资源应用到当前内容。这也是为什么建议把 js 脚本放在底部而不是头部的原因。
二、CSS 篇
1、介绍一下 CSS 的盒子模型
有两种 IE 盒子模型、W3C 盒子模型
盒模型 内容(content)、填充(padding)、边界(margin)、 边框(border)
区 别 IE 的 content 部分把 border 和 padding 计算了进去;
2. css 选择器优先级
!important 行内样式比重1000 ID 选择器比重100 类选择器比重10 标签比重1 通配符 继承 浏览器默认属性
3、垂直居中几种方式 单行文本: line-height height 图片: vertical-align: middle; absolute 定位: top: 50%;left: 50%;transform: translate(-50%, -50%); flex: display:flex;margin:auto
4、简明说一下 CSS link 与 import 的区别和用法 link 是 XHTML 标签除了加载CSS外还可以定义 RSS 等其他事务import 属于 CSS 范畴只能加载 CSS。 link 引用 CSS 时在页面载入时同时加载import 需要页面网页完全载入以后加载。 link 是 XHTML 标签无兼容问题import 是在 CSS2.1 提出的低版本的浏览器不支持。 link 支持使用 Javascript 控制 DOM 去改变样式而import不支持。
5、rgba和opacity的透明效果有什么不同
opacity 会继承父元素的 opacity 属性而 RGBA 设置的元素的后代元素不会继承不透明属性。
6、display:none和visibility:hidden的区别 display:none 隐藏对应的元素在文档布局中不再给它分配空间它各边的元素会合拢就当他从来不存在。 visibility:hidden 隐藏对应的元素但是在文档布局中仍保留原来的空间。 display:none 会导致页面回流和重绘visibility:hidden 只会导致重绘
7、position的值 relative和absolute分别是相对于谁进行定位的 relative:相对定位相对于自己本身在正常文档流中的位置进行定位。 absolute:生成绝对定位相对于最近一级定位不为static的父元素进行定位。 fixed: 老版本IE不支持生成绝对定位相对于浏览器窗口或者frame进行定位。 static:默认值没有定位元素出现在正常的文档流中。 sticky:生成粘性定位的元素容器的位置根据正常文档流计算得出。
8、画一条0.5px的直线
考查的是css3的transform
height: 1px;transform: scale(0.5);9、calc, support, media各自的含义及用法
1、support 主要是用于检测浏览器是否支持CSS的某个属性其实就是条件判断如果支持某个属性你可以写一套样式如果不支持某个属性你也可以提供另外一套样式作为替补。
2、calc() 函数用于动态计算长度值。 calc()函数支持 “”, “-”, “*”, “/” 运算
3、media 查询你可以针对不同的媒体类型定义不同的样式。
10、1rem、1em、1vh、1px各自代表的含义
1、rem
rem是全部的长度都相对于根元素元素。通常做法是给html元素设置一个字体大小然后其他元素的长度单位就为rem。
2、em
子元素字体大小的em是相对于父元素字体大小
元素的width/height/padding/margin用em的话是相对于该元素的font-size
3、vw/vh
全称是 Viewport Width 和 Viewport Height视窗的宽度和高度相当于 屏幕宽度和高度的 1%不过处理宽度的时候%单位更合适处理高度的 话 vh 单位更好。
4、px
px像素Pixel。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。
一般电脑的分辨率有{19201024}等不同的分辨率
19201024 前者是屏幕宽度总共有1920个像素,后者则是高度为1024个像素
11、画一个三角形
这属于简单的css考查平时在用组件库的同时也别忘了原生的css
div classa/div.a {width: 0;height: 0;border-width: 100px;border-style: solid;border-color: transparent #0099CC transparent transparent;transform: rotate(90deg); /*顺时针旋转90°*/}三、HTML / CSS 混合篇
1、HTML5、CSS3 里面都新增了那些新特性
HTML5 新的语义标签 article 独立的内容。 aside 侧边栏。 header 头部。 nav 导航。 section 文档中的节。 footer 页脚。 画布(Canvas) API 地理(Geolocation) API 本地离线存储 localStorage 长期存储数据浏览器关闭后数据不丢失 sessionStorage 的数据在浏览器关闭后自动删除 新的技术webworker, websocket, Geolocation 拖拽释放(Drag and drop) API 音频、视频API(audio,video) 表单控件calendar、date、time、email、url、searc
CSS3 2d3d变换 Transition, animation 媒体查询 新的单位rem, vwvh 等 圆角border-radius阴影box-shadow对文字加特效text-shadow线性渐变gradient旋转transformtransform:rotate(9deg) scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg);//旋转,缩放,定位,倾斜 Rgba
2、BFC 是什么
BFC 即 Block Formatting Contexts (块级格式化上下文)它属于普通流即元素按照其在 HTML 中的先后位置至上而下布局在这个过程中行内元素水平排列直到当行被占满然后换行块级元素则会被渲染为完整的一个新行除非另外指定否则所有元素默认都是普通流定位也可以说普通流中元素的位置由该元素在 HTML 文档中的位置决定。
可以把 BFC 理解为一个封闭的大箱子箱子内部的元素无论如何翻江倒海都不会影响到外部。
只要元素满足下面任一条件即可触发 BFC 特性 body 根元素 浮动元素float 除 none 以外的值 绝对定位元素position (absolute、fixed) display 为 inline-block、table-cells、flex overflow 除了 visible 以外的值 (hidden、auto、scroll)
3、常见兼容性问题
浏览器默认的margin和padding不同。解决方案是加一个全局的*{margin:0;padding:0;}来统一。
Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示,
可通过加入 CSS 属性 -webkit-text-size-adjust: none; 解决.
四、JS 篇
1、JS 数据类型 ?
数据类型主要包括两部分
基本数据类型 Undefined、Null、Boolean、Number 和 String
引用数据类型 Object (包括 Object 、Array 、Function)
ECMAScript 2015 新增:Symbol(创建后独一无二且不可变的数据类型 )
2、判断一个值是什么类型有哪些方法
typeof 运算符
instanceof 运算符
Object.prototype.toString 方法
3、null 和 undefined 的区别
null 表示一个对象被定义了值为“空值”
undefined 表示不存在这个值。
1变量被声明了但没有赋值时就等于undefined。 2) 调用函数时应该提供的参数没有提供该参数等于undefined。 3对象没有赋值的属性该属性的值为undefined。 4函数没有返回值时默认返回undefined。
4、怎么判断一个变量arr的话是否为数组此题用 typeof 不行
arr instanceof Arrayarr.constructor ArrayObject.prototype.toString.call(arr) ‘[Object Array]’
5、“ ”、“ ”的区别
当且仅当两个运算数相等时它返回 true即不检查数据类型
只有在无需类型转换运算数就相等的情况下才返回 true需要检查数据类型
6、 “eval是做什么的
它的功能是把对应的字符串解析成 JS 代码并运行
应该避免使用 eval不安全非常耗性能2次一次解析成 js 语句一次执行。
7、箭头函数有哪些特点
不需要function关键字来创建函数
省略return关键字
改变this指向
8、var、let、const 区别
var 存在变量提升。
let 只能在块级作用域内访问。
const 用来定义常量必须初始化不能修改对象特殊
9、new操作符具体干了什么呢
1、创建一个空对象并且 this 变量引用该对象同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、新创建的对象由 this 所引用并且最后隐式的返回 this 。
10、JSON 的了解
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。
它是基于JavaScript的一个子集。数据格式简单, 易于读写, 占用带宽小
{‘age’:‘12’, ‘name’:‘back’}
11、document.write 和 innerHTML 的区别
document.write 只能重绘整个页面
innerHTML 可以重绘页面的一部分
12、ajax过程
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象.
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
(3)设置响应HTTP请求状态变化的函数.
(4)发送HTTP请求.
(5)获取异步调用返回的数据.
(6)使用JavaScript和DOM实现局部刷新.
13、请解释一下 JavaScript 的同源策略
概念:同源策略是客户端脚本尤其是Netscape Navigator2.0其目的是防止某个文档或脚本从多个不同源装载。
这里的同源策略指的是协议域名端口相同同源策略是一种安全协议。
指一段脚本只能读取来自同一来源的窗口和文档的属性。
14、介绍一下闭包和闭包常用场景
闭包是指有权访问另一个函数作用域中的变量的函数创建闭包常见方式就是在一个函数的内部创建另一个函数
使用闭包主要为了设计私有的方法和变量闭包的优点是可以避免变量的污染缺点是闭包会常驻内存会增大内存使用量使用不当很容易造成内存泄露。在js中函数即闭包只有函数才会产生作用域的概念。
闭包有三个特性
函数嵌套函数
函数内部可以引用外部的参数和变量
参数和变量不会被垃圾回收机制回收
应用场景设置私有变量的方法
不适用场景返回闭包的函数是个非常大的函数
闭包的缺点就是常驻内存会增大内存使用量使用不当会造成内存泄漏
15、javascript的内存(垃圾)回收机制
垃圾回收器会每隔一段时间找出那些不再使用的内存然后为其释放内存
一般使用标记清除方法(mark and sweep), 当变量进入环境标记为进入环境离开环境标记为离开环境
垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记然后去掉环境中的变量以及被环境中变量所引用的变量闭包在这些完成之后仍存在标记的就是要删除的变量了
还有引用计数方法(reference counting), 在低版本IE中经常会出现内存泄露很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数当声明了一个 变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1如果该变量的值变成了另外一个则这个值得引用次数减1当这个值的引用次数变为0的时 候说明没有变量在使用这个值没法被访问了因此可以将其占用的空间回收这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。
在IE中虽然JavaScript对象通过标记清除的方式进行垃圾回收但BOM与DOM对象却是通过引用计数回收垃圾的 也就是说只要涉及BOM及DOM就会出现循环引用问题。
16、JavaScript原型原型链 ? 有什么特点
1、任何对象都有 proto 隐式原型, 等于 构造函数 的 prototype
const obj {}
obj.proto Object.prototype // true
2、任何函数都有 prototype 显示原型 等于 原型对象(就是一个普通对象包含公共属性)
(通过Function.prototype.bind方法构造出来的函数是个例外它没有prototype属性)
function Person () {}Person.prototype 原型对象Person.prototype.constructor Person // trueconst person1 new Personperson1.__proto__ Person.prototype // trueperson1.constructor Person // true3、对象还具有 constructor 属性指向构造函数Person.prototype.constructor Person
4、原型链是依赖于__proto__, 查找一个属性会沿着 proto 原型链向上查找直到找到为止。
5、特殊
// 原型链最终点是 null
Object.prototype.proto null // true
obj.proto.proto null // true
每个对象都会在其内部初始化一个属性就是prototype(原型)当我们访问一个对象的属性时
如果这个对象内部不存在这个属性那么他就会去prototype里找这个属性这个prototype又会有自己的prototype
于是就这样一直找下去也就是我们平时所说的原型链的概念。
关系instance.constructor.prototype instance.proto
特点
JavaScript对象是通过引用来传递的我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时与之相关的对象也会继承这一改变。
17、用js递归的方式写1到100求和
function add(num1, num2) {const num num1 num2;if(num2 100) {return num;} else {return add(num, num2 1)}}var sum add(1, 2); 18、事件队列宏任务微任务
可以分为微任务micro task队列和宏任务macro task队列。
微任务一般比宏任务先执行并且微任务队列只有一个宏任务队列可能有多个。另外我们常见的点击和键盘等事件也属于宏任务。
下面我们看一下常见宏任务和常见微任务。
常见宏任务
setTimeout()setInterval()setImmediate()常见微任务
promise.then()、promise.catch()new MutaionObserver()process.nextTick()微任务和宏任务的本质区别。
宏任务特征有明确的异步任务需要执行和回调需要其他异步线程支持。
微任务特征没有明确的异步任务需要执行只有回调不需要其他异步线程支持。
setTimeout(function () {console.log(1);}, 0);async function async1() {console.log(2);const data await async2();console.log(3);return data;}async function async2() {return new Promise((resolve) {console.log(4);resolve(async2的结果);}).then((data) {console.log(5);return data;});
}async1().then((data) {console.log(6);console.log(data);});new Promise(function (resolve) {console.log(7);resolve()}).then(function () {console.log(8);});// 2 4 7 5 8 3 6 async2的结果 119、async/await
async 是一个通过异步执行并隐式返回 Promise 作为结果的函数。是Generator函数的语法糖并对Generator函数进行了改进。
改进 *内置执行器无需手动执行 next() 方法。 *更好的语义 *更广的适用性co模块约定yield命令后面只能是 Thunk 函数或 Promise 对象而async函数的await命令后面可以是 Promise 对象和原始类型的值数值、字符串和布尔值但这时会自动转成立即 resolved 的 Promise 对象。 *返回值是 Promise比 Generator 函数返回的 Iterator 对象方便可以直接使用 then() 方法进行调用。 *async 隐式返回 Promise 作为结果的函数那么可以简单理解为await后面的函数执行完毕时await会产生一个微任务(Promise.then是微任务)。
20、JavaScript 是单线程的浏览器是多进程的 *每打开一个新网页就会创建一个渲染进程 *渲染进程是多线程的 *负责页面渲染的 GUI 渲染线程 *负责JavaScript的执行的 JavaScript 引擎线程 *负责浏览器事件循环的事件触发线程注意这不归 JavaScript 引擎线程管 *负责定时器的定时触发器线程setTimeout 中低于 4ms 的时间间隔算为4ms *负责XMLHttpRequest的异步 http 请求线程 *GUI 渲染线程与 JavaScript 引擎线程是互斥的 *单线程JavaScript是因为避免 DOM 渲染的冲突web worker 支持多线程但是 web worker 不能访问 window 对象document 对象等。
五、Vue 篇
1、谈谈你对MVVM开发模式的理解 *MVVM分为Model、View、ViewModel三者。 *Model 代表数据模型数据和业务逻辑都在Model层中定义 *View 代表UI视图负责数据的展示 *ViewModel 负责监听 Model 中数据的改变并且控制视图的更新处理用户交互操作 *Model 和 View 并无直接关联而是通过 ViewModel 来进行联系的Model 和 ViewModel 之间有着双向数据绑定的联系。因此当 Model 中的数据改变时会触发 View 层的刷新View 中由于用户交互操作而改变的数据也会在 Model 中同步。
这种模式实现了 Model 和 View 的数据自动同步因此开发者只需要专注对数据的维护操作即可而不需要自己操作 dom。
2、v-if 和 v-show 有什么区别
v-if 是真正的条件渲染会控制这个 DOM 节点的存在与否。因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建也是惰性的如果在初始渲染时条件为假则什么也不做——直到条件第一次变为真时才会开始渲染条件块。
v-show 就简单得多——不管初始条件是什么元素总是会被渲染并且只是简单地基于 CSS 的 “display” 属性进行切换。
当我们需要经常切换某个元素的显示/隐藏时使用v-show会更加节省性能上的开销当只需要一次显示或隐藏时使用v-if更加合理。
3、说说你对 SPA 单页面的理解它的优缺点分别是什么
SPA single-page application 仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页 面加载完成SPA 不会因为用户的操作而进行页面的重新加载或跳转取而代之的是利用路由机制实现 HTML 内容的变换UI 与用户的交互避免页面的重新加载。
优点
用户体验好、快内容的改变不需要重新加载整个页面避免了不必要的跳转和重复渲染
基于上面一点SPA 相对对服务器压力小
前后端职责分离架构清晰前端进行交互逻辑后端负责数据处理
缺点
初次加载耗时多为实现单页 Web 应用功能及显示效果需要在加载页面的时候将 JavaScript、CSS 统一 加载部分页面按需加载
前进后退路由管理由于单页应用在一个页面中显示所有的内容所以不能使用浏览器的前进后退功能所 有的页面切换需要自己建立堆栈管理
SEO 难度较大由于所有的内容都在一个页面中动态替换显示所以在 SEO 上其有着天然的弱势。
4、你使用过 Vuex 吗
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store仓库。“store” 基本上就是一个容器它包含着你的应用中大部分的状态 ( state )。 1Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候若 store 中的状态发生变化那么相应的组件也会相应地得到高效更新。 2改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
每一个 Vuex 应用的核心就是 store仓库它包含着你的应用中大部分的状态 (state)。 状态管理有5个核心state、getter、mutation、action、module。
State 基本数据定义了应用状态的数据结构可以在这里设置默认的初始状态。 单一状态树定义应用状态的默认初始值页面显示所需的数据从该对象中进行读取。 2、Vuex 使用单一状态树用一个对象就包含了全部的应用层级状态。它便作为一个“唯一数据源”而存在。这也意味着每个应用将仅仅包含一个 store 实例。 单一状态树让我们能够直接地定位任一特定的状态片段在调试的过程中也能轻易地取得整个当前应用状态的快照。 不可直接对 state 进行更改需要通过 Mutation 方法来更改。 由于 Vuex 的状态存储是响应式的从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态
Getter 从基本数据派生的数据允许组件从 Store 中获取数据mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。
1、可以认为是 store 的计算属性对 state 的加工是派生出来的数据。 2、就像 computed 计算属性一样getter 返回的值会根据它的依赖被缓存起来且只有当它的依 赖值发生改变才会被重新计算。 3、可以在多组件中共享 getter 函数这样做还可以提高运行效率。 4、在 store 上注册 gettergetter 方法接受以下参数 state, 如果在模块中定义则为模块的局部状态 5、getters, 等同于 store.getters
Mutation 是唯一更改 store 中状态的方法且必须是同步函数。
1、Vuex中store数据改变的唯一方法就是mutation
2、通俗的理解mutations 里面装着改变数据的方法集合处理数据逻辑的方法全部放在 mutations 里使数据和视图分离。
Action 像一个装饰器包裹mutations使之可以异步。用于提交 mutation而不是直接变更状态可以包含任意异步操作。
Action action 类似于 mutation 不同在于
1、action 提交的是 mutation通过 mutation 来改变 state 而不是直接变更状态。
2、action 可以包含任意异步操作。
Module 模块化Vuex允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
1、由于使用单一状态树应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时store 对象就有可能变得相当臃肿。
2、为了解决以上问题Vuex 允许我们将 store 分割成模块module。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
总结
四大核心属性以及一个子模块管理属性state存放数据Mutations同步修改state里面的数据actions异步修改数据但需要调用Mutations里面的方法修改getters数据过滤器对数据进行整理还有一个modules当数据过多和复杂时将数据用模块化分开在住文件中用modules引用
5、Class 与 Style 如何动态绑定
Class 可以通过对象语法和数组语法进行动态绑定
对象语法
div v-bind:class{ active: isActive, text-danger: hasError }/divdata: {isActive: true,hasError: false}数组语法
div v-bind:class[isActive ? activeClass : , errorClass]/divdata: {activeClass: active,errorClass: text-danger}Style 也可以通过对象语法和数组语法进行动态绑定
对象语法
div v-bind:style{ color: activeColor, fontSize: fontSize px }/divdata: {activeColor: red,fontSize: 30}数组语法
div v-bind:style[styleColor, styleSize]/divdata: {styleColor: {color: red},styleSize:{fontSize:23px}
}6、怎样理解 Vue 的单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定父级 prop 的更新会向下流动到子组件中但是反过来则不行。
这样会防止从子组件意外改变父级组件的状态从而导致你的应用的数据流向难以理解。
额外的每次父级组件发生更新时子组件中所有的 prop 都将会刷新为最新的值。
这意味着你不应该在一个子组件内部改变 prop。如果你这样做了Vue 会在浏览器的控制台中发出警告。
子组件想修改时只能通过 $emit 派发一个自定义事件父组件接收到后由父组件修改。
7、computed 和 watch 的区别和运用的场景
computed是计算属性依赖其它属性值并且 computed 的值有缓存只有它依赖的属性值发生改变下一次获取 computed 的值时才会重新计算 computed 的值watch 更多的是「观察」的作用类似于某些数据的监听回调 每当监听的数据变化时都会执行回调进行后续操作
运用场景
当我们需要进行数值计算并且依赖于其它数据时应该使用 computed因为可以利用 computed 的缓存特性避免每次获取值时都要重新计算
当我们需要在数据变化时执行异步或开销较大的操作时应该使用 watch使用 watch 选项允许我们执行异步操作 ( 访问一个 API )限制我们执行该操作的频率并在我们得到最终结果前设置中间状态。这些都是计算属性无法做到的。
8、直接给一个数组项赋值Vue 能检测到变化吗
由于 JavaScript 的限制Vue 不能检测到以下数组的变动 *当你利用索引直接设置一个数组项时例如vm.items[indexOfItem] newValue *当你修改数组的长度时例如vm.items.length newLength
为了解决第一个问题Vue 提供了以下操作方法
// Vue.setVue.set(vm.items, indexOfItem, newValue)// vm.$setVue.set的一个别名vm.$set(vm.items, indexOfItem, newValue)// Array.prototype.splicevm.items.splice(indexOfItem, 1, newValue)为了解决第二个问题Vue 提供了以下操作方法// Array.prototype.splicevm.items.splice(newLength)9、谈谈你对 Vue 生命周期的理解
生命周期是什么
Vue 实例有一个完整的生命周期也就是从开始创建、初始化数据、编译模版、挂载 Dom - 渲染、更新 - 渲染、卸载等一系列过程我们称这是 Vue 的生命周期。
各个生命周期的作用
生命周期 描述 beforeCreate 组件实例被创建之初组件的属性生效之前 created 组件实例已经完全创建属性也绑定但真实 dom 还没有生成$el 还不可用 beforeMount 在挂载开始之前被调用相关的 render 函数首次被调用 mounted el 被新创建的 vm.$el 替换并挂载到实例上去之后调用该钩子 beforeUpdate 组件数据更新之前调用发生在虚拟 DOM 打补丁之前 updated 组件数据更新之后 activited keep-alive 专属组件被激活时调用 deadctivated keep-alive 专属组件被销毁时调用 beforeDestory 组件销毁前调用 destoryed 组件销毁后调用
10、Vue 的父组件和子组件生命周期钩子函数执行顺序
Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分
加载渲染过程 :
父 beforeCreate - 父 created - 父 beforeMount - 子 beforeCreate - 子 created - 子 beforeMount - 子 mounted - 父 mounted
子组件更新过程 :
父 beforeUpdate - 子 beforeUpdate - 子 updated - 父 updated
父组件更新过程 :
父 beforeUpdate - 父 updated
销毁过程 :
父 beforeDestroy - 子 beforeDestroy - 子 destroyed - 父 destroyed
11、父组件可以监听到子组件的生命周期吗
比如有父组件 Parent 和子组件 Child如果父组件监听到子组件挂载 mounted 就做一些逻辑处理可以通过以下写法实现
// Parent.vueChild mounteddoSomething/// Child.vuemounted() {this.$emit(mounted);}以上需要手动通过 $emit 触发父组件的事件更简单的方式可以在父组件引用子组件时通过 hook 来监听即可如下所示
// Parent.vueChild hook:mounteddoSomething /ChilddoSomething() {console.log(父组件监听到 mounted 钩子函数 ...);},// Child.vuemounted(){console.log(子组件触发 mounted 钩子函数 ...);}, / 以上输出顺序为// 子组件触发 mounted 钩子函数 ...// 父组件监听到 mounted 钩子函数 ...当然 hook 方法不仅仅是可以监听 mounted其它的生命周期事件例如createdupdated 等都可以监听。
12、谈谈你对 keep-alive 的了解
keep-alive 是 Vue 内置的一个组件可以使被包含的组件保留状态避免重新渲染 其有以下特性
*一般结合路由和动态组件一起使用用于缓存组件*提供 include 和 exclude 属性两者都支持字符串或正则表达式 include 表示只有名称匹配的组件会被缓存exclude 表示任何名称匹配的组件都不会被缓存 其中 exclude 的优先级比 include 高*对应两个钩子函数 activated 和 deactivated 当组件被激活时触发钩子函数 activated当组件被移除时触发钩子函数 deactivated。
13、组件中 data 为什么是一个函数
为什么组件中的 data 必须是一个函数然后 return 一个对象而 new Vue 实例里data 可以直接是一个对象
因为组件是用来复用的且 JS 里对象是引用关系如果组件中 data 是一个对象那么这样作用域没有隔离子组件中的 data 属性值会相互影响
如果组件中 data 选项是一个函数那么每个实例可以维护一份被返回对象的独立的拷贝组件实例之间的 data 属性值不会互相影响而 new Vue 的实例是不会被复用的因此不存在引用对象的问题。
14、v-model 的原理
我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定我们知道 v-model 本质上不过是语法糖v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件
1、text 和 textarea 元素使用 value 属性和 input 事件
2、checkbox 和 radio 使用 checked 属性和 change 事件
3、select 字段将 value 作为 prop 并将 change 作为事件。
以 input 表单元素为例
input v-modelsomething//相当于
input v-bind:valuesomething v-on:inputsomething $event.target.value如果在自定义组件中v-model 默认会利用名为 value 的 prop 和名为 input 的事件如下所示
父组件ModelChild v-modelmessage/ModelChild子组件div{{value}}/divprops:{value: String},methods: {test1(){this.$emit(input, 小红)},},15、Vue 组件间通信有哪几种方式
Vue 组件间通信只要指以下 3 类通信****父子组件通信、隔代组件通信、兄弟组件通信****下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。
1props / $emit 适用 父子组件通信
这种方法是 Vue 组件的基础相信大部分同学耳闻能详所以此处就不举例展开介绍。
2ref 与 $parent / $children适用 父子组件通信
ref如果在普通的 DOM 元素上使用引用指向的就是 DOM 元素如果用在子组件上引用就指向组件实例
$parent / $children访问父 / 子实例
3EventBus $emit / $on适用于 父子、隔代、兄弟组件通信
这种方法通过一个空的 Vue 实例作为中央事件总线事件中心用它来触发事件和监听事件从而实现任何组件间的通信包括父子、隔代、兄弟组件。
4 a t t r s / attrs/ attrs/listeners适用于 隔代组件通信 a t t r s 包 含 了 父 作 用 域 中 不 被 p r o p 所 识 别 ( 且 获 取 ) 的 特 性 绑 定 ( c l a s s 和 s t y l e 除 外 ) 。 当 一 个 组 件 没 有 声 明 任 何 p r o p 时 这 里 会 包 含 所 有 父 作 用 域 的 绑 定 ( c l a s s 和 s t y l e 除 外 ) 并 且 可 以 通 过 v − b i n d attrs包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时这里会包含所有父作用域的绑定 ( class 和 style 除外 )并且可以通过 v-bind attrs包含了父作用域中不被prop所识别(且获取)的特性绑定(class和style除外)。当一个组件没有声明任何prop时这里会包含所有父作用域的绑定(class和style除外)并且可以通过v−bindattrs 传入内部组件。通常配合 inheritAttrs 选项一起使用。 l i s t e n e r s 包 含 了 父 作 用 域 中 的 ( 不 含 . n a t i v e 修 饰 器 的 ) v − o n 事 件 监 听 器 。 它 可 以 通 过 v − o n listeners包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on listeners包含了父作用域中的(不含.native修饰器的)v−on事件监听器。它可以通过v−onlisteners 传入内部组件
5provide / inject适用于 隔代组件通信
祖先组件中通过 provider 来提供变量然后在子孙组件中通过 inject 来注入变量。provide / inject API 主要解决了跨级组件间的通信问题不过它的使用场景主要是子组件获取上级组件的状态跨级组件间建立了一种主动提供与依赖注入的关系。
6Vuex适用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store仓库。“store” 基本上就是一个容器它包含着你的应用中大部分的状态 ( state )。
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候若 store 中的状态发生变化那么相应的组件也会相应地得到高效更新。
改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
16、使用过 Vue SSR 吗说说 SSR
Vue.js 是构建客户端应用程序的框架。默认情况下可以在浏览器中输出 Vue 组件进行生成 DOM 和操作 DOM。然而也可以将同一个组件渲染为服务端的 HTML 字符串将它们直接发送到浏览器最后将这些静态标记激活为客户端上完全可交互的应用程序。
即SSR大致的意思就是vue在客户端将标签渲染成的整个 html 片段的工作在服务端完成服务端形成的html 片段直接返回给客户端这个过程就叫做服务端渲染。
服务端渲染 SSR 的优缺点如下
1服务端渲染的优点
更好的 SEO因为 SPA 页面的内容是通过 Ajax 获取而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容而 SSR 是直接由服务端返回已经渲染好的页面数据已经包含在页面中所以搜索引擎爬取工具可以抓取渲染好的页面
更快的内容到达时间首屏加载更快SPA 会等待所有 Vue 编译后的 js 文件都下载完成后才开始进行页面的渲染文件下载等需要一定的时间等所以首屏渲染需要一定的时间SSR 直接由服务端渲染好页面直接返回显示无需等待下载 js 文件及再去渲染等所以 SSR 有更快的内容到达时间
2) 服务端渲染的缺点
更多的开发条件限制例如服务端渲染只支持 beforCreate 和 created 两个钩子函数这会导致一些外部扩展库需要特殊处理才能在服务端渲染应用程序中运行并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同服务端渲染应用程序需要处于 Node.js server 运行环境
更多的服务器负载在 Node.js 中渲染完整的应用程序显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive - CPU 密集)因此如果你预料在高流量环境 ( high traffic ) 下使用请准备相应的服务器负载并明智地采用缓存策略。
17、vue-router 路由模式有几种
vue-router 有 3 种路由模式hash、history、abstract对应的源码如下所示 switch (mode) {case history:this.history new HTML5History(this, options.base)breakcase hash:this.history new HashHistory(this, options.base, this.fallback)breakcase abstract:this.history new AbstractHistory(this, options.base)breakdefault:if (process.env.NODE_ENV ! production) {assert(false, invalid mode: ${mode})}}其中3 种路由模式的说明如下
hash: 使用 URL hash 值来作路由。支持所有浏览器包括不支持 HTML5 History Api 的浏览器
history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式
abstract : 支持所有 JavaScript 运行环境如 Node.js 服务器端。如果发现没有浏览器的 API路由会自动强制进入这个模式.
19、能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗
1hash 模式的实现原理
早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站它的 location.hash 的值为 ‘#search’
https://www.word.com#search
hash 路由模式的实现主要是基于下面几个特性 URL 中 hash 值只是客户端的一种状态也就是说当向服务器端发出请求时hash 部分不会被发送 hash 值的改变都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换 可以通过 a 标签并设置 href 属性当用户点击这个标签后URL 的 hash 值会发生改变或者使用 JavaScript 来对 loaction.hash 进行赋值改变 URL 的 hash 值 我们可以使用 hashchange 事件来监听 hash 值的变化从而对页面进行跳转渲染。
2history 模式的实现原理
HTML5 提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个
history.pushState() 和 history.repalceState()。这两个 API 可以在不进行刷新的情况下操作浏览器的历史纪录。 唯一不同的是前者是新增一个历史记录后者是直接替换当前的历史记录如下所示
window.history.pushState(null, null, path);window.history.replaceState(null, null, path);history 路由模式的实现主要基于存在下面几个特性 pushState 和 repalceState 两个 API 来操作实现 URL 的变化 我们可以使用 popstate 事件来监听 url 的变化从而对页面进行跳转渲染 history.pushState() 或 history.replaceState() 不会触发 popstate 事件这时我们需要手动触发页面跳转渲染。
19、Vue 框架怎么实现对象和数组的监听
Vue 数据双向绑定主要是指数据变化更新视图视图变化更新数据。
即 输入框内容变化时Data 中的数据同步变化。即 View Data 的变化。 Data 中的数据变化时文本节点的内容同步变化。即 Data View 的变化。 其中View 变化更新 Data 可以通过事件监听的方式来实现所以 Vue 的数据双向绑定的工作主要是如何根据 Data 变化更新 View
Vue 主要通过以下 4 个步骤来实现数据双向绑定的 实现一个监听器 Observer对数据对象进行遍历包括子属性对象的属性利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话给这个对象的某个值赋值就会触发 setter那么就能监听到了数据变化。 实现一个解析器 Compile解析 Vue 模板指令将模板中的变量都替换成数据然后初始化渲染页面视图并将每个指令对应的节点绑定更新函数添加监听数据的订阅者一旦数据有变动收到通知调用更新函数进行数据更新。 实现一个订阅者 WatcherWatcher 订阅者是 Observer 和 Compile 之间通信的桥梁 主要的任务是订阅 Observer 中的属性值变化的消息当收到属性值变化的消息时触发解析器 Compile 中对应的更新函数。 实现一个订阅器 Dep订阅器采用 发布-订阅 设计模式用来收集订阅者 Watcher对监听器 Observer 和 订阅者 Watcher 进行统一管理。
20、Vue 是如何实现数据双向绑定的
如果被问到 Vue 怎么实现数据双向绑定大家肯定都会回答 通过 Object.defineProperty() 对数据进行劫持但是 Object.defineProperty() 只能对属性进行数据劫持不能对整个对象进行劫持。
同理无法对数组进行劫持但是我们在使用 Vue 框架中都知道Vue 能检测到对象和数组部分方法的操作的变化那它是怎么实现的呢我们查看相关代码如下 /**\* Observe a list of Array items.*/observeArray (items: Arrayany) {for (let i 0, l items.length; i l; i) {observe(items[i]) // observe 功能为监测数据的变化}}/**\* 对属性进行递归遍历*/let childOb !shallow observe(val) // observe 功能为监测数据的变化通过以上 Vue 源码部分查看我们就能知道 Vue 框架是通过遍历数组 和递归遍历对象从而达到利用 Object.defineProperty() 也能对对象和数组部分方法的操作进行监听。
21、Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 受现代 JavaScript 的限制 Vue 无法检测到对象属性的添加或删除。 由于 Vue 会在初始化实例时对属性执行 getter/setter 转化所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。 但是 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)来实现为对象添加响应式属性那框架本身是如何实现的呢
我们查看对应的 Vue 源码vue/src/core/instance/index.jsexport function set (target: Arrayany | Object, key: any, val: any): any {// target 为数组if (Array.isArray(target) isValidArrayIndex(key)) {// 修改数组的长度, 避免索引数组长度导致splcie()执行有误target.length Math.max(target.length, key)// 利用数组的splice变异方法触发响应式target.splice(key, 1, val)return val}// key 已经存在直接修改属性值if (key in target !(key in Object.prototype)) {target[key] valreturn val}const ob (target: any).__ob__// target 本身就不是响应式数据, 直接赋值if (!ob) {target[key] valreturn val}// 对属性进行响应式处理defineReactive(ob.value, key, val)ob.dep.notify()return val}我们阅读以上源码可知****vm.$set 的实现原理****是
如果目标是数组直接使用数组的 splice 方法触发相应式如果目标是对象会先判读属性是否存在、对象是否是响应式最终如果要对属性进行响应式处理则是通过调用 defineReactive 方法进行响应式处理 defineReactive 方法就是 Vue 在初始化对象时给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法22、虚拟 DOM 的优缺点
优点
保证性能下限 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作它的一些 DOM 操作的实现必须是普适的所以它的性能并不是最优的但是比起粗暴的 DOM 操作性能要好很多因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下依然可以提供还不错的性能即保证性能的下限
无需手动操作 DOM 我们不再需要手动去操作 DOM只需要写好 View-Model 的代码逻辑框架会根据虚拟 DOM 和 数据双向绑定帮我们以可预期的方式更新视图极大提高我们的开发效率
跨平台 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关相比之下虚拟 DOM 可以进行更方便地跨平台操作例如服务器渲染、weex 开发等等。
缺点:
无法进行极致优化 虽然虚拟 DOM 合理的优化足以应对绝大部分应用的性能需求但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
23、虚拟 DOM 实现原理
虚拟 DOM 的实现原理主要包括以下 3 部分 用 JavaScript 对象模拟真实 DOM 树对真实 DOM 进行抽象 diff 算法 — 比较两棵虚拟 DOM 树的差异 pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。
24、Vue 中的 key 有什么作用
1、key 是为 Vue 中 vnode 的唯一标记通过这个 key我们的 diff 操作可以更准确、更快速。
2、Vue 的 diff 过程可以概括为oldCh 和 newCh 各有两个头尾的变量 oldStartIndex、oldEndIndex 和 newStartIndex、newEndIndex它们会新节点和旧节点会进行两两对比即一共有4种比较方式newStartIndex 和oldStartIndex 、newEndIndex 和 oldEndIndex 、newStartIndex 和 oldEndIndex 、newEndIndex 和 oldStartIndex如果以上 4 种比较都没匹配如果设置了key就会用 key 再进行比较在比较的过程中遍历会往中间靠一旦 StartIdx EndIdx 表明 oldCh 和 newCh 至少有一个已经遍历完了就会结束比较。
所以 Vue 中 key 的作用是key 是为 Vue 中 vnode 的唯一标记通过这个 key我们的 diff 操作可以更准确、更快速!
3、更准确因为带 key 就不是就地复用了在 sameNode 函数 a.key b.key 对比中可以避免就地复用的情况。所以会更加准确。
更快速利用 key 的唯一性生成 map 对象来获取对应节点比遍历方式更快源码如下
function createKeyToOldIdx (children, beginIdx, endIdx) {let i, keyconst map {}for (i beginIdx; i endIdx; i) {key children[i].keyif (isDef(key)) map[key] i}return map}25、你有对 Vue 项目进行哪些优化
1代码层面的优化 v-if 和 v-show 区分使用场景 computed 和 watch 区分使用场景 v-for 遍历必须为 item 添加 key且避免同时使用 v-if 长列表性能优化 事件的销毁 图片资源懒加载 路由懒加载 第三方插件的按需引入 优化无限列表性能 服务端渲染 SSR or 预渲染
2Webpack 层面的优化 Webpack 对图片进行压缩 减少 ES6 转为 ES5 的冗余代码 提取公共代码 模板预编译 提取组件的 CSS 优化 SourceMap 构建结果输出分析 Vue 项目的编译优化
3基础的 Web 技术的优化 开启 gzip 压缩 浏览器缓存 CDN 的使用 使用 Chrome Performance 查找性能瓶颈
26、对于 vue3.0 特性你有什么了解的吗
Vue 3.0 的目标是让 Vue 核心变得更小、更快、更强大因此 Vue 3.0 增加以下这些新特性
1监测机制的改变
3.0 将带来基于代理 Proxy 的 observer 实现提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制 只能监测属性不能监测对象 检测属性的添加和删除 检测数组索引和长度的变更 支持 Map、Set、WeakMap 和 WeakSet。 新的 observer 还提供了以下特性 用于创建 observable 的公开 API。这为中小规模场景提供了简单轻量级的跨组件状态管理解决方案。 默认采用惰性观察。在 2.x 中不管反应式数据有多大都会在启动时被观察到。如果你的数据集很大这可能会在应用启动时带来明显的开销。在 3.x 中只观察用于渲染应用程序最初可见部分的数据。 更精确的变更通知。在 2.x 中通过 Vue.set 强制添加新属性将导致依赖于该对象的 watcher 收到变更通知。在 3.x 中只有依赖于特定属性的 watcher 才会收到通知。 不可变的 observable我们可以创建值的“不可变”版本即使是嵌套属性除非系统在内部暂时将其“解禁”。这个机制可用于冻结 prop 传递或 Vuex 状态树以外的变化。 更好的调试功能我们可以使用新的 renderTracked 和 renderTriggered 钩子精确地跟踪组件在什么时候以及为什么重新渲染。
2模板
模板方面没有大的变更只改了作用域插槽2.x 的机制导致作用域插槽变了父组件会重新渲染而 3.0 把作用域插槽改成了函数的方式这样只会影响子组件的重新渲染提升了渲染的性能。
同时对于 render 函数的方面vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。
3对象式的组件声明方式
vue2.x 中的组件是通过声明的方式传入一系列 option和 TypeScript 的结合需要通过一些装饰器的方式来做虽然能实现功能但是比较麻烦。
3.0 修改了组件的声明方式改成了类式的写法这样使得和 TypeScript 的结合变得很容易。
此外vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂之后必须有一个静态类型系统来做一些辅助管理。
现在 vue3.0 也全面改用 TypeScript 来重写了更是使得对外暴露的 api 更容易结合 TypeScript。静态类型系统对于复杂代码的维护确实很有必要。
4其它方面的更改
vue3.0 的改变是全面的上面只涉及到主要的 3 个方面还有一些其他的更改 支持自定义渲染器从而使得 weex 可以通过自定义渲染器的方式来扩展而不是直接 fork 源码来改的方式。 支持 Fragment多个根节点和 Protal在 dom 其他部分渲染组建内容组件针对一些特殊的场景做了处理。 基于 treeshaking 优化提供了更多的内置功能。
27、响应式原理变化侦测
使用发布订阅模式将数据劫持和模板编译结合实现双向绑定
1、observer: 封装 Object.defineProperty 方法用来劫持对象属性的getter和setter以此来追踪数据变化。
2、读取数据时触发getter来收集依赖(Watcher)到Dep。
3、修改数据时触发setter并遍历依赖列表通知所有相关依赖Watcher
4、Dep 类为依赖找一个存储依赖的地方用来收集和管理依赖在getter中收集在setter中通知。
5、Watcher 类就是收集的依赖实际上是一个订阅器Watcher会将自己的实例赋值给window.target全局变量上然后去主动访问属性触发属性的gettergetter中会将此Watcher收集到Dep中Watcher的update方法会在Dep的通知方法中被调用触发更新。
6、Observer 类用来将一个对象的所有属性和子属性都变成响应式的通过递归调用defineReactive来实现。
7、由于无法侦测对象上新增/删除属性所以提供 $set 和 $delete API5。
28、Object.defineProperty怎么用 三个参数有什么作用啊
Object.defineProperty 是 JavaScript 中用来定义对象属性的方法。它接受三个参数
对象 (obj)要定义属性的对象。属性名 (prop)要定义或修改的属性名。属性描述符 (descriptor)一个包含属性特性的对象。
属性描述符 (descriptor) 对象中可以包含以下可选键
value属性的值。writable属性是否可写默认为 false。enumerable属性是否可枚举默认为 false即不会出现在 for...in 循环和 Object.keys() 结果中。configurable属性是否可配置默认为 false即不允许改变属性的特性或删除属性。
Object.defineProperty 的作用是控制属性的各种特性。通过使用该方法可以定义、修改或重新定义对象的属性并且可以设置属性的可写性、可枚举性和可配置性。这使得我们能够更精确地控制对象属性的行为。
例如下面的代码示例展示了如何使用 Object.defineProperty 定义一个只读属性
const obj {};Object.defineProperty(obj, name, {value: John,writable: false,
});console.log(obj.name); // 输出John
obj.name Jane; // 无效属性是只读的赋值操作被忽略
console.log(obj.name); // 输出John在上述示例中通过定义的属性描述符将属性 name 设置为只读 (writable: false)因此任何对该属性的赋值操作都会被忽略。
29、vue2和vue3的响应式原理都有什么区别呢
vue2 用的是 Object.defindProperty 但是vue3用的是Proxy
Object.defindProperty 缺点 一次只能对一个属性进行监听需要遍历来对所有属性监听 对于对象的新增属性需要手动监听 对于数组通过push、unshift方法增加的元素也无法监听
Proxy就没有这个问题可以监听整个对象的数据变化所以用vue3.0会用Proxy代替definedProperty。
30、Vue的patch diff 算法
patch将新老VNode节点进行比对然后将根据两者的比较结果进行最小单位地修改视图而不是将整个视图根据新的VNode重绘。patch的核心在于diff算法这套算法可以高效地比较virtual DOM的变更得出变化以修改视图。
diff算法核心是通过同层的树节点进行比较而非对树进行逐层搜索遍历的方式所以时间复杂度只有O(n)是一种相当高效的算法。 同层级比较只比较同一层级不跨级比较 tag 不相同则直接删除重建不在深度比较 tag 和 key两个都相同则认为是相同节点会进行深度比较
31、Vue 模板编译原理
Vue 的模板编译原理可以概括为以下几个步骤 解析Vue 将模板字符串转换为抽象语法树 (AST)。它使用 HTML 解析器将模板解析为 DOM 树并使用指令解析器将指令和表达式解析为对应的 AST 节点。 优化Vue 对生成的 AST 进行静态节点标记和静态根节点提升的优化过程。静态节点是在编译阶段确定不需要改变的节点可以被直接复用从而提高渲染性能。静态根节点提升将多次出现的静态子树提升到 render 函数外部避免每次重新创建静态子树。 代码生成Vue 将优化后的 AST 通过代码生成器转换为渲染函数。渲染函数是一个 JavaScript 函数它会在组件实例发生变化时被调用生成最终的虚拟 DOM 并更新真实 DOM。 渲染每当组件的状态发生变化时Vue 会调用渲染函数来重新生成虚拟 DOM并计算出与上一次渲染结果的差异 (Diff)。然后Vue 会将差异应用到真实 DOM 上完成视图的更新。
通过模板编译的过程Vue 实现了将模板转换为可复用的渲染函数的能力。这种方式使得开发者可以使用类似于传统模板引擎的语法来编写组件的模板并在运行时将其转换为高效的渲染函数实现数据驱动视图的更新。
32、Vue原理总结
1、【模板编译】将template模板经过编译系统后生成VNode模板字符串→AST→Render函数
2、【渲染】然后再通过渲染系统来将VNode生成真实DOMdocument.createElement Mount挂载到真实DOM节点上
3、【响应式】通过响应式系统对数据进行监听当数据发生改变时触发依赖项组件
4、【diff Patch】组件内收到通知后会通过diff算法对比VNode的变化尽可能复用代码找出最小差异保证性能消耗最小。
5、【渲染】拿到需要新增/删除/修改的VNode后逐一去操作真实DOM进行修改通过选择器选择到对应真实DOM节点进行修改
六、Webpack 篇
1、谈谈你对Webpack的理解Webpack是什么
Webpack 是一个 静态模块打包器可以分析各个模块的依赖关系项目中的所有资源皆为模块通过分析模块间的依赖关系在其内部递归构建出一个依赖关系图其中包含应用程序需要的每个模块然后将这些模块打包成一个或多个 bundle。最终编绎输出模块为 HTML、JavaScript、CSS 以及各种静态文件图片、字体等。
webpack 就像一条生产线,要经过一系列处理流程(loader)后才能将源文件转换成输出结果。 这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。
插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。 webpack 在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。
webpack的主要作用如下
模块打包 可以将不同模块的文件打包整合在一起并且保证它们之间的引用正确执行有序。利用打包我们就可以在开发的时候根据我们自己的业务自由划分文件模块保证项目结构的清晰和可读性。
编译兼容 在前端的“上古时期”手写一堆浏览器兼容代码一直是令前端工程师头皮发麻的事情而在今天这个问题被大大的弱化了通过webpack的Loader机制不仅仅可以帮助我们对代码做polyfill还可以编译转换诸如.less.vue.jsx这类在浏览器无法识别的格式文件让我们在开发的时候可以使用新特性和新语法做开发提高开发效率。
能力扩展 通过webpack的Plugin机制我们在实现模块化打包和编译兼容的基础上可以进一步实现诸如按需加载代码压缩等一系列功能帮助我们进一步提高自动化程度工程效率以及打包输出的质量。
2、Webpack的打包过程/打包原理/构建流程
Webpack 的打包过程可以大致分为以下几个步骤 解析配置Webpack 会读取并解析项目根目录下的配置文件例如webpack.config.js获取打包相关的配置信息。 入口分析根据配置中指定的入口文件entryWebpack 会从入口开始递归地解析模块依赖关系。Webpack 会根据入口文件及其所依赖的文件构建起一个完整的模块依赖图Dependency Graph。 模块转换Webpack 在解析每个模块时会根据配置中的规则loaders对模块进行相应的转换操作。Loaders 可以将模块源代码转换成 Webpack 可以处理的有效模块代码例如将 ES6 语法转换为 ES5、将 Sass 编译为 CSS 等。 模块组装经过模块转换后Webpack 会得到一组已转换的模块。这些模块通过加载器产生的中间代码通常是 CommonJS 或 ES Module 格式被组装成一个或多个 Chunk。Chunk 是由模块之间的依赖关系构成的一个代码块。 输出生成在组装完成后Webpack 将根据配置中的出口设置output来确定输出的文件名和路径并将 Chunk 内容写入指定的输出文件中。同时根据需要可以对输出文件进行压缩、混淆、代码分割等优化处理。 构建完成Webpack 在整个构建过程中会生成一系列的构建信息、警告和错误提示以便开发者进行问题排查和优化。最终Webpack 将输出构建结果的统计信息包括文件大小、模块数量等。
总的来说Webpack 的打包原理是通过解析配置文件、分析入口文件依赖、应用加载器转换模块、组装模块成 Chunk并将结果进行输出。在这个过程中Webpack 提供了丰富的插件和工具使得开发者能够根据需求自定义构建流程并实现各种功能例如代码拆分、按需加载、缓存优化等。
3、loader的作用
webpack中的loader是一个函数主要为了实现源码的转换所以loader函数会以源码作为参数比如将ES6转换为ES5将less转换为css然后再将css转换为js以便能嵌入到html文件中。
默认情况下webpack只支持对js和json文件进行打包但是像css、html、png等其他类型的文件webpack则无能为力。因此就需要配置相应的loader进行文件内容的解析转换。
4、有哪些常见的Loader他们是解决什么问题的
常用的loader如下 image-loader加载并且压缩图片文件。 less-loader加载并编译 LESS 文件。 sass-loader加载并编译 SASS/SCSS 文件。 css-loader加载 CSS支持模块化、压缩、文件导入等特性使用css-loader必须要配合使用style-loader。 style-loader用于将 CSS 编译完成的样式挂载到页面的 style 标签上。需要注意 loader 执行顺序style-loader 要放在第一位loader 都是从后往前执行。 babel-loader把 ES6 转换成 ES5 postcss-loader扩展 CSS 语法使用下一代 CSS可以配合 autoprefixer 插件自动补齐 CSS3 前缀。 eslint-loader通过 ESLint 检查 JavaScript 代码。 vue-loader加载并编译 Vue 组件。 file-loader把文件输出到一个文件夹中在代码中通过相对 URL 去引用输出的文件 (处理图片和字体) url-loader与 file-loader 类似区别是用户可以设置一个阈值大于阈值会交给 file-loader 处理小于阈值时返回文件 base64 形式编码 (处理图片和字体)。 source-map-loader加载额外的 Source Map 文件以方便断点调试。
5、plugin的作用
plugin是一个类类中有一个apply()方法主要用于Plugin的安装可以在其中监听一些来自编译器发出的事件在合适的时机做一些事情。
webpack中的plugin赋予其各种灵活的功能例如打包优化、资源管理、环境变量注入等它们会运行在webpack的不同阶段钩子 / 生命周期贯穿了webpack整个编译周期。目的在于「解决 loader 无法实现的其他事」。
6、有哪些常见的Plugin他们是解决什么问题的 html-webpack-plugin可以复制一个有结构的html文件并自动引入打包输出的所有资源JS/CSS clean-webpack-plugin重新打包自动清空 dist 目录 mini-css-extract-plugin提取 js 中的 css 成单独文件 optimize-css-assets-webpack-plugin压缩css uglifyjs-webpack-plugin压缩js commons-chunk-plugin提取公共代码 define-plugin定义环境变量
7、Webpack中Loader和Plugin的区别
运行时机
1.loader运行在编译阶段
2.plugins 在整个周期都起作用
使用方式
Loader:1.下载 2.使用
Plugin:1.下载 2.引用 3.使用
loader是文件加载器能够加载资源文件并对这些文件进行一些处理诸如编译、压缩等最终一起打包到指定的文件中plugin赋予了webpack各种灵活的功能例如打包优化、资源管理、环境变量注入等目的是解决 loader无法实现的其他事。
在运行时机上loader 运行在打包文件之前plugin则是在整个编译周期都起作用。
在配置上loader在module.rules中配置作为模块的解析规则类型为数组。每一项都是一个 Object内部包含了 test(类型文件)、loader、options (参数)等属性plugin在 plugins中单独配置类型为数组每一项是一个 plugin 的实例参数都通过构造函数传入。
8、webpack的热更新是如何做到的说明其原理
热更新的核心就是客户端从服务端拉去更新后的文件准确的说是 chunk diff (chunk 需要更新的部分)实际上webpack-dev-server与浏览器之间维护了一个websocket当本地资源发生变化时webpack-dev-server会向浏览器推送更新并带上构建时的hash让客户端与上一次资源进行对比。客户端对比出差异后会向webpack-dev-server发起 Ajax 请求来获取更改内容(文件列表、hash)这样客户端就可以再借助这些信息继续向webpack-dev-server发起 jsonp 请求获取该chunk的增量更新。
后续的部分(拿到增量更新之后如何处理哪些状态该保留哪些又需要更新)由HotModulePlugin 来完成提供了相关 API 以供开发者针对自身场景进行处理像react-hot-loader和vue-loader都是借助这些 API 实现热更新。
详细
1、在 webpack 的 watch 模式下文件系统中某一个文件发生修改webpack 监听到文件变化根据配置文件对模块重新编译打包并将打包后的代码通过简单的 JavaScript 对象保存在内存中。
2、webpack-dev-server 和 webpack 之间的接口交互而在这一步主要是 dev-server 的中间件webpack-dev-middleware 和 webpack 之间的交互webpack-dev-middleware 调用 webpack 暴露的 API对代码变化进行监控并且告诉 webpack将代码打包到内存中。
3、webpack-dev-server 对文件变化的一个监控这一步不同于第一步并不是监控代码变化重新打包。当我们在配置文件中配置了devServer.watchContentBase 为 true 的时候Server 会监听这些配置文件夹中静态文件的变化变化后会通知浏览器端对应用进行 live reload。注意这儿是浏览器刷新和 HMR 是两个概念
4、webpack-dev-server 代码的工作该步骤主要是通过 sockjswebpack-dev-server 的依赖在浏览器端和服务端之间建立一个 websocket 长连接将 webpack 编译打包的各个阶段的状态信息告知浏览器端
同时也包括第三步中 Server 监听静态文件变化的信息。浏览器端根据这些 socket 消息进行不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值后面的步骤根据这一 hash 值来进行模块热替换。
webpack-dev-server/client 端并不能够请求更新的代码也不会执行热更模块操作而把这些工作又交回给了 webpackwebpack/hot/dev-server 的工作就是根据 webpack-dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。当然如果仅仅是刷新浏览器也就没有后面那些步骤了。HotModuleReplacement.runtime 是客户端 HMR 的中枢它接收到上一步传递给他的新模块的 hash 值它通过 JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求服务端返回一个 json该 json 包含了所有要更新的模块的 hash 值获取到更新列表后该模块再次通过 jsonp 请求获取到最新的模块代码。
5、决定 HMR 成功与否的关键步骤在该步骤中HotModulePlugin 将会对新旧模块进行对比决定是否更新模块在决定更新模块后检查模块之间的依赖关系更新模块的同时更新模块间的依赖引用。最后一步当 HMR 失败后回退到 live reload 操作也就是进行浏览器刷新来获取最新打包代码。
9、如何解决循环依赖问题
Webpack 中将 require 替换为 webpack_require会根据 moduleId 到 installedModules 找是否加载过加载过则直接返回之前的 export不会重复加载。
10、如何提高Webpack构建速度
1、代码压缩 JS 压缩 webpack 4.0默认在生产环境的时候是支持代码压缩的即modeproduction模式下。实际上webpack 4.0默认是使用terser-webpack-plugin这个压缩插件在此之前是使用 uglifyjs-webpack-plugin两者的区别是后者对 ES6 的压缩不是很好同时我们可以开启 parallel参数使用多进程压缩加快压缩。 CSS 压缩 CSS 压缩通常是去除无用的空格等因为很难去修改选择器、属性的名称、值等。可以使用另外一个插件css-minimizer-webpack-plugin。 HTML 压缩
使用HtmlWebpackPlugin插件来生成 HTML 的模板时候通过配置属性minify进行 html 优化。module.exports {plugin:[new HtmlwebpackPlugin({minify:{minifyCSS: false, // 是否压缩csscollapseWhitespace: false, // 是否折叠空格removeComments: true // 是否移除注释}})]}2、图片压缩
配置image-webpack-loader
3、Tree Shaking
Tree Shaking是一个术语在计算机中表示消除死代码依赖于 ES Module 的静态语法分析不执行任何的代码可以明确知道模块的依赖关系。在webpack实现Tree shaking有两种方案
usedExports通过标记某些函数是否被使用之后通过 Terser 来进行优化的
module.exports {...optimization:{ usedExports}}使用之后没被用上的代码在webpack打包中会加入unused harmony export mul注释用来告知Terser在优化时可以删除掉这段代码。
sideEffects跳过整个模块/文件直接查看该文件是否有副作用
sideEffects用于告知webpack compiler哪些模块时有副作用配置方法是在package.json中设置sideEffects属性。如果sideEffects设置为false就是告知webpack可以安全的删除未用到的exports。如果有些文件需要保留可以设置为数组的形式如
sideEffecis:[./src/util/format.js,*.css // 所有的css文件]4、缩小打包域
排除webpack不需要解析的模块即在使用loader的时候在尽量少的模块中去使用。可以借助 include和exclude这两个参数规定loader只在那些模块应用和在哪些模块不应用。
5、减少 ES6 转为 ES5 的冗余代码
使用bable-plugin-transform-runtime插件
6、提取公共代码
通过配置CommonsChunkPlugin插件将多个页面的公共代码抽离成单独的文件
7、其他
组件懒加载、路由懒加载、开启gzip、公共的第三方包上cdn、配置cache缓存Loader对文件的编译副本、配置resolve提高文件的搜索速度: src
七、性能优化篇
1、浏览器缓存优化
为了让浏览器缓存发挥最大作用该策略尽量遵循以下五点就能发挥浏览器缓存最大作用。 「考虑拒绝一切缓存策略」Cache-Control:no-store 「考虑资源是否每次向服务器请求」Cache-Control:no-cache 「考虑资源是否被代理服务器缓存」Cache-Control:public/private 「考虑资源过期时间」Expires:t/Cache-Control:max-aget,s-maxaget 「考虑协商缓存」Last-Modified/Etag
缓存策略通过设置HTTP报文实现在形式上分为**「强缓存/强制缓存」和「协商缓存/对比缓存」**。为了方便对比笔者将某些细节使用图例展示相信你有更好的理解。
整个缓存策略机制很明了先走强缓存若命中失败才走协商缓存。若命中强缓存直接使用强缓存若未命中强缓存发送请求到服务器检查是否命中协商缓存若命中协商缓存服务器返回304通知浏览器使用本地缓存否则返回最新资源。
有两种较常用的应用场景值得使用缓存策略一试当然更多应用场景都可根据项目需求制定。 「频繁变动资源」设置Cache-Control:no-cache使浏览器每次都发送请求到服务器配合Last-Modified/ETag验证资源是否有效 「不常变化资源」设置Cache-Control:max-age31536000对文件名哈希处理当代码修改后生成新的文件名当HTML文件引入文件名发生改变才会下载最新文件
2、渲染层面性能优化
**「渲染层面」**的性能优化无疑是如何让代码解析更好执行更快。因此笔者从以下五方面做出建议。 「CSS策略」基于CSS规则 「DOM策略」基于DOM操作 「阻塞策略」基于脚本加载 「回流重绘策略」基于回流重绘 「异步更新策略」基于异步更新
上述五方面都是编写代码时完成充满在整个项目流程的开发阶段里。因此在开发阶段需时刻注意以下涉及到的每一点养成良好的开发习惯性能优化也自然而然被使用上了。
渲染层面的性能优化更多表现在编码细节上而并非实体代码。简单来说就是遵循某些编码规则才能将渲染层面的性能优化发挥到最大作用。
**「回流重绘策略」**在渲染层面的性能优化里占比较重也是最常规的性能优化之一。上年笔者发布的掘金小册《玩转CSS的艺术之美》使用一整章讲解回流重绘本章已开通试读更多细节请戳这里。
CSS策略 避免出现超过三层的嵌套规则 避免为ID选择器添加多余选择器 避免使用标签选择器代替类选择器 避免使用通配选择器只对目标节点声明规则 避免重复匹配重复定义关注可继承属性
DOM策略 缓存DOM计算属性 避免过多DOM操作 使用DOMFragment缓存批量化DOM操作 阻塞策略 脚本与DOM/其它脚本的依赖关系很强对
3、性能优化六大指标
六大指标基本囊括大部分性能优化细节可作为九大策略的补充。笔者根据每条性能优化建议的特征将指标划分为以下六方面。 「加载优化」资源在加载时可做的性能优化 「执行优化」资源在执行时可做的性能优化 「渲染优化」资源在渲染时可做的性能优化 「样式优化」样式在编码时可做的性能优化 「脚本优化」脚本在编码时可做的性能优化 「V8引擎优化」针对V8引擎特征可做的性能优化
八、其他杂项篇
1、常见的浏览器内核有哪些
主要分成两部分渲染引擎(layout engineer或Rendering Engine)和JS引擎。 渲染引擎负责取得网页的内容HTML、XML、图像等等、整理讯息例如加入CSS等以及计算网页的显示方式然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。 JS引擎则解析和执行javascript来实现网页的动态效果。
最开始渲染引擎和JS引擎并没有区分的很明确后来JS引擎越来越独立内核就倾向于只指渲染引擎。
常见内核 Trident 内核IE, MaxThon, TT, The World, 360, 搜狗浏览器等。[又称 MSHTML] Gecko 内核Netscape6 及以上版本FF, MozillaSuite / SeaMonkey 等 Presto 内核Opera7 及以上。 [Opera内核原为Presto现为Blink;] Webkit 内核Safari, Chrome等。 [ Chrome的BlinkWebKit 的分支]
2、网页前端性能优化的方式有哪些 1.压缩 css, js, 图片 2.减少 http 请求次数 合并 css、js 、合并图片雪碧图 3.使用 CDN 4.减少 dom 元素数量 5.图片懒加载 6.静态资源另外用无 cookie 的域名 7.减少 dom 的访问缓存 dom 8.巧用事件委托 9.样式表置顶、脚本置低
3、网页从输入网址到渲染完成经历了哪些过程
大致可以分为如下7步 输入网址 发送到DNS服务器并获取域名对应的web服务器对应的ip地址 与web服务器建立TCP连接 浏览器向web服务器发送http请求 web服务器响应请求并返回指定url的数据或错误信息或重定向的新的url地址 浏览器下载web服务器返回的数据及解析html源文件 生成DOM树解析css和js渲染页面直至显示完成
4、线程与进程的区别 一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程使得多线程程序的并发性高。 另外进程在执行过程中拥有独立的内存单元而多个线程共享内存从而极大地提高了程序的运行效率。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行必须依存在应用程序中由应用程序提供多个线程执行控制。 从逻辑角度来看多线程的意义在于一个应用程序中有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
5、HTTP常见的状态码 100 Continue 继续一般在发送post请求时已发送了http header之后服务端将返回此信息表示确认之后发送具体参数信息 200 OK 正常返回信息 201 Created 请求成功并且服务器创建了新的资源 202 Accepted 服务器已接受请求但尚未处理 301 Moved Permanently 请求的网页已永久移动到新位置。 302 Found 临时性重定向。 303 See Other 临时性重定向且总是使用 GET 请求新的 URI。 304 Not Modified 自从上次请求后请求的网页未修改过。 400 Bad Request 服务器无法理解请求的格式客户端不应当尝试再次使用相同的内容发起请求。 401 Unauthorized 请求未授权。 403 Forbidden 禁止访问。 404 Not Found 找不到如何与 URI 相匹配的资源。 500 Internal Server Error 最常见的服务器端错误。 503 Service Unavailable 服务器端暂时无法处理请求可能是过载或维护。
6、图片懒加载
当页面滚动的时间被触发 - 执行加载图片操作 - 判断图片是否在可视区域内 - 在则动态将data-src的值赋予该图片
7、移动端性能优化 尽量使用css3动画开启硬件加速 适当使用touch时间代替click时间 避免使用css3渐变阴影效果 可以用transform: translateZ(0) 来开启硬件加速 不滥用float。float在渲染时计算量比较大尽量减少使用 不滥用web字体。web字体需要下载解析重绘当前页面 合理使用requestAnimationFrame动画代替setTimeout css中的属性css3 transitions、css3 3D transforms、opacity、webGL、video会触发GUP渲染耗电
8、TCP 传输的三次握手、四次挥手策略
三次握手
为了准确无误地吧数据送达目标处TCP协议采用了三次握手策略。用TCP协议把数据包送出去后TCP不会对传送后的情况置之不理他一定会向对方确认是否送达握手过程中使用TCP的标志SYN和ACK 发送端首先发送一个带SYN的标志的数据包给对方 接收端收到后回传一个带有SYN/ACK标志的数据包以示传达确认信息 最后发送端再回传一个带ACK的标志的数据包代表“握手”结束
如在握手过程中某个阶段莫明中断TCP协议会再次以相同的顺序发送相同的数据包
断开一个TCP连接需要“四次挥手” 第一次挥手主动关闭方发送一个FIN用来关注主动方到被动关闭方的数据传送也即是主动关闭方告诫被动关闭方我已经不会再给你发数据了在FIN包之前发送的数据如果没有收到对应的ACK确认报文主动关闭方依然会重发这些数据。但是此时主动关闭方还可以接受数据 第二次挥手被动关闭方收到FIN包后发送一个ACK给对方确认序号收到序号 1与SYN相同一个 FIN占用一个序号 第三次挥手被动关闭方发送一个 FIN。用来关闭被动关闭方到主动关闭方的数据传送也就是告诉主动关闭方我的数据也发送完了不会给你发送数据了 第四次挥手主动关闭方收到FIN后发送一个ACK给被动关闭方确认序号为收到序号1至此完成四次挥手
9、HTTP 和 HTTPS为什么HTTPS安全 HTTP协议通常承载与 TCP协议之上在HTTP和TCP之间添加一个安全协议层SSL或TSL这个时候就成了我们常说的HTTPS 默认HTTP的端口号为80HTTPS的端口号为443 因为网络请求需要中间有很多的服务器路由的转发中间的节点都可能篡改信息而如果使用HTTPS密钥在你和终点站才有https之所有说比http安全是因为他利用ssl/tls协议传输。包含证书流量转发负载均衡页面适配浏览器适配refer传递等保障了传输过程的安全性
10、axios和fetch区别对比
axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端本质上也是对原生XHR的封装只不过它是Promise的实现版本符合最新的ES规范它本身具有以下特征 从浏览器中创建 XMLHttpRequest 支持 Promise API 客户端支持防止CSRF 提供了一些并发请求的接口重要方便了很多的操作 从 node.js 创建 http 请求 拦截请求和响应 转换请求和响应数据 取消请求 自动转换JSON数据
fetch优势 语法简洁更加语义化 基于标准 Promise 实现支持 async/await 同构方便使用 isomorphic-fetch 更加底层提供的API丰富request, response 脱离了XHR是ES规范里新的实现方式
fetch存在问题 fetch是一个低层次的API你可以把它考虑成原生的XHR所以使用起来并不是那么舒服需要进行封装。 fetch只对网络请求报错对400500都当做成功的请求服务器返回 400500 错误码时并不会 reject只有网络错误这些导致请求不能完成时fetch 才会被 reject。 fetch默认不会带cookie需要添加配置项 fetch(url, {credentials: ‘include’}) fetch不支持abort不支持超时控制使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行造成了流量的浪费 fetch没有办法原生监测请求的进度而XHR可以
九、主观题篇
1、你都做过什么项目呢具体聊某一个项目中运用的技术.
注意用心找自己做的项目中自己感觉最拿出来手的复杂度最高用的技术最多的项目描述的时候尽可能往里面添加一些技术名词
布局我们用html5css3
我们会用reset.css重置浏览器的默认样式
JS框架的话我们选用的是jQuery(也可能是Zepto)
我们用版本控制工具git来协同开发
我们会基于gulp搭建的前端自动化工程来开发里面包含有我们的项目结构、我们需要引用的第三方库等一些信息我们还实现了sass编译、CSS3加前缀等的自动化
我们的项目中还用到了表单验证validate插件、图片懒加载Lazyload插件
2、你遇到过比较难的技术问题是你是如何解决的
3、常使用的库有哪些常用的前端开发工具开发过什么应用或组件
4、除了前端以外还了解什么其它技术么你最最厉害的技能是什么
5、对前端开发工程师这个职位是怎么样理解的它的前景会怎么样
前端是最贴近用户的程序员比后端、数据库、产品经理、运营、安全都近。 1、实现界****面交互**** 2、提升用户体验 3、有了Node.js前端可以实现服务端的一些事情 前端是最贴近用户的程序员前端的能力就是能让产品从 90分进化到 100 分甚至更好 参与项目快速高质量完成实现效果图精确到1px 与团队成员UI设计产品经理的沟通 做好的页面结构页面重构和用户体验 处理hack兼容、写出优美的代码格式 针对服务器的优化、拥抱最新前端技术。
6、你的优点是什么缺点是什么
新面试题
1、flex布局周边属性
在flex布局中有几个重要的周边属性可以调整弹性容器和弹性项目之间的间距和对齐方式。以下是这些属性的介绍 justify-content用于设置弹性项目在主轴上的对齐方式。它可以接受以下值 flex-start将弹性项目靠近主轴起始端对齐。flex-end将弹性项目靠近主轴结束端对齐。center将弹性项目居中对齐。space-between将弹性项目平均分布在主轴上首个项目靠近起始端最后一个项目靠近结束端项目之间的间距相等。space-around将弹性项目平均分布在主轴上包括首尾项目两侧项目之间和项目与容器之间的间距相等。 align-items用于设置弹性项目在交叉轴上的对齐方式。它可以接受以下值 flex-start将弹性项目靠近交叉轴起始端对齐。flex-end将弹性项目靠近交叉轴结束端对齐。center将弹性项目在交叉轴上居中对齐。stretch将弹性项目拉伸以填充交叉轴。baseline将弹性项目的基线对齐。 align-content用于设置多行弹性容器在交叉轴上的对齐方式。它可以接受以下值 flex-start将多行弹性容器靠近交叉轴起始端对齐。 flex-end将多行弹性容器靠近交叉轴结束端对齐。 center将多行弹性容器在交叉轴上居中对齐。 space-between将多行弹性容器平均分布在交叉轴上首行靠近起始端末行靠近结束端行与行之间间距相等。 space-around将多行弹性容器平均分布在交叉轴上包括首尾两侧行与行之间和容器与首尾行之间的间距相等。 stretch如果有剩余空间将行高拉伸以填充交叉轴。
4、flex-direction
flex-direction 设置主轴 的方向。
row: 默认值。作为一行水平地显示弹性项目 column 作为列垂直地显示弹性项目。 row-reverse: 等同行但方向相反。 column-reverse: 等同列但方向相反。
5、flex-wrap
设置是否换行展示
wrap: 允许换行nowrao: 不允许换行 默认值wrap-reverse 会在需要时换行以反方向。
以上就是flex布局中常用的周边属性它们可以帮助您控制弹性容器和弹性项目的对齐和间距。
6、flex-flow属性是以下属性的简写属性
flex-directionflex-wrap
2、mixins.extends 混入
在CSS预处理器如Sass、Less中mixins和extends是两个用于代码复用的关键概念。 Mixins混入混入是一种将一组CSS属性集合定义为可重用代码块的机制。通过定义混入可以在需要的地方引用它并将其中的样式应用到相应的元素上。混入通常用来避免重复编写相似的样式代码提高代码的复用性和可维护性。 下面是一个SCSS语法的混入示例 mixin button-styles {display: inline-block;padding: 10px 20px;background-color: #f1f1f1;color: #333;border-radius: 4px;
}.my-button {include button-styles;
}在上述示例中通过定义了一个名为button-styles的混入其中包含了一组按钮样式属性。然后通过include指令将该混入引入到.my-button选择器内部从而实现了样式的复用。 Extends扩展扩展是一种将一个CSS选择器的样式继承到另一个选择器上的机制。通过扩展可以使多个选择器共享相同的样式规则减少代码冗余。扩展会将被扩展选择器的样式直接复制到扩展选择器上形成一种继承关系。 下面是一个Less语法的扩展示例 .my-button {display: inline-block;padding: 10px 20px;background-color: #f1f1f1;color: #333;border-radius: 4px;
}.my-button-primary {:extend(.my-button);background-color: blue;color: white;
}在上述示例中.my-button-primary选择器通过:extend(.my-button)“指令来扩展”.my-button选择器的样式。这样.my-button-primary将会继承.my-button的所有样式并可以在此基础上进行修改或添加新的样式。
总之通过混入和扩展机制CSS预处理器可以实现代码块的复用和样式的继承提高了CSS代码的效率和可维护性。
3、无感登录 怎么实现
无感登录Seamless login是一种用户在使用应用程序或网站时无需手动输入用户名和密码进行认证的登录方式通常通过利用持久性凭证、单点登录或社交媒体授权等方式来实现。以下是实现无感登录的一些常见方法 持久性凭证Persistent Tokens该方法通过在用户设备上存储持久性凭证如长期有效的令牌或 Cookie使得用户下次访问应用程序时可以自动识别并进行登录。这种凭证可以使用加密技术保证安全性。 单点登录Single Sign-On, SSOSSO 是一种身份验证机制允许用户通过一次登录即可访问多个关联应用程序或网站。用户在通过认证后会获得一个令牌该令牌可以在不同的应用程序之间共享避免了重复登录的繁琐过程。 社交媒体授权Social Media Authorization通过允许用户使用其社交媒体账户如Facebook、Google等进行登录应用程序可以获取到用户授权的信息并将其作为有效的身份验证凭据使用。
在实现无感登录时需要考虑以下几点安全性问题
身份验证凭证的安全性持久性凭证在传输和存储过程中需要进行加密并采取合适的措施防止被窃取或滥用。用户隐私保护在收集和使用用户信息时需要明确告知用户哪些信息会被收集并遵守相关的数据隐私法规。安全漏洞和风险评估定期进行安全性评估修复潜在的安全漏洞确保用户的登录信息和数据不受到未授权访问。
综上所述实现无感登录可以结合持久性凭证、单点登录和社交媒体授权等技术手段同时务必关注用户隐私和系统安全确保用户登录过程的便利性和安全性。
4、vue 自定义指令都有哪些属性
在Vue中自定义指令可以通过钩子函数来扩展元素和组件的行为。以下是Vue自定义指令的常见钩子函数和属性 bind指令第一次绑定到元素时调用。 el: 指令所绑定的元素。binding: 一个包含以下属性的对象 value: 指令的绑定值。modifiers: 一个包含修饰符的对象。oldValue: 上一个绑定的值仅在 update 和 componentUpdated 钩子中可用。expression: 绑定值的字符串表示。 vnode: Vue编译生成的虚拟节点。 inserted被绑定元素插入父节点时调用仅保证父节点存在但不一定已插入文档。 同bind钩子函数的参数。 update被绑定元素所在的模板更新时调用但可能发生在其子元素更新之前。 同bind钩子函数的参数。 componentUpdated被绑定元素所在模板完成一次更新周期时调用。 同bind钩子函数的参数。 unbind指令与元素解绑时调用。 同bind钩子函数的参数。
除了钩子函数中的属性外还可以在自定义指令中使用一些全局属性和方法如Vue.directive用于注册全局自定义指令以及el、componentInstance等属性。
需要注意的是不同的钩子函数中可用的参数可能会有所不同请根据实际需要选择合适的钩子函数和属性来实现自定义指令的功能。
5、keep-alive 中的属性max 超过设置的数值10后如何处理
在Vue的keep-alive组件中可以使用max属性来限制缓存的组件实例数量。当缓存的组件实例数量超过max设置的数值10时keep-alive组件会按照LRULeast Recently Used的方式移除最不活跃的组件实例以保持缓存数量不超过设定值。
具体处理方式如下
当新的组件实例被缓存时会检查当前缓存的组件实例数量是否已达到或超过max设置的数值10。如果当前数量未超过max新的组件实例将被添加到缓存中不会触发删除操作。如果当前数量已达到或超过maxkeep-alive组件会根据最近访问时间和使用情况移除最不活跃的组件实例释放资源空间并将新的组件实例添加到缓存中。
这样做的目的是控制缓存组件的数量避免因缓存过多导致内存占用过高同时保持较常用的组件实例在缓存中提高系统性能和用户体验。
需要注意的是超出max设置的数值后的删除行为是自动触发的并且无法通过配置进行更改。如果需要更细粒度地控制组件实例的缓存和销毁行为可以考虑自定义缓存策略或动态调整max的值。
6、vue computed 在哪两个勾子函数中间 打印
在Vue的生命周期中computed属性的计算是在beforeMount和mounted勾子函数之间进行的。
具体的生命周期顺序如下
beforeCreate实例刚在内存中创建数据观测 (data observer) 和 event/watcher 事件配置之前。created实例已经完成了数据观测 (data observer)属性和方法的运算watch/event 事件回调。但是还未挂载到DOM上。beforeMount在将实例挂载到DOM之前此时computed属性的计算尚未开始。mounted实例已挂载到DOM上。这个阶段可以访问到DOM元素进行一些初始化操作例如使用外部插件、操作DOM等。beforeUpdate在数据更新之前发生在虚拟DOM重新渲染和打补丁之前。这里可以在更新之前访问现有的DOM。updated重新渲染和打补丁之后组件已更新。在这个钩子函数中可以操作DOM但要避免无限循环更新。beforeUnmountVue 3中为beforeUnmount实例销毁之前调用。在这里可以进行必要的清理工作。unmountedVue 3中为unmounted组件实例被销毁后调用。在这个阶段组件的数据绑定和监听器都会被移除可以进行最后的清理工作。
根据上述顺序在beforeMount和mounted之间打印computed属性的值可以在组件挂载到DOM之前获取计算属性的结果。
7、for in 和 for of的区别
for...in和for...of都是用于遍历数据的循环语句但它们有以下几个区别 遍历的目标不同 for...in用于遍历对象的可枚举属性。它会遍历对象自身及其原型链上的所有可枚举属性包括继承而来的属性。for...of用于遍历可迭代对象例如数组、字符串、Map、Set等。它可以遍历对象内部的元素或值。 遍历的值不同 for...in返回的是属性名或键名字符串类型。在遍历对象时可以通过获取属性值进一步操作属性对应的值。for...of返回的是集合中的元素或值。在遍历可迭代对象时直接获取对象的元素或值而不需要访问键名。 遍历顺序不同 for...in遍历对象属性时不保证顺序因为它是根据原始插入顺序遍历属性但在一些情况下可能会以其他顺序遍历。例如数字属性会按照升序排列for...of遍历可迭代对象时可以按照元素在集合中的顺序进行迭代。 使用场景不同 for...in适用于遍历对象属性例如需要遍历对象所有的键名。for...of适用于遍历可迭代对象中的元素或值例如遍历数组元素、字符串字符等。
示例代码说明
const obj { a: 1, b: 2 };// for...in 遍历对象属性
for (let key in obj) {console.log(key); // 输出a bconsole.log(obj[key]); // 输出1 2
}const arr [1, 2, 3];// for...of 遍历数组元素
for (let value of arr) {console.log(value); // 输出1 2 3
}综上所述for...in用于遍历对象属性而for...of用于遍历可迭代对象的元素或值。
8、nextTick 原理
nextTick是Vue中的一个异步方法用于在DOM更新后执行回调函数。它的原理如下 nextTick方法的调用时机通常是在Vue的更新队列被添加后的微任务中执行。更新队列会在数据变化后触发并对组件进行重新渲染。 当数据发生变化时Vue会将需要更新的组件添加到一个异步的更新队列中而不是立即进行DOM更新。 在下一次事件循环的微任务阶段Vue会开始遍历更新队列并执行相应的更新操作。 在每个组件更新完成之后nextTick方法注册的回调函数会被调用。这确保了回调函数在DOM更新后执行。 由于使用微任务的方式在nextTick的回调函数中执行的代码会在浏览器进行渲染之前被执行从而可以获取到最新的DOM结构。
总结来说nextTick利用Vue的异步更新机制在DOM更新后执行回调函数以便在更新完成后进行其他操作例如访问已更新的DOM元素或进行其它相关处理。
示例代码
// 模拟Vue中的异步更新和nextTick方法
const queue []; // 更新队列function updateComponent() {// 执行组件的更新操作...// ...// 更新完成后执行nextTick注册的回调函数nextTick();
}function nextTick(callback) {if (typeof callback function) {queue.push(callback);}Promise.resolve().then(() {for (let i 0; i queue.length; i) {if (typeof queue[i] function) {queue[i]();}}queue.length 0; // 清空队列});
}// 假设数据发生了变化触发组件更新
updateComponent();// 注册nextTick的回调函数
nextTick(() {console.log(DOM更新完成后执行);
});上述代码演示了一个简化的nextTick实现通过异步更新队列和微任务来保证在DOM更新完成后执行回调函数。
9、axios 中断请求
axions 中断请求有两种方式
方式一
使用 CancelToken.souce 工厂方法创建一个 cancel token代码如下
const CancelToken axios.CancelToken;
const source CancelToken.source();axios.get(https://mdn.github.io/dom-examples/abort-api/sintel.mp4, {cancelToken: source.token
}).catch(function (thrown) {// 判断请求是否已中止if (axios.isCancel(thrown)) {// 参数 thrown 是自定义的信息console.log(Request canceled, thrown.message);} else {// 处理错误}
});// 取消请求message 参数是可选的
source.cancel(Operation canceled by the user.);
复制代码中止后的网络请求变成如下所示 我们再来看看初始时和中止后的 souce 状态 可以看到初始时和中止后的 source 状态并没还有发生改变。那么我们是如何判断请求的中止状态呢axios 为我们提供了一个 isCancel() 方法用于判断请求的中止状态。isCancel() 方法的参数就是我们在中止请求时自定义的信息。 方式二
通过传递一个 executor 函数到 CancelToken 的构造函数来创建一个 cancel token
const CancelToken axios.CancelToken;
let cancel;axios.get(/user/12345, {cancelToken: new CancelToken(function executor(c) {// executor 函数接收一个 cancel 函数作为参数cancel c;})
});// 取消请求
cancel(Operation canceled by the user.);
复制代码浏览器运行结果与方式一一致此处不再赘述。
10、部署时候用hash刷新白屏问题
部署时使用哈希hash作为文件名的方式可以有效解决浏览器缓存问题确保用户获取到最新的文件版本。然而使用哈希刷新后可能会导致白屏问题这主要是因为以下原因 缓存问题 当文件名中的哈希发生变化时浏览器将视为一个新的文件并下载最新的文件。但在某些情况下浏览器可能仍然会从缓存中加载旧文件导致页面出现错误或白屏。 引用路径问题 如果在 HTML 或 CSS 文件中引用了被哈希的资源文件而没有及时更新对应的路径浏览器将无法正确地加载新文件从而导致白屏问题。
为解决哈希刷新导致的白屏问题可以尝试以下方法 强制刷新页面 可以通过按下 Ctrl F5Windows或 Command Shift RMac的组合键来强制刷新页面忽略缓存并重新加载所有文件。 更新引用路径 确保在 HTML 或 CSS 文件中对哈希文件的引用路径是正确的。可以修改对应文件引用的路径或者使用相对路径或绝对路径来确保能够正确加载最新的文件。 清除浏览器缓存 如果问题仍然存在可以尝试清除浏览器缓存。具体的步骤取决于所使用的浏览器可以通过浏览器设置或按照特定浏览器的教程进行操作。 使用版本号代替哈希 考虑在文件名中使用带有版本号的方式来解决缓存问题而不是使用哈希。每次更新文件时修改文件名中的版本号确保浏览器能够正确加载最新的文件。
请根据具体情况尝试这些方法并确保在部署前进行充分测试以避免出现白屏问题。
11、tab页用路由模式咋实现
在前端开发中可以使用路由库或框架来实现tab页的路由模式。以下是一般的实现步骤 配置路由 首先您需要配置路由以定义每个tab对应的URL路径和组件。这可以通过路由库或框架的配置文件完成。 创建tab组件 创建一个tab组件用于显示和切换不同的标签页。该组件可以包含tab标题和一个用于渲染对应标签页内容的容器。 处理tab切换事件 在tab组件中为每个tab标题添加点击事件处理程序以便在用户点击tab时触发路由切换操作。您可以使用路由库或框架提供的方法来进行导航操作将当前URL更新为对应的tab路由。 渲染对应标签页内容 当路由发生变化时根据当前URL路径获取到对应的标签页组件并将其渲染到tab组件的内容容器中。这通常由路由库或框架自动完成。
请注意在实际开发中具体的实现方式可能会因所使用的前端框架或库而有所差异。您可以参考相关框架或库的文档查找适合您项目的具体实现方法。
同时以下是几个常用的前端路由库或框架供您参考
React Router适用于React应用的流行路由库。Vue RouterVue.js官方提供的路由库。Angular RouterAngular框架自带的路由模块。
12、vuex中b.module里面咋接收根目录下面的state
在 Vuex 的模块中如果需要访问根目录下的 state你可以使用 rootState 参数。下面是一个示例:
在根目录的 state 中有一个名为 count 的状态而在 vuex 的 b 模块里需要获取这个状态的值。
// 在 b 模块的 getters、actions 或者 mutations 中可以通过 rootState 参数访问根目录下的 state// b.jsb 模块
const b {// ...getters: {getCountFromRoot(state, getters, rootState) {return rootState.count;}},// ...
};export default b;通过 rootState.count 即可在 getCountFromRoot getter 中访问到根目录下的 count 状态。
13、A跳转B再跳转C返回时怎么直接到A
在路由跳转中要实现从 C 页面返回到 A 页面可以使用路由导航的方法进行跳转和返回操作。以下是一种可能的解决方案 从页面 A 跳转到页面 B 可以使用路由库或框架提供的导航方法比如 router.push 或 router-link 组件来实现从 A 跳转到 B。 从页面 B 跳转到页面 C 同样使用路由导航方法实现从 B 跳转到 C。 在页面 C 中直接返回到页面 A 可以使用路由库或框架提供的导航方法进行返回操作。一种常见的方法是使用 router.go(-2)其中 -2 表示返回两个页面的历史记录即返回到页面 A。
具体代码示例以 Vue Router 为例
在组件 A 中进行跳转到组件 B 的操作
// 在组件 A 中调用路由导航方法跳转到组件 B
this.$router.push({ path: /b });在组件 B 中进行跳转到组件 C 的操作
// 在组件 B 中调用路由导航方法跳转到组件 C
this.$router.push({ path: /c });在组件 C 中进行直接返回到组件 A 的操作
// 在组件 C 中调用路由导航方法返回到组件 A
this.$router.go(-2);以上代码示例假设你的路由路径配置中存在名为 /b 和 /c 的路由且组件 A、B、C 分别与对应的路由关联。
希望对你有所帮助如果还有其他问题请随时提问。
14、前端怎么部署配置
前端部署和配置依赖于你使用的前端框架和工具。下面是一般性的步骤来帮助你进行前端部署和配置 打包静态资源在部署前首先需要将前端代码打包成静态资源。大多数前端框架和构建工具如Webpack、Parcel、Rollup等都提供了打包功能。运行相应的打包命令它们会将你的源代码编译、压缩和打包成最终的静态文件。 选择合适的服务器选择适合你的项目的服务器。常用的选择包括 静态文件服务器如果你的前端代码只包含静态文件HTML、CSS、JavaScript可以使用简单的静态文件服务器如Nginx、Apache等。后端服务器如果你的前端代码与后端API服务集成在同一服务器上你可以使用类似Express.js、Django等后端框架来托管前端静态文件。 配置服务器根据你选择的服务器类型对服务器进行相应的配置 静态文件服务器在服务器上设置静态文件目录并确保服务器能够访问到静态资源文件。后端服务器根据框架和服务器配置将静态文件目录设置为前端打包生成的路径并确保服务器能够正确地处理前端路由。 上传静态资源将打包生成的静态资源上传到服务器。具体的上传方法和工具取决于你使用的服务器和部署方式 如果是使用静态文件服务器可以通过FTP、SCP或其他文件传输工具将静态资源文件上传到指定目录。如果是与后端服务器集成可以将打包生成的静态资源文件与后端代码一起上传至对应的服务器路径。 配置域名和端口根据你的需求为部署的前端应用配置域名和端口。这涉及到DNS解析和服务器端口配置具体操作可以参考相关文档或咨询服务提供商。
以上是一个简单的前端部署和配置流程的概述。实际的部署过程可能因具体项目和环境而有所不同。确切的步骤和指令取决于你所使用的前端框架、工具和服务器类型。
希望对你有所帮助如有其他问题请随时提问。
15、sass和less用到哪些属性配置啥的
Sass和Less是两种常用的CSS预处理器它们为CSS提供了更强大的功能和易用性。下面是它们的一些主要属性和配置
Sass
变量使用$符号定义变量并可以在样式中多次引用。嵌套规则可以将选择器嵌套在父选择器内简化代码结构。混合器Mixins类似于函数可以重用一组样式并在需要的地方进行调用。继承使用extend关键字实现样式的继承减少重复代码。导入外部文件使用import导入外部Sass文件可以模块化管理样式。
Less
变量使用符号定义变量并可以在样式中多次引用。嵌套规则可以将选择器嵌套在父选择器内提高可读性和维护性。混合器Mixins类似于函数可以重用一组样式并在需要的地方进行调用。继承使用:extend关键字实现样式的继承减少重复代码。导入外部文件使用import导入外部Less文件可以模块化管理样式。
配置方面
安装和编译需要安装Sass或Less的编译工具例如Sass使用的是Ruby的sass gem而Less使用的是Less.js或者其他构建工具。编译选项可以配置编译工具的选项例如输入文件路径、输出文件路径、编译模式开发环境或生产环境、自动补全浏览器前缀等。文件结构组织根据项目需求可以设置Sass或Less文件的目录结构并使用适当的文件命名规范。导入顺序在主文件中使用import导入其他Sass或Less文件时要注意导入顺序确保样式按正确顺序加载。
需要注意的是具体的属性和配置细节可能会因为不同版本和工具的使用而有所变化。建议查阅相关文档或官方指南以获取最新和准确的信息。
Node
1、什么是Node.js它与前端开发有什么关系
答案: Node.js是一个基于Chrome V8引擎的JavaScript运行时环境可以让JavaScript代码在服务器端运行。与传统的前端开发相比Node.js使得开发者可以使用JavaScript开发后端应用程序。
2、Node.js的特点是什么它适用于哪些场景
答案: Node.js具有以下几个特点
非阻塞式I/ONode.js采用事件驱动、非阻塞的I/O模型能够处理大量并发请求。单线程Node.js使用单线程模型来处理请求避免了多线程之间的同步问题。轻量高效由于采用了事件驱动和单线程模型Node.js拥有较低的资源消耗和快速的响应能力。
Node.js适用于构建高并发、实时性要求高的网络应用如聊天室、实时通讯应用、推送服务、大规模数据处理等
3、如何创建一个基本的HTTP服务器并监听特定端口
答案和解析:
const http require(http);const server http.createServer((req, res) {res.statusCode 200;res.setHeader(Content-Type, text/plain);res.end(Hello, World!);
});const port 3000;
server.listen(port, () {console.log(Server running at http://localhost:${port}/);
});通过使用Node.js的内置模块http创建一个HTTP服务器并通过createServer方法传入回调函数来处理请求。在回调函数中设置响应状态码、头部信息和返回内容。最后使用listen方法指定监听的端口并在回调函数中输出服务器运行信息。
4、什么是模块Module如何在Node.js中使用模块
答案: 在Node.js中模块是可以重用的代码块用于组织和封装相关功能。每个文件就是一个模块模块里面定义的变量、函数或类默认只在当前模块内可见不同模块之间可以通过导入和导出的方式进行交互。
在Node.js中可以使用require函数来导入其他模块的导出部分并将其赋值给一个变量然后可以通过该变量访问被导入模块的内容。例如
// 导入名为myModule的模块
const myModule require(./myModule);// 使用myModule模块中导出的函数
myModule.myFunction();5、require()函数的作用是什么它如何加载和导出模块
答案: require()函数用于在Node.js中加载模块并返回模块的导出部分exports。它接收一个模块标识符作为参数可以是相对路径或模块名。
在加载模块时Node.js会根据传入的标识符解析模块的位置。如果是相对路径则根据当前模块的位置进行解析如果是模块名则按照一定的规则搜索模块并加载。
模块可以通过exports对象来导出其功能。在模块内部可以将需要导出的变量、函数或类赋值给exports对象的属性。例如
// myModule.js
exports.myFunction () {console.log(Hello from myModule!);
};其他模块在使用该模块时可以通过require()函数导入并访问其中导出的内容。
6、Node.js中常用的内置模块有
http用于创建基于HTTP协议的服务器和客户端。fs提供文件系统相关功能例如读取和写入文件。path用于处理文件路径的工具函数。os提供操作系统相关的信息和操作。events用于处理事件的模块。
7、处理异步操作以避免回调地狱的方式
使用Promise对象通过Promise对象可以更优雅地处理异步操作链式调用then()方法处理成功的结果使用catch()方法捕获错误。使用async/await结合async函数和await关键字可以以同步的方式编写异步代码避免回调嵌套。使用异步库或工具函数如Async.js、Bluebird等第三方库提供了强大的异步控制流程管理工具。
8、包管理器是管理和安装软件包包括库、框架和工具的工具。Node.js中常用的包管理器有
npmNode Package Manager是Node.js官方提供的默认包管理器。Yarn是Facebook开发的快速可靠的替代npm的包管理器。
9、读取和写入文件的代码示例
const fs require(fs);// 读取文件
fs.readFile(file.txt, utf8, (err, data) {if (err) throw err;console.log(data);
});// 写入文件
fs.writeFile(file.txt, Hello, world!, (err) {if (err) throw err;console.log(File written successfully.);
});10、如何处理表单提交数据请简述一下Express框架的用途和优势。
在Express框架中可以使用中间件如body-parser来解析请求体获取表单提交的数据。通过Express提供的路由功能可以根据不同的HTTP请求方法和URL路径编写对应的处理函数来处理表单提交的数据。处理函数可以访问请求对象req中的参数和数据并进行相应的处理逻辑最后返回响应给客户端。
Express框架是一个基于Node.js的快速、灵活的Web应用程序框架它简化了Web应用程序的开发过程。它的主要用途包括路由管理、中间件支持、静态文件服务、模板引擎等。相比原生的Node.js开发Express提供了更简洁、高效的API和丰富的插件生态系统使得开发Web应用变得更加容易。
Express框架的优势包括
简单易学Express提供了简洁而直观的API易于学习和上手。快速灵活Express具有良好的性能可灵活扩展和定制满足各种项目需求。中间件支持通过中间件机制可以方便地实现功能扩展、错误处理、身份验证等。大型社区Express拥有庞大的开发者社区和插件生态系统提供了丰富的解决方案和支持。
11、如何处理路由请求请介绍一下RESTful API的设计原则。
在Express框架中可以使用app.get()、app.post()等方法定义路由来处理不同的HTTP请求。根据请求的URL路径和请求方法配置对应的路由处理函数完成具体的业务逻辑。以RESTful API为例其设计原则包括
1、使用合适的HTTP方法
GET用于获取资源POST用于创建新资源PUT/PATCH用于更新资源DELETE用于删除资源
2、使用语义化的URL路径
使用名词表示资源避免使用动词使用URL路径传递参数避免使用查询字符串
3、返回合适的HTTP状态码
200 OK成功响应201 Created资源创建成功400 Bad Request请求参数错误404 Not Found资源不存在500 Internal Server Error服务器端错误
4、使用JSON格式进行数据交互
请求和响应的数据格式通常使用JSON来标准化表示
12、Node.js中的事件驱动编程是什么意思请给出一个具体的例子。
事件驱动编程是一种编程范式基于事件和事件处理函数之间的关系来组织代码逻辑。在Node.js中许多核心模块和第三方模块都使用事件驱动机制。例如Node.js中的EventEmitter是一个基于事件的类允许对象发出特定事件并监听相应的事件进行处理。
以下是一个示例
const EventEmitter require(events);class MyEmitter extends EventEmitter {}const myEmitter new MyEmitter();myEmitter.on(event, () {console.log(触发了event事件);
});myEmitter.emit(event);在这个例子中创建了一个自定义的MyEmitter类它继承了EventEmitter类并通过on方法监听了名为event的事件。当使用emit方法触发event事件时其中注册的事件处理函数会被调用打印出触发了event事件。
13、什么是中间件Middleware它在Node.js中的作用是什么
中间件是一种在请求和响应的处理过程中位于应用程序和服务器之间的函数或功能模块。在Node.js中Express框架广泛使用中间件机制来实现各种功能如路由处理、错误处理、身份验证等。
中间件在请求到达路由处理函数之前或响应发送给客户端之前对请求和响应进行预处理、转换或执行其他操作。它可以修改请求和响应对象、调用下一个中间件函数或终止请求-响应周期。
通过使用中间件可以实现以下功能
路由处理请求参数解析静态文件服务错误处理身份验证日志记录安全性处理等
14、要使用Node.js连接和操作数据库一般需要按照以下步骤进行 安装相关数据库驱动首先根据需要选择合适的数据库然后使用npmNode.js包管理工具安装相应的数据库驱动模块例如MongoDB可以使用npm install mongodb命令安装。 引入数据库驱动模块在代码中引入所需的数据库驱动模块例如MongoDB可以使用require(mongodb)进行引入。 建立数据库连接创建一个到数据库的连接提供数据库的连接配置信息例如服务器地址、端口号、认证凭证等。 执行数据库操作使用数据库驱动模块提供的API进行数据查询、插入、更新、删除等操作。
下面是一个简单的示例代码以连接MongoDB数据库为例
const MongoClient require(mongodb).MongoClient;// 连接配置
const url mongodb://localhost:27017;
const dbName mydb;// 创建连接实例
const client new MongoClient(url, { useUnifiedTopology: true });// 连接数据库
client.connect(function(err) {if (err) {console.error(Failed to connect to database, err);return;}console.log(Connected successfully to database);const db client.db(dbName);// 在此执行数据库操作// ...// 关闭连接client.close();
});15、关于WebSocket的解释和与HTTP通信的区别如下
WebSocket是一种在单个TCP连接上进行全双工通信的协议。它允许客户端和服务器之间进行实时数据传输而不需要在每个请求-响应周期中建立新的连接。
与HTTP相比WebSocket具有以下区别 连接方式HTTP是无状态的请求-响应协议在每个请求中都需要重新建立连接。而WebSocket在初始化阶段通过HTTP协议进行握手完成后建立了一条持久的连接可以在任意时刻双向传输数据。 传输效率由于WebSocket建立了持久连接避免了多次创建和关闭连接的开销因此在频繁通信和大量数据传输场景下更高效。 支持实时性WebSocket支持服务器主动推送数据给客户端使得实时性要求较高的应用如聊天应用、股票行情等能够及时更新数据而不需要客户端不断轮询或发送请求。
需要注意的是WebSocket协议作为一个独立的协议存在与HTTP协议并没有本质的关系但WebSocket的握手过程使用了HTTP协议来完成初始连接的建立。
16、前端常见的解决跨域的方式
前端常见的跨域处理方式包括以下几种 JSONPJSON with PaddingJSONP是一种利用script标签可以跨域加载资源的特性来实现跨域请求的方法。通过在页面中动态创建一个带有回调函数参数的script标签服务器返回包裹在回调函数中的数据从而实现跨域数据获取。但是JSONP只支持GET请求。 CORSCross-Origin Resource SharingCORS是一种在服务器上设置响应头以允许跨域请求的机制。服务器需要在响应中添加一些特定的HTTP头部如Access-Control-Allow-OriginAccess-Control-Allow-Methods等指示哪些源、方法或标头允许跨域请求。 代理服务器前端可以设置代理服务器来转发请求将跨域请求发送到同源的后端服务器上然后由后端服务器代为请求目标API并将结果返回给前端。这样前端通过访问自己的服务器实际上是同源请求避免了跨域问题。 WebSocketWebSocket协议本身支持跨域通信不受同源策略限制因此可以作为一种跨域请求的解决方案。通过建立WebSocket连接后客户端和服务器之间可以进行实时双向通信。 使用反向代理在部署环境中可以通过使用反向代理服务器将前端应用和后端API部署在同一个域名下实现同源访问。常用的反向代理工具有Nginx和Apache等。
17、如何通过 vuex 实现登录验证?
通过Vuex可以实现登录验证的功能。下面是一种使用Vuex进行登录验证的基本思路 创建Vuex Store首先在Vuex中创建一个store用于存储用户的登录状态和相关信息。 定义State在store中定义一个state包括用户是否已登录的布尔值例如isLoggedin和用户信息对象例如userInfo等。 定义Mutations在store中定义mutations用于修改state中的数据。添加一个mutation例如setLoggedin用于设置用户的登录状态另外还可以添加其它需要修改state的mutations。 定义Actions在store中定义actions用于异步操作和调用mutations修改state。添加一个action例如login用于处理用户登录的逻辑。在此action中可以发送登录请求到服务器进行验证并根据返回结果调用对应的mutation来修改用户的登录状态和用户信息。 在组件中使用在需要进行登录验证的组件中可以使用mapState将state中的数据映射到组件的计算属性或者直接使用this.$store.state来获取登录状态和用户信息。通过mapMutations将mutations映射到组件的方法中可以在组件中触发对应的mutation来修改state。
这只是一个基本的示例具体实现要根据项目需求而定。在Vuex中结合路由守卫和axios等工具可以更加灵活地实现登录验证功能保护需要登录才能访问的页面。
需要注意的是登录验证是一个涉及安全性的敏感操作要确保在处理登录验证时遵循安全规范例如对用户输入进行合法性校验、使用安全的传输方式等。
18、router 和route 区别
在Vue.js中router和route是两个相关但不同的概念。 Router路由器Router是Vue Router库提供的功能之一用于管理应用程序的路由。它是一个全局配置用于定义应用程序的路由规则并且允许用户在不同的URL路径下加载不同的组件。通常在Vue.js应用中我们会创建一个router实例配置各种路由规则然后将其与Vue实例关联起来。这样当用户在浏览器地址栏输入特定的URL或者通过页面跳转时Vue Router会根据路由规则来动态地渲染对应的组件。 Route路由Route表示当前活动的路由信息是在组件中使用的一个对象。当路由发生变化时Vue Router会将当前的路由信息注入到组件内部的 r o u t e 属 性 中 从 而 让 组 件 可 以 获 取 有 关 当 前 路 由 的 信 息 。 例 如 route属性中从而让组件可以获取有关当前路由的信息。例如 route属性中从而让组件可以获取有关当前路由的信息。例如route对象包含了当前的路径(path)、参数(params)、查询参数(query)、元信息(meta)以及一些导航相关的方法(push、replace、go等)。通过访问$route对象我们可以在组件内部根据路由信息进行条件渲染、处理页面逻辑、获取参数等操作。
总结来说router是整个Vue Router库用于管理应用程序的路由规则而route是指当前活动的路由信息对象包含了有关当前路由的详细信息可以在组件中使用。
19、created 生命周期不可以千什么事情
在Vue.js中created生命周期函数是在组件实例创建完成后立即调用的钩子函数。在created生命周期阶段组件实例已经完成了数据观测(data observer)和事件初始化(event initialization)但尚未挂载到DOM中。
在created生命周期函数中可以进行以下操作 初始化数据可以进行一些数据的初始化操作例如设置默认值、请求数据等。 进行异步操作可以使用异步请求数据例如发送网络请求或者从本地存储中读取数据。 订阅事件/监听器可以在created生命周期函数中订阅全局事件或者其他组件的自定义事件并定义对应的处理函数。 调用方法/执行逻辑可以调用组件内部的方法执行一些逻辑操作对数据进行处理等。 注册插件可以在created生命周期函数中注册Vue插件以扩展Vue功能。
需要注意的是在created生命周期函数中不能进行DOM相关的操作因为此时组件还没有被挂载到真实的DOM中。如果需要在DOM操作相关的代码则需要在mounted生命周期函数中执行。
总的来说created生命周期函数适合进行组件的初始化和数据准备的操作但不适合进行直接涉及DOM的操作。
20、数据拦截是发生在哪个生命周期的? 什么情况下不会对属性数据拦截要怎么解决?
在Vue.js中数据拦截data interception是发生在组件实例的创建过程中具体是在初始化组件实例时进行的而不是在某个具体的生命周期函数中。
Vue.js通过使用Observer来监听和拦截数据的变化。当Vue实例创建时会将组件中的data对象进行递归遍历并使用Object.defineProperty对每个属性进行拦截。这样当数据发生变化时Vue能够捕捉到并执行相应的更新操作。
然而也有一种情况下会导致属性数据不被拦截即当数据已经存在于组件实例data对象中但在创建组件实例之前就已经被定义或修改。这种情况可能发生在通过原型继承或动态添加属性时。
要解决这个问题可以采用以下两种方法 在组件实例创建之前确保所有的数据属性都已经在data对象中进行了初始化。这样属性就能够被正常拦截和观测。 对于需要后期添加或修改的属性可以使用Vue.set()或this.$set()方法进行手动拦截。这些方法可以在组件实例创建后动态地向响应式的对象中添加新的属性并确保其被拦截和观测。
总结来说数据拦截发生在组件实例的创建过程中但在组件实例创建之前已经定义或修改的属性可能不会被拦截。我们可以通过在data对象中初始化所有的属性或使用Vue.set()或this.$set()方法来解决这个问题。
21、css bfc
BFC是指块级格式化上下文Block Formatting Context它是CSS中的一种渲染机制。
BFC是一个独立的渲染区域其中的元素按照一定的规则进行布局和渲染。在BFC中每个元素都按照垂直方向依次放置两个相邻的块级元素之间的垂直边距会发生重叠。
触发BFC的条件包括
根元素HTML设置了浮动float属性不为none设置了绝对定位position为absolute或fixeddisplay的值为inline-block、flex或gridoverflow的值不为visible
BFC具有以下特性
内部的元素在垂直方向上排列从顶部开始一直到底部。阻止垂直外边距的重叠。BFC区域不会与浮动元素重叠。清除浮动使得父元素可以正确地包裹浮动的子元素。计算容器的高度时会包含浮动元素。
BFC在实际的页面布局中有多种应用例如
清除浮动当父元素包含浮动的子元素时可以将父元素触发BFC以清除浮动后造成的布局问题。防止外边距折叠当相邻的块级元素垂直外边距重叠时可以触发BFC使它们不再重叠。实现多栏布局通过将容器触发BFC可以让其中的内容按照列布局进行排列。
通过使用BFC我们可以更好地控制元素在布局中的行为和相互关系提供更灵活和可靠的页面布局解决方案。
22、什么是圣杯布局
圣杯布局Holy Grail Layout是一种用于创建三栏等高布局的技术。它由Matthew Levine提出并在CSS中被广泛应用。
在圣杯布局中有一个主要内容区域以及两个侧边栏。这三栏的高度都应当相等并且主要内容区域应当排在第一位然后是左侧边栏最后是右侧边栏。这样可以实现侧边栏优先加载、语义化结构和SEO友好的页面布局效果。
实现圣杯布局的关键是使用浮动和负边距来进行定位。具体步骤如下
将三个栏目用 等容器包装起来并设置宽度为100%。 通过浮动将左侧和右侧的侧边栏移动到左右两侧。为主要内容区域添加左右内边距以避免被侧边栏覆盖。使用负边距将左侧侧边栏移动到主要内容的前面并设置足够大的宽度使其占据正常位置。使用负边距将右侧侧边栏移动到主要内容的后面并设置足够大的宽度使其占据正常位置。使用相对定位将三个栏目包装容器还原到正常位置避免宽度受到影响。
通过以上步骤就可以实现三栏等高布局的圣杯布局效果。同时可以使用盒模型的box-sizing属性以及适当的CSS选择器来进一步完善和定制布局。
需要注意的是在实际应用中为了兼容不同浏览器和设备可能需要进行额外的调整和兼容性处理例如使用CSS Flexbox或Grid布局等。
23、什么是双飞翼布局
双飞翼布局Double-Flight Layout是一种用于创建三栏等高布局的技术与圣杯布局类似。它由淘宝Tangram团队提出并通过使用CSS浮动和负边距来实现。
在双飞翼布局中同样有一个主要内容区域和两个侧边栏。这三栏的高度都应当相等并且主要内容区域优先加载、语义化结构和SEO友好。
与圣杯布局不同的是双飞翼布局采用了更为简洁的HTML结构。具体步骤如下
将三个栏目用 等容器包装起来并设置宽度为100%。 对主要内容区域设置左右内边距以防止被侧边栏覆盖。使用浮动将主要内容区域置于最前面。同时通过设置左右外边距为对应侧边栏的宽度使其腾出位置给侧边栏。在主要内容区域内创建一个子容器在其中放置实际的内容。这个子容器可以设置左右内边距避免内容贴近浏览器窗口边界。通过负边距将两个侧边栏移动到主要内容区域的两侧实现三栏并排布局。对侧边栏进行浮动并设置相应的宽度。通过使用相对定位使得三个栏目容器回归正常位置。
通过以上步骤就可以实现双飞翼布局。同样为了兼容不同浏览器和设备可能需要进行额外的调整和兼容性处理例如使用CSS Flexbox或Grid布局等。
24、http 缓存
HTTP缓存是一种用于优化Web性能的机制它通过在客户端和服务器之间存储和重复使用资源来减少网络请求和响应的数量从而提高页面加载速度并减少带宽消耗。
HTTP缓存可以分为两种类型强缓存和协商缓存。
强缓存是指浏览器在第一次请求资源时将资源的响应标记为可缓存并在一定时间内直接从本地缓存中获取该资源而无需再次发送请求到服务器。常用的强缓存策略有两个HTTP头字段
Expires指定资源的过期时间是一个具体的时间点。Cache-Control指定资源的缓存策略包括max-age相对时间和s-maxage对共享缓存有效等参数。
如果资源命中了强缓存浏览器就会直接从缓存中读取并使用该资源不再发送请求到服务器。
协商缓存是指浏览器在第一次请求资源时向服务器发送请求并携带缓存标识如Last-Modified、ETag服务器根据这些标识判断资源是否已经发生变化。如果资源未发生变化则服务器返回304 Not Modified状态码告知浏览器直接使用缓存的资源。如果资源发生变化服务器返回新的资源和200 OK状态码。
常用的协商缓存策略有两个HTTP头字段
Last-Modified / If-Modified-Since响应中包含资源的最后修改时间请求中可以携带这个值服务器根据它来判断资源是否已经变化。ETag / If-None-Match响应中包含资源的唯一标识符类似于无法被修改的版本号请求中可以携带这个值服务器根据它来判断资源是否已经变化。
如果资源命中了协商缓存服务器返回304 Not Modified状态码浏览器就会从
除了HTTP缓存还有其他几种常见的缓存技术。以下是其中一些 数据库缓存将数据库查询结果或计算结果存储在缓存中以提高数据检索和处理的速度。常用的数据库缓存工具包括Redis和Memcached。 CDN缓存内容分发网络CDN通过将静态资源如图片、CSS和JavaScript文件缓存在全球各地的边缘节点上以减少远程请求并加快内容传输速度。 应用程序级缓存应用程序级缓存是指缓存应用程序运行时产生的数据或计算结果避免频繁的数据访问或计算操作。可以使用内存缓存如Redis、Memcached或分布式缓存如Hazelcast来实现。 操作系统缓存操作系统会将最近使用的文件、页面和执行代码等存储在内存中以加快访问速度。操作系统缓存能够有效降低磁盘IO的压力并提高系统的响应性能。
这些缓存技术可以根据需求和场景进行选择和组合使用以提高系统的性能和用户体验。
浏览器有以下几种缓存方式
强缓存通过设置HTTP响应头中的Expires或Cache-Control字段来控制缓存的有效期。当浏览器发送请求时会先检查缓存是否过期如果没有过期则直接使用缓存中的数据不会发送请求到服务器。协商缓存通过设置HTTP响应头中的Last-Modified和ETag字段来标识资源的版本信息。当浏览器发送请求时会携带If-Modified-Since和If-None-Match字段服务器根据这些字段来判断资源是否发生变化。如果资源没有变化则返回304 Not Modified浏览器直接使用缓存中的数据。Service Worker缓存Service Worker是一种在浏览器后台运行的脚本可以拦截网络请求并自定义缓存策略。开发者可以通过编写Service Worker脚本来实现离线缓存、推送通知等功能。内存缓存浏览器会将一部分资源存储在内存中以提高访问速度。内存缓存的容量较小一般只能存储一些常用的资源。硬盘缓存浏览器会将一部分资源存储在硬盘中以便在下次访问时直接读取。硬盘缓存的容量较大可以存储较多的资源。
每种浏览器的缓存大小限制不同具体大小取决于浏览器的版本和配置。一般来说内存缓存的容量较小通常在几十MB到几百MB之间。而硬盘缓存的容量较大可以达到几个GB甚至更大。开发者可以通过浏
25、http 1.0 和 2.0 的区别
HTTP/1.0和HTTP/2.0是两个不同版本的超文本传输协议HTTP。它们之间有以下主要区别 连接复用在HTTP/1.0中每个请求都需要建立一个新的TCP连接完成后立即关闭。而在HTTP/2.0中引入了多路复用技术可以在单个TCP连接上同时进行多个请求和响应减少了连接建立和关闭的开销并提高了性能。 头部压缩HTTP/2.0使用了HPACK算法对头部信息进行压缩减少了传输的数据量。相比之下HTTP/1.0的头部信息没有进行压缩导致每个请求的头部数据较大。 服务器推送HTTP/2.0引入了服务器推送机制服务器可以在客户端请求之前主动将一些与请求相关的资源推送给客户端从而减少了额外的请求数量提高了页面加载速度。 优化的二进制传输HTTP/2.0将数据传输格式从文本HTTP/1.0改为二进制格式通过分帧、流和优先级等机制提供了更高效的数据传输方式减少了延迟并增加了吞吐量。 错误处理在HTTP/2.0中错误信息以帧的形式直接传输而在HTTP/1.0中需要通过完整的响应来传递错误信息。
综上所述HTTP/2.0相较于HTTP/1.0在性能和效率方面有显著改进并提供了更多的功能和特性促进了更快速、更高效的网络通信。
26、介绍下 bfsdfs
BFS广度优先搜索和DFS深度优先搜索是两种常见的图遍历算法用于在图或树中搜索特定节点或遍历所有节点。
1、广度优先搜索BFS
从起始节点开始逐层地向外遍历先访问离起始节点最近的节点。使用队列数据结构对节点进行存储和扩展按照队列的FIFO原则。每次从队列中取出一个节点并将其未访问过的相邻节点加入队列。重复执行上述步骤直到遍历完整个图或找到目标节点。
2、深度优先搜索DFS
从起始节点开始沿着一条路径尽可能深入地访问节点直到不能继续为止然后回溯到上一个节点继续探索其他路径。使用栈数据结构对节点进行存储和回溯按照栈的LIFO原则。以递归或显式栈来实现DFS算法。对每个未访问过的相邻节点递归或压入栈中继续深度搜索直到找到目标节点或遍历完整个图。
选择使用BFS还是DFS取决于具体的应用场景和问题要求
BFS适合用于寻找最短路径或离起点最近的节点如迷宫问题、单词变换等。DFS适合用于遍历或搜索所有可能的路径如生成括号序列、图的连通性判断等。
需要注意的是BFS和DFS算法都需要标记已访问的节点以防止无限循环或重复访问。
27、输入网址发生了什么
当你输入一个网址并按下回车时发生了以下过程 URL解析浏览器会解析输入的网址URL包括协议如HTTP、HTTPS、域名如www.example.com和路径如/page等信息。 DNS解析浏览器将域名转换为对应的IP地址这个过程称为DNS解析。首先浏览器查找本地DNS缓存如果找到匹配的IP地址则直接使用该IP地址。如果没有找到浏览器将发送请求给DNS服务器以获取域名对应的IP地址。 建立TCP连接浏览器与服务器之间需要建立一个TCP连接。它使用三次握手过程来确保双方的连接可靠。握手完成后客户端浏览器和服务器之间建立起了一个TCP连接。 发送HTTP请求浏览器构建一个HTTP请求报文包含请求方法如GET、POST、请求头部、请求体等信息。然后浏览器将请求报文发送给服务器。 服务器处理请求服务器收到HTTP请求后根据请求的内容进行相应的处理。可能的处理操作包括查询数据库、读取文件、处理业务逻辑等。 服务器发送HTTP响应服务器生成一个HTTP响应报文包含响应状态码、响应头部、响应体等信息。然后服务器将响应报文发送回客户端浏览器。 浏览器渲染浏览器接收到HTTP响应后根据响应的内容进行解析和渲染。它会解析HTML、CSS和JavaScript等资源并将其呈现在屏幕上以展示网页内容给用户。 断开TCP连接在数据传输完成后浏览器和服务器之间的TCP连接会被断开释放网络资源。
以上是一个简单的描述实际情况可能还涉及到缓存机制、重定向、身份验证等其他步骤具体的过程可能会因使用的协议、服务器配置和网页内容等因素而有所不同。
28、websocket 主题
WebSocket是一种网络通信协议它允许在客户端和服务器之间建立双向通信的连接。它的主要特点是实时性和低延迟。使用WebSocket可以实现实时数据传输、推送通知、即时聊天等功能。
至于主题在WebSocket中没有直接称为主题的概念。但是可以通过在消息中添加特定的标识符或自定义数据字段来实现类似主题的功能。这样不同的消息可以根据标识符或数据字段进行区分和处理以实现不同主题或频道的消息传递。
29、前端怎么做公共模块的抽离
在前端开发中抽离公共模块是一种常见的优化方式可以提高代码的可维护性和复用性。以下是几种常用的方法来进行公共模块的抽离 函数封装将一段常用的功能逻辑封装成函数并以参数形式暴露出来在不同的地方进行调用。这样可以避免代码重复提高代码的复用性。 组件抽离将一部分功能相似或通用的代码抽离成组件通过引入和使用组件来实现公共模块的复用。例如可以将页面中多处使用的头部、导航栏等抽离成组件。 工具类/插件封装将一些常用的工具函数或第三方插件封装成库供整个项目或多个项目共享使用。这样可以提高代码的复用性并且便于在不同的项目中统一管理和更新。 模块化管理使用模块化开发的方式将公共的逻辑和功能封装成独立的模块通过导入和使用模块来实现代码复用。可以使用像ES Modules、CommonJS等模块化规范或者使用打包工具如Webpack、Rollup进行模块的打包和管理。 配置文件提取将一些通用的配置项如API接口地址、样式主题等提取出来放在单独的配置文件中进行管理。不同的模块可以共享和引用这些配置项方便统一管理和修改。
以上是几种常见的方法来进行公共模块的抽离根据具体需求和项目情况选择适合的方式进行抽离以提高代码的可维护性和复用性。
30、组件封装的时候如果在用到别人封装的组件库里面还想再用到他封装之前的方法或者插槽应该怎么用
当你在使用别人封装的组件库时想要在其中的组件中使用该库之前的方法或插槽可以通过以下几种方式来实现
组件扩展有些组件库提供了组件扩展的能力允许你在其基础上进行自定义和修改。你可以根据组件库的文档查找相关的扩展方式在扩展的过程中保留原有的方法或插槽。插槽/作用域插槽如果组件库支持插槽功能你可以在使用组件时通过插槽的方式插入额外的内容包括之前的方法或插槽。具体的插槽用法可以参考组件库的文档了解如何使用插槽并嵌套之前的方法或插槽。组件组合在某些情况下你可以创建一个新的组件利用组合的方式将别人封装的组件与之前的方法或插槽进行组合。在新组件中你可以引入别人封装的组件并在组件中使用之前的方法或插槽。
需要注意的是具体的使用方式取决于所使用的组件库的实现和文档说明。建议查阅组件库的官方文档或示例以了解如何在其组件中使用之前的方法或插槽。
31、自己在mui基础上封装组件怎么让别人在使用自己封装的组件的时候还能调用mui原生的方法和api
在封装基于 MUI 的组件时你可以通过以下方式让别人能够在使用你的封装组件时仍然调用 MUI 原生的方法和 API
透传属性Prop Forwarding将 MUI 组件所支持的属性透传到你封装的组件上。这样在使用你封装的组件时可以直接将 MUI 组件所支持的属性传递给你的组件从而实现对 MUI 原生方法和 API 的调用。插槽Slots如果 MUI 组件支持插槽功能你可以在封装的组件中提供相应的插槽以便用户可以在使用你的组件时放置 MUI 原生组件或其他内容并通过插槽来实现与原生方法和 API 的交互。扩展Extend某些情况下你可能需要通过继承 MUI 组件来扩展你封装的组件以保留并增加 MUI 原生的方法和 API。通过继承 MUI 组件你可以在你的组件中使用 MUI 组件的方法和 API并添加自定义的功能和样式。
总之要让别人能够调用 MUI 原生的方法和 API在封装的组件中要考虑如何透传属性、使用插槽和扩展 MUI 组件的功能。请仔细阅读 MUI 的文档了解组件的属性、插槽和扩展机制并根据需要进行相应的封装。
32、跨一层级的插槽怎么使用
跨一层级的插槽可以通过作用域插槽Scoped Slots来实现。作用域插槽允许你将父组件中的内容传递到子组件并在子组件中进行处理和展示。
以下是使用跨一层级插槽的一般步骤 在父组件中定义插槽 templatechild-componenttemplate v-slot:slotNameslotProps!-- 在这里编写要传递给子组件的内容和插槽相关代码 --/template/child-component
/template在子组件中接收并使用插槽内容 templatediv!-- 在子组件中使用插槽内容和处理逻辑 --slot nameslotName :slotPropspropsToSendToSlot/slot/div
/templatescript
export default {data() {return {propsToSendToSlot: {} // 可选传递给插槽的属性/数据对象};}
};
/script在上述示例中父组件中的template v-slot:slotName部分定义了跨一层级的插槽并命名为slotName。子组件中的slot标签中也使用了相同的插槽名slotName并通过:slotProps属性传递父组件中的属性或数据对象propsToSendToSlot。
请根据实际情况将代码中的slotName替换为你想要的插槽名称并根据需要传递父组件的属性或数据对象。这样父组件中的内容将会传递到子组件并在子组件中进行展示和处理。
需要注意的是作用域插槽的具体使用方式可能会因框架或组件库而异以上示例是基于 Vue.js 的语法。如果你在使用其他框架或库请参考相应的文档来了解如何使用跨一层级的插槽。
33、长列表怎么做性能优化
对于长列表的性能优化可以考虑以下几个方面 虚拟滚动使用虚拟滚动技术来渲染可见区域的内容而不是将整个列表一次性全部渲染出来。这可以减少 DOM 元素的数量提升页面的加载速度和渲染性能。常见的虚拟滚动实现方式包括使用组件库提供的虚拟滚动组件或手动计算并设置列表项的位置。 延迟加载如果列表项中包含大量内容如图片、视频等可以考虑延迟加载非可见区域的内容。通过监听滚动事件当列表项进入可见区域时再开始加载相关资源避免一次性加载过多数据而影响性能。 重用 DOM 元素在滚动过程中不断创建和销毁大量的 DOM 元素会导致性能下降。这时可以通过重用已存在的 DOM 元素来减少创建和销毁的操作。例如使用对象池或缓存列表项的 DOM 元素在需要显示新的列表项时从对象池或缓存中取出一个元素并更新其内容。 分批加载将长列表分成多个较小的批次进行加载和渲染可以减轻主线程负担提升用户体验。例如在滚动过程中先加载和渲染可见区域的列表项然后在空闲时间继续加载下一个批次的列表项。 使用合适的事件处理机制对于涉及用户交互的列表选择合适的事件处理机制也是性能优化的一部分。如对于滚动事件可以使用debounce或throttle函数来限制事件触发频率避免过多的重绘操作。 优化渲染性能在渲染列表项时尽量减少不必要的嵌套和计算避免过多的布局调整和样式计算。可以利用 CSS 技术进行样式的批量更新或动画性能的优化。
34、怎么优化首屏的加载速度
要优化首屏的加载速度可以考虑以下几个方面
压缩和优化资源对静态资源如CSS、JavaScript和图片进行压缩和优化以减少文件大小并提高加载速度。使用合适的压缩工具和技术例如Gzip压缩、图像优化工具等。延迟加载推迟非必要的资源加载只加载首屏可见内容所需的最小必需资源。这可以通过延迟加载图片、懒加载Lazy Loading技术以及按需加载组件或模块来实现。异步加载将页面中的JavaScript代码进行异步加载避免阻塞页面渲染。可以使用async或defer属性来标记脚本标签或者通过动态创建和插入脚本元素来实现异步加载。缓存机制有效利用浏览器缓存使重复访问的资源可以从缓存中加载而不是重新请求服务器。合理设置缓存头如过期时间、命中策略等确保静态资源可以被长时间缓存。代码优化优化JavaScript和CSS代码去除冗余、无用的代码并采用更高效的算法和数据结构。避免在首屏加载时执行复杂或耗时的操作尽量减少JavaScript执行时间。DNS预解析在HTML文档中添加DNS预解析标签让浏览器在加载页面时提前解析域名减少DNS查询时间加快资源获取速度。懒加载字体延迟加载自定义字体文件使其在内容需要显示时才进行加载避免阻塞其他资源的加载。使用CDN加速使用内容分发网络CDN来加速资源的加载将静态文件分发至全球各个节点使用户可以从最近的节点加载资源减少响应时间。优化服务器和网络环境确保服务器端的响应时间尽可能短并采用高速稳定的网络连接减少数据传输延迟。响应式设计针对不同设备和屏幕尺寸优化页面布局和内容展示减少无意义的加载和渲染。
通过综合考虑以上因素并进行相应的优化调整可以显著提高首屏的加载速度提升用户体验。
35、小程序 怎么做响应式布局
在小程序中实现响应式布局可以借助以下方法 百分比布局使用百分比来设置组件的宽度和高度使其相对于父元素或屏幕宽度进行自适应调整。例如将一个容器的宽度设置为100%即可使其自适应填充父容器的宽度。 弹性盒子布局Flexbox使用flex布局模型可以轻松实现弹性盒子布局它可以自动根据可用空间分配子元素的大小和位置。通过设置容器的display属性为flex并结合各种flex属性如flex-direction、justify-content和align-items等可以创建具有响应能力的布局。 媒体查询Media Queries使用媒体查询可以根据设备的屏幕尺寸、像素密度或方向等条件来应用不同的样式。你可以在样式表中使用media规则并根据需要定义不同的CSS样式从而针对不同的屏幕尺寸提供自定义的布局和样式。 尺寸单位选择在设置长度、宽度和字体大小等属性时使用相对单位如百分比、rpx而非固定像素值。这样可以确保布局在不同设备上按比例缩放适应屏幕大小的变化。 利用小程序框架提供的响应式布局支持一些小程序框架如uni-app、Taro等提供了内置的响应式布局支持可以根据不同设备自动调整页面布局。你可以参考相应框架的文档学习如何使用其提供的响应式布局特性。
以上方法可以帮助你实现小程序的响应式布局根据不同设备和屏幕尺寸提供更好的用户体验。根据具体需求选择合适的方法并在开发过程中进行实践和调试。
36、1rpx
1rpxrpx responsive pixel是小程序中的一个相对长度单位它用于实现在不同设备像素密度下的自适应布局。在小程序中屏幕宽度被分为750rpx即1rpx等于屏幕宽度的1/750。
使用1rpx作为单位可以使布局在不同设备上按比例缩放以适应不同像素密度的屏幕。例如如果要将一个容器的宽度设置为屏幕宽度的一半可以将宽度设置为375rpx750rpx / 2则在屏幕宽度为750px的设备上该容器的宽度将为375px在屏幕宽度为375px的设备上该容器的宽度也将为375px。
在编写样式时可以直接使用rpx作为单位进行设置例如
.container {width: 300rpx;height: 200rpx;font-size: 24rpx;
}需要注意的是在使用rpx单位时小程序开发工具会根据实际屏幕像素密度进行转换并生成相应的样式表。因此开发者无需手动处理不同设备像素密度的差异只需关注基于rpx单位编写的样式即可。
37、vue怎么做权限管理
在Vue中进行权限管理的一种常见方法是通过路由守卫Route Guards来控制页面的访问权限。下面是一个简单的示例 创建路由配置文件例如router.js定义需要进行权限控制的路由和对应的权限要求
import Vue from vue
import Router from vue-router
import Home from ./views/Home.vue
import Admin from ./views/Admin.vueVue.use(Router)const router new Router({routes: [{path: /,name: home,component: Home},{path: /admin,name: admin,meta: { requiresAuth: true }, // 添加requiresAuth字段表示需要登录后才能访问component: Admin}]
})export default router在主组件中编写路由守卫来进行权限判断例如在App.vue中
templatediv idapprouter-view/router-view/div
/templatescript
export default {name: App,mounted() {this.checkAuth()},methods: {checkAuth() {this.$router.beforeEach((to, from, next) {if (to.meta.requiresAuth !this.isAuthenticated()) {// 判断to路由是否需要登录访问并检查用户是否已认证next(/) // 如果未认证重定向到登录页面或其他合适的处理} else {next() // 已认证放行路由}})},isAuthenticated() {// 判断用户是否已认证的逻辑可以根据实际情况进行修改// 例如从本地存储或后台接口获取认证状态return localStorage.getItem(isAuthenticated) true}}
}
/script在上述代码中通过to.meta.requiresAuth判断访问的路由是否需要登录权限如果需要但用户未认证则重定向到登录页面或其他处理。isAuthenticated方法用于判断用户是否已经认证你可以根据实际需求来实现该方法。
当用户试图访问需要权限的页面时Vue Router会调用路由守卫在守卫中进行权限验证和控制访问。
以上是一个简单的权限管理示例实际的权限管理方案可能更加复杂包括不同的角色、权限级别等。你可以根据自己的需求扩展和修改上述代码以实现更全面的权限管理功能。
38、小程序怎么做权限管理
在小程序中进行权限管理可以通过以下几个步骤实现 定义权限列表首先你需要确定你的小程序有哪些功能或页面需要进行权限管理。将这些功能或页面列出并分配相应的权限级别。 登录认证为了进行权限管理用户通常需要登录认证。你可以使用微信提供的登录接口wx.login获取用户的唯一标识如OpenID或UnionID并将其与你的后台系统关联起来进行认证。 前端权限控制在小程序前端你可以通过条件判断和界面控制来限制和授权用户访问特定的功能或页面。 条件判断在关键性业务逻辑处比如点击某个按钮触发某个操作之前判断用户的权限等级、是否已登录等条件。界面控制根据用户的权限等级决定是否显示或禁用某些功能按钮、导航菜单等。 后端权限校验为了保证数据的安全性建议在与后端交互时进行进一步的权限校验。后端可以根据登录认证获取到的用户标识对请求的接口进行权限验证确保用户只能访问其具备权限的数据和功能。 用户角色管理如果你的小程序有多个角色如管理员、普通用户等可以根据不同的角色分配不同的权限级别。这样可更细粒度地控制每个角色的操作权限。
除了上述步骤你还可以采用以下技术手段来增强小程序的权限管理
数据加密对敏感数据进行加密处理确保数据传输和存储的安全性。接口鉴权在后端接口设计中引入接口鉴权机制通过令牌、签名等方式验证请求的合法性。数据访问控制通过数据库的读写权限控制限制用户对数据的访问和修改。
需要注意的是权限管理涉及到用户隐私和数据安全应该进行谨慎设计和合理使用避免出现安全漏洞。同时建议按照最佳实践和相关安全规范进行开发和部署保护用户和系统的安全。
39、小程序从A页面到B页面再到C页面 怎么在不刷新页面的情况下返回A页面
在小程序中实现页面的跳转和返回可以使用小程序提供的导航组件和API来完成。如果你希望在不刷新页面的情况下返回A页面可以尝试以下方法 使用wx.navigateTo进行页面跳转在从A页面跳转到B页面时使用wx.navigateTo API进行页面跳转。这个方法会将当前页面加入导航栈中并打开新的页面。 使用wx.navigateBack进行页面返回在从C页面返回A页面时你可以使用wx.navigateBack API进行页面返回。这个方法会关闭当前页面并返回到上一个页面。 通过设置delta参数为大于1的值例如wx.navigateBack({ delta: 2 })可以一次性返回多个页面。具体的值取决于你想要返回的层级数。如果你不知道当前页面的层级数可以使用wx.getCurrentPages API获取当前页面栈并根据需要计算delta的值。 避免使用wx.reLaunch和wx.redirectTowx.reLaunch和wx.redirectTo这两个API会替换当前页面或直接关闭当前页面从而刷新整个页面栈。如果你希望保持页面栈的状态并实现无刷新返回应避免使用这两个API。
综上所述通过合理地使用wx.navigateTo和wx.navigateBack你可以在小程序中实现在不刷新页面的情况下返回A页面。
40、h5页面缓存怎么实现
在H5页面中实现缓存可以通过以下几种方式实现 使用浏览器缓存H5页面可以利用浏览器的缓存机制来进行页面缓存。当用户首次访问页面时页面资源如HTML、CSS、JavaScript和图片等会被浏览器缓存下来。之后再次访问同一页面时浏览器会直接从缓存中加载资源提高页面加载速度。 页面缓存由浏览器自动处理不需要开发者额外操作。可以通过设置响应头的Cache-Control和Expires字段来控制缓存的策略如设置为公共缓存、私有缓存、强制缓存等。 使用LocalStorage或SessionStorageH5提供了LocalStorage和SessionStorage两个Web Storage对象可以在本地存储数据。这些存储是永久性LocalStorage或会话级别SessionStorage的并在页面刷新或关闭后仍然保持。 可以使用localStorage.setItem(key, value)方法将数据存储到LocalStorage中使用localStorage.getItem(key)方法获取数据。SessionStorage的使用方式与LocalStorage类似只是其数据是在单个会话中保存当浏览器窗口关闭后会自动清除。H5页面可以在加载时先检查LocalStorage或SessionStorage中是否存在缓存数据如果存在则优先使用缓存数据否则再请求服务器资源。 使用Service WorkerService Worker 是一种在浏览器后台运行的脚本可以拦截和处理网络请求。它可以用于实现高级缓存策略包括离线访问和资源缓存。 Service Worker 可以通过捕获网络请求并将其缓存到本地当用户再次访问相同资源时直接从本地缓存中加载而无需再次向服务器发起请求。Service Worker 的使用需要在页面注册和配置并编写相应的脚本逻辑。
需要注意的是在进行页面缓存时要根据实际需
41、h5页面从A页面到B页面再到C页面 怎么在不刷新页面的情况下返回A页面
在H5页面中你可以使用浏览器的历史记录History API来实现在不刷新页面的情况下返回A页面的功能。以下是一种实现方式 从A页面跳转到B页面你可以使用window.location.href或者window.location.replace方法进行页面跳转至B页面。 从B页面跳转到C页面同样使用window.location.href或者window.location.replace方法将页面跳转至C页面。 在C页面返回A页面通过调用window.history.go(-2)方法可以返回到A页面。其中-2表示要返回的历史记录步数。 如果需要返回到A页面的前一个页面B页面可以使用window.history.go(-1)。如果不知道具体的历史记录步数也可以通过window.history.length获取当前历史记录的长度并根据需要计算返回的步数。
这样操作后页面会回退到A页面而不会刷新整个页面。请注意这种方式只适用于单页应用SPA或者页面之间没有表单数据或重要状态需要保留的情况。如果需要在页面间传递数据或者保存状态建议使用其他方法例如URL参数、本地存储、全局状态管理等。
知识点 在计算机编程领域中history模式和hash模式通常用于描述前端路由的不同实现方式。1. History模式- 优点使用 history 模式可以去除 URL 中的哈希符号#使 URL 更加清晰可读并且更接近传统的网站 URL 结构。- 缺点使用 history 模式需要服务器配置来支持。如果用户直接访问 history 模式下的 URL会收到 404 错误因此需要在服务器上进行一些特殊的配置。- 用法可以通过调用浏览器的 pushState 方法或使用前端框架如Vue Router、React Router等来启用 history 模式。2. Hash模式- 优点使用 hash 模式无需对服务器进行特殊配置可以直接在普通的静态服务器上部署。同时hash 模式兼容性较好在较旧的浏览器中也能正常工作。- 缺点URL 中使用了特殊字符 #可能会引起搜索引擎的误判且对用户而言URL 可能不够直观易懂。- 用法通过修改 URL 的 hash 片段来实现页面切换通常是监听浏览器的 hashchange 事件再根据新的 hash 值来处理相应的逻辑。选择使用哪种模式取决于具体需求和场景。如果需要兼容性好且无需额外服务器配置则可以选择 hash 模式。而如果希望 URL 更加直观可读并愿意进行服务器配置来支持那么可以选择 history 模式。要在不刷新页面的情况下从 C 页面返回 A 页面可以使用 history 模式来实现。 History 模式允许通过修改 URL 的路径来切换页面同时不会触发页面的完全刷新。具体步骤如下
1. 在 A 页面中设置路由链接或按钮点击时导航到 B 页面。
2. 在 B 页面中同样设置路由链接或按钮点击时导航到 C 页面。
3. 在 C 页面中设置路由链接或按钮点击时执行前端代码调用浏览器的 history.back() 方法即可返回到 A 页面而无需刷新页面。需要注意的是使用 history 模式进行页面切换时需要确保服务器已配置了相关路由规则以确保直接访问特定路径时能正确加载对应的页面。
42、比较两个对象是否相同
要比较两个对象是否相同可以使用以下方法 浅比较使用运算符进行比较。它会直接比较两个对象的引用是否指向同一个内存地址。如果两个对象引用地址相同则认为它们是相同的。 const obj1 { name: Alice, age: 25 };
const obj2 { name: Alice, age: 25 };console.log(obj1 obj2); // false在上面的例子中尽管obj1和obj2的属性值相同但它们分别位于不同的内存地址因此返回的结果是false。 深比较如果想要比较两个对象的属性值是否相同需要逐个比较每个属性的值。这可以通过递归遍历对象的属性来实现或者使用一些库/函数来辅助实现例如lodash的isEqual()函数。 const obj1 { name: Alice, age: 25 };
const obj2 { name: Alice, age: 25 };console.log(_.isEqual(obj1, obj2)); // true在上面的示例中_.isEqual()函数会深度比较obj1和obj2的属性值并返回true。
请注意在深比较时要考虑到对象的嵌套层级和属性值的数据类型等因素。如果对象内部存在循环引用或复杂的嵌套结构比较过程可能会更加复杂。
43、vueX 跟普通的 window注册的全局变量有什么区别
VueX 是 Vue.js 框架中用于应用程序状态管理的官方库而普通的全局变量是通过将数据绑定到 window 对象上实现的。
以下是 VueX 和普通全局变量之间的一些区别 集中式状态管理VueX 提供了一个集中式的状态管理机制。它通过创建一个单一的全局状态存储对象Store来管理应用程序的状态。这个存储对象允许在组件之间共享和响应数据并提供了一些特定的方法来修改状态、响应状态变化以及进行高级状态管理例如模块化划分等。相比之下普通的全局变量需要手动管理和同步状态的更改。 响应式更新VueX 的状态存储对象是响应式的当状态发生变化时相关的组件会自动更新。这使得在组件中直接使用存储对象中的状态更加方便不需要手动监听和管理变化。然而对于普通的全局变量需要手动设置并监听变量的变化通知组件进行更新。 严格的状态变更规则VueX 强制遵循严格的状态变更规则即只能通过提交 mutation 来修改状态从而方便跟踪状态变化的来源。这样可以确保状态的可维护性和追踪性以及防止不恰当的状态修改。而普通的全局变量没有这种限制可以在任何地方直接修改。 插件扩展VueX 提供了一些插件机制可以在存储对象上执行额外的操作例如持久化、调试工具等。这些插件可以轻松地与 VueX 集成并为应用程序提供更多功能。对于普通的全局变量这些功能需要手动实现或使用第三方库。
总的来说VueX 是一个专门用于状态管理的库它通过集中式的状态存储对象和响应式更新机制提供了更便捷、可维护和可扩展的方式来管理应用程序的状态。相比之下普通的全局变量需要手动处理状态的管理和更新缺乏强制规则和插件扩展等特性。
44、不考虑写法差异 vueX 比一般设置全局变量的方式好在那里
VueX 比一般设置全局变量的方式好在以下几个方面 集中管理VueX 提供了一个单一的全局状态存储对象Store将应用程序的状态集中管理。这使得状态的修改和获取更加方便组件之间可以轻松地共享状态而无需手动处理全局变量的传递和同步。 响应式更新VueX 的状态存储对象是响应式的当状态发生变化时相关的组件会自动更新。这意味着组件可以直接订阅并使用状态无需手动监听变量的变化或触发更新操作。这样可以减少开发者的工作量并提高代码的简洁性和可读性。 严格的状态变更规则VueX 强制遵循严格的状态变更规则即只能通过提交 mutation 来修改状态。这样可以确保状态的可维护性和追踪性以及防止不恰当的状态修改。相比之下使用普通的全局变量可能会导致状态的随意修改和难以跟踪增加代码的复杂性和维护成本。 插件扩展VueX 提供了插件机制允许在存储对象上执行额外的操作例如持久化、调试工具等。这些插件可以轻松地与 VueX 集成并为应用程序提供更多功能和增强工具。相比之下使用普通的全局变量需要手动实现或依赖第三方库来实现类似的扩展功能。
综上所述VueX 提供了一种结构化、可维护和可扩展的方式来管理应用程序的状态通过集中管理、响应式更新、严格的状态
45、请写出下列代码执行结果
function f1(){console.time(time span)}
function f2(){console.timeEnd(time span);}
setTimeout(f1,100);
setTimeout(f2,200);
function waitForMs(n){
var now Date.now();while(Date.now()-now n){}
}
waitForMs(500)A、tine apan:700.077ms
B、time apan:0.066ms
C、time apan:500.077ms
D、time apan:100.077ms这四个选项那个是上述代码的正确选项?根据您提供的代码和选项正确的选项应该是C、time span: 500.077ms在这段代码中先使用setTimeout函数延迟100毫秒后调用f1函数开始计时然后再延迟200毫秒后调用f2函数结束计时。而在waitForMs函数中使用一个循环进行500毫秒的阻塞等待。因此在最终输出结果中time span 表示计时开始到计时结束的时间差因为f1函数的计时开始与f2函数的计时结束之间有一个等待时间为500毫秒的操作则应该是500.077ms。46、判断基础类型之间的比较
请选择结果为真的表达式
A、null instanceof Object
B、null undefined
C、NaNNaN
D、false undefined在给出选项的表达式中正确的是B、null undefined这是因为在JavaScript中使用进行比较时会进行类型转换。而null和undefined在相等比较时是相等的它们被认为是同一种特殊的空值因此该表达式结果为真。其他选项的表达式结果为假
A、null instanceof Object虽然null是一个特殊值但它不是Object的实例因此该表达式结果为假。
C、NaNNaNNaN (Not-a-Number)代表非数字与任何其他值包括自身进行相等比较时结果都会返回false。
D、false undefined将布尔值false与undefined进行相等比较时它们的类型不同因此该表达式结果为假。47、HTML5新增的inputtype类型有哪些?
A、image B、color C、date D、range E、tel
HTML5新增的input type类型有以下几种B、color用于选择颜色的输入控件。
C、date用于选择日期的输入控件。
D、range用于选择范围的输入控件可以拖动滑块来选择值。
E、tel用于输入电话号码的输入控件。而A选项的image不是HTML5新增的input type类型它实际上是用于显示图像按钮的而不是输入类型。
48、HTML5 input type类型 都有那些
HTML5新增的input type类型包括以下几种1. text文本输入框
2. password密码输入框
3. email电子邮件输入框
4. number数字输入框
5. search搜索输入框
6. tel电话号码输入框
7. urlURL输入框
8. date日期输入框
9. month月份输入框
10. week周数输入框
11. time时间输入框
12. datetime-local本地日期时间输入框
13. color颜色选择器
14. range范围选择器
15. checkbox复选框
16. radio单选按钮
17. file文件上传控件
18. submit提交按钮
19. reset重置按钮
20. button普通按钮
21. image图像按钮用于显示图像
22. hidden隐藏输入字段这些是HTML5中常用的input type类型可以根据不同的需求选择适合的类型。49、可以实现选择某个元素的所有奇数项的选择器是?
A、:first-of-type B、:nth-child(2n1) C、:nth-of-type(2n1) D、:nth-last-of-type(3n)
选择某个元素的所有奇数项的选择器是C、:nth-of-type(2n1)。选择器C、:nth-of-type(2n1)表示选择该类型元素中的奇数位置项。其中n代表一个整数从0开始计数。当n为0时选择第1个、第3个、第5个…的元素当n为1时选择第2个、第4个、第6个…的元素以此类推。所以:nth-of-type(2n1)选择器可以实现选择某个元素的所有奇数项。A、:first-of-type选择器
:first-of-type选择器表示选择某个元素在父元素下的同类型元素中的第一个出现的元素。该选择器不考虑位置只关注元素类型。例如使用:first-of-type选择器可以选择某个父元素下的第一个子元素无论它在父元素中的位置如何。B、:nth-child(2n1)选择器
:nth-child(2n1)选择器表示选择某个元素在父元素下的所有子元素中的奇数项。这个选择器会根据元素在父元素中的位置进行计算。其中n代表一个整数从0开始计数。当n为0时选择第1个、第3个、第5个…的元素当n为1时选择第2个、第4个、第6个…的元素以此类推。D、:nth-last-of-type(3n)选择器
:nth-last-of-type(3n)选择器表示选择某个元素在父元素下的同类型元素中距离最后一个同类型元素的位置是3的倍数的元素。其中n代表一个整数从0开始计数。该选择器从最后一个同类型元素开始向前计算位置每隔3个元素选择一次。
50、下面的执行结果
var a 100;function test() {console.log(a);var a 10;console.log(a);
}test();
// undefined 10
在修正后的代码中我们将函数定义和调用包裹在正确的语法结构中。此外console,log被更正为console.log来输出控制台日志。请注意在test函数内部我们声明了一个局部变量a并对其进行赋值这样它就会覆盖全局变量a。因此第一个console.log(a)输出的将是在函数作用域内的值 undefined而第二个console.log(a)输出的是函数作用域内重新赋值后的值 10。51、下列程序运行结果符合预期的是:
var a{}, b Object.prototype;
[a.prototype b, Object.getPrototypeOf(a) b]A:[false,true] B:[true,true] C:[false,false] D:other运行结果符合预期的是B: [false, true]。解释如下
- a.prototype b这个表达式中对于对象a来说它的原型对象应该通过Object.getPrototypeOf(a)来获取而不是直接访问.prototype属性。因此a.prototype会返回undefined即使对象a没有显式定义原型对象。另外全局变量Object.prototype表示所有对象的原型对象所以a.prototype b返回false。
- Object.getPrototypeOf(a) b这个表达式中使用了Object.getPrototypeOf()方法来获取对象a的原型对象并将其与b进行比较。由于对象a的原型对象是Object.prototype所以返回的结果为true。因此符合预期的答案是B[false, true]。Object.getPrototypeOf()是一个内建函数用于获取指定对象的原型对象。它接受一个对象作为参数然后返回该对象的原型对象。对象的原型对象可以通过 __proto__ 属性或者 Object.getPrototypeOf() 方法获得。原型对象又包含了一组属性和方法这些属性和方法可以被继承到该对象本身。下面是使用Object.getPrototypeOf()的示例javascript
const obj {};
const prototype Object.getPrototypeOf(obj);
console.log(prototype); // 输出: {}
在上面的示例中我们创建了一个空对象 obj然后使用Object.getPrototypeOf()方法获取了它的原型对象 prototype。由于obj是一个普通对象它的原型对象是 Object.prototype所以prototype的值为空对象 {}。需要注意的是Object.getPrototypeOf()只能用于获取对象的原型对象而不能用于获取其他类型的原型比如基本类型如数字、字符串的原型对象。52、可以实现只选择与div相邻的p元素的选择器是?
A.divb B、div,p C、div p D、div ~d E、divp
E、divp选择器可以实现只选择与div相邻的p元素。该选择器表示选择紧接在div元素后面的第一个p元素。它要求p元素是紧跟在div元素之后的兄弟元素并且它们有相同的父元素。53、下列程序符合预期的是
(function() {
var xy1;
})();
console.log(y)
console.log(x)符合预期的输出结果是
1
ReferenceError: x is not defined解释如下
在这段代码中自执行函数创建了一个局部作用域。变量 x 使用了 var 关键字进行声明并赋值为 1因此它是在局部作用域内有效的局部变量。而变量 y 则没有使用 var 或 let 进行声明因此它被隐式地声明为全局变量并且也被赋值为 1。由于自执行函数是立即执行的所以在执行过程中内部的变量 x 和全局的变量 y 都被赋值为 1。因此在自执行函数外部调用 console.log(y) 输出结果为 1而调用 console.log(x) 会导致引用错误ReferenceError因为变量 x 是在自执行函数的局部作用域内定义的并不在外部作用域中可见。这样正确的输出结果应该是
1
ReferenceError: x is not defined
54、请编写一个JavaScript 函数 parseQueryString它的用途是把 URL参数解析为一个对象
下面是一个 JavaScript 函数 parseQueryString 的实现它将解析 URL 参数并返回一个包含参数键值对的对象
function parseQueryString(url) {var queryString url.split(?)[1]; // 获取问号后面的参数部分var params {};if (queryString) {var keyValuePairs queryString.split(); // 将参数字符串拆分为键值对数组keyValuePairs.forEach(function(keyValuePair) {var pair keyValuePair.split(); // 拆分键值对var key decodeURIComponent(pair[0]); // 解码键var value decodeURIComponent(pair[1]); // 解码值if (params.hasOwnProperty(key)) {// 如果参数键已存在将值转换为数组并追加新值if (!Array.isArray(params[key])) {params[key] [params[key]];}params[key].push(value);} else {params[key] value; // 添加新的参数键值对}});}return params;
}使用示例
var url http://example.com/?nameJohnage30hobbiesreadinghobbiessports;var params parseQueryString(url);console.log(params);输出结果
{name: John,age: 30,hobbies: [reading, sports]
}上述函数会将 URL 参数解析为一个对象其中参数名作为对象的属性参数值作为属性的值。如果同一个参数名出现多次则参数值将被存储为数组。
需要注意的是这个示例只针对简单的参数解析并且没有考虑一些复杂的情况例如参数值包含特殊字符或嵌套对象等。如果需要更完整和健壮的 URL 解析功能可以使用现有的第三方库如 query-string、URLSearchParams 等。
55、decodeURIComponent
decodeURIComponent 是 JavaScript 中的一个全局函数用于解码使用 encodeURIComponent 编码的字符串。
在 JavaScript 中当需要将字符串作为 URL 的一部分发送时需要使用 encodeURIComponent 对特殊字符进行编码。例如空格会被编码为 %20问号会被编码为 %3F 等。而 decodeURIComponent 则可以对这些编码后的字符串进行解码恢复原始的字符串表示。
下面是 decodeURIComponent 函数的示例用法
var encodedString %E4%BD%A0%E5%A5%BD%2C%20%E4%B8%96%E7%95%8C%EF%BC%81;var decodedString decodeURIComponent(encodedString);console.log(decodedString);输出结果
你好, 世界在上述示例中我们通过 decodeURIComponent 将经过 encodeURIComponent 编码的字符串解码为原始的字符串。
需要注意的是如果传递给 decodeURIComponent 的字符串不是有效的编码格式或者包含了无效的编码序列那么函数可能会抛出异常。因此在使用 decodeURIComponent 进行解码之前最好确保输入的字符串是由 encodeURIComponent 或类似方法进行的正确编码。
Electron 面试题
Electron是一个用于构建跨平台桌面应用程序的开源框架它使用HTML、CSS和JavaScript技术。Electron的主要特点包括跨平台能力支持Windows、Mac和Linux、易于开发使用Web技术、强大的功能扩展性和兼容性。与传统的Web应用相比Electron应用具有更接近原生应用的外观和行为可以访问操作系统级别的API并且在离线环境下也可以运行。创建一个基本的Electron应用程序需要安装Node.js和npm然后使用命令行工具创建和初始化项目编写主进程和渲染进程的代码并使用Electron运行应用程序。主进程是应用程序的控制中心负责处理窗口管理、系统级别的功能和与渲染进程的通信。而渲染进程则类似于浏览器中的页面负责展示界面和处理用户交互。可以通过使用Electron提供的API来与操作系统进行交互例如创建系统托盘菜单、使用原生对话框或执行操作系统命令等。Electron应用程序可以使用打包工具如Electron Builder来将源代码和依赖项打包成可执行文件并结合软件分发渠道进行发布。Electron的优点包括跨平台能力、开发效率高、生态系统丰富缺点可能包括应用程序体积较大和性能相对较低。相对于其他类似框架Electron有着更广泛的社区支持和活跃的开发生态系统并且在一些知名应用中得到了广泛应用。在Electron应用程序中需要注意安全问题比如防止恶意代码注入、用户数据保护等可以通过使用Electron提供的安全机制和最佳实践来确保应用程序安全
11、Electron 怎么使用
以下是使用Electron构建应用程序的基本步骤 确保你已经安装了Node.js和npmNode包管理器。你可以在官方网站上下载并按照说明进行安装。 打开命令行工具创建一个新的项目文件夹并进入该文件夹 mkdir my-electron-app
cd my-electron-app在项目文件夹下初始化你的应用程序并创建一个 package.json 文件来管理依赖项 npm init -y安装Electron作为开发依赖项 npm install electron --save-dev创建一个主进程文件 main.js 这将是你的应用程序的入口点。在这个文件中你可以设置窗口、处理系统事件和与渲染进程通信等 // main.jsconst { app, BrowserWindow } require(electron);function createWindow() {const win new BrowserWindow({width: 800,height: 600,webPreferences: {nodeIntegration: true // 允许在渲染进程中使用Node.js API}});win.loadFile(index.html); // 加载显示界面的HTML文件// 在窗口准备好后显示win.once(ready-to-show, () {win.show();});
}app.whenReady().then(createWindow); // 应用准备就绪后创建窗口创建一个渲染进程文件 index.html 用于定义你的应用程序界面 !-- index.html --!DOCTYPE html
html
headtitleMy Electron App/title
/head
bodyh1Hello, Electron!/h1
/body
/html在 package.json 文件中添加一个脚本命令用于运行你的应用程序 // package.json{name: my-electron-app,version: 1.0.0,scripts: {start: electron .}
}运行以下命令启动应用程序 npm start现在你应该能够看到一个新的Electron窗口里面显示着 “Hello, Electron!”。这只是一个基本示例你可以根据需要进行扩展和定制使用Electron提供的API来实现更多功能。
请记住这只是一个简单的入门指南并不包括所有可能的细节和高级用法。对于更复杂的应用程序开发请参考Electron官方文档和其他
1、什么是 Electron
Electron 是一个开源框架用于构建跨平台的桌面应用程序。它使用 HTML、CSS 和 JavaScript 技术栈来创建像原生应用一样运行的应用程序。
2、Electron 是如何工作的
Electron 使用 Chromium 渲染引擎来呈现用户界面并通过 Node.js 提供对操作系统底层功能的访问。它将网页和本地代码结合在一起允许开发者使用 Web 技术来构建强大的桌面应用程序。
3、Electron 应用的主进程和渲染进程有什么区别
主进程是应用程序的入口点负责创建和管理渲染进程。它可以访问本地资源和操作系统级别的功能。每个 Electron 应用只能有一个主进程。渲染进程是由主进程创建的浏览器窗口中的进程。每个窗口都有自己的渲染进程。渲染进程负责显示和处理网页内容但不能直接访问操作系统的原生资源。
4、如何与主进程进行通信
渲染进程可以通过 IPC进程间通信机制与主进程进行通信。主进程和渲染进程可以通过 IPC 的事件、消息发送和共享数据来交换信息。
5、Electron 支持的操作系统是哪些
Electron 可以在 Windows、macOS 和 Linux 上运行从而实现跨平台的桌面应用程序开发。
6、如何打包 Electron 应用
Electron 提供了多种打包工具和框架例如 Electron Forge、Electron Packager 和 Electron Builder。这些工具可以将 Electron 应用打包为可执行文件或安装程序以便在不同操作系统上分发和安装应用。
7、怎样优化 Electron 应用的性能
避免在渲染进程中加载过多资源或进行复杂的计算。使用 Chrome DevTools 来检测内存泄漏和性能问题。合理管理渲染进程的生命周期如使用窗口重用等。使用原生模块或 C 扩展来提高性能关键部分的执行效率。针对特定平台进行优化如针对 macOS 的 App Nap 或 Windows 的 High DPI 设置。
这些问题涵盖了 Electron 的基本概念、核心原理和常见的开发考虑因素。希望这些问题和答案能帮助你准备好 Electron 面试。记得根据你所申请的职位和角色做进一步的学习和准备。
8、如何在 Electron 中使用本地模块Native Modules
在 Electron 中可以通过使用 Node.js 的原生模块来访问本地功能。这需要使用 node-gyp 工具将 C 代码编译为可用于 Electron 的模块。
9、如何处理 Electron 应用程序的主窗口关闭事件
可以通过监听 window.on(close) 事件来处理主窗口的关闭事件。在监听事件的回调函数中可以执行必要的清理操作并决定是否允许关闭窗口。
10、如何在 Electron 应用中实现自动更新Auto Update功能
可以使用 Electron 提供的自动更新模块如 electron-updater来实现自动更新功能。该模块可以从服务器端下载并安装应用程序的最新版本。
11、如何在 Electron 应用程序中使用本地存储
Electron 应用可以使用浏览器提供的 Web Storage API如 localStorage 和 sessionStorage。此外还可以使用 Node.js 的文件系统 API 访问本地文件系统进行数据存储。
12、如何创建自定义菜单栏和上下文菜单Context Menu
可以使用 Electron 提供的 Menu 和 MenuItem 类来创建自定义菜单栏和上下文菜单。可以通过定义菜单项和子菜单项来构建菜单并使用 IPC 将特定操作与菜单项关联起来。
13、如何调试 Electron 应用程序
可以使用 Chrome DevTools 来调试渲染进程。通过在主进程中启用 --inspect 标志可以调试主进程。
14、如何处理 Electron 应用程序的多窗口通信
可以使用 Electron 提供的 IPC进程间通信机制在不同的窗口之间进行通信。可以通过主进程作为中介来实现
15、如何处理 Electron 应用的安全性
在 Electron 应用中安全性非常重要。以下是几种处理安全性的方法 使用最新版本的 Electron 和相关依赖库以获得最新的安全补丁和修复。验证和过滤所有从用户界面传递到主进程的输入以防止恶意代码注入或跨站脚本攻击XSS。注意将敏感信息存储在安全的地方如使用 Node.js 的 crypto 模块对密码进行加密。避免在渲染进程中执行危险的操作如禁止使用 eval() 函数、限制对远程资源的加载等。使用 Electron 提供的安全策略和 API如设置 Content Security PolicyCSP限制页面内容、使用 Webview 标签时审查远程内容等。
16、如何实现自动更新 Electron 应用程序
Electron 提供了自动更新功能来简化应用程序的部署和更新流程。以下是一种常用的自动更新方式 使用 Electron Builder 或其他打包工具配置自动更新的参数。在服务器上托管应用程序的更新文件并提供一个下载地址或版本 API 给应用程序。应用程序在启动时检查最新的版本号并与当前版本进行比较。如果有可用的更新应用程序会下载新版本的文件并提示用户安装更新。
17、如何调试 Electron 应用
Electron 提供了多种调试工具和技术来帮助开发者调试应用程序。以下是一些常用的调试方法 使用 Chrome DevTools 来调试渲染进程代码包括查看 DOM、调试 JavaScript 和分析性能。在主进程中使用 Node.js 的调试工具来调试主进程代码。使用 Electron 命令行工具启动应用程序时可以添加 --inspect 或 --remote-debugging-port 参数来启用远程调试。
Vue3
1、Vue 3中的Composition API是什么它与Options API有何不同
答案Composition API是Vue 3中引入的一种新的组织代码的方式它通过函数式的形式来组合和重用逻辑。与Options API相比Composition API更灵活、可组合性更强并且更利于代码的重用和维护。
2、Vue 3中的响应式数据是如何实现的
答案Vue 3通过使用ref和reactive函数来创建响应式数据。ref用于创建包装简单值的响应式引用而reactive则用于创建包装复杂对象的代理并使其内部属性成为响应式。
3、Vue 3中的Teleport组件是用来做什么的
答案Teleport组件是Vue 3中新增的一个特性用于将子组件在DOM中的位置移动到任意指定的目标位置。这对于创建弹出窗口、模态框等场景非常有用。
4、Vue 3中的v-model指令有何变化
答案在Vue 3中v-model指令仍然用于双向绑定数据但工作原理略有变化。现在v-model指令直接绑定到组件实例上的value属性而不再依赖于组件内部通过props和$emit来传递数据。
5、Vue 3中的Suspense组件是用来做什么的
答案Suspense组件是Vue 3中新增的一个特性用于处理异步组件的加载状态。它可以在异步组件加载完成之前显示指定的fallback内容以提供更好的用户体验。
6、Vue 3中的组合式API和混入Mixin有何区别
答案组合式API是一种更灵活、可组合性更强的代码组织方式它通过函数形式将逻辑进行组合和重用。而混入是一种在Vue中共享组件选项的方式在多个组件之间共享相同的逻辑或功能。与混入不同组合式API避免了命名冲突并提供了更明确的逻辑组合方式。
7、在Vue 3中如何处理全局状态管理
答案在Vue 3中可以使用新引入的provide和inject API来处理全局状态管理。通过在父组件上使用provide提供数据然后在子组件上使用inject来访问这些数据实现跨层级组件间的状态共享。
8、Vue 3中的动态组件有何变化
答案在Vue 3中动态组件的用法没有变化仍然使用component元素和:is属性来切换动态组件。但由于编译器的改进Vue 3生成的代码更小、更高效。
9、Vue 3中支持哪些新的生命周期钩子函数
答案Vue 3中新增了两个生命周期钩子函数beforeUnmount和onVnodeMounted。beforeUnmount在组件实例销毁之前调用而onVnodeMounted在虚拟节点挂载后调用。
10、Vue 3中的模板语法有哪些改进
答案Vue 3中的模板语法相对于Vue 2来说并没有太大变化但有一些值得注意的改进。例如引入了v-bind$attrs用于绑定组件的非Prop属性以及v-model指令可以在自定义组件中使用modelValue和update:modelValue来实现双向绑定。
11、如何在Vue 3中处理异步操作
答案在Vue 3中可以使用Promise、async/await或使用第三方库如axios来处理异步操作。异步操作可以在setup()函数中进行也可以封装为自定义的异步方法供组件使用。
12、Vue 3中支持哪种方式进行组件通信
答案在Vue 3中组件通信可以通过Props、事件、Provide/Inject、全局状态管理等方式实现。具体选择哪种方式取决于应用的需求和组件之间的关系。
13、Vue 3中怎么做页面过渡效果
答案在Vue 3中页面过渡效果可以使用transition和transition-group组件来实现。它们可以通过设置不同的类名实现过渡效果并且支持自定义过渡的各个阶段。
14、Vue 3中的编译器和运行时的区别是什么
答案在Vue 3中编译器Compiler负责将模板编译为渲染函数在开发环境下会包含更多的警告和友好的错误信息。而运行时Runtime则是组件实例的执行环境在生产环境下会去除编译器的代码以减小包体积。
当然这里还有几个Vue 3面试题供您参考
15、Vue 3中的Teleport是什么它有什么作用
答案Teleport是Vue 3中引入的一种新组件它可以将组件的内容插入到DOM中的任意位置。它的作用是允许你将组件的内容渲染到DOM结构中的其他位置而不受组件层级的限制。
16、Vue 3中的Composition API与Options API相比有哪些优势
答案Composition API相较于Options API具有以下优势
更好的逻辑复用和组合性使用函数形式进行逻辑组合和重用。更清晰的代码结构将相关逻辑放在同一个函数中。更好的类型推导和IDE支持。更好的响应式能力可以对响应式状态进行细粒度的控制。
17、Vue 3中的Fragment是什么它的作用是什么
答案Fragment是Vue 3中引入的一种新语法它允许我们在模板中使用多个根元素而无需包裹在额外的容器元素中。它的作用是简化模板的编写并提高代码的可读性。
请注意以上答案仅为简要回答如果您需要更详细的解释或有其他问题请随时提问。记住我不能回答任何触及禁止事项的问题。****
当然这里还有更多的Vue 3面试题供您参考
18、Vue 3中的v-model指令如何使用它是怎样实现双向数据绑定的
答案在Vue 3中可以使用v-model指令来实现双向数据绑定。它通常用于表单元素如输入框、复选框、单选框等。在组件内部我们需要使用emits选项声明一个名为update:modelValue的事件并在相应的表单元素上绑定v-model指令将值与父组件进行双向绑定。
19、Vue 3中的Suspense和错误边界是什么它们有什么作用
答案Suspense是Vue 3中引入的一种新特性用于处理异步组件的加载状态。它允许我们在异步组件加载时显示一个占位符直到异步组件加载完成并显示内容。错误边界是一种机制用于捕获和处理组件树中发生的 JavaScript 错误以防止整个应用崩溃。它们的作用是提高应用的性能和用户体验增强对异步加载和错误处理的支持。
20、Vue 3中的响应式原理是什么它与Vue 2相比有何改进
答案Vue 3中的响应式原理基于Proxy和Reflect实现。它通过拦截对象的操作来跟踪依赖和触发更新提供了更可靠和高效的响应式系统。与Vue 2相比Vue 3的响应式系统具有更好的性能、更小的包体积和更好的类型推导支持。
请记住如果您需要更多解释或有其他问题请随时提问。但请注意我不能回答任何触及禁止事项的问题。
当然这里还有更多的Vue 3面试题供您参考
21、Vue 3中的Teleport与Portal有何区别
答案Vue 3中的Teleport与Portal概念类似都可以将组件的内容插入到DOM的其他位置。但两者的实现方式略有不同Teleport使用了新的Teleport组件来实现而Portal是通过普通的组件结构和slot来实现。另外Teleport相对于Portal提供了更细粒度的控制可以在任意位置进行渲染。
22、Vue 3中的递归组件是什么它在哪些场景下常用
答案递归组件是指在组件的模板中调用自身的一种特殊组件。它在处理树状结构的数据或需要无限嵌套的场景时非常有用比如文件夹树、评论回复等。递归组件通过递归调用自身来展示嵌套结构并且可以通过定义递归结束条件来避免无限循环。
23、Vue 3中的Fragments和Portals在性能上有何改进
答案Vue 3中的Fragments和Portals在性能上有所改进。Fragments在渲染时不会创建额外的父级包装元素减少了不必要的DOM层级提升了性能。而Portals利用了Teleport的机制在插入和移动组件时可以更快地更新目标位置的DOM减少了不必要的重渲染。
请注意以上答案仅为简要回答如果您需要更详细的解释或有其他问题请随时提问。记住我不能回答任何触及禁止事项的问题。
当然请继续参考更多的Vue 3面试题
24、Vue 3中的Composition API和Options API有何区别
答案Composition API是Vue 3引入的一种新的组合式API风格它允许开发者根据逻辑相关性组织代码。相比之下Options API是Vue 2中主要使用的API风格将相关的选项如data、methods、computed等放在一个对象中。Composition API更加灵活、可组合和易于阅读在处理复杂逻辑和代码重用方面更具优势。
25、Vue 3中的全局状态管理工具是什么它与Vue 2中的Vuex有何区别
答案Vue 3中的全局状态管理工具是vue/reactivity库它提供了一些函数和类用于创建响应式状态和触发更新。与之相比Vue 2中的Vuex是一个专门为Vue.js设计的状态管理库提供了集中式的状态管理方案包括状态定义、状态获取和状态修改等功能。相对而言vue/reactivity库更加轻量级且与其他生态系统更加兼容。
26、Vue 3中的动画过渡是如何实现的请提供一个简单的示例。
Vue 3中的动画过渡可以通过transition组件和transition-group组件来实现。
下面是一个简单的示例展示了如何使用transition组件实现元素的淡入淡出效果
templatedivbutton clicktoggleShowToggle Show/buttontransition namefadep v-ifshow这是一个会淡入淡出的元素/p/transition/div
/templatescript
export default {data() {return {show: false,};},methods: {toggleShow() {this.show !this.show;},},
};
/scriptstyle
.fade-enter-active,
.fade-leave-active {transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {opacity: 0;
}
/style在上述代码中我们使用了transition组件将希望具有动画效果的元素包裹起来。通过设置name属性为fade我们定义了过渡的名称同时也对应了CSS过渡类名的前缀。
然后在style标签中我们通过定义.fade-enter-active和.fade-leave-active样式类来控制动画的持续时间和过渡属性。同时通过定义.fade-enter和.fade-leave-to样式类来控制初始状态和目标状态之间的过渡效果。
最后点击按钮时通过修改show的值来控制元素的显示与隐藏从而触发淡入淡出的动画效果。
27、history 模式和hash模式的区别 优缺点 和分别的用法
在计算机编程领域中history模式和hash模式通常用于描述前端路由的不同实现方式。 History模式 优点使用 history 模式可以去除 URL 中的哈希符号#使 URL 更加清晰可读并且更接近传统的网站 URL 结构。缺点使用 history 模式需要服务器配置来支持。如果用户直接访问 history 模式下的 URL会收到 404 错误因此需要在服务器上进行一些特殊的配置。用法可以通过调用浏览器的 pushState 方法或使用前端框架如Vue Router、React Router等来启用 history 模式。 Hash模式 优点使用 hash 模式无需对服务器进行特殊配置可以直接在普通的静态服务器上部署。同时hash 模式兼容性较好在较旧的浏览器中也能正常工作。缺点URL 中使用了特殊字符 “#”可能会引起搜索引擎的误判且对用户而言URL 可能不够直观易懂。用法通过修改 URL 的 hash 片段来实现页面切换通常是监听浏览器的 “hashchange” 事件再根据新的 hash 值来处理相应的逻辑。
选择使用哪种模式取决于具体需求和场景。如果需要兼容性好且无需额外服务器配置则可以选择 hash 模式。而如果希望 URL 更加直观可读并愿意进行服务器配置来支持那么可以选择 history 模式。
28、h5页面从A页面到B页面再到C页面 在不刷新页面的情况下返回A页面用上面的哪一种路由模式实现
要在不刷新页面的情况下从 C 页面返回 A 页面可以使用 history 模式来实现。 History 模式允许通过修改 URL 的路径来切换页面同时不会触发页面的完全刷新。
具体步骤如下
在 A 页面中设置路由链接或按钮点击时导航到 B 页面。在 B 页面中同样设置路由链接或按钮点击时导航到 C 页面。在 C 页面中设置路由链接或按钮点击时执行前端代码调用浏览器的 history.back() 方法即可返回到 A 页面而无需刷新页面。
需要注意的是使用 history 模式进行页面切换时需要确保服务器已配置了相关路由规则以确保直接访问特定路径时能正确加载对应的页面。
29、vue3中生命周期函数
在 Vue 3 中由于引入了 Composition API生命周期函数的概念发生了改变。Vue 3 中不再使用以前的选项式 API 中的生命周期钩子函数如 beforeCreate、mounted 等而是使用了一组新的函数。
以下是 Vue 3 中常用的生命周期函数
setup在组件实例创建之前执行用于设置组件的初始状态和逻辑。可以返回一个对象供组件中其他部分使用也可以返回一个清理函数。onBeforeMount在组件挂载之前执行类似于 Vue 2.x 中的 beforeMount。onMounted在组件挂载后执行类似于 Vue 2.x 中的 mounted。onBeforeUpdate在组件更新之前执行类似于 Vue 2.x 中的 beforeUpdate。onUpdated在组件更新之后执行类似于 Vue 2.x 中的 updated。onBeforeUnmount在组件卸载之前执行类似于 Vue 2.x 中的 beforeDestroy。onUnmounted在组件卸载后执行类似于 Vue 2.x 中的 destroyed。
此外还有其他一些生命周期函数如 onActivated、onDeactivated用于 keep-alive 组件、onRenderTracked、onRenderTriggered用于调试等。
需要注意的是Vue 3 中的生命周期函数采用了更加灵活的 Composition API 及函数式编程的方式与 Vue 2.x 中的选项式 API 不完全一样。
30、Electron 如何与主进程进行通信代码示例
在 Electron 中可以使用 IPCInter-Process Communication进程间通信来实现主进程和渲染进程之间的通信。IPC 分为两种类型主进程向渲染进程发送消息和渲染进程向主进程发送消息。
以下是 Electron 中主进程与渲染进程的通信示例代码
// 主进程 main.js const { app, BrowserWindow, ipcMain } require(‘electron’); let mainWindow;
function createWindow() { mainWindow new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true // 允许渲染进程使用 Node.js API } });
mainWindow.loadFile(‘index.html’);
ipcMain.on(‘messageToMain’, (event, arg) { console.log(arg); // 打印从渲染进程传递的消息 event.reply(‘messageToRenderer’, ‘Message received in the main process.’); // 回复消息给渲染进程 }); }
app.whenReady().then(() { createWindow(); });
// 渲染进程 index.html
Electron IPC Example Send Message to Main Process 在示例代码中主进程通过 ipcMain 模块监听名为 ‘messageToMain’ 的事件并在接收到消息时输出到控制台。之后使用 event.reply 方法向渲染进程回复消息。
渲染进程通过 ipcRenderer 模块向主进程发送名为 ‘messageToMain’ 的事件并通过 ipcRenderer.on 监听名为 ‘messageToRenderer’ 的事件接收主进程的回复消息并将消息显示在网页中。
注意在 Electron 中默认情况下是禁止在渲染进程中使用 Node.js 的 API 的所以需要在 BrowserWindow 的配置中将 nodeIntegration 设置为 true以便渲染进程可以使用 Node.js 的功能。
这是一个简单的示例你可以根据自己的需求进行扩展。
31、用const 定义一个对象 这个对象能再增加属性吗
使用 const 关键字定义的对象是具有不可变性的常量一旦定义后就不能再改变其绑定的值。这意味着无法直接添加新属性到已经定义的对象上。如果尝试添加属性会导致报错。
然而可以通过一些其他方式来实现对已定义对象的扩展例如使用 Object.assign() 方法或扩展运算符 ...。
以下是两种方法的示例
使用 Object.assign() 方法
const obj { prop1: value1,prop2: value2
};const extendedObj Object.assign({}, obj, { prop3: value3 });console.log(extendedObj);
// 输出: { prop1: value1, prop2: value2, prop3: value3 }在上述示例中使用 Object.assign() 将新的属性 { prop3: value3 } 合并到了一个新的空对象 {} 和原始对象 obj 中从而创建了一个包含所有属性的新对象 extendedObj。
使用扩展运算符 ...
const obj { prop1: value1,prop2: value2
};const extendedObj {...obj,prop3: value3
};console.log(extendedObj);
// 输出: { prop1: value1, prop2: value2, prop3: value3 }在上面的示例中使用扩展运算符 ... 将原始对象 obj 的属性展开然后添加新的属性 { prop3: value3 }最终创建了一个包含所有属性的新对象 extendedObj。
无论是使用 Object.assign() 方法还是扩展运算符 ...都会创建一个新的对象并将原始对象的属性以及新属性合并到其中从而实现对已定义对象的扩展。
32、下列代码执行结果
const a {b:1,c:2}:
a.d3;抱歉我给出了错误的回答。对于用 const 定义的对象不能直接添加新属性。无论是使用点符号还是赋值语句都无法向常量对象添加新的属性。如果尝试这样做会导致语法错误。例如在你的例子中尝试给常量对象 a 添加属性 d 会导致错误。
正确的做法是在定义对象时就将所有需要的属性初始化完整或者使用其他方式来实现属性的动态增加如使用 let 关键字定义可变对象。下面是一个示例
let a { b: 1, c: 2 };
a.d 3;console.log(a);
// 输出: { b: 1, c: 2, d: 3 }在上述示例中使用 let 关键字定义了可变对象 a然后可以通过赋值语句添加新属性 d。输出结果为 { b: 1, c: 2, d: 3 }。 对于想要扩展现有常量对象的情况无法直接通过给常量对象添加新的属性来实现。而是需要参考之前提到的使用 Object.assign() 方法或扩展运算符 ... 的方式来扩展对象。
33、vue 中样式隔离
在 Vue 中可以使用不同的方式来实现组件样式的隔离和封装。以下是几种常见的方法
Scoped CSS作用域样式 Vue 提供了一种称为 “scoped” 的样式隔离方式。通过在单个组件的 style 标签上添加 scoped 属性可以将样式限制在当前组件的作用域内避免样式冲突。
templatediv classexample!-- 组件内容 --/div
/templatestyle scoped
.example {/* 组件样式 */
}
/style上述示例中.example 类名只会应用于当前组件内部不会影响其他组件中相同类名的元素。
CSS Modules CSS Modules 是一种基于文件命名约定的样式模块化方案。在 Vue 项目中启用 CSS Modules 后每个样式文件都被视为独立的模块并且在组件中通过导入和使用模块化的方式引入样式。
templatediv :class$style.example!-- 组件内容 --/div
/templatestyle module
.example {/* 组件样式 */
}
/style使用 CSS Modules组件的样式会自动与范围限定并且可以在组件模板中使用 $style 对象来访问对应的样式类名。
CSS-in-JS 库 除了上述的内建方式外还可以使用第三方的 CSS-in-JS 库如 styled-components、emotion 等。这些库允许在组件定义中直接编写样式并将其与组件绑定在一起。
templatediv classexample!-- 组件内容 --/div
/templatestyle langjs
import styled from styled-components;const StyledExample styled.div/* 组件样式 */
;export default {name: MyComponent,components: {StyledExample}
};
/style通过 CSS-in-JS 库在 Vue 组件中可以直接使用类似 CSS 的语法来定义、封装和使用样式。
以上是几种常见的在 Vue 中实现样式隔离的方法。每种方法都有其适用的场景和特点请根据具体需求选择合适的方式。
34、vue 监听属性和计算属性的实现原理
在 Vue 中属性的监听和计算属性都是利用了 Vue 的响应式系统来实现。
监听属性 Vue 提供了 $watch 方法可以用于监听属性的变化。当被监听的属性发生变化时Vue 会自动触发相应的回调函数。
this.$watch(propertyName, (newValue, oldValue) {// 属性发生变化时执行的操作
});通过 $watch 方法Vue 在内部会创建一个“观察者Watcher”它会订阅要监听的属性并在属性发生改变时收到通知。然后触发对应的回调函数。
计算属性 计算属性是由数据属性计算而来的属性它的值根据其他属性的变化而变化并且会进行缓存以提高性能。在模板中使用计算属性的方式与普通属性一致。
计算属性的实现原理是通过使用 Vue 内部的“依赖追踪”机制。Vue 会在渲染组件时跟踪属性的读取发现计算属性依赖的属性发生变化时会重新计算并缓存计算属性的值。在下次访问该计算属性时如果其依赖的属性没有发生变化Vue 就直接返回缓存的计算结果减少不必要的重复计算。
总结起来Vue 的属性监听和计算属性实现原理都依赖于其内部的响应式系统。通过观察者模式和依赖追踪Vue 能够自动跟踪属性的变化并在需要的时候触发相应的回调函数或更新计算属性的值。这也是 Vue 在数据驱动视图的过程中实现高效、灵活的响应式更新的关键机制之一。
35、vue 中数组用push()的方式为什么可以实现响应式
在 Vue 中数组使用 push() 方法可以实现响应式更新是因为 Vue 使用了特殊的技巧来拦截数组的变异方法mutation methods。
Vue 的响应式系统在创建响应式对象时会将数组的原型进行改写并劫持了数组变异方法比如 push()、pop()、shift()、unshift()、splice()、sort() 和 reverse() 等。当调用这些数组方法时Vue 能够观察到数组的变化并通知相关的依赖进行更新。
具体的实现原理如下
当创建一个响应式数组时Vue 会将数组的原型指向一个改写过的数组原型 Array.prototype。改写过的数组原型包含了对数组变异方法的重新定义以使它们能够触发依赖更新。当使用 push() 方法向数组中添加新元素时Vue 能够捕捉到这个变化并在内部执行相应的操作 添加新元素到数组中。将新元素设置为响应式并进行依赖追踪。触发视图更新重新渲染包含该数组的组件。
通过以上步骤Vue 实现了对数组变异方法的拦截与处理使得数组的变化能够被响应式系统检测到并进行相应的更新操作从而实现了数组的响应式。
需要注意的是Vue 的这种拦截和劫持机制只能处理使用变异方法改变的数组数据对于直接通过索引修改数组元素或者改变数组长度的操作Vue 是无法检测到的。为了解决这个问题Vue 提供了一些特殊的方法来处理这类情况比如使用 Vue.set 或者替换整个数组来触发更新等方式。
36、webpack 链式路由
Webpack本身并不提供链式路由chained routing的功能因为Webpack是一个模块打包工具主要负责处理模块之间的依赖关系和打包输出。
链式路由通常是指在前端应用中使用的一种路由方式比如在Vue或React等框架中通过路由器router来管理不同页面之间的跳转和展示。常见的前端路由库有Vue Router、React Router等。
如果你想在Webpack的配置中使用链式路由可以结合相应的前端框架和路由库进行设置和配置。
例如在使用Vue.js开发项目时你可以借助Vue Router来实现链式路由。首先安装Vue Router
npm install vue-router然后在Webpack的配置文件中使用Vue Loader加载Vue组件并在Vue实例中配置路由
// webpack.config.jsmodule.exports {// ...其他配置项module: {rules: [// Vue Loader的配置规则{test: /\.vue$/,use: vue-loader},// ...其他加载器规则]},// ...其他配置项
}以及在Vue实例中定义路由和创建路由对象
// main.jsimport Vue from vue;
import VueRouter from vue-router;
import App from ./App.vue;
import Home from ./components/Home.vue;
import About from ./components/About.vue;Vue.use(VueRouter);const routes [{ path: /, component: Home },{ path: /about, component: About }
];const router new VueRouter({routes
});new Vue({router,render: h h(App)
}).$mount(#app);在这个例子中我们配置了两个路由’/‘和’/about’分别对应Home组件和About组件。当用户访问不同的路由时相应的组件会被加载并显示。
需要注意的是在使用链式路由时你可能还需要通过Webpack的配置项将路由相关的文件进行打包和输出以确保在浏览器端能够正确加载和使用。
总结起来Webpack本身并不直接提供链式路由功能但你可以使用它配合前端框架和路由库来实现链式路由。对于Vue.js项目可以使用Vue Router来配置和管理路由。
37、websocket 主题
WebSocket是一种网络通信协议它允许在客户端和服务器之间建立双向通信的连接。它的主要特点是实时性和低延迟。使用WebSocket可以实现实时数据传输、推送通知、即时聊天等功能。
至于主题在WebSocket中没有直接称为主题的概念。但是可以通过在消息中添加特定的标识符或自定义数据字段来实现类似主题的功能。这样不同的消息可以根据标识符或数据字段进行区分和处理以实现不同主题或频道的消息传递。
38、前端怎么做公共模块的抽离
在前端开发中抽离公共模块是一种常见的优化方式可以提高代码的可维护性和复用性。以下是几种常用的方法来进行公共模块的抽离 函数封装将一段常用的功能逻辑封装成函数并以参数形式暴露出来在不同的地方进行调用。这样可以避免代码重复提高代码的复用性。 组件抽离将一部分功能相似或通用的代码抽离成组件通过引入和使用组件来实现公共模块的复用。例如可以将页面中多处使用的头部、导航栏等抽离成组件。 工具类/插件封装将一些常用的工具函数或第三方插件封装成库供整个项目或多个项目共享使用。这样可以提高代码的复用性并且便于在不同的项目中统一管理和更新。 模块化管理使用模块化开发的方式将公共的逻辑和功能封装成独立的模块通过导入和使用模块来实现代码复用。可以使用像ES Modules、CommonJS等模块化规范或者使用打包工具如Webpack、Rollup进行模块的打包和管理。 配置文件提取将一些通用的配置项如API接口地址、样式主题等提取出来放在单独的配置文件中进行管理。不同的模块可以共享和引用这些配置项方便统一管理和修改。
以上是几种常见的方法来进行公共模块的抽离根据具体需求和项目情况选择适合的方式进行抽离以提高代码的可维护性和复用性。
39、什么是js 设计模式
以下是一些常见的 JavaScript 设计模式 单例模式Singleton Pattern确保类只有一个实例并提供全局访问点。 工厂模式Factory Pattern通过一个工厂方法创建对象而无需直接使用 new 关键字。 观察者模式Observer Pattern定义一对多的依赖关系当一个对象主题状态发生改变时其所有依赖观察者将自动更新。 装饰者模式Decorator Pattern动态地给对象添加新功能是继承关系的一种替代方案。 适配器模式Adapter Pattern将一个类的接口转换成客户端所期望的另一个接口使得原本不兼容的类可以协同工作。 策略模式Strategy Pattern将可互换的行为封装起来并使用委托来选择要执行的行为。 代理模式Proxy Pattern提供一个代理对象控制对真实对象的访问可以添加额外的逻辑。
这些设计模式有助于提高代码的可读性、可维护性和可测试性同时提供了一种标准化的解决方案来解决常见的设计问题。在实际开发中根据具体情况选择合适的设计模式可以提升代码质量和开发效率。
当然以下是一些常见设计模式的 JavaScript 代码示例
1. 单例模式Singleton Pattern
const Singleton (function() {let instance;function createInstance() {// 创建单例对象的代码return {/* 单例对象的属性和方法 */};}return {getInstance: function() {if (!instance) {instance createInstance();}return instance;}};
})();// 使用单例对象
const singletonInstance1 Singleton.getInstance();
const singletonInstance2 Singleton.getInstance();console.log(singletonInstance1 singletonInstance2); // 输出: true2. 工厂模式Factory Pattern
function createProduct(type) {if (type A) {return new ProductA();} else if (type B) {return new ProductB();} else {throw new Error(Invalid product type.);}
}// 具体产品类 A
function ProductA() {this.name Product A;// ...
}// 具体产品类 B
function ProductB() {this.name Product B;// ...
}// 使用工厂创建产品
const productA createProduct(A);
const productB createProduct(B);console.log(productA.name); // 输出: Product A
console.log(productB.name); // 输出: Product B3. 观察者模式Observer Pattern
class Subject {constructor() {this.observers [];}subscribe(observer) {this.observers.push(observer);}unsubscribe(observer) {this.observers this.observers.filter(obs obs ! observer);}notify(data) {this.observers.forEach(observer observer.update(data));}
}class Observer {update(data) {// 处理更新通知的逻辑console.log(Received data:, data);}
}// 创建主题和观察者
const subject new Subject();
const observer1 new Observer();
const observer2 new Observer();// 订阅观察者
subject.subscribe(observer1);
subject.subscribe(observer2);// 发送通知
subject.notify(Hello observers!);这些是一些简单的代码示例来说明各
40、这三种的区别和优点缺点是什么
对于单例模式、工厂模式和观察者模式它们有不同的应用场景、特点和优缺点 单例模式Singleton Pattern 特点确保一个类只有一个实例并提供全局访问点。优点 提供了全局访问点方便在程序中的任何地方访问该实例。节省了系统资源因为只有一个实例存在。 缺点 单例模式使用全局变量增加了程序的耦合度。可能会导致单例对象过于庞大承担了太多的职责。 工厂模式Factory Pattern 特点通过一个工厂方法创建对象而无需直接使用 new 关键字。优点 封装了对象的创建过程客户端代码与具体的产品类解耦。可以根据需求灵活地创建不同类型的对象。 缺点 增加了系统复杂性需要额外编写工厂类和产品类。当需要创建多个相关产品时工厂模式的代码会变得复杂。 观察者模式Observer Pattern 特点定义一对多的依赖关系当一个对象主题状态发生改变时其所有依赖观察者将自动更新。优点 解耦了主题和观察者之间的关系使得它们可以独立变化。支持广播通信机制一个主题可以同时通知多个观察者。 缺点 如果有大量的观察者可能会影响程序性能和内存消耗。观察者和主题之间可能存在循环引用。
选择适当的设计模式取决于具体的需求和上下文。单例模式适用于需要全局访问且实例唯一的情况工厂模式适用于创建对象的场景而观察者模式适用于对象间存在一对多的关系当一个对象状态改变时需要通知其他相关对象。
41、后几种设计模式能用实例演示下吗
当然可以下面我将使用几个示例来演示工厂模式和观察者模式的实际应用。
工厂模式示例
# 工厂接口
class AnimalFactory:def create_animal(self):pass# 具体工厂狗工厂
class DogFactory(AnimalFactory):def create_animal(self):return Dog()# 具体工厂猫工厂
class CatFactory(AnimalFactory):def create_animal(self):return Cat()# 产品接口
class Animal:def sound(self):pass# 具体产品狗
class Dog(Animal):def sound(self):return 汪汪汪# 具体产品猫
class Cat(Animal):def sound(self):return 喵喵喵# 客户端调用
dog_factory DogFactory()
cat_factory CatFactory()dog dog_factory.create_animal()
print(dog.sound()) # 输出汪汪汪cat cat_factory.create_animal()
print(cat.sound()) # 输出喵喵喵在上述示例中我们定义了一个动物工厂接口和两个具体的动物工厂狗工厂和猫工厂每个具体工厂可以创建相应的动物实例狗和猫。客户端可以通过具体工厂创建对应的动物对象并调用其方法。
观察者模式示例
# 主题接口
class Subject:def attach(self, observer):passdef detach(self, observer):passdef notify(self):pass# 具体主题
class WeatherStation(Subject):def __init__(self):self.observers []self.temperature 0def attach(self, observer):self.observers.append(observer)def detach(self, observer):self.observers.remove(observer)def notify(self):for observer in self.observers:observer.update(self.temperature)def set_temperature(self, temperature):self.temperature temperatureself.notify()# 观察者接口
class Observer:def update(self, temperature):pass# 具体观察者
class User(Observer):def update(self, temperature):print(f当前温度更新为{temperature} 度)# 客户端调用
weather_station WeatherStation()user1 User()
user2 User()weather_station.attach(user1)
weather_station.attach(user2)weather_station.set_temperature(25)
# 输出
# 当前温度更新为25 度
# 当前温度更新为25 度weather_station.detach(user2)weather_station.set_temperature(30)
# 输出
# 当前温度更新为30 度在上述示例中我们创建了一个天气站主题每当温度发生变化时会通知所有的用户观察者。用户可以通过订阅主题来接收温度的更新信息。在示例中我们创建了两个用户观察者并通过主题订阅它们。当温度改变时用户观察者会接收到相应的更新通知。
当然可以下面我会为你分别介绍装饰者模式、适配器模式、策略模式和代理模式并提供相应的示例。
装饰者模式Decorator Pattern 装饰者模式允许向现有对象添加新功能同时又不改变其结构。它通过将对象放入包装器中从而为对象动态地添加更多的行为和责任。
示例
# 抽象组件接口
class Component:def operation(self):pass# 具体组件
class ConcreteComponent(Component):def operation(self):print(执行具体组件的操作)# 抽象装饰者
class Decorator(Component):def __init__(self, component):self.component componentdef operation(self):self.component.operation()# 具体装饰者A
class ConcreteDecoratorA(Decorator):def operation(self):super().operation()self.add_functionality()def add_functionality(self):print(添加额外功能A)# 具体装饰者B
class ConcreteDecoratorB(Decorator):def operation(self):super().operation()self.add_functionality()def add_functionality(self):print(添加额外功能B)# 客户端调用
component ConcreteComponent()
decoratorA ConcreteDecoratorA(component)
decoratorB ConcreteDecoratorB(decoratorA)decoratorB.operation()在上述示例中我们定义了抽象组件接口Component和具体组件ConcreteComponent。然后创建了抽象装饰者Decorator来包装组件并定义了具体的装饰者AConcreteDecoratorA和装饰者BConcreteDecoratorB来添加额外功能。最后在客户端调用时可以通过层层装饰来对组件进行功能扩展。
适配器模式Adapter Pattern 适配器模式将一个类的接口转换成客户端所期待的另一种接口。通常用于让原本不兼容的类能够一起工作。
示例
# 目标接口
class Target:def request(self):pass# 需要适配的类
class Adaptee:def specific_request(self):print(执行特殊请求)# 类适配器
class ClassAdapter(Target, Adaptee):def request(self):self.specific_request()# 对象适配器
class ObjectAdapter(Target):def __init__(self, adaptee):self.adaptee adapteedef request(self):self.adaptee.specific_request()# 客户端调用
class_adapter ClassAdapter()
class_adapter.request()adaptee Adaptee()
object_adapter ObjectAdapter(adaptee)
object_adapter.request()在上述示例中我们有一个目标接口Target需要使用该接口的客户端无法直接与需要适配的类Adaptee进行交互。为了使其能够一起工作我们创建了类适配器ClassAdapter和对象适配器ObjectAdapter。这两种
当然可以下面我将使用几个示例来演示策略模式和代理模式的实际应用。
策略模式示例
# 策略接口
class PaymentStrategy:def pay(self, amount):pass# 具体策略支付宝支付
class AlipayStrategy(PaymentStrategy):def pay(self, amount):return f使用支付宝支付 {amount} 元# 具体策略微信支付
class WechatPayStrategy(PaymentStrategy):def pay(self, amount):return f使用微信支付 {amount} 元# 上下文
class PaymentContext:def __init__(self, strategy):self.strategy strategydef set_strategy(self, strategy):self.strategy strategydef perform_payment(self, amount):return self.strategy.pay(amount)# 客户端调用
payment_context PaymentContext(AlipayStrategy())
print(payment_context.perform_payment(100)) # 输出使用支付宝支付 100 元payment_context.set_strategy(WechatPayStrategy())
print(payment_context.perform_payment(200)) # 输出使用微信支付 200 元在上述示例中我们定义了一个支付策略接口并实现了两种具体的支付策略支付宝支付和微信支付。上下文对象持有一个策略对象并根据不同的策略进行支付操作。客户端可以根据需要选择不同的支付策略并执行支付。
代理模式示例
# 主题接口
class Subject:def request(self):pass# 具体主题
class RealSubject(Subject):def request(self):print(真实主题的请求)# 代理
class Proxy(Subject):def __init__(self, real_subject):self.real_subject real_subjectdef request(self):# 在调用真实主题的请求之前或之后可以添加额外的逻辑print(代理处理请求)self.real_subject.request()print(代理请求处理结束)# 客户端调用
real_subject RealSubject()
proxy Proxy(real_subject)proxy.request()在上述示例中我们定义了一个主题接口和一个具体的真实主题。代理对象持有真实主题的引用并在执行真实主题的请求前后添加了额外的逻辑。客户端通过代理对象来间接地调用真实主题的请求代理在调用前后提供了额外的功能。
注意以上示例只是简化的代码片段没有展示完整的类结构和方法实现仅用于演示概念和原理。在实际应用中需要根据具体需求进行适当的设计和实现。
41、vuex 数据 刷新页面白屏的问题原因
刷新页面后出现白屏的问题通常与 Vuex 数据的初始化和恢复有关。下面是可能导致这种问题的几个原因 数据未进行持久化Vuex 存储的数据默认情况下是存储在内存中的当页面刷新时数据会丢失。如果你希望在刷新页面后仍然保留 Vuex 中的数据需要将数据进行持久化例如使用浏览器的 LocalStorage 或类似的技术将数据保存到本地并在页面加载时恢复数据。 缺少数据的恢复逻辑即使数据已经进行了持久化处理也需要在页面加载时恢复这些数据到 Vuex。可以通过在应用初始化阶段检查本地存储中是否存在数据并将其重新写入 Vuex 来实现数据的恢复。 异步操作的延迟加载如果在页面加载时存在一些异步操作例如获取远程数据或进行异步计算并且这些操作在完成之前会访问 Vuex 数据可能会导致白屏。为了解决这个问题你可以在异步操作完成后再渲染页面内容或在等待期间显示加载状态。
请注意以上仅是一些可能导致白屏问题的常见原因具体情况还需要根据你的应用程序架构和代码进行具体分析和调试。
42、webpack 链式列表
关于Webpack和链式列表的组合我不太清楚你具体指的是什么。Webpack是一个现代化的静态模块打包工具用于构建JavaScript应用程序。而链式列表是一种数据结构与Webpack的工作方式有一定的区别。
如果你想了解在Webpack中如何使用链式列表这可能并不直接相关。Webpack主要用于处理模块依赖关系、代码转换、资源优化等工作而链式列表是一种数据结构用于存储和组织数据元素。
然而在Webpack的配置文件中你可以使用链式风格的方法来指定各种加载器(loader)和插件(plugins)的处理顺序和配置选项。这是因为Webpack的配置文件采用了一种基于链式调用的API风格你可以按照链式的顺序添加各个配置项。
例如你可以通过使用.use()方法和.loader()方法来指定加载器的处理顺序并且每个加载器可以使用链式语法来进行配置。类似地你也可以使用.plugin()方法来添加各种插件并且可以根据需要进行链式调用和配置。
需要注意的是这里提到的链式调用和链式列表并非完全相同的概念。Webpack中的链式调用是指一种串联多个配置选项的方式而链式列表则是一种数据结构。
43、webpack 和 vite 的区别 和优缺点 和实现原理
Webpack和Vite都是用于构建现代化JavaScript应用程序的工具它们在一些方面有相似之处但也存在一些区别。 区别 构建方式Webpack是一个传统的静态模块打包器通过分析模块间的依赖关系将多个模块打包成一个或多个输出文件。而Vite采用了现代化的开发服务器原生ES模块的构建方式不需要将所有代码打包合并而是按需编译模块提供更快的开发启动和热更新效果。 开发环境Webpack在开发环境中通常需要使用热模块替换HMR来实现快速的热更新这会带来一定的性能损耗。而Vite利用浏览器原生支持的ES模块加载功能在开发环境下直接使用原生ES模块加载无需进行额外的转换和模块合并因此具有更快的热更新速度。 生产环境在生产环境下Webpack会将所有模块打包成一个或多个最终的输出文件可以进行代码压缩、优化等操作。而Vite则会根据引入模块的情况进行按需编译并生成适用于生产环境的代码以提供更好的性能和加载速度。 插件生态Webpack具有非常强大的插件系统能够通过各种插件扩展功能或优化构建过程。Vite也支持插件扩展但由于其较新的发展阶段插件生态相对较小。 优缺点 Webpack 优点 成熟稳定广泛应用于各类项目。强大的配置灵活性和丰富的插件生态。支持更多的文件类型和功能扩展。 缺点 配置复杂初学者上手难度较高。开发环境下热更新性能较差。 Vite 优点 快速的启动和热更新因为它使用原生ES模块加载。更简单的配置适合快速开发和原型验证。对现代JavaScript特性的原生支持。 缺点 相对较新插件生态相对较小。不适用于需要传统构建方式的复杂项目。 实现原理 Webpack实现原理Webpack基于构建流程和配置文件通过解析模块间的依赖关系生成依赖关系图然后根据配置进行代码转换、文件分割、资源优化等操作最终生成目标代码块。 Vite实现原理Vite利用了浏览器原生支持的ES模
44、这两种工具 实现热更新的原理是什么
Webpack和Vite是两种常用的前端构建工具它们在实现热更新Hot Module Replacement的原理上有所区别。
Webpack的热更新原理是通过Webpack Dev Server来实现的。当启动Webpack Dev Server时它会在内存中构建并存储整个项目的资源。在开发过程中当文件发生改变时Webpack只会重新构建发生变化的模块并将更新后的模块代码发送到浏览器端。浏览器接收到更新的模块代码后使用HMR Runtime将新模块与旧模块进行比较找出差异并将差异应用到页面中实现实时更新显示。
相比之下Vite采用了一种基于原生ES模块的开发模式利用了现代浏览器对ES模块的原生支持。当项目启动时Vite会创建一个轻量级的本地服务器同时会记录模块之间的依赖关系。在开发过程中当文件发生改变时Vite会重新解析受影响的模块并向浏览器推送这些更新的模块浏览器根据模块的导入关系请求新的模块并在接收到新的模块后立即执行替换从而实现快速的热更新。
从优点和缺点来看Webpack的优点是功能强大且成熟稳定支持各种高级特性和插件但配置复杂打包速度相对较慢。而Vite的优点是启动快、开发阶段的响应速度更快适用于中小型项目但对一些高级特性如代码分割的支持可能不够完善。
总结来说Webpack在构建过程中将所有资源打包处理而Vite借助现代浏览器原生ES模块的支持在开发过程中能够实现更快的模块更新。选择使用哪种工具可以根据项目需求和个人偏好来决定。
45、vite 和webpack 热更新原理 哪里不同导致了vite 更快
Vite和Webpack在热更新原理上有一些不同之处这也是导致Vite更快的主要原因之一。
Webpack的热更新机制是通过使用webpack-dev-server或webpack-hot-middleware来实现的。当文件发生变化时Webpack会重新编译整个模块并将新的模块通过WebSocket推送到浏览器端然后浏览器进行页面的刷新。这意味着每次修改都需要重新编译整个模块即使只有一个文件发生了变化。这对于大型项目来说可能会导致性能下降因为重新编译整个模块需要花费较长的时间。
而Vite采用了一种全新的热更新方式。它利用ES模块的特性在开发服务器启动时会先解析所有的模块依赖关系然后根据依赖关系创建一个虚拟的依赖图。当文件发生变化时Vite只需要重新编译被修改的文件以及与之相关的依赖模块而不需要重新构建整个应用。这样就能够极大地提高热更新的速度因为只有少量的文件需要重新编译和刷新而不是整个项目。
此外Vite还利用了浏览器的原生ES模块加载功能通过在开发过程中直接在浏览器中加载原始的ES模块而不是将它们打包成一个或多个文件。这消除了打包和构建过程中的繁琐步骤使得开发过程更加轻量级和高效。
综上所述Vite相比Webpack在热更新方面更快速主要是因为它只重新编译被修改的文件和相关依赖模块而不需要重新构建整个项目并且利用浏览器的原生ES模块加载功能提供了更高效的开发体验。
46、这两种工具 实现热更新的原理是什么
Webpack和Vite是两种常用的前端构建工具它们在实现热更新Hot Module Replacement的原理上有所区别。
Webpack的热更新原理是通过Webpack Dev Server来实现的。当启动Webpack Dev Server时它会在内存中构建并存储整个项目的资源。在开发过程中当文件发生改变时Webpack只会重新构建发生变化的模块并将更新后的模块代码发送到浏览器端。浏览器接收到更新的模块代码后使用HMR Runtime将新模块与旧模块进行比较找出差异并将差异应用到页面中实现实时更新显示。
相比之下Vite采用了一种基于原生ES模块的开发模式利用了现代浏览器对ES模块的原生支持。当项目启动时Vite会创建一个轻量级的本地服务器同时会记录模块之间的依赖关系。在开发过程中当文件发生改变时Vite会重新解析受影响的模块并向浏览器推送这些更新的模块浏览器根据模块的导入关系请求新的模块并在接收到新的模块后立即执行替换从而实现快速的热更新。
从优点和缺点来看Webpack的优点是功能强大且成熟稳定支持各种高级特性和插件但配置复杂打包速度相对较慢。而Vite的优点是启动快、开发阶段的响应速度更快适用于中小型项目但对一些高级特性如代码分割的支持可能不够完善。
总结来说Webpack在构建过程中将所有资源打包处理而Vite借助现代浏览器原生ES模块的支持在开发过程中能够实现更快的模块更新。选择使用哪种工具可以根据项目需求和个人偏好来决定。
47、JavaScript中对象调用自身的原型方法
//创建构造函数
function Foo(){ }//添加公有方法即添加原型方法Foo.prototype.bar function(){alert(hello);}创建Foo的实例后实例可以直接调用原型方法 .bar()但是对象Foo不能直接调用原型方法.bar()。
var f new Foo(hello);f.bar();// helloFoo.bar(); //undefined让Foo对象调用.bar 方法在中间加上prototype 即可。
Foo.prototype.bar(); // hello例子
var a {};
var obj {add1(){this.aa true;},add2() {this.bb true;}
};console.log(obj.aa) // undefined
console.log(obj.bb) // undefined
console.log(a.aa) // undefined
console.log(a.bb) // undefineda._proto_ obj;
a._proto_.add1();
a._proto_.add2();console.log(obj.aa) // true
console.log(obj.bb) // true
console.log(a.aa) // undefined
console.log(a.bb) // undefined
48、有一个数组数组里面存放着n个promise函数要求这些promise函数顺序执行。
调试数据arr:
function a(){return new Promise((resolve,reject){resolve(1);})
}
function b(){return new Promise((resolve,reject){resolve(2);})
}
function c(){return new Promise((resolve,reject){resolve(3);})
}
function d(){return new Promise((resolve,reject){resolve(4);})
}
const arr [a,b,c,d];
1、asyncawait 方法
async function doPromise(arr) {for (let i 0; i arr.length; i) {let res await arr[i]();console.log(res);}
}
doPromise(arr);
2、递归方法
function doPromise(arr) {function doing(i){if(iarr.length) return;arr[i]().then(res{console.log(res);doing(i);})}doing(0);
49、浏览器回流与重绘
1、浏览器的渲染过程 解析HTML生成DOM树解析CSS生成CSSOM树将DOM树和CSSOM树结合生成渲染树(Render Tree)Layout(回流):根据生成的渲染树进行回流(Layout)得到节点的几何信息位置大小Painting(重绘):根据渲染树以及回流得到的几何信息得到节点的绝对像素Display:将像素发送给GPU展示在页面上。
1.1 生成渲染树
为了构建渲染树浏览器主要完成了以下工作
1、从DOM树的根节点开始遍历每个可见节点。 2、对于每个可见的节点找到CSSOM树中对应的规则并应用它们。 3、根据每个可见节点以及其对应的样式组合生成渲染树。 不可见的节点包括
一些不会渲染输出的节点比如script、meta、link等。一些通过css进行隐藏的节点。比如display:none。
注意:
利用visibility和opacity隐藏的节点还是会显示在渲染树上的。只有display:none的节点才不会显示在渲染树上。渲染树只包含可见的节点
1.2 回流
当渲染树render Tree中的一部分(或全部)因为元素的规模尺寸布局隐藏等改变而需要重新构建。这个过程就称为回流reflow也就是重新布局relayout。
1.3 重绘
当render tree中的一些元素需要更新属性而这些属性只是影响元素的外观风格而不会影响布局的比如 background-color 。则就叫称为重绘。
2、何时发生回流重绘
回流阶段需要计算节点的位置和几何信息当页面布局和几何信息发生变化的时候就需要回流。 添加或删除可见的DOM元素 元素的位置发生变化 元素的尺寸发生变化包括外边距、内边框、边框大小、高度和宽度等 内容发生变化比如文本变化或图片被另一个不同尺寸的图片所替代。 页面一开始渲染的时候这肯定避免不了 浏览器的窗口尺寸变化因为回流是根据视口的大小来计算元素的位置和大小的
回流必定会发生重绘重绘不一定会引发回流。重绘和回流会在我们设置节点样式时频繁出现同时也会很大程度上影响性能。回流所需的成本比重绘高的多改变父节点里的子节点很可能会导致父节点的一系列回流。根据改变的范围和程度渲染树中或多或少的部分节点需要重新计算有些改变还会触发整个页面的重排。
3、浏览器的优化机制
由于每次重排都会造成额外的计算消耗因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里直到过了一段时间或者操作达到了一个阈值才清空队列。但是当你获取布局信息的操作的时候会强制队列刷新比如当你访问以下属性或者使用以下方法
offsetTop、offsetLeft、offsetWidth、offsetHeight scrollTop、scrollLeft、scrollWidth、scrollHeight clientTop、clientLeft、clientWidth、clientHeight getComputedStyle() getBoundingClientRect 以上属性和方法都需要返回最新的布局信息因此浏览器不得不清空队列触发回流重绘来返回正确的值。因此我们在修改样式的时候最好避免使用上面列出的属性他们都会刷新渲染队列。
4、减少回流和重绘
4.1 最小化重绘和重排 避免频繁操作样式合并多次对DOM和样式的修改。
一次性重写style属性 使用cssText取代多次修改样式 将样式定义为class一次性修改class属性
4.2 脱离文档流批量修改DOM 当我们需要对DOM对一系列修改的时候可以通过以下步骤减少回流重绘次数
使元素脱离文档流 对其进行多次修改 将元素带回到文档中。 该过程的第一步和第三步可能会引起回流但是经过第一步之后对DOM的所有修改都不会引起回流因为它已经不在渲染树了。
有三种方式可以让DOM脱离文档流
隐藏元素应用修改重新显示这会在展示和隐藏节点的时候产生两次重绘。 使用文档片段(document fragment)在当前DOM之外构建一个子树应用修改再把它拷贝回文档。 将原始元素拷贝到一个脱离文档的节点中修改节点后再替换原始的元素。
4.3 避免频繁读取会引发回流/重绘的属性 当我们访问元素的一些属性的时候会导致浏览器强制清空队列进行强制同步布局。需要多次使用的属性使用变量缓存起来避免直接读取。
4.4 对于复杂动画元素使用绝对定位让其脱离文档流 对于复杂动画效果由于会经常的引起回流重绘因此我们可以使用绝对定位让它脱离文档流。否则会引起父元素以及后续元素频繁的回流。
4.5 CSS3硬件加速GPU加速 使用css3硬件加速可以让transform、opacity、filters这些动画不会引起回流重绘 。但是对于动画的其它属性比如background-color这些还是会引起回流重绘的不过它还是可以提升这些动画的性能。
常见的触发硬件加速的css属性
transform opacity filter Will-change css3硬件加速的缺点
如果你为太多元素使用css3硬件加速会导致内存占用较大会有性能问题。 在GPU渲染字体会导致抗锯齿无效。这是因为GPU和CPU的算法不同。因此如果你不在动画结束的时候关闭硬件加速会产生字体模糊。
4.6 其他细节 使用CSS动画取代直接修改元素的位置或大小等。 使用 visibility 替换 display: none 因为前者只会引起重绘后者会引发回流改变了布局 避免在循环中批量修改元素样式。 避免使用table布局可能很小的一个小改动会造成整个 table 的重新布局。 JS动画中使用requestAnimationFrame取代setTimeout和setInterval。 CSS 选择符从右往左匹配查找避免节点层级过多 将频繁重绘或者回流的节点设置为图层图层能够阻止该节点的渲染行为影响别的节点。比如 video、canvas 标签浏览器会自动将该节点变为图层 尽可能在DOM树的最末端改变class。 避免设置多层内联样式。 将动画效果应用到position属性为absolute或fixed的元素上。 避免使用CSS表达式例如calc()。 requestAnimationFrame 比起 setTimeout、setInterval的优势主要有两点
requestAnimationFrame 会把每一帧中的所有DOM操作集中起来在一次重绘或回流中就完成并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率一般来说这个频率为每秒60帧。 在隐藏或不可见的元素中requestAnimationFrame将不会进行重绘或回流这当然就意味着更少的的cpugpu和内存使用量。 停止requestAnimationFrame
是否有类似clearTimeout和clearInterval这样的类似方法有。cancelAnimationFrame()接收一个参数 requestAnimationFrame默认返回一个idcancelAnimationFrame只需要传入这个id就可以停止了。
50、vue中ref属性使用详情介绍
在Vue中ref是一个特殊的属性用于获取组件实例或DOM元素的引用。通过在标签上添加ref属性可以在Vue组件内部使用该元素或组件。
1、获取DOM元素的引用
在普通HTML元素上使用ref可以获取该元素的引用。比如在以下代码中我们给一个input元素添加了ref属性并在Vue组件内部使用它
templatedivinput typetext refmyInput/div
/template
script
export default {mounted() {// 使用this.$refs获取DOM元素的引用this.$refs.myInput.focus();}
}
/script通过this.$refs.myInput获取该元素的引用并调用focus方法使其聚焦。2、获取子组件的引用
在Vue组件中使用ref可以获取子组件的引用从而可以在父组件内部调用子组件的方法或访问其数据。
templatedivchild refmyChild/child/div
/templatescript
import Child from ./Child.vue;
export default {components: { Child },mounted() {// 使用this.$refs获取子组件的引用this.$refs.myChild.sayHello();}
}
/script在父组件中将子组件引入并在子组件标签上添加ref属性然后就可以通过this.$refs.myChild获取子组件的引用在父组件内部调用子组件的sayHello方法。3、动态添加/移除ref
在Vue中ref也可以动态添加/移除。在以下代码中我们使用v-for指令循环渲染一个input元素数组并为每个元素添加一个ref然后点击按钮时移除第二个input元素的ref。
templatedivdiv v-for(item, index) in items :keyindexinput typetext :refmyInput index/divbutton clickremoveRef移除Ref/button/div
/templatescript
export default {data() {return {items: [A, B, C]}},methods: {removeRef() {// 移除第二个input元素的refthis.$refs[myInput1].forEach(elem elem.removeAttribute(ref));}}
}
/script通过$refs[‘myInput’ index]动态添加ref再通过removeAttribute方法移除第二个input元素的ref。51、js ||与|、与的区别介绍
1.情景展示
在js当中|||分别代表什么意思有什么区别
2.||
和||这个两个运算符叫做逻辑运算符
左右两侧为true条件都成立时返回true否则返回false也就是两真则真一假则假。
||左右两侧有一个为true条件一个成立即可时返回true
只有左右两侧都为false时才返回false也就是一真则真两假则假。
在js中还有一种特殊的存在那就是undefined。
我们在js当中从页面取值或者调用js对象的时候经常会遇到undefined有这样的需求
当第一个值为undefined时我们让它取第二个值当第一个有值时我们只取第一个值如何实现
通过逻辑运算符||可以实现。
性能速度方面的特点
逻辑运算符 || 中如果的第一个运算数是false就不再考虑第二个运算数直接返回false
如果||的第一个运算数是true也不再考虑第二个运算数直接返回true。
3.|
和|这个两个运算符叫做逐位运算符也就是用来进行位运算的即一位一位地进行比较平常根本用不到顶多也就是加减乘除这种普通的运算操作
特点对两个二进制操作数逐位进行比较如果不是二进制会将其转换成二进制再进行逐计算
由于js是弱类型语言所以它俩也可以进行“逻辑运算”当用和|进行逻辑运算时实际上true被转换成1false被转换成0再进行逐位运算
即逻辑逐位运算的本质是逐位运算。
逻辑逐位运算逐位运算运算结果逻辑逐位运算符’’truetrue111truefalse100falsetrue010falsefalse000逻辑逐位运算符’|’true|true1|11true|false1|01false|true0|11false|false0|00
运算示例
|运算示例
性能速度方面的特点
和|运算符它们总是要比较两个运算数才得出结果因而性能上和||会比和|好。
总而言之就是可以使用和||的地方不要用和|。
另外’|位运算符还可以对小数进行取整操作具体格式为
小数|0详细用法介绍
当小数为正数时向下取整相当于Math.floor()
当小数为负数时向上取整相当于Math.ceil()
window.onload function(){console.log(99.9999|0);console.log(Math.floor(99.9999));console.log(-99.9999|0);console.log(Math.ceil(-99.9999));
}52、js面向对象怎么理解
一、认识对象。
首先要认识对象在编程中对象(object)是具体的某一个实例唯一的某一个个体。如电脑就是一个统称而你面前的这一台电脑就是对象。而电脑的统称在编程中叫做类概念就是具有一类相同特征事物的抽象概念。在js中只有对象的概念。没有类的概念在ECMA6中必须加回来。
对象的语法(创建对象)
var obj1 new Object();var obj2 Object();var obj3 {};二 、在js中面向对象的编程思想是什么呢
直接将生活逻辑映射到我们的编程中。
1.分析应用题里的实体。2.分析实体的属性和性能。3.实体之间相互作用得出结果。
三、对象既可以存储数据又可以存储函数的结构。
四、用面向对象创建一个人的方法
var person new Object(); person.name blue; person.sex 男; person.showName function(){ alert(我的名字叫: this.name); } person.showSex function(){ alert(我的性别是 this.sex 的); }
一下是封装函数创造一个人也叫作使用new构造函数本质上是构造对象在JS中的构造函数充当的角色是类的存在。
function createPerson(name, sex){var obj new Object();obj.name name;obj.sex sex;obj.showName function(){alert(我的名字叫 this.name);}obj.showSex function(){alert(我的性别是 this.sex 的);}return obj;
}var p1 createPerson(blue, 男);五、继承和多态
在JS中一切皆对象。
继承从父一级继承到的属性和方法。 1. 原型链继承 继承方法for(funcName in Person.prototype){Worker.prototype[funcName] Person.prototype[funcName];}在任何函数上都有一个叫做prototype原型的属性他是一个对象如果我们将函数添加给某一个可以创建对象的函数的原型prototype该函数共享。2.Object.create 根据原对象生成一个新对象Worker.prototype Object.create(Person.prototype);3、直接通过调用构造函数继承worker.prototype new Person();多态我自己拓展和重写的属性和方法。
function Person(name, sex){this.name name;this.sex sex;
}Person.prototype.showName function(){alert(我的名字是 this.name);
}
Person.prototype.showSex function(){alert(我的性别是 this.sex);
}function Worker(name, sex, job){Person.call(this, name, sex);this.job job;
}
Worker.prototype.showJob function(){alert(我的工作是 this.job);
}
var w1 new Worker(red, 女, 画家);
w1.showName();
w1.showSex();
w1.showJob();六、满足继承、封装、多态的特点叫做面向对象。
理解起来就是面向对象是一种编程思想而我们要进行面向对象编程就必须用到类(ECMA6之前没有类用构造函数充当类的存在)和对象语法实现最终实现的面向对象的代码拥有继承、封装、多态的特点。
53、vue父子组件传值之 p a r e n t 、 parent、 parent、children、$refs
使用场景
$refs父组件访问子组件 如果在普通的DOM元素上使用引用指向的是DOM元素如果用在子组件上引用的是组件实例 $parent子组件访问父组件$children父组件访问子组件
示例一$parent 子组件获取父组件的数据、调用父组件的方法
父组件
templatediv classaboutMessageBox ref mes classMessageBox/h2父组件-表格/h2el-table:datatableDatastylewidth: 100%el-table-columnpropnamelabel姓名/el-table-columnel-table-columnpropdesclabel内容/el-table-columnel-table-columnproptimelabel时间/el-table-column/el-table/div
/templatescript
import MessageBox from ../components/message.vue;
export default {data() {return {tableData: [],};},methods: {fun() {console.log(父组件的方法)}},components: {MessageBox},
};
/script
style scoped langscss
.about{width: 50vw;margin: auto;
}
/style
子组件
templatediv classmessage-containerh2子组件-留言板信息输入/h2el-form refform :modelform label-width80pxel-form-item label姓名el-input v-modelform.name clearable /el-input/el-form-itemel-form-item label内容el-input typetextarea v-modelform.desc/el-input/el-form-itemel-button typeprimary clickaddMes()提 交/el-button/el-form/div
/template
script
export default {name:message,data() {return {form:{name:,desc:,}};},methods: {addMes(){const obj Object.assign(this.form,{time:this.getNowTime()});this.$parent.tableData.push(obj);// 获取父组件的数据// this.$parent.tableData;// 调用父组件的方法// this.$parent.fun();},// 获取当前时间getNowTime() {let dtime new Date();let nian dtime.getFullYear(),yue dtime.getMonth() 1,day dtime.getDate(),hours dtime.getHours(),fen dtime.getMinutes(),miao dtime.getSeconds();return nian - yue - day hours : fen : miao;}},components: {}
}
/script示例二$children 父组件获取子组件的数据、调用子组件的方法
父组件
templatediv classaboutMessageBox ref mes classMessageBox/h2父组件-表格/h2el-button typeprimary clickaddMes()提 交/el-buttonel-table:datatableDatastylewidth: 100%el-table-columnpropnamelabel姓名/el-table-columnel-table-columnpropdesclabel内容/el-table-columnel-table-columnproptimelabel时间/el-table-column/el-table/div
/templatescript
import MessageBox from ../components/message.vue;
export default {data() {return { tableData: [],};},methods: {addMes() {// 获取子组件的数据const child_val this.$children[0].form;const obj Object.assign(child_val,{time:this.getNowTime()});this.tableData.push(obj);// 调用子组件的方法this.$children[0].fun();},// 获取当前时间getNowTime() {let dtime new Date();let nian dtime.getFullYear(),yue dtime.getMonth() 1,day dtime.getDate(),hours dtime.getHours(),fen dtime.getMinutes(),miao dtime.getSeconds();return nian - yue - day hours : fen : miao;},},components: {MessageBox},
};
/script
style scoped langscss
.about{width: 50vw;margin: auto;
}
/style
子组件
templatediv classmessage-containerh2子组件-留言板信息输入/h2el-form refform :modelform label-width80pxel-form-item label姓名el-input v-modelform.name clearable /el-input/el-form-itemel-form-item label内容el-input typetextarea v-modelform.desc/el-input/el-form-item/el-form/div
/template
script
export default {name:message,data() {return {form:{name:,desc:,}, };},methods: {fun() {console.log(子组件的方法)}},components: {}
}
/script示例三$refs 父组件获取子组件的数据、调用子组件的方法
MessageBox ref mes classMessageBox/// 获取子组件的数据
const child_val this.$refs.mes.form;// 调用子组件的方法
this.$refs.mes.fun();r e f 的 用 法 与 ref的用法与 ref的用法与children的方法相同只需要将 children[0] 换成 $refs.mes 即可。
54、前端发请求阶段如何进行优化:
前端发起请求的优化可以从以下几个方面考虑
减少请求次数可以将多个小的请求合并为一个大的请求减少网络开销。可以使用 HTTP/2 的多路复用特性或者使用资源打包工具如Webpack进行合并。压缩文件大小使用压缩算法对传输的文件进行压缩减少文件大小。可以使用Gzip或者Brotli压缩算法进行压缩。缓存策略合理设置缓存策略避免重复请求相同的资源。可以通过设置Cache-Control、Expires等HTTP头来控制缓存。使用CDN加速将静态资源部署到CDN上利用就近访问提高请求速度。异步加载将页面中不需要立即显示的内容通过异步加载的方式延迟加载提高页面的首屏加载速度。懒加载对于图片、视频等资源可以使用懒加载技术延迟加载当用户需要访问时再进行加载。预加载对于即将访问的页面或资源可以使用预加载技术在空闲时间提前加载。使用HTTP/2HTTP/2引入了多路复用、头部压缩等技术提高了请求的效率。
跨域的解决方法
1jsonp的认识
2cores的认识
跨域是指在浏览器中当前网页所在的域与请求的资源所在的域不一致。由于浏览器的同源策略限制跨域请求通常会被浏览器拦截导致请求失败。
解决跨域问题的常见方法有
JSONPJSON with PaddingJSONP是一种利用
55、跨域请求被拦截后怎样判断请求是否到达后端
跨域请求被拦截后前端可以通过查看响应头中的Access-Control-Allow-Origin字段来判断请求是否成功到达后端。如果该字段的值是前端请求的域名或者通配符*则说明请求成功到达后端。如果该字段的值是其他域名则说明请求被拦截。另外前端还可以通过查看响应状态码来判断请求是否成功到达后端常见的成功状态码是200。
预检请求options
预检请求是指浏览器在发送跨域请求之前会先发送一个OPTIONS请求用于获取服务器对跨域请求的支持情况。预检请求的目的是为了保护服务器避免非法跨域请求对服务器造成安全风险。
在预检请求中浏览器会发送一个OPTIONS请求并携带一些预检请求头如Access-Control-Request-Method和Access-Control-Request-Headers。服务器收到预检请求后会根据这些头部信息来判断是否允许跨域请求。
如果服务器允许跨域请求会返回一个带有Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers等响应头的响应。浏览器收到响应后才会继续发送真正的跨域请求。
如果服务器不允许跨域请求或者没有正确配置预检请求的响应头浏览器就会拦截跨域请求并抛出一个错误。
总结起来预检请求是为了确认服务器是否允许跨域请求并且提供了一种机制来防止非法跨域请求对服务器造成风险
56、ajax中如何实现isonp跨域 说说原生js实现方法
在原生的AJAX中实现JSONP跨域可以通过以下几个步骤
创建一个script标签并设置其src属性为目标接口的URL同时传入一个回调函数名作为参数例如script srchttp://example.com/api?callbackcallbackFunc/script。回调函数名是前端定义的一个函数用于处理从服务器返回的数据。在前端代码中定义一个与回调函数名相同的函数该函数作为回调函数将在服务器返回数据时被调用。例如function callbackFunc(data) { console.log(data); }。服务器接收到请求后会将数据作为参数传入回调函数并将回调函数作为响应内容返回给前端。例如callbackFunc({name: John, age: 25})。前端在接收到响应后会自动执行回调函数并可以在其中处理返回的数据。
这种方法利用了script标签不受同源策略限制的特性通过动态创建script标签来实现跨域请求并利用回调函数来处理返回的数据。但需要注意的是JSONP只支持GET请求且返回的数据必须是经过特定格式包装的JavaScript代码需要服务器端的支持。
57、iframe跨域操作怎么实现的
在前端中使用iframe进行跨域操作可以通过以下几种方式实现
window.postMessage方法可以在父窗口和子窗口之间进行跨文档通信。通过在父窗口中使用postMessage方法向子窗口发送消息子窗口中监听message事件来接收消息并做相应的处理。location.hash方法可以通过改变子窗口的hash值来传递数据。父窗口通过设置子窗口的src属性将要传递的数据添加到hash值中然后子窗口通过监听hashchange事件来获取传递的数据。document.domain方法只适用于主域相同子域不同的情况。通过将父窗口和子窗口的document.domain属性设置为相同的主域可以实现跨域操作。注意该方法只适用于主域相同的情况不能解决不同主域之间的跨域问题。window.name方法可以通过将数据存储在window.name属性中来进行跨域传递。父窗口将要传递的数据存储在window.name属性中然后子窗口再次加载时可以获取到该数据。
这些方法都是基于浏览器的同源策略下的漏洞或者特殊机制来实现跨域操作的需要注意使用时的安全性和兼容性问题。
58、考察的是一个表单提交如何防止用户多次提交减少服务器端的压力
可以通过以下几种方法来防止用户多次提交表单减少服务器端的压力
前端禁用提交按钮在用户点击提交按钮后立即禁用按钮防止用户多次点击。可以通过设置按钮的disabled属性为true来实现禁用。前端显示加载状态在用户点击提交按钮后可以在页面上显示一个加载状态告知用户正在提交并等待响应。可以通过添加一个loading图标或者文本提示来实现。后端生成唯一标识在用户第一次提交表单时后端生成一个唯一标识并将其存储在session或者cookie中。然后将该唯一标识返回给前端并在表单中添加一个隐藏字段将唯一标识作为值。当用户再次提交表单时前端会将隐藏字段的值传递给后端后端根据唯一标识判断是否已经处理过该请求。前端倒计时在用户点击提交按钮后可以设置一个倒计时在倒计时结束之前禁止用户再次点击。可以使用JavaScript的定时器来实现倒计时并在倒计时结束后重新启用提交按钮。后端幂等性处理在后端接收到表单提交请求时可以进行幂等性处理。即判断是否已经处理过该请求如果已经处理过则直接返回之前的处理结果不再重复处理。
以上方法可以结合使用根据具体的场景和需求选择合适的防止用户多次提交表单的方法。
59、创建10个a标签点击弹出它是第几个
您可以使用JavaScript来创建10个a标签并为每个a标签添加点击事件。在点击事件中可以通过event.target获取当前点击的a标签元素并通过父元素的childNodes属性获取其在父元素中的索引位置从而得到它是第几个a标签。
以下是示例代码
// 创建父元素
var parentElement document.createElement(div);// 创建10个a标签并添加点击事件
for (var i 0; i 10; i) {var a document.createElement(a);a.innerHTML 点击我;a.addEventListener(click, function(event) {var index Array.prototype.indexOf.call(event.target.parentNode.childNodes, event.target);alert(我是第 (index 1) 个a标签);});parentElement.appendChild(a);
}// 将父元素添加到页面中
document.body.appendChild(parentElement);以上代码会创建一个包含10个a标签的父元素并为每个a标签添加点击事件。当点击某个a标签时会弹出它是第几个a标签的提示信息。
60、左固定布局右侧自适应布局
左固定布局右侧自适应布局可以通过以下几种方式实现
使用CSS的float属性将左侧固定的元素设置为float:left右侧自适应的元素设置为width:100%。这样左侧元素会占据左侧的空间右侧元素会自动填充剩余的空间。
.left {float: left;width: 200px;
}.right {width: 100%;
}
div classleft左侧固定内容
/div
div classright右侧自适应内容
/div使用CSS的flexbox布局将父容器设置为display:flex并设置flex-direction为row。左侧固定的元素设置为固定宽度右侧自适应的元素设置为flex:1。
.container {display: flex;flex-direction: row;
}.left {width: 200px;
}.right {flex: 1;
}
div classcontainerdiv classleft/divdiv classright右侧自适应内容/div
/div使用CSS的grid布局将父容器设置为display:grid并使用grid-template-columns定义两列的宽度。左侧固定的元素设置为固定宽度右侧自适应的元素设置为auto。
.container {display: grid;grid-template-columns: 200px auto;
}.left {width: 200px;
}
div classcontainerdiv classleft左侧固定内容/divdiv classright右侧自适应内容/div
/div以上是几种常见的实现左固定布局右侧自适应布局的方法根据
61、题目给一个字符串循环输出每一个字符当输出到最后一个字符时再从头开始
思路根据打印情况猜想需要返回一个函数考点也就在闭包上当时还思考了迭代器、函数柯里化两个知识点
function loopString(s) {let i 0return function () {if (i s.length) i 0let res s[i]ireturn res}
}
const bar loopString(bar);
console.log(bar(), bar(), bar(), bar()); // b a r b
62、题目给一个数判断该数是否是斐波那契数列里的一项若是则返回第几项若不是则返回-1
思路考算法递归可能是最先被想到其实动态规划也简单我习惯使用只要维护两个数两数之和即是下一位数一直反复推出后面的数字即可
function findFibonacciIndex(n) {let a 1, b 1, sum 0, index 1while (sum n) {sum a ba bb sumindex}return sum n ? index : -1
}
console.log(findFibonacciIndex(5)); // 463、js 实现一个斐波那契数列 要求小于 给定的数字n
var arr [];
function getFbArry(n) {var a1,b1,sum0;while(sumn) {sum ab;ab;bsumarr.push(a) }var arrStr (1|arr.join(|)).split(|)return arrStr}什么是斐波那契数列
斐波那契数列是一个数列其中每个数字都是前两个数字之和。数列的前两个数字通常是0和1。从第三个数字开始每个数字都是前两个数字之和。例如斐波那契数列的前几个数字是0、1、1、2、3、5、8、13等。这个数列在数学和计算机科学中都有广泛的应用。
返回斐波那契数列的第几位的代码如下
在JavaScript中可以使用递归或迭代的方式实现斐波那契数列。
递归方式
function fibonacciRecursive(n) {if (n 1) {return n;}return fibonacciRecursive(n - 1) fibonacciRecursive(n - 2);
}console.log(fibonacciRecursive(10)); // 输出55迭代方式
function fibonacciIterative(n) {if (n 1) {return n;}let prev 0;let curr 1;for (let i 2; i n; i) {const temp curr;curr prev curr;prev temp;}return curr;
}console.log(fibonacciIterative(10)); // 输出55以上代码分别使用递归和迭代的方式实现了斐波那契数列。在递归方式中通过递归调用函数本身来计算斐波那契数列的值。在迭代方式中通过循环来计算斐波那契数列的值。两种方式都可以得到相同的结果但迭代方式通常比递归方式更高效。
64、题目实现链式的加减乘除myCalculator(1).add(12).minus(3).multi(10).div(5).getValue() 25
您可以定义一个名为myCalculator的函数该函数接受一个初始值作为参数。然后在函数中返回一个对象该对象具有add、minus、multi和div方法以及一个getValue方法。
在每个方法中您可以将当前值与传入的参数执行相应的操作并将结果保存在当前值中。最后在getValue方法中返回当前值。
以下是示例代码
function myCalculator(initialValue) {let value initialValue;return {add: function(num) {value num;return this;},minus: function(num) {value - num;return this;},multi: function(num) {value * num;return this;},div: function(num) {value / num;return this;},getValue: function() {return value;}};
}const result myCalculator(1).add(12).minus(3).multi(10).div(5).getValue();
console.log(result); // 输出25在上述代码中我们首先调用myCalculator函数并传入初始值1。然后使用链式调用的方式依次调用add、minus、multi和div方法并传入相应的参数。最后调用getValue方法获取最终的结果并将结果打印出来。运行代码后输出的结果为25表示链式的加减乘除操作得到了正确的结果。
思路链式按.调用方法联想到类的知识点因为实例调用类中的方法就是按.调用的调用完后再返回this也就相当于计算完后又把实例返回了可以继续调用方法
function myCalculator(val) {// todoclass Cal {constructor(val) {this.value val}getValue() {return this.value}// 加法add(newVal) {this.value this.value newValreturn this}// 减法minus(newVal) {this.value this.value - newValreturn this}// 乘法multi(newVal) {this.value this.value * newValreturn this}// 除法div(newVal) {this.value this.value / newValreturn this}}const obj new Cal(num)return obj
}
console.log(myCalculator(121).add(1).minus(2)); // 12064、题目搜索多个文件夹路径字符串数组例如[’/usr/bin’, ‘/etc/config’]查找是否有公共路径若有则返回公共路径字符串否则返回 null
思路类似于搜索字符串的公共前缀那么双循环就可以搞定但是得对数组处理一下因为路径里包含了 ‘/’ 需要除去所以返回答案时还要还原回来
function findParentDirectory(paths) {let arr []// 把 paths 处理为二维数组赋值给 arrpaths.forEach(ele {let a ele.split(/)a.shift()arr.push(a)});let res []for (let i 0; i arr[0].length; i) {let isTrue truefor (let j 0; j arr.length - 1; j) {if (arr[j][i] ! arr[j 1][i]) {isTrue falsebreak}}isTrue res.push(arr[0][i])}if (res.length) {return / res.join(/)} else {return null}
}
console.log(findParentDirectory([/home/usr/vue, /home/usr/react]));
// /home/usr解法二
可以使用字符串的公共前缀来查找是否有公共路径。首先将第一个文件夹路径作为初始的公共路径。然后依次遍历剩余的文件夹路径将当前公共路径与当前文件夹路径进行比较找出它们的公共前缀。如果公共前缀为空字符串则表示没有公共路径直接返回null。如果公共前缀不为空则将公共前缀更新为新的公共路径。最后返回最终的公共路径。以下是示例代码function findCommonPath(paths) {if (paths.length 0) {return null;}let commonPath paths[0];for (let i 1; i paths.length; i) {let j 0;while (j commonPath.length j paths[i].length commonPath[j] paths[i][j]) {j;}if (j 0) {return null;}commonPath commonPath.substring(0, j);}return commonPath;
}const paths [/usr/bin, /etc/config];
const result findCommonPath(paths);
console.log(result); // 输出/
在上述代码中我们将文件夹路径数组设置为[/usr/bin, /etc/config]然后调用findCommonPath函数来查找是否有公共路径。最后打印结果即可。运行代码后输出的结果为/表示存在公共路径/。65、题目若a * a b * b c * c则称’a,b,c’为勾股数找出比 n 小的所有勾股数
思路三循环暴力求解暂时没想到更好的方法先感谢各位观众给出更好的方法了评论区已有大佬给出一种双指针的解法了
function gougu(n) {let arr []// 最小的勾股数从 3 开始for (let a 3; a n; a) {for (let b a 1; b n; b) {for (let c b 1; c n; c) {if (a * a b * b c * c) {arr.push(${a},${b},${c})}}}}return arr
}
console.log(gougu(10)); // 可以使用嵌套循环来找出比 n 小的所有勾股数。首先定义一个空数组来存储符合条件的勾股数。然后使用两个循环遍历所有可能的 a 和 b 的取值范围从 1 到 n-1。在每次循环中计算 a * a b * b 的值并判断是否等于 c * c。如果相等则将 a、b 和 c 加入到数组中。最后返回数组即可。
以下是示例代码
def find_pythagorean_numbers(n):pythagorean_numbers []for a in range(1, n):for b in range(1, n):c (a * a b * b) ** 0.5if c.is_integer() and c n:pythagorean_numbers.append((a, b, int(c)))return pythagorean_numbersn 10
result find_pythagorean_numbers(n)
print(result)在上述代码中我们将 n 设置为 10然后调用 find_pythagorean_numbers 函数来找出比 10 小的所有勾股数。最后打印结果即可。运行代码后输出的结果为 [(3, 4, 5), (4, 3, 5), (6, 8, 10), (8, 6, 10)]这些都是比 10 小的勾股数。
66、将一串字符串驼峰化,第一个‘-’后的字母无需转小写为大写其他‘-’后的字母需转小写为大写
例1font-size转化为fontSize 例2-weront-size转化为werontSize
function ss(str) {if (str[0] -) str str.slice(1);// 加上这个可以保证第二个案例通过否则会将空字符当作为第一个单词let res str.split(-);var fist res[0];if (fist[0] -) fist fist.slice(1);let result fist;for (let i 1; i res.length; i) {let temp res[i][0].toUpperCase();let temp2 res[i].replace(res[i][0], temp);result temp2;}return result;
}console.log(ss(font-size));
console.log(ss(-weront-size));
解法二
您可以使用正则表达式和replace方法来将一串字符串驼峰化。首先使用replace方法将所有的连字符-替换为空格或其他字符。然后使用replace方法和正则表达式来将每个单词的首字母转换为大写并将其余字母转换为小写。最后使用replace方法将所有的空格或其他字符替换为空字符串。
以下是示例代码
function toCamelCase(str) {return str.replace(/-/g, ).replace(/(?:^\w|[A-Z]|\b\w)/g, function(letter, index) {return index 0 ? letter.toLowerCase() : letter.toUpperCase();}).replace(/\s/g, );
}const result toCamelCase(hello-world-example);
console.log(result); // 输出helloWorldExample在上述代码中我们将字符串设置为’hello-world-example’然后调用toCamelCase函数来将其驼峰化。最后打印结果即可。运行代码后输出的结果为’helloWorldExample’表示字符串已成功驼峰化。
67、一串字符串当字符[a-zA-Z]重复输出true否则输出false。
解法1
function ss(str) {str str.match(/[a-zA-Z]/g); //[ h, t, h ]console.log(str);for (let i 0; i str.length - 1; i) {if (str[i] str[i 1]) return true;}return false;
}
console.log(ss(hth000));
解法2
function ss(str) {return /([a-zA-Z])\1/g.test(str);
}console.log(ss(hth000));解法3
可以使用正则表达式和Set数据结构来判断字符串中的字符是否重复。首先使用正则表达式将字符串中的非字母字符替换为空字符串。然后将字符串中的每个字符添加到Set中如果Set的大小与字符串的长度不相等则表示字符串中有重复字符返回true否则返回false。
以下是示例代码
function isDuplicate(str) {const cleanStr str.replace(/[^a-zA-Z]/g, );const charSet new Set(cleanStr);return charSet.size ! cleanStr.length;
}console.log(isDuplicate(abc)); // 输出false
console.log(isDuplicate(abca)); // 输出true在上述代码中我们分别测试了字符串’abc’和’abca’。第一个字符串中没有重复字符所以返回false第二个字符串中有重复字符’a’所以返回true。运行代码后可以得到相应的结果
js 时间复杂度 和空间复杂度
在JavaScript中时间复杂度和空间复杂度是衡量算法性能的两个重要指标。
时间复杂度Time Complexity表示算法执行所需要的时间与问题规模之间的关系。常见的时间复杂度有
常数时间复杂度 O(1)无论问题规模的大小算法的执行时间都是固定的。线性时间复杂度 O(n)算法的执行时间与问题规模呈线性关系。对数时间复杂度 O(log n)算法的执行时间与问题规模的对数值呈线性关系。平方时间复杂度 O(n^2)算法的执行时间与问题规模的平方呈线性关系。指数时间复杂度 O(2^n)算法的执行时间与问题规模的指数值呈线性关系。
空间复杂度Space Complexity表示算法执行所需要的额外空间与问题规模之间的关系。常见的空间复杂度有
常数空间复杂度 O(1)算法执行所需要的额外空间是固定的与问题规模无关。线性空间复杂度 O(n)算法执行所需要的额外空间与问题规模呈线性关系。平方空间复杂度 O(n^2)算法执行所需要的额外空间与问题规模的平方呈线性关系。
要分析算法的时间复杂度和空间复杂度可以根据算法中的循环、递归、函数调用等操作来确定执行次数并根据问题规模的变化情况来确定复杂度的增长趋势。
需要注意的是时间复杂度和空间复杂度是描述算法性能的理论指标只是对算法的估计并不能精确地表示
v-for 和v-if为什么不能一块儿用
v-for和v-if不能一起使用的原因是由于它们的编译顺序不同。v-for是在v-if之前被解析和渲染的。
在Vue的编译过程中v-for的优先级比v-if要高。这意味着v-for会先被解析和渲染然后才会去判断v-if的条件。如果v-for中的列表为空或条件不满足那么v-if将无法生效因为v-for已经在此时已经渲染了元素。
为了避免这种问题可以将v-if放置在包裹v-for的父元素上或者使用计算属性来处理过滤后的列表。
以下是示例代码
templatedivdiv v-ifshouldShowListdiv v-foritem in filteredList :keyitem.id!-- 渲染列表项 --/div/div/div
/templatescript
export default {data() {return {list: [...], // 列表数据shouldShowList: true // 是否显示列表};},computed: {filteredList() {// 根据条件过滤列表数据return this.list.filter(item item.condition);}}
};
/script在上述代码中我们将v-if放置在包裹v-for的父元素上并使用计算属性filteredList来处理过滤后的列表数据。通过这样的方式可以确保v-if在v-for之前进行判断从而避免了两者冲突的问题。
什么时候使用闭包
闭包可以在以下情况下使用
保护变量闭包可以创建一个私有作用域将变量封装在内部函数中从而保护变量不受外部访问和修改。这在一些需要隐藏数据的场景下非常有用。记忆函数闭包可以记住函数的上下文和状态使得函数可以记住之前的操作和结果。这在一些需要缓存计算结果的场景下非常有用可以提高函数的性能。延长作用域链闭包可以在函数内部访问外部函数的变量从而延长作用域链。这在一些需要访问外部函数的变量的场景下非常有用。模块化开发通过使用闭包可以将代码封装在模块中避免全局变量的污染实现模块化开发。
需要注意的是使用闭包也会带来一些问题例如内存泄漏和性能问题。因此在使用闭包时需要注意内存管理和性能优化 // 输出55 ##### 迭代方式
function fibonacciIterative(n) { if (n 1) { return n; } let prev 0; let curr 1; for (let i 2; i n; i) { const temp curr; curr prev curr; prev temp; } return curr; }
console.log(fibonacciIterative(10)); // 输出55 以上代码分别使用递归和迭代的方式实现了斐波那契数列。在递归方式中通过递归调用函数本身来计算斐波那契数列的值。在迭代方式中通过循环来计算斐波那契数列的值。两种方式都可以得到相同的结果但迭代方式通常比递归方式更高效。#### 64、题目实现链式的加减乘除myCalculator(1).add(12).minus(3).multi(10).div(5).getValue() 25您可以定义一个名为myCalculator的函数该函数接受一个初始值作为参数。然后在函数中返回一个对象该对象具有add、minus、multi和div方法以及一个getValue方法。在每个方法中您可以将当前值与传入的参数执行相应的操作并将结果保存在当前值中。最后在getValue方法中返回当前值。以下是示例代码
function myCalculator(initialValue) { let value initialValue;
return { add: function(num) { value num; return this; }, minus: function(num) { value - num; return this; }, multi: function(num) { value * num; return this; }, div: function(num) { value / num; return this; }, getValue: function() { return value; } }; }
const result myCalculator(1).add(12).minus(3).multi(10).div(5).getValue(); console.log(result); // 输出25 在上述代码中我们首先调用myCalculator函数并传入初始值1。然后使用链式调用的方式依次调用add、minus、multi和div方法并传入相应的参数。最后调用getValue方法获取最终的结果并将结果打印出来。运行代码后输出的结果为25表示链式的加减乘除操作得到了正确的结果。思路链式按.调用方法联想到类的知识点因为实例调用类中的方法就是按.调用的调用完后再返回this也就相当于计算完后又把实例返回了可以继续调用方法
js
function myCalculator(val) {// todoclass Cal {constructor(val) {this.value val}getValue() {return this.value}// 加法add(newVal) {this.value this.value newValreturn this}// 减法minus(newVal) {this.value this.value - newValreturn this}// 乘法multi(newVal) {this.value this.value * newValreturn this}// 除法div(newVal) {this.value this.value / newValreturn this}}const obj new Cal(num)return obj
}
console.log(myCalculator(121).add(1).minus(2)); // 12064、题目搜索多个文件夹路径字符串数组例如[’/usr/bin’, ‘/etc/config’]查找是否有公共路径若有则返回公共路径字符串否则返回 null
思路类似于搜索字符串的公共前缀那么双循环就可以搞定但是得对数组处理一下因为路径里包含了 ‘/’ 需要除去所以返回答案时还要还原回来
function findParentDirectory(paths) {let arr []// 把 paths 处理为二维数组赋值给 arrpaths.forEach(ele {let a ele.split(/)a.shift()arr.push(a)});let res []for (let i 0; i arr[0].length; i) {let isTrue truefor (let j 0; j arr.length - 1; j) {if (arr[j][i] ! arr[j 1][i]) {isTrue falsebreak}}isTrue res.push(arr[0][i])}if (res.length) {return / res.join(/)} else {return null}
}
console.log(findParentDirectory([/home/usr/vue, /home/usr/react]));
// /home/usr解法二
可以使用字符串的公共前缀来查找是否有公共路径。首先将第一个文件夹路径作为初始的公共路径。然后依次遍历剩余的文件夹路径将当前公共路径与当前文件夹路径进行比较找出它们的公共前缀。如果公共前缀为空字符串则表示没有公共路径直接返回null。如果公共前缀不为空则将公共前缀更新为新的公共路径。最后返回最终的公共路径。以下是示例代码function findCommonPath(paths) {if (paths.length 0) {return null;}let commonPath paths[0];for (let i 1; i paths.length; i) {let j 0;while (j commonPath.length j paths[i].length commonPath[j] paths[i][j]) {j;}if (j 0) {return null;}commonPath commonPath.substring(0, j);}return commonPath;
}const paths [/usr/bin, /etc/config];
const result findCommonPath(paths);
console.log(result); // 输出/
在上述代码中我们将文件夹路径数组设置为[/usr/bin, /etc/config]然后调用findCommonPath函数来查找是否有公共路径。最后打印结果即可。运行代码后输出的结果为/表示存在公共路径/。65、题目若a * a b * b c * c则称’a,b,c’为勾股数找出比 n 小的所有勾股数
思路三循环暴力求解暂时没想到更好的方法先感谢各位观众给出更好的方法了评论区已有大佬给出一种双指针的解法了
function gougu(n) {let arr []// 最小的勾股数从 3 开始for (let a 3; a n; a) {for (let b a 1; b n; b) {for (let c b 1; c n; c) {if (a * a b * b c * c) {arr.push(${a},${b},${c})}}}}return arr
}
console.log(gougu(10)); // 可以使用嵌套循环来找出比 n 小的所有勾股数。首先定义一个空数组来存储符合条件的勾股数。然后使用两个循环遍历所有可能的 a 和 b 的取值范围从 1 到 n-1。在每次循环中计算 a * a b * b 的值并判断是否等于 c * c。如果相等则将 a、b 和 c 加入到数组中。最后返回数组即可。
以下是示例代码
def find_pythagorean_numbers(n):pythagorean_numbers []for a in range(1, n):for b in range(1, n):c (a * a b * b) ** 0.5if c.is_integer() and c n:pythagorean_numbers.append((a, b, int(c)))return pythagorean_numbersn 10
result find_pythagorean_numbers(n)
print(result)在上述代码中我们将 n 设置为 10然后调用 find_pythagorean_numbers 函数来找出比 10 小的所有勾股数。最后打印结果即可。运行代码后输出的结果为 [(3, 4, 5), (4, 3, 5), (6, 8, 10), (8, 6, 10)]这些都是比 10 小的勾股数。
66、将一串字符串驼峰化,第一个‘-’后的字母无需转小写为大写其他‘-’后的字母需转小写为大写
例1font-size转化为fontSize 例2-weront-size转化为werontSize
function ss(str) {if (str[0] -) str str.slice(1);// 加上这个可以保证第二个案例通过否则会将空字符当作为第一个单词let res str.split(-);var fist res[0];if (fist[0] -) fist fist.slice(1);let result fist;for (let i 1; i res.length; i) {let temp res[i][0].toUpperCase();let temp2 res[i].replace(res[i][0], temp);result temp2;}return result;
}console.log(ss(font-size));
console.log(ss(-weront-size));
解法二
您可以使用正则表达式和replace方法来将一串字符串驼峰化。首先使用replace方法将所有的连字符-替换为空格或其他字符。然后使用replace方法和正则表达式来将每个单词的首字母转换为大写并将其余字母转换为小写。最后使用replace方法将所有的空格或其他字符替换为空字符串。
以下是示例代码
function toCamelCase(str) {return str.replace(/-/g, ).replace(/(?:^\w|[A-Z]|\b\w)/g, function(letter, index) {return index 0 ? letter.toLowerCase() : letter.toUpperCase();}).replace(/\s/g, );
}const result toCamelCase(hello-world-example);
console.log(result); // 输出helloWorldExample在上述代码中我们将字符串设置为’hello-world-example’然后调用toCamelCase函数来将其驼峰化。最后打印结果即可。运行代码后输出的结果为’helloWorldExample’表示字符串已成功驼峰化。
67、一串字符串当字符[a-zA-Z]重复输出true否则输出false。
解法1
function ss(str) {str str.match(/[a-zA-Z]/g); //[ h, t, h ]console.log(str);for (let i 0; i str.length - 1; i) {if (str[i] str[i 1]) return true;}return false;
}
console.log(ss(hth000));
解法2
function ss(str) {return /([a-zA-Z])\1/g.test(str);
}console.log(ss(hth000));解法3
可以使用正则表达式和Set数据结构来判断字符串中的字符是否重复。首先使用正则表达式将字符串中的非字母字符替换为空字符串。然后将字符串中的每个字符添加到Set中如果Set的大小与字符串的长度不相等则表示字符串中有重复字符返回true否则返回false。
以下是示例代码
function isDuplicate(str) {const cleanStr str.replace(/[^a-zA-Z]/g, );const charSet new Set(cleanStr);return charSet.size ! cleanStr.length;
}console.log(isDuplicate(abc)); // 输出false
console.log(isDuplicate(abca)); // 输出true在上述代码中我们分别测试了字符串’abc’和’abca’。第一个字符串中没有重复字符所以返回false第二个字符串中有重复字符’a’所以返回true。运行代码后可以得到相应的结果
js 时间复杂度 和空间复杂度
在JavaScript中时间复杂度和空间复杂度是衡量算法性能的两个重要指标。
时间复杂度Time Complexity表示算法执行所需要的时间与问题规模之间的关系。常见的时间复杂度有
常数时间复杂度 O(1)无论问题规模的大小算法的执行时间都是固定的。线性时间复杂度 O(n)算法的执行时间与问题规模呈线性关系。对数时间复杂度 O(log n)算法的执行时间与问题规模的对数值呈线性关系。平方时间复杂度 O(n^2)算法的执行时间与问题规模的平方呈线性关系。指数时间复杂度 O(2^n)算法的执行时间与问题规模的指数值呈线性关系。
空间复杂度Space Complexity表示算法执行所需要的额外空间与问题规模之间的关系。常见的空间复杂度有
常数空间复杂度 O(1)算法执行所需要的额外空间是固定的与问题规模无关。线性空间复杂度 O(n)算法执行所需要的额外空间与问题规模呈线性关系。平方空间复杂度 O(n^2)算法执行所需要的额外空间与问题规模的平方呈线性关系。
要分析算法的时间复杂度和空间复杂度可以根据算法中的循环、递归、函数调用等操作来确定执行次数并根据问题规模的变化情况来确定复杂度的增长趋势。
需要注意的是时间复杂度和空间复杂度是描述算法性能的理论指标只是对算法的估计并不能精确地表示
v-for 和v-if为什么不能一块儿用
v-for和v-if不能一起使用的原因是由于它们的编译顺序不同。v-for是在v-if之前被解析和渲染的。
在Vue的编译过程中v-for的优先级比v-if要高。这意味着v-for会先被解析和渲染然后才会去判断v-if的条件。如果v-for中的列表为空或条件不满足那么v-if将无法生效因为v-for已经在此时已经渲染了元素。
为了避免这种问题可以将v-if放置在包裹v-for的父元素上或者使用计算属性来处理过滤后的列表。
以下是示例代码
templatedivdiv v-ifshouldShowListdiv v-foritem in filteredList :keyitem.id!-- 渲染列表项 --/div/div/div
/templatescript
export default {data() {return {list: [...], // 列表数据shouldShowList: true // 是否显示列表};},computed: {filteredList() {// 根据条件过滤列表数据return this.list.filter(item item.condition);}}
};
/script在上述代码中我们将v-if放置在包裹v-for的父元素上并使用计算属性filteredList来处理过滤后的列表数据。通过这样的方式可以确保v-if在v-for之前进行判断从而避免了两者冲突的问题。
什么时候使用闭包
闭包可以在以下情况下使用
保护变量闭包可以创建一个私有作用域将变量封装在内部函数中从而保护变量不受外部访问和修改。这在一些需要隐藏数据的场景下非常有用。记忆函数闭包可以记住函数的上下文和状态使得函数可以记住之前的操作和结果。这在一些需要缓存计算结果的场景下非常有用可以提高函数的性能。延长作用域链闭包可以在函数内部访问外部函数的变量从而延长作用域链。这在一些需要访问外部函数的变量的场景下非常有用。模块化开发通过使用闭包可以将代码封装在模块中避免全局变量的污染实现模块化开发。
需要注意的是使用闭包也会带来一些问题例如内存泄漏和性能问题。因此在使用闭包时需要注意内存管理和性能优化