Bootstrap

鸿蒙面试 2025-01-11

ArkTs 和TS的关系?

ArkTS(方舟开发语言)与 TypeScript(TS)存在紧密联系,同时也有显著区别:

  • 联系
    • 语法基础:ArkTS 在语法层面大量借鉴了 TypeScript ,TypeScript 里诸如变量声明(letconst)、函数定义、类的构建、接口与类型别名等基础语法特性,在 ArkTS 中都能找到对应且相似的使用方式。这让熟悉 TypeScript 的开发者可以相对轻松地过渡到 ArkTS 开发。
    • 类型系统:二者都极为强调类型系统。TypeScript 有着一套成熟的静态类型检查体系,能提前发现代码中潜在的类型相关错误,提升代码健壮性;ArkTS 同样内置功能完备的类型检查机制,基于类型推导、类型注解等手段,助力开发者编写出更可靠的代码。
    • 面向对象编程支持:在面向对象编程范式上,二者表现出高度一致性。它们均允许开发者定义类,类中可以包含属性、方法、构造函数等元素,借此完成复杂的业务逻辑封装与复用。
  • 区别
    • 应用场景:TypeScript 是一种通用的 JavaScript 超集,广泛应用于 Web 前端、后端(搭配 Node.js )等各类 JavaScript 能涉足的开发场景,目标是为 JavaScript 生态补充类型,优化开发体验;ArkTS 则是专为鸿蒙操作系统定制开发的编程语言,聚焦于鸿蒙生态下的应用开发,例如智能穿戴设备、智能家居中控、车载系统等鸿蒙 OS 所覆盖的终端设备应用。
    • 与底层交互:ArkTS 生来就被设计成能够和鸿蒙操作系统底层能力深度融合。它可以无缝调用系统原生的功能、组件与 API ,实现诸如对设备硬件(传感器、摄像头等)的精准操控;TypeScript 基本用于上层业务逻辑编写,与底层系统交互往往需要借助一些中间层框架或者桥接技术,没有 ArkTS 这般直连底层的便利性。
    • 生态和框架:TypeScript 历经多年发展,积累起庞大且丰富的第三方库、框架以及各类开发工具的生态,开发者遇到问题时,极易找到对应的解决方案或者参考示例;ArkTS 由于问世时间较短,生态处于持续拓展阶段,虽然鸿蒙系统自身提供了一些原生的框架与组件,但在第三方资源丰富度上,暂时不及 TypeScript 。
    • 编译优化:针对鸿蒙系统的特性,ArkTS 编译器做了特殊的优化处理,让代码在鸿蒙设备上执行时更高效节能,契合物联网、移动设备等对功耗、性能有较高要求的硬件环境;TypeScript 编译成果主要适配常规的浏览器、服务器环境,编译优化方向有所不同。

 

TS泛型 和ArkTS泛型?

TS(TypeScript)的泛型与 ArkTS 的泛型有诸多相似之处,同时也因应用场景和平台特性存在一些差异:

  • 相似点
    • 代码复用与抽象:二者的泛型都致力于提升代码复用性。借助泛型,开发者能编写出不局限于特定类型的通用函数、类或者接口。例如,定义一个通用的排序函数,不管是处理数字数组、字符串数组,还是其他可比较类型的数组,都无需为每种类型单独写一个排序函数,减少了重复代码。
    • 类型安全保障:TS 和 ArkTS 的泛型体系都维持了类型安全原则。在编译阶段,编译器利用泛型参数的类型约束来排查潜在的类型不匹配问题,确保代码运行的稳定性,避免因类型错误引发的运行时异常。
    • 参数化类型表达:都支持通过泛型参数来灵活表示类型。可以把泛型参数当作类型的 “占位符”,在实际使用代码单元(函数、类等)时,再代入具体的类型,以此适应各式各样的需求。像定义一个泛型容器类,能够依据不同场景承载不同类型的数据。
  • 差异点
    • 平台适配性:TypeScript 作为通用的 JavaScript 超集,泛型设计主要适配 Web 浏览器、Node.js 服务器这类常规开发场景。它的泛型语法与特性优化围绕 JavaScript 运行环境展开;ArkTS 是服务于鸿蒙系统的,它的泛型需贴合鸿蒙系统硬件与底层框架。例如,ArkTS 的泛型在与鸿蒙系统的跨设备通信、硬件资源调用场景下,会融入适配这些功能的隐式类型约束,让开发者能更合规地利用底层资源。
    • 生态集成度:TypeScript 发展时间久,围绕泛型已经形成海量成熟的第三方库与最佳实践案例。开发者在遇到复杂的泛型使用难题时,很容易搜索到对应的解决方案、教程以及开源代码示例;ArkTS 还处于成长阶段,相关的泛型应用案例、配套库资源相对有限,开发者可能需要花费更多精力探索如何在鸿蒙特色业务场景里用好泛型。
    • 语法糖与简化倾向:ArkTS 为了降低鸿蒙应用的开发门槛,让更多开发者能快速上手,在部分泛型语法上可能会比 TS 更倾向于简化和直观表达。尤其是涉及鸿蒙系统独有的一些数据结构、组件交互场景,ArkTS 的泛型语法会尽量避免过于晦涩的类型表述,以便开发者聚焦于业务功能开发。举例来说,在处理与鸿蒙原生 UI 组件的数据绑定场景时,ArkTS 的泛型使用起来会更 “亲民”。

 

