前段HTML/CSS ES6 VUE 面试题总结

本文汇总了Vue相关的面试题目,涵盖Vue双向数据绑定、虚拟DOM、组件通讯、Vuex、路由、生命周期、事件处理等多个方面。讲解了Vue如何通过Observer、Compile和Watcher实现数据绑定,以及Vue2和Vue3的差异。此外,还讨论了HTTP状态码、Git操作、Web安全及跨域知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面试题

Vue相关

  • vue双向数据绑定

是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图,实现数据和视图同步。

第一步: 需要observer的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter,这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化

第二步: compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图

第三步: Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:

1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。

第四步: MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

  • vue虚拟domdiff算法

想要理解虚拟dom首先要知道什么是虚拟dom?

虚拟dom可以简单的用一句话概括,就是用普通的js对象来描述DOM结构,因为不是真实DOM,所以称之为虚拟DOM。

虚拟dom是相对于浏览器所渲染出来的真实dom而言的,在react,vue等技术出现之前,我们要改变页面展示的内容,只能通过遍历查询dom树的方式,找到需要修改的dom,然后修改样式行为或者结构,来达到更新视图的目的。

为什么要用虚拟DOM来描述真实的DOM呢?

创建真实DOM成本比较高,如果用 js对象来描述一个dom节点,成本比较低,另外我们在频繁操作dom是一种比较大的开销。所以建议用虚拟dom来描述真实dom。

Diff算法

diff算法就是进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方,最后用patch记录的消息去局部更新Dom。

思考

你觉得虚拟DOM的性能一定高于常规DOM吗?答案是不一定,虚拟DOM和Diff算法的出现是为了解决由命令式编程转变为函数式编程、数据驱动后所带来的性能问题的。换句话说,直接操作DOM的性能并不会低于虚拟DOM和Diff算法,甚至还会优于。

你觉得使用了虚拟DOM就真的不操作dom元素了吗?其实不是的,只是减少用户操作dom,虚拟DOM在渲染的时候其实还是会操作dom的

  • 组件通讯

1.父传子

在父组件的子组件标签上绑定一个属性,挂载要传输的变量。在子组件中通过props来接受数据,props可以是数组也可以是对象,接受的数据可以直接使用 props:["属性名"]  props:{属性名:数据类型}

2.子传父:

在父组件的子组件标签上通过绑定自定义事件,接受子组件传递过来的事件。子组件通过$emit触发父组件上的自定义事件,发送参数

3.兄弟组件传值

通过main.js初始化一个全局的$bus,在发送事件的一方通过$bus.$emit(“事件名”,传递的参数信息)发送,在接收事件的一方通过$bus.$on("事件名",参数)接收传递的事件

  • Vuex

1.vuex :是一个专为vue.js开发的状态管理器,采用集中式存储的所有组件状态,通过vuex我们可以解决组件之间数据共享的问题,后期也方便我们管理以及维护

有五个属性分别是: state、getters、mutations、actions、module

state属性: 存放状态,例如你要存放的数据

getters: 类似于共享属性,可以通过this.$store.getters来获取存放在state里面的数据 a

mutations: 唯一能改变state的状态就是通过提交mutations来改变,this.$store.commit()

actions: 异步的mutations,可以通过dispatch来分发从而改变state

2.基本使用:我通过是在根目录下新建一个store文件夹,里面创建一个index.js文件,最后在main.js中引入,并挂载到实例上,之后那个组件中需要用到vuex就调用就行

3.高级用法-数据持久化

vuex里面存放的数据,页面一经刷新会丢失:


解决办法: 存放在localStorage或者sessionStorage里面,进入页面时判断是否丢失,丢失再去localStorage或者sessionStorage里面取;

在vuex中可以通过安装vuex-persistedstate 插件,进行持久化的配置就行

4.高级用法-辅助函数(语法糖)

1. 有那几个辅助函数(4大金刚)
mapState,mapActions,mapMutations,mapGetters
2. 辅助函数可以把vuex中的数据和方法映射到vue组件中。达到简化操作的目的

3. 如何使用:

Import { mapActions, mapGetters, mapMutations, mapState } from 'vuex'

  • 自定义指令自定义过滤器

vue中的自定义指令:

vue中除了核心功能内置的指令外,也允许注册自定义指令。有的情况下,对普通DOM元素进行底层操作,这时候就会用到自定义指令。自定义指令又分为全局的自定义指令和局部自定义指令。

全局自定义指令是通过Vue.directive('第一个参数是指令的名称',{第二个参数是一个对象,这个对象上有钩子函数})

 Vue.directive('focus', {

// el:指令所绑定的元素,可以用来直接操作 DOM。

//binding:一个对象,包含以下 property:

      inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用

        el.focus();

      }

0

    });

