二月

二月 查看完整档案

上海编辑  |  填写毕业院校  |  填写所在公司/组织 github.com/hyy1115 编辑
编辑

我不入地狱谁入地狱

个人动态

二月 赞了文章 · 5月10日

windows下nvm、nrm的安装及使用

最近写项目的时候,需要切换node版本,于是发现了nvm(Node Version Manager),也就是Node版本管理工具。使用它可以很方便的在同一台windows机器上安装并维护多个Node的版本。

一、nvm

1.nvm下载
首先,如果你已经单独安装了node,建议先卸载,卸载完了再接着往下看。另外Npm的包也需要卸载。请看下面的官网截图说明。只有把这两个删除了,才能在电脑上正常安装并使用nvm。

clipboard.png

nvm的下载地址:https://github.com/coreybutle...。下载包地址:https://github.com/coreybutle...。选择第一个 nvm-noinstall.zip ,然后解压在系统盘(一般开发相关的文件我都放C盘,但是放别的盘也是可以的)。我放的目录路径是C:\dev\nvm。解压出来的文件有:

 +  elevate.cmd
 +  elevate.vbs
 +  install.cmd
 +  LICENSE
 +  nvm.exe

2.修改settings.txt

双击 install.cmd ,是以控制台形式显示的,第一下直接按回车,然后会在C盘根目录产生settings.txt,把这个文件放进刚刚解压的那个目录,然后修改settings.txt内容,改成下面那样:

 root: C:\dev\nvm
 path: C:\dev\nodejs 
 arch: 64
 proxy: none 
 node_mirror: http://npm.taobao.org/mirrors/node/
 npm_mirror: https://npm.taobao.org/mirrors/npm/

但是有些人很不幸,这个方法行不通,因为打开 install.cmd按下回车后,显示拒绝访问注册表路径,并弹出一个settings.txt。这时候,你只要淡定地叉掉那个文本以及控制台,然后在刚刚的目录里新建一个文件settings.txt,最后把上面的内容复制进去就可以了。

root : nvm的存放地址
path : 存放指向node版本的快捷方式,使用nvm的过程中会自动生成。一般写的时候与nvm同级。
arch : 电脑系统是64位就写64,32位就写32
proxy : 代理

3.配置环境变量

  • 以控制台方法执行成功的,在环境变量里会自动配置了 NVM_HOME 和 NVM_SYMLINK ,这时候只要修改相应的路径就行了。
  • 直接创建settings文件的可以在环境变量里创建 NVM_HOME 和 NVM_SYMLINK,并添加路径
  • 要是嫌弃可视化界面打开环境变量的步骤太麻烦,可以直接使用 windows+r => sysdm.cpl
    NVM_HOME: C:\dev\nvm
    NVM_SYMLINK : C:\dev\nodejs
  • 在PATH里加上;%NVM_HOME%;%NVM_SYMLINK%;。
    一键控制台install的还要检查 环境变量 PATH 上的路径有没有添加C:\dev\nvm以及C:\dev\nodejs,有的话就删掉。

4.检测安装结果

打开控制台,输入:nvm -v,若是出现版本信息,则安装。若报错,那就重新安装。

检查环境变量是否配置成功:可以在控制台输入:set [环境变量名],查看路径是否填写错误

5.使用node

(1)打开一个cmd窗口输入命令:nvm -v ,那么我们会看到当前nvm的版本信息。

clipboard.png

(2)然后我们可以安装nodejs了。继续输入命令:nvm install latest 如果网络畅通,我们会看到正在下载的提示,下载完成后 会让你use那个最新的node版本。

clipboard.png

(3)如果你是第一次下载,在use之前,C:dev目录下是没有nodejs这个文件夹的,在输入比如: nvm use 5.11.0 之后,你会发现,C:dev目录下多了一个nodejs文件夹,这个文件夹不是单纯的文件夹,它是一个快捷方式,指向了 C:devnvm 里的 v5.11.0 文件夹。
(4)同样的咱们可以下载其他版本的nodejs,这样通过命令:nvm use 版本号 比如:nvm use 5.11.0就可以轻松实现版本切换了。
(5)如果你的电脑系统是32 位的,那么在下载nodejs版本的时候,一定要指明 32 如: nvm install 5.11.0 32 这样在32位的电脑系统中,才可以使用,默认是64位的。

二、nrm

首先,什么是nrm?
nrm就是npm registry manager 也就是npm的镜像源管理工具,有时候国外资源太慢,那么我们可以用这个来切换镜像源。我们只要通过这个命令: npm install -g nrm 就可以实现安装。

clipboard.png

注意-g可以直接放到install的后面,我们以后也最好这样用,因为这样用,我们可以在cmd中上下箭头切换最近命令的时候,容易修改,更方便操作。安装完成后,我们就可以使用了。

命令:nrm ls 用于展示所有可切换的镜像地址
命令:nrm use cnpm 我们这样就可以直接切换到cnpm上了。当然也可以按照上面罗列的其他内容进行切换。

三、nrm切换npm源利器

在使用npm时,官方的源下载npm包会比较慢,国内我们基本使用淘宝的源,最近公司内部搭建了一套npm私有仓库。要添加自己公司内部的npm源,公司内部的源不可能把npm官方的npm包都全量同步,故需要npm源之间的切换,如果使用npm registry xxx的话,太不好管理了。nrm是管理npm源切换的利器。使用方法如下:
安装nrm

npm install -g nrm

nrm --help

Usage: nrm [options] [command]

Commands:

ls                           list all the registries
current                      show current registry name
use <registry>               change registry to registry
add <registry> <url> [home]  add one custom registry
del|rm <registry>            delete one custom registry
home <registry> [browser]    open the homepage of registry with optional browser
test [registry]              show response time for specific or all registries
help                         print this help

Options:

-h, --help     output usage information
-V, --version  output the version number

主要使用ls和use命令
1)nrm ls是列出来现在已经配置好的所有的原地址

nrm ls

npm ---- https://registry.npmjs.org/
* cnpm --- http://r.cnpmjs.org/
taobao - http://registry.npm.taobao.org/
nj ----- https://registry.nodejitsu.com/
rednpm - http://registry.mirror.cqupt.edu.cn
npmMirror  https://skimdb.npmjs.com/registry

2)nrm use是切换到哪个源上

nrm use npm

3)nrm add添加源
4)nrm del删除源
5)nrm test测试源的响应时间,可以作为使用哪个源的参考

参考文献:http://blog.csdn.net/tyro_jav...

查看原文

赞 7 收藏 5 评论 0

二月 赞了文章 · 4月8日

初级前端工程师面试所具备的知识梳理

初级前端工程师面试所具备的知识梳理


目录

一面/二面


一面/二面


网页布局

相对定位布局实现自适应
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .layout .box1{
            width: 33.3%;
            height: 300px;
            background-color: rosybrown;
            float: left;
            position: relative;
        }
        .layout .box2{
            width: 33.3%;
            float: left;
            height: 300px;
            background-color: brown;
            position: relative;
        }
        .layout .box3{
            width: 33.3%;
            float: left;
            height: 300px;
            background-color: aquamarine;
            position: relative;
        }
    </style>
</head>
<body>

<section class="relative layout">
    <div class="box1"></div>
    <div class="box2">相对定位自适应</div>
    <div class="box3"></div>
</section>

</body>
</html>
绝对定位布局自适应
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .layout {
            width: 100%;
        }
        .layout .box1{
            width: 33.3%;
            height: 300px;
            background-color: rosybrown;
            position: absolute;
            left: 0;
        }
        .layout .box2{
            width: 33.3%;
            height: 300px;
            background-color: brown;
            position: absolute;
            right: 0;
            left: 0;
            top: 0;
            bottom: 0;
            margin: 0 auto;
        }
        .layout .box3{
            width: 33.3%;
            height: 300px;
            background-color: aquamarine;
            position: absolute;
            right: 0;
        }
    </style>
</head>
<body>

<section class="relative layout">
    <div class="box1"></div>
    <div class="box2">相对定位自适应</div>
    <div class="box3"></div>
</section>

</body>
</html>

BFC

概念

块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视化CSS渲染的一部分,是布局过程中生成块级盒子的区域,也是浮动元素与其他元素的交互限定区域。

下列方式会创建块格式化上下文:
  • 根元素或包含根元素的元素
  • 浮动元素(元素的 float 不是 none)
  • 绝对定位元素(元素的 position 为 absolute 或 fixed)
  • 行内块元素(元素的 display 为 inline-block)
  • 表格单元格(元素的 display为 table-cell,HTML表格单元格默认为该值)
  • 表格标题(元素的 display 为 table-caption,HTML表格标题默认为该值)
  • 匿名表格单元格元素(元素的 display为 table、table-row、 table-row-group、table-header-group、table-footer-group(分别是HTML table、row、tbody、thead、tfoot的默认属性)或 inline-table)
  • overflow 值不为 visible 的块元素
  • display 值为 flow-root 的元素
  • contain 值为 layout、content或 strict 的元素
  • 弹性元素(display为 flex 或 inline-flex元素的直接子元素)
  • 网格元素(display为 grid 或 inline-grid 元素的直接子元素)
  • 多列容器(元素的 column-count 或 column-width 不为 auto,包括 column-count 为 1)
  • column-span 为 all 的元素始终会创建一个新的BFC,即使该元素没有包裹在一个多列容器中(标准变更,Chrome bug)。