TS工具函数 和 ArkTS工具函数?

  • TypeScript 工具函数
    • 类型判断类
      • typeof:这是 JavaScript 原生的操作符,在 TypeScript 里也常用,用于判断一个变量的基本类型,比如typeof 5会返回"number"typeof "hello"返回"string" ,能帮助快速做基础的类型分支处理。
      • instanceof:判断一个对象是否是某个类的实例,例如let arr = []; console.log(arr instanceof Array)会输出true,常用于面向对象编程场景下,确定对象的具体来源类。
      • isArray 来自 Array.isArray:精准判断一个值是否为数组类型,解决typeof在判断数组时只能得到"object"的局限性,例如Array.isArray([1,2])返回true
    • 通用逻辑类
      • 数组操作mapfilterreduce虽然是 JavaScript 原生数组方法,但在 TypeScript 里用得极为频繁。map用于对数组每个元素做变换生成新数组,filter筛选符合条件的元素,reduce则将数组聚合成单个值。例如:
let nums = [1, 2, 3];
let doubled = nums.map((num) => num * 2); 
let evens = nums.filter((num) => num % 2 === 0);
let sum = nums.reduce((acc, num) => acc + num, 0);
  • 对象操作Object.keysObject.valuesObject.entries用来获取对象的键、值以及键值对数组,方便遍历和处理对象数据,如:
let obj = {a: 1, b: 2};
let keys = Object.keys(obj);
let values = Object.values(obj);
let entries = Object.entries(obj);
  • 第三方库提供的工具函数:如 Lodash,它提供海量工具函数,_.cloneDeep可以做对象或数组的深度克隆,避免浅拷贝带来的引用共享问题;_.debounce_.throttle用于函数防抖和节流,优化函数频繁调用场景,像防抖常用于搜索框输入搜索,节流用于滚动事件监听。
  • ArkTS 工具函数
    • 鸿蒙系统交互类
      • UI 组件交互:用于创建和操作鸿蒙原生 UI 组件的函数。例如,和Text组件相关的函数,可能涉及设置文本样式、动态更新文本内容,让开发者无需深入底层就能灵活改变组件外观与属性。
      • 硬件调用辅助:辅助调用鸿蒙系统硬件资源的函数,像获取传感器数据时,有函数负责初始化传感器、设置采样频率,把复杂的底层硬件交互流程封装,让应用层开发者简单调用即可拿到如光照强度、加速度等传感器数据。
    • 跨设备通信类:在鸿蒙的多设备协同场景下,存在工具函数用于发现附近设备、建立连接通道、传输数据。它们简化了不同设备间蓝牙、Wi-Fi 直连等通信方式的代码实现,确保设备之间能稳定、高效地共享信息。
    • 基础数据处理类:类似 TypeScript,ArkTS 也有基本的数组、对象处理函数,不过更侧重于适配鸿蒙应用开发场景。比如在处理与鸿蒙系统配置文件相关的对象数据时,有特定函数去解析、更新配置属性,保障系统相关功能稳定运行。 目前 ArkTS 生态发展阶段,这类函数更多依赖鸿蒙官方文档逐步挖掘学习。

 

使用可选方法,使其中两个属性可选,其余的必选?

  • TypeScript
    • 在 TypeScript 里,可以结合PickPartial这两个工具类型来达成让指定的两个属性成为可选的效果。Pick用于从一个已有类型中挑选出特定属性,Partial能把属性变成可选的。
// 原始类型
type User = {
    id: number;
    name: string;
    age: number;
    email: string;
};

// 先挑出要设置为可选的属性,再用Partial处理
type OptionalUserProps = Pick<User, "age" | "email">;
type FinalUser = {
    id: number;
    name: string;
} & Partial<OptionalUserProps>;

let user1: FinalUser = {
    id: 1,
    name: "Alice"
};

let user2: FinalUser = {
    id: 2,
    name: "Bob",
    age: 30,
    email: "[email protected]"
};
  • 首先,Pick<User, "age" | "email">User类型里选中了ageemail这两个属性,生成一个新类型。接着,Partial把这个新类型里的属性都变成可选的,最后再和包含必选属性idname的对象类型做交叉,就实现了让ageemail可选,其余必选。

  • ArkTS

    • ArkTS 目前没有像 TypeScript 这么完备的标准工具类型生态。不过,我们可以模拟类似逻辑来实现目标。思路是先定义一个函数来提取指定属性,再手动处理让它们可选。
type User = {
    id: number;
    name: string;
    age: number;
    email: string;
};

function pickProps<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
    let result = {} as Pick<T, K>;
    keys.forEach((key) => {
        result[key] = obj[key];
    });
    return result;
}

type OptionalUserProps = pickProps<User, "age" | "email">;
type FinalUser = {
    id: number;
    name: string;
} & {
    [P in keyof OptionalUserProps]?: OptionalUserProps[P];
};

let user1: FinalUser = {
    id: 1,
    name: "Charlie"
};

let user2: FinalUser = {
    id: 2,
    name: "David",
    age: 25,
    email: "[email protected]"
};
  • 这里先自定义pickProps函数来模拟Pick工具类型的功能,提取出指定的ageemail属性。之后,通过映射类型手动将这两个属性变为可选,再与必选属性合并,实现需求。

 


