cesium(vue)一些面试问题(包含Three.js)

发布于:2025-02-22 ⋅ 阅读:(13) ⋅ 点赞:(0)

1.在不同的应用场景和技术栈中,模型加载方法和格式有所不同,下面主要从 Web 前端三维场景(使用 Three.js和cesium)

使用 Three.js 加载模型

常见模型格式及加载方法
GLTF/GLB 格式
  • 格式特点:GLTF(Graphics Library Transmission Format)是一种开放的、基于 JSON 的三维模型传输格式,GLB 是其二进制版本。它们具有文件小、加载快、支持动画、材质和骨骼等优点,是 Web 端三维模型的主流格式。
// 创建 GLTF 加载器
const loader = new GLTFLoader();

// 加载 GLTF/GLB 模型
loader.load(
    'path/to/your/model.glb',
    (gltf) => {
        // 将模型添加到场景中
        scene.add(gltf.scene);
    },
    undefined,
    (error) => {
        console.error('Error loading model:', error);
    }
);

还有obj fbx等。

使用 Cesium 加载模型

常见模型格式及加载方法
GLTF/GLB 格式
  • 格式特点:与在 Three.js 中类似,GLTF/GLB 在 Cesium 中也是常用的模型格式,能很好地与 Cesium 的地理空间场景融合。不能obj
  • 加载示例
const entity = viewer.entities.add({
            name: 'GLTF Model',
            position: position,
            orientation: orientation,
            model: {
                uri: 'path/to/your/model.glb',
                minimumPixelSize: 128,
                maximumScale: 20000
            }
        });
CZML 格式
  • 格式特点:CZML(Cesium Markup Language)是一种基于 JSON 的格式,专门用于描述随时间变化的地理数据和场景,在 Cesium 中可以动态展示模型的位置、姿态等随时间的变化。
  • 加载示例
 // 加载 CZML 文件
        const czmlDataSource = new Cesium.CzmlDataSource();
        czmlDataSource.loadUrl('path/to/your/file.czml').then(() => {
            viewer.dataSources.add(czmlDataSource);
            viewer.zoomTo(czmlDataSource);
        }).catch((error) => {
            console.error('Error loading CZML:', error);
        });

2.前端三维的理解和应用 

概念

前端三维指的是在网页或移动应用前端界面中,利用相关技术创建、展示和交互三维图形、场景或模型的技术领域。它借助计算机图形学原理和前端开发技术,让用户能在浏览器或应用内直观地查看和操作三维内容,带来沉浸式体验。

核心技术支撑
  • WebGL:作为前端三维的底层技术,它是基于 OpenGL ES 的 JavaScript API,允许在网页上直接进行高性能的三维图形绘制和渲染,无需额外插件。为 Three.js 和 Cesium 这类库提供了强大的图形处理能力。
  • 三维库与引擎:Three.js 和 Cesium 就是这类代表,它们对 WebGL 进行封装,简化了复杂的图形编程,提供了丰富的工具和接口,让开发者可以更便捷地创建三维场景。

Three.js 的特点及应用

特点
  • 轻量级与易用性:Three.js 是一个轻量级的 JavaScript 3D 库,API 设计简洁直观,易于学习和上手。对于初学者来说,能够快速掌握并实现基本的三维场景搭建。
  • 丰富的几何体与材质:提供了众多内置的几何体,如立方体、球体、圆柱体等,同时支持多种材质类型,包括基础材质、光照材质、纹理材质等,方便开发者创建多样化的三维模型。
  • 强大的动画系统:支持关键帧动画、骨骼动画等多种动画形式,可以实现复杂的动画效果,增强场景的动态性和交互性。
应用场景
  • 产品展示:在电商、家居等行业,可用于产品的三维展示。用户可以全方位观察产品的外观、细节和结构,提高购物的决策效率。例如家具电商网站,通过 Three.js 展示家具的不同角度和款式。
  • 游戏开发:适合开发小型的网页游戏,如休闲游戏、益智游戏等。利用其动画和交互功能,创建有趣的游戏体验。
  • 艺术与创意项目:为艺术家和设计师提供了一个创作平台,用于展示三维艺术作品、创意设计等,实现独特的视觉效果。

Cesium 的特点及应用

特点
  • 地理空间专注性:Cesium 专注于地理空间数据的可视化和分析,内置了全球地形、影像数据,能够快速搭建出逼真的三维地理场景。
  • 丰富的地理数据支持:支持多种地理空间数据格式,如 GeoJSON、KML、CZML 等,可以方便地加载和展示各种地理信息。
  • 实时交互与分析能力:提供了丰富的交互功能,如缩放、平移、旋转等,同时具备强大的分析工具,如距离测量、面积计算、视线分析等,满足地理信息系统(GIS)的专业需求。
应用场景
  • 地理信息系统(GIS):广泛应用于城市规划、土地管理、环境监测等领域,帮助用户直观地分析和处理地理数据。例如城市规划部门可以利用 Cesium 创建城市的三维模型,进行规划方案的模拟和评估。
  • 军事与航空航天:在军事模拟、飞行仿真、卫星轨道预测等方面发挥重要作用,为相关领域提供准确的地理空间信息和可视化支持。
  • 灾害应急与管理:用于灾害预警、灾情评估和应急响应等工作,通过展示灾害发生区域的三维地理信息,帮助决策者更好地制定应对策略。

 3.介绍cesium的3dtiles

1. 3D Tiles 概述

3D Tiles 是 Cesium 提出的一种用于流式传输和渲染大规模三维地理空间数据集的开放规范。在处理大规模三维地理数据(如城市建筑、地形、点云等)时,传统的三维数据格式在数据加载、渲染效率等方面存在诸多挑战,而 3D Tiles 规范通过将数据组织成层次化结构,实现了数据的按需加载和高效渲染,能显著提升大规模三维场景的展示性能。

2. 3D Tiles 的特点

  • 层次化结构
    • 3D Tiles 采用四叉树或八叉树等空间索引结构,将大规模的三维数据集划分为多个瓦片(Tiles)。每个瓦片代表一定地理范围内的三维数据,并且瓦片之间存在父子关系,形成层次化结构。当用户浏览三维场景时,系统可以根据相机的位置和视角,动态加载和渲染所需的瓦片,减少不必要的数据传输和渲染开销。
  • 多类型支持
    • 支持多种三维数据类型,包括倾斜摄影模型(如 OBJ、OSGB 转换而来)、点云数据、BIM(建筑信息模型)数据等。这使得不同来源和格式的三维数据可以统一采用 3D Tiles 规范进行组织和管理,方便在 Cesium 等平台上进行集成和展示。
  • 动态加载与卸载
    • 可以根据用户的交互行为(如缩放、平移、旋转等)动态地加载和卸载瓦片。当用户靠近某个区域时,系统会自动加载该区域更详细的瓦片;当用户远离某个区域时,相应的瓦片会被卸载,释放系统资源。这种动态加载和卸载机制确保了在有限的带宽和计算资源下,能够流畅地浏览大规模三维场景。

3. 在 Cesium 中使用 3D Tiles

加载 3D Tiles 数据

以下是一个简单的示例代码,展示了如何在 Cesium 中加载 3D Tiles 数据:

// 加载 3D Tiles 数据
        const tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
            url: 'path/to/your/3dtiles/tileset.json'
        }));

        // 定位到 3D Tiles 数据所在区域
        viewer.zoomTo(tileset);

 4.加载3dtiles遇到问题的解决

1. 数据加载失败

问题表现
  • 浏览器控制台显示网络请求错误,3D Tiles 数据未能正常加载,场景中没有显示相应的 3D 模型。
可能原因及解决办法
  • URL 路径错误
    • 原因:指定的 tileset.json 文件路径有误,导致 Cesium 无法找到对应的 3D Tiles 数据集。
    • 解决办法:仔细检查 url 参数中的路径是否正确,确保路径与实际文件存储位置一致。可以在浏览器中直接访问该路径,验证文件是否可以正常获取。例如:
  • 跨域问题
    • 原因:如果 3D Tiles 数据存储在不同的域名下,浏览器的同源策略会阻止数据的加载。
    • 解决办法
      • 服务器端配置 CORS:在服务器端配置跨域资源共享(CORS),允许来自其他域名的请求访问 3D Tiles 数据。例如,在 Node.js 中使用 cors 中间件
  • 代理服务器:使用代理服务器将请求转发到目标服务器,绕过浏览器的同源策略。例如,在开发环境中可以使用 Webpack 或 Vite 的代理配置。

 

2. 3D Tiles 显示异常

问题表现

  • 3D Tiles 模型显示不完整、变形、颜色异常等。
