js后退页面不重新加载_快应用:支持加载单独JS文件的规范思考

当前快应用的项目中,支持加载其它JS文件(通过:require('./foo.js')),然后通过webpack工具处理依赖,最终完成页面JS的构建,其中页面JS包含了引入的所有JS内容;
本文讨论的主要是:webpack打包时,能够将依赖JS不合入到页面JS中,而是在rpk的ZIP压缩文件中单独存在,然后运行时,页面在需要的时候加载该JS文件;
  1. 背景描述

当前如果每个页面都引入一个共同的JS文件,如:foo.js;那么toolkit工具打包时,会将每个foo.js都打包到页面JS代码中;

这样带来以下几个问题,造成页面渲染变慢:

1) 每个页面JS都包含了foo.js,使得JS代码重复,导致rpk包体积增大,从而运行时下载rpk时间变长;

2) 每个页面JS都包含各自的foo.js,运行时JS引擎编译代码字符串为AST和JS对象,用时变长;

3) 由于JS代码存在多个地方,导致每个页面引入的foo都是独立的JS对象,而不是:同一个JS内存对象;

面对上面的问题,我们提供一种新的规范来解决:编译时支持JS单独构建,并在运行时调用加载

2. 规范的方案拆解

针对上面所述,规范实现的工作主要拆解到两大模块中:

1) 编译时:页面中用到的公共JS文件;在构建时,生成单独的JS文件;保持rpk中只有一份该JS文件的代码;

2) 运行时:提供动态加载JS文件的API:页面中首次加载时,则从内存/磁盘中同步读取并执行;第二次加载时,选择复用内存中之前的JS对象或者重新执行;

上面划分看上去实现简单,但围绕一些拆解出来的子任务,其实思路很多,需要权衡利弊:

1) 编译时的单独JS构建方案;

2) 当前编译时用的toolkit使用的是webpack4;如果使用其它工具(如:rollupjs),如何持续兼容;

3) 动态加载JS文件时,需要考虑:模块化,缓存,页面/APP上下文,时间成本,向后兼容,可扩展能力等的问题;

比如:模块化是否要放在运行时;当页面销毁后如果保证页面里的接口调用也跟着销毁;

接下来,围绕各项子任务分别进行考虑;

3. 任务1:编译时的单独JS构建方案

目前市面上主流的构建方案分为两类:语法型,配置型;其中webpack4中的splitChunkPlugin应用就属于配置型;

3.1 语法型

这种指的是,快应用平台运行时直接给开发者暴露一个 $app_evaluate$ 的函数,开发者在自己的ux文件或者js文件中,通过`const foo = $app_evaluate$('./foo.js')`方式引入JS依赖;

当工具toolkit编译时,检测到这样的函数API的JS,就不再打包到页面JS中;

优点:

1) 开发者可以灵活改写,让某个JS既可以打包到某个页面中,又可以独立存在;

2) toolkit编译时无需开发者配置,简单场景下方便易用;

缺点:

1) 这种方式对单个JS引用,很容易替换不完全,造成重复打包;

2) 编译工具需要自行构建依赖分析的能力,将相对路径的引入转换为绝对路径;不仅无法复用webpack本身的依赖分析,而且需要增加依赖分析的逻辑和管理;实现起来增加工作量和难度;

3) 对开发者来说,有认知成本,与现有的require, import语法相比,不够贴切自然;

3.2 配置型

这种指的是,开发者通过给某个JS文件增加标识,标识为独立打包;接着在ux与js中,如果通过require或者import引入的,那么toolkit在编译时,根据标识,就不再打包到页面JS的代码中,而是单独创建一个JS文件;

比如:page1,page2等都引用了 foo.js文件,foo.js中声明:`/** quickapp:standalone */` 标识为该文件不打包到页面JS中;

优点:

1) 开发者只需要在JS文件中声明一下即可,或者采用其它配置形式(如:在manifest.json或者quickapp.config.js的配置文件中声明);

