行列
行列
### 需求背景 在更新`DOM`时,如果需要大批量的更新,浏览器会卡,比如删除几千个节点。操作`DOM`我们又无法使用`Web Worker`等手段进行优化,只能在主线程上进行,因此有必要实现一个在主线程上的分片任务执行机制,利用时间片更新一些`DOM`后让主线程去处理下其它事件,然后再更新,以此来解决卡的问题。 因该分片任务执行机制是非常非常核心的功能,同时我们的性能是绝对重要的,在实现的过程中,我们要充分考虑各种情况和边界。 执行机制可随时暂停、停止和开始,可对任务进优先级的划分,可对排队中的任务进行中止或删除。 ### 简化双尾 在magix中把任务划分为`正常任务`和`低优先级任务`,而像`用户输入`,`动画`等`高优化级`的任务,在magix更新机制中优先给予保证,并不对外暴露`API`。 最终给用户使用的只有`正常任务`和`低优先级任务`,因此对任务列表就简化成双尾的形式,具体什么是双尾可继续阅读。 ### array实现 对任务进行划分,划分后的任务片我们很容易想到使用数组来进行保存,在执行时,只需要获取上次执行到的数组下标,继续执行后续的任务即可,而暂停或停止时,我们需要把当前执行到的下标保存下来,方便下次从该位置直接开始。 但一个数组无法方便实现对任务优化级划分,如果要在一个数组中存放不同优化级的任务,比如先进来一个`正常任务`,再进来一个`低优先级`的任务,此时如果再来一个`正常任务`,我们是需要遍历查找,并把这个`正常任务`插入到第一个任务的后面。 这样一来我们需要遍历,需要移动数组,而这是很致命的,在做这种底层和核心的设计时,任何一个性能的点都必须认真对待。 我在数组版本的实现里,对`正常任务`和`低优先级任务`使用`2`个数组来保存,这样就避免了遍历查找和插入移动数组内容。 实现代码如下 ```ts let CallIndex = 0; let CallList = []; let LowCallList = [];...
昨天有人在QQ小组问起,无限分层的树状结构,数据量比较大,在一万条以上,如何设计数据库的结构。其实这是个老生常谈的问题,一般的做法是有一个pid字段,为了提高效率,还会有个FullPath字段。(一些人还设置一个层级字段,但我不知道这个字段有何作用),FullPath字段可以用id-id-id….这种方式拼字符串存储,这样可以方便地用 like 语句进行查询某个节点及其子节点。 曾经看到过另外一种存储方式,利用了一般树结构可以转换二叉树的这一做法,用二叉树进行存储,在数据量大的情况下,存储读效率比上述的常见方案更优些,所以特写此文简单介绍一番。 下图说明了这种方案 图片丢失~~~ 如图所示,在每个节点上,有left ,right两个字段,我们看到,图上从根节点顺着子节点开始画一条线,每深入一层left加一,到底后,right=left+1,然后顺着节点回溯,right逐级加一,一直回到根节点。 如果要查询某个节点及其子节点,比如 fruit 节点 ,条件为 where left between 2 and 11 要查某个节点的full path ,比如 banana,条件为 where left9 如果要插入某个节点,比如red yellow直接插入一个节点,则update left =left+2 where left>=7...
> 本文不讨论manifest,servicework等技术,但本文给出的方案不与manifest,servicework等冲突,可以一起使用,从多维度给web加速 ### 资源的分与合 开发阶段我们通常根据自己的需要,把前端文件进行拆分,以进行更好的逻辑组合。 上线时,我们拆分的这些文件,有些打包工具会默认合并,比如webpack,而某些打包工具不会合并,比如magix-composer 我们的这些资源js、css等文件,到底是合并还是拆分需要具体到应用场景,不过从整体看,pc更适合拆分的方式,而移动更适合合并的方式,本文也不再深入讨论为什么pc适合拆分,移动适合合并。 因个人长期从事pc端的开发,因此接下来我们讨论的场景基于pc端,资源不合并的情况下。 ### loading场景 在pc端因为资源不合并,我们从任意一个入口进入时,即打开某个页面,比如首页、报表或列表等,可以最小化的加载所需要资源,让页面展示更快。 当我们从某个页面进入时,需要先加载资源,再请求接口,该过程会有一个稍长的loading过程 当我们从某个页面切换到其它页面时,仍然是加载所需要资源,再进行接口请求,最后才能渲染显示页面,该过程的loading由于某些资源在之前页面可能加载过,会比之前稍快一些,但仍旧较长,我们能否做到点击页面后就能立即显示,避免loading? ### 资源加速 我们默认并不会对所有资源进行打包,或分功能打包,开发时什么样,上线后仍然保持什么样的拆分粒度。 但我们可以使用离线编译工具对整个项目中的文件进行扫描处理,生成一份资源清单,比如这样 ```js //app/files.js export default [ "~mp/app/components/xy-btn", "~mp/app/dragdrop", "~mp/app/inner", "~mp/app/keyboard", "~mp/app/test/z-inner", "~mp/galleries/mx-table/index" ] ```...
> 本文仅讨论前端的实现方式 #### 为什么需要websocket 1. 挡不住的多页。虽然前后端分离的方式前端大多采用SPA来渲染界面,但是你依然挡不住客户端采用多页来打开相应的页面,比如列表页和创建页各使用1个tab,在创建页新增数据时,我们的列表页无法感知到并更新 2. 更及时的数据通知。有些数据需要服务端审核,不通过时需要及时更新客户端列表,否则就得待到客户端主动刷新才知道哪些审核不通过。 #### websocket实例 一个页面只需要1~2个websocket,大多数场景下只需要一个即可 大多数的场景下我们传输的数据都比较轻量,只需要一个websocket即可完成任务,而对于上传等大数据量的,我们需要再实例化一个,避免1个webscoket实例被大数据长时间占用导致数据更新不及时 #### 数据约定 前后端可能需要对数据做一下约定,比如使用bizcode来指代当前数据是哪个业务模块,这样在前端收到数据时,来决定交与哪个view来消费,可能的伪代码如下 ```js import Magix from 'magix'; export default Magix.View.extend({ init(){ socket.on('message',e=>{ if(e.bizcode=='xxx'){ this.render(e.data); } }); },...
magix中的view采用html和javascript分离的形式进行开发,然后通过magix-composer进行实时把html编译到javascript文件中,而在这个过程中,我们可以采取一定的方式进行性能优化处理。 ### 事件代理 看一段代码 ```html name ``` 当div.user-info需要响应事件时,我们可以直接把事件绑定到div.user-info上,当然我们也可以绑定到它的父级或祖先节点上,比如我们直接绑定到document.body上。通过把事件绑定到祖先节点上的方案,称为事件代理 事件代理的好处是子内容随便添加或删除,无需考虑解绑事件和重新添加事件,而坏处则是需要event.target向上遍历节点来检查是否是目标节点,伪代码如下 ```js let { target } = event; while (target != document.body) { if (target.classList.contains('user-info')) { //find break; } target = target.parentNode;...
### 单双引号 #### 在js代码中 > 在js中代码中,单、双引号引起来的是字符串,如果我们要在字符串中使用单、双引号,需要反斜杠进行转义 ```js let str='user\'s name'; // or let str=" user's name"; // let str="she said:\"...\"."; ``` >如果在字符串中输出反斜杠,仍然是用反斜杠转义,即2个反斜杠输出1个反斜杠 #### 在html代码中 > html标签中,属性值通常用双引号引起来,也可以使用单引号或不用引号。 ```html ``` > 这3种写法都正确,不过通常我们是选择用双引号引起来。...
考虑以下html片断 ```html ``` 在这段html代码中,共有三个属性名称大写了的情况。 ### 向view传递数据时使用了大写的情况,即*suggestList 该场景子view接收参数时,使用的是驼峰,所以要求我们传递时也需要使用驼峰的形式,然而在属性中不支持属性大写的情况,所以我们需要把 ***suggestList** 改写为 ***suggest-list** magix会根据规则再把该属性变成驼峰形式 ### 自定义事件中使用了大写的情况,即mx-showList 这种情况直接把大写改为小写,同时派发事件的地方也要改为小写 **mx-showList** 改写为 **mx-showlist** mx-*自定义事件的设计请参考原生dom节点事件的设计。 原生事件,比如pagehide,pageshow,animationstart,fullscreenchange等,全部为小写,同时也没有使用连字符 ### 其它自定义属性,即AA 需要开发者自己改为小写 [http://w3c.github.io/html/single-page.html#element-definitions-attributes](http://w3c.github.io/html/single-page.html#element-definitions-attributes)
现阶段前端的开发思路被统一成数据驱动,我们只需要操作数据即可,而界面的更新与变化则由相应的库和框架进行代为操作。 ### 虚拟DOM 现阶段的流行类库,如`React`,`Vue`等,都使用虚拟DOM为中间层,数据变化后先更新虚拟DOM,再由算法计算新的虚拟DOM与旧虚拟DOM的差异,然后把差异应用到真实DOM上。 虚拟DOM带来的问题是频繁GC,如下代码 ```html content ``` 我们使用数据x,y来定位一个div的位置,这段代码生成的伪虚拟DOM代码如下 ```js $vnode_1 = [$createVNode(0, 'content')]; $vnode_0.push($createVNode('div', { 'style': 'left:' +$nullCheck(x) + 'px;top:' +$nullCheck(y) + 'px', }, $vnode_1)); ``` 当我们频繁更新x,y时,比如拖动,则上述虚拟DOM对象将被一直创建。对比使用完成后,旧的虚拟DOM则被GC,在高频情况下,将不断的GC。 ### Svelte...
链接地址:https://www.cnblogs.com/coco1s/p/14439760.html > 个人在处理前端细节上,与文章中契合的点非常多,因此转载记录。 > 自身在可访问性上关注及投入不多,后续需要加大投入
```js let listToTree = (list, id = 'id', pId = 'pId') => { let map = {}, listMap = {}, rootList = []; for (let i = 0, max =...