块格式化上下文包含创建它的元素内部的所有内容.

BFC子元素即使是float也会参与父元素的高度计算

DOM事件

DOM事件的级别
  • DOM0 element.onclick = function(){}
  • DOM2 element.addEventListener('click',function(){},false)
  • DOM3 element.addEventListener('keyup',function(){},false) 新增诸多鼠标键盘事件
DOM事件模型
  • 自上而下捕获,自下而上冒泡
DOM事件流
  • 第一阶段 捕获
  • 第二阶段 目标阶段 (到达目标元素)
  • 第三阶段 冒泡阶段 (从目标元素冒泡到window对象)
描述DOM事件捕获的具体流程
  • window --> document --> html --> body --> 父级元素 --> 目标元素
EVENT对象的常见应用
  • event.preventDefault() 阻止默认事件
  • event.stopPropagation() 阻止冒泡事件
  • event.stopImmediatePropagation() 在A中阻止B事件
  • event.currentTarget 当前绑定事件的对象
  • event.target 应用于事件委托
自定义事件
var eve = new Event('custome');

ev.addEventListener('custome',function(){
console.log('custome');
})

ev.dispatchEvent(eve)
  • DEMO
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <script>
        const eve = new Event('warning');
        console.log(eve);

    </script>
</head>
<body>

<button class="test">测试自定义事件</button>

<script>
    document.querySelector('.test').addEventListener('warning',function () {
         alert('你好,我是自定义事件warning!');
    });
    document.querySelector('.test').dispatchEvent(eve);
</script>

</body>
</html>

HTTP协议

HTTP协议的主要特点
  • 简单快速(URI固定)
  • 灵活()
  • 无连接(连接一次就会断掉不会保持连接)
  • 无状态(客户端与服务端是两种身份)
HTTP报文的组成部分
  • 请求报文

    • 请求行(HTTP方法,链接地址,http协议以及版本)
    • 请求头(Key and value)
    • 空行(告诉服务端解析请求体)
    • 请求体
  • 响应报文

    • 状态行(HTTP版本,状态码)
    • 响应头
    • 空行
    • 响应体
HTTP方法
  • get ----->获取资源
  • post ----->传输资源
  • put ---->更新资源
  • delete ---->删除资源
  • head ---->获得报文收首部
Post与Get的区别
  • GET在浏览器回退是无害的,而POST会再次提交请求。
  • GET产生的URL地址可以被收藏,而POST不可以。
  • GET请求会被浏览器主动缓存,而POST不会,除非手动设置。
  • GET请求只能进行URL编码,而POST支持多种编码格式。
  • GET请求参数会被完整保留在浏览器历史记录中,而POST中的参数不会被保留。
  • GET请求在URL中传送的参数是有长度限制的,而POST没有限制。
  • 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
  • GET比POST更不安全,因为GET参数会暴露在URL中,所以不能用来传递敏感信息。
  • GET参数通过URL传递,POST放在Request body中。
HTTP状态码
  • 1xx:指示信息-表示请求已经接收,继续处理
  • 2xx:成功-表示请求已经被接收
  • 3xx:重定向-要完成请求必须进步性更近异一步的操作
  • 4xx:客户端错误-请求有语法错误或者请求无法实现
  • 5xx:服务器错误-服务器未能实现合法的请求或者服务器内部代码有问题

进一步举例
  • 200 OK:客户端请求成功
  • 206 Partial Content:客户发送了一个带有Range头的GET请求,服务器完成了它
  • 301 Moved Permanently:所请求的页面已经转移至新的URL
  • 302 Found:所请求的页面已经临时转移至新的url
  • 304 Not Modified:客户端有缓冲的文档并发出了一个条件请求,服务器告诉客户端,原来缓冲的文档还可以继续使用
什么是持久连接
  • HTTP协议采用“请求-应答”模式,当使用普通模式,即非Keep-Alive模式时,每个请求/应答客户和服务器都要创建一个连接,完成之后立即断开连接(HTTP协议为无连接的协议)
  • 当使用Keep-Alive模式(又称持久连接,连接重用)时,Keep-Alive功能使得客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接
什么是管线化
  • 在使用持久连接的情况下,某个连接上的消息的传递类似于
请求1->响应1->请求2->响应2->请求3->响应3
  • 管线化指的是在持久连接的基础上把所有的请求一次性打包过去,类似于
请求1->请求2->请求3->响应1->响应2->响应3
特点
  • 管线化机制通过持久连接完成,仅HTTP/1.1支持此技术
  • 只有GET和HEAD请求可以进行管线化,而POST则有所限制
  • 初次创建连接的时候不应启动管线机制,因为对方(服务器)不一定支持HTTP/1.1版本的协议
  • 管线化不会影响响应的顺序,如上面的例子所示,响应返回的顺序并未改变
  • HTTP/1.1要求服务器端支持管线化,但是并不要求服务器端也对响应进行管线化处理,只是要求对于管线化的请求不失败即可
  • 由于上面提到的服务器端问题,开启管线化很可能并不会带来大幅度的性能提升,而且很多服务器端和代理程序对于管线化的支持并不好,因此现代浏览器如chrome和firefox默认并未开启管线化支持

原型链

创建对象有几种方法
const obj = {name:'张三'};

const obj1 = new Object({name:'张三'});
const obj = function (name){
this.name = name
}

const obj1 = new obj('张三');
const obj = {name:'张三'}

const obj1 = Object.create(obj);

//
原型、构造函数、实例、原型链

alt

  • 其实,理解原型链只需要看懂下面代码就可以了。

  Function.prototype = {
        constructor : Function,
        __proto__ : parent prototype,
        some prototype properties: ...
    };

/**
* @time  2019/2/20 11:11
* @author  Bill Wang <vuejs@vip.qq.com>
* @desc   函数的原型对象constructor默认指向函数本身,
*         原型对象除了有原型属性外,为了实现继承,还
*         有一个原型链指针__proto__,该指针指向上一
*         层的原型对象,而上一层的原型对象的结构依然
*         类似,这样利用__proto__一直指向Object的原
*         型对象上,而Object的原型对象
*         用Object.prototype.__proto__ = null表示原
*         型链的最顶端,如此变形成了javascript的原型链
*         继承,同时也解释了为什么所有的javascript对象
*         都具有Object的基本方法。
*/
instanceof的原理

alt

  • instanceOf的原理就是判断实例对象的__proto__属性和构造函数的prototype是不是同一个引用。
Demo
const obj = function (name){
this.name = name
}

const obj1 = new obj('张三');

console.log(obj1 instanceof obj)
console.log(obj1 instanceof Object)
new运算符
  • 步骤一:一个新对象被创建,它继承自foo.prototype
  • 步骤二:构造函数foo被执行,执行的时候,相应的参数会被传入,同时执行上下文(this)会被指定为这个新实例。new foo等同于new foo(),

只能用在不传递任何参数的情况下。

  • 步骤三:如果构造函数返回了一个“对象”,那么这个对象又会取代整个new出来的结果。如果构造函数没有返回对象,那么new出来的结果为步骤一创建的对象。

面向对象

类与实例
  • 类的声明
/**
 * 传统类的声明
 */

 function Animal(name) {
  this.name = name;
 }
 /**
  * ES6中类的声明
  */

  class Animal {

    constructor(name){
       this.name = name;
    }

    // other methods
  }
  • 生成实例

 /**
  * 实例化一个类
  */

  new Animal('鸽子');
类与继承
  • 如何实现继承

    • 继承的本质就是原型链
  • 继承的几种方式

    • 构造函数实现继承
      function Parent(name){
        this.name = name;
      }
    
      function Child(){
        //父类挂载在子类
        Parent.call(this);
        // Parent.apply(this);
      }
    
      //缺点
      //Parent原型链上的内容并没有被Child继承
    • 原型链实现继承
        function Parent(name){
           this.name = name;
         }
    
         function Child(){
    
         }
    
         Child.prototype = new Parent('鸽子');
    
         //缺点
         //不同的实例对象共享属性
    • 组合方式
           function Parent(name){
             this.name = name;
           }
    
           function Child(){
            Parent.call(this);
           }
    
          Child.prototype = new Parent();
    
          //缺点
          //父级的构造函数执行了两次(完全没必要)
    
    
    //-------------------------------------------------
    
          // 优化组合继承1
          function Parent(name){
             this.name = name;
           }
    
           function Child(){
            Parent.call(this);
           }
    
          Child.prototype = Parent.prototype;
    
          //缺点
          //无法区分一个对象是谁实例化的
    
      //------------------------------------------------
    
           // 优化组合继承2
                  function Parent(name){
                     this.name = name;
                   }
    
                   function Child(){
                    Parent.call(this);
                   }
    
                  Child.prototype = Object.create(Parent.prototype);
    
                  Child.prototype.constructor = Child;
    