局部自定义指令:
是定义在组件内部的,只能在当前组件中使用

directives: {

        // 指令名称

        dir1: {

            inserted(el) {

                // 指令中第一个参数是当前使用指令的DOM

                console.log(el);

                console.log(arguments);

                // 对DOM进行操作

                el.style.width = '200px';

                el.style.height = '200px';

                el.style.background = '#000';

            }

        },

        color: { // 为元素设置指定的字体颜色

          bind(el, binding) {

            el.style.color = binding.value;

          }

        }

}

钩子函数:
一个指令定义对象可以提供如下几个钩子函数 (均为可选):

inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

unbind:只调用一次,指令与元素解绑时调用。

项目中:拖拽

vue中自定义过滤器

  • 过滤器是对即将显示的数据做进一步的筛选处理,然后显示,过滤器并没有改变原来的数据,只是在原数据的基础上产生新的数据
  • 过滤器分为全局过滤器局部过滤器

全局过滤器
全局过滤器是通过Vue.filter()来定义的,定义好后,它在所有组件中都可以使用。

// global-filter是过滤器名称

// 函数第一个参数是需要过滤的数据.

// 函数第二个参数是给过滤器传递的值.

  Vue.filter('global-filter',(val,...args)=>{

    console.log(`需要过滤的数据是:${val}`)

    return val + ' 过滤器追加的数据'

  })

局部过滤器

局部过滤器,定义在组件内部 filters 属性上.它只能在此组件内部使用.

过滤器的使用方式是,在双花括号或v-bind中通过一个管道符来拼接,

项目中使用过滤器:时间,价钱

  • vue-router   瑞特(路由原理路由守卫传参)

路由原理

  1. 路由就是用来解析URL以及调用对应的控制器,并返回从视图对象中提取好的网页代码给web服务器,最终返回给客户端。

hash模式:在浏览器中符号的“#”,以及#后面的字符称之为hash,用window.location.hash读取;

特点:

hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,

hash不会重加载页面。

hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。

history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),

replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。

特点:

history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 地址后加上/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。

路由传参:

三种:

分别是query,params,动态路由传参

接收:

通过query方式传递过来的参数一般是通过this.$route.query接收

通过params方式传递过来的参数一般是通过this.$route.params接收

通过动态路由传参方式传递过来的参数一般是通过this.$route.params接收

query使用path和name传参跳转都可以,而params只能使用name传参跳转。

传参跳转页面时,query不需要再路由上配参数就能在新的页面获取到参数,params也可以不用配,但是params不在路由配参数的话,当用户刷新当前页面的时候,参数就会消失。

也就是说使用params不在路由配参数跳转,只有第一次进入页面参数有效,刷新页面参数就会消失。

路由守卫:

2.路由守卫使用的方式有几种? 全局的 单个路由独享的 组件级的

3.vue-router全局有三个守卫:           

router.beforeEach 全局前置守卫 进入路由之前

router.beforeResolve 全局解析守卫(2.5.0+) 在beforeRouteEnter调用之后调用 router.afterEach 全局后置钩子 进入路由之后

组件内的守卫:

beforeRouteEnter

beforeRouteUpdata(2.2新增)

beforeRouteLeave

3. 路由守卫钩子函数里面的三个参数分别是什么?

to,from,next 这三个参数:

to和from是将要进入和将要离开的路由对象,路由对象指的是平时通过this.$route获取到的路由对象。

next:Function 这个参数是个函数,且必须调用,否则不能进入路由(页面空白)。

next() 进入该路由。

next(false): 取消进入路由,url地址重置为from路由地址(也就是将要离开的路由地址)。 next 跳转新路由,当前的导航被中断,重新开始一个新的导航。

  • 声明周期(那几个?每一个生命周期的特点,可以做什么)

生命周期让我们在控制整个vue时更容易形成更好的逻辑,可以分为三个阶段,挂载阶段,更新阶段,销毁阶段

分别有:

创建前:beforeCreate() 只有一些实例本身的事件和生命周期函数

创建后:Created() 是最早使用data和methods中数据的钩子函数

挂载前:beforeMount() 指令已经解析完毕,内存中已经生成dom树

挂载后:Mounted() dom渲染完毕页面和内存的数据已经同步

更新前:beforeUptate() 当data的数据发生改变会执行这个钩子,内存中的数据是新的,页面是旧的

更新后:Updated() 内存和页面都是新的

销毁前:beforeDestroy() 即将销毁data和methods中的数据此时还是可以使用的,可以做一些释放内存的操作

销毁后:Destroyed() 已经销毁完毕

Vue3.0中的生命周期做了一些改动:

beforeCreate  -> setup() 开始创建组件之前,创建的是data和method

created       -> setup()

beforeMount   -> onBeforeMount 组件挂载到节点上之前执行的函数。

mounted       -> onMounted 组件挂载完成后执行的函数

beforeUpdate  -> onBeforeUpdate 组件更新之前执行的函数。

updated       -> onUpdated 组件更新完成之后执行的函数。

beforeDestroy -> onBeforeUnmount 组件挂载到节点上之前执行的函数。

destroyed     -> onUnmounted 组件卸载之前执行的函数。

  • 自定义组件

在vue中开发,都是用的组件化的思想开发的,一般在都会在搭建项目的时候,都会先建立组件的模板,把架子搭起来。也就是在组件中定义好<template>视图层,<script>逻辑层,<style>css样式层。

在vue中使用组件封装的方式可以使我们的开发效率提高,能够把页面抽象成相对独立的模块。

我一般在创建项目的时候,都会通过创建一个views目录和一个commen目录和一个feature目录,views目录中放页面级的组件,commen中放公共组件(如:head(公共头组件),foot(公共底部组件)等),feature目录内放功能组件(如:swiper(轮播功能组件),tabbar(切换功能组件)、list(上拉加载更多功能组件))。组件封装是通过定义一个组件,然后定义好props里面的数据,实现其他组件需要的逻辑化代码后,也就是封装好了,然后直接调用就可以。调用是通过在需要封装组件的组件中,通过import导入,component注册好名称,最后挂载到父组件中的template即可。

组件封装的方式解决了我们传统项目,开发效率低,难以维护,复用性低等问题。

  • 常见的指令,修饰符

常用指令

在vue中提供了一些对于页面 + 数据的更为方便的输出,这些操作就叫做指令,指令中封装了一些DOM行为, 结合属性作为一个暗号, 暗号有对应的值,根据不同的值,框架会进行相关DOM操作的绑定
vue中的指令有很多,我们平时做项目常用的有:

v-if:是动态的向DOM树中添加或者删除元素;

v-else是搭配v-if使用的,它必须紧跟在v-if或者v-else-if后面,否则不起作用

v-show:是通过标签的CSS样式display的值是不是none,控制显示隐藏

区别:
1、当条件为真的时候 没有区别 当条件为假的时候 v-if通过创建或删除DOM节点来实现元素的显示隐藏,v-show通过css中的display属性来控制
2、v-if更适合数据的筛选和初始渲染 v-show更适合元素的切换

v-for: v-for是根据遍历数据来进行渲染,要配合key使用。要注意的是当v-for和v-if同处于一个节点时,v-for的优先级比v-if更高。这意味着v-if将运行在每个v-for循环中

v-on:用来绑定一个事件或者方法,简写方式是@click=""

v-bind: v-bind用来动态的绑定一个或者多个属性。没有参数时,可以绑定到一个包含键值的对象。常用于动态绑定class和style。以及href等。简写的方式是“:属性名=""”一个冒号

v-model 只能适用于在表单元素上,可以实现数据双向绑定 ,

数据双向绑定实现的原理:

vue.js是采用数据劫持结合发布者-订阅者模式的方式,通Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。

主要分为四步

第一步: 需要Observer的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter,这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。

第二步: 通过Compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图

第三步: Watcher订阅者是Observer和Compile之间通信的桥梁。

第四步: MVVM作为数据绑定的入口,整合Observer、Compile、Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化->视图更新;视图交互变化(input)->数据model变更的双向绑定效果

修饰符:

在Vue中,事件修饰符处理了许多DOM事件的细节,让我们不再需要花大量的时间去处理这些烦恼的事情,而能有更多的精力专注于程序的逻辑处理。在Vue中事件修饰符常用的主要有

  • .stop :阻止事件冒泡:由内而外,通俗的将就是阻止事件将向上级DOM元素传递
  • .capture :事件捕获:由外而内,在捕获阶段,事件从window开始,之后是document对象,一直到触发事件的元素。
  • .self :当事件作用在元素本身时才会触发
  • .once :只触发一次
  • .prevent: 阻止默认事件
  • ·passive:告诉浏览器你不想阻止事件的默认行为
  • ·trim:自动过滤用户输入的首尾空格

语法:@事件名.修饰符=“方法名”

  • Vue2和vue3的区别
  1. 双向数据绑定原理发生了改变,使用proxy替换Object.defineProerty,使用Proxy的优势:
  1. 可直接监听数组类型的数据变化
  2. 监听的目标为对象本身,不需要像Object.defineProperty一样遍历每个属性,有一定的性能提升
  3. 可直接实现对象属性的新增/删除
  1. 默认使用懒加载

