Nginx实践
proxy_pass
nginx反向代理主要通过proxy_pass来配置,将你项目的开发机地址填写到proxy_pass后面,正常的格式为proxy_pass URL即可1
2
3
4
5
6server {
listen 80;
location / {
proxy_pass http://10.10.10.10:20186;
}
}Upstream模块实现负载均衡
ip_hash指令
server指令
upstream指令及相关变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 修改nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
upstream firstdemo {
server 39.106.145.33;
server 47.93.6.93;
}
server {
listen 8080;
location / {
proxy_pass http://firstdemo;
}
}
}worker_processes
- 工作进程数,和CPU核数相同
worker_connections
- 每个进程允许的最大连接数
upstream模块
- 负载均衡就靠它
- 语法格式:upstream name {}
- 里面写的两个server分别对应着不同的服务器
server模块
- 实现反向代理
- listen监督端口号
- location / {}访问根路径
- proxy_pass http://firstdemo,代理到firstdemo里两个服务器上
上面修改了nginx.conf之后,别忘了最重要的一步重启nginx
通过ip_hash使得用户第一次访问到其中一台服务器后,下次再访问的时候就直接访问该台服务器就好了1
2
3
4
5upstream firstdemo {
ip_hash;
server 39.106.145.33;
server 47.93.6.93;
}
ip_hash它的作用是如果第一次访问该服务器后就记录,之后再访问都是该服务器了,这样比如第一次访问是33服务器,那之后再访问也会分配为33服务器访问了
浏览器输入url到服务器响应全过程
浏览器输入url到服务器响应全过程
当在浏览器地址栏中输入一串域名“https://www.google.com”时候,都发生了什么呢?
- 首先浏览器会去缓存系统找是否有301永久重定向的缓存,如果有就直接跳转重定向的网址
- 如果未找到,就会去浏览器找缓存系统,即这个网页上次访问响应头设置了expires和max-age强制缓存
- 如果强制缓存未命中,会去DNS寻址,先去windows下 C:\Windows\System32\drivers\etc中hosts文件找对应域名是否配置相关ip地址映射,找到了就返回对应的ip地址
- 如果未找到DNS客户端会去DNS缓存系统中去找,找到了就返回对应的ip地址
- 没找到会去查询就近的DNS服务器,找到了就返回对应的ip地址,因为DNS服务器是分布式的数据库,所以就近的DNS服务器没找到对应的ip地址会去找更上层的DNS服务器,直到找到返回ip地址,未找到返回找不到网址
- DNS寻址拿到域名对应的ip地址过后,浏览器通过http协议三次握手与服务端建立tcp/ip连接
- http协议的三次握手过程大概是:
TCP三次握手
- 第一次握手:主机A发送位码为syn=1,随机产生seq number=0的数据包到服务器,主机B由syn=1知道,A要求建立联机;
- 第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包
- 第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。
完成三次握手,主机A与主机B开始传送数据。
常见面试题
常见面试题
1. 如何实现ajax请求
- 通过实例化一个XMLHttpRequest对象得到一个实例
- 调用实例的open方法为这次 ajax请求设定相应的http方法、相应的地址和以及是否异步,当然大多数情况下我们都是选异步
- 以异步为例,之后调用send方法ajax请求,这个方法可以设定需要发送的报文主体
- 然后通过 监听readystatechange事件,通过这个实例的readyState属性来判断这个ajax请求的状态,其中分为0,1,2,3,4这四种 状态,当状态为4的时候也就是接收数据完成的时候,这时候可以通过实例的status属性判断这个请求是否成功
1
2
3
4
5
6
7
8
9
10var xhr = new XMLHttpRequest();
xhr.open('get', 'aabb.php', true);
xhr.send(null);
xhr.onreadystatechange = function() {
if(xhr.readyState==4) {
if(xhr.status==200) {
console.log(xhr.responseText);
}
}
}
javascript有哪几种数据类型
六种基本数据类型
- undefined
- null
- string
- boolean
- number
- symbol(ES6)
一种引用数据类型
- 函数本身作用域。
- 闭包定义时的作用域。
- 全局作用域。
闭包常见用途:
- 创建特权方法用于访问控制
- 事件处理程序及回调
JavaScript有哪几种方法定义函数
- 函数声明
- 函数表达式
- ES6箭头函数
客户端存储localStorage和SessionStorage
- localStorage有效期为永久,sessionStorage有效期为顶层窗口关闭前
- 同源文档可以读取并修改localStorage数据,sessionStorage只允许同一个窗口下的文档访问,如通过iframe引入的同源文档。
- Storage对象通常被当做普通javascript对象使用:通过设置属性来存取字符串值,Storage对象的API:setItem(key, value)设置,getItem(key)读取,removeItem(key)删除,clear()删除所有数据,length表示已存储的数据项数目,key(index)返回对应索引的key
1
2
3
4
5// 枚举所有存储的键值对
for (var i = 0, len = localStorage.length; i < len; ++i ) {
var name = localStorage.key(i);
var value = localStorage.getItem(name);
}
cookie及其操作
- cookie是web浏览器存储的少量数据,最早设计为服务器端使用,作为HTTP协议的扩展出现。cookie数据会自动在浏览器和服务器之间传输。
- 通过读写cookie检测是否支持
- document.cookie setItem参数有key,value,max-age,path,domain
link与@import的区别
- link是HTML方式, @import是CSS方式
- link最大限度支持并行下载,@import过多嵌套导致串行下载,出现FOUC
- link可以通过rel=”alternate stylesheet”指定候选样式
- 浏览器对link支持早于@import,可以使用@import对老浏览器隐藏样式
- @import必须在样式规则之前,可以在css文件中引用其他文件
- 总体来说:link优于@import
MVVM
mvvm由以下三个内容组成
- View:界面
- Model:数据模型
- ViewModel:作为桥梁负责沟通View和Model
在jQuery时期,如果需要刷新UI时,需要先取到对应的DOM再更新UI,这样数据和业务的逻辑就和页面有强耦合。
在MVVM中,UI是通过数据驱动的,数据一旦改变就会相应的刷新对应的UI,UI如果改变,也会改变对应的数据。这种方式就可以在业务处理中只关心数据的流转,而无需直接和页面打交道。ViewModel只关心数据和业务的处理,不关心View如何处理数据,这种情况下,View和Model都可以独立出来,任何一方改变了也不一定需要改变另一方,并且可以将一些可复用的逻辑放在一个ViewModel中,让多个View复用这个ViewModel。
在MVVM中,最核心的也就是数据双向绑定,例如Angluar的脏数据检测,Vue中的数据劫持。
脏数据检测
数据劫持
Vue内部使用了Object.definpropty() 中的get和set方法监听数据。
在解析模板代码时,会实例化属性的watcher对象,并且把它挂在到Dep.target属性上。
通过触发属性的get取值函数把相关的watcher实例添加到dep的subs属性中。
当数据发生变化时,触发属性的set存值函数,调用dep对象的notify方法,主要内容是遍历相关的watcher实例,调用其对应的update方法,更新视图。
面向过程和面向对象的区别?
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个调用就可以了。
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。
箭头函数表达式和普通函数的区别
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或 new.target。
更短的函数、不绑定this、通过 call 或 apply 调用第一个参数忽略、不绑定arguments、箭头函数不能用作构造器,和 new一起用会抛出错误、箭头函数没有prototype属性、箭头函数不能用作生成器
vdom是什么?为何会存在vdom?
不用vdom遇到的问题:
- DOM操作是“昂贵”的,js运行效率高
- 尽量减少DOM操作,而不是“推到重来”
- 项目越复杂,影响就越严重
- vdom即可解决这个问题
问题解答: - virtual dom,虚拟DOM
- 用JS模拟DOM结构
- DOM操作非常“昂贵”
- 将DOM对比操作放在JS层,提高效率
vdom如何应用,核心API是什么?
- h(‘<标签名>’, {…属性…}, […子元素…])
- h(‘<标签名>’, {…属性…}, ‘…’)
- patch(container, vnode)
- patch(vnode, newVnode)
问题解答: - 如何使用?可用snabbdom的用法来举例
- 核心API:h函数返回vnode、patch函数打补丁
介绍一下diff算法?
问题解答: - 知道什么是diff算法,是linux的基础命令
- vdom中应用diff算法是为了找出要更新的节点
- diff实现,patch(container, vnode) patch(vnode, newVnode)
- 核心逻辑,createElement和updateChildren
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23function createElement(vnode) {
var tag = vnode.tag
var attrs = vnode.attrs || {}
var children = vnode.children || []
if (!tag) {
return null;
}
// 创建元素
var elem = document.createElement(tag)
// 属性
var attrName
for (attrName in attrs) {
if (attrs.hasOwnProperty(attrName)) {
elem.setAttribute(attrName, attrs[attrName])
}
}
// 子元素
children.forEach(function(childVnode) {
// 递归调用createElement创建子元素
elem.appendChild(createElement(childVnode))
})
return elem;
}
vuex源码注释
1 | import applyMixin from "./mixin"; |
node网站部署
node网站部署
1.安装nvm (文档 https://github.com/nvm-sh/nvm;https://www.jianshu.com/p/e21e3783304f)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
安装链接可能不是最新,具体以官方文档为准
2.安装node指定版本
nvm install 8.11.3
nvm use 8
3.安装cnpm (npm淘宝镜像)
npm install -g cnpm –registry=https://registry.npm.taobao.org
4.安装pm2
npm install -g pm2
5.跳转至项目目录(本文以/home/www/node/yhfhyy为例)安装所需依赖
cd /home/www/node/yhfhyy
cnpm i
cnpm i nuxt (新增nuxt服务端渲染框架)
6.编辑pm2.json
vim pm2.json
将cmd 属性 改为项目实际路径;如
1 | { |
7.设置启动端口
vim src/config/config.js
设置 port 属性不冲突即可(没有就新增,有就修改,默认值为8360)
1 | module.exports = { |
8.启动
pm2 start pm2.json
9.nginx 代理设置
1 | server { |
JSONP原理
JSONP原理
1 | var count = 0; |
发布订阅模块实现
发布订阅模式
1 | <!DOCTYPE html> |
1 | class EventEmitter { |
盘点那些年踩过的坑
盘点那些年踩过的坑(>﹏<)
前言
最近在找工作,所有想趁现在不是太忙复盘下以前踩过的坑,也是为了提醒自己不要在这些问题上迷糊,提高自己的工作效率。因为是第一次写,所有难免写得不好,望各位大哥,小姐姐轻拍砖,有问题的地方也请不吝赐教,感谢。
1. 微信h5页面中打开本地app,如果没有跳转下载页面
详细解决方案:
H5 唤醒APP小记
2. 点击时,屏幕键盘遮挡输入
Element.scrollIntoView()方法将调用它的元素滚动到浏览器窗口的可见区域
3. 进入页面自动聚焦输入框,并弹出软键盘
4. 安卓手机不支持手机批量上传图片
Android端通过input框focus()方法,setTimeOut,trigge()方法勉强能实现需求,iOS端不行,最后我采用的方法是在上一个页面点击跳转之前触发focus方法(),这里的弹出软键盘是动作带来的就解决了这个问题。
5. iOS系统手机拍照图片Canvas压缩上传后图片旋转的bug
使用到了EXIF.js插件,它会读取图片的信息,然后返回一个值,这个值就是图片的旋转位置,通过修正图片的旋转角度最后在调用Canvas的toDataURL方法压缩图片,微信公众号也可以直接调用JSSDK避开这个问题。详细解决方案:
解决IOS系统手机拍照图片Canvas压缩上传后图片旋转的bug
6. 微信小程序利用canvas生成海报分享图片
详细解决方案:
微信小程序利用canvas生成海报分享图片
7. webpack性能优化
图片资源优化
- url-loader
- file-loader
- image-webpack-loader
happypack
8.老生常谈的跨域问题
JSONP跨域(原理是利用
script
脚本src属性可以跨域的特性)
详细解决方案:
JSONP 教程
JSONP的缺点:
JSONP不提供错误处理。如果动态插入的代码正常运行,你可以得到返回,但是如果失败了,那么什么都不会发生。Electron项目接口出现了403问题,file协议跨域
打包成web版本,通过本地electron加载加载远程资源的方案绕过跨域限制。这个方案应该有问题,应该是在Electron的BrowserWindow模块中配置webSecurity:false
解决。1
2
3
4
5
6
7mainWindow = new BrowserWindow({
//...
webPreferences: {
webSecurity: false,
//...
},
});详细解决方案:
electron如何跨域?electron 跨域方案之禁用 webSecurity9. 角色权限控制
通过vue-router的
addRoutes
API动态添加路由配置,解决了侧边栏菜单渲染
的问题,功能点的权限控制
由后端通过前端传token去判断用户的权限,通过添加路由的全局导航守卫拦截页面,控制访问权限
。详细解决方案:
手摸手,带你用vue撸后台 系列二(登录权限篇)10. 真机调试方案
我在工作中使用过的有:
Fiddler、TBS、微信开发者工具
JavaScript设计模式与开发实践笔记
JavaScript设计模式与开发实践笔记
第16章 状态模式
状态模式的关键是区分事物内部的状态,事物内部的改变往往会带来事物的行为改变。
场景
有一个电灯,电灯上面只有一个开关。当电灯开着的时候,此时按下开关,电灯会切换到关闭状态;再按一次开关,电灯又被打开。同一个开关按钮,不同状态下表现的行为不一样。
现在用代码描述这个场景,Light代表一个电灯类,light是从Light创建出的实例,且拥有两个属性,用state记录电灯的状态,用button记录开关按钮。
16.1.1 第一个示例:电灯程序
1 | class Light { |
上面的代码虽然设计得很好,逻辑既简单又缜密,但是不利于扩展。想想世界上灯泡并非一种,如果引进新的灯泡就需要改动原先的代码。综述上面的例子犯了一下缺点:
- 很明显
buttonWasPressed
方法是违反开放-封闭原则的,每次新增或者修改light的状态,都需要改动buttonWasPressed
方法中的代码,这使得buttonWasPressed
成为了一个非常不稳定的方法。 - 所以跟状态有关的行为,都被封装在
buttonWasPressed
方法里,如果以后电灯又增加了强光,超强光,终极强光,那我们无法预料到这个方法将膨胀到什么地方。 - 状态的切换非常不明显,仅仅表现为对state变量的赋值,比如
this.state='on'
- 状态之间的切换关系,不过是往
buttonWasPressed
方法里堆砌if、else
语句,增加或者修改了一个状态可能需要改变若干个操作,这使得buttonWasPressed
更加难以阅读和维护。
16.1.2 状态模式改进电灯程序
通常我们谈到封装,一般会优先封装对象的行为,而不是对象的状态。但在状态模式中刚好相反,状态模式的关键是把事物的每种状态都封装成单独的类,跟此种状态有关的行为都被封装在这个类的内部,所有button被按下的时候,只需要在上下文中,把这个请求委托给当前的状态对象即可,该状态对象会负责渲染它自身的行为。
下面进入状态莫斯的代码编写阶段,首先将定义3个状态类,分别是OffLightState、WeakLightState、StrongLightState。这三个类都有一个原型方法buttonWasPressed
,代表在各自状态下,按钮被按下时将发生的行为,代码如下:
1 | // OffLightState |
接下来改写Light类,现在不再使用一个字符串来记录当前的状态,而是使用更加立体化的状态对象。我们在Light类的构造函数里为每个状态类都创建一个状态对象,这样一来我们可以很明显地看到电灯一共有多少种状态,代码如下:
1 | class Light { |
使用状态模式的好处是可以使每一种状态和它对应的行为之间的关系局部化,这些行为被分散和封装在各自对应的状态类中,便于阅读和管理代码。
16.2 状态模式的定义
允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
我们以逗号分隔,把这句话分为两部分来看。第一部分的意思是将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态改变时,会带来不同的行为变化。电灯的例子足以说明这一点,在off和on这两种不同的状态下,我们点击同一个按钮,得到的行为反馈是截然不同的。
第二部分是从客户的角度来看,我们使用的对象,在不同的状态下具有截然不同的行为,这个对象看起来是从不同的类中实例化来的,实际上这是使用了委托的效果。
16.3 状态模式的通用结构
16.4 缺少抽象类的变通方式
定义一个State的抽象父类,把共同的功能放进抽象父类中。
1 | class State { |
16.5 另一个状态模式示例—文件上传
16.5.1 更复杂的切换条件
相对于电灯的例子,文件上传不同的地方在于,现在我们将面临更加复杂的条件切换关系。在电灯的例子中,电灯的状态总是从关到开再到关,或者从关到弱光、弱光到强光,强光再到关。看起来总是遵循规矩的A->B->C->A
,所以即使不使用状态模式来编写电灯的程序,而是使用原始的if、else
来控制状态的切换,我们也不至于在逻辑编写中迷失自己,因为状态的切换总死遵循一些简单的规律,而文件上传的状态切换要复杂得多,控制文件上传的流程需要两个节点,第一个用于暂停和继续上传,第二个用于删除文件。
现在看看文件在不同的状态下,点击这两个按钮将分别发生什么行为。
- 文件在扫描状态中,是不能进行任何操作的,既不能暂停也不能删除文件,只能等待扫描完成。扫描完成之后,根据文件的md5值判断,若确认该文件已经文在于服务器,则直接跳到上传完成状态。如果该文件的大小超过允许上传的最大值,或者该文件已经损坏,则跳转到上传失败状态。剩下的情况才进入上传中状态。
- 上传过程中可以点击暂停按钮来暂停上传,暂停后点击同一个按钮会继续上传。
- 扫描和上传过程中,点击删除按钮无效,只有在暂停、上传完成、上传失败之后,才能删除文件。
16.5.2 一些准备工作
上传是一个异步过程,所有控件会不停的调用JavaScript提供的一个全局函数window.external.upload
,来通知JavaScript的上传进度,把控件当前的文件状态作为参数state
塞进window.external.upload
。setTimeout
负责模拟上传进度,window.external.upload
在此例中只负责打印log:
1 | window.external.upload = function(state) { |
16.5.3 开始编写代码
1 | class Upload { |
16.5.4 状态模式重构文件上传程序
第一步仍然是提供window.external.upload
函数,在页面中模拟创建上传插件,这部分代码没有改变:
1 | window.external.upload = function(state) { |
第二部,改造Upload构造函数,在构造函数中为每种状态子类都创建一个实例对象:
1 | class Upload { |
第三步,init方法无需改变,仍然负责往页面中创建跟上传流程有关的DOM节点,并开始绑定按钮的事件:
1 | init() { |
第四步,负责具体的按钮事件实现,在点击了按钮之后,Context
并不做任何具体的操作,而是把请求委托给当前的状态类来执行:
1 | bindEvent() { |
第五步,使用StateFactory编写各个状态类的实现。
1 | class State { |
16.6 状态模式的优缺点
状态模式的优点如下:
- 状态模式定义了状态与行为之间的关系,并将它们封装在一个类里。通过增加新的状态类,很容易增加新的状态和转换。
- 避免
Context
无限膨胀,状态切换的逻辑被分布在状态类中,也去掉了Context
中原本过多的条件分支。 - 用对象代替字符串来记录当前的状态,使得状态的切换更加一目了然。
- Context中的请求动作和状态类中的封装行为可以非常容易地独立变化而互不影响。
状态模式的缺点是会在系统中定义许多状态类,且逻辑分散,不能一眼看出整个状态机的逻辑。
16.7 状态模式的性能优化点
- state对象的创建销毁时机
- 利用
亨元模式
使得各个Context
共享一个state
对象
16.8 状态模式和策略模式的关系
相同点:他们都有上下文、一些策略或者状态类,上下文把请求委托给这些类来执行。
区别:使用场景不一样,策略模式多用于客户封装了一系列算法,在需要的时候切换使用;而状态模式中状态和状态对应的行为是早已封装好的,状态之间的切换也是早就规定好了,”改变行为”这件事发生在状态模式内部,对客户来说不需要了解其实现细节。