缺点:

1) 如果JS文件很多,可能每个希望独立打包的JS都需要声明;

2) 编译时webpack分析出依赖关系后,需要替换代码中的`require`关键字为`$app_evaluate$`,避免被webpack引入;

当然,配置型的表现形式也有很多:

1) 与文件所在路径放在一起,同时更新:

在JS文件中,使用标记 `/** quickapp:standalone */` 类似语法,配置每个JS文件单独打包;

2) 在manifest.json中声明:

在manifest.json文件中,配置JS文件单独打包,通过resource属性去声明单独打包的JS文件的路径;

3) 在quickapp.config.js中声明:

`quickapp.config.js`是快应用项目下的一个配置文件,代表项目将如何构建;通过在配置文件中声明,以确定哪些文件单独创建;

由于JS单独打包的功能仅仅只是一个编译时的工作,不应该与manifest这种运行时检查的文件混合在一起,因此优于上一条;

以上三种类似Java中的Annotation与XML声明,各有优缺:

前者直接分散,和代码一起,不必担心JS文件路径变化时,引起后者配置路径无效的问题;

后者直观方便,统一性强,但是与JS文件代码分离较远,容易发生路径找不到;

3.3 总结

其实对开发者来说,他关注的是:如何以最小的认知成本,来达到rpk最小的体积与最快的页面加载;

所以,最佳的方式是:什么配置都不需要就能实现,对开发者无感知;

其次是简单的一条选择项或一条配置就能完成;

语法型有较大的缺陷、难度、认知,就不再考虑了;

上面说的语法型、配置型是站在开发者的角度上说的;如果站在内部实现的角度来说,可能部分属于语法型了,因为涉及到对`require('foo.js')`替换为`$app_evaluate$('foo.js')`的更改;

4. 任务2:各页面加载同一个JS文件时,其JS对象是否应该共用

下文的`evaluate`指的是:编译JS文件的字符串代码 转换为 JS对象;(通过:eval,new Function等形式)

共用指的是:各页面都依赖同一个JS文件时,那么这个JS文件在evaluate后对应的JS对象,是否应该被各页面共用?

如果共用,意味着:各页面之间访问的是:同一个JS对象;页面之间对该模块可以一起更新并取值;

如果不共用,意味着:各页面之间访问的是:不同的JS对象;页面之间的该模块操作互不干扰影响;

4.1 总结

经过讨论,我们认为:各页面之间不要共用,利大于弊;

因为快应用是一个APP的概念,页面与页面之间应该保持很强的独立性,页面之间的数据与状态更新应该通过专有的固定通道来通信;

否则的话,开发者容易滥用,带来页面之间公用模块的互相操作影响,造成:耦合性强,状态不稳定,通信机制泛滥,监控不到位;

与浏览器中运行的SPA模式、NodeJS中模块共用的方式相比,快应用选择了不一样的设计思路,最主要的目的就是:保证页面的强独立性;

5. 任务3:如果JS模块不共用,那么页面之间隔离的方式怎么实现?

5.1 方式1. JS文件只会evaluate一次;

虽然JS文件仅evaluate一次,但是利用编译时webpack的能力,它将每个JS文件封装为模块化代码(`function (module, exports, __webpack_require__) { ...code... }`);

当每个页面重新加载该JS时,其实是填充到了各自页面级别的module缓存`installedModules`中;

这样,当每次页面引入该JS时,得到的就是,该页面下的该模块的JS对象;

优点:JS的evaluate只会一次,但页面对应的该JS模块会重新填充一次;

缺点:JS中模块化代码之外的代码只会执行一次,但是模块内的代码会执行N次(N个页面加载);

5.2 方式2. 每次页面加载JS文件时,都重新evaluate对应的JS代码,得到不同的JS对象;

优点:做到了运行时的天然隔离每个JS模块,即使以后更换为没有模块化的打包工具时,仍然能够无缝隔离;

