在Koajs中实现文件上传的接口

文件上传是一个基本的功能,每个系统几乎都会有,比如上传图片、上传Excel等。那么在Node Koa应用中如何实现一个支持文件上传的接口呢?本文从环境准备开始、最后分别用 Postman 和一个HTML页面来测试。 环境准备首先当然是要初始化一个Koa项目了,安装 Koa、koa-router 即可。 npm install koa koa-router设置图片上传目录,把图片上传到指定的目录中,在 app 路径下新建 public 文件夹,目录结构如下: koa-upload/--app----public------uploads----index.js--package.json编写 index.js const koa = require('koa')const app = new koa()router.post('/upload', ctx => { ctx.body = 'koa upload demo'})app.use(router.routes());app.listen(3000, () => { console.log('启动成功') console.log('http://localhost:3000')});然后启动,确保这一步没有问题。 使用 koa-body 中间件获取上传的文件koa-body 支持文件、json、form格式的请求体,安装 koa-body npm install koa-body设置 koaBody 配置参数,index.js const koa = require('koa')const koaBody = require('koa-body')const path = require('path')const app = new koa()app.use(koaBody({ // 支持文件格式 multipart: true, formidable: { // 上传目录 uploadDir: path.join(__dirname, 'public/uploads'), // 保留文件扩展名 keepExtensions: true, }}));... ...接下来完善 /upload 路由,获取文件,然后直接返回文件路径 ...

October 7, 2019 · 1 min · jiezi

译JavaScript工作原理V8编译器的优化

原文链接:https://blog.logrocket.com/ho...原文标题:How JavaScript works: Optimizing the V8 compiler for efficiency 本文首发于公众号:符合预期的CoyPan 理解JavaScript的工作原理是写出高效JavaScript代码的关键。 忘记那些无关紧要的毫秒级改进:错误地使用对象属性可能导致简单的一行代码速度降低7倍。 考虑到JavaScript在软件堆栈所有级别中的普遍性,即使不是所有级别的基础设施,也可能会出现微不足道的减速,而不仅仅是网站的菜单动画。 有许多的方法来编写高效的JavasScript代码,但在这篇文章里面,我们将着重介绍编译器友好的优化方法,这意味着源代码使编译器优化变得简单有效。 我们将把讨论范围缩小到V8,即支持electron、node.js和google chrome的JavaScript引擎。为了理解编译器友好的优化,我们首先需要讨论JavaScript是如何编译的。 JavaScript在V8中的执行可以分为三个阶段: 源代码到抽象语法树:解析器将源代码生成抽象语法树(AST)抽象语法树到字节码:V8的解释器Ignition从抽象语法树生成字节码。请注意,生成字节码这一步在2017年以前是没有的。字节码到机器码:V8的编译器TurboFan从字节码生成一个图,用高度优化的机器代码替换字节码的部分。第一个阶段超出了本文的范围,但是第二个和第三个阶段对编写优化的JavaScript有直接的影响。 我们将讨论这些优化方法以及代码如何利用(或滥用)这些优化。通过了解JavaScript执行的基础知识,您不仅可以理解这些性能方面的建议,还可以学习如何发现自己的一些优化点。 实际上,第二和第三阶段是紧密耦合的。这两个阶段在即时(just-in-time,JIT)范式中运行。为了理解JIT的重要性,我们将研究以前将源代码转换为机器代码的方法。 Just-in-Time (JIT) 范式为了执行任意一段程序,计算机必须将源代码转换成机器可以运行的代码。 有两种方法可以进行转换。 第一种选择是使用解释器。解释器可以有效地逐行翻译和执行。 第二种方法是使用编译器。编译器在执行之前立即将所有源代码转换为机器语言。 下面,我们将阐述两种方法的优点和缺点。 解释器的优点、缺点解释器使用read-eval-print loop (REPL,交互式解释器)的方式工作 —— 这种方式有许多的优点: 易于实现和理解及时反馈更合适的编程环境然而,这些好处是以缓慢执行为代价的: (1)eval的开销,而不是运行机器代码。 (2)无法跨程序的对各个部分进行优化。 更正式地说,解释器在处理不同的代码段时不能识别重复的工作。如果你通过解释器运行同一行代码100次,解释器将翻译并执行同一行代码100次,没有必要地重新翻译了99次。 总结一下,解释器简单、启动快,但是执行慢。 编译器的优点、缺点编译器会在执行前翻译所有的源代码。 随着复杂性的增加,编译器可以进行全局优化(例如,为重复的代码行共享机器代码)。这为编译器提供了比解释器唯一的优势 —— 更快的执行时间。 总结一下,编译器是复杂的、启动慢,但是执行快。 即时编译(JIT)即时编译器尝试结合了解释器和编译器的优点,使代码转换和执行都变得更快。 基本思想是避免重复转换。首先,探查器会通过解释器先跑一遍代码。在代码执行期间,探查器会跟踪运行几次的热代码段和运行很多次的热代码段。 JIT将热代码片段发送给基线编译器,尽可能的复用编译后的代码。 JIT同时将热代码片段发送给优化编译器。优化编译器使用解释器收集的信息来进行假设,并且基于这些假设进行优化(例如,对象属性总是以特定的顺序出现)。 但是,如果这些假设无效,优化编译器将执行 去优化,丢弃优化的代码。 优化和去优化的过程是昂贵的。由此产生了一类JavaScript的优化方法,下面将详细描述。 JIT需要存储优化的机器代码和探查器的执行信息等,自然会引入内存开销。尽管这一点无法通过优化的JavaScript来改善,但激发了V8的解释器。 V8的编译V8的解释器和编译器执行以下功能: 解释器将抽象语法树转换为字节码。字节码队列随后会被执行,并且通过内联缓存收集反馈。这些反馈会被解释器本身用于随后的解析,同时,编译器会利用这些反馈来做推测性的优化。编译器根据反馈将字节码转换为特定于体系结构的机器码,从而推测性地优化字节码。V8的解释器 - IgnitionJIT编译器显示了开销内存消耗。Ignition通过实现三个目标来解决这个问题:减少内存使用、减少启动时间和降低复杂性。 这三个目标都是通过将AST转换为字节码并在程序执行期间收集反馈来实现的。 字节码被当做源代码对待,省去了在编译期间重新解析JavaScript的需要。这意味着使用字节码,TurboFan的去优化过程不再需要原始的代码了。作为基于程序执行反馈的优化示例,内联缓存允许V8优化对具有相同类型参数的函数的重复调用。具体来说,内联缓存存储函数的输入类型。类型越少,需要的类型检查就越少。减少类型检查的数量可以显著提高性能。AST和字节码都会暴露给TurboFan。 V8的编译器 - TurboFan在2008年发布时,V8引擎最初直接将源代码编译为机器代码,跳过了中间字节码表示。在发布时,V8就比竞争对手快了10倍。 然而,到今天,TurboFan接受了Ignition的字节码,比它发布的时候快了10倍。V8的编译器经过了一系列的迭代: 2008 – Full-Codegen 具有隐藏类和内联缓存,快速遍历AST的编译器缺点:无优化的即时编译2010 – Crankshaft ...

October 7, 2019 · 2 min · jiezi

Vuecli连接mysql

本文把前后台一起串起来,前端使用vue-cli后台用nodejs连接数据库,vue-cli请求接口其数据是来自于mysql数据。一、vue-cli请求接口部分 <template> <div id="app"> <router-view/> </div></template><script>export default { name: 'App', mounted(){ this.axios.get("api/list").then((res)=>{ console.log(res.data[0]); }) }}</script>接口部分当然要设置代理了。设置代理连接:https://www.3mooc.com/front/a...二、nodejs连接数据部分 express-generator 1. 全局安装 npm install express-generator -g2. express --view=ejs 项目名称 例如: express --view=ejs server3. npm install mysqlvar mysql = require('mysql');var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : 'rootroot', database : 'demo'});module.exports = connection;var express = require('express');var router = express.Router();var connection = require('../db/sql.js');/* GET home page. */router.get('/', function(req, res, next) { res.render('index', { title: 'Express' });});router.get('/list', function(req, res, next) { connection.query("select * from user",function(error, results, fields){ res.json(results); })});module.exports = router;每日分享文章-附带视频教程:https://www.3mooc.com/front/c... ...

October 7, 2019 · 1 min · jiezi

使用hexo和github搭建静态博客网站三

博客网站功能完善这节我们只会介绍几个完善网站功能的方法,如果你还想增加其他功能,可以通读NexT 使用文档、文档|hexo,根据自己的需要来增加功能。 设置语言修改站点配置文件(_config.yml)的language字段,比如设置为简体中文 language: zh-Hans此时页面变为 设置菜单修改主题配置文件(/themes/next/_config.yml)的menu字段 menu: home: / || home #about: /about/ || user #tags: /tags/ || tags #categories: /categories/ || th archives: /archives/ || archive #schedule: /schedule/ || calendar #sitemap: /sitemap.xml || sitemap #commonweal: /404/ || heartbeat修改为 menu: home: / || home #about: /about/ || user tags: /tags/ || tags categories: /categories/ || th archives: /archives/ || archive #schedule: /schedule/ || calendar #sitemap: /sitemap.xml || sitemap #commonweal: /404/ || heartbeat创建tags页面 ...

October 6, 2019 · 1 min · jiezi

Nodejs到底是什么

接触前端也有一段时间了,逐渐开始接触Node.js,刚刚接触Node.js的时候一直都以为Node.js就是JavaScript,当对Node.js有一定的了解之后,其实并不然两者之间有关系,其中的关系又不是必然的,对Node.js进行的一些了解,对其进行一些概述,本篇文章并没有对Node.js的API进行讲解,而是能够更加的明白Node.js是什么。 到底什么是Node.js先看一下Node.js官网中是如何形容Node.js的,打开官网看到的第一句话就是Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.(Node.js是一个JavaScript运行时建立在Chrome的V8 JavaScript引擎。)在上面这段话中最重要的一点就是运行时。 到底什么是运行时呢?其实在笔者看来运行时就是程序在运行时所需要的组件,可以将其想象成为是一种编程语言的运行环境。然而这个运行环境包含了代码运行时所需要的解释器和底层操作系统的支持等。 文章开头也说过Node.js与JavaScript之间有关系,但是其关系也不是必然,到这里大概也就有点眉目了。对于任何语言来说,其中最终要的就是其解释器如何去处理这些编程语言。Node.js的底层是使用C++实现的,然而语法则是遵循ECMAScript规范,其实完全可以把其实现换乘一种新的编程语言,更换语言的同时也就意味着其解释器发生了翻天覆地的变化。 Node.js为什么要选择JavaScript到了这里可能有些疑问,编程语言和解释器有关系,那么为什么要选择了JavaScript然而不是其他的语言呢?Node.js作者(Ryan Dahl)说,在创造Node.js的时,其目的是为了实现高性能的Web服务器,其看重的并不是JavaScript这门语言。但是他需要的事一种编程语言来实现其想法,这种编程语言不能带有任何的IO功能,并且需要良好的支持事件机制。说到这里感觉就是在说JavaScript这门语言(感觉就是天命之选,O(∩_∩)O哈哈~)。首先JavaScript完全满足上述的两个条件,然而就顺其自然的JavaScript就成了Node.js的主导者。 Runtime上面一直提到的就是Runtime,Runtime是什么?运行时刻是指一个程序在运行(cc或者在被执行)的状态。也就是说,当你打开一个程序使它在电脑上运行的时候,那个程序就是处于运行时刻。在一些编程语言中,把某些可以重用的程序或者实例打包或者重建成为运行库。这些实例可以在它们运行的时候被链接或者被任何程序调用(节选自百度百科)。 其实对于开发者来说根本就不用去考虑其背后到底是怎样实现的,我们站在开发的角度来想一想,对于某一种语言的Runtime表示开发者可以在Runtime上运行某种语言所编写的代码,如果把这个概念扩大一下说的话,Chorome也是一个JavaScript运行时依赖于背后的JavaScript引擎来运行JavaScript代码而已。 其对应的Runtime可以对其编程语言进行一些拓展,比如在Node.js中的fs、Buffer就是其对ECMAScript的拓展,Runtime并不包含整个ECMAScript中的全部特性。反过来讲,就算一个特性没有体现在标准里,而大多数的运行时都支持它,也可以变成实际上的规范。通过上述所说我们可以理解到对于任何语言来讲我们无需对其底层的实现,所有的东西都依赖于其运行时的实现而已,运行时环境对其支持情况才能表现出其语言的特性。 同样的一段代码可能在浏览器端可以顺利执行,但是放到Node.js中不一定可以顺利执行,反之也是一样的,这样的就足可以说明上述问题了。 Node.js内部机制Node.js中有几个很重要的关键词单线程,非阻塞异步IO,在笔者刚刚接触Node.js的时候,这几个词经常听到,有些懵懵懂懂不是太能理解。为了更好的了解其内部机制那么针对这些东西进行说明。 回调函数为什么要说回调函数呢?对Node.js模块有一定了解的话Node.js中模块都是依赖于回调函数的,那么什么是回调函数呢? 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。(节选自百度百科)。 上面说了一堆套话,其实回调函数就是讲一个函数作为参数传给另一个函数作为参数,并且该函数可以被执行。回调方法和主线程处于同一个线程,假设主线程发起了一个底层的系统调用,操作系统会执行这个系统调用,当这个系统调用完成之后则会再回到主进程去执行后续的方法。 在Node.js中在操作过程中可能会有一个比较耗时的IO操作,当IO操作有了返回结果之后才会继续向下执行,其中在进行IO操作时就造成了代码的阻塞,在Node.js最初设计的时候已经考虑到了这一点,所以提出了异步函数加回调函数的方式,也能实现高并发的处理。对于前端来讲Ajax就是一个异步回调函数,当发起请求时如果有后续代码会先向下继续执行,而不会等待期请求结果。 回调函数机制: 定义一个回调函数;提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。同步/异步有关于同步/异步也搜索了一些文献,但是都是简简单单概括一下,没有细致的说明。所谓同步和异步其描述的事进程和线程的调用方式。因为Node.js的单线程,因此同个时间只能处理同个任务,所有任务都需要排队,前一个任务执行完,才能继续执行下一个任务,但是,如果前一个任务的执行时间很长,比如文件的读取操作或网络请求,后一个任务就不得不等着,拿文件的读取操作来说,当用户向后台读取大量的文件时,不得不等到所有数据都读取完毕才能进行下一步操作,后续程序只能在那里干等着,很有可能造成响应超时。因此,Node.js在设计的时候,就已经考虑到这个问题,主线程可以完全不用等待文件的读取完毕,可以先挂起处于等待中的任务,先运行排在后面的任务,等到文件的读取有了结果后,再回过头执行挂起的任务,因此,任务就可以分为同步任务和异步任务。 同步任务:同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务,当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务异步任务:异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务上述所说同步调用指的是进程/线程发起调用后,一直等待调用结果返回后才会继续向下执行,但是对于Node.js来说虽然也是这样,但是并不代表的CPU在这段时间内也会一直等待,操作系统多半会切换到另一个进程/线程上等调用返回结果后在切回原有进程/线程。然而异步则恰恰相反,当发起异步调用时,进程/线程会继续向下执行,当调用返回结果后通过某种技术手段通知其调用者已经有其结果。 我们一直都在说的一句话就是JavaScript是一门异步语言,但是对于ECMAScript而言并没有对异步有明确的规范,其实是其解释器(Node.js或浏览器)的runtime的其他线程来实现的,这些并不是JavaScript这门语言本身的功能。 对于异步请参考:浅析JavaScript异步 阻塞/非阻塞笔者在没有了解阻塞/非阻塞之前一直以为同步/异步与阻塞/非阻塞之间是没有区别的,然而现实就是这么的打脸,阻塞/非阻塞和同步/异步完全就是两组概念,他们之间没有任何的必然关系。很多人大概和我一样同步=阻塞,异步=非阻塞,这种概念是完全不对的。 在了解阻塞与非阻塞之前首先要了解一下什么是IO操作,IO操作其实是内存与外部设备之间复制数据的过程。 在阻塞的情况,是会一直等待直到write完全部的数据再返回。这点行为上与读操作有所不同,究其原因主要是读数据的时候,通常刚开始我们并不知道要读的数据的长度,而是在数据的头部设置了一个长度,在读完指定长度的头部后,才知道整个要读的数据长度。如果一开始就贸然设置一个要读的数据长度,然后像阻塞的write那样去等读完,则很可能会造成死循环;而对于write,由于需要写的长度是已知的,所以可以一直再写,直到写完。不过问题是write是可能被打断造成write一次只write一部分数据,所以write的过程还是需要考虑循环write, 只不过多数情况下一次write调用就可能成功。 非阻塞写的情况,是采用可以写多少就写多少的策略。与读不一样的地方在于,有多少读多少是由网络发送端是否有数据传输到本地内核缓存为准。但是对于可以写多少是由本地的网络堵塞情况为标准的,在网络阻塞严重的时候,网络层没有足够的内存来进行写操作,这时候就会出现写不成功的情况,阻塞情况下会尽可能(有可能被中断)等待到数据全部发送完毕, 对于非阻塞的情况就是一次写多少算多少,没有中断的情况下也还是会出现write到一部分的情况。 其实用一句话来说讲的话,同步调用会造成进程的IO阻塞,而异步不会造成调用进程的IO阻塞。 单线程与多线程Node.js并没有提供多进程的支持,这代表在程序中所编写的代码只能运行在当前进程中,用于运行代码的事件也是单线程进行的。开发者无法在一个独立进程中增加新的线程吗,但是可以派生出多个进程来达到必行完成任务。 进程 进程是指在操作系统中正在运行的一个应用程序 线程 线程是指进程内独立执行某个任务的一个单元。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈)。 对于Node.js,如果说JavaScript的函数式编程方式使得其异步编程的思想对程序员展现得更自然,那么它背后的功臣Libuv,则为异步编程的实现提供了可能。 上图中从左往右分为两部分,一部分是与Network I/O相关的请求,而另外一部分是由File I/O, DNS Ops以及User code组成的请求。 从图中可以看出,对于Network I/O和以File I/O为代表的另一类请求,异步处理的底层支撑机制是完全不一样的。 对于Network I/O相关的请求, 根据OS平台不同,分别使用Linux上的epoll,OSX和BSD类OS上的kqueue,SunOS上的event ports以及Windows上的IOCP机制。 而对于File I/O为代表的请求,则使用thread pool。利用thread pool的方式实现异步请求处理,在各类OS上都能获得很好的支持。Libuv团队为什么要选择thread pool的机制。基本上原因不外乎编码和维护复杂度太高、可支持的API太少且质量堪忧、技术支持较弱,而用thread pool则很好地避开了这些问题。 Node.js的异步调用时由Libuv来支持的,以readFile为例的话,读取文件的系统调用是由Libuv来完成的,Node.js只负责调用Libuv所提供的接口就可以了,等结果返回后在执行对应的回调方法。 并行与并发自从Node.js出现后,JavaScript开始涉及后端领域,因为其出色的并发模型,被很多企业用来处理高并发请求。 ...

October 6, 2019 · 1 min · jiezi

使用hexo和github搭建静态博客网站二

使用hexo搭建博客网站全局安装hexo-clinpm install hexo-cli -gnpm安装速度较慢,可以切换到国内的淘宝NPM镜像,使用cnpm命令代替npm命令安装。 安装完成后执行hexo -v检查安装是否完成。 hexo -vhexo-cli: 1.1.0os: Darwin 18.2.0 darwin x64http_parser: 2.8.0node: 10.15.3v8: 6.8.275.32-node.51uv: 1.23.2zlib: 1.2.11ares: 1.15.0modules: 64nghttp2: 1.34.0napi: 3openssl: 1.1.0jicu: 62.1unicode: 11.0cldr: 33.1tz: 2018e初始化博客工程hexo init blogcd blog安装NexT主题我们这里选取了NexT主题替换默认的landscape主题,当然你完全可以使用默认的landscape主题,或者根据自己的喜好选择其他主题。安装主题的方式非常简单,只需要将主题文件克隆至工程目录的 themes目录下, 然后修改下配置文件_config.yml即可。 在工程目录下克隆最新版本的next主题 cd bloggit clone https://github.com/iissnan/hexo-theme-next themes/next修改根目录下_config.yml配置文件,找到theme字段,将landscape改为next。 # Extensions## Plugins: https://hexo.io/plugins/## Themes: https://hexo.io/themes/theme: landscape修改为 # Extensions## Plugins: https://hexo.io/plugins/## Themes: https://hexo.io/themes/theme: next执行hexo server,启动本地服务器。 hexo server访问网址http://localhost:4000/便可以看到使用next主题的博客网站的样子了。 将本地hexo工程连接到git远端仓库我们用前面建立的hexo-test和blog两个工程做演示。其中本地hexo为blog目录,hexo-test为git远端仓库,我们需要将本地blog目录里的文件提交到远端的hexo-test仓库。 首先,我们之前提交的index.html文件,我们不再需要了,先删除它。 cd hexo-testrm -rf index.htmlgit add .git commit -m 'remove index.html'git push origin masterblog目录git初始化 ...

October 5, 2019 · 1 min · jiezi

使用hexo和github搭建静态博客网站一

前言使用hexo+github可以免费、快速地搭建一个静态博客网站,并且使用hexo提供的命令以及git自身的功能可以很便捷地管理博客。 使用github部署静态页面在了解hexo之前,我们先看看如何使用github部署静态页面。 注册github账号访问github官网注册一个账号,该流程和一般网站注册账号一样,在此不赘述。 创建一个git仓库 其他项如果需要可以自主填写,这里只填写仓库名,点击Create repository创建仓库。 提交一个测试页面执行git clone命令将仓库克隆到本地 git clone git@github.com:mfaying/hexo-test.git 向hexo-test仓库提交一个测试的html页面,命名为index.html,内容如下: <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>hexo test</title></head><body> hexo test</body></html>提交并推到远端 git add .git commit -m 'add index.html'git push origin master提交成功以后,hexo-test仓库的内容变成了 setting设置点击setting设置 找到GitHub Pages,点击Choose a theme 直接点击Select theme 此时你会发现GitHub Pages发生了变化,Your site is ready to be published at https://mfaying.github.io/hexo-test/说明你的静态网站已经建成了。 直接点击https://mfaying.github.io/hex... 访问便会出现如下内容,静态服务器默认会访问index.html文件。 至此,我们成功地使用github搭建了一个静态网站。当然啦,这个网站几乎没有什么内容,所以接下来我们将使用hexo搭建一个功能完备的博客网站,但是部署方法就是这里介绍的github的静态服务器功能。

October 4, 2019 · 1 min · jiezi

gitbook-入门教程之小白都能看懂的-Gitbook-插件开发全流程

什么是插件Gitbook 插件是扩展 GitBook 功能(电子书和网站)的最佳方式. 只要是 Gitbook 默认没有提供的功能,基于插件机制都可以自行扩展,是插件让 Gitbook 变得更加强大. 本文将全面介绍插件的相关知识并重点介绍插件开发的全流程,只有熟悉插件开发流程才能做到有的放矢,心中有数,进而开发出自己的插件. 关于插件请参考 Gitbook 入门教程高级进阶系列文章,本文重点讲解开发 Gitbook 的基本流程. gitbook 入门教程之插件介绍gitbook 入门教程之实用插件gitbook 入门教程之主题插件如何发现插件您可以在Gitbook官网轻松搜索插件,也可以在npmjs 官网搜索 gitbook-plugin-<name> 插件. 目前 Gitbook 官方已不再为维护插件网站,只能通过 npmjs 发现 Gitbook 插件.如何安装插件一旦你找到你想要安装的插件,你需要将它添加到你的 book.json 配置文件,如果没有该文件则自行创建. { "plugins": ["myPlugin", "anotherPlugin"]}您还可以使用以下命令指定特定版本: myPlugin@0.3.1 .默认不填写版本的情况下,GitBook 使用最新版本(兼容版本)的插件. 安装插件如果是官网在线环境,网站会自动帮你安装插件.如果是在本地环境,直接运行 gitbook install 来安装插件.$ gitbook install或者使用 npm 提前下载插件再安装到本地项目: $ npm install gitbook-plugin-<name>$ gitbook install配置插件插件的配置在 book.json 配置文件中的 pluginsConfig 属性中(如果没有该属性请自行创建),安装插件时,最好浏览插件的文档了解相关选项的详细信息. { "plugins": ["github"], "pluginsConfig": { "github": { "url": "https://github.com/snowdreams1006/snowdreams1006.github.io" } }}有些插件并未提供插件配置项,可以省略该步骤,有的插件会提供配置项,以插件介绍文档为准.如何开发插件GitBook 插件是在 npm 上发布的遵循传统定义的 node 包,除了标准的 node 规范外还有一些 Gitbook 自身定义的相关规范. ...

October 4, 2019 · 1 min · jiezi

借助webpackdevmiddleware和webpackhotmiddleware实现热替换功能的服务器

修改webpack的配置:entry: ['webpack-hot-middleware/client?noInfo=true&reload=true', './src/main.js'],plugins: [new webpack.HotModuleReplacementPlugin()]服务器代码的实现:const express = require('express')const webpack = require('webpack')const config = require('./webpack.config.js')const webpackDevMiddleware = require('webpack-dev-middleware')const webpackHotMiddleware = require('webpack-hot-middleware') const app = new express()const compiler = webpack(config) app.use(webpackDevMiddleware(compiler, { publicPath: config.output.publicPath}))app.use(webpackHotMiddleware(compiler, { log: (info) => console.log(info),heartbeat: 1000})) app.listen(8080, () => { console.log('server started!')})

October 2, 2019 · 1 min · jiezi

axios源码系列四-Axios和dispatchRequest与拦截器

前言这应该是一个大多数都常用的请求库,因为它可以支持多种配置,跨平台实现,返回promise进行链式调用.完全过一遍源码可以提升自己对请求库的理解知识 axios源码系列(一) --- 目录结构和工具函数axios源码系列(二) --- 适配器内部axios源码系列(三) --- 默认配置和取消请求axios源码系列(四) --- Axios和dispatchRequest与拦截器 axios创建这就是Axios库暴露出来的使用方法 axios/lib/axios.jsvar utils = require('./utils');var bind = require('./helpers/bind');var Axios = require('./core/Axios');var mergeConfig = require('./core/mergeConfig');var defaults = require('./defaults');/** * Create an instance of Axios * * @param {Object} defaultConfig The default config for the instance * @return {Axios} A new instance of Axios */function createInstance(defaultConfig) { var context = new Axios(defaultConfig); var instance = bind(Axios.prototype.request, context); // Copy axios.prototype to instance utils.extend(instance, Axios.prototype, context); // Copy context to instance utils.extend(instance, context); return instance;}// Create the default instance to be exportedvar axios = createInstance(defaults);axios实例实际上就是由Axios构造函数创建出来,再将原型上的request的this指向axios实例,然后调用utils.extend扩展函数将axios.prototype和context附加在axios实例上, ...

October 1, 2019 · 6 min · jiezi

axios源码系列三-默认配置和取消请求

前言这应该是一个大多数都常用的请求库,因为它可以支持多种配置,跨平台实现,返回promise进行链式调用.完全过一遍源码可以提升自己对请求库的理解知识 axios源码系列(一) --- 目录结构和工具函数axios源码系列(二) --- 适配器内部axios源码系列(三) --- 默认配置和取消请求axios源码系列(四) --- Axios和dispatchRequest与拦截器 默认配置axios/lib/defaults.js这个Axios库的默认参数配置 var utils = require('./utils');var normalizeHeaderName = require('./helpers/normalizeHeaderName');var DEFAULT_CONTENT_TYPE = { 'Content-Type': 'application/x-www-form-urlencoded'};function setContentTypeIfUnset(headers, value) { if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) { headers['Content-Type'] = value; }}function getDefaultAdapter() { var adapter; if (typeof XMLHttpRequest !== 'undefined') { // For browsers use XHR adapter adapter = require('./adapters/xhr'); } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') { // For node use HTTP adapter adapter = require('./adapters/http'); } return adapter;}这里只有四件事: ...

October 1, 2019 · 3 min · jiezi

axios源码系列二-适配器内部

默认适配器axios/lib/adapters/http.js核心是nodejs的http(s).request方法进行请求 var utils = require('./../utils');var settle = require('./../core/settle');var buildFullPath = require('../core/buildFullPath');var buildURL = require('./../helpers/buildURL');var http = require('http');var https = require('https');var httpFollow = require('follow-redirects').http;var httpsFollow = require('follow-redirects').https;var url = require('url');var zlib = require('zlib');var pkg = require('./../../package.json');var createError = require('../core/createError');var enhanceError = require('../core/enhanceError');var isHttps = /https:?/;我们先看看里面引用的一些库作用 库作用httphttp请求库httpshttps请求库follow-redirects替代nodejs的http和https模块,自动跟随重定向。url解析和格式化urlzlib简单,同步压缩或解压node.js buffers.里面还有其他的内置模块 axios/lib/core/settle.jsvar createError = require('./createError');/** * Resolve or reject a Promise based on response status. * * @param {Function} resolve A function that resolves the promise. * @param {Function} reject A function that rejects the promise. * @param {object} response The response. */module.exports = function settle(resolve, reject, response) { var validateStatus = response.config.validateStatus; if (!validateStatus || validateStatus(response.status)) { resolve(response); } else { reject(createError( 'Request failed with status code ' + response.status, response.config, null, response.request, response )); }};在得到响应请求的基础上,决定返回成功或者失败的Promise态,当失败的时候会创建自定义错误 ...

October 1, 2019 · 12 min · jiezi

axios源码系列一-目录结构和工具函数

前言这应该是一个大多数都常用的请求库,因为它可以支持多种配置,跨平台实现,返回promise进行链式调用.完全过一遍源码可以提升自己对请求库的理解知识 什么是 axios?Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中 特性从浏览器中创建 XMLHttpRequests从 node.js 创建 http 请求支持 Promise API拦截请求和响应转换请求数据和响应数据取消请求自动转换 JSON 数据客户端支持防御 XSRF源码目录结构├── /cancel/ # 定义取消功能实现│ ├── Cancel.js # 请求被取消后抛出的对象,构造函数实现│ ├── isCancel.js # 返回是否已经取消的布尔值│ └── CancelToken.js # 用来请求取消操作的对象,构造函数实现├── /core/ # 主要的核心功能实现│ ├── Axios.js # axios的构造函数│ ├── buildFullPath.js # 返回基础地址和请求地址的合并URL│ ├── createError.js # 创建指定错误对象的构造函数│ ├── dispatchRequest.js # 使用配置的适配器发送请求到服务器│ ├── enhanceError.js # 修改指定错误对象的信息│ ├── InterceptorManager.js # 实现拦截器的构造函数│ ├── mergeConfig.js # 合并配置│ ├── transformData.js # 转换数据│ └── settle.js # 根据http响应状态,改变Promise的状态├── /helpers/ # 一些辅助方法│ ├── bind.js # bind实现方法│ ├── buildUR.js # URL末尾追加参数│ ├── combineURLs.js # 合并URL│ ├── cookies.js # cookie操作│ ├── deprecatedMethod.js # 警告已废弃请求方式│ ├── isAbsoluteURL.js # 返回是否绝对路径的布尔值│ ├── isURLSameOrigin.js # 返回是否同源的布尔值│ ├── normalizeHeaderName.js # 标准化对比替换请求头│ ├── parseHeaders.js # 将请求头解析成对象│ └── spread.js # 用于调用函数和展开参数数组的语法糖├── /adapters/ # 定义请求的适配器 xhr、http│ ├── http.js # nodejs默认适配器│ └── xhr.js # 浏览器默认适配器├── axios.js # 默认入口├── defaults.js # 默认配置 └── utils.js # 工具函数工具函数这是axios的工具函数,我个人习惯是先看工具函数,因为这些一般会充斥到整个库,所以得先有个底知道有什么可以用的工具 ...

October 1, 2019 · 7 min · jiezi

node-初步-五-streams-模块

流在 HTTP 模块章节中的静态文件服务器中,我们已经看过了两个可写流的例子,即服务器可以向 response 对象中写入数据和向 request 返回的请求对象中写入数据。可写流是在 node 接口中广泛使用的概念,所有可写流都有一个 write 方法,用来传递字符串或者 buffer 对象; 还有一个 end 方法用于关闭流,如果给定一个参数,end 会在流关闭前输出指定的一段数据。这两个方法都可以接受一个回调函数作为参数。 但是在我们搭建的静态文件服务器中有一个问题,就是文件夹中的文件我们是直接用 readfire 方法读取的,无论文件的大小,对于体积小一点的文件来说还好说,但是如果是文件是一个几个 G 大小的电影的话,占用的内存空间就需要几个 G 的大小,而且在这个文件完全写入网络另一端之前这些内存是无法释放掉的,这对我们的电脑来说是无法承受的 事实上,对于文件来说我们也没有必要非得一次性的将文件传输完成,我们完全可以读一点写一点,一点一点地完成文件的传输。当然也不能是读一点马上就写一点,传输速度可能不同,比如文件读取的快、写入的慢,电脑完全可以读出了一定量的数据在内存里用来写入后去运行其他任务,这就需要有一个缓冲区来提高运行的效率。 流的目的: 可以控制内存占用(控制内存占用不要超过一个水位线)可以将大的文件分解为小的片段,再一点一点的传输,以减轻内存的压力协调不同阶段处理速度之间的差异流的分类: 可读流可写流双工流转换流例如:我们想传输一个电影在以往的方式,可能我们会这么写: const fs = require('fs')var file = 'movie.mp4'fs.readFile(file, (err, data) => { if (err) { console.log(err) } else { fs.writeFile('movie-1', data , () => { console.log('done') }) }})写法十分简单,就是读取然后写入,这个过程是一次性完成的,也就意味着如果电影的大小是1 G 的话,内存的占用也至少为一个 G 如果用流的方式写呢: const fs = require('fs')var file = 'D:/Users/movie.mp4'var rs = fs.createReadStream(file)var ws = fs.createWriteStream('D:/Users/movie-1.mp4', {highWaterMark: 65536})//这里的 highWaterMark 可以指定缓冲内存的大小,默认大小是65536个字节,即 64krs.on('data', data => { if(ws.write(data) === false) { //这里的write函数会返回一个布尔值,false 表示缓冲内存已满,不可继续写入了 rs.pause() //如果内存已满,rs 暂停 }})ws.on('drain', () => { // ws 内存耗尽 rs.resume( ) // rs 回复执行})rs.on('end', () => { ws.end()})如果读取的速度快于写入的速度缓冲内存满了之后,rs 就会在中途暂停读取,等缓冲内存里的数据写入完了之后再继续读取,这就是一个简单的流注意,虽然 write 函数返回 false 时就告诉内存不可写入了,但是如果依旧写入的话,内存还是会接收数据的,而不会扔掉,当然这会导致内存占用过高 ...

September 30, 2019 · 3 min · jiezi

JavaScript-是如何运行的

什么是JavaScript?我们来确认一下JavaScript的定义:JavaScript 是一门解释型的动态语言。 解释型语言是相对于编译型语言存在的,源代码不是直接编译为目标代码,而是转成中间代码,再由解释器对中间代码进行解释运行。 主流编程语言有编译型(如 C++)、解释型(如 JavaScript)、和半解释半编译(如 Java)这几大类型。 代码是怎么运行的?首先我们来了解一下代码是怎么运行的。 我们知道,代码是由CPU执行的,而目前的CPU并不能直接执行诸如if…else之类的语句,它只能执行二进制指令。但是二进制指令对人类实在是太不友好了:我们很难快速准确的判断一个二进制指令1000010010101001代表什么?所以科学家们发明汇编语言。 汇编语言汇编语言实际上就是二进制指令的助记符。 假设10101010代表读取内存操作,内存地址是10101111,寄存器地址是11111010,那么完整的操作101010101010111111111010就代表读取某个内存地址的值并装载到寄存器,而汇编语言并没有改变这种操作方式,它只是二进制指令的映射: LD:10101010 id:10101111R:11111010这样上述指令就可以表达为LD id R ,大大增强了代码的可读性。 但是这样还不够友好,CPU只能执行三地址表达式,和人的思考方式、语言模式相距甚远。所以伟大的科学家们又发明了高级语言。 高级语言“代码是写给人看的,不是写给机器看的,只是顺便计算机可以执行而已。”高级语言之所以称之为“高级”,就是因为它更加符合我们的思维和阅读习惯。if…else这种语句看起来要比1010101010舒服的多了。但是计算机并不能直接执行高级语言,所以还需要把高级语言转化为汇编语言/机器指令才能执行。这个过程就是编译。 JavaScript 需要编译吗?JavaScript毫无疑问是高级语言,所以它肯定是需要编译后才能执行。但为什么我们又称之为解释型语言呢?它和编译型语言、半解释半编译型语言又有什么区别呢?我们先从编译说起。 编译之前我们已经了解编译的概念,下面我们来聊聊平台:同样一份C++代码在Windows上会编译成.obj文件,而在Linux上则生成.o文件,两者不能通用。这是因为一个可执行文件除了代码外还需要操作系统 API、内存、线程、进程等系统资源,而不同的操作系统其实现也不尽相同。比如我们熟悉的I/O多路复用(事件驱动的灵魂),在Windows上的实现方案是IOCP方案,在Linux上是epoll。所以针对不同的平台,编译型语言需要分别编译,甚至需要分别编写,而且生成的可执行文件其格式并不相同。 跨平台Java在此之上更进一步,它通过引入字节码实现了跨平台运行:无论是在什么操作系统上.java文件编译出的都是.class文件(这就是字节码文件,一种中间形态的目标代码)。然后Java对不同的系统提供不同的Java虚拟机用于解释执行字节码文件。解释执行并不生成目标代码,但其最终还是要转为汇编/二进制指令来给计算机执行的。 假如我们自己完全独立的新写一个简单的操作系统,那么它能不能运行Java呢?很显然是不能的,因为并没有这个系统相应的JVM。所以Java的跨平台、任何其他语言的跨平台,都是有局限性的。 Java采用半解释半编译的好处就是大大提升了开发效率,然而相应的则降低了代码的执行效率,毕竟虚拟机是有性能损失的。 解释执行JavaScript则更进一步。它是完全的解释执行,或者叫做即时编译。它不会有中间代码生成,也不会有目标代码生成。这个过程通常由宿主环境(如浏览器、Node.js)包办。 编译过程现在我们确认了,即使是解释执行的语言,也是需要编译的。那么代码是如何编译的呢?我们来简单了解一下。 词法分析词法分析会把语句分解成词法单元,即Token。 function square(n){ return n*n;}这个函数会被词法分析器识别为function ,square,(,n,),{,return,,n ,*,n ,}并且给它们加上标注,代表这是一个变量还是一个操作。 语法分析这个过程会把Token转化成抽象语法树(AST): { type:'function', id:{ type:'id' name:'square' }, params:[ { type:'id', name:'n' } ] ...}优化及代码生成在这一步编译器会做一些优化工作,比如删除多余运算、删除未用赋值、合并部分变量等等操作,最后生成目标代码。 由于即时编译型语言的编译通常发生在运行前几微秒,所以编译器来不及做太多的优化工作。这也是相比编译型语言,早期JavaScript性能孱弱的原因之一。不过就现在而言,益于 V8 引擎(相比早期的JavaScript的引擎转换成字节码或解释执行,Node.js可以用 V8 提供的 JS2C 工具将 JavaScript 转译为 C++代码),JavaScript 和其他语言性能上的差距已经不足为道了。 链接及装载目标代码基本不能独立运行。应用程序一般都会由多个部分(模块)组成 ,比如C++中一个简单的输出就要引入标准库 iostream: #include <iostream>using namespace std;int main(){ cout << "Happy Hacking!\n"; return 0;}编译器需要把多份目标代码(库)链接起来才能生成可执行文件。至此,我们简单的了解了编译过程。但实际上编译比我们所讲的要复杂得多,在此就不在展开了。 ...

September 20, 2019 · 1 min · jiezi

极客时间Nodejs-开发实战带你开发一个完整的-Nodejs-项目专栏返现

返现有课学是课返现平台,支持极客时间、知识星球、小专栏...等平台,希望每个人都能【学好课,有所获】关注有课学公众号,回复 node 获取极客时间专栏《Node.js 开发实战》购买链接,提交购买截图即可获取返现。 课程收获Node.js 开发必备基础知识;使用 Node.js 构建 BFF 层;一个完整项目的开发重构实战;性能优化和工程化建设核心方法。讲师简介杨浩,腾讯高级工程师。之前曾在腾讯视频负责 Web 端的相关工作,设计并完成了腾讯视频整站大部分页面的 Node.js 化。 腾讯视频是从 2015 年开始使用 Node.js 对整站进行改造的,杨浩与同事一起从零开始一步一步把整个 Node.js 的开发运维步骤打通,搭建了一个运行于后台服务和浏览器前端之间的 Node.js 中间层用作 SSR(Server Side Rendering),以提高搜索引擎抓取的效果以及首屏展现的速度。 在 2018 年由 InfoQ 举办的 ArchSummit 全球架构师峰会深圳站上,杨浩也对在腾讯视频进行 Node.js 改造这一经历做了公开分享: 专栏目录

September 19, 2019 · 1 min · jiezi

node笔记json文件的写入

做的是一个小demo大致内容如下:1.Vue框架写的前端2.axios调用接口3.通过接口对json进行写入4.能够在局域网内所有电脑进行访问 首先需要install一些东西前端是 npm install axios后端安装express框架 npm install express --savenpm install body-parser --savenpm install cookie-parser --savenpm install multer --save前端页面: write() { this.axios .get(`http:// 自己的ip地址 :8081/?name=${this.name}&score=${this.score}`) .then(res => { console.log("res --->", res); }) .catch(err => { console.log("err --->", err); }); }内容比较简单,就写一个write方法通过axios调用接口并传参填自己的IP地址是因为要在局域网内访问,相当于本机是一个服务器,局域网内其他人填写的name和score都会到访问到本机的8081端口并写入本机的JSON文件 Api.js var fun = require('./fun')var express = require('express');var app = express();var bodyParser = require('body-parser');app.all("*", function (req, res, next) { //设置允许跨域的域名,*代表允许任意域名跨域 res.header("Access-Control-Allow-Origin", "*"); //允许的header类型 res.header("Access-Control-Allow-Headers", "content-type"); //跨域允许的请求方式 res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS"); if (req.method.toLowerCase() == 'options') res.send(200); //让options尝试请求快速结束 else next();})// 创建 application/x-www-form-urlencoded 编码解析var urlencodedParser = bodyParser.urlencoded({ extended: false })app.use('/public', express.static('public'));app.get('/', function (req, res) { fun.writeJson(req.query)})var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("<---------- 服务已启动 ---------->") console.log("<--------- 服务端口:8081 --------->")})fun是另一个提供JSON写入的函数,这个Api主要功能是设置跨域并调用接口,可以看到app.get调用了fun.writeJson方法 ...

September 19, 2019 · 1 min · jiezi

日常总结

node关于node的相关总结node彩色字体表 node输出彩色字体 [\x1b32m xxx [\x1b0m 命名 vue关于vue相关总结vue-loader vue-router vue3.0+electron Vue源码解析 vue表單插件 vue-echart插件 electron桌面端应用制作 可以制作mac window linux包electron-vue php相关Laravel fastadmin 移动端相关移动开发 移动版jquery 移动端布局 vh vw 移动端调试工具插件 超级好用 cssless详解 贝塞尔曲线css动画效果提升 贝塞尔曲线css动画效果提升 uiui库和好看的ui网站以及ui相关支付宝vueui库 好看的ui网站 好看的ui网站 好看的ui网站 ui原创馆 个人站 吸色板 配色表 中国色 抠图网站 h+ 线型图 h- 线型图 线型图 线型图 仪表盘 highcharts echart将3d饼图删掉了,为了补全这块空缺,找到了highcharts 工具实用工具总结编写测试用例 Axure原型设计 coniuse查看兼容性 标签嵌套检测 showdoc 接口文档编辑 在线工具合集 21个提升用户体验的插件 好用的神器 ip代理池 vpn 搭建 在线压缩图片 全国省市区统计 博客搭建 网页编辑器 google调试详解 正则在线编辑 好工具网 ...

September 19, 2019 · 1 min · jiezi

windows下将ca证书导入受信任证书储存区

win+r 运行mmc;文件>添加删除管理单元;在可用的管理单元中选择”证书“,点击添加》确定;在控制节点中展开证书》受信任的证书颁发机构》证书,右击所有任务》导入.以下配图仅供参考:

September 19, 2019 · 1 min · jiezi

Nodejs-中的事件循环计时器和processnextTick

前言本篇文章翻译自 Node.js 官网的同名文章也算是经典老物了, 不过官网的文章也随着 Node.js 的演化在修改, 这篇文章最后的编辑时间是 2019年9月10日请注意时效性, 地址在文章的最后有给出. 首次翻译英语水平有限, 错误之处还请多多指教. 什么是事件循环事件循环允许node.js执行非阻塞I/O操作. 虽然 JavaScript 是单线程的, 但是事件循环会尽可能的将操作转移到系统内核中来完成. 现代的操作系统内核都是多线程的, 它们可以在后台处理多种操作. 一旦这些操作完成, 系统内核会通知 Node.js 以便将事件回调放入轮询队列中等待执行. (我们会在随后的内容讨论它们的具体工作细节) 解析事件循环当 Node.js 启动的时候, 他会初始化事件循环, 处理输入的脚本内容 (或者进入 REPL), 脚本可能会调用异步接口, 设置定时器, 或者调用 process.nextTick(), 然后开始处理事件循环(eventloop). 下面的简图中展示了事件循环的操作流程: ┌───────────────────────┐┌─>│ timers ││ └──────────┬────────────┘│ ┌──────────┴────────────┐│ │ I/O callbacks ││ └──────────┬────────────┘│ ┌──────────┴────────────┐│ │ idle, prepare ││ └──────────┬────────────┘ ┌───────────────┐│ ┌──────────┴────────────┐ │ incoming: ││ │ poll │<─────┤ connections, ││ └──────────┬────────────┘ │ data, etc. ││ ┌──────────┴────────────┐ └───────────────┘│ │ check ││ └──────────┬────────────┘│ ┌──────────┴────────────┐└──┤ close callbacks │ └───────────────────────┘每一个方框代表了事件循环中不同的阶段(所有阶段执行完成算是一次事件循环).每一个阶段都有一个由回调组成的 FIFO 队列被用于执行. 虽然不同的队列执行方式不同, 总的来看, 当事件循环进入该阶段后会执行该阶段对应的操作, 然后调用对应的回调直到队列耗尽或者达到了回调执行上限. 在到达上述情况后事件循环进入下一阶段, 然后继续这样的流程. ...

September 11, 2019 · 4 min · jiezi

一看就懂之webpack原理解析与实现一个简单的webpack

前情回顾一看就懂之webpack基础配置一看就懂之webpack高级配置与优化 一、简介本文主要讲述的是webpack的工作原理,及其打包流程,一步步分析其打包过程,然后模拟实现一个简单的webpack,主要是为了更深刻地了解其打包流程,为了充分体现其山寨的意义,故名称定为web-pack。二、webpack的一些特点webpack的配置文件是一个.js文件,其采用的是node语法,主要是导出一个配置对象,并且其采用commonjs2规范进行导出,即以module.exports={}的方式导出配置对象,之所以采用这种方式是为了方便解析配置文件对象,webpack会找到配置文件然后以require的方式即可读取到配置文件对象。webpack中所有的资源都可以通过require的方式引入,比如require一张图片,require一个css文件、一个scss文件等。webpack中的loader是一个函数,主要为了实现源码的转换,所以loader函数会以源码作为参数,比如,将ES6转换为ES5,将less转换为css,然后再将css转换为js,以便能嵌入到html文件中,plugin是一个类,类中有一个apply()方法,主要用于Plugin的安装,可以在其中监听一些来自编译器发出的事件,在合适的时机做一些事情。三、webpack打包原理解析webpack通过自定义了一个可以在node和浏览器环境都能执行__webpack_require__函数来模拟Node.js中的require语句,将源码中的所有require语句替换为__webpack_require__,同时从入口文件开始遍历查找入口文件依赖,并且将入口文件及其依赖文件的路径和对应源码映射到一个modules对象上,当__webpack_require__执行的时候,首先传入的是入口文件的id,就会从这个modules对象上去取源码并执行,由于源码中的require语句都被替换为了__webpack_require__函数,所以每当遇到__webpack_require__函数的时候都会从modules对象上获取到对应的源码并执行,从而实现模块的打包并且保证源码执行顺序不变。四、webpack打包流程分析webpack启动文件: webpack首先会找到项目中的webpack.config.js配置文件,并以require(configPath)的方式,获取到整个config配置对象,接着创建webpack的编译器对象,并且将获取到的config对象作为参数传入编译器对象中,即在创建Compiler对象的时候将config对象作为参数传入Compiler类的构造函数中,编译器创建完成后调用其run()方法执行编译。编译器构造函数: 编译器构造函数要做的事:创建编译器的时候,会将config对象传入编译器的构造函数内,所以要将config对象进行保存,然后还需要保存两个特别重要的数据:一个是入口文件的id,即入口文件相对于根目录的相对路径,因为webpack打包输出的文件内是一个匿名自执行函数,其执行的时候,首先是从入口文件开始的,会调用__webpack_require__(entryId)这个函数,所以需要告诉webpack入口文件的路径。另一个是modules对象,对象的属性为入口文件及其所有依赖文件相对于根目录的相对路径,因为一个模块被__webpack_require__(某个模块的相对路径)的时候,webpack会根据这个相对路径从modules对象中获取对应的源码并执行,对象的属性值为一个函数,函数内容为当前模块的eval(源码)。总之,modules对象保存的就是入口文件及其依赖模块的路径和源码对应关系,webpack打包输出文件bundle.js执行的时候就会执行匿名自执行函数中的__webpack_require__(entryId),从modules对象中找到入口文件对应的源码执行,执行入口文件的时候,发现其依赖,又继续执行__webpack_require__(dependId),再从modules对象中获取dependId的源码执行,直到全部依赖都执行完成。 编译器构造函数中还有一个非常重要的事情要处理,那就是安装插件,即遍历配置文件中配置的plugins插件数组,然后调用插件对象的apply()方法,apply()方法会被传入compiler编译器对象,可以通过传入的compiler编译器对象进行监听编译器发射出来的事件,插件就可以选择在特定的时机完成一些事情。编译器run: 编译器的run()方法内主要就是: buildModule和emitFile。而buildModule要做的就是传入入口文件的绝对路径,然后根据入口文件路径获取到入口文件的源码内容,然后对源码进行解析。其中获取源码过程分为两步: 首先直接读出文件中的源码内容,然后根据配置的loader进行匹配,匹配成功后交给对应的loader函数进行处理,loader处理完成后再返回最终处理过的源码。源码的解析,主要是: 将由loader处理过的源码内容转换为AST抽象语法树,然后遍历AST抽象语法树,找到源码中的require语句,并替换成webpack自己的require方法,即webpack_require,同时将require()的路径替换为相对于根目录的相对路径,替换完成后重新生成替换后的源码内容,在遍历过程中找到该模块所有依赖,解析完成后返回替换后的源码和查找到的所以依赖,如果存在依赖则遍历依赖,让其依赖模块也执行一遍buildModule(),直到入口文件所有依赖都buildModule完成。入口文件及其依赖模块都build完成后,就可以emitFile了,首先读取输出模板文件,然后传入entryId和modules对象作为数据进行渲染,主要就是遍历modules对象生成webpack匿名自执行函数的参数对象,同时填入webpack匿名自执行函数执行后要执行的__webpack_require__(entryId)入口文件id。五、实现一个简单的webpack① 让web-pack命令可执行 为了让web-pack命令可执行,我们需要在其package.json中配置bin,属性名为命令名称即web-pack,属性值为web-pack启动文件,即"./bin/index.js",这样web-pack安装之后或者执行npm link命令之后,就会在/usr/local/bin目录下生产对应的命令,使得web-pack命令可以在全局使用,如:// package.json { "bin": { "web-pack": "./bin/index.js" },}② 让web-pack启动文件可以在命令行直接执行 虽然web-pack命令可以执行了,但是该命令链接的文件是"./bin/index.js",即输入web-pack命令执行的是"./bin/index.js"这个js文件,而js文件是无法直接在终端环境下执行的,所以需要告诉终端该文件的执行环境为node,所以需要在"./bin/index.js"文件开头添加上#! /usr/bin/env node,即用node环境执行"./bin/index.js"文件中的内容,如:// ./bin/index.js #! /usr/bin/env node③ 获取配置文件,创建编译器并执行 // ./bin/index.js #! /usr/bin/env nodeconst path = require("path");const config = require(path.resolve("webpack.config.js")); // 获取到项目根目录下的webpack.config.js的配置文件const Compiler = require("../lib/Compiler.js");// 引入Compiler编译器类const compiler = new Compiler(config); // 传入config配置对象并创建编译器对象compiler.run(); // 编译器对象调用run()方法执行④ 编译器构造函数 之前说过,编译器的构造函数主要就是保存config对象、保存入口模块id、保存所有模块依赖(路径和源码映射)、插件安装。// ../lib/Compiler.js class Compiler { constructor(config) { this.config = config; // ① 保存配置文件对象 this.entryId; // ② 保存入口模块id this.modules = {} // ③ 保存所有模块依赖(路径和源码映射) this.entry = config.entry; // 入口路径,即配置文件配置的入口文件的路径 this.root = process.cwd(); // 运行web-pack的工作路径,即要打包项目的根目录 // ④遍历配置的插件并安装 const plugins = this.config.plugins; // 获取使用的plugins if(Array.isArray(plugins)) { plugins.forEach((plugin) => { plugin.apply(this); // 调用plugin的apply()方法安装插件 }); } }}⑤ 编译器run()方法 ...

September 11, 2019 · 5 min · jiezi

使用createreactapp脚手架-npm-run-eject提示确认后输入yes就报错了

一把我们创建的react项目是默认隐藏配置文件的。如果我们想要配置就需要自己手动开启配置。 运行一下命令开启 npm run eject注意:输入y也可能报错, 原因如下:这是个git问题,你的版本库有未提交的文件,因为reject后会多出来一些文件。为了不影响。你应该把这些文件加到ignore里或者删掉。还有错误信息已经非常明显的告诉你具体要怎么做了 处理错误:git init (在项目中生成git仓库,如果已经生成了可忽略这一步,不要带括号的字)git add .git commit -m 'save'npm run eject

September 10, 2019 · 1 min · jiezi

nodeexpressnginx架构关于上传文件出现-Request-Entity-Too-Large-的问题

解决方法分两步走: 1、修改express框架设置请求的允许最大值将原框架中这两行代码: app.use(bodyParser.json());app.use(bodyParser.urlencoded({ extended: false }));修改为: app.use(bodyParser.json({limit: '50mb'})); // 这里limit值可以根据实际情况自由设定app.use(bodyParser.urlencoded({ limit: '50mb', extended: true}));重启服务继续上传大文件,如果问题解决,到此OK。如果仍然没有解决就有可能是代理nginx配置文件设置的问题,进入第二步。 2、修改nginx的配置文件nginx.conf在 http{} 中添加或者修改client_max_body_size设置 http { client_max_body_size 100m; //添加或修改本行配置,最大允许值可根据需求自由设定 include mime.types; default_type application/octet-stream; ...}修改后上传覆盖掉原来的配置 路径/usr/local/nginx/conf/nginx.conf 一般路径是这样,特殊情况自己去查。重启nginx nginx -s reload或者/usr/local/nginx/sbin/nginx -s reload问题解决。

September 10, 2019 · 1 min · jiezi

koa2sequelizemysqlpm2支持node-webpack打包线上部署日志查询

node+koa2+sequelize+mysql+pm2 (欢迎star) 简介koa2 作为主要node service 入口webpack 打包node 环境pm2 服务负载均衡mysql 数据库mysql 强大的事务 sequelizekoa-body,文件上传中间件koa-cors koa 跨域中间件log4 日志输出......项目独立提供服务接口,可作为前后端分类提供良好的解决方案 依赖node -v 8.4.0npm -v 5.3.0npm2 -v 3.5.1目录.├─auto //sequelize-auto 自动生成 models实体类└─src | main.js //入口文件 | router.js // controller 入口 | ├─config //配置文件 ├─controller //api层 ├─models // 实体类 └─utils //工具类 部署 git https://github.com/shanyanwt/koa_vue_blog.git npm install 开发环境 npm run dev localhost:8081 生产环境 npm run build //生成app.js npm run pm2 localhost:8081supervisor nodejs 热加载 开发环境使用supervisor -w src ,添加需要监听的文件,默认是全部但是有时不起作用,加上监听的文件即可 ...

September 10, 2019 · 2 min · jiezi

空手套白狼我的互联网草根创业亲身经历

和大多数人一样,我出生寒门学子,没钱没势,所有的一切都是从零开始,只能空手套白狼,本文章主要是为了分享下我个人的创业经历以及一个产品从无到有的过程,后面也会聊到我做H5工具的相关技术方案,希望能帮助读者在未来的工作或者是创业路上有所帮助。2010年第一次创业务实的人都不甘于平庸,我亦如此,也许越是有挑战的东西,我越是感兴趣,创业的想法就像一颗毒瘤一直在我脑中去不掉,大学第一天上学,我把所有的生活费在阿里巴巴上进了一堆跳舞毯,开始了人生的第一次创业,我记得那是2010年上大一的事情了,虽然没有赚回本钱,但是我意外的认识了生产跳舞毯的老板。在学校里卖不完跳舞毯,于是我开了个淘宝店铺在网上卖,虽然网上也没卖完,却阴差阳错的成为了跳舞毯公司的网店兼职设计师(精通美图秀秀),再后面就自学了PS,成为了一个UI设计师,我大学学的是java,因为学习了PS,也就顺理成章的成了前端工程师。也为接下来的创业奠定了技术的基础。 2012年第一个工作室大学2年期间,我的前端技术逐渐成熟了,可以做一些官网站了,于是我打算在乐山本地找一家网络公司做兼职,很快我成功的拿到了他们的网站订单,创意的设计风格加上物美价廉,很快成为了这家公司稳定的合作方,于是我在学校里面创立了自己的工作室(馒头工作室)。我的头像也是M这个LOGO也一直沿用至今。 2014年人生第一家公司14年,我们都被大学老师赶出去找工作,刚刚出来的我找了一家公司,但是很快就按耐不住想要创业的想法,于是聚集了几个同学,我们一起创办了一家公司,受到了“云来场景应用”的启发,当时我们就在想,要是能批量生产H5就好了,我们提出了“即见即所得”的概念,很快在14年发布了第一款产品"酷APP",我这里还保留了几张珍贵的截图: 但是当时缺乏商业经验以及面临公司生存的问题,没有拿到融资的我们很快就坚持不下去了,然后项目以失败告终!团队也各奔东西。 小知识:什么是H5?微信扫描以下二维码即可查看H5 H5DS v1.x 版本虽然第一款自主研发的产品失败了,但是我依然觉得这个产品是个好东西,于是贼心不死的我在15年团队解散后发布了另外一个编辑器产品(H5DS)也就是现在的1.x版本。界面是参考PhotoShop来做的,是不是感觉很像PS? H5DS v2.x 版本有了1.x版本,那就有2.x是吧?期间我去了一家创业公司,在那家公司我用下班的业余时间,在H5DS的基础上又重构了一个版本,2.x的编辑器。界面清爽了很多,而且功能也更多了。 H5DS v3.x 版本16年的时候,大多数创业公司都输给了资本,我所在的创业公司也无法避免,随着上家创业公司的倒闭,我没有马上找工作,回到家里,我继续捣鼓我的编辑器,于是很快发布了3.x版本。界面从白变黑,整个代码也重写了。可以说3.x版本完全是重构的。 H5DS v4.x 版本到现在3年了,因为兼职的原因,所以进度很慢,后来在一次技术分享会上面,我认识了我的技术合伙人,基于3.x版本,我们一起发布了4.0版本,3.0版本是jquery的版本,那么4.x就是react的版本。 H5DS v5.x 版本4.x版本的生命周期是很短的,在多次讨论后,我们在4.x之上,我们很快就发布了5.x版本。5.x版本在界面上没有做任何改动,但是代码完全重构了,5.x也就是现在的版本。从此选择了拥抱react生态,海量react组件库在H5DS中都可以使用。 产品版本总结任何产品都非一来就完美的,如果是那样,我也不用重构那么多版本了,慢慢改进打磨迭代升级,产品就会越来越完善,越来越强大。可以看到产品的进步,也可以看到我们想法的转变,任何一次升级都是为了更好的迎合市场的需求,如果希望自己的产品能走的更远,那就不要停下来!创业也是一样,并非一开始就什么都会,什么都有,也是慢慢积累起来的经验和知识,然后学以致用。感觉有点像玩游戏打怪升级,一开始在新手村混,然后到普通玩家区,再到高玩(高端玩家)区,一开始就到高玩区,肯定死翘翘了! H5DS产品概述H5DS(HTML5 design software)h5ds.com 是一款真正意义上的HTML5页面制作工具,非常难得是这个项目我做了5年,版本也换了好几个,依旧初心不改!未来还有很多个五年,我也希望自己的工具会越做越好,下面就来聊聊这个花了我无数心血做的产品。 准确的产品定位类似的竞品也比较多。比如易企秀,maka,兔展,人人秀,wps秀堂,凡科H5等。我们的产品究竟有何不同? 普通用户而言:H5是手机上用于营销的滑动页面。 专业技术人员:H5是HTML5的简称,是一门技术方案,滑动页面只是H5的一小块应用领域。 很明显,其他竞品的定位是做营销滑动页面,而我们的定位是HTML5的编辑器。可以做滑动页面,网站,3D虚拟现实,数据报表,PC网站,小程序,在线PS,在线动画制作等应用。 从一个技术到产品再到销售任何成熟稳定的产品都不是一朝一夕就能完成的,从1.x到5.x,我从一个技术慢慢也有了产品的思维,这个项目让我学到了太多的东西,我觉得是任何公司都给不了一个技术的东西,做了这个项目,让我有了和可客户坐在咖啡厅唇枪舌战的经历,同时我也有了产品经理的经验,我规划的功能也曾遭受用户的质疑,甚至有用户专门发了一个word文档给我,里面罗列了40多个编辑器改进意见,做一个产品是一件非常有意义的事情,我也兼职了UI的工作,玩起了PS,如何设计交互让用户感觉更友好,让我掉了不少头发!这个项目让我体验了设计师,技术,产品,销售,至少哪天不写代码了,还可以去跑下业务! 创业九十九死一生一路走来,我感觉自己创业以来什么都缺,缺人才,缺资金,缺资源,的确,创业是非常艰难的,如果说九死一生是其他行业的创业,那IT行业真的是99死一生。14年在风口的创业公司有多少能活到现在?14年我们创业的时候也是冲着融资去的,但是现在不是了,我觉得首先要活下去,然后再想如何发展壮大,如何赚钱。我所知道的很多IT公司都需要靠融资才可以活下去,一旦资金链断裂了,公司立马倒闭,H5DS在发布4.x版本的时候就已经能做到自负盈亏了,所以目前我们不需要融资,不用考虑生存的问题,虽然节奏慢了点,但是不至于倒闭做不下去了,先解决温饱,才可以创造价值。我希望我们的产品能走的很远,只要活着,就有希望!虽然看上去比较悲观,但是创业真的非常残酷!我非常庆幸自己没有放弃,一直坚持做自己的产品,算下来,今年是5年了,正好是v5版本。产品也比较接近我的预期。 未来会走的更远非常庆幸,我们在第一个5年活下来了,未来我们会站在巨人的肩膀上,走的更远!至于未来五年的规划,虽然我已经做好了,但是市场永远是变化不可测的,我们也会不断的更新产品,推出新鲜的功能,只希望能在这条道路上走的更远! 技术干货分享聊了太多和技术不挂边的东西,差点忘记自己是一个技术了,接下来会聊一些技术相关的东西,也算是分享一些干货给给位技术同僚!就编辑器技术部分,这里给大家做个分享。 技术选型技术选型我们采用react + mobx + koa2 + mysql 整体都是JS技术栈,至于为什么要这样选型,因为我们人少,技术保持统一,方便维护!另外一方面原因是因为我们是一个创业团队,技术资源非常宝贵,必须尽可能的节约开发成本,也是为什么我们会选择纯JS的技术栈,这样我们每个人都可以做前端也可以做后端。 mobx 最最核心的概念只有2个。 @observable 和 @observer ,它们分别对应的是被观察者和观察者。这是大家常见的观察者模式,这里使用了ES7 中的 装饰器。参数发生变化时自动触发render更新视图,这个和vue是一样的原理。之所以没有使用vue是因为我们也需要react的state配合起来做性能优化以及灵活的数据处理。我们可以结合防抖函数去做性能优化,控制或者选择性的去更新视图。下面举个例子: import React, { Component } from 'react';import { inject, observer } from 'mobx-react';import debounce from 'lodash/debounce';@inject('h5ds')@observerclass Demo extends Component { constructor(props) { super(props); this.state = { count: props.h5ds.count // 默认是1 } } // 防抖函数控制性能 updateOtherRender = debounce(() => { const { count } = this.state; // 如果大于10才会去更新其他地方的视图 if(count > 10) { this.props.h5ds.count = this.state.count } }, 500) changeValue = e => { this.setState({count: e.target.value}, this.updateOtherRender) } render() { return <input type="number" value={this.state.count} onChange={this.changeValue}/> }}我们在很多地方都有用到上面这种写法,react提倡的最小模块化,我们也希望模块之间的影响会最小,如果一个参数在多个模块中被使用,在快速输入的时候务必会变的很慢。 ...

September 10, 2019 · 2 min · jiezi

借助云开发轻松实现后台数据批量导出丨实战

小程序导出数据到excel表,借助云开发后台实现excel数据的保存我们在开发小程序的过程中,可能会有这样的需求:如何将云数据库里的数据批量导出到excel表里?这个需求可以用强大的云开发轻松实现!这里需要用到云函数,云存储和云数据库。可以说通过这一个例子,把小程序云开发相关的知识都用到了。下面就来介绍如何实现实现思路1,创建云函数2,在云函数里读取云数据库里的数据3,安装node-xlsx类库(node类库)4,把云数据库里读取到的数据存到excel里5,把excel存到云存储里并返回对应的云文件地址6,通过云文件地址下载excel文件一、创建excel云函数关于如何创建云开发小程序,这里我就不再做具体讲解。不知道怎么创建云开发小程序的同学,可以去翻看腾讯云云开发公众号内菜单【技术交流-视频教程】中的教学视频。 创建云函数时有两点需要注意的,给大家说下1、一定要把app.js里的环境id换成你自己的 2,你的云函数目录要选择你对应的云开发环境(通常这里默认选中的)不过你这里的云开发环境要和你app.js里的保持一致 二、读取云数据库里的数据我们第一步创建好云函数以后,可以先在云函数里读取我们的云数据库里的数据。 1、先看下我们云数据库里的数据 2、编写云函数,读取云数据库里的数据(一定要记得部署云函数) 3、成功读取到数据 把读取user数据表的完整代码给大家贴出来。 // 云函数入口文件const cloud = require('wx-server-sdk')cloud.init({ env: "test-vsbkm"})// 云函数入口函数exports.main = async(event, context) => { return await cloud.database().collection('users').get();}三、安装生成excel文件的类库 node-xlsx通过上面第二步可以看到我们已经成功的拿到需要保存到excel的源数据,我们接下来要做的就是把数据保存到excel 1、安装node-xlsx类库这一步需要我们事先安装node,因为我们要用到npm命令,通过命令行 npm install node-xlsx 可以看出我们安装完成以后,多了一个package-lock.json的文件 四、编写把数据保存到excel的代码,下图是我们的核心代码:这里的数据是我们查询的users表的数据,然后通过下面代码遍历数组,然后存入excel。这里需要注意我们的id,name,weixin要和users表里的对应。 for (let key in userdata) { let arr = []; arr.push(userdata[key].id); arr.push(userdata[key].name); arr.push(userdata[key].weixin); alldata.push(arr) }还有下面这段代码,是把excel保存到云存储用的 //4,把excel文件保存到云存储里 return await cloud.uploadFile({ cloudPath: dataCVS, fileContent: buffer, //excel二进制文件 })下面把完整的excel里的index.js代码贴给大家,记得把云开发环境id换成你自己的。 const cloud = require('wx-server-sdk')//这里最好也初始化一下你的云开发环境cloud.init({ env: "test-vsbkm"})//操作excel用的类库const xlsx = require('node-xlsx');// 云函数入口函数exports.main = async(event, context) => { try { let {userdata} = event //1,定义excel表格名 let dataCVS = 'test.xlsx' //2,定义存储数据的 let alldata = []; let row = ['id', '姓名', '微信号']; //表属性 alldata.push(row); for (let key in userdata) { let arr = []; arr.push(userdata[key].id); arr.push(userdata[key].name); arr.push(userdata[key].weixin); alldata.push(arr) } //3,把数据保存到excel里 var buffer = await xlsx.build([{ name: "mySheetName", data: alldata }]); //4,把excel文件保存到云存储里 return await cloud.uploadFile({ cloudPath: dataCVS, fileContent: buffer, //excel二进制文件 }) } catch (e) { console.error(e) return e }}五、把excel存到云存储里并返回对应的云文件地址经过上面的步骤,我们已经成功的把数据存到excel里,并把excel文件存到云存储里。可以看下效果。接着,就可以通过上图的下载地址下载excel文件了。其实到这里就差不多实现了基本的把数据保存到excel里的功能了,但是为了避免每次导出数据都需要去云开发后台下载excel的麻烦,接下来介绍如何动态获取下载地址。 ...

September 10, 2019 · 2 min · jiezi

搭建脚手架工具-commander

随着NodeJs的不断发展,对于前端来说要做的东西也就更多,Vue脚手架React脚手架等等等一系列的东西都脱颖而出,进入到人们的视野当中,对于这些脚手架工具来讲也只是停留在应用阶段,从来没有想过脚手架是如何实现的?vue init webpack 项目名称是如何通过这样的命令创建了一个项目,其最重要的模块就是今天要说的Commander。 Commander模块又国外TJ大神所编写 项目地址:Commander Commander基本用法Commander文档写的很详细,跟着文章详细的学习一下,Commander是一个Nodejs模块,需要在Node环境中运行,在使用前确认一下Node环境是否已安装。 安装依赖npm install commander --saveOptions 解析在Commander模块下存在option方法用来定义commander的选项options,用来作为选项的文档。 var program = require('commander');program .option('-g, --git [type]', 'Add [marble]', 'Angie') .parse(process.argv);console.log("process.argv",process.argv)console.log("program.args",program.args)console.log('you ordered a pizza with:');if (program.git) console.log(' - git');console.log(' - %s git', program.git);上面的示例将解析来自process.argv的args和options,然后将剩下的参数(未定义的参数)赋值给commander对象的args属性(program.args),program.args是一个数组。 打印输出一下process.argv和program.args并查看了一下输出结果如下,使用如下命令运行一下文件: node index -g type Aaronprocess.argv ['F:\\node\\installation\\node.exe', 'C:\\Users\\wo_99\\Desktop\\cli-dome\\index', '-g', 'type', 'Aaron' ]program.args [ 'Aaron' ] option方法可以接收三个参数: 自定义标志必须:分为长短标识,中间用逗号、竖线或者空格分割;标志后面可跟必须参数或可选参数,前者用<>包含,后者用[]包含。选项描述省略不报错:在使用 --help 命令时显示标志描述默认值可省略:当没有传入参数时则会使用默认值若我们执行node index -g得到的结果则是Angie git其第三个参数则作为了默认值填写在了对应的位置上。除了上面所说还可以使用如下命令: // 执行 -g 参数 a// 执行 -b 参数 snode index -g a -b s// 执行 -g和-b 传入a参数给-g// -b 参数暂时不知道怎么传入node index -gb a版本选项调用版本会默认将-V和--version选项添加到命令中。当存在这些选项中的任何一个时,该命令将打印版本号并退出。 ...

September 9, 2019 · 4 min · jiezi

NodeJS-应用最佳实践日志

作者:Mahesh Haldar翻译:疯狂的技术宅 原文:https://blog.bitsrc.io/loggin... 未经允许严禁转载 日志记录是每个开发人员从第一天编写代码时就要做的事情,但很少有人知道它可以产生的价值和最佳实践。 在本文中,我们将讨论以下主题: 什么是日志,为什么很重要性?记录日志的最佳做法日志的重要部分正确使用日志级别为什么选择 Winston?什么是日志,为什么很重要?日志是反映程序各个方面的事件,如果能够正确编写,那么它就是最简单的故障排除和诊断程序的模式。 当你启动 Node.js 服务器时,如果数据库由于某些问题而没有运行,或服务器端口已经被占用时,如果没有日志,你将永远不知道服务器失败的原因。 作为开发人员,你经常需要调试一些问题,我们很喜欢用调试器和断点来定位故障的位置和内容。 当你的程序在生产环境中运行时,你会做些什么?你能在那里附加调试器并重现 bug 吗?显然没有。因此,这是日志记录能够帮助你的地方。 在不使用调试器的情况下,你可以通过浏览日志找到问题并了解出现问题的原因和位置。最佳实践1)日志的三个重要部分程序日志既适用于人类,也适用于机器。人类参考日志来调试问题,机器用日志生成各种图表,并通过数据分析来产生关于客户使用的各种结论。 每个日志都应包含三个最重要的部分: 日志源当我们有一个微服务架构时,这对于了解日志的来源、服务名称、区域、主机名等信息非常重要(有关管理微服务中的公共代码的更多信息请在此处阅读) 有关源的详细元数据主要由日志 agent 进行处理,日志 agent 将日志从所有微服务推送到集中式日志系统。 ELK 栈的 Filebeat 是日志 agent 的最佳选择之一。 时间戳事件发生或生成日志的时间非常重要。所以要确保每个日志都有时间戳,以便我们进行排序和筛选。 级别和上下文在通过查看日志查找错误时,如果日志没有提供足够的信息,你就必须回到代码中,那将非常令人沮丧。因此在记录时我们应该传递足够的上下文 例如。没有上下文的日志将如下所示: The operation failed! 有意义的上下文应该是是: Failed to create user, as the user id already exist 2)日志的使用方法日志方法和输入:在调试的同时,如果我们知道调用了哪个函数以及传递了哪些参数,它就能发挥真正的作用。 import logger from '../logSetup';getInstallment(month: number, count: number ): number { logger.debug(`>>>> Entering getInstallment(month = ${month}, count= ${count}"); // process const installment: number = 3; log.debug("<<<< Exiting getIntallment()"); return installment;}通过日志 >>>> 和 <<<< 将给出函数输入和退出的信息。这是受到了 git merge 冲突的启发。 ...

September 9, 2019 · 3 min · jiezi

Protocol-Buffers浅出指南

什么是socket在了解pb协议之前,首先要明确socket是什么,socket是网络套接字,即IP+端口号的形式,更准确的说套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。 总而言之:1、socket是系统底层提供的一个被应用程序调用的通用接口2、socket的形式:IP地址+端口号 3、每一个socket都会有一个应用程序与之对应。 Q&A?1、Protocol Buffers是什么? Protocol Buffers是一种广泛使用结构化数据存储格式,可以用于结构化数据的序列化/反序列化,也是很多rpc框架的基础之一,和json、xml类似。 2、pb协议是哪一层的协议? Protocol Buffers是数据存储格式,基于此我们可以对请求和响应包结构进行再定义,就有了pb协议,直接处理的是字节流,不再需要基于http协议解析和传输。所以pb协议是应用层协议。 3、pb协议需要考虑数据安全问题吗?http有https加密,那pb协议呢? pb协议暂时适用于内网服务器通信,并没有进行数据加密。 4、pb协议没有区分get post,甚至还没有状态码的概念?网络连接出错怎么办?服务端没有响应数据又怎么办呢? pb协议和http协议类似,get、post区分本身不影响请求,而对于其他所描述的情况,都要根据socket接口再封装,做错误处理、超时检测等。 5、相比JSON和XML 有什么优势呢? JSON和XML都基于HTTP协议进行数据传输,同时需要对JSON和XML字符串进行解析,相比pb协议的方式,pb协议基于字节流进行处理解析,pb协议传输的数据量更小,处理速度更快捷。 6、这种数据传输方式仅适用于服务端通信吗?客户端可以使用吗? 并不是,其他客户端也可以使用。在前端web页面中,可以通过websocket传输ArrayBuffer的形式,直接传输字节流到达服务端,之后从服务端拿到传输回来的二进制流,进行解析;或者基于formdata也可以传输二进制文件对象,通过把Arraybuffer放进blob对象中,传输给后台;当然也可以通过charCode的方式把buffer转换成字符串,但是这样会更加耗时。但是要注意的一点是,客户端和服务端都要同步更新proto文件。 protocol buffers 的使用proto文件中的数据定义方式如下: Message 消息名{ 字段规则 字段类型 字段名 = 分配标识号 [default=xxx]; } 1、字段规则:required(必须设置) 、 optional(可以有0或1个) 、repeated(可以有0或多个) required:实例中必须包含的字段 optional:实例中可以选择性包含的字段,若实例没有指定,则为默认值,若没有设置该字段的默认值,其值是该类型的默认值。如string默认值为””,bool默认值为false,整数默认值为0。 repeated: 可以有多个值的字段,这类变量类似于vector,可以存储此类型的多个值。 2、字段类型:可以是标准类型、枚举类型、自定义message类型 3、分配标识名:1、2、3…… 在proto数据结构中,每一个变量都有唯一的数字标识。这些标识符的作用是在二进制格式中识别各个字段的,一旦开始使用就不可再改变。 此处需要注意的是1-15之内的标号在存储的时候只占一个字节,而大于15到162047之间的需要占两个字符,所以我们尽量为频繁使用的字段分配1-15内的标识号 。另外19000-19999之内的标识号已经被预留,不可用。最大标识号为2^29-1。 举个简单的栗子: Package MYPACKAGE;message Person { required string name=1; required int32 id=2; optional string email=3; enum PhoneType { MOBILE=0; HOME=1; WORK=2; } message PhoneNumber { required string number=1; optional PhoneType type=2 [default=HOME]; } repeated PhoneNumber phone=4;}message AddressBook { repeated Person person=1;}可以看到我们定义了一个包,报名为MYPACKGE; ...

September 9, 2019 · 2 min · jiezi

iijs-一个基于nodejskoa2构建的简单轻量级MVC框架

iijsA simple and lightweight MVC framework built on nodejs+koa2 项目介绍一个基于nodejs+koa2构建的简单轻量级MVC框架,最低依赖仅仅koa和koa-router 官网:js.i-i.me 源码:github 码云 QQ:331406669 使用安装 npm i iijs应用结构├── app //应用目录 (非必需,可更改)│ ├── Controller //控制器目录 (非必需,可更改)│ │ └── index.js //控制器│ ├── view //模板目录 (非必需,可更改)│ │ └── index //index控制器模板目录 (非必需,可更改)│ │ └── index.htm //模板│ ├── model //模型目录 (非必需,可更改)│ ├── logic //逻辑目录 (非必需,可更改)│ └── **** //其他目录 (非必需,可更改)├── app2 //应用2目录 (非必需,可更改)├── common //公共应用目录 (非必需,可更改)├── config //配置目录 (非必需,不可更改)│ ├── app.js //APP配置 (非必需,不可更改)│ ├── route.js //路由配置 (非必需,不可更改)│ └── **** //其他配置 (非必需,可更改)├── public //静态访问目录 (非必需,可更改)│ └── static //css image文件目录 (非必需,可更改)├── node_modules //nodejs模块目录├── server.js //应用入口文件 (必需,可更改)└── package.json //npm package.json应用入口// server.jsconst {app} = require('iijs');app.listen(3000, '127.0.0.1', function(err){ if(!err) console.log('http server is ready on 3000');});Hello world !// app/controller/index.jsclass Index { constructor(ctx, next) { this.ctx = ctx; this.next = next; } async hello() { this.ctx.body = `hello iijs, hello world !`; }}module.exports = Index;访问URL:http://localhost/app/index/hello ...

September 8, 2019 · 2 min · jiezi

你好websocket

你好,WebSocket定义WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议(注意是协议)。 WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。 为什么使用WebSocket原本的通过HTTP协议的客户端与服务端的通信,只能由客户端发起。HTTP 协议做不到服务器主动向客户端推送信息。而且一次客户端与服务端的通信完成之后,客户端与服务端的链接就会断开,但是WebSocket是长链接,当一次连接建立以后,如果不是其中一端主动断开连接,两端的连接就会一直连接着。 WebSocket是为了解决类似聊天室的场景,原本HTTP协议这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。 一:组成和特点长连接:一个连接上可以连续发送多个数据包,在连接期间,如果没有数据包发送,需要双方发链路检查包。 TCP/IP:TCP/IP属于传输层,主要解决数据在网络中的传输问题,只管传输数据。但是那样对传输的数据没有一个规范的封装、解析等处理,使得传输的数据就很难识别,所以才有了应用层协议对数据的封装、解析等,如HTTP协议。 HTTP:HTTP是应用层协议,封装解析传输的数据。从HTTP1.1开始其实就默认开启了长连接,也就是请求header中看到的Connection:Keep-alive。但是这个长连接只是说保持了(服务器可以告诉客户端保持时间Keep-Alive:timeout=200;max=20;)这个TCP通道,直接Request - Response,而不需要再创建一个连接通道,做到了一个性能优化。但是HTTP通讯本身还是Request - Response。 socket:与HTTP不一样,socket不是协议,它是在程序层面上对传输层协议(可以主要理解为TCP/IP)的接口封装。我们知道传输层的协议,是解决数据在网络中传输的,那么socket就是传输通道两端的接口。所以对于前端而言,socket也可以简单的理解为对TCP/IP的抽象协议。 WebSocket:WebSocket是包装成了一个应用层协议作为socket,从而能够让客户端和远程服务端通过web建立全双工通信。websocket提供ws和wss两种URL方案。 WebSocket 实例WebSocket 协议本质上是一个基于 TCP 的协议。 为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息"Upgrade: WebSocket"表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。 二:WebSocket使用创建WebSocket对象 var ws = new WebSocket(url, [protocol] ); 以上代码中的第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。 1:属性ws.readyState: 0 - 表示连接尚未建立。 1 - 表示连接已建立,可以进行通信。 ...

September 8, 2019 · 2 min · jiezi

如何写一份好的前端面试简历

项目地址项目地址: https://github.com/Wscats/CV简历的本质在写简历之前,我们必须清楚的了解一件事情,那就是简历是什么?它不是人生履历,不是项目清单,也不是技能大放送。简历的存在只有一个目的 —— 帮你约到面试。只要能达到这个目的,简历可以是一段视频,一个开源项目,一张照片,甚至是一行字,比如: I wrote javascript当然,绝大部分简历的形式,就是我们所熟知的,是一篇文章。即使你通过其他方式获得了面试,当你入职的时候,还是要有这么一份纸质简历的,所以不要想着偷懒。 其实简历不只是表现自己,更是传递以下三个信息,增强通过率。 是什么(你能做什么?,擅长什么?)比别人好在哪些地方(相比其他同行,你的优势?)如果雇佣你,招聘方会得到什么好处 (能不能为企业带来效益?)正如你抉择跳槽,思考要不要留在这个公司的时候,你也可能考虑下面三点 待遇(给的钱够不够?福利好不好)环境(同事nice不?老板好不好?事少离家近?工作开心与否?是否帅哥美女多?嗯哼)个人(有晋升机会不?能否再进步?)当然企业和你的相互选择其实正如上面几点中的考量和博弈,当然面试是平等的,是相互选择的结果,所以有你对企业的考量,也有企业对你的考量 回归到写简历,和很多人在大学写议论文写作文是不同的,过分的论证会显得自夸,反而容易引起反感,所以要点到为止。这里的技巧是,提供论据,把论点留给阅读简历的人自己去得出。放论据要具体,最基本的是要数字化,好的论据要让人印象深刻。 举个例子,下边内容是虚构的: 2015年(时间可以具体到年月,或者经历的项目周期),我共同(一般都是团队开发了,单干就别写了,除非你个人能力非常突出)参与了XX新闻网站后端发布系统的开发(这部分是大家都会写的)。作为团队里面的核心程序员,我不但完成了网站界面、功能逻辑的开发工作,更提出了高效的组件化和模块化开发方案,通过模块化开发让团队成员较少编写代码时候的冲突,通过组件化让团队成员分工合作,各善其职,有效的提升了系统的的开发效率。(这部分是很多人忘掉的,记得写出你在这个项目中具体负责的部分,以及你贡献出来的价值。)在该系统上线后,Web前端性能从10QPS提升到200QPS,服务器由10台减少到3台( 通过量化的数字来增强可信度 )。2016年我升任WAPCMS项目负责人,带领一个3人小组支持着每天超过2亿的PV( 这就是你自身的优势。你能带给旧企业的价值,也就是你能带给新企业的价值。 )。 有同学问,如果我在项目里边没有那么显赫的成绩可以说怎么办?讲不出成绩时,就讲你的成长。因为学习能力也是每家公司都看中的东西。你可以写你在这个项目里边遇到了一个什么样的问题,别人怎么解决的,你怎么解决的,你的方案好在什么地方,最终这个方案的效果如何。 具体、量化、有说服力,是技术简历特别需要注重的地方。 (以上内容在写完简历后,对每一段进行评估,完成后再删除) 联系方式(HR会打印你的简历,用于在面试的时候联系,所以联系方式放到最上边会比较方便) 头像(如果你觉得自己长得可以,你可以放哈)手机:139 (如果是外地手机,可注明。如经常关机,要写上最优联系时间)Email:wscats@gmail.com (虽然我觉得QQ邮箱无所谓,不过有些技术人员比较反感,建议用163,gmail也可以)QQ/微信号:7*(提供一个通过网络可以联系到你的方式)个人信息易佳梦/女/1990本科/XX大学计算机系工作年限/经验:3年技术博客:https://wscats.github.io/blog ( 使用GitHub Page映射页面会显得的逼格较高 )Github:https://github.com/Wscats ( 原创repo+多star的Github帐号会极大的提升你的个人品牌 )期望职位:Web前端程序员/工程师期望薪资:税前月薪15k~20k,特别喜欢的公司可例外期望城市:广州工作经历工作经历按逆序排列,最新的在最前边,因为HR更多的是了解你在上一家公司或者近几年你的工作情况,我建议实际工作小于半年的你也可以不写当然(IT行业跳槽如此平凡~),因为短的在职经历会让HR觉得你不够稳定,存在录用后的风险,还有工作经历最好有层次感,比如你刚开始第一份前端工作你用的技术比较简单,你可以写你自己负责切图,用jQuery写逻辑,用sass写样式,然后再下一份工作你可能技术加强了,你就可以写你用vue,angular来开发了一个单页面应用,还负责写了部分后端接口,这样可以让面试官觉得你有自我学习,不断进步的习惯。 123公司 ( 2016年9月 ~ 2017年9月 )456项目我在此项目负责了哪些工作,分别在哪些地方做得出色/和别人不一样/成长快,这个项目中,我最困难的问题是什么,我采取了什么措施,最后结果如何。这个项目中,我最自豪的技术细节是什么,为什么,实施前和实施后的数据对比如何,同事和领导对此的反应如何。 789项目我在此项目负责了哪些工作,分别在哪些地方做得出色/和别人不一样/成长快,这个项目中,我最困难的问题是什么,我采取了什么措施,最后结果如何。这个项目中,我最自豪的技术细节是什么,为什么,实施前和实施后的数据对比如何,同事和领导对此的反应如何。 其他项目(每个公司写2~3个核心项目就好了,如果你有非常大量的项目,那么按分类进行合并,每一类选一个典型写出来。其他的一笔带过即可。) 根据上面的模板你可以这样来写 广州XX有限公司(2016.09 ~ 2017.03)技术部-前端工程师这个项目目的主要以成人学历为主线开发,有大专、本科、资格证等线上销售的教育培训产品。我在此项目负责了前期界面研究,做出原型设计,原型切图,配合后台开发人员设计界面与开发功能,并负责框架开发,封装公共组件,以及根据需要引进第三方插件。我在项目中遇到的最大困难是实现产品前端界面效果与设计图纸在某些界面上难以绝对统一我采用Vue+jQuery开发方案,在细节上我运用了jwplayer.js、jquery.KinSlideshow来实现幻灯片轮播效果,增强用户体验更好等。自我评价最好突出自己的优势,比如学习能力,解决问题能力,带队能力,沟通能力和技术能力等等想简单点可以这样写: 1.喜欢学习新技术,有良好的文档编写和代码书写规范,耐心打磨用户体验和用户界面2.有良好的沟通能力和团队合作能力,性格随和,认真负责,自学能力强详细的你也可以这样来 1. 对前端技术具有浓厚的兴趣,喜欢逛CSDN,博客园等技术论坛;2. 具备良好的审美能力,有良好的代码编程习惯;(说好的审美,可以和设计师好好玩耍)3. 学习适应能力强,愿意不断学习新知识丰富自己;(学习能力大企业最看重)4. 抗压能力强,有信心在不同的工作强度下进行开发工作;(抗压技能点加上,去常加班的公司有独特优势,额)5. 性格随和,具有良好的团队合作精神。(性格最好能适应团队)开源项目和作品选取你自己Github上的某个开源项目即可,当然开源项目最好写清楚文档,比如安装方法,核心代码的分析(这一段用于放置工作以外的、关键来证明你的能力的材料,也可以理解为其他工程师或者开源社区对你的认可度) 开源项目(对于程序员来讲,没有什么比Show me the code能有说服力了) Piano:????用键盘8个键演奏一首蒲公英的约定送给自己或月亮代表我的心送给她(项目的简要说明,Star和Fork数多的可以注明)Articles:????分享我的学习片段和与你的回忆(项目的简要说明,Star和Fork数多的可以注明)技术文章(挑选你写作或翻译的技术文章,好的文章可以从侧面证实你的表达和沟通能力,也帮助招聘方更了解你) 我如何用前端技术得到XXOO网站的VIP前端程序员经常忽视的一个JavaScript面试题 ( 或者好的翻译文章可以侧证你对英文技术文档的阅读能力)技能清单(我一般主张将技能清单写入到工作经历里边去。不过很难完整,所以有这么一段也不错)以下均为我熟练使用的技能 后端开发:PHP/Node后端框架:ThinkPHP/CodeIgniter前端框架:ReactJS/VueJS/AngularJS/Omi/EmberJS/Cocos2dJS/Ionic前端样式库:WeUI/Bootstrap/AntDesign/ElementUI/AmazeUI前端工具库:jQuery/Zepto/Lodash/Axios前端调试:Postman/Charles/Vue(React) Dev Tools前端插件:Swiper/Iscroll前端模块工具:Webpack/Gulp/RequireJS/Fis/Grunt预编译语言:Typescript/Sass/Less/Pug/JadeNative开发:Weex/React Native/Cordova/Uni App小程序相关:Mpvue/Taro/Kbone/Wepy数据库相关:MySQL/MongoDB/PgSQL/PDO/SQLite版本管理、文档和自动化部署工具:SVN/Git单元测试:PHPUnit/SimpleTest/Qunit/Mocha云和开放平台:SAE/BAE/AWS/微博开放平台/微信应用开发例如你可以这样写,当然你得根据自己的个人情况进行修改 1、熟练使用合理的结构和样式编写兼容主流浏览器的页面;2、能适当运用CSS3使页面在现代浏览器上效果更佳;3、熟练运用rem单位和flex布局实现不同浏览器宽度下的整页缩放;4、熟练使用photoshop软件进行简单的图片处理,能根据图片特征保存成最优的格式;5、熟练使用原生javaScript以及常用javaScript库,如jQuery、zepto;6、能运用模块化、面向对象的方式编程;7、了解http协议,能够根据http请求报文、响应报文和状态码排查问题;8、熟练运用ajax实现异步数据传递,熟悉JSON等数据交换格式;了解php和数据库;9、了解如何使用node.js搭建本地server模拟接口返回数据测试ajax接口;10、能使用Bootstrap、WeUI、Ionic等前端样式库和框架进行开发;11、熟练运用AngularJS,VueJS进行模块开发,了解双向数据绑定原理,熟练编写控制器、服务、组件、过滤器等12、熟练使用Gulp对代码进行压缩合并;熟悉使用Git和SVN进行代码管理和版本控制;当然再举个例子(精通慎用!熟悉可以多用,不够自信就用了解吧,额哼) 1、熟练HTML、DIV+CSS的页面布局,能根据设计图完成页面制作2、熟练响应式布局、弹性盒布局,熟悉基于HTML5的WebApp开发以及移动端适配3、熟练主流浏览器的兼容性以及相应的适配技术,完成页面的完整兼容4、熟练掌握HTML5、CSS3新增功能,了解Canvas动画制作5、熟悉掌握原生JavaScript,熟悉jQuery,了解Zepto6、熟练Bootstrap、AngularJS等前段框架,了解MUI、Vue框架7、熟练使用AJAX进行数据交互,协助后端开发工程师完成项目8、熟练JSON数据的使用以及处理方式9、熟练LocalStorage、SessionStorage、Cookie等本地存储10、熟悉使用Swiper、Font-awesome、jQueryUI以及jQuery插件等前端插件11、熟练使用Cordova插件将WebApp打包成安卓App,实现混合App开发12、熟练使用Visual Studio Code、SublimeText、HBulider、Git、Gulp、Scss、Photoshop 等开发工具这里尽量不要重复叙述相同的技能点,保持好每一条都是独特的,每一条描述出现的英文技能点关键词尽量保持大小统一,比如Bootstrap、WeUI、Ionic,这样写没问题,但是不要个别大写个别小写bootstrap、WeUI、ionic,遇到强迫症的HR就尴尬了,不过有些还是例外的比如jQuery,还是第二个字母大写吧(...额,不解释),还有这里不要出现一些“感性“的描述,比如熟悉使用PHP,能跟后端工程师愉快的合作(什么鬼),这些句子就不要出现了 ...

September 8, 2019 · 1 min · jiezi

Python-和-NodeJS-绘图对比

在绘图方面,Python 比 NodeJS 好用,首先 Python 绘图库多,而且 API 强大而完善。 NodeJS 还是少点,一些库还没测就放弃了,需要 window 支持,而我需要在后端执行。最近一直在搞地面雷达的可视化工作。在研究如何将基数据可视化过程中,着实汲取了相当多的知识和技能。 传统的雷达数据可视化采用图片方式,同一时刻,雷达基数据会产生多种单站产品和组合产品,针对不同数据产品生成不同的图像。这样会产生大量的图像数据和基数据,对数据容量是个考验不说,图像本身也非矢量,渲染效果总不是那么好。 所以最开始想用 GIS 的方式将数据直接渲染,理想中使用 Mapbox 是美丽的,省去了图像数据,使用 Geojson Layer 在缩放过程中,都能清晰得看影像。但最终效果不然,明显的一点是会存在空隙,即使将 Circle 放大尺寸,也达不到理想的效果。 最终除了风速风向数据(数据量小,而且不需要填充),其余的还是采用了传统的图片渲染方式。在接下去的过程中,我开始同时使用 NodeJS 和 Python 来实现。说真的,我越来越喜欢 NodeJS,他无所不能,从前端到后端,无孔不入。我已经开始慢慢从 Python 迁移到 NodeJS。在这个工作上,他的异步方式让读取文件,简单快速,比 Python 好用多了。让我在用 Python 实现的同时,也想用 NodeJS 写一遍,如果可行的话,直接在后期将项目迁移到 NodeJS。 扯远了,回到主题。大部分数据产品的生成方式是一样的,总共会出现两种情况。 第一种是分辨率为 620*490 格点数据,每个格点的值对应一种颜色。这种情况比较简单,只需要创建一个宽高为 620x490 的图像,然后修改对应格点坐标的颜色即可。 Python PIL 处理时间 1s,其中读文件数据 80 msNodeJS pngjs 处理时间从读取到绘图 35ms可谓差距明显。 第二种是扫描一圈的数据,半径等分 920 个圈,每圈再等分 372 个点数据,需要填充成圆弧的值对应一种颜色,这个是最效率最低的,因为每个圈等分后需要填充,涉及到圆弧的计算。 比较完美的做法就是使用 drawArc 方法,这样能画出一个完整漂亮的圆,计算 372×920 个圆弧,但是 Python PIL 库的 arc 方法不支持浮点型的弧度值,从而不得不使用其他方法。对 Python 来说,绘图的库无非就是 Matplotlib 和 PIL 这俩,Matplotlib 使用了一下算是翻车了,等得我直接 ctrl c 了。 ...

September 8, 2019 · 1 min · jiezi

政采云前端小报第46期

政采云前端小报第46期浏览更多往期小报,请访问: https://weekly.zoo.team 【前端词典】一文读懂单页应用和多页应用的区别 - 掘金 前言 最近看到一些人在问单页面和多页面应用的区别。因为最近在整理 Vue 相关的内容,所以也就输出这一篇短文希望可以给你一个整体的认识。 这里也会大体介绍单页应用实现的核心 —— 前端路由。 单页应用 VS 多页应用 直观对比图 单页应用( SinglePa...在JavaScript中,如何判断数组是数组? - 前端修炼 - SegmentFault 思否 少年,你不能太天真了,我们朝夕面对的这门语言,可是JavaScript呀,任何你觉得已经习以为常的东西都可能瞬间转化成一个大坑,令人百思不得其解。Node.js软肋之CPU密集型任务-InfoQ Node.js的EventLoop只考虑了I/O操作,但对于需要占用大量CPU资源的计算,显得有点儿先天不足。本文分析了Node.js不擅于...前端必看的数据可视化入门指南 这是一篇给大家提供数据可视化开发的入门指南,介绍了可视化要解决的问题和可以直接使用的工具,我将从下面几个方面给大家介绍,同时以阿里/蚂蚁的可视化团队和资源举例说明:...【第1717期】Nginx入门指南 nginx了解weekly/101.精读《持续集成 vs 持续交付 vs 持续部署》.md at v2 · dt-fe/weekly 前端精读周刊. Contribute to dt-fe/weekly development by creating an account on GitHub.网站性能优化实战(二) - 腾讯Web前端 IMWeb 团队社区 | blog | 团队博客 Web前端 腾讯IMWeb 团队社区面试官角度看应聘:问题到底出在哪?(上) 原创不易,希望能关注下我们,再顺手点个赞~~面试官角度看应聘:问题到底出在哪?(下) 原创不易,希望能在掘金上关注下我们,再顺手点个赞~~自动化 Web 性能优化分析方案 原创不易,希望能关注下我们,再顺手点个赞~~ 本文首发于政采云前端团队博客: 自动化 Web 性能优化分析方案 在构建 Web 站点的过程中,任何一个细节都有可能影响网站的访问速度。如果开发人员不了解前端性能相关知识,很多不利网站访问速度的因素会在线上形成...Javascript垃圾回收机制 ...

September 6, 2019 · 1 min · jiezi

前端技术日志-在生产环境中使用原生-JavaScript-模块

本期刊专注于 Web 前端前沿技术,收集的内容来自于国外各大前端技术周刊,这里把自己感兴趣的,并值得分享的内容做了整理。部分链接可能无法直接打开,你需要通过科学上网的方式来解决。 本期热文在生产环境中使用原生 JavaScript 模块归功于打包技术的发展,现在,你可以将生产代码部署为 ES2015 模块了——包括静态和动态导入,而且比目前的非模块方式能获得更好的性能。 https://philipwalton.com/arti... PHILIP WALTON Web Template Studio 2.0Web Template Studio 是来自微软的 Visual Studio Code 的一个扩展,可以从 VS Code 以“向导”式风格生成新的全栈应用程序。 支持 Angular,Vue 和 React。 https://blogs.windows.com/win... LEA AKKARI (MICROSOFT) 2019年8月的 JavaScript 框架状态(视频)每隔半年,Tracy Lee 就会与几位不同框架的代表坐下来,来简单了解他们的工作状况。这一小时的剧集包含了 Evan You(Vue.js),Minko Gechev(Angular),Michael Dawson(Node.js),Jen Weber(Cardstack),Manu Mtz.-Almeida(Ionic)和 Marvin Hagemeister(Preact)。 https://www.youtube.com/watch... TRACY LEE 你应该知道的 React 中的 JavaScript在学习和使用 React 时,您应该要熟悉的 JavaScript 特性示例。 https://kentcdodds.com/blog/j... KENT C DODDS Node v12.9.0 发布,推出了 V8 7.6升级到 V8 7.6 后开辟了一些新机会,比如 Promise.allSettled()、JSON.parse 和 frozen/sealed 的数组性能改进,BigInt 现在有一个 toLocaleString 的方法可用于大数字的本地格式化。 ...

August 28, 2019 · 2 min · jiezi

WEB-安全漏洞之目录遍历

什么是目录遍历第一次接触到目录遍历漏洞还是在 ThinkJS 2 的时候。代码如下图,目的是当用户访问的 URL 是静态资源的时候返回静态资源的地址。其中 pathname 就是用户访问的 URL 中的路径,我们发现代码中只是简单的解码之后就在22行将其与资源目录做了拼接,这就是非常明显的目录遍历漏洞了。 为什么这么说呢?假设用户访问的 URL 是 http://xxx.com/../../../xxx.jpg 的话最终返回的文件地址就会变成 think.RESOURCE_PATH 的上三层目录中的文件了。而这种利用网站的安全缺陷来列出服务器目录或者文件的方式就成为目录遍历漏洞(Directory traversal),也称之为路径遍历漏洞(英文:Path traversal)。 目录遍历在英文世界里又名../ 攻击(Dot dot slash attack)、目录攀登(Directory climbing)及回溯(Backtracking)。其部分攻击手段也可划分为规范化攻击(Canonicalization attack)。via: wikipedia目录遍历的危害目录遍历最大的危害是能够让任意用户访问系统的敏感文件,继而攻陷整个服务器。例如获取linux下的/etc/passwd文件后可能会破解出root用户的密码等。 防御方法可以看到大部分情况下问题的关键就是 ../ 目录跳转符,所以防御的第一要务就是它进行过滤。除了过滤之外,还可以针对最终的文件路径进行判断,确保请求文件完整目录后的头N个字符与文档根目录完全相同,如果相同则返回内容,否则则可能是攻击地址不予返回。 回到文章开头说的那个代码问题,最终就是通过上述方法修复的,对最终的文件地址进行规范化后判断开头是否包含 RESOURCE_PATH 目录,如果不包含则返回空。

August 28, 2019 · 1 min · jiezi

关于云开发新服务实时数据推送你需要了解的全在这了

“微信小程序工程师邓坤力带你了解如何利用千呼万唤始出来的云开发实时数据推送服务打造生动的小程序和小游戏!”在数据库在小程序·云开发中的应用一文中,我们了解到实时数据推送作为云开发即将上线的一项新能力,主要指客户端使用官方SDK发起socket连接建立对一个集合的监听,目标集合中如果有符合过滤条件的数据发生变更,将会直接推送到建立监听的客户端。 简单来说,使用实时数据推送可以更有效率的拉取数据,帮你把你的应用变成实时有状态,场景会非常有用,比如可以用来做弹幕,做实时排名更新,做实时刷新,或者实时推送一些通知给到用户。 那么,实时数据推送具体是如何为小程序与小游戏赋能,提升开发效率的?让我们跟随微信小程序工程师邓坤力一起,深入了解这项新服务诞生的来龙去脉。 为什么要做实时数据推送? 介绍实时数据推送服务之前,弄懂一个直击灵魂问题将有助于我们的理解,那就是“为什么要做实时数据推送?” 想要更回答这个问题,需要从即时通信服务说起。 我们都知道建立一个简单、常规的即时通信服务需要长连作为实现实时性的基础,需要足够的存储来保证消息与文件的持久化,还需要实时推送功能来实现主动同步客户端的能力。在此过程中,开发者往往需要面临: (1)需从零自建完整服务,无法聚焦在原型和核心业务开发上。 (2)开发成本高。由于前后端逻辑复杂,开发者往往需要经历设计基础设施搭建,长连管理、数据库开发、安全管理等琐碎繁杂的步骤。 (3)维护成本高。开发者还需负责维护,完成基础设施管理、异常处理等。 (4)微信能力集成。自建服务器的开发者如要基于微信用户登录态进行操作并让小程序安全运行,就不可避免地需要接入微信鉴权体系,整合accesstoken和 sessionkey流程并保证其安全性。 看到这里,可能有很多读者已经想到了具有开箱即用、集成原生微信能力、自带云数据库、云函数、云存储的云开发,那么云开发的这些优势能否有效解决开发者在建立即时通信服务中常常会遇到的难题呢? 答案是不完全能,由于云开发不支持长连,并且不具备主动同步客户端的能力,因此只能通过短轮询以次级长连和推送的次级替代方案,在即时通讯服务构建时往往需要面临短轮询带来的资源浪费、成本与体验难平衡以及实时性差等问题。 可能又有小伙伴要问了,让云开发支持长连不就可以弥补这些缺陷?答案也是否定的,因为若云开发支持长连,整个即时通讯服务的实现仍避免不了对长连的开发和管理,并且需要接受和处理消息,导致流程仍较为复杂,而这恰恰有悖于云开发作为高效率、轻量级解决方案的理念。 由此,云开发的实时推送服务应运而生,它将即时通讯服务所需的能力与云开发独有的优势串联起来,让开发者可以更便捷地使用并快速实现需求。 实时数据推送有哪些能力? 能力概述 实时数据推送是云开发数据库新增的服务,通过这项服务,小程序端可实时监听数据库变更,即它支持根据开发者给定的查询语句进行监听,每当查询语句的结果发生变化时,小程序端就会收到包含更新内容的推送,并对实时数据变化做出响应。 总体来说,使用云开发的实时数据推送能力相比起自建服务可以享受以下便利,从而使其更专注于业务逻辑的设计: 原生能力,开箱即用无需管理长连无需编写服务端代码无需搭建和管理基础设施自动收到更新推送丰富的应用场景 实时数据推送的应用场景十分丰富。 在即时通信方面,实时数据推送支持小程序直播聊天室、弹幕等以及小游戏的区服聊天、房间聊天、私信等功能的实现。 在状态同步方面,小程序可以使用实时数据推送来保持应用最新状态的同步,以信息流为例,可以支持实时提示有新的文章、评论、点赞,从而达到更好的用户体验;对小游戏来说,可以支持使用状态同步的模型开发的小游戏,比如棋牌类小游戏。 而在实时协作方面,实时数据推送可以为在线共享文档、项目管理协作工具等提供支持。 简单易用的API 实时数据推送提供简洁易用的API,调用方便,并且可以完整描述整个维度的数据变化,以便开发者对具体业务逻辑做出响应。 自动处理异常 SDK在异常时会尽可能自动恢复状态,并且此恢复为开发者无感知,开发者仅需处理 SDK 无法自动恢复的错误。具体来说: 实时数据推送在断网、网络切换、NAT 地址刷新等情况时均能自动检测异常和恢复连接,并且在更新事件推送失败或丢失时有机制保障会成功拉取,而在更新事件乱序时有机制保障开发者收到的是顺序事件。 云开发新能力矩阵 云调用:云函数免鉴权调用微信服务端开放接口,获取微信开放数据,接收微信服务端消息推送。HTTP API:小程序外访问云开发资源。数据库聚合:分组查询、统计查询、流水线批处理。控制台数据库高级查询:控制台中批量数据库增删查改。云开发Network面板:小程序Network面板支持展示云开发请求。实时数据推送如果你有关于使用云开发CloudBase相关的技术故事/技术实战经验想要跟大家分享,欢迎留言联系我们哦~比心!

August 28, 2019 · 1 min · jiezi

基于Node的Axure文件在线预览

前言公司现在的产品Axure文档在生成好Html文件之后,都是通过git来进行管理的,每次文件更新,大家都需要从git上进行拉取,然后在本地查看,更新会出现不及时的问题,大家有时候忘记git拉取导致出现实现效果与最终产品稿不一致。最近在看Koa的东西,因此通过git hooks 搭配Koa来实现了一个在线预览,实现上大概是根据访问路径查找指定目录,然后对目录进行遍历,然后根据目录和文件类型的区分返回相应的数据。 实现做的过程中需要注意的点, 对于在Git服务端的文件是在.Git文件夹下的,因此我们无法得到其文件目录,实现的方式是通过git clone的方式将其clone下来,然后通过git hook的方式,当有push操作之后,则执行git pull来将文件拉取到本地,通过这种方式来将做到文件的实时更新。文件的返回,对于图片文件的读取方式要通过二进制的方式,对于其它的文件,如css,js之类要通过Utf-8的方式,开始统一通过binary的方式读取,然后返回导致图片可以显示,但是js执行报错。 实现代码如下 const Koa = require('koa');const path = require('path');const fs = require('fs');let mimes = { 'css': 'text/css', 'html': 'text/html', 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'json': 'application/json', 'js': 'text/javascript', 'xml': 'text/xml', 'png': 'image/png', 'pdf': 'application/pdf', 'less': 'text/css', 'gif': 'image/gif', 'txt': 'text/plain', 'tiff': 'image/tiff', 'svg': 'image/svg+xml'};// Scan dirfunction walk(reqPath) { let files = fs.readdirSync(reqPath); let fileList = []; for (let i = 0, len = files.length; i< len; i++) { let item = files[i]; if (item.startsWith('.')) { continue; } let realPath = reqPath + '/' + files[i]; if (isDir(realPath)) { fileList.push(filter(reqPath, files[i])); } else { fileList.push(files[i]); } } return fileList;}function isDir(path){ return fs.existsSync(path) && fs.statSync(path).isDirectory(); } //function filter(reqPath, filePath) { let files = fs.readdirSync(reqPath + '/' + filePath); for (let i = 0; i < files.length; i++) { let item = files[i]; if (item.endsWith('index.html')) { return filePath + '/' + 'index.html'; } } return filePath;}function dir(url, reqPath) { let contentList = walk(reqPath); let result = '<div align="center" style="font-family:arial;font-size:30px;">Product Document</div><ul>'; if (url == '/') { url = ''; } for (let [index, item] of contentList.entries()) { let realHref = url + '/' + item; let realItem = item.split('/'); result += '<li> <a style="font-family:arial;font-size:20px;" href=' + realHref + '>' + realItem[0] + '</a></li>'; } result = result + '</ul>'; return result;}async function file (url, filePath) { let resultMime = parseMime(url); let content; if (resultMime && resultMime.indexOf('image/') >= 0) { content = fs.readFileSync(filePath, 'binary'); } else { content = fs.readFileSync(filePath, 'utf8'); } return content;}async function content(ctx, fullStaticPath) { let url = decodeURI(ctx.url); let reqPath = path.join(fullStaticPath, url); let exist = fs.existsSync(reqPath); let content = ''; if (!exist) { content = 'Local file not exists'; } else { let stat = fs.statSync(reqPath); if (stat.isDirectory()) { content = dir(ctx.url, reqPath); } else { content = await file(ctx.url, reqPath); } } return content;}function parseMime(url) { let extName = path.extname(url); extName = extName ? extName.slice(1) : 'unknown'; return mimes[extName];}const app = new Koa();const staticPath = '../onlinedoc';`请输入代码`app.use(async(ctx) => { let fullStaticPath = path.join(__dirname, staticPath); let resultContent = await content(ctx, fullStaticPath); let resultMime = parseMime(ctx.url); if (resultMime) { ctx.type = resultMime; } if(resultMime && resultMime.indexOf('image/') >= 0) { ctx.res.writeHead(200); ctx.res.write(resultContent, 'binary'); ctx.res.end(); } else { ctx.body = resultContent; }});app.listen(8080, () => { console.log('Running');});

August 28, 2019 · 2 min · jiezi

我在node升级的遇到的错误及其解决方案和正确升级方法小记

现在我主要是针对安装了node的用户如何对node进行升级或者安装指定版本,由于我个人是linux系统,我下面介绍一下升级步骤: linux安装升级方法:没有安装node的可以参考连接 linux下node官方安装方法 1.查看node版本,没安装的请先安装; $ node -v没有安装的可能会提示命令未找到之类的信息。 2.清楚node缓存;$ sudo npm cache clean -f 3.安装node版本管理工具'n';$ sudo npm install n -g4.使用版本管理工具安装指定node或者升级到最新node版本;$ sudo n stable (安装node最新版本)$ sudo n 8.9.4 (安装node指定版本8.9.4)5.使用node -v查看node版本,如果版本号改变为你想要的则升级成功。若版本号未改变则还需配置node环境变量1.查看通过n安装的node的位置;$ which node (如:/usr/local/n/versions/node/6.12.3)2.cd进入/usr/local/n/versions/node/你应该能看到你刚通过n安装的node版本这里如:8.9.4;编辑/etc/profile; $ vim /etc/profile3.将node安装的路径(这里为:/usr/local/n/versions/node/8.9.4)添加到文件末尾;#set node pathexport NODE_HOME=/usr/local/n/versions/node/8.9.4export PATH=$NODE_HOME/bin:$PATH4.wq退出保存文件,编译/etc/profile;$ source /etc/profile5.再次使用node -v查看node版本,不出意外版本号应该变为你想要的。接下来介绍我在node升级的遇到的错误Error: EACCES: permission denied, access '/usr/local/lib/node_modules' npm ERR! at Error (native)使用升级命令行保持类似以下错误: Error: EACCES: permission denied, access '/usr/local/lib/node_modules' npm ERR! at Error (native) 原因: 执行命令行命令时没有获得管理员权限 解决办法: 在命令前面加上sudo即可.然后输入电脑的管理员密码操作即可完成. ...

August 28, 2019 · 1 min · jiezi

你不知道WebSocket吗

什么是WebSocket?WebSocket是一种在单个TCP连接上进行全双工通信的协议。这里我们发现了一个有趣的词:”全双工”,那我们就来简单了解下通信方式有哪些! 单工通信双方中,一方固定为发送端,一方则固定为接收端。信息只能沿一个方向传输。例如计算机与打印机之间的通信是单工模式 说的简单些就是:我打你你只能忍着! 半双工允许数据在两个方向上传输,但是同一时间数据只能在一个方向上传输,其实际上是切换的单工。例如HTTP协议:客户端向服务器发送请求(单向的),然后服务器响应请求(单向的) 说的简单些就是:我打你,你忍完后可以打我,我忍着… 全双工允许数据在两个方向上同时传输。例如手机通话,WebSocket就是这个样子! 说的简单些就是:两个人同时可以互相打对方 说了这么多其实目的就是让大家知道,WebSocket是支持双向通信的!双向通信的优点为什么要支持双向通信?单向通信有什么问题?还是从HTTP说起,我们知道HTTP协议是半双工的,而且服务器不能主动推送消息给浏览器!这个就是他的缺陷。假若我希望实现一个股票交易系统,可能股价每秒钟都有变化,但是价格变化了如何通知我们的客户端? 咱们来看看以前是怎么实现的! 轮询什么叫轮询?就是不停的轮番询问!说的直白些就是客户端定期发送请求给服务端。 短轮询配段代码,Talk is cheap,show me your code. const express = require("express");const app = express();// express 静态服务中间件用来返回静态文件app.use(express.static(__dirname));// 当前价格是100元let currentPrice = 100;// 获取最新价格接口app.get("/getPrice", (req, res, next) => { res.send('¥'+currentPrice * Math.random());});app.listen(3000);客户端不停的发送请求,去服务端获取最新价格。 <div>当前交易价格: <span id="price"></span></div><script> setInterval(() => { fetch('/getPrice'). then(res=>res.text()). then(data=>price.innerHTML = data) }, 1000);</script>很快我们就看出了这样编写代码的缺陷!如果数据变化的不快呢,那就会发送很多无意义的请求。每次发送请求都会有HTTP的Header会消耗大量流量,同时也会消耗CPU的利用率!长轮询长轮询是对短轮询的改进版,就是当第一个请求回来时再发送下一个请求! (function poll(){ fetch('/getPrice'). then(res=>res.text()). then(data=>{price.innerHTML = data;poll()})})()问题依旧是显而易见的!如果服务端数据变化很快,那么请求数目会更多;如果变化很慢,可能ajax会出现超时的问题。Iframe方式我们并不希望每次都创建一个新的请求,此时就可以使用Iframe来实现长连接 app.get("/getPrice", (req, res, next) => { setInterval(()=>{ // 不能使用end 否则会中断请求,我们要实现的是长连接 res.write(` <script> parent.document.getElementById('price').innerHTML = ${currentPrice * Math.random()} </script> `); },1000);});<body> <div>当前交易价格: <span id="price"></span></div> <iframe src="/getPrice" frameborder="0"></iframe></body>现在确实可以利用Iframe实现了长连接通信,但是页面的状态一直是加载态!EventSource流EventSource 接口用于接收服务器发送的事件。它通过HTTP连接到一个服务器,以text/event-stream 格式接收事件, 不关闭连接。 ...

August 28, 2019 · 2 min · jiezi

require时exports和moduleexports的区别你真的懂吗

面试会问require 的运行机制和缓存策略你了解吗?require 加载模块的是同步还是异步?谈谈你的理解 exports 和 module.exports 的区别是什么? require 加载模块的时候加载的究竟是什么? require提到 exports 和 module.exports 我们不得不提到 require 关键字。大家都知道 Node.js 遵循 CommonJS 规范,使用 require 关键字来加载模块。 require 重复引入问题问题:不知道小伙伴们在使用 require 引入模块的时候有没有相关,多个代码文件中多次引入相同的模块会不会造成重复呢? 因为在 C++ 中通常使用#IFNDEF等关键字来避免文件的重复引入,但是在 Node.js 中无需关心这一点,因为 Node.js 默认先从缓存中加载模块,一个模块被加载一次之后,就会在缓存中维持一个副本,如果遇到重复加载的模块会直接提取缓存中的副本,也就是说在任何时候每个模块都只在缓存中有一个实例。 require 加载模块的时候是同步还是异步?先回答问题,同步的!但是面试官要是问你为什么是同步还是异步的呢?其实这个答案并不是固定的,但是小伙伴们可以通过这几方面给面试官解释。 一个作为公共依赖的模块,当然想一次加载出来,同步更好模块的个数往往是有限的,而且 Node.js 在 require 的时候会自动缓存已经加载的模块,再加上访问的都是本地文件,产生的IO开销几乎可以忽略。require() 的缓存策略Node.js 会自动缓存经过 require 引入的文件,使得下次再引入不需要经过文件系统而是直接从缓存中读取。不过这种缓存方式是经过文件路径定位的,即使两个完全相同的文件,但是位于不同的路径下,会在缓存中维持两份。可以通过 console.log(require.cache)获取目前在缓存中的所有文件。 exports 与 module.exports 区别js文件启动时在一个 node 执行一个文件时,会给这个文件内生成一个 exports 和 module 对象,而module又有一个 exports 属性。他们之间的关系如下图,都指向一块{}内存区域。 exports = module.exports = {};看一张图理解这里更清楚: require()加载模块require()加载模块的时候我们来看一段实例代码 //koala.jslet a = '程序员成长指北';console.log(module.exports); //能打印出结果为:{}console.log(exports); //能打印出结果为:{}exports.a = '程序员成长指北哦哦'; //这里辛苦劳作帮 module.exports 的内容给改成 {a : '程序员成长指北哦哦'}exports = '指向其他内存区'; //这里把exports的指向指走//test.jsconst a = require('/koala');console.log(a) // 打印为 {a : '程序员成长指北哦哦'}看上面代码的打印结果,应该能得到这样的结论: ...

August 28, 2019 · 1 min · jiezi

前端20个灵魂拷问-彻底搞明白你就是中级前端工程师-下篇

不知不觉,已经来到了最后的下篇 其实我写的东西你如果认真去看,跟着去写,应该能有不少的收获。最近一些跨平台技术,React-native和flutter之类的,比较火。但是,我还是不准备把它们放进来,因为那是为做App而生,我想把Electron这个桌面端跨平台的技术放进来。理由是什么,后面说 这是上篇和中篇,如果你是第一次看这个系列文章,欢迎去从头开始学习: 前端20个灵魂拷问 彻底搞明白你就是中级前端工程师 【上篇】前端20个灵魂拷问 彻底搞明白你就是中级前端工程师 【中篇】 以及一些比较不错的文章: 从零编写一个React框架 我们为什么要熟悉这些通信协议 单页面应用SPA原理 9102年:手写一个React脚手架 【优化极致版】 性能优化不完全手册 Electron跨平台入门系列 上面的文章,gitHub上,都有对应的源码。进入正题一千个人眼里有一千个哈姆雷特,我们做不到完美每个人评判的标准不一样,我们唯有拿出碾压这个层级的能力的时候,才能堵住质疑者的嘴。当然,我们不做技术杠精,技术本身没有好坏。不喜欢就不理会 最后问题,我准备如下内容:前端的性能优化方向从传输层面去优化的方向预解析地址 首次请求解析地址如果没有缓存 那么可能消耗60-120ms 性能优化不完全手册这里面有介绍 preload预请求必要内容,prefetch预请求可能需要内容 这种请求方式不会阻塞浏览器的解析,而且能将预请求的资源缓存起来,而且可以设置crossorgin进行跨域资源的缓存,不会推迟首屏的渲染时间,还会加快后面的加载时间,因为后面的本身需要的资源会直接从缓存中读取,而不会走网络请求。 使用 preload 前,在遇到资源依赖时进行加载: 使用 preload 后,不管资源是否使用都将提前加载: 可以看到,preload 的资源加载顺序将被提前: 使用 preload 后,Chrome 会有一个警告: preload 和 prefetch 混用的话,并不会复用资源,而是会重复加载。若不确定资源是必定会加载的,则不要错误使用 preload,以免本末倒置,给页面带来更沉重的负担。 preload 加载页面必需的资源如 CDN 上的字体文件,与 prefetch 预测加载下一屏数据,兴许是个不错的组合。 preload和prefetch详解 这篇文章写得很棒 感谢作者 减少传输次数部分图片base64处理,然后使用雪碧图。多张图拼成一张传输 当然base64这个东西慎用,实际开发中它表现并那么好 减少传输体积例如后端返回数据:“该用户没有拥有权限” 可以改成:0 约定优于配置的思想一定要有 使用probbuffer协议ProtoBuffer是由谷歌研发的对象序列化和反序列化的开源工具 它的本质就是将一段数据序列化,转变成二进制形式传输 然后另外的服务器端或者客户端接受到之后 反序列化,转换成对应的数据格式(json) 好像还有人没有听说这个传输协议 其实它传输过程就是2进制流的形式 用得最多的是和GRPC配合Go语言或者服务器之间传输数据 ...

August 28, 2019 · 3 min · jiezi

技术型产品既要轻速度也要重壁垒

轻速度在打磨产品的过程中,很容易陷入技术的思维怪圈. 既想要快速的完成功能的开发,也想要性能的稳定和优化. 这大概是完美主义在作祟,加上一点点代码洁癖的影响. 在尝试开发各种类型的产品之后. 才发现,没有必要一开始就尽善尽美, 快速开发,小步迭代,才是王道. 在最快的时间开发出一款最小应用. 有着最基本的功能和反馈系统. 剩下的再合理分配时间,逐步添加和调整功能. 如小程序,自身有着完善的生态体系, 集成了登录,支付,分享等一些常用的API APP,在很多公司依旧采用着混合开发的方式. PC客户端,就拿Electron和PyQt来说, 初期的产品根本用不着原生的写法,直接套用网页即可. 体验需要增加的地方,留到后面在单独分离出来开发. 现在的前端相比以前 无论是在功能上还是体验上,都有很大的提升. 后端也可以尝试ServerLess,后端云服务 解决数据和运维相关的问题 总而言之,从时间上考虑 能够复用的页面和功能,就不要用多种语言重新开发 能够使用第三方产品和服务的,就不要自己造轮子. 前期开发,留给后期修改的空间即可. 重壁垒如果说互联网的产品有一大半是建立在数据的基础上 那么,物联网的产品就是建立在其硬件模块上. 5G到来,势必会重新刺激一度混乱的物联网市场. 物联网的本质其实还是互联网 只不过比起互联网,多了硬件成本 要想了解和学习互联网 还需要学习一些内容,如: 数电模电、单片机编程,嵌入式开发网络技术,也就是互联网的客户端和服务端无线技术,即通信协议,wifi,蓝牙等传感器技术,各类对应传感器的使用...... 现在的物联网,还没有一套业界公认的规范和协议 编程语言和通信协议等都是各自为政,很难互通 再加上硬件上的多种多样性,开发的难度可想而知. 重点是,要想入门物联网,要投入远比互联网多几倍的时间和精力 如果不是兴趣使然和拥有一个百折不挠爱折腾的心态 怕是很难接受,幸辛苦苦好几天,就只是点亮一个灯...... 但是,越是混乱,越是困难,壁垒越高 普通人很容易望而生畏,也很难提起兴趣 因为没有足够的即时反馈 互联网的产品很容易复制,模仿UI开发或者爬取数据 只要竞争者烧更多的钱和投入更多地资源 那么你的产品,就失败了,因为你能做,别人也能 甚至比你做的更好. 抄袭?不存在的,你说了我也不认. 但是物联网不一样,成本越高,越难入局 小结 轻速度和重壁垒开起来是两个相反的方向 但也存在相互交叉包含的场景 在互联网应用里找到属于自己的专利和功能 在物联网应用里找到最快速开发的方法和技术 先速度,后壁垒,先功能,后优化.

August 28, 2019 · 1 min · jiezi

Nodejs调试的各种姿势

Node.js 调试的痛点对于绝大部分前端人员,对JavaScript的调试更多停留在浏览器中,类似console.log和debugger,但这种方式对代码侵入性较高,甚至需要刷新页面或重启编译器。转向服务端后,没有浏览器界面,如果仅停留在原来的调试方式,开发效率想必是较低的。因此,前端人员转向服务端开发时,要习惯于命令行及 IDE 等调试手段,走出舒适区,才能准确定位问题,提高开发效率。 Node.js 调试的手段下文中涉及到的依赖库及软件版本Node.js - v10.15.3Chrome - v72.0.3626VS Code - v1.13.0以下代码段为例,用koa起一个简单的http serverconst Koa = require('koa');const app = new Koa();app.use(async ctx => { const time = new Date(); ctx.body = `hello, hiker! ${time}`;});app.listen(3000, () => { console.log('nodejs listening 3000.');});1. console.log()对前端人员非常友好,与浏览器中调试一样,console.log()、console.error()、console.time()等各种console形式,在代码中需要调试的地方直接写上,只是展示形式有所不同,在Node.js中是在终端命令行中打印。这是最简单快速的调试手段,缺点也很明显,对原有代码入侵较大,在特定场景中使用较局限。 举例app.use(async ctx => { const time = new Date(); console.time('TIME_TAKE'); console.log('this is time', time); ctx.body = `hello, hiker! ${time}`; console.timeEnd('TIME_TAKE');});app.listen(3000, () => { console.log('nodejs listening 3000.');});结果终端起服务:node index.js,并浏览器访问localhost:3000, 即可在终端命令行中看到相应打印的日志。 ...

August 27, 2019 · 1 min · jiezi

关于Nodejs-Streams-你需要知道的所有

NodeJs流以难以使用而闻名,甚至更难理解。 好吧,我给你带来了好消息 - 现在已经不是这样了。多年来,开发人员创建了许多软件包,其唯一目的是为了更容易的使用流。 但是,在本文中,我将重点介绍NodJs 原生的流API。 “Streams are Node’s best and most misunderstood idea.”— Dominic TarrStreams究竟是什么?流是数据的集合 - 就像数组或字符串一样。 不同之处在于流可能无法一次全部可用,并且它们不必适合内存。(译者注:流可以分片处理数据,所以不是一次全部可用,也不用担心数据太大,内存不够) 这使得流在处理大量数据时非常强大,或者一次来自外部源的数据。 但是,流不仅仅是处理大数据。 它们还为我们提供了代码中可组合性的强大功能。 就像我们可以通过管道联结其他较小的Linux命令来组合强大的Linux命令一样,通过使用流,我们在Node中也可以这样做。 const grep = ... // A stream for the grep outputconst wc = ... // A stream for the wc inputgrep.pipe(wc)Node中的许多内置模块实现了流接口: Readable StreamsWritable StreamsHTTP response, on the clientHTTP requests, on the clientHTTP requests, on the serverHTTP responses, on the serverfs read streamsfs write streamszlib streamszlib streamscrypto streamscrypto streamsTCP socketsTCP socketschild process stdout & stderrchild process stdinprocess.stdinprocess.stdout, process.stderr上面的列表包含一些原生Node对象的示例,这些对象是可读或可写的流。 其中一些对象是可读写的流,如TCP套接字,zlib和加密流。 ...

August 27, 2019 · 4 min · jiezi

node-express使用HTML模板

前言一般我们在做node web项目的时候,想使用我们平时常用的html模板,express默认使用jade模板,本身是没有HTML的,那么如何实现呢? ejs模板配置使用var app = express();app.set('views', path.join(__dirname, 'views'));//设置视图引擎app.set('view engine', 'ejs');html模板配置安装ejsnpm install ejs -D引入var ejs = require('ejs');使用var app = express();var ejs = require('ejs');app.set('views', path.join(__dirname, 'views'));app.engine('html', ejs.__express);app.set('view engine', 'html');说明app.engine(ext, cb)app.engine('html', require('ejs').__express);将ejs模板映射至.html文件; 上面实际上是调用了ejs的.renderFile()方法,ejs.__express是该方法在ejs内部的另一个名字。 因为加载的模板引擎后调用的是同一个方法.__express,所以如果使用的是ejs模板,不用配置该项。 app.set(name, value)在.set()方法的参数中,有一项是'view engine',表示没有指定文件模板格式时,默认使用的引擎插件; 如果这里设置为html文件,设置路由指定文件时,只需写文件名,就会找对应的html文件。

August 21, 2019 · 1 min · jiezi

setTimeout-或者-setInterval关于-Javascript-计时器你需要知道的一切都在这里

先来回答一下下面这个问题:对于 setTimeout(function() { console.log('timeout') }, 1000) 这一行代码,你从哪里可以找到 setTimeout 的源代码(同样的问题还会是你从哪里可以看到 setInterval 的源代码)? 很多时候,可以我们脑子里面闪过的第一个答案肯定是 V8 引擎或者其它 VM们,但是要知道的一点是,所有我们所见过的 Javascript 计时函数,都没有出现在 ECMAScript 标准中,也没有被任何 Javascript 引擎实现,计时函数,其实都是由浏览器(或者其它运行时,比如 Node.js)实现的,并且,在不同的运行时下,其表现形式有可能都不一致。 在浏览器中,主计时器函数是 Window 接口的一部分,这保证了包括如 setTimeout、setInterval 等计时器函数以及其它函数和对象能被全局访问,这才是你可以随时随地使用 setTimeout 的原因。同样的,在 Node.js 中,setTimeout 是 global 对象的一部分,这拿得你也可以像在浏览器里面一样,随时随地的使用它。 到现在可能会有一些人感觉这个问题其实并没有实际的价值,但是作为一个 Javascript 开发者,如果不知道本质,那么就有可能不能完全的理解 V8 (或者其它VM)是到底是如何与浏览器或者 Node.js 相互作用的。 暂缓一个函数的执行计时器函数都是更高阶的函数,它们可以用于暂缓一个函数的执行,或者让一个函数重复执行(由他们的第一个参数执行需要执行的函数)。 下面这是一个暂缓执行的示例: setTimeout(() => { console.log('距离函数的调用,已经过去 4 秒了')}, 4 * 1000)在上面的示例中, setTimeout 将 console.log 的执行暂缓了 4 * 1000 毫秒,也就是 4 秒钟, setTimeout 的第一个函数,就是需要暂缓执行的函数,它是一个函数的引用,下面这个示例是我们更加常见到的写法: const fn = () => { console.log('距离函数的调用,已经过去 4 秒了')}setTimeout(fn, 4 * 1000)传递参数如果被 setTimeout 暂缓的函数需要接收参数,我们可以从第三个参数开始添加需要传递给被暂缓函数的参数: ...

August 21, 2019 · 4 min · jiezi

NestJs学习之旅3服务提供者

欢迎持续关注NestJs之旅系列文章 简介服务提供者是NestJs一个非常重要的概念,一般来说,被装饰器@Injectable()修饰的类都可以视为服务提供者。服务提供者一般包含以下几种: Services(业务逻辑)Factory(用来创建提供者)Repository(数据库访问使用)Utils(工具函数)使用下文中将以Services来说明服务提供者的具体使用。 典型的MVC架构中其实有一个问题,业务逻辑到底放哪里? 放在控制器,代码复用成了问题,不可能去New一个控制器然后调用方法,控制器方法都是根据路由地址绑定的放在Model,导致Model层臃肿,Model应该是直接和数据库打交道的,业务逻辑跟数据库的关系并不是强制绑定的,只有业务逻辑涉及到数据查询/存储才会使用到Model层现阶段比较流行的架构是多添加一个Services层来写业务逻辑,分离Model层不应该做的事情。 // 业务类 user.service.ts@Injectable()export class UserServices { private readonly users: User[] = []; create(user: User) { this.users.push(user); } findAll(): User[] { return this.users; }}// 用户控制器@Controller('users')export class UserController { constructor(private readonly userService: UserService) {} // 注入UserService @Post() async create(@Body() createUserDTO:CreateUserDTO) { this.userService.create(createUserDTO); } @Get() async findAll() { return this.userService.findAll(); }}服务提供者的ScopeSpringBoot中提供了Scope注解来指明Bean的作用域,NestJs也提供了类似的@Scope()装饰器: scope名称说明SINGLETON单例模式,整个应用内只存在一份实例REQUEST每个请求初始化一次TRANSIENT每次注入都会实例化@Injectable({scope: Scope.REQUEST})export class UserService {}可选的依赖项默认情况下,如果依赖注入的对象不存在会提示错误,中断应用运行,此时可以使用@Optional()来指明选择性注入,但依赖注入的对象不存在时不会发生错误。 @Controller('users')export class UserController { constructor(@Optional() private readonly userService:UserService){}}基于属性的注入上文中的注入都是基于构造函数的,这样做有一个缺陷,如果涉及到继承的话,子类必须显示调用super来实例化父类。如果父类的构造函数参数过多的话反而成了子类的负担。 ...

August 21, 2019 · 2 min · jiezi

作为一个前端工程师也要掌握的几种文件路径知识

前言之前在做webpack配置时候多次用到路径相关内容,最近在写项目的时候,有一个文件需要上传到阿里云oss的功能,同时本地服务器也需要保留一个文件备份。多次用到了文件路径相关内容以及Node核心API的path模块,所以系统的学习了一下,整理了这篇文章。 node中的路径分类node中的路径大致分5类,dirname,filename,process.cwd(),./,../,其中dirname,filename,process.cwd()绝对路径 通过代码对每个分类进行说明: 文件目录结构如下: 代码pra/ - node核心API/ - fs.js - path.jspath.js中的代码 const path = require('path');console.log(__dirname);console.log(__filename);console.log(process.cwd());console.log(path.resolve('./'));在代码pra目录下运行命令 node node核心API/path.js,我们可以看到结果如下: /koala/Desktop/程序员成长指北/代码pra/node核心API/koala/Desktop/程序员成长指北/代码pra/node核心API/path.js/koala/Desktop/程序员成长指北/代码pra/koala/Desktop/程序员成长指北/代码pra然后我们有可以在node核心API目录下运行这个文件,node path.js,运行结果如下: /koala/Desktop/程序员成长指北/代码pra/node核心API/koala/Desktop/程序员成长指北/代码pra/node核心API/path.js/koala/Desktop/程序员成长指北/代码pra/node核心API/koala/Desktop/程序员成长指北/代码pra/node核心API对比输出结果,暂时得到的结论是 __dirname: 总是返回被执行的 js 所在文件夹的绝对路径__filename: 总是返回被执行的 js 的绝对路径process.cwd(): 总是返回运行 node 命令时所在的文件夹的绝对路径./: 跟 process.cwd() 一样,返回 node 命令时所在的文件夹的绝对路径为什么说上面是暂时得到的结论,因为是有错误的,再看一段代码:我们在path.js中加上这句代码 exports.A = 1;之前直接通过readFile读取文件路径报错, fs.readFile('./path.js',function(err,data){ });现在在刚才报错的fs.js里面加这两句代码看看: const test = require('./path.js');console.log(test)在代码pra/目录下运行node node核心API/fs.js,最后查看结果,说明是可以访问到的: { A: 1 }那么关于 ./ 正确的结论是: 在 require() 中使用是跟 __dirname 的效果相同,不会因为启动脚本的目录不一样而改变,在其他情况下跟 process.cwd() 效果相同,是相对于启动脚本所在目录的路径。 路径知识总结:__dirname: 获得当前执行文件所在目录的完整目录名__filename: 获得当前执行文件的带有完整绝对路径的文件名process.cwd():获得当前执行node命令时候的文件夹目录名./: 不使用require时候,./与process.cwd()一样,使用require时候,与__dirname一样只有在 require() 时才使用相对路径(./, ../) 的写法,其他地方一律使用绝对路径,如下: ...

August 21, 2019 · 1 min · jiezi

学Node必须掌握的Buffer和Stream

本文并不介绍 Buffer 和 Stream 使用的api,而是把对 Buffer 和 Stream 的理解带给大家。 之前发了篇文章《Nodejs核心模块简介》,笼统的介绍了下 Events模块、fs模块、stream的使用、http模块。 文章也在我的 github 博客上,欢迎订阅。 因为想学好 node 这些东西几乎是必须掌握的。这篇文章来说一下在 node 中几乎无处不在的 Buffer 和 Stream,什么是 Buffer 以及它和 Stream 到底什么关系? 马上揭晓。 BufferBuffer 是个类数组的对象,可以把它当做数组更好理解些,只不过里面存的是二进制数据。 先创建个 buffer 来看看它打印出来的样子: const str = 'hello';const buf = Buffer.from(str);console.log(buf); // <Buffer 68 65 6c 6c 6f>buf 里装的数据是字符串 hello,而 buf 的长度为 5 ,hello 的长度也是 5,所以 Buffer 中每个元素占一个字节(英文每个字母是一个字节)。 Buffer 是什么从代码使用来看,Buffer 是类数组对象。从内存角度看,Buffer 是在内存中开辟的一块区域。 Buffer 翻译过来是缓冲器,它主要用来暂存数据。 为了更好理解,用大白话把上面哪句翻译一下:Buffer 就是我们常坐的公交车,人就是数据,人上车就表示在 Buffer 中输入数据, 到站了人就下车,Buffer 里的数据就会输出 ...

August 20, 2019 · 1 min · jiezi

Cloud-Studio搭建Hexo

Cloud StudioCloud Studio是在线集成开发环境,它提供了完整的 Linux 环境, 并且支持自定义域名指向。IDE 中有近 20 种开发环境,支持一键切换,进度实时保存。 Hexo快速、简洁且高效的博客框架,Hexo依赖于Node.js,并且使用Markdown解析文章,在几秒内,即可利用靓丽的主题生成静态网页。 随时随地搭建?我们都知道,Github、码云、Coding都免费提供了静态网页托管服务,我们写好的代码上传到托管平台,通过pages服务可以实现外网访问。文章开头所述,Cloud Studio提供了完整的 Linux 环境,并且进度实时保存,我们只要有浏览器就可以随时开发并且部署,配合Pages服务,不用买服务器,就可以拥有自己的博客系统。 说了这么多,就是想让大家了解一下工作原理,下面让我们开始吧!创建仓库首先,我们打开腾讯开发者平台)(需要注册腾讯云账号),点击右上角+号,新建项目。然后按照图示,开启pages服务。 新建工作空间打开Cloud Studio)官网,点击新建工作空间,来源选择“腾讯云开发者平台”,项目选择上一步创建的仓库,运行环境选择Hexo。 搭建Hexo生成所需文件由于我们选择的运行环境为Hexo,所以工作空间自带了Node.js、Git,hexo-cli。我们只需要运行以下命令就可以。 hexo init <folder>cd <folder> npm install启动服务器hexo cleanhexo dhexo s创建访问链接通过Cloud Studio右侧栏“访问链接”测试是否成功。 需要注意的是:端口改为4000,选择创建链接,然后点击创建的链接即可访问。部署到Pages安装 hexo-deployer-gitnpm install hexo-deployer-git --save修改 _config.yml 参数打开站点配置文件_config.yml,修改deploy属性。 deploy: type: git repo: https://gitee.com/giteetop/giteetop.git branch: masterrepo:你的仓库地址,可以是Github、Gitee以及Coding。部署hexo cleanhexo g -d过程中输入仓库的账号和密码,等待提交完成,然后就可以生成静态页面了。 常见问题如果生成静态页面后,发现页面没有样式了,这是因为使用了域名访问,但是没有配置url路径。打开站点配置文件_config.yml,修改url和root属性。 # URL## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'url: http://yoursite.com/root: /permalink: :year/:month/:day/:title/permalink_defaults:如果您的网站存放在子目录中,例如 http://yoursite.com/blog,则请将您的 url 设为 http://yoursite.com/blog 并把 root 设为 /blog/。

August 20, 2019 · 1 min · jiezi

搭建一个好用的API-Mock服务

上篇文章讲述了怎么用Node实现一个API服务 现在开始讲述如何搭建一个好用的API Mock服务 达到的效果: 在开发环境中就可以在url后面添加?ismock=1参数来实现数据mock,(没有该参数就访问正常数据),且不会对测试环境和生产环境造成任何影响 实现步骤: 通过Webpack设置代理。 //webpack.config.js proxy: { '/mock': { target: 'mock', changeOrigin:true, pathRewrite: { '^/mock': '' } } }拦截请求(比如Axios自带的拦截器) 判断url参数(如?ismock=1)判断当前环境(如process.env.NODE_ENV == 'development')添加baseUrl = /mock在webpack的压缩处理中删除不可达代码(见webpack配置表)源码地址

August 20, 2019 · 1 min · jiezi

serverless在微店node领域的探索应用

背景目前微店中台团队为了满足公司大部分产品、运营以及部分后端开发人员的尝鲜和试错的需求,提供了一套基于图形化搭建的服务端接口交付方案,利用该方案及提供的系统可生成一副包含运行时环境定义可立即运行的工程代码,最后,通过 “某种serverless平台” 实现生成后代码的部署、CI、运行、反向代理、进程守、日志上报、进程分组扩容等功能。 这样,产品和运营人员可基于此种方式搭建的接口配合常用的cms系统实现简单查询需求如活动大促的自主“研发”上线,代码的可靠性、稳定性由中台研发侧提供的“某种serverless平台”保障,有效支撑了多个业务快速上线,节省后端开发人员的人力与硬件资源的开销(大多数需求下,nodejs业务对虚拟机的资源开销小于java业务)。 接口搭建系统此处并不讲解接口搭建系统的原理与实现,只说明其与上文提到的 “某种serverless平台” 的关系。 ](https://si.geilicdn.com/viewm... 这是系统的全貌,部分细节由于敏感信息而省略。平台使用方可基于每个功能组件搭建出一套复杂的业务流,在搭建阶段,提供在线debug和日志功能,可用于排错;在部署CI阶段,可集成不同的运行时平台,既可以是自主实现的运行时,也可是第三方云平台;在运行阶段,通过使用agentool工具实时监控当前服务的性能,并可通过traceId一览请求在各系统的全貌。 serverless方案本节以资源隔离粒度为度量,介绍了我对三种serverless方案的取舍以及最终为何选择了隔离程度更高的kubeless云平台。 基于函数隔离的Parse Server方案Parse Server提供了基础功能:基于类与对象的权限控制、基于document的nosql存储、简易的用户身份认证、基于hook的自定义逻辑等,但经过笔者的调查与论证,最终并没有采用类似单进程下基于函数隔离的Parse Server及类似方案,这有几点原因: Parse Server方案很重,额外引入了非常多不需要的功能,如权限控制、认证、存储等服务隔离级别低,多个服务在一个进程运行,多个服务会在libuv层互相抢占CPU,互相影响对方的业务处理水平扩容难度大,针对单个服务的扩容无法做到底层基于express框架,无法满足运行时接口调用链路的trace追踪当多个服务同时引入不同的资源如db、es或者服务创建的对象足够多时,会存在Parse Server主进程溢出的风险,毕竟64位机的node堆内存是有1.4GB上限的,尽管这个上限是可配置的Parser Server发布的接口需通过其client调用,这在公司商用情况下需要做许多额外的配置才能满足Parse Server官网基于进程隔离的super-agent方案为了解决多个服务抢占libuv的问题,我选择了自主研发的 super-agent方案,通过其名称便可知它是一个超级代理,但它不仅是代理,还是一个具有极简功能且可靠的发布系统和运行时容器;它是一个分布式应用,节点间功能划分明确;它同时提供实时调试功能。 super-agent是一个多角色分布式系统,它即可以看做node容器,也可看成serverless的node实现,它以进程为粒度管理服务。它分为“协调者”和“参与者”,协调者实现 应用CI部署、启动、进程维护与管理和反向代理功能,参与者实现 业务请求代理、接受协调者调度。 在super-agent架构中,端口是区分服务的唯一标识,端口对客户端而言是透明的,这层端口资源的隔离由super-agent来做掉,因此多个服务可避免在libuv层的互相竞争,提供水平扩容的可能性。 反向代理super-agent最核心的功能在于反向代理。由于每个服务都被包装成有单独端口的独立HTTP应用,因此当用户请求流量经过前端转发层后进入super-agent,由其根据相关规则做二次转发,目前支持基于 “路径、端口”规则的转发。 部署后端应用部署需要进行 “优雅降级、流量摘除、健康检查、应用初始化完毕检查、流量导入、所有参与节点的部署状态查询” 等步骤,需要妥善处理;同时,协调者负责协调众多参与节点全部完成部署操作,这是一个分布式事务,需要做好事务出错后的相关业务补偿。 关于流量上图并未画出节点角色的区别,实际上只有参与者节点才真正接受用户请求。 协调者流量全部来自于内部系统,包括接受 “接口搭建系统”调用或者其他系统实现的dashboard服务;同时其会向参与者发送相关信令信息,如部署、扩容、下线、debug等。 参与者流量来自于内部系统和外部流量,其中大部分来自于外部流量。内部流量则承载协调者的信令信息。 水平扩容服务的水平扩容重要性不言而喻。super-agent方案中,协调者负责管理服务的扩容与逻辑分组。 此处的服务是通过服务搭建平台通过拖拽生成的nodejs代码,它是一个包含复杂业务逻辑的函数,可以是多文件。具体的,super-agent通过将该服务包装成一个HTTP服务在单独的进程中执行。因此,如果要对服务进行水平扩容,提供多种策略的选择: 默认每台虚拟机或物理机一个服务进程,总体上N个机器N个服务进程扩容时默认每台机器再fork一个服务进程,N机器2*N个服务进程为了更充分利用资源,可为每台机器划分为逻辑组,同时选择在某几个组的机器单独扩容这些策略对下游应用透明,只需选择相关策略即可。 水平扩容的缺点:每台虚拟机或物理机资源有上限,一个正常的node进程最多消耗1.4GB内存,计算资源共享,在一台8C16G的虚拟机上,最多可同时运行16个服务。及时通过分组扩容的方式,每当扩展新的虚拟机或物理机,都需要由super-agent根据分组信息实现进程守护,同时每次服务CI部署也同样如此。运维管理上需要配合非常清晰明了的dashboard后台才能快速定位问题,这点在多服务的问题上尤其突出。 在线调试super-agent提供消息机制,由搭建平台中组件开发人员使用提供的serverless-toolkit工具debug相关逻辑,最终可在super-agent的协调者后台查看实时debug结果。 总结super-agent是符合常规的基于业务进程隔离的解决方案,它有效的支撑了微店的几个活动及产品,虽然峰值QPS不高(100左右),但它也论证了super-agent的稳定性及可靠性(线上无事故,服务无重启,平稳升级)。 但是,super-agent仍然存在几个问题,它让我们不得不另觅他法: 日常运维困难,需要开发一系列后台系统辅助运维,这需要不少人力成本这是一个典型的一机多应用场景,当部署super-agent时会对运行其上的服务有所影响(重启),尽管这个影响并不影响用户访问(此时流量已摘除),但仍然是个风险点水平扩容实现繁琐,当下掉某几台参与节点时会带来不少影响基于内核namespace隔离的kubeless方案基于kubeless的方案则是隔离最为彻底的解决方法,kubeless是建立在K8s之上的serverless框架,因此它可以利用K8s实现一些非常有用的特性: 敏捷构建 - 能够基于用户提交的源码迅速构建可执行的函数,简化部署流程;灵活触发 - 能够方便地基于各类事件触发函数的执行,并能方便快捷地集成新的事件源;自动伸缩 - 能够根据业务需求,自动完成扩容缩容,无须人工干预。其中,自动伸缩则解决了 super-agent 的痛点。 kubeless中与我们紧密相关的有两个概念:“function和runtime” ,function是一个统称,它包括运行时代码、依赖以及其他配置文件;runtime则定义运行时依赖的环境,是一个docker镜像。 若要接入kubeless平台,我们需要解决如下几个问题: 开发自定义运行时,满足商用需求,如trace、日志分片、上报采集自定义构建镜像,实现ts编译基于yaml的function创建、更新、删除流程探索,并自动化function部署,包括流量摘除、流量导入、业务健康检查中间件日志、业务日志、trace日志隔离与挂载function运行时用户权限问题水平扩容探索与尝试资源申请规范指定与部署规范约定因此,前进的道路仍然很曲折,而且很多需求需要自己从源码上去寻找解决方法。 一些说明kubeless实现的serverless体系中,function所在pod中的所有容器共享网络和存储namespace,但是默认外网是不可访问k8s集群的所有pods,因此需要通过一层代理实现请求的转发,这就是“Service”。Service负责服务发现及转发(iptables四层),因此在Kubeless或者K8s中不会直接通过pod IP来访问服务,而是通过Service转发四层流量完成。Service有K8s分配的cluserIp,clusterIp是集群内部虚拟IP,无法被外部寻址,而是通过Kube-Proxy在容器网络之上又抽象了一层虚拟网络,Kube-Proxy负责Service的路由与转发(关于kube-proxy细节,请看参考资料)。 Service后端对应是一个或多个pods,这些pods中的一个容器则运行相同的业务代码。那么流量是如何路由至Service上来呢?这就涉及到Service的“发布”,常用的是Ingress。Ingress包括HTTP代理服务器和ingress controller,代理服务器负责将请求按照规则路由至对应Service,这层需要Kube-Proxy实现虚拟网络的路由;ingress controller负责从K8s API拉取最新的HTTP匹配规则。](https://si.geilicdn.com/viewm... 问题解决自定义镜像:这里的镜像包括两部分:构建镜像和运行时镜像。运行时镜像需要解决宿主代码的鲁棒性,以及提供 livenessProbe、readinessProbe、metric接口实现;构建镜像则负责构建阶段的操作,如编译、依赖安装、环境变量注入等操作。具体编写可参考 implement runtime images)。构建镜像参考1关于function的CRUD操作,笔者先通过命令行走通整个流程后,又切换成基于yaml的配置文件启动,使用yaml启动好处在于:a,可使用kubeless自带的流量导入与摘除策略 b,水平扩容简单 c,命令简单 d,配置文件模板化,自动化部署策略由于涉及到业务特点,此处不详细介绍日志的挂载是必要的,否则pod一旦重启,容器内的所有日志全部丢失。尽管会存在日志收集的操作,可是日志收集进程大多数都是异步进行,因此会存在丢失日志的情况。因此,必须通过挂载volumn的形式在K8s node上映射文件。但在这过程中会出现权限的问题,这在下一点说明权限问题在于kubeless将function的执行权限设置为非root。这是安全且符合常理的设定,但有时function需要root权限,这需要修改K8s的security context配置,需要谨慎处理水平扩容基于K8s的HPA组件完成,目前支持基于CPU和QPS等指标进行扩容,目前笔者并未专门测试这项内容,因为它足够可靠资源申请的指定需要符合每个公司的实际情况以及业务特点,以node技术栈为例,pod中每个容器设置1C2GB的内存符合实际情况;而至于部署规范,则要兼顾运行时容器的特点,合理配置K8s的node与pod、function的对应关系总结运行在kubeless 中的函数和运行在super-agent的代码没有什么不同,可是周边的环境准备可大大不同。为了让kubeless中的function可以接入公司内部中间件服务,笔者费了不少功夫,主要集中在日志及收集部分。好在事在人为,解决的办法总是多于失败的方法。 ...

August 20, 2019 · 1 min · jiezi

什么是npm系列二install-的十八般武艺

本文同步发表于作者博客: 什么是npm系列:二、install 的十八般武艺我们平时使用npm install只是用来安装npm上的包,其实它比我们想象的更强大,不仅仅是安装npmjs上的包,还能够从git、gitlab直接安装,我们先看下install命令的参数列表: // 从npm源安装npm install npm install [<@scope>/]<name>npm install [<@scope>/]<name>@<tag>npm install [<@scope>/]<name>@<version>npm install [<@scope>/]<name>@<version range>// 从git源、tar包、本地目录安装npm install <git-host>:<git-user>/<repo-name>npm install <git repo url>npm install <tarball file or url>npm install <folder>1. npm install默认情况下,执行命令后会安装package.json中罗列的所有模块。 如果添加--production标记,或者环境变量NODE_ENV被设置为production,npm就不会安装devDependencies中的模块。 1.1 npm install <folder>通过symlink的形式,把包目录连接到项目中。 1.2 npm install <tarball file or url>从tar包装有两种方式: file : 本地tar文件npm install ./package.tgzurl : 远程tar的地址npm install https://github.com/indexzero/forever/tarball/v0.5.61.3 npm install <git remote url>另一个比较常见的方式是从git地址安装npm包,不过使用的时候需要注意仓库是否太大,以及源地址是否在外国,这些都会影响安装速度。 协议地址的格式如下: <protocol>://[<user>[:<password>]@]<hostname>[:<port>][:][/]<path>[#<commit-ish> | #semver:<semver>]<protocol> 可以是 git, git+ssh, git+http, git+https, 或者 git+file. ...

August 19, 2019 · 1 min · jiezi

前端知识体系之拓展边界-玩转Nodejs命令行

背景在做 cli 工具的时候,非常需要命令行相关的第三方库。一个比较稳健成熟的命令行应该考虑以下 4 种需求: 读取传入的各种参数,例如: --help, -v=123逻辑处理和友好的 UI 交互,例如:提供列表选择细致控制字体颜色和背景颜色状态显示,例如:等待过程前面是转圈圈,完成过程前面自动换成对号读取参数: commander这里用到的是 commander 这个库。它的文档地址是:https://www.npmjs.com/package... 请先看下面代码: const program = require("commander");// 分为2种操作, 2种操作互相冲突// Options 操作program .version("0.0.1") .option("-t, --types [type]", "test options") // option这句话必须加 .parse(process.argv);// Commands 操作program // 命令与参数: <> 必填; [] 选填 .command("exec <cmd> [env]") // 别名 .alias("ex") // 帮助信息 .description("execute the given remote cmd") // 没用,option和command是冲突的 .option("-e, --exec_mode <mode>", "Which exec mode to use") // 执行的操作 .action((cmd, env, options) => { // 参数可以拿到 console.log(`env is ${env}`); console.log('exec "%s" using %s mode', cmd, options.exec_mode); }) // 自定义help信息 .on("--help", function() { console.log("自定义help信息"); });// 参数长度不够, 打印帮助信息if (!process.argv.slice(2).length) { program.outputHelp();}if (program.types) { console.log(program.types);}// 解析命令行参数program.parse(process.argv);文档上基本都写明白了,但是有几个需要注意的点: ...

August 19, 2019 · 2 min · jiezi

eggjs-eggtx-接口级事务管理插件

egg-tx 一个 egg 事务插件,它支持 Mysql、Mongo 数据库,它能做到请求接口级别的事务管理。 依赖的插件对于使用 Mysql 数据库你需要开启 egg-sequelize 插件。对于使用 Mongo 数据库你需要开启 egg-mongoose 插件。安装$ npm i egg-tx --save开启插件// {app_root}/config/plugin.jsexports.tx = { enable: true, package: 'egg-tx',};配置// {app_root}/config/config.default.jsexports.tx = { reqAction:['POST','PUT','DELETE'], dbType:'mysql'};reqAction:将为指定动作的所有请求进行事务管理,该数组的值可为 GET、POST、PUT、DELETE、HEAD、PATCH、OPTIONS(默认值为 POST、PUT、DELETE)dbType:所使用的数据库类型,该值可为 mysql 或 mongo (默认值为 mysql)使用例子你可以通过 ctx.tx.session 获取到本次请求的事务会话对象,前提是它已经被事务管理器所管理。 mysqlawait this.ctx.model.User.create(user, { transaction: this.ctx.tx.session,});mongoawait this.ctx.model.User.insertMany([ { username: 'lyTongXue', password: '123456' },], { session: this.ctx.tx.session });注解@tx使用该注解的接口方法将会进行事务管理,即便 reqAction 配置项中未包含该动作类型的请求。 // {app_root}/app/controller/{controller_name}.js/*** @tx*/async create(){}@txIgnore即便 reqAction 配置项中包含了该动作类型的请求,使用了该注解的接口方法将不会进行事务管理。 // {app_root}/app/controller/{controller_name}.js/*** @txIgnore*/async index(){}提问交流1、接口方法的 jsDoc 是否有一定要求? ...

August 19, 2019 · 1 min · jiezi

ReactNative分布式热更新系统

热更新是一个非常方便的方案。在应对大量用户和深度定制的时候一定不能使用开源的方案。一般第三方的这种方案,服务器带宽较小,或者不够灵活,不能满足自己的想法。这里推荐自己实现对应的热更新方案。只需要少量代码即可支持。下面推荐一种灵活的热更新方案。包括客户端的改造、接口设计、界面开发,同时是开源的!可以自由改造。体验地址:demo 用户名密码都是:admin 基础数据的准备和实现首先第一点,一个APP如果要支持热更新,需要在打开APP(或者其他进入RN页面之前)就要判断是否需要更新bundle文件。这里就是我们实现热更新的节点。一旦需要热更新就开始下载文件,而判断的接口就是我们这次文章的核心内容。这里简单贴出安卓和ios两端的下载逻辑。 请求之前需要在head中附带上客户端的几个重要信息。客户端版本号version、客户端唯一id:clientid、客户端类型platform、客户端品牌brand。ios下载的例子 -(void)doCheckUpdate{ self.upView.viewButtonStart.hidden = YES; if ([XCUploadManager isFileExist:[XCUploadManager bundlePathUrl].path]) {//沙盒里已经有了下载好的jsbundle,以沙盒文件优先 self.oldSign = [FileHash md5HashOfFileAtPath:[XCUploadManager bundlePathUrl].path]; }else {//真机计算出的包内bundlemd5有变化,可能是压缩了,所以这里写死初始化的md5 // NSString *ipPath = [[NSBundle mainBundle] pathForResource:@"main" ofType:@"jsbundle"]; // self.oldSign = [FileHash md5HashOfFileAtPath:ipPath]; self.oldSign = projectBundleMd5; } AFHTTPSessionManager *_sharedClient = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"http://test.com"]]; [self initAFNetClient:_sharedClient]; [_sharedClient GET:@"api/check" parameters:nil progress:nil success:^(NSURLSessionDataTask * __unused task, id JSON) { NSDictionary *dic = [JSON valueForKeyPath:@"data"]; BOOL isNeedLoadBundle = YES; if ([dic isKindOfClass:[NSDictionary class]]) { self.updateSign = [dic stringForKey:@"sign"]; self.downLoadUrl = [dic stringForKey:@"downloadUrl"]; if(self.updateSign.length && self.oldSign.length && (![self.updateSign isEqualToString:self.oldSign])) { //需要更新bundle文件了 self.upView.viewUpdate.hidden = NO; [self updateBundleNow]; isNeedLoadBundle = NO; }else { //不需要更新bundle文件,再处理跳过按钮显示逻辑 [self.upView showSkipButtonOrNot]; } } if (isNeedLoadBundle) { [self loadBundle]; } } failure:^(NSURLSessionDataTask *__unused task, NSError *error) { [self loadBundle]; }];}安卓下载的例子 ...

August 19, 2019 · 2 min · jiezi

NestJs学习之旅1快速开始

经过NodeJs系列课程和Typescript系列课程,终于开始了激动人心的NestJs学习之旅。 欢迎持续关注NestJs之旅系列文章 介绍Nest(或NestJS)是一个用于构建高效,可扩展的Node.js服务器端应用程序的框架。它使用渐进式JavaScript,内置并完全支持TypeScript(但仍然允许开发人员使用纯JavaScript编写代码)并结合了OOP(面向对象编程),FP(功能编程)和FRP(功能反应编程)的元素。 import { Controller, Get } from '@nestjs/common';@Controller('cats')export class CatsController { @Get() findAll(): string { return 'This action returns all cats'; }}熟悉Java的同学应该有似曾相识的感觉,SpringBoot中大量使用注解来简化开发。现在,使用基于ES6装饰器构建的NestJs框架,你也可以做到! 优缺点先说说优点吧: 完美支持Typescript,因此可以使用日益繁荣的TS生态资源兼容express中间件,降低造轮子成本完美支持响应式编程框架rxjs完美支持依赖注入模块化思想,方便开发以及后期维护使用装饰器简化开发,减少样板代码组件化设计,解决Node.js无全栈框架约束的现存问题当然,"缺点"也是有点的,不过熟练之后这些都不是缺点: 基于TS导致的语言门槛代码设计上对模块化/组件化思想有一定要求第一个NestJs应用使用NestJs的命令行工具,可以简化项目的创建以及项目文件的创建。 npm install -g @nestjs/cli安装命令行工具nest new 项目名称初始化项目初始化完毕后可以看到一个完整的项目结果,目录如下(忽略node_modules): ├── README.md 自述文件├── nest-cli.json NestJs项目配置├── package.json npm文件├── src 项目源码│   ├── app.controller.spec.ts 控制器测试文件│   ├── app.controller.ts 控制器类│   ├── app.module.ts 模块类│   ├── app.service.ts 服务类│   └── main.ts 项目入口文件├── test 测试目录│   ├── app.e2e-spec.ts 应用e2e测试│   └── jest-e2e.json jest e2e测试配置├── tsconfig.build.json 生产环境Typescript所用├── tsconfig.json 开发环境Typescript配置├── tslint.json tslint配置└── yarn.lock yarn锁文件NestJs有几大类文件是主要的是下面几种,其他类型的文件在后续课程会讲解; ...

August 19, 2019 · 1 min · jiezi

想学Nodejsstream先有必要搞清楚

什么是stream定义流的英文stream,流(Stream)是一个抽象的数据接口,Node.js中很多对象都实现了流,流是EventEmitter对象的一个实例,总之它是会冒数据(以 Buffer 为单位),或者能够吸收数据的东西,它的本质就是让数据流动起来。可能看一张图会更直观: 注意:stream不是node.js独有的概念,而是一个操作系统最基本的操作方式,只不过node.js有API支持这种操作方式。linux命令的|就是stream。 为什么要学习stream视频播放例子小伙伴们肯定都在线看过电影,对比定义中的图-水桶管道流转图,source就是服务器端的视频,dest就是你自己的播放器(或者浏览器中的flash和h5 video)。大家想一下,看电影的方式就如同上面的图管道换水一样,一点点从服务端将视频流动到本地播放器,一边流动一边播放,最后流动完了也就播放完了。 说明:视频播放的这个例子,如果我们不使用管道和流动的方式,直接先从服务端加载完视频文件,然后再播放。会造成很多问题 因内存占有太多而导致系统卡顿或者崩溃因为我们的网速 内存 cpu运算速度都是有限的,而且还要有多个程序共享使用,一个视频文件加载完可能有几个g那么大。读取大文件data的例子有一个这样的需求,想要读取大文件data的例子 使用文件读取 const http = require('http');const fs = require('fs');const path = require('path');const server = http.createServer(function (req, res) { const fileName = path.resolve(__dirname, 'data.txt'); fs.readFile(fileName, function (err, data) { res.end(data); });});server.listen(8000);使用文件读取这段代码语法上并没有什么问题,但是如果data.txt文件非常大的话,到了几百M,在响应大量用户并发请求的时候,程序可能会消耗大量的内存,这样可能造成用户连接缓慢的问题。而且并发请求过大的话,服务器内存开销也会很大。这时候我们来看一下用stream实现。 const http = require('http');const fs = require('fs');const path = require('path');const server = http.createServer(function (req, res) { const fileName = path.resolve(__dirname, 'data.txt'); let stream = fs.createReadStream(fileName); // 这一行有改动 stream.pipe(res); // 这一行有改动});server.listen(8000);使用stream就可以不需要把文件全部读取了再返回,而是一边读取一边返回,数据通过管道流动给客户端,真的减轻了服务器的压力。 ...

August 19, 2019 · 2 min · jiezi

vue全家桶框架搭建

1.首先具备一定的前端基础,常规的HTML+CSS+Js/jqeury必须熟练,这是后期开发的基础。2.明白node是什么东西,明白npm能干什么。3.打开cmd窗口,我用的PowerCmd. node -v //检查是否安装nodenpm install webpack -g //webpack是一个包管理工具,也是vue-cli的构建工具,全局安装npm install --global vue-cli //vue-cli的安装,快速搭建项目的工具vue -V //查看vue-cli是否安装成功4.构建工程文件 vue init webpack projectName //'git' 不是内部或外部命令,也不是可运行的程序或批处理文件。下载git解决E:\>vue init webpack fristvue? Project name fristvue//项目名称(注意名称中不要出现大写字母,否则会报错)? Project description A Vue.js project//项目描述(可写可不写,看个人需要)? Author //作者(可写可不写,看个人需要)? Vue build standalone//vue编译,这个选默认即可,运行加编译Runtime + Compiler? Install vue-router? //Yes是否安装vue-router是否安装vue路由工具? Use ESLint to lint your code? //No是否使用代码管理工具ESLint管理你的代码? Set up unit tests //Yes设置单元测试? Pick a test runner //karma选择测试运行程序? Setup e2e tests with Nightwatch? //Yes? Should we run `npm install` for you after the project has been created? //项目创建后,我们应该为您运行“NPM安装”吗?使用cmd命令cd移动到项目根目录 ...

August 18, 2019 · 1 min · jiezi

函数计算在身份认证云中的应用场景

Authing 用来做用户验证,函数计算用来处理具体的逻辑,非常完美的搭配。 越来越多的企业接受身份认证上云后,Authing 也承接了越来越多「用户迁移」需求,在用户迁移的过程中,最重要的一点是「终端用户必须无感知」,而无感知主要有以下两点要求: 用户不需要修改密码;完成切换后,用户的所有数据都能正常访问且具备相应的访问权限;在一个用户系统中,用户的密码通常情况下都会加密存储在数据库中,这个加密算法一般是不可逆的,同时可以由一个函数完成,因此将函数计算应用在身份认证云中就能满足第一个要求。 首先我们看一下什么是函数计算: 通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,并提供日志查询、性能监控、报警等功能。借助于函数计算,您可以快速构建任何类型的应用和服务,无需管理和运维。而且,您只需要为代码实际运行所消耗的资源付费,代码未运行则不产生费用。阿里云 - 函数计算简单来说,函数计算让程序员不需要再管理服务器,只需要上传代码,后台的云就可以帮你自动按需分配资源,这将极大释放程序员的生产力。函数计算非常适合解决身份认证云中自定义密码加密的问题,以下是应用了函数计算后,用户注册的流程图: 以下是用户登录的流程图: 以上流程图是一个简化版,只展示了几个比较核心的步骤。 从开发者的角度来说,我们希望开发者只需要上传一个 index.js 文件就可以自定义密码的加密方式。如果开发者需要引入第三方包,那么仅需要将引入的 node_modules 打包为 .zip 上传即可。上传完成后,Authing 会取得一个可通过 HTTP 调用的网址,此网址不会公布给任何第三方,同时每个网址都会有独特的验证方式,最大限度的保证安全。 在 Authing 控制台中依次点击基础配置 -> 密码管理即可自定义密码加密方法 其他场景除了自定义密码加密外,函数计算可以很好的和 Authing 配合,帮助开发者在后端十分容易的完成用户 Token 的验证。此外,在实际场景下,若开发者接受全部上云,完全可以基于 Authing + 函数计算完成所有业务流程:Authing 用来做用户验证,函数计算用来处理具体的逻辑,非常完美的搭配。 什么是 Authing?Authing 提供专业的身份认证和授权服务。我们为开发者和企业提供用以保证应用程序安全所需的认证模块,这让开发人员无需成为安全专家。你可以将任意平台的应用接入到 Authing(无论是新开发的应用还是老应用都可以),同时你还可以自定义应用程序的登录方式(如:邮箱/密码、短信/验证码、扫码登录等)。 网站:http://authing.cn小登录:https://wxapp.authing.cn/#/仓库:欢迎star,欢迎pr https://gitee.com/Authi_nghttps://github.com/authingDemo: https://sample.authing.cnhttps://github.com/Authing/qr...文档:https://docs.authing.cn/authing/

August 18, 2019 · 1 min · jiezi

前端环境搭建记录Mac版

前言这几天重装了一下Mac的系统,将盘全部抹掉了,所有的环境和工具都需要重新配置,期间发现很多配置都已经忘记了,所以觉得有必要写一篇文章记录一下,方便以后查阅,当然随着以后接触的东西越来越多,还会不断更新内容。 编辑器VSCode 使用的扩展有: Chinese language PackEasy LESSESLintMaterial Icon Themeopen in browserVetur至于ESLint需要在VSCode的配置json文件中写入一下配置: "eslint.validate": [ "javascript", "javascriptreact", { "language": "html", "autoFix": true }, { "language": "vue", "autoFix": true }],"eslint.autoFixOnSave": true,Sublime 使用的扩展有: EmmetConvertToUTF8SideBarEnhancementsNodenvm使用nvm来管理node版本 安装参见我的另一篇文章在Mac下安装nvm管理nodenrm使用nrm来管理npm源,我不使用cnpm来下载包。因为npm和cnpm混用,会出现莫名其妙的错误。 nrm use taobao接口调试Postman使用Postman来进行接口的调试 数据库管理工具Sequel Pro免费、开源、体量小 注意:需要下载官网中More中的Test Builds版本,否则在Mac最新系统中退出时,报意外退出错误本地服务器环境的工具MAMP 注意:在第一次,我们需要在设置中将端口修改为80和3306MarkdownTypora免费、简洁、功能齐全 视频播放IINA免费、开源、Mac播放软件不二之选

August 17, 2019 · 1 min · jiezi

mongodb用户创建mongoose数据库连接

一、用户创建1、创建超级管理员 a.首先开启Mongo服务,然后切换admin数据库 use admin;b.创建 db.createUser({user:"root",pwd:"ceshi123",roles:[{role:"root",db:"admin"}]});c.修改mongodb.conf配置文件添加代码: auth=true如果有以下代码,可修改 noauth=true // 修改为auth=true保存mongodb.conf文件 d.创建超级管理员之后,重新启动mongodb服务 use admin;db.shutdownServer(); //关闭服务exit;下一步要进入到mongodb安装目录下的bin如果是windows系统,可以直接去mongodb/bin目录下打开命令shell,具体操作方法:按住shift+鼠标左键可快速打开shell,输入启动命令: ./mongod -f /usr/local/mongodb/conf/mongodb.conf./mongod如果是linux服务器,可以直接到mongodb/bin目录下 cd /usr/local/mongodb/bin // 小编mongodb的安装目录是/usr/local./mongod -f /usr/local/mongodb/conf/mongodb.conf./mongod经过以上操作,现在创建了操作mongodb的超级管理员;下边我们针对我们要使用的数据库进行创建管理员 2、创建指定数据库管理员a.进入到指定数据库,这里采用testDB mongo //启动mongodbuse admin;db.auth("root","ceshi123");如果shell出现1则为进入mongodb成功,为0进入失败。进入成功后,执行以下: use testDB;db.createUser({user:"admin",pwd:"test123",roles:[{role:"root",db:"admin"}]}); // 这里role的权限一定是root,否则node-express-mongoose会读不到数据创建后可以查看: show users;如果输出以上输入信息,则添加成功。下面我们来看下 mongodb 一共有哪些权限: 1. 数据库用户角色:read、readWrite; 2. 数据库管理角色:dbAdmin、dbOwner、userAdmin; 3. 集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager;4. 备份恢复角色:backup、restore;5. 所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase6. 超级用户角色:root // 这里还有几个角色间接或直接提供了系统超级用户的访问(dbOwner 、userAdmin、userAdminAnyDatabase)7. 内部角色:__systemread:允许用户读取指定数据库readWrite:允许用户读写指定数据库dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profileuserAdmin:允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。root:只在admin数据库中可用。超级账号,超级权限 二、mongoose连接const config = { DB_URL: 'mongodb://admin:test123@127.0.0.1:27017/testDB'};mongoose.connect(config.DB_URL);解释说明:'mongodb://admin:test123@127.0.0.1:27017/testDB'// admin 管理员用户名// test123 管理员密码// testDB 要连接的数据库 参考链接:1、https://blog.csdn.net/zgrbsbf...2、https://blog.csdn.net/szu_lzz...3、https://blog.csdn.net/leihui_...4、https://www.cnblogs.com/leino...5、http://yijiebuyi.com/blog/f9e...

August 17, 2019 · 1 min · jiezi

前端包管理Bower入门教程

Bower了解bower是twitter的一个开源项目,使用nodejs开发,用于web包管理。如果越来越多得开源项目都托管在github上,bower只需要将github上项目加上一个配置文件既可以使用bower方式使用安装包。作为包管理,bower能提供添加新web包,更新web包,删除web包,发布web包功能,管理包依赖。web包通常认为由html+css+javascript构成。 环境依赖bower是依赖于 Node.js的,所以安装之前需要Node环境,Node.js官网 安装最新版本,然后命令行中严重是否安装成功 node -v //版本号查询 我的是v10.16.0安装Bowerbower官网 优点:项目依赖安装,可以固定资源文件,支持资源版本升级,可以支持缓存安装等全局安装bower npm install -g bower //如果你觉得npm安装较慢,可以用淘宝镜像cnpm安装安装完成后,查询版本号 bower -v //如果出现对应的版本号说明安装成功Bower 使用创建文件夹(以我的为例,创建了一个空文件夹bowerDemo),然后命令行到该空文件下 cd bowerDemo初始化bower bower init会提示你输入一些基本信息,根据提示按回车或者空格即可,然后会生成一个bower.json文件,用来保存该项目的配置 插件安装接下来你就可以安装插件了,比如安装下载jquery,在根目录先输入下面命令安装 bower install jquery --save安装成功后你就会在跟文件夹里看到一个bower_components文件夹,这也是插件默认的文件夹 你也可以输入命令查询包的信息,输入命令后会出现包的所以版本,然后你就可以更新安装不同的版本 bower info jquery //包的信息bower update //包的更新bower uninstall jquery //包的卸载这就是一个简单的bower的安装以及插件包的下载 当然,这些仅仅是不够的,作为一个前端开发,有时候会写很多html,css,js页面,甚至每次创建新的项目,都要用同样的模板,所以这个时候,就用到bower的注册功能,把自己的包或者插件发布到bower平台,并下载使用。 bower平台发布插件并下载首先打标签(假设你已经把项目模板发布到github上面),在你的本地仓库根目录下运行下面命令 // -a是添加 标签名,一般写版本号, -m为标签添加注释信息git tag -a 1.0.0 -m “version info″// –tags参数表示提交所有tag至服务器端,普通的git push origin master操作不会推送标签到服务器端git push origin --tags一切准备就绪后,开始注册插件 bower register projectName 项目的github地址 eg:bower register fontend https://github.com/mengnn/fontendGet.git这样,你的插件就注册成功了,你就可以在你的项目根目录下下载你的插件(模板),也就不用每次重复的copy bower install fontend --save-dev //fontend是我注册的插件名称运行成功,你就可以看到你的模板下载到你的项目文件夹下面,just so so! ...

August 17, 2019 · 1 min · jiezi

vscode-调试-帮助你快递开发

前端不只是只能在浏览器调试,现在越来越多单页面,在编辑器里面进行调试将大大提高你的开发效率~~ 1、调试vue 开发环境需要打开 source-map 方便调试 workspaceRoot 为根路径 在 launch.json 中配置 { "version": "0.2.0", "configurations": [ { "type": "chrome", "request": "launch", "name": "vuejs: chrome", "url": "http://localhost:8087", // 8087 为你启动vue项目的端口 "webRoot": "${workspaceFolder}/src", "breakOnLoad": true, "sourceMapPathOverrides": { "webpack:///src/*": "${webRoot}/*", "webpack:///./src/*": "${webRoot}/*" } }, ]}然后按 F5 就会启动一个google 的页面在编辑器里就可以打断点了 2、调试 node 环境下某个 js 文件 { "version": "0.2.0", "configurations": [ // 此为数组可以写多个 { "type": "node", "request": "launch", "name": "Launch Program", "program": "${workspaceRoot}/build/creat.js" }, ]}3、调试 node 后台项目 注意:自己写node时候一般是 node ./bin/www 启动一个服务,调试时候不需要启动直接 按 F5 启动调试即可(会自动启动服务) 否则会导致端口冲突调试失败 ...

August 8, 2019 · 1 min · jiezi

UI2CODE系列文章如何批量制造高质量样本

在 UI2CODE 项目中,我们大量使用了深度学习方法来做一些物体检测。而深度学习模型的训练,避免不了需要大量的样本,因此如何制造大量样本,来满足模型训练需要是我们必须要解决的一个问题。在这篇文章中,我们将介绍我们如何利用工具,批量泛化出大量样本,为模型训练提供数据保障。 1.样本现状我们的模型要解决的问题是在一个设计稿图片上识别出基础控件等信息,包括位置和类别。而它所需要的样本,主要存在两个问题: 数据量少:一个APP的页面是有限的,特别是针对单个APP做优化适配的时候,页面的数量是相对较少的,可能在几十到上百个。而模型的对样本数量的需求是巨大的,特别像较为复杂的模型,对数据量的要求至少是万级别的,单靠真实样本,是远远达不到要求的。标注成本高:物体检测的样本标注,不仅需要标注物体的类别,更需要标注出物体的具体位置,而一个样本上会存在多个物体标注。因此,这类样本打标成本非常大。2.样本获取途径获取样本,主要有几种途径。 对于真实样本,这类质量是最高的,要想训练出效果很好的模型,这类样本基本是必不可少的,但是由于这类样本数量少,成本高,因此还需要其他方法来补充样本量。 对于数据增广,这种方法简单快速,但是效果也有限,特别是对于我们 UI2CODE 里识别控件这个任务来说,做旋转等操作基本是无效的。 因此,我们需要利用样本Mock,来扩充我们的数据量,尽量模拟出质量又多,量又大的样本。这里我们选择的是利用Weex页面来进行样本的Mock泛化。(当然还有一些其它方法,比如利用 Android 的特性,在运行时的APP页面,抓取页面数据,经过过滤和清洗,得到带标注的样本,这里不做展开) 3.WEEX页面样本泛化在这里,我们介绍如何利用 Weex 页面,来批量泛化样本,并且得到样本标注的方法。 前端页面特点之所以选择使用前端页面来生成样本,是因为前端页面更多的是做一些数据展示,并且其拥有完整的 DOM 树,只要我们拿着DOM树就可以解析出里面的各个元素。 对于节点内容,只要我们改变元素内容即可。这样我们就可以由一个前端页面很方便地泛化出不同文字、不同图片的多个样本。 当然,我们的闲鱼APP上有大量的Weex活动页,这也是我们选择做Weex页面泛化的原因之一。 泛化思路我们需要的基础控件的分类有“文本”、“图片”、“Shape”这三类,对于一个页面来说,我们的文本和图片内容基本都是可替换的,因此我们解析出所有节点以后,对里面的文本和图片进行替换,再进行渲染就可以得到新的样本。 利用 Puppeteer 实现泛化要想得到Weex页面,需要有一个渲染容器,并且我们可以很方便地修改其内容。这里,我们选择了Google的Puppeteer,它是Google推出的可以运行 Chrome Headless 环境以及对其进行操控的js接口套装。通过它,我们可以模拟一个Chrome运行环境,并且进行操控。官方简介在这里. 首先启动一个不带界面的浏览器: const browser = await puppeteer.launch({ headless: true});启动一个页面,然后打开一个网站: const page = await browser.newPage();await page.goto(nowUrls, {waitUntil: ['load','domcontentloaded','networkidle0']});模拟IPhone6环境: await page.emulate({ 'name': 'iPhone 6', 'userAgent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', 'viewport': { 'width': 750, 'height': 1334, 'deviceScaleFactor': 1, 'isMobile': true, 'hasTouch': true, 'isLandscape': false }});搜索所需控件: ...

August 8, 2019 · 2 min · jiezi

可靠React组件设计的7个准则之SRP

翻译:刘小夕原文链接:https://dmitripavlutin.com/7-... 原文的篇幅非常长,不过内容太过于吸引我,还是忍不住要翻译出来。此篇文章对编写可重用和可维护的React组件非常有帮助。但因为篇幅实在太长,我不得不进行了分割,本篇文章重点阐述 SRP,即单一职责原则。 ————————————我是一条分割线———————————— 我喜欢React组件式开发方式。你可以将复杂的用户界面分割为一个个组件,利用组件的可重用性和抽象的DOM操作。 基于组件的开发是高效的:一个复杂的系统是由专门的、易于管理的组件构建的。然而,只有设计良好的组件才能确保组合和复用的好处。 尽管应用程序很复杂,但为了满足最后期限和意外变化的需求,你必须不断地走在架构正确性的细线上。你必须将组件分离为专注于单个任务,并经过良好测试。 不幸的是,遵循错误的路径总是更加容易:编写具有许多职责的大型组件、紧密耦合组件、忘记单元测试。这些增加了技术债务,使得修改现有功能或创建新功能变得越来越困难。 编写React应用程序时,我经常问自己:如何正确构造组件?在什么时候,一个大的组件应该拆分成更小的组件?如何设计防止紧密耦合的组件之间的通信?幸运的是,可靠的组件具有共同的特性。让我们来研究这7个有用的标准(本文只阐述 SRP,剩余准则正在途中),并将其详细到案例研究中。 单一职责当一个组件只有一个改变的原因时,它有一个单一的职责。编写React组件时要考虑的基本准则是单一职责原则。单一职责原则(缩写:SRP)要求组件有一个且只有一个变更的原因。 组件的职责可以是呈现列表,或者显示日期选择器,或者发出 HTTP 请求,或者绘制图表,或者延迟加载图像等。你的组件应该只选择一个职责并实现它。当你修改组件实现其职责的方式(例如,更改渲染的列表的数量限制),它有一个更改的原因。 为什么只有一个理由可以改变很重要?因为这样组件的修改隔离并且受控。单一职责原则制了组件的大小,使其集中在一件事情上。集中在一件事情上的组件便于编码、修改、重用和测试。 下面我们来举几个例子实例1:一个组件获取远程数据,相应地,当获取逻辑更改时,它有一个更改的原因。 发生变化的原因是: 修改服务器URL修改响应格式要使用其他HTTP请求库或仅与获取逻辑相关的任何修改。示例2:表组件将数据数组映射到行组件列表,因此在映射逻辑更改时有一个原因需要更改。 发生变化的原因是: 你需要限制渲染行组件的数量(例如,最多显示25行)当没有要显示的项目时,要求显示提示信息“列表为空”或仅与数组到行组件的映射相关的任何修改。你的组件有很多职责吗?如果答案是“是”,则按每个单独的职责将组件分成若干块。 如果您发现SRP有点模糊,请阅读本文。在项目早期阶段编写的单元将经常更改,直到达到发布阶段。这些更改通常要求组件在隔离状态下易于修改:这也是 SRP 的目标。 1.1 多重职责陷阱当一个组件有多个职责时,就会发生一个常见的问题。乍一看,这种做法似乎是无害的,并且工作量较少: 你立即开始编码:无需识别职责并相应地规划结构一个大的组件可以做到这一切:不需要为每个职责创建组成部分无拆分-无开销:无需为拆分组件之间的通信创建 props 和 callbacks这种幼稚的结构在开始时很容易编码。但是随着应用程序的增加和变得复杂,在以后的修改中会出现困难。同时实现多个职责的组件有许多更改的原因。现在出现的主要问题是:出于某种原因更改组件会无意中影响同一组件实现的其它职责。 不要关闭电灯开关,因为它同样作用于电梯。 这种设计很脆弱。意外的副作用是很难预测和控制的。 例如,<ChartAndForm> 同时有两个职责,绘制图表,并处理为该图表提供数据的表单。<ChartandForm> 就会有两个更改原因:绘制图表和处理表单。 当你更改表单字段(例如,将 <input> 修改为 <select> 时,你无意中中断图表的渲染。此外,图表实现是不可重用的,因为它与表单细节耦合在一起。 解决多重责任问题需要将 <ChartAndForm> 分割为两个组件:<Chart> 和<Form>。每个组件只有一个职责:绘制图表或处理表单。组件之间的通信是通过props 实现。 多重责任问题的最坏情况是所谓的上帝组件(上帝对象的类比)。上帝组件倾向于了解并做所有事情。你可能会看到它名为 <Application>、<Manager> 、<Bigcontainer> 或 <Page>,代码超过500行。 在组合的帮助下使其符合SRP,从而分解上帝组件。(组合(composition)是一种通过将各组件联合在一起以创建更大组件的方式。组合是 React 的核心。) 1.2 案例研究:使组件只有一个职责设想一个组件向一个专门的服务器发出 HTTP 请求,以获取当前天气。成功获取数据时,该组件使用响应来展示天气信息: import axios from 'axios';// 问题: 一个组件有多个职责class Weather extends Component { constructor(props) { super(props); this.state = { temperature: 'N/A', windSpeed: 'N/A' }; } render() { const { temperature, windSpeed } = this.state; return ( <div className="weather"> <div>Temperature: {temperature}°C</div> <div>Wind: {windSpeed}km/h</div> </div> ); } componentDidMount() { axios.get('http://weather.com/api').then(function (response) { const { current } = response.data; this.setState({ temperature: current.temperature, windSpeed: current.windSpeed }) }); }}在处理类似的情况时,问问自己:是否必须将组件拆分为更小的组件?通过确定组件可能会如何根据其职责进行更改,可以最好地回答这个问题。 ...

August 8, 2019 · 4 min · jiezi

更快助你弄懂React高阶组件

谈到react,我们第一个想到的应该是组件,在react的眼中可真的是万物皆组件。就连我们获取数据用到的axios也可以用组件来表示...比如,我们可以这样封装 <Request instance={axios.create({})} /* custom instance of axios - optional */ method="" /* get, delete, head, post, put and patch - required */ url="" /* url endpoint to be requested - required */ data={} /* post data - optional */ params={} /* queryString data - optional */ config={} /* axios config - optional */ debounce={200} /* minimum time between requests events - optional */ debounceImmediate={true} /* make the request on the beginning or trailing end of debounce - optional */ isReady={true} /* can make the axios request - optional */ onSuccess={(response)=>{}} /* called on success of axios request - optional */ onLoading={()=>{}} /* called on start of axios request - optional */ onError=(error)=>{} /* called on error of axios request - optional *//>在项目中我们可以这样写 ...

August 7, 2019 · 3 min · jiezi

用键盘8个键演奏一首蒲公英的约定送给996的自己或者一首月亮代表我的心给七夕的她

体验地址: https://wscats.github.io/pian...项目地址: https://github.com/Wscats/piano 用键盘8个键演奏一首蒲公英的约定送给996的自己或月亮代表我的心给七夕的她,非常简单~ 这个项目仅仅用了几个简单的前端技术实现,献给每一位挚爱音乐的代码家???? 如果你喜欢或者对你有帮助,给我点个赞支持下吧???? 技术点和目录结构项目中没有使用市面主流的框架(React,Vue 和 Angular )和热门的技术,而用的是Omi框架(JSX+WebComponents),还有 Omil 的单文件组件 SFCs 加载器,组件通讯基于Proxy特性,并结合了 VScode 的插件 Eno-Snippets基于AST和正则实时编译.eno或.omi 后缀组件减轻部分的 Webpack 的局部编译压力,当然其他同学们熟知的技术这里就不提及了。 src assetselement app-piano songs 钢琴简谱目录app-piano.eno 单文件组件app-piano.js 组件编译后的JS文件notes.js 键盘按键和音符的映射index.js 组件根容器,配置Proxy的通信方法public samples/piano 钢琴单音符素材app-piano.eno开发中你需要编写的单文件组件app-piano.js经过Eno-Snippets修改或者保存文件Hello.eno后经过插件转化的js文件如下图,左边的代码是我们编写的 .eno 后缀的单文件组件,右边是经过 Eno Snippets 生成的 .js 后缀文件。 Develop & Installation<!-- <img src="./public/demo.png"> -->开发,构建和运行。 # 获取远程仓库代码git clone https://github.com/Wscats/piano# 进入目录cd piano# 安装依赖npm install# 启动项目npm start# 在浏览器访问 http://localhost:3000使用 npm 包管理器安装。 npm install omi-piano运行或者发布属于自己的演奏版本。 # 进入目录cd omi-piano# 安装依赖npm install# 启动项目npm start# 发布自已的演奏版本npm run build简单乐理知识首先我们先补习点音乐基础,提前收集好最基本的钢琴单音素材,每个音符对应一份.mp3文件,用一个对象记录起来,类似下面这样,举个例子这里的A指的是CDEFGAB音名中A也就是Sol,这是最基本的乐理,有没有让你想起小时候上音乐课,画板上的五线谱。 ...

August 7, 2019 · 7 min · jiezi

基于koa2的前后端管理系统

koa2koa2框架,mongodb作为数据库,Es6/7语法编写,babel编译ES语法。 增加ts语法支持,进行ing 前后端分离,后台管理系统, Koa后端 系统目前包含 文章发布管理系统、标签系统、评论系统、用户系统,四大模块 技术栈使用koa+mongoose 开发; 使用koa2.0作为开发框架mongoose作为数据库,保存数据使用jwt进行token的生成和校验通过Es6语法进行项目编写文件结构采用MC拆分babel-register编译Es6/7/8esLint语法规则server下为目录结构:.|——server|  |—— config 全局配置| |—— constant 常量|  |   |—— index.js             暴露全部常量| | └── user.js 用户常量|  |—— controller 对应路由的逻辑处理|  |   |—— article.js             文章 控制器 接口| | └── comment.js 评论 控制器 接口| | └── tag.js 标签 控制器 接口| | └── user.js 用户 控制器 接口| |—— middleware 路由中间件|  |—— model mongoose数据库模型| |   |—— ArticleModel.js        文章模型| |   |—— TagModel.js            标签模型| |   └── UserModel.js           用户模型| | └── CommentModel.js 评论模型| |—— mongoose 数据库方法暴露| |—— public 静态资源目录| |—— router 路由文件| |   |—— index.js               路由| |   |—— api.js                 api路由| |   └── user.js                user路由| |—— utils                     公共方法| |—— app.js app入口文件调试运行$ yarn install <!-- 需要开启管理权限设置 -->$ mongod //开启mongoDB$ npm run dev //本地测试服务API接口后端 接口文档 ...

July 26, 2019 · 1 min · jiezi

JavaScript中的算法附10道面试常见算法题解决方法和思路

JavaScript中的算法(附10道面试常见算法题解决方法和思路)关注github每日一道面试题详解 Introduction面试过程通常从最初的电话面试开始,然后是现场面试,检查编程技能和文化契合度。几乎毫无例外,最终的决定因素是还是编码能力。通常上,不仅仅要求能得到正确的答案,更重要的是要有清晰的思维过程。写代码中就像在生活中一样,正确的答案并不总是清晰的,但是好的推理通常就足够了。有效推理的能力预示着学习、适应和进化的潜力。好的工程师一直是在成长的,好的公司总是在创新的。 算法挑战是有用的,因为解决它们的方法不止一种。这为决策的制定和决策的计算提供了可能性。在解决算法问题时,我们应该挑战自己从多个角度来看待问题的定义,然后权衡各种方法的优缺点。通过足够的尝试后,我们甚至可能看到一个普遍的真理:不存在“完美”的解决方案。 要真正掌握算法,就必须了解它们与数据结构的关系。数据结构和算法就像阴阳、水杯和水一样密不可分。没有杯子,水就不能被容纳。没有数据结构,我们就没有对象来应用逻辑。没有水,杯子是空的,没有营养。没有算法,对象就不能被转换或“消费”。 要了解和分析JavaScript中的数据结构,请看JavaScript中的数据结构 Primer在JavaScript中,算法只是一个函数,它将某个确定的数据结构输入转换为某个确定的数据结构输出。函数内部的逻辑决定了怎么转换。首先,输入和输出应该清楚地提前定义。这需要我们充分理解手上的问题,因为对问题的全面分析可以很自然地提出解决方案,而不需要编写任何代码。 一旦完全理解了问题,就可以开始对解决方案进行思考,需要那些变量? 有几种循环? 有那些JavaScript内置方法可以提供帮助?需要考虑那些边缘情况?复杂或者重复的逻辑会导致代码十分的难以阅读和理解,可以考虑能否提出抽象成多个函数?一个算法通常上需要可扩展的。随着输入size的增加,函数将如何执行? 是否应该有某种缓存机制吗? 通常上,需要牺牲内存优化(空间)来换取性能提升(时间)。 为了使问题更加具体,画图表!当解决方案的具体结构开始出现时,伪代码就可以开始了。为了给面试官留下深刻印象,请提前寻找重构和重用代码的机会。有时,行为相似的函数可以组合成一个更通用的函数,该函数接受一个额外的参数。其他时候,函数柯里的效果更好。保证函数功能的纯粹方便测试和维护也是非常重要的。换句话说,在做出解决问题的决策时需要考虑到架构和设计模式。 Big O(复杂度)为了计算出算法运行时的复杂性,我们需要将算法的输入大小外推到无穷大,从而近似得出算法的复杂度。最优算法有一个恒定的时间复杂度和空间复杂度。这意味着它不关心输入的数量增长多少,其次是对数时间复杂度或空间复杂度,然后是线性、二次和指数。最糟糕的是阶乘时间复杂度或空间复杂度。算法复杂度可用以下符号表示: Constabt: O(1)Logarithmic: O(log n)Linear: O(n)Linearithmic: O(n log n)Quadratic: O(n^2)Expontential: O(2^n)Factorial: O(n!) 在设计算法的结构和逻辑时,时间复杂度和空间复杂度的优化和权衡是一个重要的步骤。 Arrays一个最优的算法通常上会利用语言里固有的标准对象实现。可以说,在计算机科学中最重要的是数组。在JavaScript中,没有其他对象比数组拥有更多的实用方法。值得记住的数组方法有:sort、reverse、slice和splice。数组元素从第0个索引开始插入,所以最后一个元素的索引是 array.length-1。数组在push元素有很好的性能,但是在数组中间插入,删除和查找元素上性能却不是很优,JavaScript中的数组的大小是可以动态增长的。 数组的各种操作复杂度Push: O(1)Insert: O(n)Delet: O(n)Searching: O(n)Optimized Searching: O(log n)Map 和 Set是和数组相似的数据结构。set中的元素都是不重复的,在map中,每个Item由键和值组成。当然,对象也可以用来存储键值对,但是键必须是字符串。 Iterations与数组密切相关的是使用循环遍历它们。在JavaScript中,有5种最常用的遍历方法,使用最多的是for循环,for循环可以用任何顺序遍历数组的索引。如果无法确定迭代的次数,我们可以使用while和do while循环,直到满足某个条件。对于任何Object, 我们可以使用 for in 和 for of循环遍历它的keys 和values。为了同时获取key和value我们可以使用 entries()。我们也可以在任何时候使用break语句终止循环,或者使用continue语句跳出本次循环进入下一次循环。 原生数组提供了如下迭代方法:indexOf,lastIndexOf,includes,fill,join。 另外我们可以提供一个回调函数在如下方法中:findIndex,find,filter,forEach,map,some,every,reduce。 Recursions在一篇开创性的论文中,Church-Turing论文证明了任何迭代函数都可以用递归函数来复制,反之亦然。有时候,递归方法更简洁、更清晰、更优雅。以这个迭代阶乘函数为例: const factorial = number => { let product = 1 for (let i = 2; i <= number; i++) { product *= i } return product}如果使用递归,仅仅需要一行代码 ...

July 26, 2019 · 4 min · jiezi

nodejs获取控制台中命令行参数

我应该如何获取到 viewportWidth=750 这个参数? postcss style.css -r viewportWidth=750场景需要公司之前的项目使用的是*.less样式文件,然后再浏览器直接引入; 使用less.js文件在浏览器中解析运行;如何在浏览器中使用less 引入*.less和less.js文件<link rel="stylesheet/less" type="text/css" href="styles.less" /><script src="less.js"></script>编译less在前端工程化今天,我选择使用打包编译的方式对less文件进行编译,然后使用postcss压缩把之前项目中 3000行代码200kb使用cssnano进行压缩,使其60kb; 今天分享的是如何获取控制台中命令行中的变量; const arguments = process.argv.reduce((a, b, index) => { if (/^[^=]*=[^=]*$/.test(b)) { const arr = b.split('=') a[arr[0]] = arr[1] } return a}, {})console.log(arguments);这样我就可以获取到所有命令行中的参数了; 在后面的代码中我可以这样去使用 // ... minPixelValue: arguments.minPixelValue || 2 viewportWidth: arguments.viewportWidth || 1024 // ....

July 26, 2019 · 1 min · jiezi

快速实现以深度链接为基础的闭环分享功能

什么是闭环分享 现在,市场上主流app都具有分享功能。但是绝大部分app的分享功能仅仅局限于分享出去,而且在微信限制了开发者获取分享结果状态后,通过分享功能进行app或者业务推广的方式受到很大的限制。比如说: 1.你不知道用户是否真的分享了 2.不知道分享的链接传播效果怎么样,多少人点击 3.不知道如何有效的引导用户到App,转化沉淀成自己的客户 ShareSDK从这几个问题入手,基于深度链接打造出闭环分享功能,从而真正意义上解决以上烦恼。使用体验如下:快速实现以深度链接为基础的闭环分享功能快速实现流程 一、注册、登陆开发者后台 打开 Mob官网 点击右上角登陆或注册 成功登陆后,点击进入 “产品中心” ,如下图: 快速实现以深度链接为基础的闭环分享功能在 “开发者服务” --> “开发者平台” 栏目,点击 “立即进入” ,进入之后如下图: 快速实现以深度链接为基础的闭环分享功能二、下载SDK包 先进入Mob官网 SDK下载页 ,iOS开发者请从官网下载,如下图: 快速实现以深度链接为基础的闭环分享功能保存配置后即可点击 “下载” ,如下图: 快速实现以深度链接为基础的闭环分享功能安卓请使用Gradle在线集成,先选择配置,再使用Gradle脚本,如下图: 快速实现以深度链接为基础的闭环分享功能快速实现以深度链接为基础的闭环分享功能三、开发者后台配置 如果您尚未使用ShareSDK,请点击 “添加应用” ,输入您的 “应用名称” ,点击 “保存”,如下图: 快速实现以深度链接为基础的闭环分享功能然后在左边栏点击 “ShareSDK” ,在弹出框中选择 “确定添加” ,如下图: 快速实现以深度链接为基础的闭环分享功能如果您已经使用了ShareSDK,请选择您的应用并打开ShareSDK边栏,点击 “闭环分享” 标签,如下图: 快速实现以深度链接为基础的闭环分享功能图上有对闭环分享功能的简单介绍和相关集成文档,直接点击 “立即体验” 即可开始使用,点击后如下图: 快速实现以深度链接为基础的闭环分享功能对于iOS开发者,请配置好 “TeamID” 、 “Bundle ID” 和 “下载地址” ,然后点击 “保存设置” ,如下图: 快速实现以深度链接为基础的闭环分享功能图上系统自动生成的 “Scheme” 和 “Association Domain” 是用于Xcode项目配置的,请参考下文: 五、iOS客户端开发配置 ...

July 26, 2019 · 2 min · jiezi

expressautoloadrouter源码分析

模块介绍express-autoload-router模块用于自动加载并注册路由。 模块使用基本用法基本使用步骤如下。 安装模块$ npm install express-autoload-router构建路由程序将路由程序文件放在专门的目录app/controllers下面。格式上有两点需要注意: 路由程序文件名称必须为xxx_controller.js;路由程序中的action函数/对象名称必须为yyyAction;$ cat app/controllers/user_controller.js/** * 对象方式,多个路由放在一起 */module.exports = { listAction: { method: ["GET"], middlewares: [], handler: function(req, res) { return res.send("user list action"); } }, getAction: { method: ["GET"], middlewares: [], handler: function(req, res) { return res.send("user get action"); } }};在主程序中引入并加载路由$ cat app.jsconst express = require("express");const path = require('path');const loadRouter = require('express-autoload-router');const app = express();loadRouter(app, "/demo", path.join(__dirname, "app/controllers"));app.listen(3000, function() { console.log("Listening on port 3000!");});其中,loadRouter()函数的第二个参数指定一级路由,第二个参数指定路由程序文件所在的目录。 ...

July 16, 2019 · 1 min · jiezi

浅谈http协议二cookie的历史由来与实际应用

HTTP协议当初为了让协议尽量简洁,制定为无状态协议,即指每次request请求之前是相互独立的,当前请求并不会记录它的上一次请求信息。那么问题来了,开发中经常需要用到状态记录,比如日常的登录网站,不可能每次登录都要客户重新输入密码,这样用户体验肯定会很差,那如何让无状态的http协议将状态记录下来呢?于是浏览器厂商发明了cookie来解决这个难题。 Cookie总是保存在客户端中,按在客户端中的存储位置,可分为内存Cookie和硬盘Cookie,设置了过期时间Expires(Cookie的几个属性之一)的Cookie会存储在硬盘里面,不同操作系统cookie的储存路径会有所不同,而没有设置该属性的Cookie会存储在内存里面,此时浏览器的选项卡可以共享同一个cookie信息,关闭浏览器就会清除该Cookie!首先看一下cookie在前后端交互中的应用: 第一次验证成功后,服务端会在响应头信息中带上“set-cookie”字段,浏览器监测到该字段之后,会把其中的值对号入座写入浏览器的cookie的属性中 存储在cookie中的数据,在它过期之前,每次都会被浏览器自动放在http请求中,如果这些数据并不是每个请求都需要发给服务端的数据,浏览器这设置自动处理无疑增加了网络开销;但如果这些数据是每个请求都需要发给服务端的数据(比如身份认证信息),浏览器这设置自动处理就大大免去了重复添加操作。所以对于那设置“每次请求都要携带的信息(最典型的就是身份认证信息token)”就特别适合放在cookie中。而服务器端通过验证cookie中的信息,实现了验证,也让浏览器端省去了每次都需要输入登录信息的麻烦。 为了实现cookie的作用,除了name和value之外,设计者还给它增加了七个属性,分别是expires/max-age、domain、path、secure、HttpOnly、samesite,这些属性是通过cookie选项来设置的,在设置任一个cookie时都可以设置相关的这些属性,当然也可以不设置,这时会使用这些属性的默认值。 Expires和Max-Ageexpires选项用来设置“cookie 什么时间内有效”。expires其实是cookie失效日期,expires必须是 GMT 格式的时间(可以通过new Date().toGMTString()或者 new Date().toUTCString() 来获得)。如expires=Thu, 25 Feb 2016 04:18:00 GMT表示cookie讲在2016年2月25日4:18分之后失效,对于失效的cookie浏览器会清空。如果没有设置该选项,则默认有效期为session,即会话cookie。这种cookie在浏览器关闭后就没有了,属于内存cookie。而max-age就是cookie写入之后的有效时长,比如max-age=600就是600秒后,cookie则会失效。max-age的优先级高于expires domain 和 pathdomain是域名,path是路径,两者加起来就构成了 URL,domain和path一起来限制 cookie 能被哪些 URL 访问,domain属性的默认值是创建cookie的网页所在服务器的主机名。不能将一个cookie的域设置成服务器所在的域之外的域,要想cookie在多个二级域名中共享,需要设置domain为顶级域名。 HttpOnly告知浏览器不允许通过脚本document.cookie去更改这个值,同样这个值在document.cookie中也不可见。但在http请求张仍然会携带这个cookie。注意这个值虽然在脚本中不可获取,但仍然在浏览器安装目录中以文件形式存在。 secure安全标志,指定后,只有在使用SSL链接(https)时候才能发送到服务器,如果是http链接则不会传递该信息。就算设置了secure 属性也并不代表他人不能看到你机器本地保存的 cookie 信息,所以不要把重要信息放在cookie中。 除了服务器端返回的set-cookie字段,浏览器端也可以对cookie进行操作,有npm为我们封装好的插件js-cookie: https://www.npmjs.com/package...,不过我们还是用原生方法手动实现一遍 获取cookie:在控制台输入document.cookie,或者js代码中console.log(document.cookie)可以得到下图:可以看到,cookie是一个个键值对(“键=值”的形式)加上分号空格隔开组合而成, 形如: "name1=value1; name2=value2; name3=value3",可以用正则表达式来提取等号后面的值 function getCookie(name) { var value = '; '+ document.cookie; var parts = value.split('; ' + name + '='); if(parts.length === 2) { return parts.pop().split(';').shift(); }}写入cookie function setCookie (name, value, day) { if(day !== 0){ //当设置的时间等于0时,不设置expires属性,cookie在浏览器关闭后删除 var expires = day * 24 * 60 * 60 * 1000 var date = new Date(+new Date()+expires); document.cookie = name + "=" + escape(value) + ";expires=" + date.toUTCString() }else{ document.cookie = name + "=" + escape(value) }}注意:expires使用GMT或UTC格式的时间, 我这里没有指定路径(path)和域(domain), 当没有指定路径时默认为当前路径下,如对 于“https://home.cnblogs.com/u/ma...”下设置的cookie,其path为"/u/maderlzp", 其domain为当前域名“home.cnblogs.com” ...

July 16, 2019 · 1 min · jiezi

TypeScript最佳实践是否使用strictnullcheck

原文2017年7月发布并于2017年9月更新strictnullcheck(严格的null检查) 我应该使用strictnullcheck TypeScript编译器标志吗?空指针是最常见的bug之一,而通过strictnullcheck TypeScript编译器标志可以在很大程度上避免空指针。因为strictnullcheck标志在TypeScript 2时添加的,所以它的使用还没有那么广泛。截至2017年9月,Angular项目和typeORM项目中使用了该标志,而VSCode、RxJS、ionor或Babylon.js都没有使用该标志。此外,新建一个TypeScript项目时strictnullcheck并不默认开启,以保证向后兼容,并保持TypeScript是JavaScript的超集。 如果你准备编写一个新TypeScript项目,或者有时间将strictnullcheck标志引入到现有的项目中,我建议你这样做。你的应用会因此具备更高的安全性,使用严格的null检查也不会打乱代码,因应用程序本应包含这些检查。缺点是新开发人员还需要学习一个概念。对我来说,利大于弊,所以我建议打开严格的空检查。 严格的空检查的一个例子是: tsconfig.json { "compilerOptions": { "module": "commonjs", "target": "es5", "noImplicitAny": true, "strictNullChecks": true, "outDir": "./dist" }, "include": [ "src/**/*" ]}src/user.ts interface User { name: string; age?: number;}function printUserInfo(user: User) { console.log(`${user.name}, ${user.age.toString()}`) // => error TS2532: Object is possibly 'undefined'. console.log(`${user.name}, ${user.age!.toString()}`) // => OK, you confirm that you're sure user.age is non-null. // => 好的,你已经确认user.age是非空的。 if (user.age != null) { console.log(`${user.name}, ${user.age.toString()}`) } // => OK, the if-condition checked that user.age is non-null. // => 好的,if条件检查了user.age是非空的。 console.log(user.name + ', ' + user.age != null ? user.age.toString() : 'age unknown'); // => Unfortunately TypeScript can't infer that age is non-null here. // => 不幸的是TypeScript不能在这里推断年龄是非空的。(译注:截止至2019年7月16日,TS依旧会报此错)}如上所述: ...

July 16, 2019 · 1 min · jiezi

vue-hls-循环-实时监控视频m3u8格式列表

默认vue已经安装好了,UI组件这里以vux为例,用什么UI库问题不大注:循环这种实时视频的做法其实不推荐,但是你保不准,真的有这样的需要 1. 安装依赖 hls.jsnpm i hls.js -S 2. 使用2.1 html 循环渲染video节点<div v-for="video in videos" :key="video.ref" class="videoList"> <p> <span>XX监控</span> <span>通道{{video.which}}</span> <span><x-button @click.native="playVideo(video.which)" mini>播放</x-button><span> </p> <div class="videoContainer"> <video :ref='video.ref'></video> </div></div>2.2 【js】hls挂载节点,解析// 结构略import Hls from 'hls.js';data() { return { videos: [] }},methods: { // 节点挂载---$refs attach() { for (let index = 0; index < this.videos.length; index++) { if (Hls.isSupported()) { this.videos[index].hls = new Hls(); this.videos[index].hls.attachMedia(this.$refs[this.videos[index].ref][0]); } } }, // 播放实时监控 playVideo(channel) { let _this = this; let videoRef = this.videos[channel-2].ref; this.$refs[videoRef][0].controls = 'controls'; // 请求和心跳等涉及业务的略 _this.videos[channel-2].hls.loadSource(res.data.url); // 正常解析 _this.videos[channel-2].hls.on(Hls.Events.MANIFEST_PARSED, function () { _this.$refs[videoRef][0].play() }); // 失败 _this.videos[channel-2].hls.on(Hls.Events.ERROR, function () { 根据业务对失败情况进行分别处理 } }}// 选择生命周期(需要$el已经存在,mounted()或者keep-alive的activated())// 我这里使用的是activated()activated(){ // axios 请求略(这里演示用固定数量,通道从2开始) this.videos = []; for (let i = 0; i < 5; i++) { let item = { hls: null, ref: `video${i+2}`, which: i+2, } this.videos.push(item) this.$nextTick(() => { this.attach() }) }}// 销毁deactivated() { for (let i = 0; i < this.videos.length; i++) { this.videos[i].hls.destroy(); }}

July 16, 2019 · 1 min · jiezi

使用nodejs实现socks5协议

本文出处https://shenyifengtk.github.io/如有转载,请说明出处socks5 介绍socks5s是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。当防火墙后的客户端要访问外部的服务器时,就跟SOCKS代理服务器连接。这个代理服务器控制客户端访问外网的资格,允许的话,就将客户端的请求发往外部的服务器。 根据OSI模型,SOCKS是会话层的协议,位于表示层与传输层之间,也就是说socks是在TCP 之上的协议。 和HTTP代理相比HTTP代理只能代理http请求,像TCP、HTTPS这些协议显得很无力,有一定的局限性。SOCKS工作在比HTTP代理更低的层次:SOCKS使用握手协议来通知代理软件其客户端试图进行的连接SOCKS,然后尽可能透明地进行操作,而常规代理可能会解释和>重写报头(例如,使用另一种底层协议,例如FTP;然而,HTTP代理只是将HTTP请求转发到所需的HTTP服务器)。虽然HTTP代理有不同的使用模式,CONNECT方法允许转发TCP连接;然而,SOCKS代理还可以转发UDP流量和反向代理,而HTTP代理不能。HTTP代理通常更了解HTTP协议,执行更高层次的过滤(虽然通常只用于GET和POST方法,而不用于CONNECT方法)。 SOCKS协议内容官方协议RFC选择认证方法大体说下socks连接过程,首先客户端发送一个数据包到socks代理 VarNMETHODSMETHODS110-255表格里面的单位表示位数 Var 表示是SOCK版本,应该是5;NMETHODS 表示 METHODS部分的长度METHODS 表示支持客户端支持的认证方式列表,每个方法占1字节。当前的定义是 0x00 不需要认证0x01 GSSAPI0x02 用户名、密码认证0x03 - 0x7F由IANA分配(保留)0x80 - 0xFE为私人方法保留0xFF 无可接受的方法服务器会响应给客户端 VERMETHOD11Var 表示是SOCK版本,应该是5;METHOD是服务端选中方法,这个的值为上面METHODS 列表中一个。如果客户端支持0x00,0x01,0x02,这三个方法。服务器只会选中一个认证方法返回给客户端,如果返回0xFF表示没有一个认证方法被选中,客户端需要关闭连接。我们先用一个简单Nodejs在实现sock连接握手.查看客户端发送数据报 const net = require('net');let server = net.createServer(sock =>{sock.once('data', (data)=>{console.log(data);});});server.listen(8888,'localhost');使用curl工具连接nodejs curl -x socks5://localhost:8888 https://www.baidu.comconsole输出 <Buffer 05 02 00 01>使用账号密码认证当服务器选择0x02 账号密码方式认证后,客户端开始发送账号 、密码,数据包格式如下: (以字节为单位) VERULENUNAMEPLENPASSWD111 to 25511 to 255VER是SOCKS版本ULEN 用户名长度UNAME 账号stringPLEN 密码长度PASSWD 密码string可以看出账号密码都是明文传输,非常地不安全。服务器端校验完成后,会响应以下数据(): VERSTATUS11STATUS 0x00 表示成功,0x01 表示失败封装请求认证结束后客户端就可以发送请求信息。客户端开始封装请求信息SOCKS5请求格式(以字节为单位): VERCMDRSVATYPDST.ADDRDST.PORT110x001动态2VER是SOCKS版本,这里应该是0x05;CMD是SOCK的命令码 0x01表示CONNECT请求 CONNECT请求可以开启一个客户端与所请求资源之间的双向沟通的通道。它可以用来创建隧道(tunnel)。例如,CONNECT 可以用来访问采用了 SSL is a standard protocol that ensures communication sent between two computer applications is private and secure (cannot be read nor changed by outside observers). It is the foundation for the TLS protocol.") (HTTPS is an encrypted version of the HTTP protocol. It usually uses SSL or TLS to encrypt all communication between a client and a server. This secure connection allows clients to safely exchange sensitive data with a server, for example for banking activities or online shopping.")) 协议的站点。客户端要求代理服务器将 TCP 连接作为通往目的主机隧道。之后该服务器会代替客户端与目的主机建立连接。连接建立好之后,代理服务器会面向客户端发送或接收 TCP 消息流。0x02表示BIND请求 ...

July 16, 2019 · 4 min · jiezi

一次被动的前端面试经历

2018年,变动的一年,随着P2P的雷声不断轰鸣,各企业纷纷开始裁员过冬,“开猿节流”一词正式被创造,很不幸,笔者所在的金融公司也不得不进入“冬眠”,而我也在农历新年前变成了求职大军中的一员。 因为不想回家过年时连工作的没有,然后焦虑的心情每日缠绕于身,压力愈来愈大,随之下决心一定要在年前拿到offer,就为回家过年也能图个轻松。看了看招聘的一些网站,年前的招人的公司也少的可怜,开始主动在各个渠道投递简历,也主动找人内推,大概经历了一个月之久,最终黄天不负有心人,拿到还算心仪的公司offer,也是平时一直在心底视之为目标默默努力的公司。 开始新工作也过去了一段时间了,现在回想,也是感慨,塞翁失马焉知非福呢。今天特地整理了一下年初面试过的几个公司的相关的题目,给需要使用的同学参考一下,也告诫大家一定要有目标,有了目标后便有了力量,便有了恒心决心为之努力! 下面整理了一下面试的几家公司的面试题,大家有需要的看看,有问题一起探讨! 一. 某在线教育公司 职位: 全栈工程师 简述: 一共一下午,两面现场技术。 第一面比较年轻的一个小哥,给人一看就是大佬的感觉,上来看了一下简历,开始做了自我介绍后,进入提问环节。 说一下对bind,call,apply三个函数的认识,自己实现一下bind方法。前端的requestAnimationFrame了解吗?有使用过吗?说一下使用场景。对前端性能优化有什么了解?有在之前的工作中解决过性能问题么?一般都通过那几个方面去优化的?对前后端跨域可以说一下吗?有碰到过跨域问题吗?如何解决跨域的?介绍一下对nodejs的异步IO原理。一道简单的算法题,实现输入一个字符串,返回字符串翻转输出。 第二面进来气场就不一样,年纪差不多35以上,自我介绍了一下,便开始提问环节。 说一下之前应用服务的部署模式。对nodejs的进程维护有了解过么?你们有遇到过nodejs一些内存泄露方面的问题吗?说一下内存泄露。之前用redis做缓存中间件是为了解决什么问题?说一下你们关于redis的设计架构。说一下你做过最有成长的一个项目,简单总结一下。讲一下你对之前项目里使用的消息中间件的理解,为什么引入这个东西,它解决了什么问题。他开始介绍他们团队做的事。 后面就是HR面试了,两个HR问了一些情况后,留了微信便离开了,公司比较偏,找了半天公交站,坐到地铁站,因为心事重重,结果地铁还给坐反了,回家已经晚上11点多。 二. 某创业公司 因为我是有点想加入创业团队的,想着自己还年轻,还能加班,况且万一运气好蹭个大佬,从此坐上开挂的列车,走上人生巅峰呢!便去了一个创业公司面试。 职位:全栈工程师 简述:中午12:30去面试,不理解为什么在正中午面,大概是为了考验候选人?我去的时候面试大佬正好出来扔外卖盒,看到我背个包喊出了我的名字,心想"我去,还没面试,大佬这就记住我了?so excited",因为公司就三个人,而且面我的大佬还是其他公司在职的,只是偶尔会过来,所以一共一面技术。 第一面上来就是做题,心态完全没有调整过来,贼紧张。 手写一下快速排序算法。看你简历有加密算法的内容,简单讲一下关于加密算法相关的内容。说一下https的工作原理,里面涉及到的加密算法都有哪些?设计一个后台管理系统,从数据库表设计到后端服务提供。一道算法题,一个数组中找出所有相同的元素,并且做出分类,在彩笔白板上写完,面试官笑了一下,js写还真方便。 因为没有hr,大佬跟我谈了薪资,并且告诉我他们现在已经在盈利了,年终可以分很多... 三. 某数据广告公司(500人左右) 在这里是我遇到真正最难的一次面试,被大佬虐的体无完肤,最后还是拿到公司的offer。首先面试的是公司的类似于“基础建设”的部门,应该是只搞技术不做业务的部门,但是需要的比较资深的nodejs大牛,最终不合适,但是大佬推荐到了一个业务部门,这边的又经历了两次面试,最终拿到offer,一共经历了三轮技术面 职位:全栈工程师 第一面未面试通过,简历被推荐到另一个部门。 说一下nodejs里对Buffer数据类型的认识,对于初始化的Buffer,可以实现增加长度吗?对nodejs的异步IO的认识,异步IO内部的工作原理,以及内部线程池相关内容。说一说Linux的几种IO模型,分别描述一下是怎么一回事。多进程部署的Nodejs应用有何优缺点,简述一下进程之间的通信方式。TCP三次握手四次挥手的具体细节。Nodejs源码层面的一些内容。第二面 说一下Vue和React的认识,做一个简单的对比。React的Dom的diff算法描述一下。浏览器cookie和session的认识。跨域分哪几种类型,如何解决各个跨域的问题。nodejs的setTimeOut不准时的原因分析。 第三面 nodejs进程间通信方式。nodejs高并发怎么理解?为什么不适合运算量大的操作?如果我要用实现运算量大的操作有什么方式?redis缓存系统的相关内容。在之前工作中做的项目中有收获的,系统描述一下收获了什么?介绍了一些他们使用的技术栈和正在做的事情。 HR面试也给到了非常不错的薪资,心里觉得先进去,后面再找机会进一面的大佬的团队,随着有了两个offer心情也逐渐放松了。 四. TMD某条大厂 大厂就是大厂,一来就给发个牌带上,面试官很守时也很客气,我被带到了一个会议室,开始了面试之旅,一共三面技术面。 职业:全栈工程师 第一面首先上来给了一套题,两大业,我拿到后,翻看了一下,汗水直流啊!一边擦汗一边做!题目做完开始按照做的题提问环节。 关于css3里阴影的一些知识。div布局相关的知识,涉及双翼齐飞,盒模型等。javascript同步异步的输出顺序问题。关于Promise的then,catch,reject,all,race一些api的用法问题。一道关于动态规划的算法题。一道概率论关于摇硬币正反面概率的问题。问完后,接下来又做了一套题... 这一套关于nodejs的,主要涉及流(stream)与Buffer,事件触发器(EventEmitter)等相关模块的认识与使用。这一套基本满分通过,因为之前专门学习过这几个模块,接下来进入二面。 第二面面试官是一个漂亮的妹子,但是非常犀利,上来做了自我介绍后,直接进入正题。 可以手写一些Promise么?不是写Promise怎么用哦,让你实现一下Promise。^O^nodejs中的异步回调中的错误怎么处理。闭包为什么会造成内存泄漏?javascript的垃圾回收机制讲一下。了解express的内部原理么?简单实现一下。写一下希尔排序算法,注意空间和时间复杂度。第三面由于三面面试官出差,中间隔了一周多。后来约到面试后,一位非常年轻的小哥,大概刚30左右的样子,很儒雅客气。 从页面输入一个链接到加载成功过程中发生了什么,尽可能详细。https相关原理,涉及中间人攻击,证书协议,加解密内容。nodejs的运行原理,有哪些优缺点?对nodejs怎样的看法?之前项目中的一些总结。聊了一些关于他们正在做的事。 面试体验很好,HR也非常nice,拿到了满意的offer,最终因为其他原因没有去这边,HR和三面面试官也非常客气的跟我说,后面想去还可以联系他,非常满意的面试体验,为头条打波广告。 五. 某金融巨头 说句老实话,面试其实不是非常难,而是简历太不容易能被捞起来,而且我是一年半的,团队基本招3年起步的,我是通过内推渠道才被推到团队的,后来进来发现,这简历是真不容易能被捞起来,全程一共两面技术。 职位:前端/Nodejs工程师 第一面 前端模块化,使用过的打包工具有哪些,打包原理,关于webpack多一些。简历有做过断点续传的一些内容,问了一些断点续传在实现方面的一些内容。cookie与session原理,还有token相关的一些内容。React的使用经验,react-router的内部原理解释。XSS,CSRF攻击过程,前端怎么去防止这类攻击。nodejs的事件循环怎么理解?事件循环里各个阶段的认识。 第二面 Nodejs的内部运行机制,异步非阻塞IO与事件循环原理,事件循环中的每个阶段描述。前端数据流管理工具用过哪些?解释一下这数据流管理工具出现的原因,解决的问题和它的本质原理。redux这一类的工具在解决什么问题,它的本质原理详述。在使用开源框架的时候有没有发现一些坑,如何去处理这些坑的。做过的所有项目简单梳理一遍,有哪些收获和心得描述一下。

July 16, 2019 · 1 min · jiezi

局域网-pm2-离线安装

大家习惯了公网下边安装pm2,速度还是蛮快的,也不用去担心安装后是否可以正常使用,按照网上大部分方法安装都可正常使用;但是局域网下安装确实少见,但是有些特殊情况下还需要本地部署。针对无公网本地怎么部署到服务器,记录一下我的部署过程。 1、 首先在公网下正常安装pm2,这就很简单了。npm install pm2 -g //全局安装2、 将pm2打包下载到本地A、到安装好的文件包找到pm2, 路径 node/lib/node_modules/ //你会看到安装的文件 pm2B、打包 tar czvf pm2.tar.gz pm2/ //将pm2打包到node/lib/node_modules/下C、下载到本地 到此准备工作完成,下一步是部署到局域网服务器。 3、部署到局域网服务器首先安装node到目录/usr/local/下,具体方法这里不再过多讲述。下边重点来了。A、将打包文件上传服务器目录/usr/local/node/lib/node_modules/目录下,可以看到npm文件包,你就放置对了。 B、解压压缩包 tar xvf pm2.tar.gz到这一步你可以尝试使用一下pm2命令/usr/local/node/lib/node_modules/pm2/bin/pm2 C、使用总不能每次搞大串代码来启东程序吧,太不人性化,添加一个链接到服务区全局环境中,方法: 第一步,添加到node环境中 /usr/local/node/bin目录下: ln -s /usr/local/node/lib/node_modules/pm2/bin/pm2 /usr/local/node/bin/pm2ln -s /usr/local/node/lib/node_modules/pm2/bin/pm2-dev /usr/local/node/bin/pm2-devln -s /usr/local/node/lib/node_modules/pm2/bin/pm2-docker /usr/local/node/bin/pm2-dockerln -s /usr/local/node/lib/node_modules/pm2/bin/pm2-runtime /usr/local/node/bin/pm2-runtime第二步:添加到linux全局环境下, 也就是/usr/local/bin目录下 ln -s /usr/local/node/bin/pm2 /usr/local/bin/pm2测试一下,直接输入pm2,出现以下场景,可以宣布大功告成,祝贺一下。 (原创文章,装在注明出处,谢谢合作)

July 16, 2019 · 1 min · jiezi

JavaScript深入浅出第4课V8引擎是如何工作的

摘要: 性能彪悍的V8引擎。 《JavaScript深入浅出》系列: JavaScript深入浅出第1课:箭头函数中的this究竟是什么鬼?JavaScript深入浅出第2课:函数是一等公民是什么意思呢?JavaScript深入浅出第3课:什么是垃圾回收算法?JavaScript深入浅出第4课:V8是如何工作的?最近,JavaScript生态系统又多了2个非常硬核的项目。 大神Fabrice Bellard发布了一个新的JS引擎QuickJS,可以将JavaScript源码转换为C语言代码,然后再使用系统编译器(gcc或者clang)生成可执行文件。 Facebook为React Native开发了新的JS引擎Hermes,用于优化安卓端的性能。它可以在构建APP的时候将JavaScript源码编译为Bytecode,从而减少APK大小、减少内存使用,提高APP启动速度。 作为JavaScript程序员,只有极少数人有机会和能力去实现一个JS引擎,但是理解JS引擎还是很有必要的。本文将介绍一下V8引擎的原理,希望可以给大家一些帮助。 JavaScript引擎我们写的JavaScript代码直接交给浏览器或者Node执行时,底层的CPU是不认识的,也没法执行。CPU只认识自己的指令集,指令集对应的是汇编代码。写汇编代码是一件很痛苦的事情,比如,我们要计算N阶乘的话,只需要7行的递归函数: function factorial(N) { if (N === 1) { return 1; } else { return N * factorial(N - 1); }}代码逻辑也非常清晰,与阶乘数的学定义完美吻合,哪怕不会写代码的人也能看懂。 但是,如果使用汇编语言来写N阶乘的话,要300+行代码n-factorial.s: 这个N阶乘的汇编代码是我大学时期写的,已经是N年前的事情了,它需要处理10进制与2进制的转换,需要使用多个字节保存大整数,最多可以计算大概500左右的N阶乘。 还有一点,不同类型的CPU的指令集是不一样的,那就意味着得给每一种CPU重写汇编代码,这就很崩溃了。。。 还好,JavaScirpt引擎可以将JS代码编译为不同CPU(Intel, ARM以及MIPS等)对应的汇编代码,这样我们才不要去翻阅每个CPU的指令集手册。当然,JavaScript引擎的工作也不只是编译代码,它还要负责执行代码、分配内存以及垃圾回收。 虽然浏览器非常多,但是主流的JavaScirpt引擎其实很少,毕竟开发一个JavaScript引擎是一件非常复杂的事情。比较出名的JS引擎有这些: V8 (Google)SpiderMonkey (Mozilla)JavaScriptCore (Apple)Chakra (Microsoft)IOT:duktape、JerryScript还有,最近发布QuickJS与Hermes也是JS引擎,它们都超越了浏览器范畴,Atwood's Law再次得到了证明: Any application that can be written in JavaScript, will eventually be written in JavaScript.V8:强大的JavaScript引擎在为数不多JavaScript引擎中,V8无疑是最流行的,Chrome与Node.js都使用了V8引擎,Chrome的市场占有率高达60%,而Node.js是JS后端编程的事实标准。国内的众多浏览器,其实都是基于Chromium浏览器开发,而Chromium相当于开源版本的Chrome,自然也是基于V8引擎的。神奇的是,就连浏览器界的独树一帜的Microsoft也投靠了Chromium阵营。另外,Electron是基于Node.js与Chromium开发桌面应用,也是基于V8的。 V8引擎是2008年发布的,它的命名灵感来自超级性能车的V8引擎,敢于这样命名确实需要一些实力,它性能确实一直在稳步提高,下面是使用Speedometer benchmark的测试结果: V8在工业界已经非常成功了,同时它还获得了学术界的肯定,拿到了ACM SIGPLAN的Programming Languages Software Award: V8's success is in large part due to the efficient machine code it generates.Because JavaScript is a highly dynamic object-oriented language, many experts believed that this level of performance could not be achieved. V8's performance breakthrough has had a major impact on the adoption of JavaScript, which is nowadays used on the browser, the server, and probably tomorrow on the small devices of the internet-of-things.JavaScript是一门动态类型语言,这会给编译器增加很大难度,因此专家们觉得它的性能很难提高,但是V8居然做到了,生成了非常高效的machine code(其实是汇编代码),这使得JS可以应用在各个领域,比如Web、APP、桌面端、服务端以及IOT。 ...

July 16, 2019 · 3 min · jiezi

electronrebuild编译nodeffi遇到的坑nodegyp

在能够正常使用node-ffi的日子里过于膨胀,导致在家使用node-ffi遭到了毁灭性打击,用时5个晚上终于把问题解决了,下面记录一下遇到的坑 1、因为之前下载过windows-build-tools所以python 和 vs2015是有安装的,所以不在安装以管理员身份运行命令行 执行 npm install --global windows-build-tools 2、安装node-gypnpm install -g node-gyp (好吧,本来用yarn安装的,后来被整奔溃了,换npm好了) 3、好了下来在node-gyp目录下创建binding.gyp文件,文件和package.json同级 // binding.gyp{ "targets": [ { "target_name": "binding", "sources": [ "src/binding.cc" ] } ]}npm查看全局安装目录npm root -gyarn查看全局安装目录yarn global dir python问题第一个问题是python路径的问题根据node-gyp的提示 设置python路径 npm config set python %python%\python.exe(%python% === 你python文件安装路径);你以为这样就完了? 好吧,依然报python不存在,好吧!设置环境变量 嗯这样第一个python路径的问题就圆满解决了设置完环境变量记得重启命令行!设置完环境变量记得重启命令行!设置完环境变量记得重启命令行! node-gyp问题在看到python路径问题后, 你还会遇到找不到`node-gyp的问题 有了python的设置经验,这个完全没什么难度上图 好了,保存随便找个目录运行 node-gyp build设置完环境变量记得重启命令行!设置完环境变量记得重启命令行!设置完环境变量记得重启命令行! 当你看到这个的时候,就没问题了,好下来我们到目录下开始安装依赖上配置 "scripts": { "start": "electron .", "rebuild": ".\\node_modules\\.bin\\electron-rebuild.cmd" }, "dependencies": { "electron": "4.0.0", "ffi": "2.3.0", "electron-rebuild": "^1.8.5", "ref": "^1.3.5" }运行,编译成功 ...

July 15, 2019 · 1 min · jiezi

程序员过关斩将来自于静态方法和实例方法的联想翩翩

这两周没有妹子来找我问问题,有点小伤感,所以耽误更新了。哈哈,别当真,因为菜菜这两周周末都有事(你可以认为去公司加班了),实在是没有精力,忘各位见谅!!以下为菜菜自己观点,不代表任何妹子的观点,请轻喷面向对象作为一个久经考验并得到业界肯定的编程思想莫过于面向对象编程思想了。 面向对象(Object Oriented,OO)是软件开发方法。面向对象的概念和应用已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。谈到面向对象思想,首先你得有一个对象才可以。所以计算机天才在语言角度发挥抽象能力,在编程中把对象抽象创建了出来,典型的代表作就是java/c# 中的类(class)。把每一个class的类型看做现实世界中的一类对象,然后根据class 可以创建出来多个class的实例,把这些实例看做是面向的具体对象。 为了完美的支持面向对象,大多数语言都支持了特性:封装,继承,多态。这也是诸多蛋疼的面试题中的常见题型。应用场景引入实例化方法概念是面向对象概念出现以后的事情了,区分静态方法和实例化方法不能单单从性能上去理解,创建c++,java,c#这样面向对象语言的大师引入实例化方法一定不是要解决什么性能、内存的问题,而是为了让开发更加模式化、面向对象化。这样说的话,静态方法和实例化方式的区分是为了解决模式的问题。 说的白话一点,到底是使用实例方法还是静态方法取决于业务的场景,当你的业务中每个对象都有自己的状态,或者行为,这些状态和行为是只属于当前对象的,那你的行为可以设计成实例方法。举一个很简单的例子:一个游戏的项目中,每个玩家(player)都有自己的状态,比如玩家有一个行为:跳跃,不同的玩家跳的距离可能不同,所以这个跳跃的行为体现到代码上就是一个player类型实例的方法。 至于静态方法,一般的定义成类型的行为和状态。因为类型是所有实例共享的,所以通常用作全局共享用途。实际项目中会发现有很多的helper类里边都是静态方法,因为这些方法和具体对象,和具体对象的行为状态没有任何关系。因为和具体实例没有连接,所以这类型的静态方法几乎都是线程安全的。举个很简单的例子:项目中有很多加密的方法,这些方法的作用就是给一个参数,返回一个结果,没有任何自己的状态,所以这些方法被设计成静态方法。 在多数项目中,实例方法的使用量要大于静态方法,为什么呢?因为在多数系统中充斥着各种对象的设计,各种XX设计模式的使用,而这些最终都使用了面向对象的思想。举一个最简单的mvc例子,无论是java中还是c#的 mvc框架,controller中的方法都是实例方法,因为每个http请求都有自己的状态,像header头信息,body信息等,这些状态是属于当前http请求的,所以这些controller必须是实例方法才行。 几乎现代所有的流行编程语言都提供了类型实例的继承和多态,统统都是为了更好的服务面向对象这个理念。为什么不提供类型的继承和多态呢?小伙伴们可以留言! 常见问题静态方法是类型的方法,实例方法是每个实例的方法(每个语言形式不太一样): class Bird { //静态方法 static bool IsAnimal() { return true; } //实例方法 bool IsCanFly() { return true; } }静态方法比实例方法快?菜菜认为这是错误的。一个方法的代码被加载到内存中,然后被cpu去执行,执行的速度快慢和是不是静态方法没有任何关系。但是有一个特殊的场景,那就是GC。实例化太多对象在java/c#这类带有GC的编程语言中会引发垃圾回收操作,当垃圾回收进行的时候会挂起所有的线程,所以在这个短暂的时间里,程序会卡顿。 静态方法常驻内存?在一个类型第一次被使用的时候,会把静态方法和静态变量载入内存,直到进程被销毁。说道常驻内存,也算是一种误解,正确的说法是只有在被使用之后才会加载进入内存。当然在一些语言中可以手动卸载当前类型。 静态方法没有线程安全问题菜菜认为是错的。有没有线程安全问题不是是不是静态所决定的,一个类型也可以有自己的状态和行为,只不过在一个进程中只有一份而已。当一个类型中的状态被多个线程修改的时候,就会有资源竞争问题,就会有线程安全问题。当一个类型的状态只有读的情况下,可以认为读这个方法是线程安全的。 自己运行一下以下程序的结果 class Program { static void Main(string[] args) { for (int i = 0; i < 20; i++) { Thread t = new Thread(() => { for (int i2 = 0; i2 < 100000; i2++) { Add(); } }); t.Start(); } //为了模拟程序一直运行 while (true) { Console.WriteLine($"Num的值:"+Num); Thread.Sleep(1000); } Console.Read(); } public static int Num; public static void Add() { Num= Num+1; } }至于实例方法的线程安全问题,原理类似。有没有线程安全问题取决于状态有没有被多个线程并发修改,有没有资源竞争,和是否静态完全没关系。 ...

July 15, 2019 · 1 min · jiezi

nodejs进阶1npm使用技巧和最佳实践

nodejs进阶教程,小白绕道!!! npm使用技巧和最佳实践前提:请确保安装了node.js npm的最佳实践npm install是最常见的npm cli命令,但是它还有更多能力!接下来你会了解npm是如何在应用的整个生命周期帮助你的-从创建一个项目到开发和部署整个生命周期 认识npm在开始之前,我们先来看看一些查看正在运行的npm版本的命令 npm version获取当前npm cli正在使用的版本,你可以执行命令npm version,这条命令除了返回版本外,还可以返回很多信息-当前包的版本,你正在使用的node.js的版本,openSSL或者V8的版本,以我本地安装的node为例,结果如下: npm help和大多数的命令行工具一样,npm也有一个内置的帮助函数, 可以获取命令的描述和提要,比入我们想看看npm test命令是做什么的,执行`npm help test` 1 使用npm init创建新项目使用npm init命令可以帮助你交互式地创建package.json文件,会弹出项目名称和描述的问题,有一个快速的解决方法npm init --yes(或者简写npm init -y),那么就不会弹出任何问题了,仅仅是使用默认配置创建一个package.json, 使用下面的命令,你可以配置这些默认配置 npm config set init.author.name YOUR_NAMEnpm config set init.author.email YOUE_EMAIL2 查找正确的npm包查找正确的npm包相当具有挑战性-有成千上万的包可供选择,因此选择正确的npm 包很令人苦恼了,然而,我们可以选择一个模块帮助我们发送HTTP请求有一个网站可以帮助我们简单地完成这个任务,它就是npms.io,它会展示包的质量,受欢迎度,和维护性,这些是通过一个模块是否更新了依赖,是否有提示配置,是否做了测试覆盖,最近有没有提交记录来综合计算得出的 3 检查npm包一旦我们找到了我们的模块(在我们的例子中是一个request模块),我们应该看一下文档,查看issues,以便更好地了解我们即将引入到应用中的包,不要忘记你使用的npm包越多,你的项目的风险就越高 如果你想在cli中打开这个模块的首页,执行 `npm home request` 打开模块的issues npm bugs request 或者你只是想查看模块的git仓库,执行下面的命令 npm repo request 4 保存依赖npm采用semver进行控制 所谓SEMVER,指的是语义化版本,其规则概述如下:版本格式:主版本号.次版本号.修订号,版本号递增规则如下: 主版本号:新的架构调整,不兼容老版本次版本号:新增功能,兼容老版本修订号:修复bug,兼容老版本。一旦你决定将第三方包引入你的项目,你必须安装和保存它,最常见的命令是npm install some-package,如果你想自动将包信息添加进package.json文件,你需要执行npm install some-package --save npm默认会带着'^’这个前缀保存你的包,具体的安装规则如下: ^version 兼容某个版本版本号中最左边的非0数字的右侧可以任意如果缺少某个版本号,则这个版本号的位置可以任意如:^1.1.2 ,表示>=1.1.2<2.0.0,可以是1.1.2,1.1.3,.....,1.1.n,1.2.n,.....,1.n.n如:^0.2.3 ,表示>=0.2.3 <0.3.0,可以是0.2.3,0.2.4,.....,0.2.n如:^0.0,表示 >=0.0.0 <0.1.0,可以是0.0.0,0.0.1,.....,0.0.n这也意味着当你下次执行npm install的时候,即使主版本没有更改,次版本或者修订版本发生了改变,你的这个模块会重装,你可以执行下面的命令修改安装包版本规则npm config set save-prefix='~' ...

July 15, 2019 · 1 min · jiezi

如何在项目中使用log4js

如何在项目中使用log4.jspm2中自带的日志内容是不能满足日常的需求的,因此需要在项目中加上日志管理,这里研究了下log4的使用方法,效果挺好的,想要查看的都可以找到,记录下简单的使用步骤log4的配合// config.jslet path = require('path');// 日志根目录let baseLogPath = path.resolve(__dirname, '../../../logs');// 请求日志目录let reqPath = '/request';// 请求日志文件名let reqFileName = 'request';// 请求日志输出完整路径let reqLogPath = baseLogPath + reqPath + '/' + reqFileName;// 响应日志目录let resPath = '/response';// 响应日志文件名let resFileName = 'response';// 响应日志输出完整路径let resLogPath = baseLogPath + resPath + '/' + resFileName;// 错误日志目录let errPath = '/error';// 错误日志文件名let errFileName = 'error';// 错误日志输出完整路径let errLogPath = baseLogPath + errPath + '/' + errFileName;module.exports = { appenders: { // 所有的日志 'console': {type: 'console'}, // 请求日志 'reqLogger': { type: 'dateFile', // 日志类型 filename: reqLogPath, // 输出文件名 pattern: '-yyyy-MM-dd-hh.log', // 后缀 alwaysIncludePattern: true, // 上面两个参数是否合并 encoding: 'utf-8', // 编码格式 maxLogSize: 1000, // 最大存储内容 }, // 响应日志 'resLogger': { type: 'dateFile', filename: resLogPath, pattern: '-yyyy-MM-dd-hh.log', alwaysIncludePattern: true, encoding: 'utf-8', maxLogSize: 1000, }, // 错误日志 'errLogger': { type: 'dateFile', filename: errLogPath, pattern: '-yyyy-MM-dd-hh.log', alwaysIncludePattern: true, encoding: 'utf-8', maxLogSize: 1000, } }, // 分类以及日志等级 categories: { default: { appenders: ['console'], level: 'all' }, reqLogger: { appenders: ['reqLogger'], level: 'info' }, resLogger: { appenders: ['resLogger'], level: 'info' }, errLogger: { appenders: ['errLogger'], level: 'error' } },}log4的日志封装这里是把log4封装成一个中间件,在app.js中直接调用就可以了// 先安装log4js// log4.jsconst log4Config = require('./config')const log4js = require('log4js')// 调用配置文件log4js.configure(log4Config)class CommonHandle { constructor(){} // 格式化请求日志 static formatReqLog(ctx, time){ let text = '------------request start------------' let method = ctx.method text += `request method: ${method} \n request url: ${ctx.originalUrl } \n` if(method = 'GET'){ text += `request data: ${JSON.stringify(ctx.query)} \n` }else{ text += `request data: ${JSON.stringify(ctx.body)} \n` } text += `ctx all: ${JSON.stringify(ctx)}` return text } // 格式化相应日志 static formatResLog(ctx,time){ let text = '------------response start------------' text += `response result: ${JSON.stringify(ctx.response.body)} \n` text += `response all: ${JSON.stringify(ctx)} \n` text += `response time: ${time} \n` return text } // 格式化错误日志 static formatErrorLog(ctx,error,time){ let text = '------------error start------------' text += this.formatResLog(ctx,time) text += `error content: ${JSON.stringify(error)}` return text }}class HandleLogger extends CommonHandle{ constructor(){ super() } // 请求日志 static reqLogger(ctx){ log4js.getLogger('reqLogger').info(this.formatReqLog(ctx)) } // 相应日志 static resLogger(ctx, time){ log4js.getLogger('resLogger').info(this.formatResLog(ctx,time)) } // 错误日志 static errorLogger(ctx, error, time){ log4js.getLogger('errLogger').info(this.formatErrorLog(ctx,error,time)) }}module.exports = (options) => { return async (ctx,next) => { const startTime = new Date() let period; try{ // 请求日志 HandleLogger.reqLogger(ctx) await next() period = new Date() - startTime // 响应日志 HandleLogger.resLogger(ctx,period) }catch(err){ period = new Date() - startTime // 错误日志 HandleLogger.errorLogger(ctx, err, period) } }}调用封装好的日志函数这里直接以中间件的形式调用就可以了 ...

July 15, 2019 · 2 min · jiezi

借助URLOS快速安装nodejs环境

环境需求最低硬件配置:1核CPU,1G内存(1+1)提示:如果你的应用较多,而主机节点的硬件配置较低,建议在部署节点时开通虚拟虚拟内存;生产环境建议使用2G或以上内存;推荐安装系统:Ubuntu-16.04、Ubuntu-18.04、CentOS7.X、Debian9X的64位的纯净的操作系统;URLOS安装curl -LO www.urlos.com/iu && sh iunodejs环境安装流程登录URLOS系统后台,在应用市场中搜索“nodejs”,找到之后,直接点击安装按钮 填写服务名称、选择运行节点、服务端口、选择智能部署 填写ssh密码(这里的密码是root密码) 然后点击“提交”按钮,等待部署完成;

July 15, 2019 · 1 min · jiezi

手写简版无三方依赖的NodeServer

预告:今天写的可能用处不大,各位看官谨慎选择要不要看下去...????又是显得D疼造轮子系列之手写无三方依赖的轻量级Node-server,哇,好多的形容词,且看需求。 产品说要看一下页面写的怎么样了但是他又不想动或者你不想看到他,我觉得多半是因为后者????,那么问题来了,凭咱自己能不能把产品拒在10米之外又让他把页面瞅一眼,那必须是可以的,废话少说,直接开写。 Node知识点fshttpos从开启一个基本的Node服务开始, http模块参考文档http.createServer(function (request, response) { // 发送 HTTP 头部 // HTTP 状态值: 200 : OK // 内容类型: text/plain response.writeHead(200, {'Content-Type': 'text/plain'}); // 发送响应数据 "Hello World" response.end('Hello World\n');}).listen(8888);// 这就开启了一个最基础的node服务,浏览器访问 http://127.0.0.1:8888 即可看到Hello World那么万里长征第一步的踮脚动作算是完成了,接下来就是实现怎么打开我们自己的html页面。 想要打开html页面肯定就要访问文件,于是就得用到fs模块,以下代码可以实现深层遍历脚本同目录下的html文件。 遍历页面需要的文件, fs模块参考文档function geFileList(path) { var filesList = []; readFile(path, filesList); return filesList;}//获取文件类型function getType(filename){ var index=filename.lastIndexOf("."); if(index!=-1){ var type=filename.substring(index+1,filename.length); return type; }}//遍历读取文件 function readFile(path, filesList) { files = fs.readdirSync(path);//需要用到同步读取 files.forEach(walk); function walk(file) { states = fs.statSync(path + '/' + file); if (states.isDirectory()) { readFile(path + '/' + file, filesList); } else { var obj = new Object(); obj.size = states.size; obj.name = file;//文件名 obj.type = getType(file) filesList.push(obj); } }}// 统计各类文件数量function countFileByType(obj){ var keyContainer = {}; obj.forEach(item => { keyContainer[item.type] = keyContainer[item.type] || []; keyContainer[item.type].push(item); }); return keyContainer}module.exports.geFileList = geFileListmodule.exports.countFile = countFileByTypemodule.exports.getType = getType//方法模块化,把文件读取操作剥离比较容易整理思路,emmmmmmm,但是写的不是很优雅,有待改进Node服务可以开启了,文件可以遍历了,至此基础建设算是搞起来了,接下来就需要开一个不用看见产品的服务了。 ...

July 15, 2019 · 2 min · jiezi

TypeScript最佳实践是否使用noImplicitAny

我应该使用noImplicitAny TypeScript编译器标志吗?noImplicitAny编译器选项所做的,基本上是将TypeScript从可选类型语言转换为强制类型检验语言。这使得TypeScript离JavaScript的超集稍微远了一些,因为简单的: function logMe(x) { console.log(x);}// error TS7006: Parameter 'x' implicitly has an 'any' type.也将报错——你必须明确声明x的类型为any: function logMe(x: any) { console.log(x);} // OK这意味着,如果你要把现有的JS代码库迁移到TS,那除了更改文件扩展名,你还得做一些较复杂的东西。这还意味着,在编写代码时,您需要更多地关注类型,如果不指定类型,编译器就总是会「抱怨」。由于在实际情况中显式地声明any被认为是不好的实践,所以在开发过程的早期,您就需要分配正确的类型。如果没有显式的声明,这可能意味着「我太懒了,没有正确地注释这里的类型」。 这样子到底是好是坏是有很大争议的,社区在这个问题上似乎存在分歧。下面是一些业界领先的TypeScript项目,以及它们是否使用了noImplicitAny编译器标志: ProjectUses noImplicitAnyAngularYESRxJSYESVSCodeNOBabylon.jsNO接下来下面是我的观点:我们使用TypeScript,是因为类型提供了有意义的额外信息,可以作为文档、并在早期捕获错误。如果想在项目的代码中都享受这种益处,那就不应该只在某个地方添加类型——把它们添加到任何地方,就可以完成了。 否则你就有可能做出如下额外的思考: “嗯,我应该在这里添加类型吗?我有点懒,但这很好,但我还有其他工作要做……「我们明天再做吧。」因此,我的建议是将noImplicitAny设置为true。

July 15, 2019 · 1 min · jiezi

npm的scripts在Windows下无法并行或串行执行多命令的解决

我用MacOS开发,这个npm的scripts是可以很好的执行并行或者串行的脚本的,比如我们来看下我这个在MacOS下的正常执行的scripts代码段: "scripts": { "dev": "webpack --watch --config webpack.dev.js & npm run s", "build": "webpack --config webpack.prod.js", "prod": "webpack --config webpack.prod.js & npm run s", "lint": "eslint --ext ./src/*.js", "lintfix": "eslint --fix ./src/*.js", "sa": "nodemon ./servers/51la/server.js", "sb": "nodemon ./servers/jump/server.js", "sc": "nodemon ./server.js", "s": "npm run sa & npm run sb & npm run sc" },这里我执行npm run dev可以并行处理webpack --watch --config webpack.dev.js和npm run s,而执行后面的这个名领的时候又可以触发执行npm run sa & npm run sb & npm run sc,然后再次触发对应的三个命令。我暂时不关心他多层调用的问题。 ...

July 15, 2019 · 1 min · jiezi

VueNodeExpressMySql的尝试

前言这是一次很简单的尝试,初衷是使用nodejs替换PHP,搭建一个完整的web项目。 项目逻辑vue开发前端,目前还在dev模式,使用proxy代理和node后端进行通信。node+express构建后端web服务,连接mysql,进行增删改查。 功能实现1.前端通过axios,已经实现了get、post,formdata图片上传的功能。2.后端接收get、post、formdata数据,查询数据库返回数据,保存图片返回图片地址的功能。3.图片存储在指定的文件夹,通过xampp指定了一个静态目录做图片存储的功能。 下一步的开发1.目前只实现了对mysql的select操作,下一步需要实现inset、update、delete操作。2.vue项目目前还是dev模式,需要build项目进入product模式,服务器为xampp。 最后最后一步,所有项目迁移到外网,暂定为阿里云。 codeVue端的图片上传代码:upfile.vue: change(){ let that = this let file = that.$refs.avatar.files[0]; let URL = window.URL || window.webkitURL; let imageURL = URL.createObjectURL(file); that.avatar = imageURL let fd = new FormData() fd.append('file', file) that.$store.dispatch('upfile', { fd: fd, callback: function(res){ that.avatar = that.$store.state.imageURL + res.data console.log(res) } }) }Vue端的vuex代码:store.js: upfile (context, data) { axios.create().post('/upfile', data.fd, { headers: { 'Content-Type': 'multipart/form-data' } }).then(response => { console.log('success') data.callback(response.data) }).catch(res => { console.log('error') data.callback(res) }) },Node端的upfile.js: ...

July 15, 2019 · 2 min · jiezi

Node-前端build-时内存溢出问题的原因及解决方案

使用webpack build 现象描述:构建过程中频繁报内存溢出:FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed -process out of memory。原因是node 环境可使用内存为512M,build时候node_module 文件超出最大内存。 解决方案:通过 package.json 中的 "build" 加大内存 "build": "node --max_old_space_size=8000"

July 15, 2019 · 1 min · jiezi

vue项目搭建

最近在研究前端框架,发现vue是国人写的一个前端框架,自然就引起了强烈的爱国情怀,学习一下。先来安装一下node.js,如果没有安装的可以去官网下载对应的版本进行安装。 安装完成后需要查看一下是否安装成功以及版本号是否与下载安装的一致。 安装镜像,这里推荐安装淘宝的镜像。 npm install -g cnpm --registry=https://registry.npm.taobao.org安装webpack。 cnpm install webpack -g 根据模板创建项目,当然这里也可以使用HBuikder X(或者其他)工具进行项目的创建。 vue init webpack-simple 你的项目名称使用HBuikder X创建项目: 根据自己的需要修改相关的信息即可。 需要安装依赖,需要一段时间。 依赖安装过程:运行项目即可正常访问页面了。 访问上面的两个地址都可以。 进入创建好的项目中安装项目依赖。 npm install启动项目 npm run dev 这就是vue项目的创建过程,以后会继续分享vue和element的整合。

July 15, 2019 · 1 min · jiezi

Sequelize手记-一

最近开始接触数据库,现在普遍用的都是Mysql数据库,简单的了解了一下sql语句,没有太深入的学习,然后就开始找相关的ORM框架,然后锁定了Sequelize,个人感觉很强大,搜索了一些文档,但是很让人费解,讲的每一部分都是那么的官方,不太容易理解,记录一下学习路程。本文档以koa+Sequelize进行编码测试。 准备工作在尝试使用Sequelize之前先确认是否安装了Mysql数据库,安装node,这里使用的Mysql 5.6版本。 首先要创建一个项目执行命令如下: mkdir 文件夹名称cd 文件夹名称npm init -y // 直接略过所有问答,全部采用默认答案在开始之前首先要安装相关依赖: // 安装 sequelize koa mysql2npm install --save-dev sequelize koa mysql2注意:这里是mysql2 建立连接创建main.js文件,分别引入koa和sequelize建立与mqsql数据库连接。在实例化Sequelize时需要配置一些参数,第一个接收的参数时数据库的名称,第二个是用户名,第三个是密码,第四项是连接mysql的相关配置。 const Koa = require("koa");const Sequelize = require("sequelize");const app = new Koa();const mysqlConfig ={ host: 'localhost', // 接数据库的主机 port: '3306', // 接数据库的端口 protocol: 'tcp', // 连接数据库使用的协议 dialect: 'mysql', // 使用mysql pool: { max: 5, // 最大连接数量 min: 0, // 最小连接数量 idle: 10000 // 连接空置时间(毫秒),超时后将释放连接 }, retry: { // 设置自动查询时的重试标志 max: 3 // 设置重试次数 }, omitNull: false, // null 是否通过SQL语句查询 timezone: '+08:00' // 解决时差 - 默认存储时间存在8小时误差};const sequelize = new Sequelize('aarontest', 'root', '123456',mysqlConfig );app.listen(3000);通过上述代码就已经与Mysql建立了连接。这个地方没有什么可以讲的,按照上述进行配置就可以了。test数据库不一定要存在不存在也是可以正常建立连接的。配置参数还有很多,这里只说了一些常用的,就不多赘述了。如果觉得上述方法比较麻烦可以通过也提供了其他方法进行连接: ...

July 15, 2019 · 4 min · jiezi

经常被面试官问道的JavaScript数据类型知识你真的懂吗

前言之前面试了几个开发者,他们确实做过不少项目,能力也是不错的,但是发现js基础不扎实, 于是决定写一下这篇javascrip数据类型相关的基础文章,其实也不仅仅是因为面试了他们,之前自己在面试的时候,也曾经被虐过,面试官说过的最深刻的一句话我到现在都记得。 基础很重要,只有基础好才会很少出bug,大多数的bug都是基础不扎实造成的。这里给出两道我们公司数据类型基础相关的面试题和答案,如果都能做对并且知道为什么(可以选择忽略本文章): //类型转换相关问题var bar=true;console.log(bar+0);console.log(bar+"xyz");console.log(bar+true);console.log(bar+false);console.log('1'>bar);console.log(1+'2'+false);console.log('2' + ['koala',1]);var obj1 = { a:1, b:2}console.log('2'+obj1);var obj2 = { toString:function(){ return 'a' }}console.log('2'+obj2)//输出结果 1 truexyz 2 1 false 12false 2koala,1 2[object Object] 2a//作用域和NaN 这里不具体讲作用域,意在说明NaNvar b=1;function outer(){ var b=2; function inner(){ b++; console.log(b); var b=3; } inner();}outer();//输出结果 NaN本篇文章会以一个面试官问问题的角度来进行分析讲解 js中的数据类型面试官:说一说javascript中有哪些数据类型?JavaScript 中共有七种内置数据类型,包括基本类型和对象类型。 基本类型基本类型分为以下六种: string(字符串)boolean(布尔值)number(数字)symbol(符号)null(空值)undefined(未定义)注意: string 、number 、boolean 和 null undefined 这五种类型统称为原始类型(Primitive),表示不能再细分下去的基本类型;symbol是ES6中新增的数据类型,symbol 表示独一无二的值,通过 Symbol 函数调用生成,由于生成的 symbol 值为原始类型,所以 Symbol 函数不能使用 new 调用;null 和 undefined 通常被认为是特殊值,这两种类型的值唯一,就是其本身。对象类型对象类型也叫引用类型,array和function是对象的子类型。对象在逻辑上是属性的无序集合,是存放各种值的容器。对象值存储的是引用地址,所以和基本类型值不可变的特性不同,对象值是可变的。 ...

July 15, 2019 · 4 min · jiezi

一个前端界面vue-ssr-后端界面-react-spa-服务node的项目

Kite This is a vue + react project kite前台演示网站地址: 小随笔https://www.xiaosuibi.com/ 后台演示网站地址: 小随笔https://www.xiaosuibi.com/_admin 后台演示网站账户:kitetest 密码:q123456 (资源有点大,可能要加载一段时间) 兼容方面目前还是主推荐用google浏览器吧 备注:因为项目是一直在写的,周期比较长,改了又改,代码可能比较乱,大家能看则看,有意见的,直接提意见,发出来的目的,就是希望大家多提建议,或者意见然后我再来改,代码方面我会一直优化的!!!!!!关于项目的维护,会一直维护下去的 再次备注:代码方面美观,或者有问题的代码,各位大佬直接指出即可,都是自己一个人在学,所以代码方面质量方面肯定没那么好 后续是先做小程序版本+app版本 然后边维护和改bug 目前还需要对编辑器进行优化 Build# npm install || cnpm install 安装所有的包,可能有些多,前台和后台是在一起的打包后台界面 npm run admin-build打包前台界面 npm run client-buildStart# 目前用的数据库只有mysql 本地开发的话,下一个phpstudy即可初始化:npm run init 然后打开浏览器收入 localhost:8086 按照步骤操作即可然后可以选择pro or dev 开始pro 生产环境pro1.1 在cmd 中输入 npm run server 即可进入程序pro1.2 (url或者ip)+ :8086端口即可看到客户端主页pro1.3 (url或者ip)+ :8086/admin端口即可看到客户端后台页面dev 本地开发环境dev1.1 在cmd 中输入 npm run server-start 即可进开启接口服务dev1.2 在cmd 中输入 npm run admin-start 即可进入后台开发预览(地址为:localhost:8083)dev1.3 在cmd 中输入 npm run client-start 即可进入前台开发预览(地址为:localhost:8081)dev1.4 开发环境下 一定要先运行dev1.1的情况下再运行 dev1.2 或者 dev1.3目前cli部分代码写的比较乱,等后期有时间再继续优化,哈哈项目断断续续的写着,主体基本写完,目前就是优化和改bug,代码的逻辑啥的,能看则看,不能看就略过吧,也是自己学习的一个过程,放心这个代码会一直优化的,已经坚持了很久了,可以看提交,哈哈目录结构kite/ | ├──admin/ * 后台页面目录react | ├──client/ * 前台ssr文件目录 │ ├──build * vur ssr build 配置文件 │ ├──config * 部分配置文件 │ ├──public * index模版文件 │ ├──request * 请求配置文件 │ ├──server * dev 模式下的开始文件 │ ├──src * src ssr 主文件目录 │ └──static * 静态资源目录 │ │──config/ * 部分可配置文件 │ │──db/ * mysql and lowdb | ├──server/ * 服务层,所有前台后台接口 │ ├──static/ * 静态资源目录 | ├──views/ * cli 模版目录 │ │──plugins/ * 第三方组件 + 自有js库 + 其他插件性质的脚本 │ │──static/ * 不经编译器处理的静态资源 │ │──store/ * 全局数据状态管理 │ │──package.json * 包信息 │ │──.eslintrc * Eslint配置 │ │──_nodemon.json * _nodemon配置 │ │──.gitignore * Git忽略文件配置 │ └──pm2.json * pm2配置初始化 ...

July 15, 2019 · 2 min · jiezi

译如何连接-React-和-NodeExpress

原文:How to create a React frontend and a Node/Express backend and connect them作者:João Henrique译者:博轩 在本文中,我将引导你创建一个简单的 React 应用,以及一个简单的 Node/Express API,并将两者相互连接。 我不会详细介绍本文中提到的任何技术,但是我会留下链接,以便你想了解更多信息。 您可以在我为本教程制作的代码库中找到源码。 译注:嘤嘤嘤,我也写了Demo的... client-react-001 , api-node-001这里的目标是为您提供有关如何设置和连接前端客户端和后端API的实用指南。 在我们开始之前,确保您的机器上已经安装了 Node.js 创建项目主目录在终端,导航到你要保存项目的目录。现在为您的项目创建一个新目录并导航到它: mkdir my_awesome_projectcd my_awesome_project创建一个 React 应用这个过程非常简单。 我们将使用 Facebook 的 create-react-app 来... 你猜对了,简单的创建一个名为 client 的应用程序: npx create-react-app clientcd clientnpm start让我们看看这里做了什么: 1.使用 npm 的 npx 创建一个 React 应用,并将其命名为 client。2.cd (更改目录)到客户端目录中。3.启动了应用程序。 在浏览器中,访问:http://localhost:3000/ 如果一切正常,您将看到 React 欢迎页面。恭喜!这意味着您现在在本地计算机上运行了一个基本的 React 应用程序。是不是很简单? 要停止您的 React 应用程序,只需在终端按下 Ctrl + c 即可。 ...

July 15, 2019 · 2 min · jiezi

常见核心前端面试问题与详细解答

本文总结了前端老司机经常问题的一些问题并结合个人总结给出了比较详尽的答案。关于知识点原理和详细,参考讲堂的视频教程:前端增长-重新定义大前端课程知识在不断更新,本片内容也逐步更新官方博客:FED123前端学堂 1.关于性能优化说说js文件摆放顺序、减少请求、雪碧图等等原理, 说下window.performance.timing api是干什么的?浏览器是按照文档流解析html,为了更快构建DOM树和渲染树将页面呈现到屏幕上,建议是降js放在文档dom树结尾,body标签闭合前。浏览器并发HTTP请求有限制(6个左右),加载页面html后开始解析,解析到外链资源比如js css和图片,就会发http请求获取对应资源。减少请求就是减少这些资源请求, 可以 css资源合并,js资源合并,图片资源合并同时做lazyload,区分首屏非首屏接口,按需请求数据。雪碧图是一种图片资源的合并方法,将一些小图片合成一张图,通过background-position来定位到对应部分。window.performance.timing 参考下前端页面性能指标数据计算方法, performance接口属于w3c标准hight resolution time中的一部分,通过navigation timeline api 、 performance timeline api,user timing api,resource timeline api 这四个接口做了增强实现。其中navigation timeline api中PerformanceTiming 接口数据放在 performance.timing这个对象上。主要记录了浏览器从跳转开始的各个时间点的时间,比如navigationStart是页面开始跳转时间,fetchStart是页面开始时间,domainLookupStart是DNS开始时间,domainLookupEnd是DNS结束时间, 查找到DNS后建立http链接,connectStart和connectEnd分别是链接开始和结束时间,然后是requestStart开始发起请求时间,responseStart开始响应时间,responseEnd响应结束时间。然后是苟安DOM树时间,分别是domLoading, domInteractive, domContentLoad和domComplete时间,分别对应document.readyState状态loading、interactive和complete。最后是页面onload,分别是loadEventStart和loadEventEnd时间节点。可以通过这个接口统计前端的页面性能数据。 domainLookupStart - fetchStart = appCache时间,这段时间浏览器首先检查缓存domainLookupEnd -domainLookupStart = DNS时间connectEnd - connectStart = TCP时间responseStart - requestStart = FTTB首字节时间,或者说是服务器响应等待时间domContentLoad - navigationStart = 页面pageLoad时间 loadEventEnd - navigationStart = 页面onLoad时间 关于知识点原理和详细,参考讲堂的视频教程:前端增长-重新定义大前端 2.请你描述下一个网页是如何渲染出来的,dom树和css树是如何合并的,浏览器的运行机制是什么,什么是否会造成渲染阻塞?参考下:浏览器工作原理   浏览器渲染与阻塞原理 第一部分通过performance.time这个api我们可以了解浏览器加载网页的流程,浏览器边加载html边构建DOM树,当然会有容错和修正机制。浏览器解析到行内css和内联css会立马加入到构建渲染树,解析到外链css就开始加载,加载完之后也会合并到渲染树的构建中,最后将渲染树和DOM做节点链路匹配,也叫layout阶段,计算每个DOM元素最终在屏幕上显示的大小和位置。 遍历顺序为从左至右,从上到下,绘制在屏幕上,layout过程中可能会触发页面回流和重绘,比如某个外链css加载完解析之后合并构建到渲染树中,其中某个css改变了DOM树种某个元素的定位(改成绝对定位)或者改变了长宽边距等位置信息,会触发重新layout,所以会回流reflow。重绘是比如css改变了之前的背景图片颜色,浏览器会重新绘制。 会有渲染阻塞,浏览器刷新的频率大概是60次/秒, 也就是说刷新一次大概时间为16ms,如果浏览器对每一帧的渲染工作超过了这个时间, 页面的渲染就会出现卡顿的现象。浏览器内核中有3个最主要的线程:JS线程,UI渲染线程,事件处理线程。此外还有http网络线程,定时器任务线程,文件系统处理线程等等。 JS线程负责JS代码解析编译执行,称为主线程。常说‘浏览器是单线程’指的是JS主线程只能有一个,主线程执行同步任务,会阻塞UI渲染线程。JS线程核心是js引擎 (IE9+: Chakra firefox:monkey chrome:v8)。webworker可以创建多个js线程,但是受主线程控制,主要用于cpu密集型计算。UI渲染线程当然是负责构建渲染树,执行页面元素渲染。核心是渲染引擎(firefox:gecko、chrome/safari:webkit),由于JS可以操作DOM元素处理样式等,JS主线程是执行同步任务的,所以设计上JS引擎线程和GUI渲染线程是互斥的。 也就是说JS引擎处于运行状态时,GUI渲染线程将处于冻结状态。事件处理线程,由于浏览器是事件驱动的,事件处理线程用来控制事件回调处理,浏览器触发某个事件后会把事件回调函数放到任务队列中,可以看下下面会提到。其他线程统称工作线程,如处理 ajax 的线程,dom事件线程、定时器线程、读写文件的线程等,工作线程的任务完成之后, 会推入到一个任务队列(task queue) ...

July 14, 2019 · 5 min · jiezi

NPM包管理器

NPM安装和定义NPM(全称 Node Package Manager,即“node包管理器”)是Node.js默认的、以JavaScript编写的软件包管理系统。以上是维基百科给NPM的定义。理解软件包管理系统。软件包管理系统是在计算机中自动安装、配制、卸载和升级软件包的工具组合,在各种系统软件和应用软件的安装管理中均有广泛应用。比如:PHP中composer、JAVA中maven、LIUNX系统中的yum NPM软件包管理系统由3部分组成: 网站 可以通过https://www.npmjs.com网站,搜索包、管理自己的包和个人信息。 注册表(registry)一个巨大的数据库,保存着每个包(package)的信息。 命令行工具 (CLI)通过命令行和终端运行,开发者通过CLI与NPM打交道。在安装Node.js时会默认安装NPM包管理器,NPM完全由Javascript写成,和模块没啥区别,所以我们可以npm install -g npm 更新安装自己。 随着前端工程化、模块化的快速发展,前端开发已经离不开npm包管理器了。在我们使用前端各大框架Vue、React、Angluar时,我们只需要npm install [package name]就可以安装使用我们需要的模块包,非常便捷高效。 通过下图我们就能看出npm之火爆。总包量100多万,周下载量114亿。来自各大洲的开源软件开发者都在使用npm借鉴学习或者分享。同时也是Node.js获得成功重要原因之一。 NPM命令以及使用 以上图片罗列出NPM所有的命令,捡几个常用的命令学习; npm install 安装模块安装形式: npm install (with no args, in package dir)npm install [<@scope>/]<pkg>npm install [<@scope>/]<pkg>@<tag>npm install [<@scope>/]<pkg>@<version>npm install [<@scope>/]<pkg>@<version range>npm install <folder>npm install <tarball file>npm install <tarball url>npm install <git:// url>npm install <github username>/<github project>alias: i,addcommon options: [-S|--save|-D|--save-dev|-O|--save-optional] [-E|--save-exact][--no-save][-B|--save-bundle][--dry-run]其中:-S与--save 安装到dependenices生产环境中,对应package.json中的dependencies //npm i -S ecpress || npm install --save express"dependencies": { "express": "^4.17.1",}-D与--save-dev 安装到devDependenices开发环境中,对应package.json中的devDependencies ...

July 14, 2019 · 5 min · jiezi

依赖Nodejs

背景在前端历史演变中已经提到,Nodejs的爆发从2009年开始。Nodejs的出现,基于作者Ryan Dahl 对Web开发高性能的追求,要达到高性能,异步IO/事件驱动是基本原则。对比一些高级语言,最终选择Javascript作为开发语言,由于Javascript天生的事件驱动和单线程,奠定了Nodejs编写高性能Web服务轻而易举。 简单了解NodejsNode.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。 Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。 以上是官方给出的解释,惯例我们还是抽取关键词理解。 Chrome V8 引擎介绍V8 引擎是Chrome于2008年9月2日发布开源。V8使用C++开发,相比其它的JavaScript的引擎转换成字节码或解释执行,V8将其编译成原生机器码(IA-32, x86-64, ARM, or MIPS CPUs),并且使用了如内联缓存(inline caching)等方法来提高性能。有了这些功能,JavaScript程序在V8引擎下的运行速度媲美二进制程序。Chrome浏览器在Webkit渲染引擎中使用v8引擎来提高浏览器的渲染性能。上图是webkit大致结构,红色部分是webkit的默认引擎,在谷歌系列产品中被替换为v8引擎;Nodejs是站在“巨人的肩膀”上进行一系列的封装,它的高性能,离不开Chorme V8引擎。 JavaScript 运行环境Javascript 是一个静态脚本语言,运行时必须要借助于引擎才能运行。Javascript 运行环境一般分为两种: 浏览器运行环境 ( 通常我们写的js代码要在浏览器中才能运行)非浏览器运行环境 (比如Nodejs,借助于V8引擎实现运行的环境) 事件驱动我们在Javascript中注册个事件(回调函数)。但这个事件不是马上执行。只有等事件被触发的时候,才会去执行这个事件(回调函数)。这种形式就是事件驱动。 非阻塞 I/O 阻塞:前一个程序未执行完就得一直等待。比如当你打电话问个问题时,那边说你等等我给你查查,这时候你电话仍然是挂起的,等待等待,直到拿到结果。 非阻塞:前一个程序未执行完时可以挂起,继续执行其他程序,等到使用时再执行。比如当你打电话过去问一个问题,然后挂电话,等那边找到结果就打电话给你。查问题这段时间,你该干嘛就干嘛。I/O: 磁盘的写入(in)磁盘的读取(out)。在程序执行过程中必然要进行很多I/O操作,读写文件、输入输出、请求响应等等。I/O操作时最费时,举个例子,你要读一个文件,整个线程都暂停下来,等待文件读完后继续执行。换言之,I/O操作阻塞了代码的执行,极大地降低了程序的效率。在Nodejs里面单线程可以通过回调函数(事件驱动)来做异步操作,达到非阻塞I/O的效果。 安装Nodejs可以在官网自行选择安装包下载MacOS用户建议使用brew安装#安装brew install -g node#卸载brew uninstall nodejs使用nvm安装管理Nodejs版本curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash# orwget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash# 放入环境变量source ~/.bash_profile# 判断是否安装成功nvm list# 安装nodenvm install 6.14.4 # or 10.10.0, 8.9.1, etc# 判断node安装成功node -vnpm -v Nodejs 的简单使用 模块化规范 CommonJs在前端模块化中已经对CommonJs做了介绍,并使用exports导出模块,require引入模块,实现了一个简单案例。 ...

July 14, 2019 · 1 min · jiezi

前端增长重新定义大前端

前端增长-重新定义大前端 精心打造全新课程,欢迎吐槽!反馈宝贵意见! 在线课程:思否课堂限时免费哦! 课程介绍前端知识点很多,很细碎。一般同学都是死记硬背一些知识点。机灵的同学会背一些案例,更聪明的同学会背一下原理,理解下大概。奈何时光催人老,再好的记忆也会有忘记的时候,况且人生的不同阶段所侧重的点也不一样。所以本课程从面试考察的知识点入手,梳理前端知识点脉络,精讲各个点的长问问题和设计原理,让你从死记硬背转化为理解,实现前端能力增长。再也不需要死记硬背,该忘记就忘记吧,有事没事想一想,捋一捋就行。 课程大纲本课程参考总结的脑图逐个展开讲解原理。(目前课件还在更新中) 第一章 HTML-相识1.1 前端增长,业界发展,盘他?1.2 学习目标,人生就是起起落落落?1.3 HTML咋解析的呢?DOM构建1.4 CSSOM如何构建?会阻塞吗1.5 RenderTree上来秀一波1.6 Layout布局引擎,新交规解析1.7 牛逼的render进程合成层,拯救世界1.8 HTML加载阻塞?咋不上天呢1.9 页面渲染会堵车吗?FM93交通之声 第二章 CSS-相知2.1 啥是Containing Block?有鸟用2.2 要BFC?要啥自行车2.3 到底是怎么定位?挖坑吗2.4 咋布局?设套吗?flex兄弟上车2.5 CSS优先级,优生优育2.6 CSS预处理原理2.7 合成器和非合成器动画,爆GPU菊花2.8 大哥,你的动画卡顿了,快逃2.9 基线和行高的坑 第三章 JavaScript-相爱 课时19 浏览器引擎与webkit课时20 JavaScript虚拟机运行原理流程剖析课时21 JavaScript类型推断课时22 JavaScript虚拟机对象访问优化课时23 秒懂事件循环原理课时24 事件循环之宏任务与微任务课时25 JavaScript虚拟机垃圾回收课时26 JavaScript数据类型与内存模型课时27 数据类型检测与深浅克隆课时28 数据监听方法有哪些?课时29 模块数据通信的方法 第四章 浏览器-相生 【待更新】 第五章 框架-相克 【8月10更新】 第六章 编码能力-相辅 【8月10更新】 第7章 NodeJs-相成 【8月20更新】 第8章 打包-相情 【8月20更新】 第9章 小程序-相怨【8月20更新】 第10章 前端架构-相恨 【8月20更新】 课件思维导图:

July 14, 2019 · 1 min · jiezi