Blogs
Blogs copied to clipboard
SugarTurboS Blog
> * 苏格团队 > * 作者:Jason # 背景 由于业务的需要,笔者最近需要实现一个大量图片同时加载的需求。在实现这个需求的过程中,笔者遇到了很多的坑,也总结了一些优化方案。这里将笔者使用或准备使用的优化方案总结一下。 # 具体场景 在描述如何解决问题,我们现在先来申明,**问题是什么?** 笔者的需求大概是在某个页面显示 **1~1000张**,**200~500k**大小的图。好消息是这些图片来源于本地硬盘而非网络。(否则这个问题就要变成优化网络....) # 踩坑历程 由于不是纯前端的项目,笔者可以从本地文件夹中读取文件。然后一段代码劈里啪啦的就出现了。 ``` const fileList = this.props.fileList; return ( { fileList.map((file) => { }) }...
### 什么是迭代器 迭代器就是为实现对不同集合进行统一遍历操作的一种机制,只要给需要遍历的数据结构部署Iterator接口,通过调用该接口,或者使用消耗该接口的API实现遍历操作。 ### 迭代器模式 在接触迭代器之前,一起先了解什么是迭代器模式,回想一下我们生活中的事例。我们在参观景区需要买门票的时候,售票员需要做的事情,他会对排队购票的每一个人依次进行售票,对普通成人,对学生,对儿童都依次售票。售票员需要按照一定的规则,一定顺序把参观人员一个不落的售完票,其实这个过程就是遍历,对应的就是计算机设计模式中的迭代器模式。迭代器模式,提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。 ### 为什么要有迭代器 回忆在我们的javascript中,可遍历的结构以及方式有很多。JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set,这样就有了四种数据集合,而遍历这四种结构都有不同的方法。举个栗子,服务端提供数据给前端,前端进行数据可视化工作,对数据进行遍历展示使用的for,但是由于业务的变化,使得后端返回的数据结构发生变化,返回对象或者是set,map,导致前端遍历代码大量重写。而迭代器的目的就是要标准化迭代操作。 ### 如何部署迭代器接口 ES6为迭代器引入了一个隐式的标准化接口。Javascript许多内建的数据结构,例如Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList 对象都具备 Iterator 接口。可以通过在控制台打印一个Array实例,查看其原型上具有一个Symbol.iterator属性(Symbol.iterator其实是Symbol('Symbol.iterator')的简写,属性名是Symbol类型代表着这个属性的唯一以及不可重写覆盖),它就是迭代器函数,执行这个函数,就会返回一个迭代器对象。 虽然Javascript许多内建的数据结构已经实现了该接口,还有些结构是没有迭代器接口的(比如对象),那怎么办,我们需要写迭代器,那么就需要知道迭代器是如何工作的。下面代码实现的一个简单迭代器: ```js //迭代器就是一个函数,也叫迭代器生成函数 function Iterator(o){ let curIndex = 0; let next...
## 程序启动参数 当你的应用启动时,你可能需要同时带上一些参数来影响程序的执行逻辑。比如,通过设置启动参数,来让程序换不同的皮肤。 以windows系统为例,在windows的exe上右键属性,可以看到如下界面  在目标一栏,是程序exe的路径,我们可以在这个路径后面加入特殊的参数来满足我们的需求。 "C:\Program Files (x86)\Tencent\WeChat\WeChat.exe" --args theme=dark 我们在路径上加入了theme的参数,值为dark,应用启动时读取这个参数,可以以dark的主题来显示界面。在Electron开发的程序中,如何实现这样的功能呢? 其实原理很简单,我们都知道Electron的主进程其实就是个node进程,所以可以直接通过node支持的方式去获取启动参数。在以往用node实现web server的时候,我们会通过传入 --worker=2 的形式来配置worker数量,然后使用process.argv来获取。同理,在electron中也可以这么干。 ```js //主进程 main.js const { app, BrowserWindow } = require('electron'); console.log(process.argv); ``` 上面代码的输出结果为下图 ...
## 进程之间通信 在使用Electron开发应用的时候,我们往往需要在主进程和渲染进程之间互相传递数据来实现产品的需求。 假设我们现在要做一个APP,APP中有一个窗口来显示当前机器的CPU型号,总内存等信息。逻辑很简单,只需要拿到相关信息后,展示在窗口中就行了。在Electron的渲染进程中,没有直接提供API去获取相关的信息,所以要借助主进程去获取信息后,传递到渲染进程显示。 要实现这样的功能,我们可以用以下三种方式来实现 * 方法调用 * 数据共享 * 数据消息 我们来用代码例子来分别讲解下这三种方式 示例程序的主要目录结构如下 ``` demo/ ├── main │ ├── systemInfo.js │ └── index.js ├── package.json └── renderer └── index.js ```...
`本文主要讲解上一章节提到的主进程与渲染进程的概念,以及它们是如何被使用的` `如果对文章的内容有任何疑问或吐槽,请直接在下方评论,大家共同学习和改进` `阅读时间:约4min` ## 主进程与渲染进程 我们来回顾一下,程序目录结构章节中所给出的基本目录结构 app----------------------------应用程序代码目录 ├─main.js----------------------程序启动入口,主进程 ├─common-----------------------通用模块 ├─log--------------------------日志模块 ├─config-----------------------配置模块 ├─ipc--------------------------进程间模块 ├─appNetwork-------------------应用通信模块 └─browserWindows---------------窗口管理,渲染进程 ├─components---------------通用组件模块 ├─store--------------------数据共享模块 ├─statics------------------静态资源模块 └─src----------------------窗口业务模块 ├─窗口A----------------窗口 └─窗口B----------------窗口 在上面的目录结构中,`main.js`就是我们所说的主进程。而通过`browserWindows`目录下窗口文件创建的进程,我们称之为渲染进程。渲染进程需要通过主进程来创建,并被主进程所管理。这里大家可能会有疑问了,什么是进程?为什么要分主进程和渲染进程呢? ### 进程的概念 计算机进程相关的知识在搜索引擎中可以搜到很多,我们这里不做过多的讲解。读者可以把进程理解为操作系统管理应用程序的基本单位,每个进程之间的资源是不能直接共享的。打开操作系统的任务管理器,我们可以看到当前操作系统都有那些进程,如下图  ### 主进程 回顾以往的web开发,我们的代码,无论是HTML、CSS还是Javascript,都是运行在浏览器的沙盒中的,我们无法越过浏览器的权限访问系统本身的资源,代码的能力被限制在了浏览器中。浏览器之所以这么做,是为了安全的考虑。设想一下,我们在使用浏览器的时候,会打开各式各样不同来源的网站,如果JavaScript代码有能力访问并操作本地操作系统的资源,那将是多么可怕的事情。你在某天不小心打开了一个恶意的网站,可能你存储在硬盘上的文件就被偷走了(都用不着去修电脑)。 但我们要开发的是桌面应用程序,如果无法访问到本地的资源肯定是不行的。Electron将nodejs巧妙的融合了进来,让nodejs作为整个程序的管家。管家拥有较高的权限,可以访问和操作本地资源,使用原本在浏览器中不提供的高级API。同时管家也管理着渲染进程窗口的创建和销毁。所以,我们将这个管家称之为`主进程`。在使用Electron开发的程序中,会使用main.js作为程序的主入口,该文件内代码执行的内容,就是主进程中执行的内容。 下面我们一起来看看主进程中一般都做些什么。...
`这篇文章主要是通过对简单的Electron应用程序的开发目录进行介绍,让读者对整个开发视图有初步的了解,能大概知道开发一个Electron程序需要具备哪些模块。` `如果对文章的内容有任何疑问或吐槽,请直接在下方评论,大家共同学习和改进` `阅读时间:约5min` ## 程序目录结构 Electron应用程序分成三个基础模块: 1. 主进程 2. 进程间通信 3. 渲染进程  对于做纯web开发的新人,可能对多进程开发结构不熟悉。在浏览器中,基于安全策略考虑,web页面通常是在一个沙盒环境中运行的,不被允许去接触原生的资源。然而在Electron中允许页面(渲染进程)调用Node.js的API,所以页面可以与操作系统底层进行交互。 我们知道每个进程都是一个独立运行单位,相互不能直接通信。在Electron中提供两种方法在主进程与渲染进程之间进行通信。 - 使用ipcRenderer和ipcMain模块发送消息 - 使用remote模块进行 RPC 方式的通信 Electron程序目录基础目录结构如下: app----------------------------应用程序代码目录 ├─main.js----------------------程序启动入口,主进程 ├─ipc--------------------------进程间通信模块 └─browserWindows---------------窗口管理,渲染进程 └─src----------------------窗口业务模块 ├─窗口A----------------窗口A └─窗口B----------------窗口B ###...
`系列简介:本系列文章首先围绕Electron框架的关键知识点进行详细讲解,然后对DEMO程序进行分析,让前端开发人员对使用Electron开发桌面应用程序有一个初步的了解。该系列文章更新周期为每周1~2篇。` `阅读时间:约5min` ## Electron 是什么 ### 定义 Electron是一个能让你使用传统前端技术(Nodejs, Javascript, HTML, CSS)开发一个跨平台桌面应用的框架。这里所说的桌面应用指的是在Windows、OSX及Linux系统上运行的程序。 ### 历史 2013年的时候,Atom编辑器问世,作为实现它的底层框架Electron也逐渐被熟知,到2014年时被开源,那时它还是叫`Atom Shell`。 接下来的几年,Electron在不断的更新迭代,几乎每年都有一个重大的里程碑 * 2013年4月11日,Electron以Atom Shell为名起步。 * 2014年5月6日,Atom以及Atom Shell以MIT许可证开源。 * 2015年4月17日,Atom Shell改名为Electron。 * 2016年5月11日,1.0版本发布。 * 2016年5月20日,允许向Mac应用商店提交软件包。 *...
## 背景 组织为了更好的对各个业务的请求日志进行统一的分析,制定了统一的日志打印规范,比如: ```bash [time][processId][traceId][userid] Hello World.... ``` 统一格式之后,业务现有业务的日志工具打印出来的格式是无法满足该规范的,所以我们需要对此进行改造。 我们前端目前Node中间层使用的框架是Egg.js,所以下文讲述下如何在Egg.js上自定义请求日志格式。 ## 开始动手 Egg.js中自带了三种logger,分别是 * Context Logger * App Logger * Agent Logger Context Logger主要是用来记录请求相关的日志。每行日志都会在开头自动的记录当前请求的一些信息,比如时间、ip、请求url等等。 App Logger用于记录应用级别的日志,比如程序启动日志。 Agent Logger用于记录多进程模式运行下的日志。 我们想自定义请求级别的日志,那重点就要从`Context Logger`去研究怎么做。最理想的方案就是,`Context...
我们知道,TCP是面向连接流传输的,其采用Nagle算法,在缓冲区对上层数据进行了处理。避免触发自动分片机制和网络上大量小数据包的同时也造成了粘包(小包合并)和半包(大包拆分)问题,导致数据没有消息保护边界,接收端接收到一次数据无法判断是否是一个完整数据包。那有什么方案可以解决这问题呢? *** ### 1、粘包问题解决方案及对比 很简单,既然消息没有边界,那我们在消息往下传之前给它加一个边界识别就好了。 1. 发送固定长度的消息 2. 使用特殊标记来区分消息间隔 3. 把消息的尺寸与消息一块发送 第一种方案不够灵活;第二种有风险,如果数据内刚好有该特殊字符会出问题;第三种方案虽然要增加对消息头的解析,不过相对而言还是要安全一些。 ### 2、分包与拆包 既然使用第三种方案,就必然涉及到封包和拆包的问题。 首先肯定需要定义数据包的结构,这类似Http包一样,有包头和包体。包头其实上是个大小固定的结构体,其中有个结构体成员变量表示包体的长度,其他的结构体成员可根据需要自己定义。根据包头长度固定以及包头中含有包体长度的变量就能正确的拆分出一个完整的数据包。包体则存放数据内容。  在发送端,需要进行封包。封包就是给一段数据加上包头,这样一来数据包就分为包头和包体两部分内容了。 在接受端,则需要进行拆包。主要流程如下: 1. 为每一个连接动态分配一个缓冲区,同时把此缓冲区和SOCKET关联. 2. 当接收到数据时首先把此段数据存放在缓冲区中. 3. 判断缓存区中的数据长度是否够一个包头的长度,如不够,则不进行拆包操作. 4. 根据包头数据解析出里面代表包体长度的变量. 5. 判断缓存区中除包头外的数据长度是否够一个包体的长度,如不够,则不进行拆包操作. 6....
由于种种原因,我们选择了Ueditor作为我们的富文本编辑器选型。 Ueditor不支持模块化,所以无法在代码中使用import去引入。一开始我们在项目中是将Ueditor的js文件直接通过script标签引入,在React的代码里直接使用window.UE去使用编辑器。但是这就有一个问题,我们对UE的源码进行了改动,加入了定制化的功能。而直接引入的UE文件在浏览器是有缓存的,我们每次改动都要清除缓存才能生效。 我们要解决缓存这个问题,webpack配置就必须满足以下条件: 1. 每次改动代码后,能自动给UE的文件名加hash 2. 能自动插入html模板文件并在主入口文件加载之前加载完成 ## 第一步 为了能让UE的文件进入打包流程,我们将它作为一个新的入口文件 ```js const entry = { main: ['babel-polyfill', './src/main.js'], ueditor_config: ['./src/common/UEditor/ueditor.config.js'], ueditor_all: ['./src/common/UEditor/ueditor.all.js'] }; new HtmlWebpackPlugin({ template: `./src/app/${key}/templates/${filename}`, filename: `../view/${targetHtml}`, hash:...