缺点:每次新页面加载,都重新evaluate,引起同样的JS代码执行多次;

5.3 总结

综上所述,我们认为:页面中的JS模块独立性是一项运行时的能力;

这种能力不应该依赖于编译时的保证,以后即使不使用webpack编译工具时,仍然能够保证隔离,而且还提高了页面的安全性;

所以,选择方式2,页面每次加载都重新evaluate对应的JS代码;

6. 任务4. 单独JS文件的模块化管理

既然要支持单独的JS文件加载,那么就需要对所有的单独JS文件做模块化管理;

需要考虑几个方面:实现位置,递归依赖,路径转换;

实现位置指的是:模块化的功能在运行时实现,还是编译时实现;

递归依赖的问题,可以参考NodeJS中的模块化实现,是在首次加载JS文件前,先定义模块为空对象,然后执行时包裹一段模块化代码:`function (exports, require, module, __filename, __dirname) { ...code... }`;当发生递归式依赖时,传递之前已经定义的模块对象;

由此来看,模块化无论发生在编译时还是运行时,都需要解决递归依赖;

6.1 实现位置:编译时

在webpack构建中,如果是仅生成一个JS文件,是通过`installedModules`内部缓存了各个模块;如果要生成多个JS文件时,是通过`webpackJsonp`完成的,因此只需要对这块针对快应用添加适配,提供同步读取rpk中JS文件的能力,即可完成编译时模块化的能力;

编译时同时也需要完成源代码中相对路径到绝对路径的转换,这块都可以使用webpack现有的实现方式;

6.2 实现位置:运行时

运行时模块化增加这块机制的好处在于:可以摆脱对webpack编译工具的模块管理依赖,以后如果有其它的编译工具,那么也能够无缝支持;

6.3 总结

考虑到规范发版,开发的时间成本,本次先利用成熟的webpack工具,即:编译时能力解决,待以后再增加对应的运行时实现;

关于模块化中的`动态加载`模块的能力(`const variable1 = 'test'; require(variable1);`),以及异步加载的能力,由于需求不太紧迫,因此本次规范暂不考虑;

7. 任务5:流式加载

当前快应用项目构建的RPK文件是一个ZIP文件,里面根据运行时加载JS文件的顺序,相应的安排了每个文件在ZIP索引中的顺序;比如根据顺序,先后主要为:`manifest.json`,`app.js`,每个`page.js`;

这项任务指的是:如何安排独立JS文件的位置(哪些在页面JS之前与之后),让页面首屏渲染需要的文件能够保证内存级别的同步读取耗时最少(避免磁盘读引起的时间损耗);

也就是说,这项任务的核心是:是否有办法确定哪些JS是属于页面首屏渲染必须的?

经过分析之后,目前没有直接的办法确定,通过让开发者加标记FLAG也并不是一件好办法;

但可以换个思路考虑问题:假设所有的独立JS放在页面JS之前,因为它并不会被立即执行为为JS对象,所以放在页面之前也仅仅只是增加了RPK下载与ZIP解压缩这些独立JS的时间;目前看,这些时间相对较小;

所以实现思路上,可以将:单独JS全部放在app.js与页面JS之前,因为app.js也可能会用到独立JS;

8. 任务6:分包支持

当前快应用支持分包能力,非独立包又分为:页面模块包与基础包;

为了管理上的方便,建议:将所有的独立JS放置在一个统一的固定目录下,如:(`%PROJECT%/build/chunks/foo.js`);

定义一个`chunks`的文件夹,将独立JS在里面,当然这个目录下可能还存在子目录的路径;

8.1 非独立包

如果是非独立包的话,可以将所有的独立JS也就是对应的目录,移动到:`基础包`中;

8.2 独立包

如果是独立包的话,则将自己用到的独立JS放在该模块下;

9. 文章总结