鸿蒙开发怎么多设备适配?

在鸿蒙(HarmonyOS)开发中,实现多设备适配可以通过几种不同的方式,主要是通过判断设备类型和窗口大小来动态调整应用的布局和功能。以下是两种常用的适配方式:

  1. 基于设备类型和状态的适配
    这种方法通过检查设备的类型(如平板、手机)和状态(如折叠屏的展开状态)来决定应用是否支持自动旋转。例如,如果设备是平板或者折叠屏处于展开状态,应用则支持自动旋转;否则,应用保持竖屏显示。这种方式的代码示例如下:

    if (deviceInfo.deviceType === 'tablet' || display.getFoldStatus() === display.FoldStatus.FOLD_STATUS_EXPANDED) {
        windowObj.setPreferredOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED);
    } else {
        windowObj.setPreferredOrientation(window.Orientation.PORTRAIT);
    }
    
  2. 基于窗口宽高的适配 (一般是根据宽度)
    另一种方法是根据设备的屏幕宽度和高度来决定是否启用自动旋转。如果屏幕的宽度或高度大于一定值(例如840vp),则应用支持自动旋转。这种方法不直接依赖于设备类型,而是基于屏幕的实际大小。代码示例如下:

    let displayInfo: display.Display = display.getDefaultDisplaySync();
    let displayWidth: number = px2vp(displayInfo.width);
    let displayHeight: number = px2vp(displayInfo.height);
    if (displayWidth >= 840 || displayHeight >= 840) {
        windowObj.setPreferredOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED);
    } else {
        windowObj.setPreferredOrientation(window.Orientation.PORTRAIT);
    }
    

以上两种方法各有优缺点,选择哪种方法取决于应用的具体需求和目标设备的特性。例如,如果你的应用需要在多种类型的设备上提供一致的用户体验,可能需要考虑设备类型的适配方法。如果你的应用更关注设备的物理特性(如屏幕大小),则窗口大小的适配方法可能是更好的选择。

实现原理

横向断点使用窗口宽度计算,纵向断点使用窗口的宽高比计算,详细断点分类可以参考横纵向断点的设计原理。根据不同断点对应的UX设计,使用横向及纵向断点判断并展示不同的页面布局。

使用window (窗口)用on('windowSizeChange')接口获取窗口创建时的宽高计算横纵向断点。并使用on('windowSizeChange')接口监听窗口尺寸变化,窗口更新时获取变更后的窗口尺寸,更新横纵向断点。


普通手机不同尺寸怎么适配?

在鸿蒙系统中,适配不同尺寸的手机屏幕主要通过响应式布局来实现。这意味着您的应用需要能够根据设备的屏幕大小自动调整其布局和界面元素。以下是几个关键的适配策略:

  1. 使用百分比布局 :尽量使用百分比来进行布局设计,这样可以确保界面元素的比例在不同尺寸的屏幕上保持一致。

  2. 灵活的网格系统 :运用Flexbox或CSS Grid等现代布局技术,可以使界面元素根据屏幕大小动态排列,从而优化用户体验。

  3. 媒体查询 :使用媒体查询(Media Queries)来针对不同的屏幕尺寸应用不同的样式。这可以帮助你在小屏手机和平板电脑等不同设备上提供优化的视图。

  4. 图片和字体的适应性 :确保图片和字体同样适应不同的屏幕尺寸。使用可缩放的矢量图形(SVG)代替位图,并使用相对单位(如rem或em)设置字体大小。

  5. 测试和调整 :在多种不同尺寸的设备上测试你的应用,确保在各种情况下都能提供良好的用户体验。根据测试结果调整布局和样式。

通过上述方法,你可以有效地适配不同尺寸的手机屏幕,确保应用在所有设备上的可用性和美观性。


鸿蒙数据架构?三层架构?

鸿蒙操作系统(HarmonyOS)采用了一种称为“三层架构”的设计,这种架构主要是为了实现“一次开发,多端部署”的理念,使开发者能够更高效地开发和部署应用1。以下是鸿蒙操作系统三层架构的详细介绍:

  1. 产品定制层 :这一层专注于满足不同设备或使用场景的个性化需求,包括UI设计、资源和配置,以及针对特定场景的交互逻辑和功能特性1。产品定制层是用户直接互动的界面,可以根据特定产品需求进行灵活的调整和扩展1。

  2. 基础特性层 :位于公共能力层之上,这一层用于存放基础特性集合,如相对独立的功能UI和业务逻辑实现1。每个功能模块都具有高内聚、低耦合、可定制的特点,以支持产品的灵活部署。基础特性层为上层的产品定制层提供稳健且丰富的基础功能支持,包括UI组件、基础服务等。

  3. 公共能力层 :这一层用于存放公共基础能力,集中了公共UI组件、数据管理、外部交互以及工具库等共享功能1。公共能力层为上层的基础特性层和产品定制层提供稳定可靠的功能支持,确保整个应用的稳定性和可维护性1。

这种三层架构的设计,使得鸿蒙操作系统能够在一个统一的框架下,支持多种多样的设备和应用场景,同时也提高了开发的效率和应用的可维护性。

鸿蒙开发数据处理?(装饰器)

在鸿蒙(HarmonyOS)开发中,装饰器是用来实现特定功能的代码标记,它们可以帮助开发者管理和共享状态,减少代码耦合,提高应用性能。以下是几种常用的装饰器及其在数据处理中的应用:

  1. @State+@Prop、@State+@Link、@State+@Observed+@ObjectLink 1

    • 这三种组合允许状态在组件间逐级向下传递,适用于父子组件间的状态共享。@State修饰的状态与其所属组件共享生命周期,而@Prop、@Link和@ObjectLink允许状态沿着组件路径被共享1。
    • 使用场景:当母组件需要向多个子组件传递相同的状态时,可以使用@State在母组件中定义状态,然后通过@Prop、@Link或@ObjectLink将状态传递给子组件。
  2. @Provide+@Consume

    • @Provide装饰器可以在一个组件中提供一个状态,而@Consume允许后代组件消费(访问)这个状态。这使得状态可以在一个组件树中被广泛共享。
    • 使用场景:当需要在一个组件树中的多个组件之间共享状态时,可以使用@Provide在树的根部提供状态,然后各个子组件可以通过@Consume来使用这个状态。
  3. LocalStorage 和 AppStorage

    • LocalStorage主要用于在同一UIAbility内不同组件树间共享状态,其生命周期与应用程序相关1。
    • AppStorage用于应用全局状态的管理,与应用的进程绑定1。
    • 使用场景:当状态需要在应用的各个部分之间广泛共享时,可以使用AppStorage来管理这些全局状态;而对于需要在较小范围内共享的状态,可以使用LocalStorage。

通过合理使用这些装饰器,可以有效地管理和共享数据,提高应用的性能和响应速度。


 

组件通信、页面通信?

HarmonyOs DevEco Studio小技巧34--传值通信的方式_arkts link传值-CSDN博客
东西有点多,移步看这个


view 的Pinia ?

Pinia 是 Vue 的状态管理库,用于替代 Vuex,有诸多优势与特性:

  • 简洁的 API:相较于 Vuex ,Pinia 的 API 更加简洁直观。它没有复杂的 mutations 概念,只有 state、getters 和 actions。例如创建一个简单的 store:
import { defineStore } from 'pinia';

// 这里的 'user' 是 store 的唯一标识
const userStore = defineStore('user', {
    state: () => ({
        name: 'John',
        age: 30
    }),
    getters: {
        userInfo(state) {
            return `${state.name}, ${state.age}`;
        }
    },
    actions: {
        updateName(newName) {
            this.name = newName;
        }
    }
});
  • 更好的 TypeScript 支持:在使用 TypeScript 开发 Vue 项目时,Pinia 能无缝配合。其类型推导能力出色,让开发者在编写代码时可以轻松利用类型系统,减少类型相关错误,提升代码健壮性。
  • 插件系统与模块化:Pinia 自带插件系统,方便开发者拓展功能。并且,各个 store 天然具备模块化特性,不同业务模块的状态管理可以分别放在不同的 store 定义中,便于大型项目的代码组织与维护,例如一个电商项目,可以分别有 productStorecartStoreuserStore 等。
  • 响应式系统:它利用 Vue3 的响应式系统,当 store 中的 state 发生变化时,与之绑定的组件会立刻更新,确保 UI 和数据状态始终同步,优化用户体验。

使用时,先安装 pinia 包:

npm install pinia

然后在 Vue 项目的入口文件(通常是 main.js)中引入并挂载:

import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from 'pinia';

const pinia = createPinia();
const app = createApp(App);
app.use(pinia);
app.mount('#app');

后续就可以在各个组件里使用 userStore 这类定义好的 store 实例,进行数据获取、更新操作了。


 

什么模式?观察者还是发布订阅

  • 观察者模式特性
    • 响应式原理:Pinia 利用 Vue3 的响应式系统,而响应式系统底层基于观察者模式。在 Pinia 的 state 部分,当数据发生变化时,依赖这些数据的组件(即观察者)会被自动通知,从而更新 UI 画面。例如,一个组件获取了 userStore 中的 name 状态,只要 name 发生更改,该组件就像一个观察者一样,察觉到变化并重新渲染。
    • 自动追踪依赖:就如同典型的观察者模式,Pinia 无需开发者手动去注册谁依赖了哪些数据,Vue 的响应式机制会自动追踪依赖关系,组件访问 state 某个属性时,就自动建立起观察联系,后续属性变更,对应组件自动响应。
  • 发布 - 订阅模式特性
    • Actions 作为发布者:Pinia 的 actions 可以看作是发布者。当一个 action 被触发执行,比如在上面例子中触发 updateName 这个 action,它可以向整个应用 “发布” 一个事件 —— 数据发生了变更。这个变更不一定只针对单一组件,可能关联多个不同地方的组件,它们订阅了相关状态的变化,会随之更新。
    • 跨组件通信:不同组件之间不需要直接互相知晓对方,通过 Pinia 的 state 和 actions 构建起一种发布 - 订阅关系。组件 A 触发 action 修改 state,组件 B、C 只要依赖了相关的 state ,就像订阅了对应频道一样,收到变化通知,完成跨组件的间接通信,这契合发布 - 订阅模式中解耦发布者与订阅者的理念。