通信类

什么是同源策略及限制
  • 同源策略是从一个源加载的文档或脚本如何来自另一个源的资源进行交互。
  • 这是一个用于隔离潜在恶意文件的关键的安全机制。
什么是源?

包含三部分:协议、域名、端口。三者有一不一样就算跨域。

什么是限制?

不是一个源的文档,你没有权利去操作另一个源。

  • Cookie、LocalStorage和IndexDB无法读取
  • DOM无法获取
  • AJAX请求不能发送
前后端如何通信
  • Ajax(一般只适合同源通信)
  • WebSocket(不受同源策略限制)
  • CORS (支持跨域通信也支持同源通信)
如何创建ajax
  • XMLHttpRequest对象的工作流程
  • 兼容性处理
  • 事件的触发条件
  • 事件的触发顺序
const XHR = XMLHttpRequest ? new XMLHttpRequest() : new Window.ActiveXObject('Microsoft');

XHR.open("get", "yourFile.txt", true);
XHR.send();
跨域通信的几种方式
  • JSONP

    • 原理

      • 主要原理就是利用script标签异步加载的原理工作的。
    • 实现
    function handleCallback(result) {
        console.log(result.message);
    }
    
    const jsonp = document.createElement('script');
    const element = document.getElementById('demo');
    jsonp.type = 'text/javascript';
    jsonp.src = 'http://localhost:8080?callback=handleCallback';
    element.appendChild(jsonp);
    element.removeChild(jsonp);
    • JSONP只支持get请求
    • JSONP有一个弊端就是需要服务器的配合,也就是服务器如果传输一段恶意代码,浏览器也会毅然决然的执行。
  • Hash(Hash改变页面不会刷新)

    • 场景:利用hash,场景是当前页面A通过iframe嵌入了跨域的页面B
    • 实现

      const B = document.getElementByTagName('iframe');
      B.src = B.src + '#' + 'data';
      
      window.onhashchange = function () {
         var data = window.location.hash;
      };
      
  • postMessage(H5新增)
  • WebSocket
  • CORS(可以理解为支持跨域通信的Ajax)

    • 实现
    fetch('http://example.com/movies.json')
      .then(function(response) {
        return response.json();
      })
      .then(function(myJson) {
        console.log(myJson);
      });
    • 原理 :浏览器会拦截ajax请求,如果ajax是跨域的。它会加一个origin。

安全类

CSRF
  • 基本概念和缩写

    • CSRF通常称为跨站请求伪造,英文名Cross-site request forgery缩写CSRF。
  • 攻击原理

    ALT

    • 网站某一个接口存在漏洞
    • 用户确实登录过
  • 防御措施

    • Token验证(访问API附带Token)
    • Referer验证(页面来源)
    • 隐藏令牌(隐藏在Http Header头中)
XSS
  • 基本概念以及缩写
  • 原理

    • 向页面内部注入JS

算法类

  • 排序

    • 快速排序
    • 选择排序
    • 希尔排序
    • 冒泡排序
  • 堆栈、队列、链表
  • 递归
  • 波兰式和逆波兰式

二面/三面


面试技巧

  • 知识面要广
  • 理解要深刻
  • 内心要诚实
  • 态度要谦虚
  • 回答要灵活
  • 要学会赞美

渲染机制

什么是DOCTYPE及作用
DTD(document type definition ,文档类型定义) 是一系列的语法规则,用来定义XML或者HTML的文件类型。浏览器会使用它来判断文档类型,决定使用何种协议来解析以及切换浏览器。

DOCTYPE是用来声明文档类型和DTD规范的,一个主要的用途便是文件的合法性验证。如果文件代码不合法,那么浏览器解析时便会出一些差错。

  • 常见的DOCTYPE

    • <!DOCTYPE html> HTML5
    • HTML 4.01 Strict
    • HTML 4.01 Transitional
浏览器渲染过程

alt

重排Reflow
  • 定义:DOM结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式来计算并根据计算结果将元素放到它该出现的位置,这个过程称为reflow。
  • 触发Reflow

    • 当增加、删除、修改DOM节点时,会导致Reflow或者Repaint
    • 当移动DOM的位置的时候,或者有动画的时候
    • 当修改CSS样式的时候
    • 当Resize窗口的时候(移动端设备不存在此类问题),或者滚动的时候
    • 当修改网页的默认字体的时候
重绘Repaint
  • 定义:当各种盒子的位置、大小以及其它属性,例如颜色、字体大小等都确定下来后,浏览器于是便把这些元素都按照各自的特性绘制一遍,于是页面的内容出现了,这个过程称为repaint。
  • 触发Repaint

    • DOM改动
    • CSS改动
布局Layout

JS运行机制

Demo引出
console.log(1);

setTimeout(() =>{
   console.log(2);
},0);

console.log(3);

//print code

1

3

2
  • 同步任务持续执行
  • 异步任务会挂起
  • JS运行机制,单线程优先执行同步任务
 console.log('A');

 while(true) {

 }
 console.log('B');
 
 //print code

 A
  • 此处是进入同步无限循环,永远也进入不到B

for(var i = 0;i<4;i++) {
 setTimeout(()=>{
   console.log(i);
 },1000)
}

//print code

4
4
4
4
  • 异步任务的放入时间和执行时间
  • 当时间到了之后,会把setTimeout扔进异步队列中
异步任务
  • setTimeout和setInterval
  • DOM事件
  • ES6中的Promise
总结
  • 理解JS的单线程的概念
  • 理解任务队列
  • 理解Event Loop
  • 理解哪些语句会放入异步任务队列
  • 理解语句放入异步任务队列的时机

页面性能

提升页面性能的方法有哪些?
  • 资源压缩合并,减少Http请求
  • 非核心代码异步加载——>异步加载的方式——>异步加载的区别

    • 异步加载的方式
     #(1)动态脚本的加载(也就是利用JS动态创建script标签加载)
     #(2)defer
     #(3)async
    • 异步加载的区别
    #(1)defer是在HTML解析完成之后才会执行,如果是多个,按照加载顺序依次执行
    #(2)async是在加载完之后立即执行,如果是多个执行顺序和加载顺序无关
  • 利用浏览器缓存——>缓存的分类——>缓存的原理

    • 缓存的分类

(1)强缓存

  Expires  Expires:Thu,21 Jan 2017 23:39:02 GMT(服务器的绝对时间)
  Cache-Control Cache-Control:max-age = 3600(浏览器的相对时间)

(2)协商缓存(与服务器协商一番)

  Last-Modified If-Modified-Since  Last-Modified:Wed,26 Jan 2017 00:35:11 GMT
  Etag If-None-Match
  • 使用CDN
  • 预解析DNS
<meta http-equiv = "x-dns-prefetch-control" content = "on">(强制打开a标签的DNS预解析)
<link rel = "dns-prefetch" href = "//host_name_to_prefetch.com">

错误监控

前端错误的分类
  • 即时运行错误:代码错误
  • 资源加载错误
错误的捕获方式
  • 即时运行错误
#(1)try catch
#(2)window.onerror
  • 资源加载错误(不会冒泡)
#(1)object.onerror
#(2)performance.getEntries()  [获取资源加载时长,返回一个数组]
#(3)Error事件捕获

window.addEventListener('error',function(e){
console.log('捕获'+e)
},true)

  • 延伸:跨域的js运行错误可以捕获吗,错误提示是什么,应该怎么处理?
Resource interpreted as script but transferred
错误信息:Script error
出错行号:0
出错列号:0
错误详情:null
  • 在script标签中增加crossorigin属性
  • 设置js资源响应头Access-Control-Allow-Origin:*
上报错误的基本原理
  • 采用Ajax通信的方式上报
  • 利用Image对象上报

(new Image()).src = 'http://www.baidu.com'
查看原文

赞 1 收藏 0 评论 0

二月 赞了文章 · 4月8日

『Node.js』FFmpeg与Node.js (2) Node.js 操作 FFmpeg 工具选择

我们先来看看node如何操作ffmpeg?一种是使用node C++插件操作,另外一种是使用命令组合方式操作。
在GitHub中搜索了两种实现方式:

  1. 使用C++插件实现的有ffmpeg.js
  2. 使用命令组合方式的是node-fluent-ffmpeg

因为命令组合方式能快速理解使用方式,为了快速完成需求,选择了后者node-fluent-ffmpeg

node-fluent-ffmpeg

This library abstracts the complex command-line usage of ffmpeg into a fluent, easy to use node.js module.

这个库将ffmpeg的复杂命令行用法抽象为一个流,使得它是一个易于使用的node.js模块。

node-fluent-ffmpeg可以操作ffmpeg 和 ffprobe

为了更深入了解这个库和优化视频加载速度,简单阅读了一下该库的源码。该库整体围绕着Node.js的子进程知识点。

image.png

文件与文件夹解析
options命令
preset预设
capabilities.js检查是否有ffmpeg
ffprobe.jsfprobe方法封装
fluent-ffmpeg.js主体
processor.js进程封装
recipes.js常用方法
utils.js工具