可能原因及解决办法

  • 数据损坏或格式不兼容
    • 原因:3D Tiles 数据在转换或传输过程中可能发生损坏,或者数据格式不符合 3D Tiles 规范。
    • 解决办法:重新生成 3D Tiles 数据,确保数据转换过程正确。可以使用专业的 3D 数据处理工具(如 FME、Cesium ion 等)进行数据转换,并验证转换后的数据是否符合规范。
  • 材质和纹理问题
    • 原因:3D Tiles 模型的材质和纹理文件缺失或路径错误,导致模型显示异常。
    • 解决办法:检查材质和纹理文件的存储路径,确保路径正确。如果文件缺失,可以尝试重新导出或获取这些文件。同时,注意材质和纹理文件的格式是否被 Cesium 支持。

3. 性能问题

问题表现
  • 加载 3D Tiles 数据时页面卡顿、响应缓慢,帧率较低。
可能原因及解决办法
  • 数据量过大
    • 原因:3D Tiles 数据集包含大量的模型和纹理信息,超出了浏览器和设备的处理能力。
    • 解决办法
      • 数据优化:对 3D Tiles 数据进行简化和压缩,减少模型的面数和纹理的分辨率。可以使用 3D 建模软件或专业的数据处理工具进行数据优化。
      • 渐进式加载:Cesium 支持渐进式加载,可以根据相机的位置和视角动态加载和卸载瓦片,减少不必要的数据传输和渲染开销。确保 3D Tiles 数据采用了合理的层次化结构。
  • 硬件性能不足
    • 原因:用户的设备硬件配置较低,无法满足 3D 场景的渲染需求。
    • 解决办法:建议用户升级硬件设备,或者调整 Cesium 的渲染设置,降低场景的复杂度。例如,减少光照效果、关闭阴影等。可以通过以下代码调整渲染设置:
      viewer.scene.globe.enableLighting = false; // 关闭光照效果
      viewer.scene.shadowMap.enabled = false; // 关闭阴影

    • 4. 样式定制问题

      问题表现
    • 对 3D Tiles 应用样式后,没有达到预期的显示效果。
    • 可能原因及解决办法
    • 样式语法错误
      • 原因:在使用 Cesium3DTileStyle 类定义样式时,样式语法可能存在错误。
      • 解决办法:仔细检查样式代码,确保语法正确。可以参考 Cesium 官方文档中的样式语法说明进行调试。例如,正确设置模型颜色的样式代码如下:
        const style = new Cesium.Cesium3DTileStyle({
            color: "color('red')"
        });
        tileset.style = style;
      • 数据属性不匹配
        • 原因:样式中引用的数据属性在 3D Tiles 数据中不存在,导致样式无法正确应用。
        • 解决办法:检查 3D Tiles 数据的属性信息,确保样式中引用的属性名称与数据中的属性名称一致。可以通过 tileset.featureProperties 方法查看数据的属性信息。

5.处理数据的软件 

GIS(地理信息系统)处理数据的软件有很多种,不同软件适用于不同的场景和需求,以下为你详细介绍常见的 GIS 数据处理软件:

ArcGIS
  • 简介:由 Esri 公司开发,是全球应用最广泛的 GIS 软件之一。它提供了一套完整的 GIS 解决方案,涵盖数据采集、处理、分析、可视化等各个环节。
  • 特点
    • 功能强大:具备丰富的空间分析工具,如缓冲区分析、叠加分析、网络分析等,可满足各种复杂的地理数据分析需求。
    • 数据兼容性好:支持多种数据格式的导入和导出,包括 Shapefile、GeoDatabase、KML 等,方便与其他系统进行数据交互。
    • 应用领域广泛:在城市规划、自然资源管理、环境保护、交通、公共安全等领域都有广泛应用。
  • 适用场景:适合专业的 GIS 机构、政府部门和大型企业进行大规模地理数据处理和分析。
QGIS
  • 简介:是一款开源的跨平台 GIS 软件,具有丰富的功能和活跃的社区支持。
  • 特点
    • 开源免费:用户可以自由使用、修改和分发软件,降低了使用成本。
    • 插件丰富:拥有大量的第三方插件,可扩展软件的功能,如数据处理、空间分析、地图制作等。
    • 跨平台支持:可在 Windows、Mac OS、Linux 等多种操作系统上运行。
  • 适用场景:适用于各类科研机构、教育机构以及对成本敏感的用户进行地理数据处理和分析。

SuperMap

(超图软件)是一款知名的 GIS 软件,在地理信息系统领域应用广泛,下面从基本概况、特点、应用场景等方面详细介绍:

基本概况

SuperMap 是由北京超图软件股份有限公司研发的一系列 GIS 软件产品,涵盖桌面端、服务器端、移动端等多种应用形态,能够为不同行业和用户提供全面的地理信息系统解决方案。

特点

  • 功能全面
    • 数据处理:支持多种地理数据格式的导入、导出和转换,如 Shapefile、GeoJSON、KML 等。可以对矢量数据、栅格数据进行编辑、处理和分析,包括数据的拓扑检查、投影转换、裁剪、拼接等操作。
    • 空间分析:具备丰富的空间分析工具,例如缓冲区分析、叠加分析、网络分析、地形分析等。通过这些分析功能,能够挖掘地理数据背后的潜在信息,为决策提供支持。
    • 地图制图:提供了强大的地图制图功能,可制作出高质量的专题地图。支持对地图元素的样式设置、符号化表达、注记配置等,还能实现地图的排版、输出和打印。
  • 跨平台与兼容性
    • 多平台支持:SuperMap 软件支持在 Windows、Linux、Mac OS 等多种操作系统上运行,并且提供了桌面端、服务器端、移动端等不同版本,方便用户在不同设备和环境下使用。
    • 数据兼容:能够与其他常见的 GIS 软件进行数据交互,如 ArcGIS、QGIS 等,便于用户整合不同来源的地理数据。

 6.cesium抗锯齿

在 Cesium 中,抗锯齿是提升三维地理场景渲染质量的重要手段,它可以减少图形边缘的锯齿状瑕疵,让场景看起来更加平滑和自然。下面从原理、开启抗锯齿的方法以及相关注意事项等方面详细介绍。

全局抗锯齿设置

在创建 Viewer 实例时,可以通过 antialias 选项来开启全局抗锯齿。antialias 是一个布尔值,默认值为 true,即默认开启抗锯齿。示例代码如下:

 // 创建 Viewer 实例并开启抗锯齿
        const viewer = new Cesium.Viewer('cesiumContainer', {
            antialias: true // 开启抗锯齿,此为默认值,可不写
        });
特定场景的抗锯齿调整

有时候,全局抗锯齿设置可能无法满足特定场景的需求,你可以通过修改 Scene 对象的 postProcessStages 属性来调整抗锯齿效果。例如,使用 FXAA(快速近似抗锯齿)后处理阶段:

// 获取场景对象
const scene = viewer.scene;

// 开启 FXAA 抗锯齿
scene.postProcessStages.fxaa.enabled = true;

7.czml

CZML(Cesium Markup Language)是一种基于 JSON 的开放格式,专为描述随时间变化的地理数据和场景而设计,主要与 CesiumJS(一个用于创建基于 Web 的三维地理信息系统(GIS)的开源 JavaScript 库)配合使用。以下为你详细介绍:

基本概述

  • 用途:CZML 允许用户在 Cesium 的虚拟地球环境中动态展示地理数据,这些数据可以包括点、线、面、模型等各种地理要素,并且可以定义它们随时间的变化,如位置移动、颜色改变、大小缩放等。
  • 结构特点:采用 JSON 数据结构,易于阅读、编写和解析,同时方便与其他基于 Web 的系统进行集成。

主要组成部分

  • ID:每个 CZML 对象都有一个唯一的 ID,用于标识该对象。通过 ID,可以在后续的更新中对特定的对象进行修改或删除操作。
  • 属性:定义了对象的各种属性,如位置、外观、时间等。不同类型的对象具有不同的属性集合。例如,对于一个点对象,可能会定义其位置(经纬度和高度)、颜色、大小等属性;对于一个模型对象,可能会定义其模型文件的 URL、姿态等属性。
  • 时间动态:CZML 的一个重要特性是支持时间动态数据。可以为对象的属性指定随时间变化的值,通过定义不同时间点的属性值,Cesium 可以自动进行插值计算,实现对象在时间上的平滑变化。

示例代码

以下是一个简单的 CZML 示例,用于在 Cesium 中创建一个随时间移动的点:

[
    {
        "id": "pointObject",
        "position": {
            "cartographicDegrees": [
                0, 0, 0, 0,
                1000, 10, 0, 0,
                2000, 20, 0, 0
            ]
        },
        "point": {
            "color": {
                "rgba": [255, 0, 0, 255]
            },
            "pixelSize": 10
        }
    }
]

在这个示例中:

  • "id": "pointObject":定义了对象的唯一 ID 为 pointObject
  • "position":定义了点的位置。"cartographicDegrees" 数组中,每四个值为一组,分别表示时间(以毫秒为单位)、经度、纬度和高度。这里表示在 0 毫秒时,点位于经度 0、纬度 0、高度 0 的位置;在 1000 毫秒时,点移动到经度 10、纬度 0、高度 0 的位置;在 2000 毫秒时,点移动到经度 20、纬度 0、高度 0 的位置。
  • "point":定义了点的外观属性。"color" 指定了点的颜色为红色(RGBA 值为 [255, 0, 0, 255]),"pixelSize" 指定了点的大小为 10 像素。