总体而言,Pinia 巧妙借助 Vue3 的响应式机制,融合两种经典设计模式,让状态管理更加灵活高效,既保障了数据与 UI 之间紧密的响应关系,又能处理复杂的跨组件数据交互场景。


鸿蒙原生的网络请求?RCP 

RCPP (Remote Communication Protocol for HarmonyOS) 是鸿蒙操作系统中用于处理网络请求的一个重要模块。它允许开发者以程序方式发送各种HTTP请求,如GET、POST、HEAD、PUT、DELETE、PATCH、OPTIONS等,从而与网络服务进行交互。

主要功能

  • 发送HTTP请求 :RCPP支持发送多种类型的HTTP请求,并可以通过配置请求头、请求体等参数来满足不同的需求。
  • 异步处理 :使用Promise进行异步处理,这意味着可以在请求完成时通过.then().catch()方法来处理响应或错误。
  • 权限管理 :进行网络请求需要ohos.permission.INTERNET权限,如果使用的是'cellular'模式,还需要ohos.permission.GET_NETWORK_INFO权限。

示例代码 以下是一个使用RCPP发送HTTP POST请求的示例:

import { BusinessError } from '@kit.BasicServicesKit';
const session = rcp.createSession();
let req = new rcp.Request("http://example.com/fetch", "POST");
session.fetch(req).then((response) => {
    console.info(`Succeeded in getting the response $${response}`);
}).catch((err: BusinessError) => {
    console.error(`err code is $${err.code}, err message is $${JSON.stringify(err)}`);
});

在这个示例中,首先创建了一个RCPP会话,然后构造了一个HTTP POST请求,并通过session.fetch()方法发送请求。最终,根据响应或错误来进行日志输出。

通过这种方式,开发者可以灵活地在鸿蒙应用中实现网络请求功能,与服务器进行高效的数据交换。

RCP支持流式上传

可以使用Session.uploadFromStream()方法来实现这一功能。这种方法允许您将数据流直接上传到服务器,而无需首先将文件保存到本地。这样可以有效地减少存储空间的使用,并提高上传效率。

在使用Session.uploadFromStream()方法时,rcp会自动设置HTTP请求头和请求体格式,具体如下:

  • 请求头Content-Type会被设置为application/octet-stream1。
  • 请求体将使用binary二进制格式发送。

这种方式特别适用于处理大型文件或在网络条件不佳的情况下进行文件上传,因为它可以避免因文件大小而导致的内存不足问题。


axios和feach的区别?

axios 和 fetch 都是用于在前端发起网络请求的工具,不过它们之间存在一些显著区别:

  • 语法简洁性与使用体验
    • axios:语法简洁直观,基于 Promise 封装,对初学者很友好。发起一个简单的 GET 请求示例如下:
import axios from 'axios';

axios.get('https://example.com/api/data')
   .then(response => {
        console.log(response.data);
    })
   .catch(error => {
        console.error('请求出错:', error);
    });
  • fetch:语法基于原生 JavaScript 的 fetch API,更加贴近底层,使用起来稍显复杂。同样发起 GET 请求:
fetch('https://example.com/api/data')
   .then(response => response.json())
   .then(data => {
        console.log(data);
    })
   .catch(error => {
        console.error('请求出错:', error);
    });
  • 可以看到,axios 直接返回处理好的数据,而 fetch 第一步只是拿到响应对象,还需额外调用 json() 等方法来解析数据,上手难度更高一点。
  • 请求与响应拦截
    • axios:内置强大的拦截器功能,无论是请求发出前对请求参数、headers 进行修改,还是在响应返回后统一处理错误、数据格式化,都极为方便。例如:
// 请求拦截器
axios.interceptors.request.use(config => {
    config.headers['Authorization'] = 'token123';
    return config;
}, error => {
    return Promise.reject(error);
});

// 响应拦截器
axios.interceptors.response.use(response => {
    return response.data;
}, error => {
    console.error('响应出错:', error);
    return Promise.reject(error);
});
  • fetch:原生的 fetch API 并没有提供拦截器机制,若要实现类似功能,开发者需自行编写函数,包裹所有的 fetch 请求,额外增加开发成本与代码复杂度。
  • 跨域处理与兼容性
    • axios:对跨域处理支持较好,搭配后端设置的 CORS 规则,能顺畅发起跨域请求;同时,它对老版本浏览器兼容性也不错,经过打包工具处理后,能在一些不支持原生 fetch API 的浏览器上使用。
    • fetch:在跨域场景下,完全依赖后端的 CORS 配置,自身没有额外的辅助手段;并且,它是较新的 API,一些老旧浏览器(如 IE)不支持,需要引入额外的 polyfill 才能兼容。
  • 错误处理方式
    • axios:当网络请求出现错误,如 404500 等状态码,会直接进入 catch 块,便于集中处理各类错误,错误信息也较为详细。
    • fetchfetch 把 404500 这类状态码当作正常响应,只有当网络故障、无法连接服务器时才会进入 catch 块,所以针对 HTTP 状态码的错误处理,往往需要开发者在 then 块里手动判断,操作更繁琐。

 