其中最重要的知识点是子进程child_process操作。

node-fluent-ffmpeg库的使用

node-fluent-ffmpeg使用简便。引入后可以直接使用。并且整体是链式操作,使用对象上的方法非常方便。

const ffmpeg = require('fluent-ffmpeg');
const command = ffmpeg('/path/to/file.avi')
    .videoBitrate('1024k')
    .videoCodec('mpeg4')
    .size('720x?')
    .audioBitrate('128k')
    .audioChannels(2)
    .audioCodec('libmp3lame')
    .outputOptions(['-vtag DIVX']);

为了能高效的实现需求,最终选择使用node-fluent-ffmpeg。

查看原文

赞 7 收藏 5 评论 0

二月 赞了文章 · 2019-12-26

# Node.js葵花宝典

Node.js葵花宝典

前言

​ 欲练此功,必先自宫;

​ 不必自宫,亦可练成;

​ 兄台还是好好修炼此功吧!保持一个清醒的头脑,你将驾驭这匹野马!!!

—— 致读者

​ 知识就像海洋一样,永远也学不完,但是不断精益求精是一种态度,是对新事物的一种持续保持瞻望的态度,但愿你在学习的乐园里不断完善己身,不断修炼,等待破茧成蝶。

文档风格

  • 书名采用 #
  • 大标题采用##
  • 单元小结标题采用####

​ —— 致开发者

​ 好的书写风格能够让读者的思路清晰,同样能让人有继续阅读的兴趣,但愿你能按照此风格继续完善本书籍。

第一篇 为何偏爱Node.js

1.1 引子

  • 前端职责范围变大,为了统一流程开发体验
  • 在处理高并发,I/O密集型场景性能优势足够明显

1.2 CPU密集 ==VS== IO密集

  • CPU密集:压缩、解压、加密、解密
  • I/O密集:文件操作、网络操作、数据库

1.3 Web常见的场景

  • 静态资源获取
  • ......

1.4 高并发对应之道

  • 增加物理机的个数
  • 增加每台机器的CPU数------多核

1.5 关于进程线程那些事儿

  • 进程:用一句比较接地气的一句话叙述就是,执行中的程序就叫做进程。
  • 多进程:启动多个进程处理一个任务。
  • 线程:进程内一个相对独立、可以调度的执行单元,与同属一个进程的线程共享进程资源。

1.6 再来谈谈Node.js的单线程

  • 单线程只是针对主进程,I/O操作系统底层进行多线程调度。也就是它仅仅起一个监听作用。

    ###  举个栗子叭
         场景:饭店
         情节:人流量高并发
         BOSS的策略:雇佣多名厨师,只雇佣一个服务员,当多名顾客点餐的时候,服务员告诉厨师,做菜,上菜。
  • 单线程并不是只开一个进程。

    ###  Node.js中的cluster(集群)模块
         官方介绍:单个 Node.js 实例运行在单个线程中。 为了充分利用多核系统,有时需要启用一组 Node.js 进程去处理负载任务。
         Demo:
    const cluster = require('cluster');
    const http = require('http');
    const numCPUs = require('os').cpus().length;
    
    if (cluster.isMaster) {
      console.log(`主进程 ${process.pid} 正在运行`);
    
      // 衍生工作进程。
      for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
      }
    
      cluster.on('exit', (worker, code, signal) => {
        console.log(`工作进程 ${worker.process.pid} 已退出`);
      });
    } else {
      // 工作进程可以共享任何 TCP 连接。
      // 在本例子中,共享的是 HTTP 服务器。
      http.createServer((req, res) => {
        res.writeHead(200);
        res.end('你好世界\n');
      }).listen(8000);
    
      console.log(`工作进程 ${process.pid} 已启动`);
    }
  • 友情链接Cluster

1.7 Node.js的常用场景

  • Web server
  • 本地代码构建

第二篇 环境&调试

2.1 环境安装

2.2 环境必须

  • CommonJS

    • 每个文件都是一个模块,有自己的作用域。(它会自动包裹函数)
    • 在模块内部module变量代表模块本身。
    • module.exports属性代表模块对外接口。

      ModuleDemo.js

      console.log('this is a module');
      
      const testVar = 100;
      
      function test () {
          console.log(testVar);
      }
      
      module.exports.testVar = testVar;
      module.exports.testFn = test;
    • 使用模块之require规则

​ - /表示绝对路径,./表示相对于当前文件的路径。

​ - 支持jsjsonnode拓展名

​ - 不写路径则认为是build-in模块或者各级node-modules内的第三方模块

CommonJS Use Module

const modu = require('ModuleDemo.js');

console.log(modu.testVar);

