1、MVVM和MVC的区别?
(1)MVC中M表示Model模型,V表示view视图, C表示controller控制器;MVVM中M表示model模型,V表示view视图,VM表示viewmodel;
(2)MVC的看法是界面上的每个变化都是一个事件,我们只需要针对每个事件来写一堆代码,来把用户的输入转换成model里的对象,而这段转换的代码就是controller。简言之,MVC是单向通信,view和model必须通过controller来承上启下。
MVVM的看法是我给view里面的各种控件也定义一个对应的数据对象,只需要修改这个数据对象,view里面显示的内容就会自动更新,而view里做了任何操作,这个数据对象也会跟着更新。这就是所谓的双向数据绑定。viewmodel就是与界面view对应的model,因为数据库结构往往是不能直接和界面控件一一对应,所以我们需要再定义一个数据对象专门对应view上的控件,而viewmodel的职责就是把model对象封装成可以显示和接受输入的界面数据对象。简单来说,viewmodel就是view和model的连接器,view和model通过viewmodel实现双向绑定。
(3)MVC各部分通信如下:
view传送指令到controller,controller完成业务逻辑后,要求model改变状态,model将新的数据发送到view,用户得到反馈。
MVVM各部分通信如下:
各部分之间的通信都是双向的,view与model不发生联系,而通过viewmodel传递,view非常薄,不部署任何业务逻辑,称为‘被动视图’,即没有任何主动性,而viewmodel非常厚,所有的逻辑都部署在那里。
MVVM和MVP的主要区别在于,MVVM采用的是双向绑定,view的变动自动反映在viewmodel上,反之亦然。angular、ember、vue都采用这种模式。
2、Mobx和Redux的区别?
(1)Mobx的优势来源于可变数据和可观察数据;Redux的优势来源于不可变数据。
(2)redux和mobx都没有使用传统的mvc/mvvm形式,而是采用flux的结构,action处理请求,然后将请求dispatch到store中,这样设计也十分契合react单向数据流的概念。两者使用flux结构也略有不同,mobx在store和view中处理数据是使用双向绑定。双向绑定无疑会增加性能消耗,但是mobx在双向绑定的同时禁掉了react自身的刷新。
(3)框架体验,开发效率,学习成本方面mobx更好
3、angular和vue的区别?
(1)数据绑定
angular和vue都是双向绑定,但是vue在不同组件之间强制使用单项数据流,这样使应用中的数据流更加清晰易懂。
(2)指令和组件
在vue中指令和组件分得很清晰。指令只封装dom操作,而组件代表自给自足独立的单元,就是有自己的视图和数据逻辑。
在angular中,每件事都由指令来做,而组件只是一种特殊的指令。
4、axios
(1)处理并发请求助手函数(两个请求都完成才执行操作)
axios.all(iterable),axios.spread(callback)
function getUserAccount() { return axios.get('/user/123456') } function getUserPermissions() { return axios.get('/user/123456/permissions') } axios.all([getUserAccount(), getUserPermissions()]) .then(axios.spread(function(acct, perms) { // 两个请求现在都执行完成 }))
(2)拦截器:在请求或响应被then或者catch处理之前拦截他们
// 添加请求拦截器 axios.interceptors.request.use(function(config) { // 在发送请求前做些什么 return config }, function(error) { // 对请求错误做些什么 return Promise.reject(error) }); // 添加响应拦截器 axios.interceptors.response.use(function(response){ // 对响应数据做些什么 return response }, function(error) { // 对响应错误做些什么 return Promise.reject(error) })
如果想在稍后移除拦截器:
var myInterceptor = axios.interceptors.request.use(function(){}); axios.interceptors.request.eject(myInterceptor);
(3)、使用cancel token取消请求
可以使用CancelToken.source工厂方法创建cancel token:
var CancelToken = axios.CancelToken; var source = CancelToken.source(); axios.get('/user/123456', { cancelToken: source.token }).catch(function(throw) { if (axios.isCancel(throw)) { console.log('request cancel', throw.message) } else { // 处理错误 } }) // 取消请求,message参数是可选的 source.cancel('Operation canceled by the user');
还可以通过传递一个executor函数到CancelToken的构造函数来创建cancelToken:
var CancelToken = axiox.CancelToken; var cancel; axios.get('/user/123', { cancelToken: new CancelToken(function executor(c) { // executor函数接收一个cancel作为参数 cancel = c }) }) // 取消请求 cancel();
可以使用同一个cancel token取消多个请求。
5、promise
promise是为了解决回调地狱的问题,是异步编程的一种解决方案。简单来说,它就是一个容器,里面存放着某个未来才会结束的事件(通常是一个异步操作的结果)。从语法上说,他就是一个对象(构造函数),从它可以获取异步操作的消息。它提供统一的API,各种异步操作都可以用同样的方法进行处理。
promise的原理并不难, 它有三个状态,分别是pending、fulfilled、rejected。pending是对象创建后的初始状态,当对象fulfill(成功)时变为fulfilled,当对象reject(失败)时变为rejected。且只能从pending变为fulfilled或rejected,而不能逆向或者从fulfilled变为rejected或者从rejected变为fulfilled。
(1)promise.all()方法是等所有异步操作都执行完毕后才执行then回调
(2)promise.race()
race就是赛跑的意思,所以它是指只要有一个异步操作执行完成就会立刻执行then回调,其他没有执行完成的异步操作仍然在继续执行,而不是停止。
(1)less和sass在语法上有些共性:(8个)
-
混合:class中的class(将一个定义好的class A引入到另一个class B中,从而简单实现class B继承了class A的所有属性)
-
参数混合: 可以将class像函数的参数一样进行传递
-
嵌套规则:class中嵌套class,从而减少重复的代码(在一个选择器中嵌套另一个选择器来实现继承)
-
运算: css中的数学计算(在css中使用加减乘除进行数学计算,主要运用于属性值和颜色的运算)
-
颜色功能:可以编辑你的颜色(颜色的函数运算,颜色会被先转化成HSL色彩空间,然后在通道级别操作)
-
命名空间: 样式分组,从而方便被调用(将一些变量或者混合模块打包封装,更好的组织css和属性集的重复使用)
-
作用域:局部修改样式(先从本地查找变量或者混合模块,如果没有找到的话就会去父级作用域中查找,直到找到为止,这一点和其他程序语言的作用域非常的相似)
-
js表达式:在css样式中使用js表达式赋值(在less或者sass文件中可以使用js的表达式,用来赋值)
(2)less和sass之间的区别:
他们之间的主要区别在于实现方式不同,less是基于js运行,所以less是在客户端处理;sass是基于ruby的,是在服务器端处理的。
很多开发者不选择less是因为less输出修改过的css到浏览器需要依赖于js引擎,而js引擎需要额外的时间来处理代码。关于这个有很多种方式,我选择的是只在开发环节使用less。一旦开发完成,我就复制粘贴less输出到一个压缩器,然后到一个单独的css文件来替代less文件。另一种方式是使用less app来编译和压缩你的less文件。两种方式都将是最小化你的样式输出,从而避免由于用户的浏览器不支持js而可能引起的任何问题。
7、es6
(1)、新增了let和const命令
let用来声明变量,用法类似var,但是所声明的变量只在let命令所在的代码块内有效,const一般用作声明常量,只声明不赋值就会报错;
let和const都不存在变量提升,也就是说只能在声明后使用,否则报错;
let和const都存在‘暂时性死区’,就是说只要在块级作用域内存在const或者let命令声明的变量,那么它声明的变量就绑定这个区域,不再受到外部的影响,这就叫‘暂时性死区’;
let和const不允许在相同作用域内重复声明同一个变量;
let和const实际上是为js新增了块级作用域。
(2)、对字符串进行了扩展
比如模板字符串:
模板字符串是增强版的字符串,用反引号(`)标识,它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。模板字符串中嵌入变量,需要将变量名写在${}中。
(3)、箭头函数
箭头函数在定义之后,this的指向就不会发生改变了,无论用什么方式调用它,this的指向都不会改变。因为:
函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象;
不可以当作构造函数,也就是说不可以使用new命令,否则报错;
不可以使用arguments对象,该对象在函数体内不存在,如果要用,可以用rest参数代替;
不可以使用yield命令,因此箭头函数不能用作Generator函数。
eg:
(4)promise
(5)变量的解构赋值
ES6允许按照一定的模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。本质上,这种写法属于‘模式匹配’,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
对象的解构与数组有一个重要的不同,数组元素是按照次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
解构赋值允许指定默认值:
还有很多。。。。。
8、cookie、sessionStorage、localStorage异同点
(1)数据生命周期
cookie生成时就会被指定一个maxAge值,这就是cookie的生存周期,在这个期间内cookie都有效,默认是浏览器关闭失效;
sessionStorage页面会话期间可用;
localStorage除非数据被清除,否则一直存在。
(2)存放数据大小
cookie4k左右,因为每次http请求,都会携带cookie;
sessionStorage和localStorage一般5M或者更大;
(3)与服务器通信
cookie由对服务器的请求来传递,每次都会携带在http头中,如果使用cookie保存过多的数据会带来性能问题;
sessionStorage和localStorage数据不是由每个服务器请求传递的,而是只有在请求时使用数据,不参与与服务器的通信
(4)易用性
cookie需要自己封装setCookie和getCookie;
sessionStorage和localStorage可以用源生接口,也可以再次封装来对object和array有更好的支持
(5)共同点
都是保存在浏览器端,和服务器端的session机制不同
9、JSONP的原理
ajax请求受到同源策略的影响,不允许进行跨域请求,而script标签的src属性中的链接却可以访问跨域的js脚本,利用这个特性,服务端不再返回json格式的数据,而是返回一段调用某个函数的js代码,在src中进行了调用,这样就实现了跨域。
10、vue的生命周期(参考:https://segmentfault.com/a/1190000008010666#articleHeader6)
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed
(1)beforeCreate:el和data未初始化
(2)created:完成了data数据的初始化,el未初始化
(3)beforeMount:el和data已经初始化,还未渲染好,只是使用虚拟DOM把坑先占住。
(4)mounted:完成挂载,已经渲染完成。
闭包的概念:
闭包就是能够读取其他函数内部变量的函数。在js中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解为‘定义在一个函数内部的函数’。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
闭包的用途:
(1)可以读取函数内部的变量
(2)让这些变量的值始终保持在内存中,因为这些变量始终被引用着,所以不会被垃圾回收机制回收
闭包的注意点:
(1)由于闭包使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页性能问题,在IE中可能导致内存泄漏。解决办法:在退出函数之前,将不使用的局部变量全部删除。
(2)闭包会在父函数外部改变父函数内部变量的值。所以,如果你把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时候要小心,不能随便改变父函数内部变量的值。
12、原型链
(1)、构造函数、原型、实例的关系
构造函数都有一个proptotype属性;
原型对象prototype里面有一个constructor属性,该属性指向原型对象所属的构造函数;
实例对象都有一个__proto__属性,该属性也指向构造函数的原型对象,他是一个非标准属性,不可以用于编程,是浏览器自己使用的。
(2)prototype和__proto__关系
prototype是构造函数的属性,__proto__是实例对象的属性。这两者都指向同一个对象。
(3)原型链属性搜索(什么是原型链)?
在访问对象的某个成员的时候,会先从对象本身进行查找,如果对象中查找不到,那么就会去它的构造函数的原型对象中进行查找,如果没有找到,那么就会去它的原型对象的原型对象中查找,这样一层一层往上查找,直到object的原型对象的原型是null为止。
13、如何理解同步和异步?
所有任务都可以分成两种,一种是同步任务(syn),另一种是异步任务(asyn)。同步任务指的是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程,而进入‘任务队列’的任务,只有‘任务对列’通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
运行机制如下:
(1)所有同步任务都在主线程上执行,形成一个执行栈
(2)主线程外,还存在一个任务队列,只要异步任务有了运行结果,就在任务队列中放置一个事件
(3)一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面有哪些事件。那些对应的异步任务就会结束等待状态,而进入执行栈,开始执行
(4)主线程上不断重复上面三步。
14、webpack
(1)什么是webpack?
webpack可以看作是模块打包机,她做的事情是分析你的项目结构,找到js模块以及其他的一些浏览器不能直接运行的拓展语言(sass、typescript等),并将其转换和打包为合适的格式供浏览器使用。
(2)webpack的工作方式
wenpack的工作方式是把你的项目当作一个整体,通过一个给定的主文件(如index.js),webpack将从这个文件开始找到你项目的所有依赖文件,使用loaders处理他们,最后打包为一个或者多个浏览器可识别的js文件
(3)打包原理
把所有依赖打包成一个bundle.js文件,通过代码分割成单元片段并按需加载。
(4)webpack的核心思想
一切皆模块:正如js文件可以是一个模块,其他文件css、image或者html也可以视作模块,因此,可以require('xxx.css'),这意味着我们将事物分割成更小的易于管理的片段,从而达到重复利用的目的。
按需加载:传统的模块打包工具最终将所有的模块编译生成一个庞大的bundle.js文件,但是在真实的app里面,bundle.js文件可能有10M或者15M之大,可能会导致应用一直处于加载中状态。因此webpack使用很多特性来分割代码然后生成多个bundle.js文件,而且异步加载部分代码以实现按需加载。
15、vue双向数据绑定的原理
主要是通过object对象的defineProperty属性,重写data的set和get函数来实现的。
16、React组件生命周期过程说明
(1)实例化
首次实例化:
-
getDefaultProps
-
getInitialState
-
componentWillMount
-
render
-
componentDidMount
实例化完成后的更新:
-
getInitialState
-
componentWillMount
-
render
-
componentDidMount
(2)存在期
组件已存在时的状态改变:
-
componentWillReceiveProps
-
shouldComponentUpdate
-
componentWillUpdate
-
render
-
componentDidUpdate
(3)销毁&清理期
-
componentWillUnmount
(4)说明
生命周期共提供了10个不同的API
getDefaultProps:作用于组件类,只调用一次,返回对象用于设置默认的props,对于引用值,会在实例中共享。
getInitialState:作用于组件的实例,在实例创建时调用一次,用于初始化每个实例的state,此时可以访问this.props。
componentWillMount:在完成首次渲染之前调用,此时仍可以修改组件的state。
render:必选的方法,创建虚拟DOM,该方法具有特殊的规则:
-
只能通过this.props和this.state访问数据
-
可以返回null、false或者任何React组件
-
只能出现一个顶级组件(不能返回数组)
-
不能改变组件的状态
-
不能修改DOM的输出
componentDidMount:真实的DOM被渲染出来后调用,在该方法中可以通过this.getDOMNode()访问到真实的DOM元素,此时已经可以使用其他类库来操作这个DOM。
在服务器端,该方法不会被调用。
componentWillReceiveProps:组件接收到新的props时调用,并将其作为参数nextProps使用,此时可以更改组件props以及state。
componentWillReceiveProps: function(nextProps) { if (nextProps.bool) { this.setState({ bool: true }); } }
shouldComponentUpdate:组件是否应当渲染新的props或state,返回false表示跳过后续的生命周期方法,通常不需要使用以避免出现bug。在出现应用的瓶颈时,可以通过该方法进行适当的优化。
在首次渲染期间或者调用了forceUpdate方法后,该方法不会被调用。
componentWillUpdate:接收到新的props或者state后,进行渲染前调用,此时不允许更改props或者state。
componentDidUpdate:完成渲染新的props或者state后调用,此时可以访问到新的DOM元素。
componentWillUnmount:组件被移除之前调用,可以用于做一些清理工作,在componentDidMount方法中添加的所有任务都需要在该方法中撤销,比如创建的定时器或者添加的事件监听器。
17、React+Mobx核心概念
(1)state----状态
状态是驱动应用的数据
(2)observable(value)&&@observable
observable值可以是JS基本数据类型、引用类型、普通对象、类实例、数组和映射。其修饰的state会暴露出来供观察者使用。
(3)observer(观察者)
被observer修饰的组件,将会根据组件内使用到的被observable修饰的state的变化而自动重新渲染
(4)action(动作)
只有在actions中,才可以修改Mobx中的state的值。
注意:当你使用装饰器模式时,@action中的this没有绑定在当前这个实例上,要用@action.bound来绑定使得this绑定在实例对象上
(5)computed
计算值(computed values)是可以根据现有的状态或者其他计算值衍生出的值
getter:获取计算得到的新的state并返回
setter:不能用来直接改变计算属性的值,但是他们可以用来作‘逆向’衍生。
18、Vue和React的异同点?
Vue的优势:
(1)模板或渲染的灵活选项
(2)语法以及项目设置的简单
(3)渲染速度更快,体积更小
React的优势:
(1)更大的规模、更多的使用者、更好的可测试性
(2)同时适用Web和原生App
(3)能提供更多支持和工具的更大的生态系统
两者共同的优势:
(1)都是优秀的UI库
(2)使用虚拟DOM快速渲染
(3)轻量级
(4)响应式组件
(5)服务端渲染
(6)轻松集成的路由、捆绑和状态管理
(7)强大的支持和社区
19、web前端性能优化常见方法(参考地址:https://segmentfault.com/a/1190000008829958)
(1)内容优化
-
减少HTTP请求数:常见方法有合并多个css和js文件,利用css sprites整合图像,Inline Images(使用data:URL scheme在实际页面嵌入图像数据),合理设置http缓存等。
-
减少DNS查找
-
避免重定向
-
使用Ajax缓存
-
延迟加载组件、预加载组件
-
减少DOM元素数量
-
最小化iframe数量
-
避免404
(2)服务器优化
-
使用内容分发网络(CDN):把网站内容分散到多个、处于不同地域位置的服务器上可以加快下载速度
-
GZIP压缩
-
设置ETag
-
提前刷新缓存区
-
对ajax请求使用GET方法
-
避免空的图像src
(3)cookie优化
-
减小cookie大小
-
针对web组件使用域名无关的cookie
(4)css优化
-
将css代码放在HTML页面顶部
-
避免使用css表达式
-
使用link代替@import
-
避免使用filters
(5)js优化
-
将js脚本放在页面底部
-
将js和css作为外部文件来引用,在实际应用中使用外部文件可以提高页面速度,因为js和css文件都能在浏览器中产生缓存
-
缩小js和css
-
删除重复的脚本
-
最小化DOM的访问:使用js访问DOM元素比较慢
-
开发智能的事件处理程序
-
js代码注意:谨慎使用with,避免使用eval Function函数,减少作用域链的查找
(6)图像优化
-
优化图片大小
-
通过css Sprites优化图片
-
不要在HTML中使用缩放图片
-
favicon.ico要小而且可缓存
20、call()、apply()、bind()区别
概念:
(1)call()、apply()可以看作是某个对象的方法,通过调用方法的形式来间接调用函数,简单来说就是让函数在某个指定的对象下执行。
(2)bind()就是将某个函数绑定到某个对象上。
(3)三个方法的作用都是改变函数的执行上下文。
区别:
(1)call和apply的第一个参数相同,就是指定的对象。这个对象就是该函数的执行上下文。他们的区别在于参数不同,call第一个参数之后的其他所有参数就是传入该函数的值,以逗号分隔;apply只有两个参数,第二个是数组,这个数组就是该函数的参数。
(2)bind和两者的区别在于bind方法会返回执行上下文被改变的函数而不会立即执行,前两者是直接执行该函数。bind的参数和call相同。
21、flex布局(弹性布局)(参考:http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html?^%$)
常问的几个属性:
(1)flex-direction项目排列的方向,column垂直排列。
(2)flex-wrap如果一条轴线排不下,如何换行。wrap换行。
(3)justify-content项目在主轴上的对齐方式。canter居中;space-between两端对齐且项目之间间隔相等;space-around每个项目两侧间隔相等,所以项目之间的间距大于项目与边框之间的间距。
(4)align-items项目在交叉轴上的对齐方式。center居中;stretch默认值,如果项目未设置高度或者auto,那么将占满整个容器的高度。
22、移动端常见的问题(参考地址:http://www.open-open.com/lib/view/open1449325854077.html)
(1)移动端click屏幕产生200-300ms的延时响应
-
使用fastclick解决手机上点击事件的300ms延迟;
-
zepto的touch模块,tap事件也是为了解决在click的延迟问题。
(2)点击元素产生背景或者边框怎么去掉
-
ios用户点击一个链接会出现一个半透明灰色遮罩,如果想要禁用,可设置-webkit-tap-highlight-color的alpha值为0
-
android用户点击一个链接,会出现一个边框或者半透明灰色遮罩,可设置-webkit-tap-highlight-color的alpha值为0
-
winphone系统,点击标签产生的灰色半透明背景,通过设置<meta name="msapplication-tap-highlight" content="no">去掉
-
有些机型去不掉,如小米2,对于按钮类还有个方法,不要使用a标签或者input标签,直接用div标签
a,button,input,textarea { -webkit-tap-highlight-color: rgba(0,0,0,0); -webkit-user-modify: read-write-plaintext-only; // -webkit-user-modify有个副作用,就是输入法不再能够输入多个字符 } // 或者 * { -webkit-tap-highlight-color: rgba(0,0,0,0); } // winphone下 <meta name="msapplication-tap-highlight" content="no">
(3)美化表单元素
//1、使用appearance改变webkit浏览器的默认外观 input,select { -webkit-appearance: none; appearance: none; } // 2、winphone下,使用伪元素改变表单元素默认外观 // 2.1 禁用select默认箭头,::ms-expand修改表单控制下拉箭头,设置隐藏并使用背景图片来修饰 select::ms-expand { display:none; } // 2.2 禁用radio和checkbox默认样式,::ms-check修改表单复选框或者单选框默认图标,设置隐藏并使用背景图片来修饰 input[type=radio]::ms-check, input[type=checkbox]::ms-check {display: none;} // 2.3 禁用pc端表单输入框默认清除按钮,::ms-clear修改清除按钮,设置隐藏并使用背景图片来修饰 input[type=text]::ms-clear,input[type=tel]::ms-clear,input[type=number]::ms-clear {display:none;}
(4)input在ios下,输入的时候英文首字母默认大写,怎么解决?
<input autocapitalize="off" autocorrect="off" />
(5)消除transition闪屏
.css { -webkit-transform-style: preserve-3d; -webkit-backface-visibility: hidden; -webkit-perspective: 1000; }
23、Vue1.0和2.0的区别
(1)生命周期
2.0生命周期变得更加语义化了,增加了beforeUpdate、updated等,删除了attached、detached。
(2)过滤器
-
2.0将1.0所有自带的过滤器都删除了,我们需要自己定义过滤器:
Vue.filter('toDou', function(n, a, b) { return n < 10? n + a + b : '' + n; })
-
如果想展示JSON数据,不需要调用过滤器了,框架会自动帮我们解析出来;
-
2.0过滤器的传参方式不是以前的方式,是以函数传参的方式:
//1.0 {{ msg | mimi '12' '5' }} //2.0 {{ msg | mimi('12', '5') }}
(3)循环
在Vue2.0中丢弃了$index和$key,将track-by替换成key属性,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素。
(4)片段代码
在编写template的时候,2.0必须要用一个根元素(如div)将代码片段包裹起来,否则报错。1.0则不需要。
(5)el属性绑定的元素,限制为一个普通的元素,不能再绑定再body、html元素上。
(6)v-model增加了.trim、.number等后缀修饰符