流式传输?

  • 概念
    • 流式传输指数据以连续流的形式在网络中传输,而不是等待整个文件下载完成后才开始处理。接收端不必等全部数据到达,就可以边接收边解析、展示,这大大减少了用户等待时间,提升了响应速度与交互体验。以在线视频播放为例,观众无需等待整部影片下载完,点开就能即时观看,得益于视频数据的流式传输。
  • 应用场景
    • 视频与音频播放:像常见的在线视频平台(如爱奇艺、腾讯视频)、音乐类 APP(如网易云音乐),运用流式传输持续推送音视频片段给用户设备,让播放流畅不间断。高清视频每秒传输的数据量巨大,流式传输按帧、按音频片段输送,保障了播放的实时性。
    • 实时通信:视频通话、语音通话场景里,数据要实时交互,双方声音与画面即时传输。例如微信视频通话,借助流式传输,把摄像头采集的画面、麦克风录入的声音实时变成数据流,穿梭于网络,让通话双方无缝沟通。
    • 大数据传输与处理:当处理海量数据时,比如从云端数据库持续拉取日志文件用于分析,或是接收气象卫星不间断传回的气象数据,采用流式传输,能让后端一边接收、一边开启初步运算,无需漫长等待数据完整接收,提升处理效率。
  • 实现技术
    • HTTP 流式传输:HTTP/1.1 开启了长连接模式,支持持续推送数据;HTTP/2 更是强化了该特性,实现多路复用,允许同时推送多个数据流,优化资源利用效率,减少延迟。一些基于 HTTP 的直播协议,如 HLS(HTTP Live Streaming),把视频切分成多个小的 TS 片段,按顺序依次通过 HTTP 传输,客户端持续拼接播放。
    • RTMP:实时消息传输协议(Real-Time Messaging Protocol),专为流媒体设计,广泛用于直播行业早期,能快速建立客户端与服务器间的双向连接,保障音频、视频数据快速、稳定传输,不过它逐渐受到新兴 HTTP 类协议冲击。
    • WebRTC:是一套开源项目,能让浏览器与浏览器之间实现实时通信,无需插件。它集成了音频、视频采集、编解码、网络传输等一系列功能,支持端到端的流式传输,是当下视频通话类应用热门的底层技术。 它通过 ICE(Interactive Connectivity Establishment)来建立连接,在不同网络环境下寻找最优传输路径。

 

webview 混合开发? 通信的API 方法?

在HarmonyOS中,webview混合开发主要涉及到应用侧与前端页面的通信。这可以通过多种API方法实现,具体选择哪种方法取决于您的应用架构和性能需求。

1. JSBridge通信 JSBridge是一种允许网页前端和应用后端进行双向通信的机制。在HarmonyOS中,您可以使用ArkWeb组件来实现这一功能。以下是实现步骤:

  • 绑定ArkWeb组件 :首先,您需要在ArkTS侧声明一个ArkWeb组件,并通过Node-API将一个唯一的webTag传递到应用的Native侧。这个webTag将用于标识组件。
  • 实现通信 :使用ArkWeb_Native接口,您可以在这个Native环境中直接与前端页面进行通信,无需经过ArkTS环境,从而提高执行效率并避免UI阻塞。

2. PostWebMessage通信 这种方法允许应用侧向前端页面发送消息。同样,这不需要ArkTS环境的介入,可以直接在C++环境中实现。以下是关键步骤:

  • 绑定ArkWeb组件 :与JSBridge相同,您需要先在ArkTS侧设置好ArkWeb组件,并通过Node-API传递webTag到Native侧。
  • 发送消息 :使用ArkWeb_WebMessageAPI,您可以发送string或buffer类型的数据到前端页面。这种方式支持的消息类型较少,但足以满足简单的数据传输需求。

3. JavaScriptProxy代理机制 这是一种更高级的通信方式,允许前端页面直接调用应用侧的方法。通过在Webview中注入一个代理对象,前端可以像调用本地JavaScript函数一样调用应用侧的功能。

  • 设置代理 :在您的Webview组件中,您可以使用.javaScriptProxy()方法注入一个代理对象,该对象指向应用侧的某个实际对象。
  • 调用方法 :前端页面可以通过这个代理对象直接调用应用侧的方法,实现复杂的业务逻辑交互。

每种方法都有其适用场景和优缺点,选择合适的通信方式可以显著提升应用的性能和用户体验。


 

即时通讯功能 ,webScoket的及时通信,封装,性能优化?

在即时通讯功能开发中,WebSocket确实是一种常用的实时通讯技术,它提供了双向通信通道,允许服务器和客户端实时交换数据。对于使用ArkWeb(方舟Web)平台的开发者来说,优化WebSocket的性能和封装变得尤为重要。以下是一些关键的优化和封装建议:

  1. 性能优化 :

    • 减少消息大小 :尽量减少通过WebSocket传输的数据量,这可以显著减少带宽使用和传输时间。
    • 使用压缩 :启用WebSocket的消息压缩,如使用DEFLATE算法,可以有效减少传输的数据量。
    • 优化数据处理 :在服务器和客户端优化数据处理逻辑,减少不必要的数据处理步骤,提高数据处理效率。
    • 心跳检测:超时自动断开连接
  2. 封装 :

    • 抽象接口 :设计一个抽象的WebSocket封装层,隐藏底层的实现细节,提供简洁的接口给上层应用。
    • 错误处理 :封装层应包含 robust 的错误处理机制,确保网络问题或其他异常情况不影响应用的稳定性。
    • 连接管理 :实现自动重连和连接状态管理,确保WebSocket连接的可用性和可靠性。
  3. 具体实现 :

    • 在ArkWeb平台上,你可以利用JavaScript或TypeScript来封装WebSocket,这些语言提供了丰富的库和API来处理网络通信。
    • 使用Promise或Async/Await来处理异步操作,使代码更易于理解和维护。

