2024年12月

一、事件冒泡和事件捕获

  • 事件冒泡:当一个元素上的事件被触发时,该事件会沿着 DOM 树向上传播,从触发事件的最内层元素开始,依次触发其祖先元素上的相同事件。例如,在一个包含多个嵌套 <div> 的页面中,如果点击最内层的 <div>,那么这个点击事件会先在该 <div> 上触发,然后依次在其父元素、祖父元素等上触发,直到到达文档根节点。
  • 事件捕获:与事件冒泡相反,事件捕获是从文档根节点开始,沿着 DOM 树向下传播,直到到达目标元素。在事件捕获阶段,事件会先在最外层的祖先元素上触发,然后逐渐向内层元素传播。
  • 应用场景与示例:在实际开发中,可以利用事件冒泡和捕获来实现一些复杂的交互逻辑。例如,在一个列表中,点击每个列表项时,既需要在列表项本身触发一个点击事件,又需要在列表容器上统计点击次数。可以通过在列表容器上添加一个捕获阶段的点击事件监听器来统计点击次数,而在列表项上添加冒泡阶段的点击事件监听器来处理列表项的具体点击操作。

二、项目中用flex实现一个左固定,右自适应的布局

  • 使用 flexbox 布局:

    • 父容器设置 display: flex
    • 左固定部分设置固定宽度,例如 width: 200px
    • 右自适应部分设置 flex: 1,表示自动填充剩余空间。

三、flex:1 是什么效果

  • flex: 1flex-grow: 1flex-shrink: 1flex-basis: 0% 的缩写。
  • flex-grow: 1:表示当父容器有剩余空间时,该元素会按照比例分配剩余空间并增长。如果有多个子元素都设置了 flex-grow,则它们会按照各自的 flex-grow 值比例分配剩余空间。例如,一个父容器内有两个子元素,一个设置 flex: 1,另一个设置 flex: 2,那么剩余空间会按照 1:2 的比例分配给这两个子元素。
  • flex-shrink: 1:当父容器空间不足时,该元素会按照比例缩小。如果所有子元素的 flex-shrink 都为 1,它们会平均缩小;如果值不同,则按照比例缩小。
  • flex-basis: 0%:指定了元素在分配剩余空间或收缩之前的初始大小。设置为 0% 表示元素在分配空间之前不占据任何空间,完全依赖于 flex-grow 来分配空间。

四、js 中的原型链

  • 原型链概念:在 JavaScript 中,每个对象都有一个原型(prototype),而原型本身也是一个对象,它又有自己的原型,这样就形成了一个原型链。当访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或方法或者到达原型链的顶端(Object.prototype 的原型为 null)。
  • 示例
function Person() {
  this.name = 'John';
}

Person.prototype.sayHello = function() {
  console.log('Hello, I am'+ this.name);
};

const person = new Person();
person.sayHello(); // 调用原型上的方法,输出 "Hello, I am John"