在2.x版本里。不管数据多大,都会在一开始就为其创建观察者,在数据很大时,就会造成性能的问题。在3.x中,只会对渲染出来的数据创建观察者,而且3.x的观察者更高效。

  1. 3.0新加入了TypeScript以及PWA支持
  2. 重构Virtual DOM
  1. 模板编译时的优化,将一些静态节点编译成常量
  2. Slot优化,将slot编译为lazy函数,将slot的渲染的决定权交给子组织
  1. 生命周期有了一定的区别

Vue2--------------vue3

beforeCreate  -> setup() 开始创建组件之前,创建的是data和method

created       -> setup()

beforeMount   -> onBeforeMount 组件挂载到节点上之前执行的函数。

mounted       -> onMounted 组件挂载完成后执行的函数

beforeUpdate  -> onBeforeUpdate 组件更新之前执行的函数。

updated       -> onUpdated 组件更新完成之后执行的函数。

beforeDestroy -> onBeforeUnmount 组件挂载到节点上之前执行的函数。

destroyed     -> onUnmounted 组件卸载之前执行的函数。

activated     -> onActivated 组件卸载完成后执行的函数

deactivated   -> onDeactivated

  • Keep-alive

keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。

在组件切换过程中 把切换出去的组件保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性

被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated(组件激活时使用) 与 deactivated(组价离开时调用)

如果需要缓存整个项目,直接在app.vue中用keep-alive包裹router-view即可要缓存部分页面需要在路由地址配置中,在meta属性中添加一个状态,在app.vue中判断一包裹的router-view即可

例如有一个商品页面和一个详情页面,这样在两个页面切换的时候就可以用到keep-alive,在切换到详情的时候,把状态保留在内存中,而不是销毁,从而提高一个性能的优化

  • 多环境变量

首先是通过在根目录下创建.env.*(配置文件)文件,development 本地开发环境配置、staging 测试环境配置、production 正式环境配置。因为我在创建的文件中并没有定义很多变量,只定义了基础的env,所以需要在src目录下创建一个config文件夹,创建对应的环境变量文件,用来管理不同的环境。在config中创建对应的文件是为了后期修改起来方便,不需要重启项目,符合开发习惯。之后就是根据需要的环境,在封装的axios中通过解构赋值的方式导入,放在baseURL中就可以使用。

  • 对axios封装(url统一管理、axios请求拦截、响应拦截函数封装)

首先要安装axios一般我会在项目的src目录中,新建一个network文件夹,作为我们的网络请求模块,然后在里面新建一个http.js和一个api.js文件和一个reques.js。http.js文件用来封装我们的axios,api.js用来统一管理我们的接口url,

在request.js中添加请求拦截和响应拦截。在请求拦截中,会给请求头添加token字段,还有loading动画的开启。在响应拦截中,可以做一些loading动画的关闭,还有可以根据后端返回的状态码,做一些检验token是否有效或者过期的操作。接着就是做一些axios进行的api接口的封装,这里我用到了asyncawait封装请求接口函数,这样可以将异步操作同步化操作,代码更加友好,避免回调地域的出现。

  • Element-ui和vant-ui按需引入

首先安装按需引入的插件,在babel.config.js中添加按需引入的配置,创建一个plugin文件夹,定义一个js文件用来存放按需引入的代码,之后在建好的js文件中首先导入vue,再导入需要的vant-ui插件,通过vue.use()全局注入。

修改样式可以用样式穿透 /deep/

  • Sass配置

安装node-sass sass-loader

使用lang=”scss”

  • Rem、vm/vh设置
  1. 通过安装cnpm install lib-flexible postcss-pxtorem --save-dev
  2. Main.js 中导入插件
  3. 根目录创建.postcssrc.js配置
  • Webpack配置(配置跨域路径别名打包分析、cdn映入、去掉console.log单独打包第三方模块、ie兼容、eslint规范、图片压缩)
  • Slot插槽

插槽就是父组件往子组件中插入一些内容。

有三种方式,默认插槽,具名插槽,作用域插槽

  1. 默认插槽就是把父组件中的数据,显示在子组件中,子组件通过一个slot插槽标签显示父组件中的数据
  2. 具名插槽是在父组件中通过slot属性,给插槽命名,在子组件中通过slot标签,根据定义好的名字填充到对应的位置。
  3. 作用域插槽是带数据的插槽,子组件提供给父组件的参数,父组件根据子组件传过来的插槽数据来进行不同的展现和填充内容。在标签中通过slot-scope来接受数据。

  • 为什么v-for使用key

key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点

  • 为什么data是一个函数

1.根实例对象data可以是对象也可以是函数(根实例是单例),不会产生数据污染情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值