经过上面几个任务的分析,为了使得v1070版本提供的规范尽快发版,将使用编译时的模块化能力,运行时仅提供文件的同步读取API,以支持:加载单独JS文件的规范;

针对于运行时模块化(即:动态加载)、异步加载的其它需求,将在后面收到开发者需求后再制定快应用联盟规范。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/430147.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux Linux程序练习七

题目&#xff1a;实现两个程序mysignal、mycontrl&#xff0c;mycontrl给mysignal发送SIGINT信号&#xff0c;控制mysignal是否在屏幕打印“hello”字符串。 //捕捉信号#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h>…

python读取每一行文字二十四_python接口自动化(二十四)--unittest断言——中(详解)...

简介上一篇通过简单的案例给小伙伴们介绍了一下unittest断言&#xff0c;这篇我们将通过结合和围绕实际的工作来进行unittest的断言。这里以获取城市天气预报的接口为例&#xff0c;设计了 2 个用例&#xff0c;一个是查询北京的天气&#xff0c;一个是查询南京为例&#xff0c…

spring MVC配置form支持PUT和DELETE方法

REST的关键原则之一就是“使用标准接口”&#xff08;the use of the Uniform Interface&#xff09;&#xff0c;也就是提倡根据不同的语义使用GET, PUT, POST和DELETE方法&#xff0c;而html的form标签只支持两种提交方法&#xff1a;POST和GET&#xff0c;所以&#xff0c;为…

如何学习编译原理?

编译原理很难学&#xff0c;但如果自己动手编&#xff0c;会简单一点&#xff0c;而且不要想一口吃成胖子&#xff0c;从词法分析开始写&#xff0c;一步步来&#xff0c;下个龙书电子版看看。慢慢来&#xff0c;一点一点地去消化它&#xff0c;让它成为你的知识。 转载于:http…

Spring MVC访问不到静态资源

运行Spring MVC项目&#xff0c;发现.css&#xff0c;js等文件加载不了&#xff0c;一般是spring MVC的拦截匹配导致的。 例如我在web.xml里这样配置&#xff1a; <servlet><servlet-name>spring</servlet-name><servlet-class>org.springframework.w…

206. Reverse Linked List

Reverse a singly linked list. Solution 1: 思路&#xff1a;null的使用。用一个null node来承接&#xff0c;一个一个接上去即可。一刷的时候还觉得这node转化好麻烦好神奇&#xff0c;熟悉之后其实做起来很快。 /*** Definition for singly-linked list.* public class List…

labview项目实例_labview操作者框架

0.引言操作者框架适合于多并行任务的项目。在这样的项目中&#xff0c;多个并行任务之间往往需要相互通信&#xff0c;传统的解决办法是&#xff0c;每个任务一个队列&#xff0c;一个while循环&#xff0c;多任务项目需要在一个程序框图使用多个while&#xff0c;不好看。NI说…

eclipse发布web不带项目名的url

默认发布后访问地址是&#xff1a; http://localhost:8080/huanle/ 要达到的效果是&#xff1a; http://localhost:8080/ 不带项目名的url的好处是&#xff0c;不用再在每个地址前加<% path%>了&#xff1a; &#xff08;很烦有木有&#xff09; 操作方法&#x…

php 随机在文章中添加锚文本_页面SEO优化的锚文本优化概念

SEO优化分为结构优化和页面优化&#xff0c;页面的SEO优化中其中一个重点是锚文本的建设。锚文本的概念就是网站的内部链接&#xff0c;较为常见的就是将文章中的关键词作为锚文本。但在具体建设当中&#xff0c;很多网站对锚文本的处理出现了很多错误。这是因为对锚文本的优化…

求一个整数数组的最大元素,递归方法实现

Java版本&#xff1a; public class Main {public static void main(String[] args) {int[] array {1, 3, 5, 6, 3, 5, 3, 8};System.out.println(findMaxValue(array));}private static int findMaxValue(int[] array) {if(array.length < 0 || array null) {return -1;}…