应用场景

  • 地理信息可视化:在地理信息系统(GIS)领域,CZML 可以用于展示随时间变化的地理数据,如气象数据(如台风路径、气温变化等)、交通数据(如车辆轨迹、航班动态等)、环境监测数据等。
  • 模拟和仿真:在军事、航空航天等领域,CZML 可以用于模拟和仿真场景,如作战模拟、卫星轨道预测、飞行器飞行轨迹模拟等。通过定义对象的初始状态和随时间的变化规律,可以在 Cesium 中直观地展示模拟过程和结果。
  • 历史数据展示:对于历史事件的地理可视化展示,CZML 可以帮助用户呈现历史数据随时间的演变过程,如城市发展、人口迁移、战争进程等。用户可以通过时间滑块等工具,回顾不同时间点的地理信息。

在 Cesium 中使用 CZML

以下是一个简单的 HTML 示例,展示了如何在 Cesium 中加载和显示 CZML 数据:

// 初始化 Cesium Viewer
        const viewer = new Cesium.Viewer('cesiumContainer');

        // 加载 CZML 文件
        const czmlDataSource = new Cesium.CzmlDataSource();
        czmlDataSource.loadUrl('path/to/your/file.czml').then(() => {
            viewer.dataSources.add(czmlDataSource);
            viewer.zoomTo(czmlDataSource);
        }).catch((error) => {
            console.error('Error loading CZML:', error);
        });

 在上述代码中,通过 Cesium.CzmlDataSource 加载 CZML 文件,并将其添加到 Cesium Viewer 中进行显示。需要将 'path/to/your/file.czml' 替换为实际的 CZML 文件路径。

8.着色器

着色器(Shader)是一种运行在图形处理单元(GPU)上的小程序,在计算机图形学中扮演着至关重要的角色,它能够高效地处理图形渲染过程中的各种计算任务。以下从着色器的基本概念、常见类型、工作原理、应用场景等方面进行详细介绍:

基本概念

着色器本质上是一种专门为图形处理设计的程序,其主要目的是计算和确定图形的颜色、光照、纹理等视觉属性。在图形渲染流程中,CPU 负责处理场景的整体逻辑和数据组织,而着色器则在 GPU 上并行处理大量的图形数据,以实现高效的图形渲染。

js方面的

1.最新es6 和es7 的特性 和兼容性

ES6(ECMAScript 2015)特性及兼容性

特性
  1. 块级作用域(let 和 const)
    • let:用于声明块级作用域的变量,解决了 var 存在的变量提升和作用域问题。
    • const:用于声明常量,一旦声明必须赋值,且不能再重新赋值,但如果常量是引用类型,其内部属性可以修改。
{
  let a = 10;
  const b = 20;
  // a 和 b 只在这个块级作用域内有效
}

箭头函数

  • 提供了更简洁的函数定义语法,并且没有自己的 thisargumentssuper 或 new.target。它的 this 值继承自外层函数。
const add = (a, b) => a + b;

