我的阿里面经(中)
我的阿里面经(中)
Q4:Webpack中plugins和loader的作用和区别
我的回答:loader是针对特定规则指明的文件进行定向处理。plugins则提供更多的webpack功能,往往是针对整个打包的过程。loader更关注什么样的文件用什么方式处理,而plugins则关心整个项目的情况。
A4:了解一些Webpack打包的过程
根据官方文档的说明,plugins应该是:
插件是 webpack 的 支柱 功能。webpack 自身也是构建于,你在 webpack 配置中用到的相同的插件系统之上!插件目的在于解决 loader 无法实现的其他事。
比如常用的插件[CommonsChunkPlugin](https://webpack.docschina.org/plugins/commons-chunk-plugin)
,其作用就是抽取共享模块,这个动作显然是针对整个项目的,而不是针对单个文件或者某一类资源进行的。
而loader就比较明确了:
loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。
loader需要设置特定的匹配规则,针对某一种资源施行特定的任务。
从Webpack的功能上看,是一个打包工具,大体上可以认为它分析项目中各个文件,获取它们的类型和依赖关系,并将它们整合成一个或多个bundle。核心概念概念包括入口、出口、loader、插件、模式以及浏览器兼容性。不难理解webpack从入口文件或目录下手,能够在内部创建一个依赖关系图谱,借用loader对不同的文件进行特殊的处理,比如TS-JS的转换,高级别ES代码转换为低级别ES代码等。同时使用插件对整个工程进行扩展,比如注入环境变量等。最终生成打包的结果。说白了webpack是一个包含“翻译”和“打包”的过程,靠loader将各种资源进行处理,靠插件在打包的过程中完成所需的功能。
Q5:Babel的作用和原理,高级别语言特性哪些能向下哪些不能
我的回答:babel通过将源码转化成AST抽象语法树,根据不同的翻译要求,由语法树重新组合成目标代码。具体哪些特性不能被shim,我是真的没有想起来。
A5:Babel是一个JavaScipt的编译器
Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。功能主要包括:
- 语法转换
- 通过 Polyfill 方式在目标环境中添加缺失的特性 (通过 @babel/polyfill 模块)
- 源码转换 (codemods)
语法转换可以认为是不同写法的处理,比如Class语法转换成普通的原型写法。通过Polyfill的方式增加特性,也可以认为是利用低版本的特性,来实现高版本的特性,比如Promise的polyfill。源码转化则包含更多的翻译工作,比如Flow转换会将类型声明的内容删除等。
大部分的新语言特性,可以通过翻译的方式在ES2015的环境下得到相同的功能,比如箭头函数,字符串模板,结构,let/const,export/import等。
这里着重注意不支持的部分:迭代器(包含异步函数),Symbol,Array.from本身不支持,但是可以通过polyfill。
@babel/preset-env 最智能的包
@babel/preset-stage-0到@babel/preset-stage-3 被弃用,针对不同提案阶段的特性进行转换
@babel/preset-flow 针对flow语法进行转换
@babel/preset-react 包含jsx翻译等
@babel/preset-typescript 针对ts翻译
Q6:Http状态码中301和302分别是什么,了解过Http/2
2xx系状态码,3xx状态码,4xx状态码,5xx状态码,HTTP协议的状态码很重要就对了。HTTP/2和SPDY啥的见过没了解过,既然问肯定就跟前端会有关系。
A6:301 Move Permanently和302 Moved Temporarily
简而言之,3xx的状态码表达请求的资源已经不在这里,可能在别的地方。
301 Move Permanently资源永久移动,用作重定向,响应必须包含一个Location字段表示重定向目标,一般的浏览器都具备连接编辑功能,GET请求得到301响应,浏览器会自动跳转,所以301是网站由HTTP自动跳转HTTPS最好的方式。
302 Moved Temporarily资源暂时移动,一般情况下(不带特殊缓存头),该状态码不可缓存。响应中也包含Location字段,但是对于后续的请求仍然使用老的URL。当一个连接a使用302指向另一个连接b,对于搜索引擎来说,可能收录目标连接b,也有可能因为连接b的一些原因只收录连接a,这可能会引起收录“劫持”。
303 See Other
303主要目的是允许POST请求的响应将客户端定位到某个资源上
304 Not Modified,大名鼎鼎的未修改。这会牵扯到一个复杂的浏览器缓存问题,稍后会有专门的文章介绍
其他还有一些3xx系的状态码,就不在这具体展开了。
HTTP/2这里略提一些,稍后也有专门的文章学习。HTTP/2学名叫超文本传输协议第2版,主要基于SPDY协议。这里先列一些关键点:
- 请求方法,状态码以及大部分头部字段都和HTTP/1.1高度兼容
- Server Push(这一点我认为和前端相关性比较大)
- 请求管线化
- 修复队头阻塞问题
- 数据传输采用多路复用,使多个请求合并在一个TCP链接中
- 强制压缩,包含请求头
HTTP/2和SPDY的区别有哪些呢?
HTTP/2的开发基于SPDY进行跃进式改进。在诸多修改中,最显著的改进在于,HTTP/2使用了一份经过定制的压缩算法,基于霍夫曼编码,以此替代了SPDY的动态流压缩算法,以避免对协议的Oracle攻击——这一类攻击以CRIME为代表。此外,HTTP/2禁用了诸多加密包,以保证基于TLS的连接的前向安全。
Q7:聊一下Node如何实现多进程,EventLoop怎么Loop的
玩JS怎么都躲不过EventLoop,因为多线程特性基本上是各个编程语言都具备的,且与编程思想紧密关联,这个不了解是不可能的。
A7:多线程、多进程以及EventLoop
线程和进程这种字眼儿真是学了忘忘了学,重新再看看:
进程(Process):在面向进程的操作系统下,是基本的执行实体;在面向线程的操作系统下,是线程的容器。用比较直观的说法就是,用户双击运行一个程序,就会产生一个独立的进程,有些程序支持启动多个进程相互独立。打开任务管理器,或者活动监视器,你就能看到一堆进程信息,占用的CPU资源,启动的线程数,GPU资源等。
线程(Thread):操作系统中运算调度的最小单位。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
进程与线程的区别:进程是计算机管理运行程序的一种方式,一个进程下可包含一个或者多个线程。线程可以理解为子进程。
CPU的核心数和线程数:一般来说CPU的核心数和同时执行线程数相同,而有了超线程技术后,就出现了“双核四线程”这样的线程数超过核心数的情况。所以线程数就表达了这款CPU同一时刻有多少个线程同时能被运算。
Node.js® 是一个基于 Chrome V8 引擎 的 JavaScript 运行时。
Node.js是JavaScript的运行时,而“运行时”在wiki上的定义是指整个运行时期。全局安装的Node.js的前提下,命令行陨星node命令启动REPL,在任务管理器中你就会发现新建了一个node进程。所以Node.js也被叫做”能够在服务端执行JavaScript的运行环境“。Node.js使用V8引擎作为JavaScript的运行引擎,同时包含运行依赖库,以及和底层交互的依赖库等。
那么所谓的”Node.js是以单线程运行“,其实表达的是你编写的程序在Node.js环境下运行是单线程的模式,或者JavaScript代码在V8引擎中是单线程运行的。
Node.js的事件循环官方介绍看这里。单线程在执行I /O时理论上是要等待的,但这明显不符合现代编程需要,所以Node.js通过事件循环来实现非阻塞I/O。一个时间循环包含诸多阶段,每个阶段逐个完成,每个阶段都执行完后再回到第一个阶段。每个阶段包含一个FIFO的回调队列,先执行这个阶段特定的操作,然后清空队列(或者到上限),才会进入下一阶段。
Node.js通过将事件循环注册到操作系统的方式,来响应请求。产生连接时,操作系统产生一个回调,这些回调会在事件循环中被处理。所以在Node.js中用户请求并不是对应到单独的线程,而是对应到具体的回调。
使用http模块可以创建一个简单的服务器,形如:
const http = require('http');
const server = http.createServer((request, response) => {
// magic happens here!
});
调用createServer提供的回调函数,其实就相当于处理一个链接的逻辑,以此可以稍微具体的看到在Node.js中连接与回调之间的关系。
说了一大通,忘了介绍Node.js如何进行多进程操作的。Node.js通过child_process模块来创建子进程:
- child_process.exec(command[, options], callback),使用子进程执行命令,命令结果将传递到callback中。注意,这里是可以通过子进程来执行”node xxx.js“的,通过全局对象process来获取当前进程的一些变量,比如process.argv表示运行起该进程的命令参数集合。比如我们使用”node some.js ‘args’“启动一个Node.js程序,在some.js或者整个应用中,可以通过process.argv[2]来获取到‘args’这个字符串。
- child_process.spawn(command, args),和exec差不多,只不过参数从command中独立出来。注意,该方法产生子进程是异步的,不会阻止父进程的事件循环,通过on('event name',callback)来响应子进程的各种窗台
- child_process.fork(modulePath, args),直接提供一个可执行的Node.js模块,并以此产生一个子进程。
通过上述方法建立的子进程,通过stdin、 stdout 和 stderr 的管道与父进程进行通信,这些管道通过stdout.on('data',callback)的形式在父进程中监听信息,且管道有容量限制。比如当子进程想stdout中写入大量内容,且没有将这些输出消耗掉,缓冲区满了会导致子进程阻塞,直到缓冲区可以继续写入内容。初次之外还要注意创建同步进程:
child_process.spawnSync()、child_process.execSync() 和 child_process.execFileSync() 方法是同步的,并且将会阻塞 Node.js 事件循环、暂停任何其他代码的执行,直到衍生的进程退出。
使用 child_process.spawn()、child_process.exec()、child_process.execFile() 或 child_process.fork() 方法会创建 ChildProcess 的实例:
- 当子进程的stdio流关闭时触发close事件
- 子进程中subprocess.disconnect()调用会产生disconnect事件,不能发送或接受消息
- 无法衍生进程,无法发送消息,无法杀死进程,都会导致error事件
- 退出事件exit,正常退出会有退出码,default为null,接到信号终止,将通过第二个参数标明
- 子进程通过process.send()发送信息时触发message事件
更多信息请查看Node.js官方文档child_process模块一章。
当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »