-
css
-
居中为什么用transform,不用marin+left/top
考虑浏览器性能,transform创建一个渲染层,marin+left/top需要计算布局
-
移动端适配1px的问题
- 原因:不同手机dpr(像素密度不同),如视网膜屏1px看起来会变胖
- 解决方案:伪元素+transform,相对定位、设置transform:scale(0.5)
- 可满足圆角,但是伪元素不够用时可能要新增元素
- 现在已经不常见
-
介绍flex布局
- 弹性布局
- justify-content:主轴对齐方式,如是否两端对齐
- align-item:交叉轴对齐方式,类似line-height垂直居中那种效果
-
如何实现H5手机端配置
- flexible.js,配合设置meta viewport width=device-width
- 原理:将屏幕宽度分成10份,一份是1rem,给body的font-size设置这个值
-
css实现三角形
- 元素width、height都设置成0
- 设置border宽度、颜色(透明)形成三角形(border想象成木板投影)
-
BFC - 块格式上下文
- BFC是一个独立的布局容器,不影响外界、不受外界影响
- 创建BFC:
- float
- position: absolute | fixed
- display: inline-block | flex | table-cell
- overflow: hidden | auto | scroll | !visible
-
CSS3新特性:position: sticky
父元素在屏幕内时表现为position: static,父元素滚出屏幕,表现为position: fixed
-
-
node
-
使用过的koa2中间件
- koa-bodyparser - 可直接在ctx.request.body上获取post请求参数
- koa-router 路由
- 不使用koa-router中间件:根据url返回不同文件渲染
- 使用koa-router中间件:new KoaRouter.get().get().post()...,可以方便设置路由层级
- koa-session
- koa-statics
-
koa-bodyparser原理
- node获取request body通过监听request的data事件完成
- koa-bodyparser封装这一过程,把post请求数据解析后挂载到koa上下文中
-
定时任务执行顺序
- 执行顺序:process.nextTick > setImmidate > setTimeout > setInterval
- 微任务:process.nextTick、promise.then;宏任务:setImmidate、setTimeout、setInterval
- setTimeout最小时间间隔:4ms
-
koa、express、redux中间件的差异
- express是依次执行中间件函数,next之后的方法不执行,在最后一个next中处理response
- koa是使用async、await实现的一种洋葱圈模型,通过next把执行权交给下一个中间件,直到最后一个中间件不调用next,控制权沿途返回。next之后的代码可以执行
- redux中间件是一系列函数的柯里化,依次传递action,直到redux的原生dispatch。
-
介绍EventLoop
- 浏览器:
- 主线程 - js执行栈
- 异步操作、网络请求、点击事件回调等cb放入任务队列
- 当前宏任务执行完成后,会一次性清空任务队列
- 宏任务包括:setTimeout/setInterval、执行js、UI rendering等
- 微任务:promise.then等
- node:
- 收到一个请求时,使用一个js闭包(request、response的cb)排队进入EventLoop
- 如果是耗时任务,Libuv库给其分配一个工作线程
- 工作完成后触发相应的cb返回给主线程
- EventLoop将响应返回客户端
- 宏任务包括:setTimeout/setInterval/setImmediate、执行js、I/O等
- 微任务:promise.then、process.nextTick等
- 浏览器:
-
EventEmmiter
- event = new require('event').Emmiter(); event.emit('a'); event.on('a', listener1); event.removeListener('a', listener1)
- 这是一种订阅-发布模式,vue bus也是这样的
- 订阅-发布与观察者的区别:
- 订阅-发布有中间商-调度中心,发布、订阅者不知道对方的存在
- 观察者模式中观察者必须对目标触发事件作出响应
- 发布-订阅是观察者模式的进化版,耦合度更低
-
-
HTTP
-
http报文:
- 分类
- 请求报文
- 响应报文
- 内容
- 请求头部
- 空行
- 请求主体
- HTTP请求常见方式:get post option(预处理)
- 分类
-
如何处理跨域
- 跨域拦截是浏览器做的,服务端本身没有这样的限制。
- jsonp,给server的callback标记client方法名fn,server返回执行fn的字符串,并进行赋值,client把这段字符串放入script标签中执行,jsonp只能用get
- ajax跨域
- 先预检,可以用OPTION,或者content-type设置成其他,预检的作用是防止post请求在server产生作用而client没有接收到正确返回
- ajax请求头加入origin,询问服务端是否可跨域
- server处理可跨域情况:
- 加入3个Allow头信息,标记可跨域的源(就是刚才传入的origin)、方法、自定义头信息
- 如果需要携带cookie,server端还需返回Allow-Credentials头信息,client在xhr中也需要设置xhr.withCredential = true;
- nginx设置反向代理、或wepack的dev-server做反向代理(本质是express)
- 跨域是针对ajax的,form表单提交没有跨域问题。
-
http2.0
-
与http1的区别:
- 采用二进制而非文本
- 头部压缩
- 多路复用 - 一个连接上可以有多个请求
-
如何知道请求是http1还是http2?
浏览器network里查看头信息版本
-
-
缓存策略
- 强缓存
- expires
- HTTP 1.0
- 返回过期时间,客户端用本地时间与之对比
- cache-control
- HTTP 1.1
- 优先级高于expires
- 返回一个是时间长度,不依赖客户段时间
- expires
- 协商缓存
- 先设置no-cache、no-store忽略本地缓存
- 先去请求服务端有没有更新
- 时间维度:last-modified + if-Modified-Since
- 内容维度:ETag + if-None-Match
- 前者server返回,后者client带回去
- 返回304命中本地缓存,返回200正常返回内容
- 强缓存
-
鉴权
- session-cookie 利用redis,保存用户信息
- token/JWT(json web token)服务端不需要保存信息
- sso 项目实践 jsonp去其他站点请求下
- oauth 发给快递小哥进门的令牌
-
-
Web安全
-
常见Web攻击
- XSS - 跨站脚本攻击
- 类型:
- 反射形 - url参数上带脚本注入页面,执行恶意脚本
- 存储形 - 攻击者在网站表单里输入恶意脚本保存在该网站服务端,比如一条评论,其他看这个评论的用户都会执行这段恶意脚本
- 目的:盗取用户cookie
- 防范措施:
- cookie设置http-only
- 检查用户输入,设置黑名单白名单检测,对特殊字符进行编码-> xss filter,serve输出检查-内容转义
- 启用浏览器自己的xss防范措施:ctx.set('X-XSS-Protection', 0),浏览器检查到xss会清除页面
- 类型:
- csrf - 跨站请求伪造
- 利用用户cookie,在恶意网站上请求用户登录网站的接口
- 防范措施:
- 验证码提醒用户执行了某个操作
- referer check:HTTP头信息中的Referer记录了HTTP请求来源地址,server检查是否来自可靠的源
- 添加token验证:每次请求携带token,此token不能直接从cookie中获取
- xss、csrf区别:xss窃取用户cookie,csrf只是借用,不窃取
- 点击劫持
- 利用透明iframe做视觉欺骗,结合xss和csrf干坏事
- 防范措施:
- 设置HTTP响应头X-FRAME-OPTIONS,设置iframe的展示策略
- if(window.top != window.self) window.top.location = window.self.location // 恶意iframe直接跳转到自己页面
- SQL注入
- 利用server处理SQL的漏洞
- 防御措施:
- 参数化执行SQL,不要直接拼接用户输入的内容到SQL中
- 限制Web应用程序操作数据库权限
- 入库字符串转义
- 请求劫持
- DNS劫持
- 一般用户上网的DNS服务器是运营商分配的,运营商返回的ip是一个302到目标站的带广告地址
- HTTP劫持
- 运营商发现是HTTP请求,篡改HTML数据内容
- 防御措施:升级至HTTPS
- DNS劫持
- DDOS - 流量攻击
- 防御手段:
- 备份网站
- 硬件层 防火墙等
- 防御手段:
- XSS - 跨站脚本攻击
-
防御手段
- HTTPS配置
- HTTPS = HTTP + SSL
- SSL=证书
- 作用:网站身份验证、加密传输
- 证书保存本地,mac保存在钥匙串里
- HTTPS过程
- client发起一个请求,server返回数字证书,client验证证书是否有效,并获取公钥R
- client生成一个随机对称密钥E,用来加密url链接s,并用R来加密E,把加密后的E和s一起发到server
- server用R的私钥揭秘出E,在解密出s,获得ur请求,并用E把返回内容加密发回给serve
- client收到加密后的响应,用自己的E解密,即获得内容
- HTTPS配置
-
-
异步
-
回调函数
-
事件监听
-
发布订阅
- Node EventEmitter
- Vue EventBus
-
Promise
-
状态:pending、resolved、rejected
-
解决痛点
- 回调地狱 - 提高代码可读性
- 支持并发
- 解决信任——回调执行过早、没有回调、回调次数太多等
-
then微任务,结合浏览器事件循环
-
构造函数同步,then异步
-
Promise error catch:
-
每个promise添加.catch
-
改成async + await,用try catch
-
window.addEventListener('unhandledrejection', event => ···);
-
-
promise与generator、async、await的区别和关系
- 首先Promise和这些不是一个概念上的东西,Promise是一种异步解决方案
- generator是在yield处阻塞,next()才走下一步,这样暗示我们可以yield一个promise,在promise的then中调用这个generator的next获取异步返回值
- 使用promise + generator,需要每次都手动调用generator,于是有人写了一个runner方法,自动执行
- runner方法被吸收进ES7标准中,即async、await语法糖
-
手写promise、promise.all、promise + ajax
- 手写promise思路(简版,忽略细节):
- new Promise时,会传入一个执行函数exec,其参数是两个方法:resolve、reject,一般exec会做一些异步操作,有结果时调用resolve和reject,传入一些值,修改一些状态(pending、onFullfilled、onReject)
- then方法定义在prototype上,then方法接收两个回调函数,分别是成功和失败的回调,then方法执行时如果是pending状态,就把这两个回调函数push进构造方法中维护的两个回调队列,然后在刚刚说的resolve、reject中依次调用两个队列里保存的回调方法,then还要返回promise
- 用的时候,new Promise(),传入一个做异步操作的方法,在之前写successCb和failCb的地方替换成resolve、reject
- 手写promise.all的思路:(race类似)
- Promise上的静态方法
- 传入一个promise数组,使用Array.map循环,把返回结果保存到一个result中,注意顺序
- 每个prmise执行完后判断数组长度,完成时resolve返回结果数组
- race类似,但是循环的时候要用Array.forEach
- 手写promise + ajax
- 返回一个promise对象 return new Promise(function (resolve, reject) {...})
- var xhr = new XMLHttpRequest(); xhr.open('GET', '/url', true);
- xhr.onreadystatechage = () => { 替换原回调函数为resolve、reject }
- 手写promise思路(简版,忽略细节):
-
-
-
webpack
-
哪些常见的loader?
- url-loader
- souce-map-loader
- babel-loader
- css-loader
-
常见的plugin
- Commons-chunk-plugin 提取公共代码
- uglifyjs-webpack-plugin 压缩js
-
loader和plguin的区别?
- loader -> 解析非js文件
- plugin -> 扩展webpack功能,在webpack生命周期中监听事件,通过webpack API改变输出结果
- 用法不同:
- loader配置在module.rules中
- plugin单独配置
-
webpack的构建流程:
- 初始化参数 - 配置文件 + 命令
- 开始编译:初始化compiler对象
- 确定入口:entry
- 编译模块:从入口文件出发,调用所有配置的loader进行编译,递归找出依赖模块进行编译
- 完成编译:得到结果文件和他们之间的依赖关系
- 输出资源:最后可以修改输出内容的机会
- 输出完成:写入环境系统
-
是否写过loader?
- loader是一个编译器,可以拿到源文件内容字符串source,经过一系列的处理,将处理后的内容返回,或调用callback返回给webpack
- 抽象语法树:
- 含义:表示源代码的语法结构,是编译器语法分析的结果,类似于一个json
- estraverse遍历更新语法树
- escodegen将AST重新生成源码
-
webpack热更新 HMR
- express启动本地服务,对浏览器请求资源作出响应
- 使用websocket实现长连接
- webpack的watch模式下监听文件系统的改变,即开发者保存文件时出发webpack重新编译
- 编译完后socket向客户端推送当前编译的hash戳
- 客户端websocket监听推送过来的hash戳,和上一次对比,一致走缓存,不一致通过ajax和jsonp向express服务器请求资源
- 客户端局部刷新
-
如何利用webpack优化前端性能
- 利用插件压缩代码
- 利用CND加速
- html文件不要缓存
- css、js等文件放到cdn上
- 设置wbpack的publicPath
- 生产环境的publicPath:为静态文件路径前面加上publicPath,应该设置成cdn地址
- 开发环境:使用webpack-dev-server,和上面不同,是webpack-dev-server打包出来的静态文件访问路径,这些打包文件存在于内存。
- 删除死代码,启动webpack时增加--optimize-minimize来实现
- 利用插件提取公共代码
-
如何提高webpack构建速度
- happypack实现多线程加速编译
- moment.js -> day.js
-
按需加载
-
利用组件库现成的解决方案
-
Element:babel-plugin-component
-
Antd:babel-plugin-import
-
antd按需加载原理:
把import { Button } from 'antd';编译成
var _button = require('antd/lib/button'); require('antd/lib/button/style/css');
-
-
-
webpack优化:
- 减少webpack打包时间
- 优化loader
- 影响打包效率最高的就是Babel
- 优化loader文件搜索范围
- 缓存babel编译过的文件 babel-loader?cacheDirectory=true
- 使用HappyPack
- 多线程
- HappyPack在插件中配置loader,在rules的loader中使用babel-loader?cacheDirectory=true
- DllPlugin
- DllPlugin将特定的类库提前打包引入
- 减少打包类库的次数
- 配置dllConf
- 使用DllReferencePlugin将依赖文件引入项目
- module.noParse:一个文件不引用其他文件,配置此项让webpack不查找依赖
- 优化loader
- 减小打包后的体积:
- 按需引入
- tree shaking为什么没用?
- 原理:ES6模块引入是静态分析,因此可以在编译阶段知道哪些没用
- 分析程序流,判断哪些变量没有被引用,进而删除代码
- 为什么你的webpack uglifyJS没用:
- class编译的时候,方法不是使用原型链方式,而是Object.defineProperty,这里是有闭包的,导致一些副作用,treeShaking不是按预想的方式工作
- rollup是有代码流程分析的,适合做一些库文件的打包
- 减少webpack打包时间