console.log(modu.test);
    • require特性

      • module被加载的时候执行,加载后缓存。(后边这一句的意义就是,只加载一次,加载完缓存)【注:可以做一个小test,在一个test文件中,同时加载两次模块,你就会发现其中的奥秘了。】
      • 一旦出现某个模块被循环加载,就只会输出已经执行的部分,还未执行的部分不会输出。
    • Global
    • Process

    2.3 Node.js引用系统模块与第三方模块

    • 引用系统模块

      const fs = require('fs');
      
      const result = fs.readFile('fs.js',( err, data) => {
          if (err) {
              return err;
          }
          
          console.log(data);
      });
      
      console.log(result);
      
    • 引用第三方模块

      npm i chalk -S
      const chalk = require('chalk');

    2.4 exports与module.exports

    {
        function(exports,require,module,__filename,__dirname) {
            // code
        }
    }
    • 简而言之,exports就是module.exports的一个简写,也就是一个引用,别名。

      exports = {
          a: 1,
          b: 2,
          test: 123
      }
      //这样是错误的
      exports.test = 100;
      //只能添加属性,但是不能修改其指向
      
      //当然
      module.exports = {
          a:1,
          b:2,
          test:123,
      }
      //这样是没问题的

    2.5 Global对象

    • CommonJS
    • Buffer、process、console
    • timer
    global.test = 200;

    2.6 process模块

    /**
     *  argv
     *  argv0 
     *  execArgv
     *  execPath
     */
    
    const {argv, argv0, execArgv, execPath} = require('process');
    
    argv.forEach( item => {
        console.log(item);
    })
    
    //打印当前工作进程的路径
    
    console.log(process.cwd());
    
    //setImmediate(fn),不需要任何时间参数,执行最慢
    //process.nextTick(fn)
    //二者的区别就是后者的执行会先于前者
    
    
    
    • 简单说明一下,就是process.nextTick()会把任务放在当前事件循环队列的队尾,而setImmediate()会把任务放在下一个队列的队首,而setTimeout()会把任务放在它俩中间。

    2.7 Debug

    第三篇 Node.js-API

    3.1 path

    和路径有关的内置模块

    • path.basename()取得一个路径的最后一部分文件名
    • path.normalize()帮助修正路径
    • path.join()用于路径拼接(参数为多个路径参数)
    • path.resolve()将一个相对路径解析为绝对路径
    • {basename, dirname, extname}

      • basename 完整名
      • dirname 上级路径名
      • extname 后缀名
    • {parse, format}

      • parse用于解析当前路径为一个json格式的数据
      • format相当于格式化json数据为一个字符串

    说明:__dirname__filename总是返回文件的绝对路径

    process.cwd()总是返回node命令所在文件夹

    3.2 Buffer

    三个要点:

    • Buffer用于处理二进制数据流
    • 实例类似整数数组,大小固定
    • C++代码在V8堆外分配物理内存

    Buffer常用的方法

    • byteLength统计buffer所占字节数
    • isBuffer用来判断目标数据是不是一个Buffer
    • concat合并连接Buffer
    • from将目标数据转换为Buffer
    • toString用来转换Buffer为字符串

    3.3 events

    • eventEmitter.on('eventName',callback())用于注册监听器
    • eventEmitter.emit('eventName')用于触发事件
    const EventEmitter = require('events');
    
    class CustomEvent extends EventEmitter {
        
    }
    
    const ce = new CustomEvent();
    
    ce.on('eventName',callback);
    
    ce.emit('eventName','your msg to eventEmit',....);
    
    //有一个通用参数就叫error
    
    ce.on('error',fn);
    
    //Example
    ce.on('error',(err, item) => {
        console.log(err);
        console.log(item);
    });
    
    ce.emit('error', new Error('出错了'), Date().now);

    针对事件只需要响应一次:

    ce.once('test', ()=> {
        console.log(test);
    });
    
    

    针对事件需要移除的话:

    ce.removeListener('eventName',fn);
    
    //or
    
    ce.removeAllListeners('test');

    3.4 fs

    ​ 首先需要注意的就是Node.js的设计模型就是错误优先的模式。

    fs.readFile('fileUrl', 'utf8', (err, data) => {
        if(err) throw err;
        
        console.log(data);
    })
    • stat()查看文件的详细信息

      const fs = require('fs');
      
      fs.stat('fileUrl', (err, data) => {
          if (err) {
              throw err;//这里可以判断文件是否存在
          }
          
          console.log(data);
      });
    • rename()更改文件名

      fs.rename('./text.txt','hahah.ttx');
    • unlink删除文件
    fs.unlink('fileName', err => err);
    • readdir()读取文件夹
    • mkdir()创建文件夹
    • rmdir()删除文件夹
    • watch()监视文件或目录的变化
    fs.watch('fileUrl', {
        recursive:true //是否监视子文件夹
    }, (eventType, fileName) => {
        console.log(eventType, fileName);
    })
    • readStream()读取流
    const rs = fs.createReadStream('urlPath');
    
    rs.pipe(process.stdout);//导出文件到控制台
    • writeStream()写入流
    • pipe()管道,导通流文件

      const ws  = fscreateWriteStream('urlPath');
      
      ws.write('some content');
      
      ws.end();
      
      ws.on('finish',()=>{
         console.log('done!!!'); 
      });

    第四篇 项目Init

    4.1 .gitignore

    • 匹配模式前/代表项目根目录
    • 匹配模式最后加\代表是目录
    • 匹配模式前加!代表取反
    • *代表任意一个字符
    • ?匹配任意字符
    • **匹配任意目录

    4.2 ESLint

    完整配置.eslintrc.js

    module.exports = {
        env: {
            es6: true,
            mocha: true,
            node: true
        },
        extends: ['eslint:recommended', 'plugin:sort-class-members/recommended'],
        parser: 'babel-eslint',
        plugins: ['babel', 'sort-class-members'],
        root: true,
        rules: {
            'accessor-pairs': 'error', // 强制 getter 和 setter 在对象中成对出现
            'array-bracket-spacing': 'off', // 强制数组方括号中使用一致的空格
            'arrow-parens': 'off', // 要求箭头函数的参数使用圆括号
            'arrow-spacing': 'error', // 强制箭头函数的箭头前后使用一致的空格
            'babel/arrow-parens': ['error', 'as-needed'],
            'babel/generator-star-spacing': ['error', 'before'],
            'block-scoped-var': 'error', // 强制把变量的使用限制在其定义的作用域范围内
            'block-spacing': 'off', // 强制在单行代码块中使用一致的空格
            'brace-style': 'off', // 强制在代码块中使用一致的大括号风格
            camelcase: 'off', // 强制使用骆驼拼写法命名约定
            'comma-dangle': 'off', // 要求或禁止末尾逗号
            'comma-spacing': 'off', // 强制在逗号前后使用一致的空格
            'comma-style': 'off', // 强制使用一致的逗号风格
            complexity: 'off', // 指定程序中允许的最大环路复杂度
            'computed-property-spacing': 'error', // 强制在计算的属性的方括号中使用一致的空格
            'consistent-return': 'off', // 要求 return 语句要么总是指定返回的值,要么不指定
            'consistent-this': 'error', // 当获取当前执行环境的上下文时,强制使用一致的命名
            'constructor-super': 'error', // 要求在构造函数中有 super() 的调用
            curly: 'off', // 强制所有控制语句使用一致的括号风格
            'default-case': 'error', // 要求 switch 语句中有 default 分支
            'dot-location': ['error', 'property'], // 强制在点号之前和之后一致的换行
            'dot-notation': 'off', // 强制在任何允许的时候使用点号
            'eol-last': 'off', // 强制文件末尾至少保留一行空行
            eqeqeq: ['error', 'smart'], // 要求使用 === 和 !==
            'func-names': 'off', // 强制使用命名的 function 表达式
            'func-style': ['error', 'declaration', { // 强制一致地使用函数声明或函数表达式
                allowArrowFunctions: true
            }],
            'generator-star-spacing': 'off', // 强制 generator 函数中 * 号周围使用一致的空格
            'id-length': ['error', { // 强制标识符的最新和最大长度
                exceptions: ['_', 'e', 'i', '$']
            }],
            indent: ['error', 4, { // 强制使用一致的缩进
                SwitchCase: 1
            }],
            'key-spacing': 'off', // 强制在对象字面量的属性中键和值之间使用一致的间距
            'keyword-spacing': ['off', { // 强制在关键字前后使用一致的空格
                overrides: {
                    case: {
                        after: true
                    },
                    return: {
                        after: true
                    },
                    throw: {
                        after: true
                    }
                }
            }],
            'linebreak-style': 'off',
            'lines-around-comment': 'off',
            'max-depth': 'error', // 强制可嵌套的块的最大深度
            'max-nested-callbacks': 'off',
            'max-params': ['error', 4],
            'new-cap': 'off',
            'new-parens': 'error', // 要求调用无参构造函数时有圆括号
            'newline-after-var': 'off',
            'no-alert': 'error', // 禁用 alert、confirm 和 prompt
            'no-array-constructor': 'error', // 禁止使用 Array 构造函数
            'no-bitwise': 'error', // 禁用按位运算符
            'no-caller': 'error', // 禁用 arguments.caller 或 arguments.callee
            'no-catch-shadow': 'off',
            'no-class-assign': 'error', // 禁止修改类声明的变量
            'no-cond-assign': ['error', 'always'], // 禁止条件表达式中出现赋值操作符
            'no-confusing-arrow': 'error', // 不允许箭头功能,在那里他们可以混淆的比较
            "no-console": 0,
            'no-const-assign': 'error', // 禁止修改 const 声明的变量
            'no-constant-condition': 'error', // 禁止在条件中使用常量表达式
            'no-div-regex': 'error', // 禁止除法操作符显式的出现在正则表达式开始的位置
            'no-dupe-class-members': 'error', // 禁止类成员中出现重复的名称
            'no-duplicate-imports': 'off', // disallow duplicate module imports
            'no-else-return': 'error', // 禁止 if 语句中有 return 之后有 else
            'no-empty-label': 'off',
            'no-empty': 'off',
            'no-eq-null': 'error', // 禁止在没有类型检查操作符的情况下与 null 进行比较
            'no-eval': 'error', // 禁用 eval()
            'no-extend-native': 'error', // 禁止扩展原生类型
            'no-extra-bind': 'error', // 禁止不必要的 .bind() 调用
            'no-extra-parens': 'error', // 禁止不必要的括号
            'no-floating-decimal': 'error', // 禁止数字字面量中使用前导和末尾小数点
            'no-implied-eval': 'error', // 禁止使用类似 eval() 的方法
            'no-inline-comments': 'error', // 禁止在代码行后使用内联注释
            'no-iterator': 'error', // 禁用 __iterator__ 属性
            'no-label-var': 'off',
            'no-labels': 'off',
            'no-lone-blocks': 'error', // 禁用不必要的嵌套块
            'no-lonely-if': 'off',
            'no-loop-func': 'error', // 禁止在循环中出现 function 声明和表达式
            'no-mixed-requires': 'error', // 禁止混合常规 var 声明和 require 调用
            'no-mixed-spaces-and-tabs': 'off',
            'no-multi-spaces': 'off',
            'no-multi-str': 'off',
            'no-native-reassign': 'error', // 禁止对原生对象赋值
            'no-nested-ternary': 'error', // 不允许使用嵌套的三元表达式
            'no-new-func': 'error', // 禁止对 Function 对象使用 new 操作符
            'no-new-object': 'error', // 禁止使用 Object 的构造函数
            'no-new-require': 'error', // 禁止调用 require 时使用 new 操作符
            'no-new-wrappers': 'error', // 禁止对 String,Number 和 Boolean 使用 new 操作符
            'no-new': 'error', // 禁止在非赋值或条件语句中使用 new 操作符
            'no-octal-escape': 'error', // 禁止在字符串中使用八进制转义序列
            'no-path-concat': 'error', // 禁止对 __dirname 和 __filename进行字符串连接
            'no-process-env': 'error', // 禁用 process.env
            'no-process-exit': 'error', // 禁用 process.exit()
            'no-proto': 'error', // 禁用 __proto__ 属性
            'no-restricted-modules': 'error', // 禁用指定的通过 require 加载的模块
            'no-return-assign': 'error', // 禁止在 return 语句中使用赋值语句
            'no-script-url': 'error', // 禁止使用 javascript: url
            'no-self-compare': 'error', // 禁止自身比较
            'no-sequences': 'error', // 禁用逗号操作符
            'no-shadow-restricted-names': 'error', // 禁止覆盖受限制的标识符
            'no-shadow': 'off',
            'no-spaced-func': 'off',
            'no-sync': 'off', // 禁用同步方法
            'no-this-before-super': 'error', // 禁止在构造函数中,在调用 super() 之前使用 this 或 super
            'no-throw-literal': 'error', // 禁止抛出非异常字面量
            'no-trailing-spaces': 'error', // 禁用行尾空格
            'no-undef-init': 'error', // 禁止将变量初始化为 undefined
            'no-undefined': 'off',
            'no-underscore-dangle': 'off',
            'no-unexpected-multiline': 'error', // 禁止出现令人困惑的多行表达式
            'no-unneeded-ternary': 'error', // 禁止可以在有更简单的可替代的表达式时使用三元操作符
            'no-unused-expressions': 'error', // 禁止出现未使用过的表达式
            "no-unused-vars": [1, { // 禁止出现未使用过的变量
                "vars": "all",
                "args": "after-used"
            }],
            'no-use-before-define': 'error', // 不允许在变量定义之前使用它们
            'no-useless-call': 'error', // 禁止不必要的 .call() 和 .apply()
            'no-useless-concat': 'error', // 禁止不必要的字符串字面量或模板字面量的连接
            'no-var': 'off',
            'no-void': 'error', // 禁用 void 操作符
            'no-warning-comments': 'off',
            'no-with': 'off',
            'object-curly-spacing': 'off',
            'object-shorthand': 'error', // 要求或禁止对象字面量中方法和属性使用简写语法
            'one-var': 'off',
            'operator-assignment': 'error', // 要求或禁止在可能的情况下要求使用简化的赋值操作符
            'operator-linebreak': 'off',
            'padded-blocks': 'off',
            'prefer-arrow-callback': 'off',
            'prefer-const': 'error', // 要求使用 const 声明那些声明后不再被修改的变量
            'prefer-spread': 'error', // 要求使用扩展运算符而非 .apply()
            'prefer-template': 'off', // 要求使用模板字面量而非字符串连接
            quotes: 'off',
            'quote-props': 'off',
            radix: 'error', // 强制在parseInt()使用基数参数
            'require-yield': 'error', // 要求generator 函数内有 yield
            "semi": ["error", "always"], // 要求或禁止使用分号
            'semi-spacing': 'off',
            'sort-vars': 'error', // 要求同一个声明块中的变量按顺序排列
            'space-before-blocks': 'off',
            'space-before-function-paren': 'off',
            'space-in-parens': 'off',
            'space-infix-ops': 'off',
            'space-unary-ops': 'off',
            'spaced-comment': 'off',
            strict: 'off',
            'valid-jsdoc': 'error', // 强制使用有效的 JSDoc 注释
            'vars-on-top': 'off',
            yoda: 'off',
            'wrap-iife': 'off',
            'wrap-regex': 'error' // 要求正则表达式被括号括起来
        }
    }

    第五篇 静态资源服务器

    5.1 http模块

    const http = require('http');
    const chalk = require('chalk');
    const hostname = '127.0.0.1';
    
    const port = '3000';
    const server = http.createServe((req, res) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Hello World');
    });
    
    server.listen(port, hostname, () => {
        const addr = `Server running at http://${hostname}:${port}/`;
        console.log(`${chalk.green(addr)}`);
    })
    • supervisor监视文件模块
    • hotnode热启动模块

    完整代码:请参照

    第六篇 测试

    6.1 断言测试

    • assert- 断言调试
    console.assert(a===b,msg);

    常用断言库:chai.js

    6.2 mocha.js

    //you should install mocha
    
    // $ npm install --save-dev mocha
    
    // you should know the '#math' only is it's description
    describe('#math', ()=> {
        describe('#add', ()=> {
            it('should return 5 when 2 + 3', () => {
                ecpect('your test','targetResult');
            });
        });
    });

    Note :如果你想只执行一个模块的,你可以在package.json中配置

    "scripts": {
      "test": "mocha /url/xxx.js
    }

    6.3 istanbul

    你可以参照)

    6.4 benchmark.js

    • 性能测试框架,具体参见
    //常见测试方法
    console.time('markPoint');
    
    console.timeEnd('markPoint');

    6.5 jsbench.js

    • 在FCC社区听老外告诉的,自己做了测试,确实是可以测试两段代码的性能差异。

    第七篇 UI测试

    7.1 传统测试

    ## 传统测试自然就是人工点击
    
    ## 弊端就是发现错误不健全

    7.2 webdriver

    参照

    第八篇 爬虫

    8.1 爬虫与反爬虫

    ==爬虫==:按照一定的规则自动抓取网络信息的程序

    ==反爬虫==:

    • User-Agent,Referer,验证码
    • 单位时间的访问次数,访问量
    • 关键信息用图片混淆
    • 异步加载

    8.2 爬虫思路

    思路一:使用cheerio与http/https

    • cheerio:一个可以像jQuery那样操作DOM元素
    • http/https:我就不用说了吧
    //简单示例
    const cheerio = require('cheerio');
    const https = require('https');
    let html = '';
    const $ = '';
    https.get('url',res => {
        res.on('data',(data) => {
            html += data;
        });
        res.on('finish',()=> {
            $ = cheerio.load(html);
            //some code
        })
    })

    思路二:puppeteer

    • 下边贴一段爬虫代码吧!

    index.js

    const puppeteer = require('puppeteer');
    const https = require('https');
    const fs = require('fs');
    (async () => {
        //跳过安装  npm i --save puppeteer --ignore-scripts
        const browser = await puppeteer.launch({
            executablePath: 'G:/chrome-win/chrome-win/chrome.exe'
        });
        const page = await browser.newPage();
        //指定浏览器去某个页面
        await page.goto('https://image.baidu.com');
        // await page.cookies('https://image.baidu.com')
        //     .then(data => {
        //         console.info(data)
        //     });
        //调大视口,方便截图,方便容纳更多地图
        await page.setViewport({
            width:2000,
            height:1000,
        });
        //模拟用哪个户输入
        await page.keyboard.sendCharacter('车模');
        //模拟用户点击搜索
        await page.click('.s_search');
        console.info('开始点击查询......');
        //await page.screenshot({path: 'example.png'});
         page.on('load', async()=> {
             console.info('页面已加载完毕,开始爬取');
            await page.screenshot({path: 'example.png'});
              let srcs = await page.evaluate(() => {
                 let img =  document.querySelectorAll('img.main_img');
                 return Array.from(img).map(item => item.src);
             });
             await srcs.forEach((item,index) => {
                      if(/^https:/g.test(item))
                      https.get(item,res =>{
                          res.pipe(fs.createWriteStream(__dirname + `/img/${index}.png`))
                              .on('finish',()=>{
                                  console.info(`已成功爬取${index + 1}条记录!`);
                              });
                      })
                  })
             await browser.close();
        });
         
    })();

    package.json

    {
      "name": "reptiles",
      "version": "1.0.0",
      "description": "This is a replite",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start": "node index.js"
      },
      "author": "vuejs@vip.qq.com",
      "license": "ISC",
      "dependencies": {
        "puppeteer": "^1.14.0"
      }
    }
    
    • How To Use?
    ## 初始化你的项目
    
    $ npm init
    
    ## 建立文件
    
    $ cd your project
    $ mkdir index.js
    ## 拷贝我的package.json
    ## 注意,这是重点,因为被墙,所以必须跳过安装
    $ npm i --save puppeteer --ignore-scripts
    
    ## 启动项目
    
    $ npm start
    
    • Note SomeDetails

      • 1.安装puppeteer,必须跳过Chromium 的连接
      • 2.安装好你的Chromium ,在lanuch的时候必须用绝对路径,切记不可把chrom.exe单纯拷贝到项目目录
      • 3.不可把浏览器视口调节的太大,不然会触碰反爬虫协议。
      • 4.如果你调大了也没关系,我们可以page.waitFor(200),让任务沉睡一会儿

    结语

      这篇大长文就算彻底结束了,我相信文章里面还有很多不足或者小错误,诚恳的希望大家不要喷我。

    ​    "你能不能,在于你想不想!"

    ​    加油,每一天都是新生。

    ​    对于这篇文章,我也会不断的完善它。

    查看原文

    赞 2 收藏 0 评论 1

    二月 关注了用户 · 2019-12-25

    不觉 @bigbigdreamer

    关注 1

    二月 赞了文章 · 2019-12-25

    盘点Chrome中实用的插件

    盘点Chrome中实用的插件

    github
    我是一名浪迹江湖的剑客,甘愿四海为家,一路行侠仗义,潇洒一生。

    前言

    工欲善其事,必先利其器。

    盘点一下一个前端Bug工程师在Chrome中必下的插件锦集。

    TOP1. Infinity New Tab - Productivity&Speed Dial

    Infinity新标签页:自由定制chrome新标签页;开启页面添加时代,无论你浏览那个页面,都能一步将网址添加到新标签页中;独创新标签页中谷歌邮件自动提醒功能,还有精美天气,待办事项,历史记录管理,应用程序管理,印象笔记一样的记事应用,高清壁纸,必应,百度,谷歌搜索。让你的使用更加简单方便快捷。

    top1

    TOP2.AdGuard AdBlocker

    广告拦截扩展对抗广告和弹出窗口。拦截 Facebook,Youtube 和所有其它网站的广告。
    AdGuard 广告拦截器可有效的拦截所有网页上的所有类型的广告,甚至是在 Facebook、Youtube 以及其他万千网站上的广告!

    至少在国内网站,我亲测可以拦截所有的广告,例如CSDN,很清爽,所以我把它放在了第二位。

    TOP3.Clean Master: the best Chrome Cache Cleaner

    一键清理您的浏览器缓存和垃圾,保护您的隐私,并使您的浏览器更快,更高效
    只需一次点击,使您的浏览器超快!

    比起手动点击清理而言,省事太多了!

    TOP3

    TOP4.Extension Manager

    一键管理所有扩展,快速开启/禁用、批量闪电管理,智能排序,右键卸载、锁定、选项配置,角标提醒,大小布局随心配。快捷、简单、安全。

    可能你跟我一样烦恼过,装这么多插件,浏览器会不会卡?这么乱,我还需要一个一个查找,好麻烦啊!但是不要慌,它来了~

    TOP4

    TOP5.Resolution Test

    An extension for developers to test web pages in different screen resolutions, with an option to define your own resolutions.

    是不是懵逼了?来,我可以告诉昂,这个插件主要作用就是改变浏览器的分辨率,作为前端功能作者,经常要去测试网页在不同分辨率设备的显示情况,现在,恭喜你,遇到了“爱情”,有时候缘分就是这样,很迅速~~~

    image

    TOP5.达达划词翻译

    划词翻译、生词本、吐司弹词、与扇贝同步

    我就不配图了昂,好难受,不对,我好懒,改天再补昂。

    TOP6.User-Agent Switcher for Chrome

    The User-Agent Switcher for Chrome is the answer. With this extension, you can quickly and easily switch between user-agent strings. Also, you can set up specific URLs that you want to spoof every time.

    哈哈哈,你又懵逼了,它主要就是修改浏览器的UA,也就是给浏览器换个外壳,伪装成别的浏览器。

    主要用途就是测试代码兼容性,其次是为了使用百度网盘的网页破解版,需要修改UA为Opera,启用百度网盘高速下载,说白了就是你不用冲会员了!

    传送门:PanDownload网页版

    TOP7.SetupVPN - Lifetime Free VPN

    Unblock any blocked website in your country, school or company. It's free and easy to use.
    Bypass any website that has been blocked by your government, school or company with just single click. SetupVPN comes with UNLIMITED bandwidth and it is completely FREE for everyone!

    额,不调侃你了,它就是为了让你有一个直达的“梯子”,懂吧,就是VPN,可以在浏览器访问google,不过就是有流量限制与时间限制,当然肯定也有网速限制得啦,想要高速的???来找我吧,小意思。

    TOP8.VisBug

    Open source web design debug tools built with JavaScript: a FireBug for designers.

    魔改一切在线网页,具体请看动图,我不偷懒了,我录制就是了,鉴于图片大小限制,只在gif中演示个别功能,还有一些强大的功能等待你去探索~~~哈哈哈嗝~

    TOP8

    THE END

    其实还有一些比较优秀的插件要推荐给各位的,例如:浏览器取色,截屏,量取网页大小,等等,但是我觉得都可以用一些更好的更轻量的软件去代替,请看传送门:

    古他妈黑暗之神:传送门

    我叫EricWang,我很开心认识你们,也很开心把知识分享给你们,希望我以及我分享的东西可以为你们带来轻松与快乐,谢谢。

    注意: 总结不易,如果需要转载请联系我~

    Email:vuejs@vip.qq.com

    查看原文

    赞 1 收藏 0 评论 0

    二月 收藏了文章 · 2019-12-25

    『Node.js』FFmpeg与Node.js (1) 初识FFmpeg与视频知识

    前言

    产品中有canvas制作的动画。需要把这个动画绘制一个视频的需求。
    经过调研,决定使用FFmpeg来生成视频。

    FFmpeg是什么?

    FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源程序。

    其中的Tools

    • ffmpeg:该项⽬提供的⼀个⼯具,可⽤于格式转换、解码或电视卡 即时编码等;
    • ffsever:⼀个 HTTP 多媒体即时⼴播串流服务器;
    • ffplay:是⼀个简单的播放器,使⽤ffmpeg 库解析和解码,通过 SDL显示;
    • ffprobe: 简单的多媒体码流分析器;

    视频知识

    为了选择正确的视频格式,还需要了解一些视频知识。

    • 常见封装格式
    • 常见编码格式
    • 码率

    常见封装格式

    封装格式就是视频文件的后缀,常见如下:

    • AVI 微软推出的视频封格式,比较旧而且占有空间大。
    • WMV 微软新推出的视频格式,缺点兼容差。
    • MOV 苹果推出的视频封装格式,优秀。
    • MP4 动态图像专家组(MPEG)推出的视频封装格式,跨平台兼容性好,是视频封装格式的首选

    常见编码格式

    视频编码可以分为帧内压缩和帧间压缩

    • 帧内压缩是对每一帧进行压缩,适合编辑时使用

      • ProRes 苹果开发的有损影片压缩技术
      • DNxHD Avid
      • GoPro Cineform
    • 帧间压缩是通过对多帧画面比较来进行压缩,文件大小较小,适合输出。

      • H.264(MPEG4)网络传播最佳
      • H.265(HEVC)未普及
      • VP9谷歌研发,与H.265争夺下一代视频编码之王

    码率

    8bits等于1byte,即1Mbps等于0.125MB/s。码率就是1秒内包含的视频信息,码率决定了视频的大小,也决定了视频的质量。

    未完待续!

    查看原文

    二月 赞了文章 · 2019-12-25

    『Node.js』FFmpeg与Node.js (1) 初识FFmpeg与视频知识

    前言

    产品中有canvas制作的动画。需要把这个动画绘制一个视频的需求。
    经过调研,决定使用FFmpeg来生成视频。

    FFmpeg是什么?

    FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源程序。

    其中的Tools

    • ffmpeg:该项⽬提供的⼀个⼯具,可⽤于格式转换、解码或电视卡 即时编码等;
    • ffsever:⼀个 HTTP 多媒体即时⼴播串流服务器;
    • ffplay:是⼀个简单的播放器,使⽤ffmpeg 库解析和解码,通过 SDL显示;
    • ffprobe: 简单的多媒体码流分析器;

    视频知识

    为了选择正确的视频格式,还需要了解一些视频知识。

    • 常见封装格式
    • 常见编码格式
    • 码率

    常见封装格式

    封装格式就是视频文件的后缀,常见如下:

    • AVI 微软推出的视频封格式,比较旧而且占有空间大。
    • WMV 微软新推出的视频格式,缺点兼容差。
    • MOV 苹果推出的视频封装格式,优秀。
    • MP4 动态图像专家组(MPEG)推出的视频封装格式,跨平台兼容性好,是视频封装格式的首选

    常见编码格式

    视频编码可以分为帧内压缩和帧间压缩

    • 帧内压缩是对每一帧进行压缩,适合编辑时使用

      • ProRes 苹果开发的有损影片压缩技术
      • DNxHD Avid
      • GoPro Cineform
    • 帧间压缩是通过对多帧画面比较来进行压缩,文件大小较小,适合输出。

      • H.264(MPEG4)网络传播最佳
      • H.265(HEVC)未普及
      • VP9谷歌研发,与H.265争夺下一代视频编码之王

    码率

    8bits等于1byte,即1Mbps等于0.125MB/s。码率就是1秒内包含的视频信息,码率决定了视频的大小,也决定了视频的质量。

    未完待续!

    查看原文

    赞 7 收藏 5 评论 0

    二月 评论了文章 · 2019-05-20

    前端系列——与众不同的移动端底部固定栏 fixed、absolute 兼容 iOS 和 Android 方案

    相信我,我分享的和你在其他博客上看到的终极方案是如此的与众不同!

    做过移动端开发的同学,对底部DOM定位出现的各种奇葩情况已经深恶痛绝了吧,底部DOM设置不同的position,在Android和ios上表现都不一样。

    为了兼容Android和ios,很多人都煞费苦心,也包括我。

    打开你做的H5,尤其是在微信上打开看看,是不是觉得很恶心,如果自我感觉很恶心,那么请往下看这篇文章,不恶心说明你成功了,可以走了!

    最终还是成功解决了,这篇文章记录一下兼容2种设备的方案。

    第一种情况

    据我所知,网上还找不到一个能够真正解决这个问题的教程,因为大多数人都是只考虑在body scroll的情况下,设置底部为fixed或者absolute,然后设置滚动区域padding-bottom的值,这种做法反正我是无法接受的,体验太不爽了,也没有兼容Android和ios。

    下图是第一种情况,滚动区域有表单,底部一个固定栏,当填写表单的时候,我们看看ios和Android的表现情况:

    1、底部定位为fixed的情况下

    ios:激活输入框时,底部不会弹出来(合理)。
    Android:激活输入框时,底部会跟着输入框弹出来(不合理)。

    2、底部定位为absolute的情况下

    ios:激活输入框时,底部不会弹出来(合理)。
    Android:激活输入框时,底部会跟着输入框弹出来(不合理)。

    android后遗症:输入框失焦的时候,可能导致底部显示在浏览器中间某个位置,回不到原位置。

    absolute后遗症:底部按钮和输入框区域一起随着body滚动,不再置顶独立。当滚动区域超过一屏幕时,底部输入框定位出现错乱。

    clipboard.png

    传统解决办法

    通常将底部设置为fixed,当激活输入框的时候,将底部定位改为relative,即可兼容ios和Android。

    第二种情况

    底部如果是个输入框的情况下,我们肯定需要输入框在激活的时候弹出来,和第一种情况是相反的。

    1、底部定位为fixed的情况下

    ios:激活输入框时,底部不会弹出来(不合理)。
    Android:激活输入框时,底部会跟着输入框弹出来(合理)。

    2、底部定位为absolute的情况下

    ios:当滚动区域超过一屏幕时,底部输入框定位出现错乱(不合理)。
    Android:当滚动区域超过一屏幕时,底部输入框定位出现错乱(不合理)。

    clipboard.png

    传统解决办法:

    仍旧是采用fixed定位

    ios:在激活输入框的时候,执行下面代码

    setTimeout(() => document.body.scrollTop = document.body.scrollHeight, 500)

    android: 表现正常

    传统解决方案的后遗症

    除了抖动问题,还有就是微信端滚动body会显示微信浏览器背景,也就是超出滚动边界回弹效应,还有一个恶心的问题是当有弹框的时候,弹框和body滚动累加的双重滚动会有点击穿透造成的卡顿问题。

    由此,如果你还寄希望于body滚动,那么你的移动端开发体验真的一塌糊涂。

    搭建真正的移动端滚动架构

    看到这里,你可以暂时把上面的传统解决方案统统忘记。

    下面我将会分享移动端最舒适的架构方案。

    1、你可能听过Iscroll,这个东西是我们今天要用到的框架的鼻祖,但我们不是用它,而是我曾经另外一篇文章介绍到的JRoll框架(比IScroll更加轻量和兼容的移动端滚动框架)。

    2、使用这款框架对我们解决底部定位问题还有优化弹框体验有什么帮助呢?他可以完美解决传统解决方案的后遗症,因为他并不是使用body滚动,而是使用css3滚动,采用GPU加速,在ios和Android上测试并不卡顿。如果你想做出像app一样流程的H5,别再用那恶心的body滚动了。

    源码(复制查看效果,别忘了导入js插件)

    下面的源码你可以直接复制到一个html文件上测试,代码中我提供了多种功能的解决方案:

    1、采用滚动框架时,何时获取滚动区域的高度(看源码)

    2、输入框底部固定时,在该框架中兼容ios和Android的方法(看源码)

    3、采用DocumentFragment动态渲染5000个列表元素,说到这个有点意思,记得腾讯某部门的社招面试题就是考察这个知识点,一般人可能采用的是for循环加innerHTML的方法(看源码)

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5, user-scalable=no">
        <title>Title</title>
        <style>
            * {
                padding: 0;
                margin: 0;
            }
            body, html {
                font-size: 24px;
                height: 100%;
            }
            ul {
                padding-bottom: 1rem;
            }
            ul li {
                list-style: none;
            }
            .bottom {
                position: fixed;
                bottom: 0;
                left: 0;
                width: 100%;
                height: 4.0833rem;
            }
            .bottom > input {
                width: 100%;
                border: 0;
                outline: 0;
                background: rgb(246, 246, 246);
                color: rgb(255, 255, 255);
                text-align: center;
                line-height: 4.0833rem;
                font-size: 1.25rem;
            }
        </style>
    </head>
    <body>
    <div id="scroll-body">
        <ul></ul>
    </div>
    <div class="bottom"><input type="text" placeholder="请输入内容" onfocus="evocation()"></div>
    <script data-original="./js/jroll.js"></script>
    <script>
        function getClientHeight() {
    //        获取移动端屏幕高度
            var winHeight
            if (window.innerHeight) {
                winHeight = window.innerHeight;
            } else if ((document.body) && (document.body.clientHeight)) {
                winHeight = document.body.clientHeight;
            } else if (document.documentElement && document.documentElement.clientHeight && document.documentElement.clientWidth) {
                winHeight = document.documentElement.clientHeight;
            }
            return winHeight
        }
    
        var scrollBody = document.querySelector('#scroll-body') //获取滚动区域的DOM
        var bottom = document.querySelector('.bottom') //获取底部DOM
        function renderLi() {
            //渲染li列表,采用DocumentFragment方案
            var ul = document.querySelector('ul')
            var dFrag = document.createDocumentFragment()
            var startTime = new Date().getTime()
            for (var i = 0; i < 5000; i++) {
                var li = document.createElement("li")
                li.textContent = i
                dFrag.appendChild(li)
            }
            ul.appendChild(dFrag)
            var endTime = new Date().getTime()
            console.log('渲染耗时:', endTime-startTime, 'ms')
        }
        function evocation() {
            //ios唤出弹框,Android的不需要
            setTimeout(() => document.body.scrollTop = document.body.scrollHeight, 500)
        }
        renderLi()
        document.addEventListener('DOMContentLoaded', function() {
            var height = getClientHeight() - bottom.offsetHeight //获取滚动区域高度
            scrollBody.style.height = height + 'px' //计算出实际的滚动区域的高度,然后设置
            new JRoll(scrollBody) //实例化JRoll插件
        })
    </script>
    </body>
    </html>

    总结

    使用上面提供的框架,你在移动端开发中,不再需要担心底部固定的问题,不再需要担心滚动造成的一系列问题,不再需要担心弹框滚动以及点击弹框造成的穿透问题等。

    而且,不知道你发现没有,底部固定栏你现在可以尝试使用fixed、absolute、relative等设置,不再局限于只能使用fixed了。感兴趣就好好研究一下代码吧!

    但是

    在IOS11版本中,我发现了document.body.scrollTop = document.body.scrollHeight无效的bug,目前还没找到原因,小于IOS11一切正常。

    这里也封装了React版本的插件,可以下载使用:react-roll-container

    查看原文

    二月 发布了文章 · 2019-04-26

    react与redux通信之hook

    react和redux建立通信的方式

    有2种方案:

    • 老方案connect
    • 新方案hook

    老方案connect

    曾经,我们会使用connect建立react和redux的通信,例如,在一个class写法的组件中:

    import React from 'react'
    import { bindActionCreators } from 'redux'
    import { connect } from 'react-redux'
    import globalAction from 'actions/global'
    @connect(
        // 取得reducer中的state
        state => ({global: state.global}), 
        // 取得action
        dispatch => bindActionCreators({ globalAction }, dispatch)
    )
    class Component extends React.Component {
        componentDidMount() {
            // 从props中读取reducer和action
            const {global, globalAction} = this.props
            globalAction()
            console.log(global)
        }
        render() {
            return <div />
        }
    }

    对于用习惯了class组件的开发者来说,这种写法烂熟于心了。但是,不管你多喜欢这种模式,还是得学习react hook。

    新方案hook

    随着react16.8的发布,hook功能正式投入使用。
    将react的class组件转换成函数式组件,想必你已经看过官网的demo了,如果没看,回头看一下也不晚。那么,如果我们使用了hook,又该如何跟redux通信呢?
    针对于这个问题,业界有人提供了一个取代react-redux的新插件redux-react-hook。
    redux-react-hook使用了react提供的Context(上下文)功能,给顶层组件Provide传入了store对象,绑定到上下文。
    使用了redux-react-hook之后,上面的demo就变成了下面这种写法:

    import React, { useEffect } from 'react'
    import { useDispatch, useMappedState, StoreContext } from 'redux-react-hook'
    import globalAction from 'actions/global'
    function Component {
        // 获取上下文的store对象
        const store = useContext(StoreContext)
        // 从store中读取reducer
        const {global} = store
        // 从store中读取dispatch
        const dispatch = useDispatch()
            
        useEffect(() => {
            dispatch(globalAction())
            console.log(global)
        }, [global, dispatch, globalAction])
        
        render() {
            return <div />
        }
    }

    修改后的demo使用到了redux-react-hook提供的其中2个API,StoreContext和useDispatch,其次,还可以使用useMappedState来获取reducer中的状态。

    const mapState = useCallback(
        state => ({
            global: state.global
        }),
        [],
    );
    const { global } = useMappedState(mapState);

    redux-react-hook

    简单介绍写3个API,StoreContext,useDispatch,useMappedState。

    StoreContext

    React提供的createContext创建上下文,返回该对象。

    import { createContext } from 'react';
    // 创建context
    const StoreContext = createContext<TStore | null>(null)
    return StoreContext

    useDispatch

    读取StoreContext,返回dispatch。

    function useDispatch(): Dispatch<TAction> {
        // 从上下文读取store
        const store = useContext(StoreContext);
        if (!store) {
          // store不存在,抛出异常
          throw new MissingProviderError();
        }
        return store.dispatch;
      }
    return useDispatch

    useMappedState

    useMappedState跟其他2个API不太一样,它是一个自定义的hook,用来订阅reducer里的状态。

    总结

    hook式的写法究竟是好是坏,暂且无法分辨,就像有人觉得函数式编程很好,但有人觉得函数式编程使得代码难于维护。
    可以预见的是,当你使用了hook,会在项目中逐渐把class消灭,最后跟class语法糖告别,回归函数的世界。

    查看原文

    赞 18 收藏 10 评论 1

    认证与成就

    • 获得 814 次点赞
    • 获得 16 枚徽章 获得 1 枚金徽章, 获得 3 枚银徽章, 获得 12 枚铜徽章

    擅长技能
    编辑

    开源项目 & 著作
    编辑

    注册于 2016-12-20
    个人主页被 7.4k 人浏览