eclipse发布web显示异常,清除缓存也无效?

发布的项目突然之间显示就不正常了&#xff0c;清除了n遍缓存&#xff0c;tomcat目录&#xff0c;一点效果都没有&#xff0c;这时候&#xff0c;应该用Dreamweaver检查下页面代码&#xff0c;很可能是你不小心删了点什么。 下面是一个血的教训&#xff0c;浪费了一上午时间&a…

python画图模块_学习python画图模块plotnine:第一步安装

官网链接https://plotnine.readthedocs.io/en/stable/index.html知乎介绍的文章https://zhuanlan.zhihu.com/p/47814353简单介绍语法类似于R语言的ggplot2包的python画图模块安装按照官网教程直接使用pip安装pip install plotnine遇到报错ERROR: Cannot uninstall certifi. It …

解决Oracle 11gR2 空闲连接过多,导致连接数满的问题

今天又遇到了11gR2连接数满的问题&#xff0c;以前也遇到过&#xff0c;因为应用那边没有深入检查&#xff0c;没有找到具体原因&#xff0c;暂且认为是这个版本Oracle的BUG吧。 上次的处理办法是用Shell脚本定时在系统中kill v$session.statusINACTIVE的连接&#xff0c;但是…

Java自定义标签

简单例子 实现一个标签分为两步&#xff1a;&#xff08;1&#xff09;继承SimpleTagSupport或TagSupport实现一个控制器&#xff08;2&#xff09;创建一个描述标签的tld文件。下面是一个简单例子&#xff1a; &#xff08;1&#xff09;实现一个标签控制器&#xff0c;向页…

python调用adb shell命令_Python之使用adb shell命令启动应用的方法详解

Python之使用adb shell命令启动应用的方法详解一直有一个心愿希望可以用Python做安卓自动化功能测试&#xff0c;在一步步摸索中&#xff0c;之前是用monkeyrunner&#xff0c;但是发现对于控件ID的使用非常具有局限性&#xff0c;尤其是ID的内容不便于区分具有重复性时&#x…

关于NPN和PNP传感器的应用区别(转载)

http://blog.csdn.net/greatwgb/article/details/9120735 PNP与NPN型传感器其实就是利用三极管的饱和和截止&#xff0c;输出两种状态&#xff0c;属于开关型传感器。但输出信号是截然相反的&#xff0c;即高电平和低电平。PNP输出是高电平1&#xff0c;NPN输出的是低电平0。 …

spring security:第一个程序

spring security虽然已经简化了&#xff0c;但配置还是要小心翼翼的。这里运行起第一个spring security程序。 环境&#xff1a; spring 4.2.4spring security 4.0.4 &#xff08;GA&#xff09; 搭建步骤&#xff1a; pom.xml 加入web和config模块&#xff0c;config模块是…

文献引用的标准格式_外语论文文献引用格式—APA Style

APA Style写外语论文时&#xff0c;非常注意引用格式的使用。APA作为常见的论文引用格式之一&#xff0c;常被用于心理学、教育学和社会学科领域。可是你知道APA格式要如何使用&#xff1f;APA格式有哪些要求&#xff1f;下面小编为大家整理一下~什么是APA格式&#xff1f;APA格…

每日一九度之 题目1030:毕业bg

时间限制&#xff1a;1 秒 内存限制&#xff1a;32 兆 特殊判题&#xff1a;否 提交&#xff1a;2046 解决&#xff1a;894 题目描述&#xff1a;每年毕业的季节都会有大量毕业生发起狂欢&#xff0c;好朋友们相约吃散伙饭&#xff0c;网络上称为“bg”。参加不同团体的bg会有不…

spring security:第一个程序解析

上一篇在一个项目里配置了spring security&#xff0c;这里大致说一些这些配置的作用。 pom.xml 文件解析 <!-- spring security --><!-- spring 安全--><dependency><groupId>org.springframework.security</groupId><artifactId>spring…