通过上述方法,你可以有效地优化和封装WebSocket在即时通讯功能中的使用,提高应用的性能和用户体验。


鸿蒙轻量级SQLite,和游览器持久化缓存是一样的?

鸿蒙操作系统中的轻量级SQLite存储和浏览器的持久化缓存虽然都是用于数据的持久化存储,但在使用场景和实现方式上可能有所不同。

  1. 轻量级SQLite存储 :

    • 在鸿蒙操作系统中,轻量级SQLite存储通常是用于应用内部的数据存储需求,它允许开发者在一个较轻量的数据库环境中存储和查询数据。这种存储方式支持多种数据类型,并可以通过SQL查询语言来进行复杂的数据操作。
  2. 浏览器持久化缓存 :

    • 浏览器的持久化缓存通常指的是如Cookies、Local Storage或IndexedDB等技术,它们主要用于在网络浏览过程中存储用户数据,以便在不同的浏览会话之间保持数据的一致性。这些技术通常提供了较简单的数据存储和检索功能,但在数据组织和查询能力上可能不如SQLite。

相似之处 :

  • 两者都涉及到数据的持久化存储,即数据在设备上存储一段时间,而不是临时存储在内存中。
  • 都可以用来改善用户体验,例如通过存储用户偏好或频繁访问的数据来提高应用或网站的响应速度。

不同之处 :

  • 轻量级SQLite存储提供了更强的数据管理和查询功能,适合于需要复杂数据操作的应用场景。
  • 浏览器的持久化缓存则更适用于需要简单数据存储和快速读取的网页应用。

 

用户首选项是啥?

用户首选项是鸿蒙操作系统(HarmonyOS)中提供的一种轻量级数据存储解决方案,主要用于应用内部的键值对形式的数据处理和持久化。它允许应用以键值对的形式存储和检索数据,其中键是字符串类型,值可以是数字、字符、布尔类型或这些类型的数组。

主要功能和特性:

  1. 数据处理能力 :用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。
  2. 数据存储形式 :数据以键值对形式存储,键的类型为字符串,值的存储数据类型包括数字型、字符型、布尔型以及这三种类型的数组类型。
  3. 轻量级数据存储 :由于数据存储在内存中,用户首选项能够快速从内存中获取数据,适用于存储应用的个性化设置,如字体大小、夜间模式开关等。
  4. 持久化能力 :可以通过调用flush接口将内存中的数据写入持久化文件中。

使用限制和注意事项:

  • 用户首选项无法保证进程并发安全,存在文件损坏和数据丢失的风险,因此不支持在多进程场景下使用。
  • 键(Key)的最大长度限制为1024个字节,值(Value)的最大长度限制为16兆字节。
  • 由于数据存储在内存中,建议存储的数据量不超过一万条,以避免内存开销过大。

通过使用用户首选项,开发者可以有效地管理和存储应用的配置信息和用户偏好设置,提高应用的响应速度和用户体验。


flutter通信是什么?

在 Flutter 中,通信涵盖多个层面,包括组件间通信、页面间通信以及与原生平台通信:

  • 组件间通信
    • 父子组件通信
      • 父传子:在 Flutter 里,父组件可以很方便地将数据传递给子组件。例如,父组件有一个变量 message,要传给子组件 ChildWidget,只需在构建子组件时当作参数传入:
class ParentWidget extends StatelessWidget {
  final String message = "Hello from parent";
  @override
  Widget build(BuildContext context) {
    return ChildWidget(message: message);
  }
}

class ChildWidget extends StatelessWidget {
  final String message;
  const ChildWidget({Key? key, required this.message}) : super(key: key);
  @Override
  Widget build(BuildContext context) {
    return Text(message);
  }
}
  • 子传父:当子组件需要向父组件反馈信息时,常借助回调函数。父组件定义一个回调方法,传递给子组件,子组件触发该回调,把数据传回父组件,比如子组件中的按钮点击事件向父组件传值。
  • 兄弟组件通信:由于 Flutter 组件树的特性,兄弟组件不能直接通信。一般会借助共同的父组件作为 “中间人”,先把数据从一个兄弟组件传给父组件,再由父组件转发给另一个兄弟组件。
  • 页面间通信
    • 路由传值:Flutter 使用路由来管理页面跳转,在跳转时可以携带数据。例如,从页面 A 跳转到页面 B 时,页面 A 可以把数据放在路由参数里:
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => PageB(data: "Some data from A"),
  ),
);
  • 状态管理工具:对于复杂的多页面应用,像 Provider、MobX、GetX 等状态管理库能起到关键作用。它们可以跨页面共享状态,某个页面修改状态后,依赖该状态的其他页面能实时更新,实现间接的页面间通信。例如使用 Provider,定义一个全局的状态变量,不同页面只要监听这个变量,就能获取最新数据。
  • 与原生平台通信
    • Flutter 与 iOS 通信:在 iOS 平台,借助 MethodChannel 实现双向通信。Flutter 端创建 MethodChannel 实例,定义调用原生方法的通道名,iOS 端在 AppDelegate 或其他合适的类中注册对应的方法,接收 Flutter 传来的请求并响应;反过来,iOS 也能通过通道向 Flutter 发送事件通知。
    • Flutter 与 Android 通信:和 iOS 类似,使用 MethodChannel。在 Android 的 MainActivity 类里,注册与 Flutter 交互的方法,处理 Flutter 请求,也能从 Android 原生代码向 Flutter 推送如系统事件、硬件状态更新这类消息,让 Flutter 应用融入更多原生平台特性。

 

