鸿蒙OS中的JS开发框架是什么?
那鸿蒙的JavaScript框架是怎么样的??
我相信我和其他开发者最感兴趣的是鸿蒙的JS框架ace_lite_jsfwk。乍一看,这是一个非常轻量级的框架。当我阅读它的源代码后,我发现它确实很轻。其核心代码只有5个js文件,大概300-400行代码。 (没有单元测试)
运行时核心\src\core\index.jsruntime-core\src\observer\observer.jsruntime-core\src\observer\subject.jsruntime-core\src\observer\utils.jsruntime-core\src\profiler\index。 js代码实现了观察模式。也就是说,它实现了一种非常轻量级的MVVM模式,利用类似于vue2的属性劫持技术实现了响应式系统。这应该是当前培训课程中的“三大自我实现”之一。 (自己实现Promise,自己实现vue,自己实现react)
utils中定义了一个观察者栈,用于存储观察者。主体定义了观察者。那么当我们观察一个对象的时候,也就是劫持该对象的属性的操作还包括一些数组函数,比如push、pop等。这个文件的代码应该是最多的,大概160行左右。观察者代码就更简单了,大概五十、六十行左右。
我们开发时,可以使用Toolkit将开发者编写的HML、CSS、JS文件编译打包成JS Bundle。然后将JS Bundle 解析并运行为C++ 原生UI View 组件进行渲染。
在src\core\base\framework_min_js.h文件中,将这个编译好的js编译到运行时。编译出来的js文件不到3K,确实非常轻量。
js运行时不使用V8,jscore也不选择JerryScript。 JerryScript 是一个用于物联网的超轻量级JavaScript 引擎。能够在内存小于64KB 的设备上执行ECMAScript 5.1 源代码。这就解释了为什么文档中鸿蒙JS框架支持ECMAScript 5.1。
总体而言,js 框架使用了大约96% 的C/C++ 代码。用html 文件编写的组件将被编译为原生组件。不过app_style_manager.cpp以及同级的七八个文件是用来解析css并最终生成原生布局的。总的来说,我认为比我预期的要好。
聊完鸿蒙的JavaScript框架,现在就逐行分析一下鸿蒙系统的JavaScript框架。为此,我没有踏入陷阱,向鸿蒙贡献了多个PR(所有代码均基于鸿蒙最新版本)。
鸿蒙JS框架零依赖,在开发过程中只使用了一些npm包。打包后的代码不依赖任何npm包。我先给大家展示一下鸿蒙JS框架写的JS代码是什么样的?
导出默认值{ data() { return { count: 1 }; }, 增加() { ++this.count; }, 减少() { –this.count; },} 让我问你一个问题,如果我不告诉你这是鸿蒙。你以为是Vue还是小程序?其实单独使用JS时(没有鸿蒙系统),代码是这样的:
const vm=new ViewModel({ data() { return { count: 1 }; }, 增加() { ++this.count; }, 减少() { –this.count; },});console.log( vm.count); //1vm.increase();console.log(vm.count); //2vm.decrease();console.log(vm.count); //1仓库石象路中的所有JS代码都指向一个响应式系统,充当MVVM中的ViewModel。
接下来我们来逐行分析下。
scr目录中有4个目录,8个文件受到攻击。其中一个是单元测试,另一个是性能测试。除去两个index.js文件,总共有四个有用的文件。这也是我们分析的重点。
src __test__ index.test.js核心 index.jsindex.js观察者index.js观察者.js subject.js utils.js profiler index.js 首先是入口文件src/index.js的分析,只有2行代码:
从’./core’导入{ViewModel};导出默认ViewModel;实际上是再出口。
另一个类似的文件是src/observer/index.js,它也有两行代码:
从’./observer’ 导出{ Observer };从’./subject’导出{Subject};观察者和主题实现观察者模式。主体是主体和被观察者。观察者就是观察者。当主体有任何变化时,需要主动通知观察者,这是被动的。
可以看到两个文件中都使用了src/observer/utils.js,所以我们先分析utils文件。分为3部分。
第一部分
导出const ObserverStack={ stack: [], Push(observer) { this.stack.push(observer); }, pop() { 返回this.stack.pop(); }, top() { return this.stack[this.stack.length – 1]; }};首先,它定义了一个用于存储观察者的堆栈。它遵循后进先出原则,内部使用栈数组进行存储。
堆栈的压入操作与数组压入功能相同。观察者被放置在堆栈的顶部。 pop操作和数组pop函数一样,删除栈顶的观察并返回删除的观察者。栈顶元素与出栈操作不同。 top 取出栈顶元素,但不删除它。第二部分
导出常量SYMBOL_OBSERVABLE=’__ob__’;导出const canObserve=target=typeof target===’object’;本节定义了一个字符串常量SYMBOL_OBSERVABLE。目的是为了以后使用方便。
定义一个函数canObserve来判断目标是否可以被观察。只能观察对象,所以可以使用typeoftrue。如果null 不可观察,那么这是一个错误。 (因为我已经提交了一份PR,询问这种行为是否是预期的。)
第三部分
导出const DefineProp=(目标, 键, 值)={ Object.defineProperty(目标, 键, { enumerable: false, 值});};这是因为Object.defineProperty代码太长。定义一个函数以避免代码重复。
接下来我们来分析观察者src/observer/observer.js,它可以分为四个部分。
第一部分:构造函数。接受4 个参数。
导出函数Observer(上下文,getter,回调,元){ this._ctx=context; this._getter=getter; this._fn=回调; this._meta=元; this._lastValue=this._get();}contextcurrent 观察者所在的上下文,类型为ViewModel。当调用第三个参数回调时,函数的this就是this上下文。
getter 类型是用于获取属性值的函数。
回调类型是当某个值发生变化时执行的函数。
元元数据。观察者不关注元数据。
在构造函数的最后一行,this._lastValue=this._get()。我们来分析一下_get函数。
第2部分:ObserverStack是用于存储前面分析的所有观察者的堆栈。将当前观察者压入堆栈,并通过_getter获取当前值。结合第一部分中的构造函数,该值存储在_lastValue 属性中。
Observer.prototype._get=function() { 尝试{ ObserverStack.push(this);返回this._getter.call(this._ctx); } 最后{ ObserverStack.pop(); }};执行完之后,这个观察者初始化已经完成了。
第三部分:该部分实现数据更新时的脏检查机制。
Observer.prototype.update=function() { const lastValue=this._lastValue; const nextValue=this._get(); const 上下文=this._ctx; const 元=this._meta; if (nextValue !==lastValue || canObserve (nextValue)) { this._fn.call(context, nextValue, lastValue, meta); this._lastValue=nextValue; }};将更新后的值与当前值进行比较。如果两者不同,则执行回调函数。如果这个回调函数是渲染UI的话,就可以实现按需渲染。如果两个值不同,则检查是否可以观察到新的值集合,然后决定是否执行回调函数。
第4 部分:订阅和取消订阅。
Observer.prototype.subscribe=function(subject, key) { const detach=subject.attach(key, this); } if (typeof detach !==’函数’) { return; } if (!this._detaches) { this. _分离=[]; } this._detaches.push(detach);};Observer.prototype.unsubscribe=function() { const detaches=this._detaches;如果(!分离){ 返回; } while (detaches.length) { detaches.pop()(); }};上面我们经常提到观察者和被观察的对象。我们还有另一种说法来表示观察者模式,即订阅/发布模式。这部分代码实现了主题的订阅。
首先调用主题的attach方法进行订阅。如果订阅成功,subject.attach方法会返回一个函数,当调用这个函数时,订阅就会被取消。为了将来能够取消订阅,必须保存此返回值。
相信大家都能猜到subject的实现。观察者订阅了主体,那么主体要做的就是当数据发生变化时及时通知观察者。主体如何知道数据已经改变?其机制与vue2相同,使用Object.defineProperty进行属性劫持。
接下来我们来分析观察者src/observer/subject.js,它分为7个部分。
第一部分:构造函数,
导出函数主题(目标){ const subject=this; subject._hijacking=true; DefineProp(目标,SYMBOL_OBSERVABLE,主题); if (Array.isArray(target)) { hijackArray(target); } } Object.keys(目标)。 forEach(key=hijack(target, key, target[key]));} 基本上没有什么难度。将_hijacking 属性设置为true 表示该对象已被劫持。遍历Object.keys来劫持各个属性。如果它是一个数组,则调用hijackArray。
第2 部分:两个静态方法。
subject.of=function(target) { if (!target || !canObserve(target)) { return target; } } } if (目标[SYMBOL_OBSERVABLE]) { 返回目标[SYMBOL_OBSERVABLE]; } 返回新主题(目标);}; subject.is=function(target) { return target target._hijacking;};Subject的构造函数不是直接外部调用的,而是封装在Subject.of静态方法中。
如果无法观察到目标,则直接返回目标。
否则,调用构造函数来初始化Subject。
subject.is用于判断目标是否被劫持。
第三部分:前面的Observer.prototype.subscribe中调用的。观察者使用它来订阅主题。而这个方法就是“如何订阅主题”。
subject.prototype.attach=function(key,observer) { if (typeof key===’undefined’ || !observer) { return; } } if (!this._obsMap) { this._obsMap={}; } if ( !this._obsMap[key]) { this._obsMap[key]=[]; } const 观察者=this._obsMap[key]; if (observers.indexOf(observer) 0) { Observers.push(observer); } return function () { Observers.splice(observers.indexOf(observer), 1); }; }};观察者维护该主题的哈希表_obsMap。哈希表的key就是需要订阅的key。例如,一个观察者订阅了name属性的变化,另一个观察者订阅了age属性的变化,而属性的变化可以同时被多个观察者订阅,所以哈希表的值是一个数组,数组的每个元素都是一个观察者。
第四部分:当某个属性发生变化时,通知订阅该属性的观察者,遍历每个观察者,调用观察者的update方法。正如我们上面提到的,脏检查是在这个方法内完成的。
subject.prototype.notify=function(key) { if ( typeof key===’undefined’ || !this._obsMap || !this._obsMap[key] ) { return; } } this._obsMap[key].forEach(observer=observer.update());};第五部分:用来处理属性嵌套的问题,比如这个对象{ user: { name: ‘JJC’ } }。
subject.prototype.setParent=function(parent, key) { this._parent=父级; this._key=key;};Subject.prototype.notifyParent=function() { this._parent this._parent.notify(this._key) ;};第6 部分:展示如何使用Object.defineProperty 进行属性劫持。设置属性时,调用set(value),设置新值,然后调用主题的notify方法。
函数劫持(目标,密钥,缓存){ const subject=target[SYMBOL_OBSERVABLE]; Object.defineProperty(target, key, { enumerable: true, get() { const Observer=ObserverStack.top); if (观察者) { 观察者.订阅(主题, 键); } const subSubject=subject.of(缓存); if (Subject.is(subSubject)) { subSubject.setParent(subject, key); }返回缓存; }, set(值) { 缓存=值; subject.notify(key); } });} 这里不做任何检查,只要设置属性就可以调用,即使属性的新值与旧值相同。通知将通知所有观察者。
第7 部分:劫持数组方法。
const ObservedMethods={ PUSH: ‘推’, POP: ‘弹出’, UNSHIFT: ‘取消移位’, SHIFT: ‘移位’, SPLICE: ‘拼接’, REVERSE: ‘反向’}; const OBSERVED_METHODS=Object.keys(ObservedMethods).map( key=ObservedMe thods [key]);ObservedMethods 定义了需要劫持的数组函数。前面的大写是作为密钥,后面的小写是需要劫持的方法。
函数hijackArray(target) { OBSERVED_METHODS.forEach(key={ const originalMethod=target[key]; DefineProp(target, key, function() { const args=Array.prototype.slice.call(arguments); originalMethod.apply(this , args); if (ObservedMethods.PUSH===key || ObservedMethods.UNSHIFT===key) { 插入=args; } else if (ObservedMethods.SPLICE) { 插入=args.slice(2); if (inserted insert.length) { insert.forEach(Subject.of); } const subject=target[SYMBOL_OBSERVABLE]; if (subject) { subject.notifyParent() } });} 数组劫持和对象同样,不能使用Object.defineProperty。
我们将劫持6 个数组方法。它们是同步添加、头部删除、尾部添加、尾部删除、某些项目的替换/删除以及数组反转。
通过重写数组方法来实现数组劫持。然而,有一点需要注意。数据的每个元素都被观察到了,但是当向数组添加新元素时,这些元素还没有被观察到,所以代码仍然需要判断当前的方法是否是push、unshift、splice。这需要在观察员团队中注入新的元素。
另外两个文件是单元测试和性能分析,这里就不分析了。
作为一名曾经的JavaScript 程序员,我当然支持鸿蒙使用JavaScript 作为开发语言。这也增强了所有熟悉JavaScript的前端开发者对前端道路、理论、文化和技术栈的信心。然而,如果你想做一些底层开发,你可能还需要使用其他编程语言。
用户评论
陌離
鸿蒙OS的JS开发框架听起来很有前景,不知道具体是哪个框架,期待能带来更好的开发体验。
有5位网友表示赞同!
恰十年
之前一直在用Android开发,听说鸿蒙OS的JS框架,想了解一下具体有哪些优势。
有17位网友表示赞同!
坠入深海i
JS开发框架?鸿蒙OS的这个新特性真的让我很兴奋,期待能有大佬分享一下具体用法。
有13位网友表示赞同!
汐颜兮梦ヘ
不知道鸿蒙OS的JS框架,但我对JS开发一直很感兴趣,希望这个框架能让我学有所用。
有8位网友表示赞同!
浅嫣婉语
听说鸿蒙OS的JS框架和Android的有所不同,不知道兼容性如何?
有16位网友表示赞同!
涐们的幸福像流星丶
作为前端开发,对鸿蒙OS的JS开发框架非常感兴趣,不知道能不能用React Native来开发?
有10位网友表示赞同!
几妆痕
鸿蒙OS的JS框架,听起来像是鸿蒙生态的一大步,期待它能够带来更好的用户体验。
有18位网友表示赞同!
志平
我之前用Vue.js开发过,不知道鸿蒙OS的JS框架会不会支持类似的框架,很期待呢。
有10位网友表示赞同!
一笑傾城゛
鸿蒙OS的JS框架,难道是华为自研的框架?期待能有详细的介绍和教程。
有17位网友表示赞同!
落花忆梦
JS开发框架在鸿蒙OS中的应用,感觉华为这次在操作系统上下了很大功夫。
有14位网友表示赞同!
强辩
作为一个开发者,对鸿蒙OS的JS框架很感兴趣,但是担心学习曲线会比较陡峭。
有20位网友表示赞同!
有恃无恐
鸿蒙OS的JS框架,不知道和WebAssembly有什么关系,有没有可能更高效?
有5位网友表示赞同!
念旧情i
华为这次在鸿蒙OS上投入了不少,JS开发框架的推出,感觉华为真的要在操作系统上有所作为。
有17位网友表示赞同!
陌然淺笑
JS框架是鸿蒙OS的一大亮点,希望华为能提供更多的技术支持,让开发者更容易上手。
有8位网友表示赞同!
栀蓝
鸿蒙OS的JS框架,如果真的和Android的框架有很大差异,那学习成本会不会太高?
有11位网友表示赞同!
良人凉人
期待鸿蒙OS的JS框架能带来更好的跨平台开发体验,这样我们开发者就能更专注于业务逻辑。
有17位网友表示赞同!
青衫负雪
听说鸿蒙OS的JS框架和Flutter有些相似之处,不知道具体有哪些区别。
有14位网友表示赞同!
北染陌人
鸿蒙OS的JS框架,感觉华为是要打造一个全新的生态系统,这对于开发者来说是个大机遇。
有7位网友表示赞同!