模板字符串

  • 使用反引号(`)来定义字符串,可以嵌入表达式,支持多行字符串。
const name = 'John';
const message = `Hello, ${name}!`;

 解构赋值

  • 可以从数组或对象中提取值并赋值给变量。
// 数组解构
const [x, y] = [1, 2];
// 对象解构
const { foo, bar } = { foo: 'value1', bar: 'value2' };

 默认参数

  • 函数参数可以设置默认值。
function greet(name = 'Guest') {
  return `Hello, ${name}!`;
}

扩展运算符(...)

  • 可以将数组或对象展开。
// 数组展开
const arr1 = [1, 2];
const arr2 = [...arr1, 3, 4];
// 对象展开
const obj1 = { a: 1 };
const obj2 = { ...obj1, b: 2 };

类和继承

  • 引入了 class 关键字来定义类,使用 extends 实现继承,提供了更面向对象的编程方式。

 

class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  speak() {
    console.log(`${this.name} barks.`);
  }
}

Promise 对象

  • 用于处理异步操作,避免回调地狱。有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。
    const promise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('Success');
      }, 1000);
    });
    
    promise.then(result => {
      console.log(result);
    }).catch(error => {
      console.error(error);
    });
    兼容性

    ES6 特性在现代浏览器中得到了较好的支持。例如,Chrome、Firefox、Safari 等主流浏览器的较新版本都能很好地支持大部分 ES6 特性。对于旧版本浏览器或一些特定环境(如 IE),可以使用 Babel 等工具将 ES6 代码转换为 ES5 代码以实现兼容。

ES7(ECMAScript 2016)特性及兼容性

特性
  1. 指数运算符()**
    • 提供了一种更简洁的方式来计算指数。
const result = 2 ** 3; // 相当于 Math.pow(2, 3)

 Array.prototype.includes()

  • 用于判断数组中是否包含某个元素,返回布尔值。
const arr = [1, 2, 3];
const hasTwo = arr.includes(2); // true
兼容性

ES7 特性的兼容性和 ES6 类似,现代浏览器对这些特性的支持也比较好。对于需要兼容旧环境的情况,同样可以使用 Babel 进行代码转换。在 Node.js 环境中,较新的版本也能很好地支持 ES7 特性。

2. DOM的理解和操作

基本概念

DOM(Document Object Model)即文档对象模型,它是针对 HTML 和 XML 文档设计的一种编程接口。可以将其想象成一种树形结构,把网页文档里的每个部分(如元素、属性、文本等)都看作树中的节点,这些节点之间存在着父子、兄弟等关系。通过这个模型,JavaScript 等脚本语言能够访问、修改、删除文档中的元素和内容,实现网页的动态交互效果。

节点类型
  • 元素节点:代表 HTML 标签,如 <html><body><div> 等。它们是 DOM 树的主要组成部分,可包含其他元素节点或文本节点。
  • 文本节点:包含元素中的文本内容。例如,<p>Hello, World!</p> 中的 “Hello, World!” 就是一个文本节点。
  • 属性节点:表示元素的属性,如 idclasssrc 等。每个元素节点可以有多个属性节点。

2.操作 DOM 

访问 DOM 节点
  • 根据 ID 访问:使用 document.getElementById() 方法可以通过元素的 id 属性获取特定的元素节点。这是一种非常高效的访问方式,因为 id 在文档中是唯一的。
    const element = document.getElementById('myElement');
  • 根据标签名访问document.getElementsByTagName() 方法可以返回具有指定标签名的元素节点集合。返回的是一个 HTMLCollection 对象,类似于数组。等。。。。。

修改 DOM 节点

  • 修改内容:可以使用 innerHTML 或 textContent 属性来修改元素的内容。innerHTML 可以解析 HTML 标签,而 textContent 只处理纯文本。
    const element = document.getElementById('myElement');
    element.innerHTML = '<strong>New content</strong>';
    element.textContent = 'New text content';
  • 修改属性:直接通过元素节点的属性名来修改元素的属性。
    const img = document.getElementById('myImage');
    img.src = 'new-image.jpg';
  • 修改样式:使用 style 属性可以修改元素的内联样式
const element = document.getElementById('myElement');
element.style.color = 'red';
element.style.fontSize = '20px';
创建和插入 DOM 节点
  • 创建元素节点:使用 document.createElement() 方法可以创建一个新的元素节点
  • const newDiv = document.createElement('div');
  • 创建文本节点:使用 document.createTextNode() 方法可以创建一个新的文本节点。
    const textNode = document.createTextNode('This is a new text');
  • 插入节点:使用 appendChild() 方法可以将一个节点作为子节点添加到另一个节点的末尾;使用 insertBefore() 方法可以将一个节点插入到指定节点之前。
    const parent = document.getElementById('parentElement');
    parent.appendChild(newDiv);
    
    const referenceElement = document.getElementById('reference');
    parent.insertBefore(newDiv, referenceElement);
    删除 DOM 节点

    使用 removeChild() 方法可以删除指定父节点的子节点。

    const parent = document.getElementById('parentElement');
    const child = document.getElementById('childElement');
    parent.removeChild(child);

    操作 DOM 的影响和注意事项

  • 性能影响:频繁的 DOM 操作会导致浏览器的重排(reflow)和重绘(repaint),影响页面的性能。例如,每次修改元素的大小、位置等属性时,浏览器可能需要重新计算元素的布局(重排),并重新绘制元素(重绘)。为了减少性能开销,可以批量进行 DOM 操作,或者使用文档片段(DocumentFragment)来临时存储要插入的节点。
  • 事件绑定:在操作 DOM 时,需要注意事件绑定的问题。如果在删除节点时没有正确解绑事件,可能会导致内存泄漏。可以使用 removeEventListener() 方法来解绑事件。
    const button = document.getElementById('myButton');
    const clickHandler = function() {
        console.log('Button clicked');
    };
    button.addEventListener('click', clickHandler);
    // 解绑事件
    button.removeEventListener('click', clickHandler);

    3.vue中虚拟dom

1. 虚拟 DOM 的定义与本质

  • 定义:虚拟 DOM(Virtual DOM)是一种轻量级的 JavaScript 对象,它是真实 DOM 树的抽象表示。在 Vue 里,虚拟 DOM 以树状结构组织,树中的每个节点被称为虚拟节点(VNode),这些虚拟节点包含了真实 DOM 元素的相关信息,如标签名、属性、子节点等。
  • 本质:本质上是 JavaScript 对象,通过 JavaScript 的计算性能来模拟 DOM 操作,以此提升页面渲染的效率。

2. 工作原理

2.1 初始化渲染
  • 模板编译:Vue 会把组件内的模板代码编译成渲染函数(render 函数)。例如,以下 Vue 模板:
1. 虚拟 DOM 的定义与本质
定义:虚拟 DOM(Virtual DOM)是一种轻量级的 JavaScript 对象,它是真实 DOM 树的抽象表示。在 Vue 里,虚拟 DOM 以树状结构组织,树中的每个节点被称为虚拟节点(VNode),这些虚拟节点包含了真实 DOM 元素的相关信息,如标签名、属性、子节点等。
本质:本质上是 JavaScript 对象,通过 JavaScript 的计算性能来模拟 DOM 操作,以此提升页面渲染的效率。
2. 工作原理
2.1 初始化渲染
模板编译:Vue 会把组件内的模板代码编译成渲染函数(render 函数)。例如,以下 Vue 模板:

 编译后可能生成如下简化的渲染函数:

function render(h) {
  return h('div', [
    h('p', this.message)
  ]);
}

这里的 h 是 createElement 函数的别名,用于创建虚拟节点。

  • 生成虚拟 DOM 树:调用渲染函数,它会返回一个虚拟 DOM 树。这个树由一系列的虚拟节点构成,每个节点对应着一个真实 DOM 元素或组件。
  • 创建真实 DOM:Vue 根据生成的虚拟 DOM 树创建对应的真实 DOM 节点,并将其挂载到页面指定的位置。
2.2 数据更新时的操作
  • 数据响应式监听:Vue 利用响应式原理对数据进行监听。当组件的数据发生变化时,会触发相应的更新流程。
  • 重新生成虚拟 DOM 树:数据变化后,Vue 会重新调用渲染函数,生成一个新的虚拟 DOM 树。
  • Diff 算法比较:使用 Diff 算法将新生成的虚拟 DOM 树和旧的虚拟 DOM 树进行对比,找出两者之间的差异。Diff 算法采用同层比较策略,只比较同一层级的节点,极大地减少了比较的复杂度。
  • 更新真实 DOM:根据 Diff 比较的结果,只对真实 DOM 中发生变化的部分进行更新,避免了对整个 DOM 树的重新渲染。

3. 虚拟 DOM 的优势

3.1 提升性能
  • 减少 DOM 操作次数:直接操作真实 DOM 代价高昂,因为每次操作都可能引发浏览器的重排和重绘。而虚拟 DOM 借助 Diff 算法找出差异后,会将多次的 DOM 操作合并为一次,从而减少了实际的 DOM 操作次数,提高了性能。
  • 批量更新:在数据发生多次变化时,虚拟 DOM 会把这些变化暂存起来,在合适的时机一次性更新到真实 DOM 上,避免了频繁更新带来的性能损耗。
3.2 跨平台能力

由于虚拟 DOM 是纯 JavaScript 对象,不依赖于特定的 DOM 环境,因此可以在不同的平台上使用,比如进行服务器端渲染(SSR)、开发移动端应用(如使用 Vue Native)等。

3.3 提高可维护性

虚拟 DOM 为开发者提供了一个抽象层,让开发者可以更专注于业务逻辑的实现,而无需过多关注底层的 DOM 操作细节,使得代码的可维护性和可读性更高。

4. 虚拟 DOM 的局限性

4.1 内存占用

虚拟 DOM 需要在内存中维护一份与真实 DOM 对应的 JavaScript 对象,这会占用一定的内存空间。对于一些对内存要求极高的应用,可能会有一定的影响。

4.2 首次渲染性能

在首次渲染时,由于需要进行模板编译、生成虚拟 DOM 树等额外步骤,相较于直接操作真实 DOM 会有一些性能开销。不过,在后续的数据更新场景中,虚拟 DOM 的优势会更加显著。

 4.后台管理其他的token。权限问题

当面试中遇到 “后台管理其他的 token 权限” 相关问题时,可能面试官想考察你对后台管理系统中 Token 和权限管理更深入、全面的了解。以下从问题可能的含义、回答思路和示例回答来为你应对这种情况提供参考。

问题可能的含义

  1. Token 的其他应用场景:除了常见的身份验证和授权,Token 在后台管理系统里可能还有别的用途,例如防止 CSRF(跨站请求伪造)攻击、用于消息通知的身份标识等。
  2. 权限管理的其他维度或方法:除了传统的基于角色的访问控制(RBAC),可能想了解你是否知道基于属性的访问控制(ABAC)、多因素权限验证等其他权限管理方式;也可能想考察对细粒度权限控制(如字段级、行级权限)的理解。
  3. Token 与权限管理的其他关联和优化:例如 Token 的续签策略、权限缓存机制、如何利用 Token 实现动态权限更新等。

回答思路

  1. 确认问题:如果问题表述比较模糊,你可以礼貌地向面试官进一步确认问题的具体指向,例如 “您提到的‘其他的 token 权限’,是指 Token 在权限管理方面的其他应用场景,还是其他类型的权限管理方式呢?”
  2. 全面阐述知识体系:按照你对问题的理解,从多个角度进行回答。可以先简要回顾常见的 Token 和权限管理概念,再重点阐述其他方面的内容。
  3. 结合实际案例:如果有相关的项目经验,可以适当举例说明,增强回答的可信度和说服力。

示例回答

针对 Token 其他应用场景

“我理解在后台管理系统中,Token 最常见的用途是身份验证和授权。不过,它还有其他一些应用场景。比如在防止 CSRF 攻击方面,服务器可以为每个用户生成一个唯一的 Token,并将其嵌入到表单或页面中。当用户提交请求时,客户端需要将这个 Token 一同发送给服务器,服务器验证 Token 的有效性,以此确保请求是由合法用户发起的。

在我的上一个项目中,我们的后台管理系统涉及到大量的表单提交操作。为了防止 CSRF 攻击,我们就采用了这种 Token 机制。服务器在生成页面时会为每个表单添加一个隐藏的 Token 字段,客户端提交表单时会携带该 Token,服务器在接收请求后会验证 Token 的正确性,有效避免了 CSRF 攻击。”

针对权限管理其他维度或方法

“在后台管理系统中,基于角色的访问控制(RBAC)是比较常见的权限管理方式。但除此之外,还有基于属性的访问控制(ABAC)。ABAC 会根据用户的属性(如部门、职位、工作年限等)、资源的属性(如数据的机密级别、所属项目等)以及环境条件(如时间、地点等)来动态地决定用户是否有权限访问某个资源。这种方式更加灵活,可以实现更细粒度的权限控制。

另外,在一些对安全性要求较高的系统中,还会采用多因素权限验证。除了传统的用户名和密码,还会结合 Token(如短信验证码、硬件令牌等)进行验证,大大提高了系统的安全性。在我参与的一个金融类后台管理项目中,就采用了多因素权限验证,用户登录时除了输入用户名和密码,还需要输入手机短信验证码,确保只有合法用户才能访问系统。”

针对 Token 与权限管理的其他关联和优化

“在 Token 与权限管理的关联方面,除了常见的在 Token 中携带权限信息,还可以通过 Token 实现动态权限更新。例如,当用户的角色或权限发生变化时,服务器可以生成一个新的 Token 并推送给客户端,客户端使用新的 Token 进行后续请求,从而实现权限的实时更新。

关于 Token 的续签策略,为了避免用户频繁登录,可以采用滑动窗口机制。当用户的 Token 即将过期时,如果用户仍在进行操作,服务器可以为用户续签 Token,延长其有效期。在权限缓存方面,可以将用户的权限信息缓存在 Redis 等缓存系统中,减少每次请求时对数据库的查询,提高系统的响应速度。在之前的项目中,我们就采用了这种方式,将用户的权限信息缓存到 Redis 中,权限验证的响应时间明显缩短。”

5.后端联调

后端联调是开发过程中非常关键的环节,它主要指的是前后端开发人员协作,对前端页面发送的请求与后端接口的处理逻辑和返回数据进行匹配验证,确保整个系统能够正常运行。以下从联调前的准备、联调流程、常见问题及解决办法几个方面详细介绍:

联调前的准备

后端方面
  • 接口文档准备:后端开发者需要编写详细的接口文档,明确每个接口的功能、请求方式(如 GET、POST 等)、请求参数(包括参数名、类型、是否必填等)、返回数据格式(如 JSON 结构)以及可能出现的错误码和对应的错误信息。例如,一个用户登录接口的文档可能如下:
    • 接口功能:用户登录验证
    • 请求方式:POST
    • 请求参数
      参数名 类型 是否必填 描述
      username string 用户名
      password string 用户密码
    • 返回数据格式
      {
          "code": 200,
          "message": "登录成功",
          "data": {
              "token": "xxxxxx"
          }
      }

    • 错误码及信息
      错误码 错误信息 描述
      400 用户名或密码错误 输入的用户名或密码不正确
      500 服务器内部错误 服务器处理请求时出现异常
    • 接口开发与自测:后端完成接口的开发后,需要进行充分的自测。可以使用工具如 Postman、Swagger 等模拟前端的请求,验证接口的功能是否正常,返回的数据是否符合预期。
    • 前端方面
    • 理解接口文档:前端开发者要仔细阅读后端提供的接口文档,明确每个接口的使用方式和返回数据结构,以便在代码中正确调用接口。
    • 前端代码开发:根据接口文档,完成前端页面的开发,实现与后端接口的交互逻辑。例如,在 JavaScript 中使用 fetch 或 axios 等库发送请求
      // 使用 axios 发送登录请求
      import axios from 'axios';
      
      const login = async () => {
          const username = 'testuser';
          const password = 'testpassword';
          try {
              const response = await axios.post('/api/login', {
                  username,
                  password
              });
              console.log(response.data);
          } catch (error) {
              console.error(error);
          }
      };

6.前端项目的优化 

代码层面
  • HTML 优化:去除了大量不必要的标签嵌套和空标签,保证结构简洁。同时使用语义化标签,像 <header><main><footer> 等,这不仅让代码可读性提升,对 SEO 也有帮助。
  • CSS 优化:把重复的样式提取出来写成公共类,减少代码冗余。还对代码进行了压缩,去除空格和注释。另外,避免了内联样式和过深的选择器嵌套,提高了样式的加载和解析效率。
  • JavaScript 优化:采用模块化开发,用 ES6 的 import 和 export 规范组织代码,提升了代码的可维护性。对代码进行了压缩和混淆,减少文件大小。同时,避免使用全局变量,防止命名冲突。
资源加载优化
  • 图片优化:将大部分图片替换成 WebP 格式,在保证质量的前提下大幅减小了文件大小。并且实现了图片懒加载,当图片滚动到可视区域时才加载,减少了首屏加载的资源量。
  • 字体优化:按需加载字体,只加载页面中用到的字符集,还生成了字体子集。使用 <link rel="preload"> 预加载字体,加快字体显示速度。
  • 脚本和样式文件:把多个 CSS 和 JavaScript 文件合并成一个,减少 HTTP 请求次数。对不影响首屏渲染的脚本采用异步加载,通过 async 和 defer 属性,让页面可以在脚本加载的同时继续渲染。
性能优化
  • 缓存机制:合理设置 HTTP 缓存头,对于不经常变化的静态资源,如图片、CSS、JavaScript 等,设置较长的缓存时间,减少重复请求。还利用 localStorage 缓存用户的部分信息和配置数据,加快页面响应。
  • 懒加载:除了图片懒加载,在路由层面也做了优化。使用路由懒加载技术,当用户访问某个路由时才加载对应的组件,减少首屏加载的代码量。
用户体验优化
  • 首屏加载优化:设计了骨架屏,在页面内容加载之前显示大致的页面结构,让用户能感知到页面正在加载,提升等待时的体验。
  • 动画和交互优化:避免频繁修改 DOM 元素的布局样式,多用 transform 和 opacity 属性做动画,减少重排和重绘。对于滚动、窗口大小改变等频繁触发的事件,使用节流和防抖技术,减少事件处理函数的执行次数,提升交互流畅度。

通过这些优化措施,页面加载时间缩短了约 35%,首屏渲染时间缩短了 40%,用户跳出率明显降低,转化率有所提升。在这个过程中,我也意识到要持续关注性能指标,根据用户反馈和业务变化及时调整优化策略。

突出性能指标型回答

在我之前参与的一个企业官网前端项目中,最初页面的性能指标不太理想,像首次内容绘制(FCP)时间较长,累积布局偏移(CLS)数值较大,影响了用户体验和搜索引擎排名。下面是我采取的优化步骤和取得的效果。

发现问题

借助 Google Lighthouse、Chrome DevTools 等工具对页面进行性能分析。发现图片文件过大、CSS 和 JavaScript 加载阻塞渲染、首屏渲染元素过多是主要问题。

优化策略与实施
  • 图片优化:将所有图片转换为 WebP 格式,并使用 TinyPNG 等工具进行压缩,平均每个图片文件大小减少了 60%。同时实现图片懒加载,减少首屏加载的图片数量。
  • 代码优化:对 HTML、CSS 和 JavaScript 代码进行压缩和合并。去除不必要的空格、注释和冗余代码,将多个 CSS 和 JavaScript 文件合并成一个,减少 HTTP 请求次数。对于 JavaScript 代码,使用 Tree - Shaking 技术去除未使用的代码。
  • 资源加载优化:对关键的 CSS 和 JavaScript 文件采用内联方式,减少外部文件的加载时间。对于非关键资源,使用异步加载,确保页面可以快速渲染。
  • 缓存机制:设置合理的 HTTP 缓存头,对于静态资源,如图片、CSS、JavaScript 等,设置较长的缓存时间。使用 localStorage 缓存一些不经常变化的数据,如网站配置信息。
优化效果

经过一系列优化,页面的性能指标有了显著提升。首次内容绘制(FCP)时间从原来的 3 秒缩短到 1.2 秒,最大内容绘制(LCP)时间从 4.5 秒缩短到 1.8 秒,累积布局偏移(CLS)从 0.2 降低到 0.05。这些优化不仅提升了用户体验,也让网站在搜索引擎中的排名有所上升。

强调新技术应用型回答

我在最近的一个前端项目优化中,积极引入了一些新的技术和理念,有效提升了项目的性能和用户体验。这个项目是一个社交类单页面应用,存在加载速度慢、交互不流畅的问题。

代码分割与动态导入

采用 Webpack 的代码分割功能,将应用代码分割成多个小块。根据用户的访问路径和功能模块,使用动态导入(import())按需加载代码。比如,用户在浏览主页时,只加载主页相关的代码,当用户点击进入聊天模块时,再动态加载聊天模块的代码。这样大大减少了首屏加载的代码量,首屏加载时间缩短了 30%。

服务端渲染(SSR)与静态站点生成(SSG)结合

为了提高首屏渲染速度和 SEO 效果,项目采用了服务端渲染和静态站点生成相结合的方式。对于一些不经常变化的页面,如关于我们、帮助中心等,使用静态站点生成(如 Next.js 的 SSG 功能),提前生成静态 HTML 文件,减少服务器端的渲染压力。对于动态内容较多的页面,如用户个人主页、消息列表等,采用服务端渲染(SSR),在服务器端渲染好 HTML 后再发送给客户端。通过这种方式,页面的 SEO 效果明显提升,同时首屏渲染速度也有了很大改善。

Web Workers 优化复杂计算

对于一些复杂的计算任务,如图片处理、数据排序等,使用 Web Workers 在后台线程中进行处理。这样可以避免阻塞主线程,保证页面的流畅交互。例如,在用户上传图片时,使用 Web Workers 对图片进行压缩和裁剪,用户可以继续进行其他操作,不会感觉到卡顿。

渐进式 Web 应用(PWA)

将项目改造成渐进式 Web 应用,添加了 Service Worker 来缓存静态资源和页面数据。当用户再次访问网站时,可以直接从缓存中加载资源,实现离线访问。同时,PWA 还支持添加到主屏幕,提供类似原生应用的体验,用户留存率提高了 20%。

面展开,以下为你详细介绍可能涉及的优化点及回答示例:

7.关于cesium优化可能涉及的优化点

数据处理优化
  • 数据简化:对地理数据进行简化处理,减少不必要的细节。例如,降低地形数据的精度、减少矢量数据的顶点数量等,以减少数据量和处理负担。
  • 数据分块与分级:采用分块和分级的方式组织地理数据。根据不同的视角和距离,加载不同精度的数据,实现数据的按需加载,提高加载效率。
渲染性能优化
  • 纹理压缩:对纹理数据进行压缩,减少纹理内存的占用。选择合适的纹理压缩格式,如 DXT、ETC 等,在保证纹理质量的前提下降低内存开销。
  • 剔除不必要的渲染对象:使用视锥体剔除、背面剔除等技术,剔除不在视锥体范围内或不可见的对象,减少渲染工作量。
  • 减少渲染状态切换:尽量减少渲染状态的频繁切换,如材质、纹理、着色器等的切换,提高渲染效率。
内存管理优化
  • 对象复用:在代码中尽量复用已经创建的对象,避免频繁创建和销毁对象,减少内存分配和垃圾回收的开销。
  • 及时释放资源:当不再使用某些资源(如纹理、模型等)时,及时释放其占用的内存,避免内存泄漏。
网络加载优化
  • 数据预加载:提前加载可能会用到的数据,减少用户等待时间。可以根据用户的操作习惯和浏览路径,预测用户可能访问的区域,并提前加载该区域的数据。
  • 缓存机制:实现数据缓存机制,将已经加载过的数据缓存到本地,当再次需要访问相同数据时,直接从缓存中读取,减少网络请求。

 

8.vue2 和和vue3的区别

架构层面

  • 响应式系统
    • Vue 2:使用 Object.defineProperty() 来实现数据的响应式。不过它存在一定局限,比如无法检测对象属性的新增或删除操作,对于数组,直接通过索引修改元素时不能自动触发视图更新,需要使用特定的数组变异方法才行。在我之前做的一个电商项目里,动态给商品对象添加属性时,就没办法直接触发视图更新,得手动调用 Vue.set 方法,比较麻烦。
    • Vue 3:采用 Proxy 对象实现响应式。Proxy 可以拦截对象的各种操作,包括属性的添加、删除等,从根本上解决了 Vue 2 的响应式缺陷,而且性能上也更出色。像在一个数据交互频繁的后台管理系统项目中,使用 Vue 3 的响应式系统,代码编写更简洁,数据更新也更顺畅。
  • 代码组织
    • Vue 2:主要依赖选项式 API(Options API),组件代码按照 datamethodscomputed 等不同选项来组织。随着组件功能变复杂,代码会变得冗长,不同功能的代码分散在各个选项中,维护起来比较困难。我参与过一个大型单页面应用项目,组件里的逻辑多了之后,找某个功能的代码就得在不同选项里来回切换。
    • Vue 3:引入了组合式 API(Composition API),允许开发者依据逻辑功能来组织代码,把相关逻辑封装在一起。这样代码更模块化,复用性和可维护性大大提高。在最近的一个项目中,我把表单验证的逻辑封装成一个函数,在多个组件中复用,代码结构清晰了很多。

语法特性层面

  • API 使用
    • Vue 2:主要使用选项式 API,组件逻辑分散在不同选项中。例如定义一个计数器组件,需要在 data 里定义数据,在 methods 里定义方法。
    • Vue 3:既支持选项式 API,更推荐使用组合式 API。以同样的计数器组件为例,使用组合式 API 代码更简洁,逻辑更集中。
  • 新增组件
    • Vue 2:没有内置 Teleport 和 Suspense 组件。如果要实现将组件内容渲染到 DOM 其他位置或者处理异步组件加载状态,需要手动编写复杂的逻辑。
    • Vue 3:引入了 Teleport 组件,能轻松将组件内容渲染到指定的 DOM 位置,比如实现模态框就很方便;还有 Suspense 组件,专门用于处理异步组件加载,在加载完成前显示加载状态,加载完成后显示组件内容。
      <template>
        <div>
          <button @click="isOpen = true">打开模态框</button>
          <teleport to="body">
            <div v-if="isOpen" class="modal">
              <p>这是一个模态框!</p>
              <button @click="isOpen = false">关闭</button>
            </div>
          </teleport>
          <Suspense>
            <template #default>
              <AsyncComponent />
            </template>
            <template #fallback>
              <p>加载中...</p>
            </template>
          </Suspense>
        </div>
      </template>
      <script setup>
      import { ref, defineAsyncComponent } from 'vue';
      const isOpen = ref(false);
      const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'));
      </script>

      性能表现层面

    • 渲染速度
      • Vue 2:处理大型组件树时,受响应式系统和虚拟 DOM 算法的限制,渲染速度会受到一定影响。在一个有大量数据展示和复杂交互的项目中,页面加载和更新都比较慢。
      • Vue 3:对虚拟 DOM 算法进行了优化,采用静态标记(Static Hoisting)和 Patch Flag 等技术,能更精准地更新需要更新的部分,减少不必要的渲染,渲染速度明显提升。同样是大型数据展示项目,使用 Vue 3 后页面响应更快。
    • 内存占用
      • Vue 2:响应式系统和组件实例创建会占用一定内存空间,处理大量数据和组件时,内存占用较高。
      • Vue 3:采用 Proxy 实现响应式,以及更高效的组件实例创建方式,内存占用相对较低。在内存资源有限的设备上,Vue 3 的优势更明显。
    • 生态系统层面

    • 插件和库兼容性
      • Vue 2:有丰富的插件和库生态,但部分插件不兼容 Vue 3。在升级项目时,可能会遇到插件适配问题。
      • Vue 3:随着时间推移,支持 Vue 3 的插件和库越来越多,但目前仍有一些旧插件需要时间来适配。
    • 工具链
      • Vue 2:常用 Vue CLI 作为构建工具,它主要针对 Vue 2 进行优化。
      • Vue 3:推荐使用 Vite 作为构建工具,Vite 冷启动和热更新速度更快,更适合 Vue 3 的开发。我在使用 Vite 开发 Vue 3 项目时,开发效率明显提高。

 9.双向数据绑定

概念阐述

双向数据绑定是指数据和视图之间建立起一种实时的同步关系。也就是说,数据的变化会自动反映到视图上,让用户能看到最新的数据展示;同时,当用户在视图上进行操作(比如输入内容、选择选项等)改变了视图状态,数据也会相应地自动更新。举个生活中的例子,就像银行账户的余额显示和实际账户金额,余额显示是视图,实际金额是数据,当账户金额发生变动时,显示的余额会马上更新;如果通过转账等操作改变了显示的余额,实际账户金额也会同步变化。

原理

Vue 2 基于 Object.defineProperty ()

在 Vue 2 中,双向数据绑定主要是通过 Object.defineProperty() 方法来实现响应式系统。该方法可以劫持对象属性的 getter 和 setter,当属性被访问时触发 getter,当属性被修改时触发 setter。以下是一个简单的示例代码:

const data = {
    message: 'Hello'
};
const proxy = new Proxy(data, {
    get(target, key) {
        console.log('正在获取', key);
        return target[key];
    },
    set(target, key, newValue) {
        console.log('正在将', key, '设置为', newValue);
        target[key] = newValue;
        // 触发视图更新
        updateView();
        return true;
    }
});
function updateView() {
    console.log('视图已更新');
}
console.log(proxy.message);
proxy.message = 'Hi';

9.v-show 和 v-if

v-show 和 v-if 都是 Vue.js 中用于控制元素显示与隐藏的指令,但它们在原理、性能和适用场景上存在明显区别。 

v-show

  • 概念v-show 指令根据表达式的值来控制元素的显示与隐藏。当表达式的值为 true 时,元素会显示;当表达式的值为 false 时,元素会隐藏。
  • 原理v-show 本质上是通过修改元素的 display CSS 属性来实现显示与隐藏的切换。当表达式为 true 时,display 属性恢复为元素原本的值;当表达式为 false 时,display 属性会被设置为 none
  • v-if
    • 概念v-if 也是根据表达式的值来决定元素是否渲染到 DOM 中。当表达式的值为 true 时,元素会被渲染到 DOM 中;当表达式的值为 false 时,元素不会被渲染,即从 DOM 中移除。
    • 原理v-if 是一种条件渲染指令,它会根据条件判断是否创建或销毁元素节点。在 Vue 的虚拟 DOM 渲染过程中,当条件满足时会创建对应的真实 DOM 节点并插入到页面中;当条件不满足时,会移除已存在的 DOM 节点。示例如下:

使用场景

  • v-show 的适用场景
    • 适用于需要频繁切换显示与隐藏状态的场景。因为 v-show 只是修改 display 属性,不会涉及 DOM 节点的创建和销毁,切换开销较小。比如在一个电商网站的商品筛选面板中,用户可能会频繁地展开和收起筛选条件,这时使用 v-show 就比较合适。
  • v-if 的适用场景
    • 适用于在运行时条件很少改变的场景。例如,根据用户的登录状态决定是否显示用户信息面板,因为用户的登录状态通常不会频繁改变,使用 v-if 可以在初始渲染时就根据条件决定是否创建 DOM 节点,避免不必要的节点创建。

性能差异

  • v-show
    • 初始渲染时,无论表达式的值是 true 还是 false,元素都会被渲染到 DOM 中,只是通过 display 属性控制显示与隐藏。因此,初始渲染的性能开销相对固定。在频繁切换显示状态时,由于只是修改 CSS 属性,性能开销较小。
  • v-if
    • 初始渲染时,如果表达式的值为 false,元素不会被渲染,减少了初始渲染的 DOM 节点数量,可能会提升初始渲染性能。但在条件切换时,涉及到 DOM 节点的创建和销毁,性能开销相对较大。

对 DOM 操作的区别

1. 初始渲染阶段
  • v-if
    当 v-if 指令绑定的表达式初始值为 false 时,对应的 DOM 元素不会被创建,也就是在初始的 DOM 树中不会存在该元素节点。只有当表达式的值变为 true 时,Vue 才会动态创建这个 DOM 元素并插入到 DOM 树中。
  • v-show
    无论 v-show 指令绑定的表达式初始值是 true 还是 false,对应的 DOM 元素都会被创建并插入到 DOM 树中。只是当表达式值为 false 时,元素的 display 样式属性会被设置为 none,使其在页面上不可见

10.v-for 中 key 的作用

在 Vue 的 v-for 指令中,key 是一个非常重要的属性,它在提升渲染效率和保证数据更新的准确性方面发挥着关键作用。

基本概念和作用

  • 提升渲染效率key 主要用于给 Vue 一个标识,帮助 Vue 识别哪些元素发生了变化。当列表中的数据发生更新时,Vue 可以根据 key 更高效地进行虚拟 DOM 的差异比较(Diff 算法),从而最小化 DOM 操作,提高渲染性能。
  • 保证数据更新的准确性:在对列表进行增删改等操作时,key 能确保每个数据项和 DOM 元素之间的对应关系保持稳定,避免出现数据和视图不匹配的问题。

使用场景和注意事项

  • 使用场景
    • 动态列表:在列表数据会动态变化的场景下,一定要使用 key。例如,一个待办事项列表,用户可以添加、删除和修改待办事项,此时为每个待办事项提供唯一的 key 能保证列表的正确更新。
    • 复杂组件列表:当 v-for 渲染的是包含表单元素、子组件等复杂内容的列表时,使用 key 可以避免组件状态混乱。
  • 注意事项
    • 唯一性key 必须是唯一的,不能重复。如果使用了重复的 key,会导致 Vue 无法正确识别元素,从而引发渲染错误。
    • 避免使用索引作为 key:在大多数情况下,不建议使用数组索引作为 key。因为当数组的顺序发生变化时,索引也会改变,这可能会导致 Vue 错误地复用元素,破坏元素的状态。比如在一个排序功能中,如果使用索引作为 key,排序后元素的索引改变,可能会使表单输入值等状态出现错乱。

 11.vue常用的修饰符

 Vue 修饰符是一种特殊的后缀,用于为指令提供额外的功能或改变其默认行为,在不同场景下能极大提升开发效率和代码的简洁性

事件修饰符

  • .stop:主要用于阻止事件冒泡。在 DOM 事件流里,事件会从触发元素开始向父元素传播,.stop 可中断这一传播过程,避免父元素的相同事件被触发。
  • .prevent:作用是阻止事件的默认行为。比如在表单提交时,表单默认会刷新页面,使用该修饰符就能阻止这种默认动作,以便用自定义逻辑处理表单数据。
  • .capture:改变事件的传播模式,采用事件捕获模式。一般事件是冒泡模式,即从内向外传播,而使用 .capture 后,事件会从外向内传播。
  • .self:保证事件仅在绑定元素本身触发时才执行回调。也就是说,只有当事件源是绑定元素,而非其子元素时,回调函数才会被调用。
  • .once:让事件只触发一次。当事件首次触发并执行回调后,后续再次触发该事件时,回调函数不会再执行。

表单修饰符

  • .lazy:把 v-model 默认的实时更新机制,改为在 change 事件触发时更新数据。默认情况下,v-model 会在输入框内容改变时立即更新绑定的数据,使用 .lazy 后,数据更新会延迟到输入框失去焦点或按下回车键时。
  • .number:将用户输入的值转换为数字类型。在处理需要数字输入的场景时,使用该修饰符可确保绑定的数据是数字类型,避免出现字符串类型的数据。
  • .trim:自动去除输入内容首尾的空格。在用户输入文本时,它能帮助清理输入,避免因首尾空格影响数据处理。

按键修饰符

  • .enter.tab.delete.esc.space 等:用于监听特定按键的事件。可以在 @keyup 或 @keydown 等事件中使用这些修饰符,只有按下指定按键时才会触发相应的回调函数,方便处理特定按键的交互逻辑。

总结

这些修饰符在不同场景下能提升开发效率和代码的简洁性。根据具体需求合理运用修饰符,可使 Vue 应用的交互逻辑更清晰、易维护

12.vue的跨域

在 Vue 项目开发中,跨域是常见问题,它源于浏览器的同源策略。同源策略要求浏览器访问的页面和请求的资源必须具有相同的协议、域名和端口,否则会受到限制。以下从产生原因、解决方案及原理等方面为你详细介绍如何解决 Vue 中的跨域问题,在面试中你可以按这个逻辑进行回答:

跨域产生的原因

浏览器的同源策略是为了保证用户信息的安全,防止不同源的脚本访问和操作其他源的敏感数据。当 Vue 项目的前端页面运行在一个域名和端口下,而请求的后端接口在另一个域名或端口时,就会触发同源策略,导致跨域问题,浏览器会阻止这样的请求。

解决方案

开发环境(使用开发服务器代理)
  • 原理:在开发环境中,利用开发服务器作为中间代理,将前端的请求转发到目标服务器,由于服务器之间的请求不受同源策略限制,从而绕过浏览器的跨域限制。
  • 实现方式(以 Vue CLI 和 Vite 为例)
    • Vue CLI:在 vue.config.js 文件中进行配置。通过 devServer.proxy 选项来设置代理规则,将特定前缀的请求转发到目标服务器。例如:
      module.exports = {
          devServer: {
              proxy: {
                  '/api': {
                      target: 'http://backend-server.com', // 目标服务器地址
                      changeOrigin: true, // 是否改变请求头中的 origin
                      pathRewrite: {
                          '^/api': '' // 路径重写,去掉请求路径中的 /api 前缀
                      }
                  }
              }
          }
      };
    • Vite:在 vite.config.js 文件中配置。使用 server.proxy 选项实现代理。示例如下:
      import { defineConfig } from 'vite';
      import vue from '@vitejs/plugin-vue';
      
      export default defineConfig({
          plugins: [vue()],
          server: {
              proxy: {
                  '/api': {
                      target: 'http://backend-server.com',
                      changeOrigin: true,
                      rewrite: (path) => path.replace(/^\/api/, '')
                  }
              }
          }
      });
      生产环境
    • JSONP(JSON with Padding)
      • 原理:JSONP 是一种古老的跨域解决方案,它利用了 <script> 标签的 src 属性不受同源策略限制的特点。前端通过动态创建 <script> 标签,向服务器请求一个 JSON 数据,并在请求的 URL 中添加一个回调函数名作为参数。服务器收到请求后,会将 JSON 数据包装在这个回调函数中返回给前端。前端的 <script> 标签会执行这个回调函数,从而获取到服务器返回的数据。
      • 缺点:只支持 GET 请求,安全性较低,容易受到 XSS 攻击。
    • CORS(Cross - Origin Resource Sharing,跨域资源共享)
      • 原理:CORS 是现代浏览器支持的跨域解决方案,它需要服务器端进行相应的配置。当浏览器发起跨域请求时,会在请求头中添加一些额外的信息,如 Origin 字段,用于标识请求的来源。服务器收到请求后,会根据配置的 CORS 规则,在响应头中添加一些特定的字段,如 Access - Control - Allow - OriginAccess - Control - Allow - Methods 等,告诉浏览器该请求是否被允许。如果浏览器验证通过,就会正常处理响应数据。
      • 实现方式:在后端服务器进行配置。以 Node.js 的 Express 框架为例:
        const express = require('express');
        const app = express();
        const cors = require('cors');
        
        // 使用 cors 中间件
        app.use(cors());
        
        // 其他路由和配置
        app.listen(3000, () => {
            console.log('Server is running on port 3000');
        });

        总结

        在 Vue 项目中,开发环境推荐使用开发服务器代理的方式解决跨域问题,简单方便且不影响生产环境。生产环境中,CORS 是更安全和通用的解决方案,而 JSONP 由于其局限性,一般较少使用。在实际开发中,需要根据具体情况选择合适的跨域解决方案。

13.vue的组件,父子键的交互

在 Vue 里,父子组件交互是非常重要的功能,它使得组件之间可以进行有效的通信和数据共享。下面我会从传值方式、事件触发以及特殊情况处理等方面详细介绍父子组件交互的相关内容。

父组件向子组件传值

  • 使用 props 属性:这是最常用的父传子方式。在父组件中,通过在子组件标签上绑定数据;在子组件里,使用 props 选项来接收这些数据。
    • 优点:清晰直观,便于理解组件之间的数据流向,且子组件可以对接收的数据进行类型校验和默认值设置。
    • 注意事项props 是单向数据流,子组件不能直接修改 props 传来的值,如果需要修改,应该通过自定义事件通知父组件进行更改。
  • 示例:在父组件中 <ChildComponent :message="parentMessage" />,子组件中 props: ['message'] 。

子组件向父组件传值

  • 使用自定义事件:子组件通过 $emit 方法触发自定义事件,并可以携带数据;父组件在子组件标签上监听这个自定义事件,当事件触发时,执行相应的方法。
    • 优点:实现了子组件与父组件的解耦,子组件只负责触发事件,父组件决定如何响应事件。
    • 示例:子组件中 this.$emit('childEvent', data),父组件中 <ChildComponent @childEvent="handleChildEvent" /> 。

父子组件间的其他交互方式

  • 访问父组件实例:子组件可以通过 $parent 访问其父组件的实例,进而调用父组件的方法和访问其数据。不过这种方式会使组件之间的耦合度增加,不建议频繁使用,通常在调试或小型项目中使用。
  • 访问子组件实例:父组件可以通过给子组件添加 ref 属性,然后使用 $refs 来访问子组件的实例,调用子组件的方法和访问其数据。比如 <ChildComponent ref="childRef" />,在父组件中通过 this.$refs.childRef 访问子组件。

特殊情况处理

  • 多层嵌套组件通信:当组件嵌套层级较深时,使用 props 和自定义事件传递数据会变得复杂。这时可以考虑使用事件总线(Event Bus)或 Vuex 状态管理库来简化通信。事件总线是一个简单的事件中心,组件可以在其中发布和订阅事件;Vuex 则适用于更复杂的状态管理场景,它提供了集中式的状态存储和修改机制。
  • 同步和异步交互:在处理父子组件交互时,要注意同步和异步操作的区别。例如,在子组件触发事件通知父组件更新数据时,如果涉及异步操作(如网络请求),要确保在数据更新完成后再进行后续操作,避免出现数据不一致的问题。

14.vue插槽的理解和使用场景 

Vue 插槽是一个强大且实用的特性,它为组件的复用和扩展提供了很大的灵活性,下面我从概念、类型、使用场景几个方面详细介绍

概念理解

Vue 插槽是一种内容分发机制,允许我们在定义组件时预留一个或多个 “占位符”,在使用该组件时,可以将自定义的内容插入到这些占位符中。简单来说,插槽就像是组件中的一个 “洞”,我们可以根据需要往这个 “洞” 里填充不同的内容,让组件更加灵活和通用。

插槽类型及使用方式

  • 默认插槽
    • 概念:最基本的插槽类型,当组件中只有一个插槽且没有指定名称时,就是默认插槽。它用于接收组件标签内的所有未指定插槽名称的内容。
    • 使用方式:在组件模板中使用 <slot></slot> 定义默认插槽,在使用组件时,直接在组件标签内写入要插入的内容。例如:
      <!-- 定义组件 -->
      <template>
        <div>
          <slot></slot>
        </div>
      </template>
      
      <!-- 使用组件 -->
      <MyComponent>
        <p>这是插入到默认插槽的内容</p>
      </MyComponent>

      具名插槽

    • 概念:当组件需要多个插槽来接收不同位置或类型的内容时,就可以使用具名插槽。每个插槽有一个唯一的名称,通过名称来区分不同的插槽。
    • 使用方式:在组件模板中使用 <slot name="插槽名"></slot> 定义具名插槽,在使用组件时,使用 <template #插槽名> 或 <template v-slot:插槽名> 来指定要插入到哪个插槽的内容
      <!-- 定义组件 -->
      <template>
        <div>
          <slot name="header"></slot>
          <slot></slot>
          <slot name="footer"></slot>
        </div>
      </template>
      
      <!-- 使用组件 -->
      <MyComponent>
        <template #header>
          <h1>这是头部内容</h1>
        </template>
        <p>这是主体内容</p>
        <template #footer>
          <p>这是底部内容</p>
        </template>
      </MyComponent>

    • 作用域插槽
      • 概念:作用域插槽允许子组件向父组件传递数据,父组件可以根据这些数据来动态渲染插入的内容。也就是说,插槽内容可以访问子组件的数据。
      • 使用方式:在组件模板中,通过 <slot :数据名="数据值"></slot> 绑定数据,在使用组件时,使用 <template #插槽名="slotProps"> 接收数据并使用。例如:
        <!-- 定义组件 -->
        <template>
          <div>
            <slot :user="user"></slot>
          </div>
        </template>
        <script>
        export default {
          data() {
            return {
              user: { name: 'John', age: 25 }
            };
          }
        };
        </script>
        
        <!-- 使用组件 -->
        <MyComponent>
          <template #default="slotProps">
            <p>用户姓名:{{ slotProps.user.name }}</p>
          </template>
        </MyComponent>

        使用场景

      • 组件复用与扩展
        • 当我们创建一个通用组件时,可能不同的使用场景需要不同的内容展示。例如,创建一个模态框组件,模态框的标题、内容和底部按钮可能在不同的使用场景下需要不同的内容,这时就可以使用插槽来实现。通过插槽,我们可以在不同的地方使用同一个模态框组件,只需要传入不同的标题、内容和按钮即可。
      • 布局组件
        • 在开发布局组件时,如页面布局、卡片布局等,插槽可以让我们更灵活地填充不同的内容。比如一个卡片组件,卡片的头部、主体和底部可能需要不同的内容,使用具名插槽可以方便地实现这种布局。
      • 列表渲染
        • 在列表渲染时,作用域插槽非常有用。例如,我们有一个列表组件,列表项的显示内容可能需要根据不同的条件进行定制。使用作用域插槽,子组件可以将列表项的数据传递给父组件,父组件根据这些数据来决定如何渲染列表项。

14.vue自定义指令 

Vue 的自定义指令是一个很实用的特性,它能让我们在 Vue 项目里对普通 DOM 元素进行底层操作,拓展 Vue 的功能。下面我从定义、钩子函数、使用场景等方面详细介绍。

自定义指令的定义方式

  • 全局指令
    • 定义:全局指令可以在整个 Vue 应用中使用。通过 Vue.directive() 方法(Vue 2)或者在 createApp 创建的应用实例上使用 app.directive() 方法(Vue 3)来定义。
    • 示例(Vue 3)
import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

// 定义一个全局自定义指令 v-focus
app.directive('focus', {
    // 当被绑定的元素插入到 DOM 中时……
    mounted(el) {
        // 聚焦元素
        el.focus();
    }
});

app.mount('#app');
  • 局部指令
    • 定义:局部指令只能在定义它的组件内部使用。在组件选项中通过 directives 选项来定义。
    • 示例(Vue 3)
      <template>
        <input v-focus />
      </template>
      
      <script>
      export default {
          directives: {
              focus: {
                  mounted(el) {
                      el.focus();
                  }
              }
          }
      };
      </script>

自定义指令的钩子函数

自定义指令有多个钩子函数,它们在不同的阶段被调用,下面是一些常用的钩子函数:

  • beforeMount:在指令第一次绑定到元素并且在挂载父组件之前调用,可用于进行一些初始化操作。
  • mounted:在绑定元素的父组件被挂载后调用,通常用于进行 DOM 操作,如上面示例中的聚焦操作。
  • beforeUpdate:在组件更新之前调用,可用于在更新前保存一些旧的数据状态。
  • updated:在组件更新之后调用,可用于根据更新后的数据对 DOM 进行相应的调整。
  • beforeUnmount:在卸载绑定元素的父组件之前调用,可用于做一些清理工作。
  • unmounted:在指令与元素解绑后调用,用于释放一些资源。

使用场景

  • 输入框聚焦:如上面提到的 v-focus 指令,当页面加载时,自动让输入框获取焦点,提升用户体验。
  • 权限控制:创建一个自定义指令来控制元素的显示与隐藏,根据用户的权限来决定是否显示某些按钮或菜单
app.directive('permission', {
    mounted(el, binding) {
        const permissions = ['admin', 'editor']; // 假设这是用户拥有的权限列表
        if (!permissions.includes(binding.value)) {
            el.style.display = 'none';
        }
    }
});

<template>
  <button v-permission="'delete'">删除</button>
</template>
  • 防抖和节流:对于一些频繁触发的事件,如按钮点击、窗口滚动等,可以使用自定义指令实现防抖或节流功能,减少性能开销。例如防抖指令:
    app.directive('debounce', {
        mounted(el, binding) {
            let timer;
            el.addEventListener('click', () => {
                if (timer) {
                    clearTimeout(timer);
                }
                timer = setTimeout(() => {
                    binding.value();
                }, 300);
            });
        }
    });
    

    指令钩子函数的参数

    指令钩子函数会接收到一些参数,这些参数可以帮助我们更好地使用指令:

  • el:指令所绑定的元素,可用于直接操作 DOM。
  • binding:一个对象,包含以下属性:
    • value:指令的绑定值,如 v-my-directive="1 + 1" 中,绑定值为 2
    • oldValue:指令绑定的前一个值,仅在 beforeUpdate 和 updated 钩子中可用。
    • arg:传给指令的参数,如 v-my-directive:foo 中,参数为 'foo'
    • modifiers:一个包含修饰符的对象,如 v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • vnode:Vue 编译生成的虚拟节点。
  • prevVnode:上一个虚拟节点,仅在 beforeUpdate 和 updated 钩子中可用。

网站公告

今日签到

点亮在社区的每一天
去签到