git版本控制 git pull 和 git rebase 的区别?

  • git pull
    • 执行逻辑:前面提到,git pull 是 git fetch 与 git merge 的组合操作。先从远程仓库抓取最新的改动,存到本地的远程分支副本,之后,将这些新获取的、与当前分支对应的改动,通过git merge合并进本地分支。合并时,如果有文件改动冲突,会生成一个合并提交记录,让分支历史呈现出合并的状态 ,分支图可能会有分叉再合并的复杂线条。
    • 使用场景:日常协作场景下,快速同步远程仓库最新代码时极为常用。比如每天上班开工前,或者完成一个小功能点后,想快速知晓并融入队友的新工作,就执行git pull
  • git rebase
    • 执行逻辑git rebase 同样也是用于整合远程仓库与本地仓库的代码,但做法不一样。它先把本地分支的提交 “挪动” 到基于远程最新提交之后,也就是提取本地分支尚未推送的提交,然后把它们逐个应用到远程分支最新提交的末端,重新线性排列本地分支的提交顺序,整个过程不产生合并提交,使得提交历史更加整洁、线性。例如,本地有 3 个提交,执行git rebase 时,这 3 个提交会被依次重新 “贴” 到远程分支更新后的末尾。
    • 使用场景:当你希望本地分支的提交历史保持简洁、呈线性,不想出现合并分支那种分叉的复杂情况时,就会选择git rebase。尤其在向开源项目贡献代码时,很多开源项目维护者更偏好接收经过git rebase整理、有着干净提交历史的代码。另外,在需要频繁更新本地分支,持续紧跟远程仓库进度时,git rebase也能让分支演进看起来条理清晰。

总结来说,git pull更注重快速同步代码,不怕出现合并分支的复杂情况;而git rebase侧重于整理提交历史,打造简洁线性的分支轨迹。


Pinia的实现原理?

  • 响应式系统
    • 依赖 Vue3 响应式:Pinia 构建在 Vue3 的响应式系统之上。Vue3 使用 Proxy 来实现响应式,当创建一个 Pinia 的 state 时,实际上是利用了这种底层的响应式能力。例如,在定义一个简单的 userStore 时:
import { defineStore } from 'pinia';

const userStore = defineStore('user', {
    state: () => ({
        name: 'John',
        age: 30
    }),

这里的 state 数据会被 Vue3 的 Proxy 包裹,一旦 name 或者 age 这类属性值发生改变,依赖它们的组件能自动感知到变化,实现数据驱动 UI 更新。

  • Store 结构
    • 单一职责:每个 store 都有清晰的结构,围绕 stategetters 和 actions 展开。state 存放数据,getters 用于从 state 派生新的数据,类似 Vue 中的计算属性,计算过程会缓存结果,提升性能。例如:
    getters: {
        userInfo(state) {
            return `${state.name}, ${state.age}`;
        }
    },

actions 则用来承载异步操作或者修改 state 的业务逻辑,像是发起网络请求获取用户最新信息,再更新到 state 里:

    actions: {
        async updateUserInfo() {
            const response = await fetch('https://example.com/api/user');
            const data = await response.json();
            this.name = data.name;
            this.age = data.age;
        }
    }
  • 模块化与插件系统
    • 模块化:不同业务模块的状态管理可以独立封装成不同的 store,彼此互不干扰。比如电商项目里,有专门管理商品列表的 productStore、处理购物车的 cartStore 等,便于大型项目代码组织与维护。
    • 插件系统:Pinia 自带插件系统,开发者可以编写插件来扩展功能。插件能够在 store 创建、状态更新等各个环节介入,比如添加全局的日志记录插件,在每次 state 修改时打印日志,监控数据流动:
import { createPinia } from 'pinia';

const myPlugin = {
    beforeCreate(store) {
        console.log('Store is about to be created:', store.$id);
    },
    afterCreate(store) {
        console.log('Store has been created:', store.$id);
    },
    beforeStateChange(store, newState) {
        console.log('State is about to change:', newState);
    },
    afterStateChange(store, newState) {
        console.log('State has changed:', newState);
    }
};

const pinia = createPinia();
pinia.use(myPlugin);
  • 订阅发布机制:在 state 变更传播上,Pinia 融合了观察者和发布 - 订阅模式。当 state 发生改变,依赖它的组件(观察者)立即更新,同时 actions 作为发布者,触发的修改动作可以通知到订阅相关数据变化的各个组件,跨组件传递数据更新信号,维持整个应用的数据一致性。 这一机制保障了数据更新能及时反馈到 UI ,提升用户体验。

信息仅供参考,如有更优方案,欢迎评论,谢谢 

;