谈一下对于React的了解
- 优点:
- 快
- 减少了DOM的操作,做前端的都知道,在性能优化这一块,其中非常重要的一条就是减少DOM的操作成本。那么是如何做到的呢,其实就是通过虚拟DOM。
- 数据改变驱动视图变化,本地更新了代码并保存之后,页面会得到快速更新,但是你看到的并不是全部更新,而是部分更新,甚至你可能看到的知识页面当中的几个数据的改变。
- 项目结构更加清晰,公共模块放在common内,工具类的方法放在utils内,组件放在components内,对于与后端进行交互的功能放在models内,webpack相关功能放在config内等等。
- 减少了很多代码量,配合着es6以及JSX的使用,使得代码量大大减少。
- 打开了函数式的ui编程方式的大门,配合着一整套现成的ui框架写代码,可以减少很多兼容性的顾虑。
- 组件化的思路:一切皆组件,重用代码更加容易,可维护性更高。
- 从个人使用情况上来看,使用了2年多了,非常顺手。
- 快
- 缺点:
- react并不是一套完整的框架,而只是MVC里面的V而已,所以要配合着各种库一起使用,比如react-router、redux、fetch等等。
- 会有风险,比如去年出现的react15版本的开源许可证问题,如果你是小公司使用,人家肯定不会理,但是大公司呢。
- 备注:
- 虚拟DOM:使用React第一次构建DOM树的同时会构建出一个虚拟DOM树,而每当重新渲染的时候,不再直接对实际的DOM树进行渲染,而是对虚拟DOM进行渲染,然后将这一次的渲染结果跟上一次渲染结果进行一番对比,得出了差异之后,再去修改真实DOM中的那部分差异即可,这就是diff算法。
React16相对于15有什么优点,有哪些更新
使用MIT开源许可证。
-
生命周期的修改。
- 15是componentWillMount -> render -> componentDidMount -> componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate -> componentWillUnmount
- 16是static getDerivedStateFromProps(props, state) -> shouldComponentUpdate -> render -> componentDidMount -> getSnapshotBeforeUpdate(prevProps, prevState) -> componentDidUpdate -> componentWillUnmount
加了错误处理: componentDidCatch(error, info)
react的体积相对于15要小30%,主要提现在打包方式上面,新的打包策略去掉了
process.env
的检查。
对于electron的理解
- 越来越多的前端不只是把眼光放在了写一些页面,而去接触更多的后端知识,’大前端‘这个名词在前端圈子里面也越来越流行,而他们大部分人对node这门后端语言是最有兴趣的,因为入门的成本非常低,而通过使用electron你就可以将所学到的前端知识与node相结合起来,而这其实也是electron的产生背景,而且可以说它的前景是非常可观的。
- 优点:
- 摆脱了对不同浏览器版本之间的差异与限制,因为它完全遵循W3C标准,对ES6、ES7、DOM、CSS的最新规范有很大的支持,写代码的时候用起来爽歪歪。
- 基于node,node的生态已经非常成熟了,已经有足够的第三方包来支持各种各样的项目了。
- 可以跨平台使用,就想weex宣传的口号一样,"write once, run everywhere",只需要写一份代码,就可以在各个平台运行。当然,要做到多端使用的效果完全相同,这是理想情况,不过至少有8成是公用,而剩下2成还是需要做适配的。
- 缺点:
- 基础要求比较高,需要对js、node有一定的基础要求
- 打包出来的体积太大,就是简单的应用打包出来一般都有40多M,而我做的那个项目有120M。
- 测试比较麻烦,需要至少�两台电脑来测试。
主进程与渲染进程???
谈一下自己写的ui组件(bitrabbit-passport-ui)
源代码
- 在使用bootstrap的过程中碰到了很多麻烦
- 结构过于复杂,一个最简单的模态框要写20行html,而且对dom结构里面的class务必要正确,增加了编码的复杂度。针对这一点,我们的目标是使用简单明确的模板,并通过js来生成一些额外的代码。
- bootstrap使用了大量的class,我们不愿意使用大量的class,因为class过长会导致难以辨认,所以对于组件,我们更倾向使用class来定位元素,同时使用属性来配置
- bootstrap每使用一个功能,可能都需要加入对应的js,这种情况下会使得项目所要引入的外部js特别多,比如一个项目里面要使用选择时间和日历功能,就要引入两个js
- dom过多会影响性能,这是前端在做性能优化的时候要时刻注意的一点。
- 我们希望在某些情况
谈一下对模块化的理解
- 一个模块就是实现一个特定功能的文件,每个模块都是一个单独的作用域。
- CMD:Common Module Definition,公共模块定义,典型代表seaJS,用于服务器。
- AMD:Asynchronous Module Definition,异步模块定义,典型代表requireJS,用于浏览器。
seaJS和requireJS都是模块加载器,倡导模块化开发的理念,核心价值是为了让js的模块化开发变得简单自然。
如果说requireJS代表的是prototype类库时代,那么seaJS代表的是jQuery时代,es6模块化代表的就是现在这个时代 - webpack:webpack中的模块不仅是指js,连css、图片资源等都被当成了一个模块
- es6模块化:实现的相当简单,完全可以取代CommonJS和AMD规范,成为浏览器和服务器通用的模块化解决方案。
dva是什么
- 是一个基于redux和redux-saga的数据流方案,然后为了简化开发体验,内置了react-router和fetch,可以说是一个轻量级的应用框架。
MVC与MVVM
- MVC:Model(业务模型)-View(用户界面)-Controller(控制器),Backbone。
- MVVM:Model(模型)-View(视图)-ViewModel(视图模型)。
【模型】是指后端传递的数据,【视图】是指所看到的页面,【视图模型】是MVVM的核心,它是连接【模型】和【视图】的桥梁。
前端并没有真正意义上的MVC,大部分是MVVM。整体来看,MVVM比MVC精简了很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新问题,不用再用选择器操作DOM。
重绘与回流
回流必将引起重绘,而重绘不一定会引起回流。
- 重绘:由于节点的几何属性发生改变或者由于样式发生改变单不影响布局的改变的,成为重绘。
- 回流:布局或者几何属性的改变就会造成回流。
减少重绘与回流:
- CSS
- 将动画效果应用到position为fixed或者absolute的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流。
- 控制动画速度可以使用requestAnimationFrame。
- 避免使用css表达式,以免引起回流。
- JS
- 避免频繁操作样式
- 避免频繁操作dom
- 避免频繁读取会引发重绘/回流的属性
- 对具有复杂动画的元素使用绝对定位。
new一个对象做了什么
1.创了一个新对象;
2.this指向构造函数;
3.构造函数有返回,会替换new出来的对象,如果没有就是new出来的对象
什么是懒加载(lazyload),什么时候用到
路由懒加载
跨域方法
- 同源策略:只有同协议、同域名、同端口称为同源,如果浏览器缺少了同源策略就很容易受到XSS、CSFR的攻击
- 跨域解决方案
- CORS
CORS需要浏览器和后端同时支持,且IE9以下不支持。
需要服务端设置Access-Control-Allow-Origin才可以开启CORS。该属性表示哪些域名可以访问资源,如果设置通配符(*)则表示所有网站都可以访问资源。
// index.html let xhr = new XMLHttpRequest(); document.cookie = 'name=xiamen'; // cookie不能跨域 xhr.withCredentials = true; // 前端设置是否带cookie xhr.open('PUT', 'http://localhost:4040/getData', true); xhr.setRequestHeader('name', 'xiamen'); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { console.log(xhr.response); console.log(xhr.getResponseHeader('name')); } } } xhr.send();
上述代码由http://localhost:3000/index.html向http://localhost:4040跨域请求,后端是实现CORS通信的关键。// server.js const express = require('express'); const app = express(); const whitList = ['http://localhost:3000']; // 设置白名单 app.use(function(req, res, next) { const origin = req.headers.origin; if (whitList.includes(origin)) { res.setHeader('Access-Control-Allow-Origin', origin); res.setHeader('Access-Control-Allow-Headers', 'name'); res.setHeader('Access-Control-Allow-Methods', 'PUT'); res.setHeader('Access-Control-Allow-Credentials, true); if (req.method === 'OPTIONS') { res.send(); // options请求不做任何处理 } } next(); }) app.put('/getData', function(req, res) { console.log(req.headers); res.setHeader('name', 'jw'); // 返回一个响应头,后台需设置 res.end('我不爱你’); }) app.get('/getData, function(req, res) { console.log((req.headers)); res.end('我不爱你'); }) app.use(express.static(__dirname)); app.listen(4040);
- Node中间件代理(两次跨域)
- 原理:同源侧滤是浏览器需要遵循的标准,而如果是服务器想服务器发送请求就无需遵循同源策略。
- 步骤:1. 接收客户端发送的请求。2. 将请求转发给服务器。 3. 拿到服务器响应的数据。 4. 将响应转发给客户端
- nginx反向代理
- 实现原理类似于Node中间件代理,需要搭建一个中专nginx服务器,用于转发请求
// proxy服务器 server { listen 80; server_name www.domain.com; location / { proxy_pass http://www.domain1.com:8080; // 反向代理 index index.html index.htm; add_header Access-Control-Allow-Origin http://www.domain.com // 值可为 * } }
- postMessage
- 多用于与iframe进行通信
- jsonp
- 基本上很少用
- websocket
- 实现了服务器与浏览器的双向通信,使得服务器也能够主动的与浏览器就行通信
- CORS
前端缓存
居中的方法
- 自己设一个宽度,父元素
margin: 0 auto
- transform: translateY(-50%)
- float
- flex
- position: absulte;
原生js写一个轮播图
简单说一下产品
闭包
好处:
1. 方便调用上下文的变量
2. 当闭包作为另外一个函数调用的参数时,避免脱离当前逻辑而单独编写另外的逻辑
3. 加强封装性,达到保护变量的作用
坏处:
1. 内存浪费
优化:
1. 尽量避免使用闭包
2. 退出函数时,将不使用的局部变量全部删除
前端性能优化
- 减少dom的操作
- 性能操作
JS设计模式
- 工厂模式
- 观察者模式
- 代理模式
- 单例模式
- 单体模式
js堆栈
堆栈可分为堆和栈,两者都是临时存储数据的地方。
- 堆(heap):在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。对于堆,我们可以随心所欲的进行增加变量和删除变量,不用遵循次序。
- 栈(stack):一种先进后出的的数据结构。
原型链
如何判断是否为一个对象的属性
- in:
if ('name' in obj ) {}
- hasOwnProperty: 只判断是否为自身属性
- 用.或者[]: 对象或者原型链上不存在则返回undefined
箭头函数与普通函数的区别
- 箭头函数的this指向的是定义时所在的对象,而普通函数的this指向的是使用时所在的对象
- 箭头函数没有arguments
- 箭头函数的this指向不会改变,没办法用call和apply改变this的值
- 箭头函数是匿名函数,而不是构造函数,所以没法new出来
- 箭头函数没有原型
高阶组件
在浏览器里面输入url到页面渲染完的过程中发生了什么
- DNS解析
- TCP连接
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器解析渲染页面
- 连接结束
promise的原理
- promise是对异步编程的一种抽象,它有三种状态:pending(初始化状态)/fulfilled(已完成))/rejected(拒绝)
- 实现一个简单的promise
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
function Promise(fn) {
let values;
let callbacks = [];
function resolve()
}
## lodash里面的方法的原生实现
## http与https有什么区别
1. 默认端口的区别:http是80,https是443
2. https协议需要到ca申请证书,一般免费证书很少,需要交费。
3. HTTP(Hypertext Transfer Protocol)超文本传输协议/HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议
4.
## babel原理
* **babel默认只是转译新标准引入的语法,比如ES6的箭头函数,不转换新的API,API需要polyfill来转译**
* babel转译分为三个阶段:parsing(解析)、transforming(编译)、generating(生成)。
1. 编写es6代码
2. babylon进行解析
3. 解析得到AST树
4. plugin用babel-traverse对AST树进行遍历转译
5. 得到新的AST树
6. 用babel-generator通过AST树生成新的es5代码
## babel的preset/env,stage-0等5个阶段
* env只会去编译当年新出的语法
* stage-0到stage-4,分别为展示阶段、征求意见阶段、草案阶段、候选人阶段、正式定案阶段,一般只要能进入到stage-2阶段的基本都能进入最终的标准语法内。
## promise.all 与 promise.race
* promise.all:接收一个数组作为参数,返回的是一个数组
``` js
const promise1 = Promise.resolve(3);
const promise2 = 2;
const promise3 = new Promise((res, rej) => {
setTimeout(res, 100, 4);
})
Promise.all([promise1, promise2, promise3]).then(values => {
console.log(values); // [3, 2, 4]
});
- promise.race:也接收一个数组为参数,但是返回的是最快返回的promise的值
const promise1 = new Promise((res, rej) => {
setTimeout(res, 100, 'foo');
});
const promise2 = new Promise((res, rej) => {
setTimeout(res, 200, 'bar');
});
Promise.race([promise1, promise2]).then(values => {
console.log(values); // 'foo'
});
settimeout的第三个参数
- 从第三个参数开始都作为前面的回调函数的参数
setTimeout((...args) => {
console.log(args);
}, 1000, 3, 4, 5); // [3, 4, 5]
setTimeout((...args) => {
console.log(args); // [xx]一个随机数
}, 2000, setTimeout((...args) => {
console.log(args); // [4]
}, 3000, 4));
通过Promise.race实现fetch的timeout
const _fetch = (requestPromise, timeout = 6000) => {
let timeoutAction = null;
const timerPromise = new Promise((res, rej) => {
timeoutAction = () => {
rej('[timeout]');
};
};
setTimeout(timeoutAction, timeout);
return Promise.race([timeoutAction, requestPromise]);
};
## localStorage/sessionStorage/cookie的区别
|特性|cookie|localStorage/sessionStorage|
|:-----:|:----:|:----:|:----:|
|声明周期|一般由服务器生成,可设置失效时间|除非主动删除,否则永久有效/关闭浏览器就失效|
|体积大小|4k左右|5M|5M|
|与服务器的通信|每次都存放在http请求的头中,如果过多数据放在cookie中会存在性能问题|不与服务器通信,仅在浏览器保存|
|易用性|需要程序员自己封装,源生的Cookie接口不友好 |源生接口可以接受,亦可再次封装来对Object和Array有更好的支持|
|使用场景|登录信息携带|存储语言,其他一些需要存储的数据|
|API|document.cookie|setItem/removeItem/clearItem|
## BFC(block formatting context):块级格式上下文
## 如何将string变成html
```js
function str2ele(template) {
const $el = document.createElement('div');
$el.insertAdjacentHTML('beforeend', template);
return $el.firstChild;
}
str2ele('123456')
判断数组的三个方法
Object.prototype.toString.call()
Object.prototype.toString.call('an') // '[object String]'
Object.prototype.toString.call([1, 2, 3]) // '[object Array]'
Object.prototype.toString.call({}) // '[object Object]'
Object.prototype.toString.call(function() {}) // '[object Function]'
Object.prototype.toString.call(Symbol(1)) // '[object Symbol]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(1) // '[object Number]'
Array.isArray()
instanceof
npm模块安装机制,为什么输入npm install就可以自动安装对应的模块
- npm模块安装机制
- 发出
npm install
命令 - 查询
node_module
目录中是否已经存在指定模块
如果存在,则不再重新安装
如果不存在
1. npm先通过registry查询到模块压缩包的下载网址
2. 下载压缩包,存放在根目录下的.npm目录中
3. 解压压缩包到当前项目的node_module目录中
- 发出
- npm实现原理
- 在输入npm install的命令并敲下回车键之后,会经历如下几个阶段
- 执行工程的preinstall命令
- 确定首层依赖模块
- 获取模块
- 模块扁平化(dedupe)
上一次获取到的模块是一颗完整的依赖树,其中会包含大量的重复模块,比如A模块包含了lodash,B模块也包含了lodash,在npm3之前会严格按照依赖树进行按照,从而造成大量模块的冗余。
从npm3开始默认增加了一个dedupe过程,它会遍历各个节点,逐个将模块放入根节点下,一旦发现有重复的模块,则将其丢弃。
重复模块:指的是模块名相同,且semver兼容,每个semver都对应一段版本允许范围,如果两个模块的版本都在允许的范围之中,就会得到一个兼容的版本,而不用一定保持版本一致性 - 安装模块
执行模块的生命周期(preinstall、install、postinstall) - 执行工程的自身生命周期
- 在输入npm install的命令并敲下回车键之后,会经历如下几个阶段
Redux原理剖析
Proxy是什么
Proxy可以理解成,在目标对象之前设置一层拦截,外界对该对象的访问,必须要通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
const proxy = new Proxy(target, handler);
正则表达式
const regExp = /e/; // 字面量赋值,要加斜杠
const regExp1 = new RegExp('abc', ig);
console.log(regExp.test('abcde')); // true,test方法返回true或者false
console.log('abcdefg'.match(regExp)); // ["e", index: 4, input: "abcdefg", groups: undefined]
console.log(Array.from('abcdefg'.match(regExp))); // ['e']
console.log(regExp.exec('abcdefg')); // ["e", index: 0, input: "abcdef", groups: undefined]
5G
- 5G的底层技术已经完全成熟,正在开发商业应用。
- 核心:快、智能。
- 未来的应用场景:清晨,5G路由器叫醒你,感知你在床垫上翻身的次数,感知你的睡眠质量;随后,5G路由器连接咖啡机,自动送给你泡一杯咖啡;在你喝完咖啡,吃完早餐后,自动帮你预约一台无人驾驶汽车,帮你规划一条最不拥堵的道路;到达办公室后,你利用虚拟现实技术召开一个远程的会议,身临其境。
关于图片问题
- 完整代码
-
axios
默认返回的是 json 文本形式,二进制图片数据被强制转换成了 json 文本形式,就会出现乱码。
nrm: 管理多个npm的registry,可以通过nrm ls 查看目前绑定的所有registry
semver:语义化版本控制规范
版本如1.3.4。
其中1是MAJOR版本,BREAKING CHANGE时才能修改;
3是MINOR版本,feat类型的提交可修改;
4是PATCH版本,fix类型提交可修改。
monorepo
- 这是一种管理项目代码的方式,在这种方式下会摒弃原先一个Module一个repo的方式,取而代之的是把所有的modules都放在一个repo内来管理。
- 比如React仓库里面包含了很多个子仓库,我们可以在packages目录下看到很多单独的packages。
- 目前诸如Babel,React,Angular,Ember,Meteor,Jest等等都采用了这种方式来进行源码管理
源码阅读
- 为什么看源码:是为了解决问题,不要为了看源码而看源码——问题驱动,要做到有的放矢
- 学会如何看源码
- 看最新版的源码:建议看最新的源码,
- 本地构建:把代码仓库clone到本地,然后按照readme上的构建指南,在本地build一下
- 理清目录结构:了解monorepo代码结构,webpack和babel一样,可以说是基于插件的系统
- debugger && 全局搜索:node工具调试可以在运行node命令时加上--inspect参数
- 总结:看源码是为了解决问题
js时间处理的痛点以及解决办法
痛点
实现一个node爬虫,可抓取最热新闻信息、NBA日报、美图、前端咨询汇总,好文
display:none/visibility: hidden/ opacity: 0的使用场景以及使用优劣
结构:
display: none: 会让元素完全从渲染树中消失,渲染的时候不占据任何空间, 不能点击,
visibility: hidden:不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,可以点击
opacity: 0: 不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,可以点击
继承:
display: none和opacity: 0:是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示。
visibility: hidden:是继承属性,子孙节点消失由于继承了hidden,通过设置visibility: visible;可以让子孙节点显式。
性能:
display: none: 修改元素会造成文档回流,读屏器不会读取display: none元素内容,性能消耗较大
visibility:hidden: 修改元素只会造成本元素的重绘,性能消耗较少读屏器读取visibility: hidden元素内容
opacity: 0: 修改元素会造成重绘,性能消耗较少
时间复杂度是什么意思
- 衡量算法好坏的一个重要指标
- 衡量代码好坏的两个重要指标:
- 运行时间
- 占用空间
栈/队列
栈
队列
图片宽度为400px,通过css修改图片宽度为300px的方法
- max-width: 300px;
- transform: scale(0.625);
- zoom: 0.625;
- box-sizing: border-box;padding: 0 50px;
节流防抖
防抖
当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
应用场景:按钮点击事件/input事件,防止用户多次重复提交
手写debounce:
// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait = 50) => {
if (typeof func !== 'function') {
throw new TypeError('Error')
}
// 缓存一个定时器id
let timer = null
// 这里返回的函数是每次用户实际调用的防抖函数
// 如果已经设定过定时器了就清空上一次的定时器
// 开始一个新的定时器,延迟执行用户传入的方法
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
节流
每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作
应用场景:
- 鼠标/触摸屏的mouseover/touchmove事件
- 页面窗口的resize事件
- 滚动条的scroll事件
手写throttle:
function throttle(func, wait) {
var previous = 0;
return function(...args) {
let now = Date.now();
if (now - previous > wait) {
func.apply(this, args);
previous = now;
}
}
}
// 或者
function throttle(func, wait) {
let timer = null;
return function(...args) {
if (!timer) {
timer = setTimeout(() => {
func.apply(this, args);
timer = null;
}, wait)
}
}
}
// 防抖和节流可视化比较:http://demo.nimius.net/debounce_throttle/
alpha对照表
//透明度;alpha 取值对照
100% — FF
95% — F2
90% — E6
85% — D9
80% — CC
75% — BF
70% — B3
65% — A6
60% — 99
55% — 8C
50% — 80
45% — 73
40% — 66
35% — 59
30% — 4D
25% — 40
20% — 33
15% — 26
10% — 1A
5% — 0D
0% — 00
linux命令总结
tar:解压
-c 建立压缩档案 compress
-x 解压
-z 支持gzip解压文件
-v 显示所有过程
-f 使用档案名字,切记,这个参数是最后一个参数,后面只能接档案名。
commonjs(nodejs实现规范)
一个文件就是一个模块
resolve:解析
path.resovle可以将相对路径解析成绝对路径
path.parse可以将绝对路径解析成一个对象
图片对比
图片格式 | 支持透明 | 动画支持 | 压缩方式 | 浏览器支持 |
---|---|---|---|---|
baseline-jpeg | 不支持 | 不支持 | 有损 | 所有 |
progressive-jpeg | 不支持 | 不支持 | 有损 | 所有 |
gif | 支持 | 支持 | 无损 | 所有 |
png | 支持 | 不支持 | 无损 | 所有 |
svg | 支持 | 支持 | 无损 | 所有(IE8以上) |
正常总结,待补充