// 查看原型链
console.log(person.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null

五、同源策略

  • 同源策略定义:同源策略是浏览器的一种安全机制,它限制了一个源(协议、域名、端口)的脚本只能与同来源的资源进行交互。例如,一个页面从 http://example.com 加载,那么该页面中的 JavaScript 脚本只能向 http://example.com 发送请求,不能向其他域名或不同协议、端口的资源发送请求,除非有特殊的跨域设置。
  • 目的:主要目的是防止恶意网站通过脚本获取其他网站的敏感信息,保障用户数据的安全和网站的隐私。例如,防止一个恶意网站读取用户在银行网站的账户信息等。
  • 跨域解决方案:虽然同源策略限制了跨域访问,但在实际开发中,有多种方式可以实现跨域,如 JSONP(利用 <script> 标签不受同源策略限制的特性)、CORS(服务器端设置允许跨域的响应头)、代理服务器(在同源的服务器端代理请求其他域的资源)等。

六、Nodejs 代理服务器

  • 作用:Node.js 代理服务器可以在客户端和目标服务器之间起到中转的作用。它可以用于解决跨域问题,通过在同源的 Node.js 服务器上代理客户端的请求到不同源的目标服务器,然后将目标服务器的响应返回给客户端,从而绕过浏览器的同源策略限制。此外,还可以用于请求过滤、缓存、日志记录等功能。

七、echarts 传值方式、及多个echarts更新单个

  • 数据更新:可以直接通过修改 echarts 实例的 option 对象中的数据来更新图表。例如,如果有一个柱状图,要更新柱子的高度数据,可以这样操作:
  • 事件传值:echarts 提供了丰富的事件,如 clicklegendselectchanged 等。在事件回调函数中,可以获取到相关的数据信息并进行传值操作。
  • 单独更新:在子组件中使用watch监听进行数值更新操作。

八、微信小程序提高响应速度

  • 优化代码结构:合理划分页面和组件,避免代码过于臃肿。将公共的逻辑提取到独立的模块或组件中,减少页面加载时的代码量。
  • 数据请求优化:减少不必要的数据请求,对数据进行缓存。例如,可以使用 wx.getStoragewx.setStorage 对经常使用的数据进行本地缓存,减少重复请求服务器的次数。同时,优化数据请求的时机,避免在页面加载时同时发起过多请求,可以采用延迟加载或合并请求的方式。
  • 图片和资源优化:对图片进行压缩处理,选择合适的图片格式(如 webp 格式在某些情况下比 jpg、png 更具优势)。对于一些较大的资源文件,可以考虑使用分包加载,将不常用的资源放在分包中,在需要时再加载。
  • 使用骨架屏或加载提示:在页面数据加载过程中,显示骨架屏或加载提示,让用户有等待的预期,避免页面长时间空白或无响应给用户带来的不良体验。

九、前端身份验证

  • 基于 Token 的身份验证:

    • 流程:用户登录成功后,服务器生成一个包含用户身份信息的 Token(通常是一个加密的字符串)并返回给前端。前端将 Token 存储起来,通常存储在 localStoragesessionStorage 中。在后续的请求中,前端将 Token 添加到请求头(如 Authorization 字段)中发送给服务器。服务器收到请求后,验证 Token 的有效性,如果有效则允许访问相应资源,否则返回错误信息。
  • 基于 Cookie 的身份验证:

    • 流程:用户登录成功后,服务器在响应中设置一个包含用户身份信息的 Cookie。浏览器会自动在后续的同源请求中携带该 Cookie。服务器根据 Cookie 中的信息进行身份验证。不过,由于 Cookie 存在一些安全风险(如跨站请求伪造 CSRF),需要采取相应的防范措施,如设置 SameSite 属性等。
继续阅读->

一、你目前在长春吗?预计多久可以实习?

我目前在长春,预计拿到offer后可以开始实习。

二、描述一下你的项目,你最得意的页面

三、八股问答

(一)前端路由导航激活(切换按钮的激活状态 css)

在实现前端路由导航激活时,通过监听路由的变化,利用 Vue Router 提供的导航守卫或者在组件内使用 watch 监听 $route 对象的变化。当路由发生改变时,根据当前路由路径与导航菜单的对应关系,动态地为相应的菜单项添加或移除激活状态的 CSS 类名。

(二)vue 中 histroy 为什么变化后会显示元素

在 Vue 中使用 vue-routerhistory 模式时,当 URL 的 history 发生变化,vue-router 会根据新的 URL 匹配对应的路由组件,并将该组件渲染到指定的视图区域,从而显示对应的元素。这是因为 vue-router 对浏览器的 history API 进行了封装,监听了 popstate 事件以及在路由跳转时手动更新 history 状态,然后根据内部的路由映射表来决定显示哪个组件。

(三)页面之间传参的方式

  • 路由传参:

    • 动态路由参数:在路由配置中定义动态参数,例如 { path: '/user/:id', component: User },在组件内通过 this.$route.params.id 获取参数。
    • 查询参数:通过 this.$router.push({ path: '/search', query: { keyword: 'vue' } }) 传递参数,在接收页面通过 this.$route.query.keyword 获取。
  • 父子组件传参:

    • 父传子:在父组件的模板中,通过属性绑定将数据传递给子组件,例如 <child-component :message="parentMessage"></child-component>,在子组件中通过 props 接收,props: ['message']
    • 子传父:子组件通过 $emit 事件触发一个自定义事件,并传递数据,例如 this.$emit('sendData', data),在父组件中监听该事件并接收数据,<child-component @sendData="receiveData"></child-component>methods: { receiveData(data) { console.log(data); } }

(四)vue 父传子子传父

如上述页面传参方式中所描述,父传子通过属性绑定和 props 实现,子传父通过 $emit 触发自定义事件实现,这种方式使得父子组件之间的数据传递和交互更加灵活和可控,方便构建复杂的组件结构和应用逻辑。

(五)vue3 生命周期函数

Vue 3 中的生命周期函数有了一些变化,主要的生命周期钩子包括:

  • setup:在组件创建之前执行,用于初始化组件的一些数据和逻辑,是 Vue 3 中新增的组合式 API 的入口函数。
  • onBeforeMount:在组件挂载之前被调用。
  • onMounted:在组件挂载完成后被调用。
  • onBeforeUpdate:在组件数据更新之前被调用。
  • onUpdated:在组件数据更新完成后被调用。
  • onBeforeUnmount:在组件卸载之前被调用。
  • onUnmounted:在组件卸载完成后被调用。

(六)vue2 和 vue3 生命周期区别

  • Vue 2 中的 beforeCreatecreated 在 Vue 3 中被整合到了 setup 函数中,setup 函数在组件实例化之前执行,可以访问到 propscontext,但不能访问到组件实例 this
  • Vue 3 中移除了 beforeDestroydestroyed,取而代之的是 onBeforeUnmountonUnmounted,使得生命周期函数的命名更加语义化,符合组件挂载和卸载的概念。
  • Vue 3 的生命周期函数采用了基于函数的注册方式,而不是 Vue 2 中的选项式注册,例如在 Vue 3 中使用 import { onMounted } from 'vue'; onMounted(() => { console.log('mounted'); });,这种方式使得代码更加灵活和可复用。

(七)前后端是怎么实现交互的

前后端交互通常基于 HTTP 协议。前端通过发送 HTTP 请求(如 GETPOSTPUTDELETE 等请求方法)到后端服务器的特定接口地址,后端服务器接收到请求后,根据请求的内容进行相应的业务逻辑处理,如查询数据库、执行业务算法等,然后将处理结果以 HTTP 响应的形式返回给前端。前端在收到响应后,根据响应的数据进行页面的更新和展示。在 Vue 项目中,常使用 axios 等库来发送 HTTP 请求

(八)表单元素用户名,性别,右边按钮查询,后台请求列表,姓名,性别,职业;设计数据结构并描述整套流程

  • 数据结构设计:

    • 前端表单数据结构
{
  username: '', // 用户名
  gender: '', // 性别
}
  • 后端返回数据结构
[
  {
    name: '', // 姓名
    gender: '', // 性别
    profession: '' // 职业
  },
...
]
  • 流程描述:

    • 前端页面展示包含用户名输入框、性别选择框(如男、女)和查询按钮的表单。当用户输入用户名并选择性别后,点击查询按钮,前端会将表单数据(usernamegender)封装成一个对象,使用 axios 发送一个 POST 请求到后端的查询接口(如 /api/search)。后端接收到请求后,根据传入的用户名和性别参数在数据库中进行查询操作,查询符合条件的用户信息,将查询结果(包含姓名、性别、职业)封装成一个数组形式的 JSON 数据,然后将该数据作为 HTTP 响应返回给前端。前端在收到响应后,解析响应数据,将用户信息展示在页面的列表中,例如使用 v-for 指令循环渲染列表项。

(九)水平居中元素的方法

  • 行内元素或文本:可以使用 text-align: center 样式属性将父元素内的行内元素或文本水平居中
  • 定宽块级元素:可以使用 margin: 0 auto 样式,将块级元素的左右外边距设置为自动,使其在父元素内水平居中
  • 不定宽块级元素:对于不定宽的块级元素,可以使用 display: flexjustify-content: center 实现水平居中。

(十)js 中的数组操作有哪些

JavaScript 中的数组操作非常丰富,常见的有:

  • 创建数组:

    • 使用字面量方式 const arr = [1, 2, 3];
    • 使用 Array 构造函数 const arr = new Array(1, 2, 3);
  • 访问数组元素:通过索引访问,例如 arr[0] 获取数组的第一个元素。
  • 修改数组元素:直接通过索引赋值,例如 arr[1] = 5;
  • 数组长度:使用 length 属性获取或修改数组长度,例如 arr.length 返回数组的长度,arr.length = 5 可以改变数组的长度。
  • 添加元素:

    • push:在数组末尾添加一个或多个元素,例如 arr.push(4, 5),返回修改后的数组长度。
    • unshift:在数组开头添加一个或多个元素,例如 arr.unshift(0),返回修改后的数组长度。
  • 删除元素:

    • pop:删除数组末尾的元素,返回被删除的元素。
    • shift:删除数组开头的元素,返回被删除的元素。
    • splice:可以删除、插入或替换数组中的元素,例如 arr.splice(1, 2) 删除从索引 1 开始的 2 个元素,arr.splice(2, 0, 'new') 在索引 2 处插入元素 'new'
  • 数组遍历:

    • for 循环for (let i = 0; i < arr.length; i++) { console.log(arr[i]); }
    • forEach 方法arr.forEach(item => console.log(item));
    • map 方法:创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后的返回值,例如 const newArr = arr.map(item => item * 2);
    • filter 方法:创建一个新数组,其包含通过所提供函数实现的测试的所有元素,例如 const filteredArr = arr.filter(item => item > 2);
    • reduce 方法:对数组中的每个元素执行一个由您提供的 reducer 函数 (升序执行),将其结果汇总为单个返回值,例如 const sum = arr.reduce((acc, item) => acc + item, 0);

四、你未来是打算怎么发展的

想在大城市发展

五、我看你简历上主修课程都是后端课程,为什么想到要做前端呢?(问了两次)

继续阅读->