(会持续更新,有朋友在面试中遇到有意思的题也可以下方留言)
html相关
说一下label标签的用法
label标签主要是方便鼠标点击使用,扩大可点击的范围,增强用户操作体验
遍历A节点的父节点下的所有子节点
document.getElementById(‘a’).parentNode.children
img标签中title和alt区别
- title 通常当鼠标滑动到元素上的时候显示
- alt是img特有的属性,是图片内容的等价评价,用于图片无法加载时显示,读屏器阅读图片,可提高图片访问性,除了纯装饰图片外都必须设置有意义的值,搜索引擎会重点分析
html5新增了那些属性,移除了哪些元素
新增元素
- 绘画canvas
- 用于媒介回放的video和audio元素
- 本地离线缓存localstorage长期存储数据,浏览器关闭后数据不丢失
- sessionstorage的数据在浏览器关闭后自动删除
- 语义化更好的内容元素,比如article、footer、header、nav、section
- 表单控件calender、date、time、email、url、search
- 新的技术,webworker、websocket、Geolocation
移除的元素
- 纯表现的元素:basefont、big、center、font、s、strike、tt、u
- 对可用性产生负面影响的元素:frame、frameset、noframes
支持html5新标签
- IE8/IE7/IE6支持通过document.createElement方法产生的标签
- 可以利用这一特性让这些浏览器支持html5新标签
- 浏览器支持新标签后,还需要添加标签默认的样式
iframe有哪些缺点
- iframe会阻塞页面的onload事件
- 搜索引擎的检索程序无法解读这种页面,不利于seo
- iframe和主页面共享连接池,而浏览器对相同域的连接有限,所以会影响页面进行并行加载
- 使用iframe之前需要考虑这两个缺点,如果需要使用iframe,最好是通过javascript动态给iframe添加src属性值,这样可以绕开以上两个问题
html w3c的标准
标签闭合、标签小写、不乱嵌套、使用外链css和js、结构行为表现的分离
Doctype作用?严格模式与混杂模式如何区分?它们有何意义?
- !DOCTYPE声明位于文档中的最前面,处于html标签之前,告知浏览器的解析器,用什么文档类型、规范类型来解析这个文档
- 严格模式的排版和js运作模式是以该浏览器支持的最高标准运行
- 在混杂模式中,页面以宽松的向后兼容的方式显示,模拟老式浏览器的行为以防止站点无法工作DOCTYPE不存在或格式不正确会导致文档以混杂模式呈现
html全局属性(global attribute)有哪些
- class:为元素设置类标识
- data-*:为元素增加自定义属性
- draggable:设置元素是否可拖拽
- id:元素id,文档内唯一
- lang:元素内容的语言
- style:行内css样式
- title:元素相关的建议信息
viewport的content属性作用
<meta name="viewport" content="" />
width viewport的宽度[device-width | pixel_value]width如果直接设置pixel_value数值,大部分的安卓手机不支持,但是ios支持;
height – viewport 的高度 (范围从 223 到 10,000 )
user-scalable [yes | no]是否允许缩放
initial-scale [数值] 初始化比例(范围从 > 0 到 10)
minimum-scale [数值] 允许缩放的最小比例
maximum-scale [数值] 允许缩放的最大比例
target-densitydpi 值有以下(一般推荐设置中等响度密度或者低像素密度,后者设置具体的值dpi_value,另外webkit内核已不准备再支持此属性)
-- dpi_value 一般是70-400//没英寸像素点的个数
-- device-dpi设备默认像素密度
-- high-dpi 高像素密度
-- medium-dpi 中等像素密度
-- low-dpi 低像素密度
附带问题:怎样处理移动端1px被渲染成2px问题
局部处理:
- meta标签中的viewport属性,initial-scale设置为1
- rem按设计稿走,外加利用transfrome的scale(0.5)缩小一倍即可
全局处理:
- meta标签中的viewport属性,initial-scale设置为0.5
- rem按照设计稿标准走即可
meta相关
<!DOCTYPE html> <!--H5标准声明,使用 HTML5 doctype,不区分大小写-->
<head lang=”en”> <!--标准的 lang 属性写法-->
<meta charset=’utf-8′> <!--声明文档使用的字符编码-->
<meta http-equiv=”X-UA-Compatible” content=”IE=edge,chrome=1″/> <!--优先使用指定浏览器使用特定的文档模式-->
<meta name=”description” content=”不超过150个字符”/> <!--页面描述-->
<meta name=”keywords” content=””/> <!-- 页面关键词-->
<meta name=”author” content=”name, email@gmail.com”/> <!--网页作者-->
<meta name=”robots” content=”index,follow”/> <!--搜索引擎抓取-->
<meta name=”viewport” content=”initial-scale=1, maximum-scale=3, minimum-sc
<meta name=”apple-mobile-web-app-title” content=”标题”> <!--iOS 设备 begin-->
<meta name=”apple-mobile-web-app-capable” content=”yes”/> <!--添加到主屏后的标
是否启用 WebApp 全屏模式,删除苹果默认的工具栏和菜单栏-->
<meta name=”apple-mobile-web-app-status-bar-style” content=”black”/>
<meta name=”renderer” content=”webkit”> <!-- 启用360浏览器的极速模式(webkit)-->
<meta http-equiv=”X-UA-Compatible” content=”IE=edge”> <!--避免IE使用兼容模式-->
<meta http-equiv=”Cache-Control” content=”no-siteapp” /> <!--不让百度转码-->
<meta name=”HandheldFriendly” content=”true”> <!--针对手持设备优化,主要是针对一些老的不识别viewport的浏览器-->
<meta name=”MobileOptimized” content=”320″> <!--微软的老式浏览器-->
<meta name=”screen-orientation” content=”portrait”> <!--uc强制竖屏-->
<meta name=”x5-orientation” content=”portrait”> <!--QQ强制竖屏-->
<meta name=”full-screen” content=”yes”> <!--UC强制全屏-->
<meta name=”x5-fullscreen” content=”true”> <!--QQ强制全屏-->
<meta name=”browsermode” content=”application”> <!--UC应用模式-->
<meta name=”x5-page-mode” content=”app”> <!-- QQ应用模式-->
<meta name=”msapplication-tap-highlight” content=”no”> <!--windows phone
设置页面不缓存-->
<meta http-equiv=”pragma” content=”no-cache”>
<meta http-equiv=”cache-control” content=”no-cache”>
<meta http-equiv=”expires” content=”0″>
div+css的布局较table布局有什么优点
- 改版的时候更方便,只要更改css文件
- 页面加载速度更快,结构化清晰,页面显示简洁
- 表现与结构相分离
- 易于优化(seo)搜索引擎更友好,排名更容易靠前
src与href的区别
- src用于替换当前元素,href用于在当前文档和引用资源之间确立联系
- src是source的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置,在请求src资源时会将其指向的资源下载并应用到文档内,例如js脚本、img图片和frame等元素
css相关
calc、support、media各自的含义及用法?
- @support主要是用于检测浏览器是否支持css的某个属性,其实就是条件判断,如果支持某个属性,你可以写一套样式,如果不支持某个属性,你也可以提供另一套样式作为替补;
- calc()函数用于动态计算长度值,calc()函数支持‘+’、‘-’、‘*’、‘/’运算;
- @media查询,你可以针对不同的媒体类型定义不同的样式
css水平、垂直居中的写法、请至少写出4种
水平居中
- 行内元素:text-align:center
- 块级元素:margin:0 auto
- position:absolute + left:50% + tranform:translateX(-50%)
- display:flex + justify-content: center
垂直居中
- 设置line-height等于height
- position:absolute +top:50%+ transform:translateY(-50%)
- display:flex + align-items: center
- display:table+display:table-cell + vertical-align: middle
rem、em、vh、px各自代表的含义?
- rem rem是全部的长度都相对于根元素html元素,通常做法是给html元素设置一个字体大小,然后其他元素的长度单位就为rem
- em 子元素字体大小的em是相对于父元素字体大小 元素的width/height/padding/margin用em的话是相对于该元素的font-size
- vw/vh 全称是viewport width和viewport height,视窗的宽度和高度,相当于屏幕宽度和高度的1%,不过处理宽度的时候%单位更合适,处理高度的话vh单位更好
- px px像素(pixel),相对长度单位,像素px是相对于显示器屏幕分辨率而言的
画一条0.5px的直线
height:1px transform:scale(0.5)
说一下盒模型
盒模型的组成,由里向外content、padding、border、margin 在IE盒子模型中,width表示content+padding+border这三个部分的宽度 在标准的盒子模型中,width指content部分的宽度 box-sizing的使用
- box-sizing:content-box 是w3c盒子模型
- box-sizing:border-box 是IE盒子模型
box-sizing的默认属性是content-box
画一个三角形?
{
width:0;
height:0;
border-width:100px;
border-style:solid;
border-color:transparent ##0099CC;
transparent transparent;
transform:rotate(90deg);/*顺时针旋转90°*/
}
清楚浮动的几种方式、及原理?
- ::after / br / clear:both
- 创建父级 BFC(overflow:hidden)
- 父级设置高度 BFC(块级格式化上下文),是一个独立的渲染区域,让处于BFC内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响
触发条件
- 根元素
- position:absolute / fixed
- display:inline-block / table
- float 元素
- overflow !== visible 规则
- 属于同一个BFC的两个相邻Box垂直排列
- 属于同一个BFC的两个相邻Box的margin会发生重叠 BFC的区域不会与float的元素区域重叠 计算BFC的高度时,浮动子元素也参与计算 文字层不会被浮动层覆盖,环绕于周围
css的重构回流
- 重构:当节点需要更改外观而不会影响布局
- 回流:dom结构的修改引发dom几何尺寸的变化,发生回流
常见的几何属性有width、height、padding、margin、left、top、border或者是dom节点发生增减移动
减少重构和回流的办法
- 使用css3新增属性:translate替代top等方向值
- 避免频繁使用style,而是采用class
link和import区别
link | import |
---|---|
页面被加载,link会同时被加载 | @import引用的css会等到页面被加载完成之后在加载 |
没有任何兼容问题 | 只适用与2.1之后的版本 |
支持使用js去控制dom改变样式 | 不支持 |
只能加载css |
css3新增伪类 - 伪元素
- p:first-of-type 选择属于父元素的首个p元素的每个p元素
- p:last-of-type 选择属于其父元素的最后p元素的每个p元素
- p:only-of-type 选择属于其父元素唯一的p元素的每个p元素
- p:only-child 选择属于其父元素的唯一子元素的每个p元素
- p:only-child(2) 选择属于其父元素的第二个子元素的每个p元素
- :enabled 已启用的表单元素
- :disabled 已禁用的表单元素
- :checked 单选框或复选框被选中
- ::before 在元素之前添加内容
- ::after 在元素之后添加内容,也可以用来做清除浮动
- ::first-line 添加一个特殊的样式到文本的首字母
- ::first-letter 添加一行特殊样式到首行
stylus/sass/less区别
- 均具有“变量”、”混合“、“嵌套”、”颜色混合“五大基本特性
- sass和lass语法较为严谨,less要求一定要使用大括号”{}“,sass和stylus可以通过缩进表示层次和嵌套关系
- sass无全局变量的概念,less和stylus有类似其他语言的作用域概念
- sass是基于Ruby语言的,而less和stylus可以基于nodeJs NPM 下载相应库后进行编译,这也就是为什么安装sass的时候有时候会报错,需要安装python脚本
rgba()和opacity的透明效果有什么不同?
- rgba()和opacity都能实现透明效果,但最大的不同是opacity作用于元素,以及元素内的所有内容的透明度
- 而rgba()只作用于元素的颜色或其背景色,(设置rgba透明的元素的资源色不会继承透明效果!)
js相关
js数据类型 基本类型
- string(字符串)
- number(数字)
- boolean(布尔)
- null(空)
- undefined(未定义)
- symbol(es6引入了一种新的原始数据类型,表示独一无二的值)
引用数据类型 object(对象)
- array(数组)
- funtion(函数)
null和undefined的区别
- undefined表示不存在这个值
- undefined:是一个表示”无“的原始值或者说表示”缺少值“,就是在此处应该有一个值,但是还没有定义,尝试读取时会返回undefined
- 例如变量声明了,但是没有赋值时就等于undefined
- null表示一个对象被定义了,值为”空值“
- null:是一个对象(空对象,没有任何属性和方法)
- 例如作为函数的参数,表示该函数的参数不是对象
- 在验证null时,一定要用===,因为==无法分别null和undefined
var js=function(){}和function js(){}的区别
在Javascript中,函数及变量的声明都将被提升到函数的最顶部,也就是说我们可以先使用后声明,
但函数表达式和变量表达式只是将函数或者变量的声明提升到函数顶部,函数表达式和变量的初始化将不被提升
- var js=function(){} 这种叫做函数表达式 必须先定义后使用
- function js(){}这种是函数声明 可以先使用后定义 它会对函数的声明进行一个提升
箭头函数和普通函数的区别是什么?
普通函数this
- this总是代表它的直接调用者
- 在默认情况下,没找到直接调用者,this指向的是window
- 在严格模式下,没有直接调用者的函数中的this是undefined
- 使用call、apply、bind绑定,this指向的是绑定的对象
箭头函数this
- 在使用=>定义函数的时候,this的指向是定义时所在的对象,而不是使用时所在的对象
- 不能够用作构造函数,这就是说,不能够使用new命令,否则就会抛出一个错误
- 不能够使用arguments对象
- 不能使用yield命令
讲一下let、var、const的区别
- var没有块级作用域,支持变量提升
- let有块级作用域,不支持变量提升,不允许重复声明,暂存性死区,不能通过window变量名进行访问
- const有块级作用域,不支持变量提升,不允许重复声明,声明一个变量一旦声明就不能改变,改变就报错
谈谈你对es6的理解
- 新增模板字符串(为javascript提供了简单的字符串插值功能)
- 箭头函数
- for-of(用来遍历数据——例如数组中的值)
- arguments对象可被不定参数和默认参数完美代替
- es6将promise对象纳入规范,提供了原生的promise对象
- 增加了let和const命令,用来声明变量
- 引入了module模块的概念
原型、原型链
原型:就是实现继承过程中产生一个概念
原型链:每个被实例对象都有_proto_对象,它指向了构造函数的prototype属性,同时该对象可以通过_proto_对象来寻找不属于自身的属性
继承
原理是:复制父类的属性和方法来重写子类的原型对象
- 原型继承
- 构造函数继承
- 组合继承
- 寄生继承
- 寄生组合继承
- class
闭包
闭包就是有权访问一个函数内部变量的函数,也就是常说的函数内部嵌套函数,内部函数访问外部函数变量,从而导致垃圾回收机制没有将当前变量回收掉,这样的操作,有可能带来内存泄漏,好处就是可以设计私有方法和变量
垃圾回收机制
js拥有的特殊的垃圾回收机制,当一个变量在内存中失去引用,js会通过特殊的算法将其回收,并释放内存
分为以下两个阶段:
- 标记阶段:垃圾回收器,从根对象开始遍历,访问到的每一个对象都会被标为可到达对象
- 清除阶段:垃圾回收器在对内存当中进行线性遍历,如果发现该对象没有被标记为可到达对象,那么就会被垃圾回收机制回收
这里牵扯了引用计数法,每次引用都会被+1,如果标记清零,那么就会被回收掉
简述深浅拷贝
浅拷贝 通常需要拷贝的对象内部只有一层的这种对象
常用方法
- object.assign方法来实现
- 扩展运算符…obj
深拷贝
通常是嵌套二层或以上的复杂对象
常用方法
- JSON.parse(JSON.stringfy(object));该方法忽略undefined,忽略symbol,忽略function,只适合简单深拷贝
- 手写递归方法去实现
函数的节流和防抖
参考另一篇文章
描述下this
this,函数执行的上下文,可以通过apply、call、bind改变this的指向,对于匿名函数或者直接调用的函数来说,this指向全局上下文(浏览器为window、nodeJS为global),剩下的函数调用,那就是谁调用它,this就指向谁,当然还有es6的箭头函数,箭头函数的指向取决于该箭头函数声明的位置,在哪里声明,this就指向哪里
call、apply区别
相同点:都是重定向this指针的方法
不同点:call和apply的第二个参数不相同,call是若干个参数的列表,apply是一个数组
关于函数的调用
- 作为一个正常的函数调用
- 函数作为方法调用
- 使用构造函数调用函数
- 作为函数方法调用函数
new操作符到底做了什么
- 创建了一个空对象,并且this变量指向该对象,同时还继承了该函数的原型
- 属性和方法被加入到this引用的对象中
- 新创建的对象由this所引用,并且最后隐式的返回this
Ajax原理
- Ajax的原理简单来说就是在用户和服务器之间加了一个中间层(AJAX引擎),通过XMLHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新新页面,使得用户操作与服务器响应异步化,这其中最关键的一步就是从服务器获得请求数据
- Ajax的过程只涉及javascript、XMLHttpRequest和DOM,XMLHttpRequest是ajax的核心机制
ajax、axios、fetch区别
ajax
- 本身是针对MVC的编程不符合现在前端MVVM的浪潮
- 基于原生XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案
- JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常不合理(采取个性化打包的方案又不能享受CDN服务)
axios
- 从浏览器中创建XMLHttpRequest
- 从node.js发出http请求
- 支持promise API
- 拦截请求和响应
- 转换请求和响应数据
- 取消请求
- 自动转换JSON数据
- 客户端支持防止CSRF/XSRF
fetch
- 只对网络请求报错,对400、500都当作成功的请求,需要封装去处理
- 默认不会带cookie,需要添加配置项
- 本身无自带abort,无法超时控制,可以使用AbortController解决取消请求问题
异步编程的实现方式
- 回调函数
优点:简单、容易理解
缺点:不利于维护,代码耦合度高 - 事件监听(采用时间驱动模式,取决于某个事件是否发生)
优点:容易理解,可以绑定多个事件,每个事件可以指定多个回调函数
缺点:事件驱动型,流程不够清晰 - promise对象
优点:可以利用then方法,进行链式写法,可以书写错误时的回调函数
缺点:编写和理解,相对比较难 - generator函数
优点:函数体内外的数据交换,错误处理机制
缺点:流程管理不方便 - async函数
优点:内置执行器、更好的语义、更广的适用性、返回的是promise、结构清晰
缺点:错误的处理机制
事件模型
W3C中定义事件的发生经历三个阶段:捕获阶段(capturing)、目标阶段(targetin)、冒泡阶段(bubbling)
- 冒泡型事件:当你使用事件冒泡时,子级元素先触发,父级元素后触发
- 捕获型事件:当你使用事件捕获时,父级元素先触发,子级元素后触发
- DOM事件流:同时支持两种事件模型:捕获型事件和冒泡型事件
- 阻止冒泡:在W3c中,使用stopPropagation()方法,在IE下设置cancelBubble =true
- 组织冒泡:阻止事件的默认行为,例如click - a后的跳转,在W3c中,使用preventDefault()方法,在IE下设置window.event.returnValue = false
作用域链
- 作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的
- 简单的说,作用域就是变量和函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期
简单介绍一个event loop
js作为单线程语言,在执行过程中,会产生执行环境,这些执行环境中的代码被顺序的加入到执行栈中,如果遇到异步代码,会被挂起并加入到任务队列当中,等到主线程任务执行完毕,event loop就会从任务队列中取出需要的代码放入执行栈中执行
任务队列有分为宏任务和微任务队列 一次正确的event loop执行顺序如下:
- 执行所有的同步代码
- 执行栈为空,查询是否有需要执行的微任务
- 微任务(有:则执行,无:则跳出)
- 必要的话开始渲染ui
- 开始下一轮的任务队列执行宏任务中的异步代码
instanceOf原理
instanceOf用来判断右边的prototype是否在左边的原型链上,告诉我们左边是否是右边的实例
function instanceof(left, right) { // 获得类型的原型 let prototype = right.prototype // 获得对象的原型 left = left.__proto__ // 判断对象的类型是否等于类型的原型 while (true) {
if (left === null){
return false
}
if (prototype === left){
return true
}
left = left.__proto__ } }
typeof
typeof检测对象,除开函数是function类型之外,像常见的数组,对象或者是正则,日期等等都是object
需要注意一下
typeof Symbol() // 'symbol'
typeof null // object
typeof undefined // undefined
typeof null检测出object因为js最初版本,使用的是32位系统,类型的标签存储在每个单元的低位中000是object类型,null全是0,所以当我们使用typeof进行检测的时候js错误的判断为object
js跨域
- jsonp跨域,原理:script标签没有跨域限制的漏洞实现的一种跨域方法,只支持get请求,安全问题会受到威胁
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参并指定回调执行函数为onBack
script.src = 'http://www.....:8080/login?user=admin&callback=onBack';
document.head.appendChild(script);
// 回调执行函数
function onBack(res) {
alert(JSON.stringify(res));
}
- cors跨域,通过后端服务器实现,Access-Control-Allow-Origin
- postMessagewindow的一个属性方法
- websocket
- nginx反向代理
- iframe跨域
//父窗口:(http://www.domain.com/a.html)
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
document.domain = 'domain.com';
var user = 'admin';
</script>
//子窗口:(http://child.domain.com/b.html)
document.domain = 'domain.com';
// 获取父窗口中变量
alert('get js data from parent ---> ' + window.parent.user);
setTimeout与setInterval的区别
setTimeout表示间隔一段时间之后执行一次调用,而setInterval是每隔一段时间循环调用,直至清除
内存方面,setTimeout只需进入一次宏队列,setInterval不计算代码执行时间,有可能多次执行多次代码
解释一下requestAnimationFrame
- 浏览器专门为DOM动画,canvas动画,SVG动画等等有一个统一的刷新机制
- 按帧对网页进行重绘,该方法告诉浏览器希望执行动画并请求浏览器在下一次重绘之前调用回调函数来更新动画
- 由系统来决定回调函数的执行时机,在运行时浏览器会自动优化方法的调用
script标签如何实现异步加载
- defer:等到整个页面在内存中正常渲染结束(DOM结构完全生成,以及其他脚本执行完成),才会执行
- async是一旦下载完成,渲染就会中断,执行这个脚本之后,再继续渲染
总结就是:defer是渲染完在执行,async是下载完就执行
另外值得注意的就是:deger脚本会按照在页面出现的顺序加载,而async是不能保证加载顺序的
html5的离线存储技术
html5的离线存储技术,是基于一个新建的.appcache文件的缓存机制(并不是存储技术),通过这个文件上的解析清单离线存储资源,这些资源就会像cookie一样被存下来,之后网络处于离线状态时,浏览器会用过被离线存储的数据进行页面展示
数组拍平
- 数组拍平也称数组扁平化,就是将数组里边的数组打开,最后合并为一个数组
- 实现
递归实现
function fn(arr){
let arr1 = []
arr.forEach((val)=>{
if(val instanceof Array){
arr1 = arr1.concat(fn(val))
}else{
arr1.push(val)
}
})
return arr1
reduce实现
function flat(arr) {
return arr.reduce((prev, cur) => {
return prev.concat(cur instanceof Array ? >flat(cur) : cur)
}, [])
}
flat
参数为层数(默认一层)
arr.flat(Infinity)
扩展运算符
function fn(arr){
let arr1 = [];
let bStop = true;
arr.forEach((val)=>{
if(Array.isArray(val)){
arr1.push(...val);
bStop = false
}else{
arr1.push(val)
}
})
if(bStop){
return arr1;
}
return fn(arr1)
}
toString
let arr1 = arr.toString().split(',').map((val)=>{
return parseInt(val)
})
console.log(arr1)
apply
function flatten(arr){
while(arr.some(item => Array.isArray(item))) >{
arr = [].concat.apply([],arr);
}
return arr;
}
js设计模式
单例:任意对象都会单列,不需要特别处理
工厂:同样形式参数返回不同的实例
代理:新建个类调用老类的接口,包一下
观察者:事件模式,比如按钮的onclick这样的应用
发布者、订阅者:发布一个对象/字符串到所有订阅者
详情见
ts相关
ts基础类型
为了让程序更有价值,我们需要能够处理最简单的数据单元:数字、字符串、结构体、布尔值等,typescript支持与javascript几乎相同的数据类型此外还提供了实用的枚举类型方便我们使用
- 布尔值 —— Boolean
let isDone: boolean = false;
- 数字 —— number
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;
- 字符串 —— string
let name: string = "bob";
name = "smith";
- 数组 —— Array
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3]; // 数组泛型
- 元组 —— tuple
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同,比如,你可以定义一对值分别为string和number类型的数组
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
- 枚举 —— enum
enum类型是对javascript标准数据类型的一个补充,像c#等其他语言一样,使用枚举类型可以为一组数值赋予友好的名字
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
- any
有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型,这些值可能来自于动态的内容,比如来自用户输入或第三方代码库,这种情况下,我们不希望类型检查器对这些值进行检查而是直接让他们通过编译阶段的检查,那么我们可以使用any类型来标记这些变量
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
- void
某种程度上来说,void类型像是与any类型相反,它表示没有任何类型,当一个函数没有返回值时,你通常会见到其返回类型是void
function warnUser(): void {
console.log("This is my warning message");
}
声明一个void类型变量没有什么大用,因为你只能为他赋予null和undefined
let unusable: void = undefined;
- null和undefined
typescript里,undefined和null两者各自有自己的类型分别叫做undefined和null,和void相似,他们本身的类型用处不是很大
// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;
- Never
never类型表示的是那些永不存在的值的类型,例如,never类型是那些总是会抛出异常或根本不会有返回值的函数表达式或箭头函数表达式的返回值类型,变量也可能是never类型,当他们永远不为真的类型保护所约束 - object
typeacript和javascript区别
typescript | javascript |
---|---|
它是由网景公司在1995年开发的 | 它是2012年由安德鲁·海尔斯伯格(Anders Hejlsberg)开发的 |
javascript源文件是‘.js’扩展名 | typescript源文件是‘.ts’扩展名 |
javascript不支持es6 | typescript支持es6 |
它不支持强类型或静态类型 | 它支持强类型或静态类型特性 |
它只是一种脚本语言 | 它支持面向对象的编程概念,如类、接口、继承、泛型等 |
javascript没有可选的参数特性 | typescript有可选的参数特性 |
它是解释型语言,这就是为什么它在运行时突出显示错误 | 它编译代码并在开发期间突出显示错误 |
javascript不支持模块 | typescript支持模块 |
在这里number和string是对象 | 在这里,number和string是接口 |
javascript不支持泛型 | typescript支持泛型 |
我们为什么需要typescript
- TypeScript快速、简单,最重要的是,容易学习。
- TypeScript支持面向对象的编程特性,比如类、接口、继承、泛型等等。
- TypeScript在编译时提供了错误检查功能。它将编译代码,如果发现任何错误,它将在运行脚本之前突出显示这些错误。
- TypeScript支持所有JavaScript库,因为它是JavaScript的超集。
- TypeScript通过使用继承来支持可重用性。
- TypeScript使应用程序开发尽可能的快速和简单,并且TypeScript的工具支持为我们提供了自动完成、类型检查和源文档。
- TypeScript支持最新的JavaScript特性,包括ECMAScript 2015。
- TypeScript提供了ES6的所有优点和更高的生产力。
- TypeScript支持静态类型、强类型、模块、可选参数等。
ts的接口是什么意思
接口是我们的应用程序中充当锲约的结构,它定义了要遵循的类的语法,这意味着实现接口的类必须实现它的所有成员,它不能被实例化,但是可以被实现它的类对象引用,无论对象是否具有特定的结构,typescript编译器都能使用接口进行类型检查(也称“duck typing” 鸭子类型或结构化类型)
interface interface_name {
// 字段声明
// 方法声明
}
接口只是声明方法和字段,它不能用来建造任何东西,不需要将接口转换为javascript来执行,它们对运行时javascript没有任何影响,因此,它们唯一的目的是在开发阶段提供帮助
vue/react相关
jq和vue区别
- 从jquery到vue的转变是一个思想的转变,就是将原有的直接操作dom的思想转变到操作数据上去
- 传统前端开发模式中是以jq为核心的,而vue是现在一个兴起的前端js库,是一个精简的mvvm
- jquery是使用选择器$选取dom对象,对其进行赋值、取值、绑定等操作,他和其他原生的html区别只是在于更方便的选取和操作dom,而数据和界面是连在一起的,他还是依赖dom元素的值
- vue则是通过vue对象将数据和view完全分离开来了,对数据进行操作不再需要引用相应的dom对象,可以说数据和view是分离的
- vue的适用场景:复杂的数据操作的后台页面,表单填写页面
- jq的使用场景:一些html5的动画页面,一些需要js来操作页面样式的页面
- 二者是可以结合起来一起使用的,vue侧重数据绑定,jq侧重样式操作、动画效果等,则会更加高效率的完成业务需求
proxy和defineProperty区别
Object.defineProperty缺点:
- 无法监控数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应,vue内部通过数组的一些方法来监听
- 只能劫持对象的属性,因此要对每个对象的属性进行遍历,vue2.x版本之后是通过递归和遍历实现对data对象的数据监控
proxy:
- 可以劫持整个对象,并返回一个新的对象
- 有多种劫持操作
MVC和MVVM区别
MVC
- M:Model数据模型(专门用来操作数据,数据库)
- V:View视图(对于前端来说就是页面)
- C:Controller控制器(是视图和数据模型沟通的桥梁,用于处理业务逻辑)
View接受用户交互请求
View将请求转交给Controller处理
Controller操作Model进行数据更新保存
数据更新保存之后,Model会通知View更新
View更新变化数据使用户得到反馈
简单来说就是通过controller的控制去操作model层的数据,并且返回给view层展示
MVVM
- M:Model是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,对于前端来说就是后端提供的api接口
- V:View是视图层,也就是用户界面,前端主要由HTML和CSS来构建
- VM:是由前端开发人员组织生成的维护的视图数据层,在这一层,前端开发者对从后端获取的Model数据进行转换处理,做二次封装,以生成符合View层使用预期的视图数据模型
在MVVM架构中,引入了ViewModel的概念,MVVM的出现促进了前端开发与后端业务逻辑的分离,极大的提高了前端开发效率,MVVM的核心是ViewModel层,它就像是一个中转站(value converter),负责转换Model中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与Model层通过接口请求进行数据交互,起呈上启下的作用
MVVM框架实现了双向绑定,这样ViewModel的内容会实时展现在View层,前端开发者再也不必低效又麻烦的通过DOM去更新视图,MVVM框架已经已经把最脏最累的一块做好了,我们开发者只需要处理和维护ViewModel,更新数据视图就会自动得到相应更新
这样View层展现的不是Model层的数据,而是ViewModel的数据,由ViewModel负责与Model层交互,这就完全解耦 了View层和Model层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环
SPA应用优点?
定义:单页面web是一种特殊的web应用,他将所有的活动局限于一个web页面中,仅在该web页面初始化时加载相应的html、js、css,一旦页面加载完成了,SPA不会因为用户的操作而进行页面新的重新加载或跳转,取而代之的是利用js动态的变换html的内容,从而实现ui与用户的交互,由于避免了页面的重新加载,SPA可以提供转为流畅的用户体验
优点:
- 良好的交互体验
- 前后端职业分离,架构清晰
- 减轻服务器压力
- 共用一套后端代码
缺点
- SEO难度高
- 首次加载时间长
- 页面复杂度提高,复杂逻辑程度
vue的双向绑定原理
双向绑定,采用数据劫持结合发布者——订阅者模式,通过object.defineproperty()来劫持各个属性的getter和setter,在数据变动时发布消息给订阅者,触发相应的监听回调
详情见
如何理解vue响应式系统
任何一个vue component都有一个与之对应的watcher实例,vue的data上的属性会被添加getter和setter属性,当vue component render函数被执行的时候,data上会被触碰(touch),即被读,getter方法会被调用,此时vue会记录此vue component 所依赖的所有data(这一过程叫做依赖收集)data被动时(主要使用户操作),即被写,setter方法会被调用,此时vue会去通知所有依赖于此data的组件去调用他们的render函数进行更新
描述一个vue从初始化页面——修改数据——刷新页面UI的过程
当vue进入初始化阶段时,一方面vue会遍历data中的属性并用object.defineproperty将它转化成getter/setter的形式,实现数据劫持,另一方面,vue的指令编辑器compiler对元素节点的各个指令进行解析,初始化视图并订阅watcher来更新视图,此时watcher会将自己添加到订阅器Dep中,此时初始化完毕,当数据发生变化时,触发obServe中setter方法,立即调用Dep.notify(),Dep这个数组开始遍历所有订阅者,并调用其update方法,vue内部在通过diff算法,patch相应的更新完成对订阅者视图的改变
虚拟DOM实现原理
- 虚拟DOM本质上是JavaScript对象,是对真实DOM的抽象
- 状态变更时,记录新树和旧树的差异
- 最后把差异更新到真正的DOM中
为什么需要Virtual Dom
首先我们都知道在前端性能优化的一个秘诀就是尽可能的少操作dom,不仅仅是dom相对较慢,更因为频繁变动dom会造成浏览器回流或者重构,这些都是性能的杀手,因此我们需要这一层抽象,在patch过程中尽可能的一次性将差异更新到dom中,这样保证了dom不会出现性能很差的情况
其次,现代前端框架的一个基本要求就是无须手动操作dom,一方面是手动操作dom无法保证程序性能,多人协作的项目中,如果review不严格,可能会有开发者写出性能较低的代码,另一方面更重要的是省略手动dom操作可以大大提高开发效率,最后,也是Virtual Dom最初的目的,就是更好的跨平台,比如node.js就没有dom,如果想实现ssr(服务端渲染),那么一个方式就是借助Virtual dom,因为Virtual Dom 本身就是JavaScript对象
既然vue通过数据劫持可以探测数据变化,为什么还需要虚拟dom diff 检测差异
现代前端框架有两种方式侦测变化,一种是pull,一种是push
- pull:其代表是react,我们可以回忆一下react是如何侦测到变化的,我们通常会用setState Api 显示更新,react会进行一层层的Virtual Dom diff 操作找出差异,然后patch到dom上,react从一开始就不知道是哪发生了变化,只是知道了有变化了,然后再进行比较暴力的diff操作查找哪变化了,另一个代表就是Angular的脏检查操作
- push:vue的响应式系统则是push的代表,当vue程序初始化的时候,就会对数据data进行依赖的收集,一旦数据发生变化,响应式系统就会立刻得知,因此vue是一开始就知道是哪发生变化了,但是这又会产生一个问题,如果你熟悉vue的响应式系统就知道,通常绑定一个数据就需要一个watcher,一旦我们的绑定细粒度过高就会产生大量的watcher,这会带来内存以及依赖追踪的开销,而细粒度过低又会无法精准侦测变化,因此vue设计是选择中等的细粒度的方案,在组件级别进行push侦测的方式,也就是那套响应式系统,通常我们会第一时间侦测到发生变化的组件,然后在组件内部进行Virtual Dom diff获取更加具体的差异,而Virtual Dom diff 则是pull操作,vue是push+pull结合的方式进行变化的侦测的
vue中key值的作用
当vue.js用v-for正在更新已渲染过的元素列表时,它默认用“就地复用”策略,如果数据项的顺序被改变,vue将不会移动DOM元素来匹配数据项的顺序,而是简单复用此处每个元素,并且确保它在特定的索引下显示已被渲染过的每个元素,key的作用主要是为了高效的更新虚拟dom
vue生命周期
vue实例对象从创建到销毁的过程
所有功能实现都是围绕其生命周期进行的,在生命周期的不同阶段用对应的钩子函数,实现组件数据管理的dom渲染两个重要功能
- beforeCreate: 在new一个vue实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没有创建,在beforeCreate生命周期执行的时候,data和methods中的数据都还没有初始化,不能在这个阶段使用data中的数据和methods方法
- created: data和methods都已经被初始化好了,如果想调用methods中的方法,或者操作data中的数据,最早可以在这个阶段中操作
- beforeMount: 执行到这个钩子的时候,在内存中已经编译好了模板了,但是还没有挂载到页面,此时页面还是旧的
- mounted: 执行到这个钩子的时候就表示vue实例已经初始化好了,此时组件脱离了创建阶段,进入到了运行阶段,如果我们想要通过插件操作页面上的dom节点,最早可以在和这个阶段中进行
- beforeUpdate: 当执行这个钩子时,页面中的显示的数据是更新前的,页面还没有和最新的数据同步
- updated: 页面显示的数据和data中的数据已经保持同步了,都是最新的
- beforeDestory: vue实例从运行阶段进入到了销毁阶段,这个时候所有的data和methods、指令、过滤器等等,都有处于可用状态,还没有真正被销毁
- destoryed: 这个时候上所有的data和methods、指令、过滤器等等,都是处于不可用的状态,组件也已经被销毁
watch、methods和computed的区别
- watch为了监听某个响应式数据的变化
- computed是自动监听依赖值的变化,从而动态的返回内容,主要目的是简化模板内复杂的运算,所以区别来源于方法,只是需要动态值那就用computed,需要知道值的改变后执行业务逻辑才用watch
- methods是一个方法,它可以接收参数,而computed不能,而computed是可以缓存的,而methods不会,computed可以依赖其他computed,甚至是其他组件的data
组件中写name选项有什么用
- 项目中使用keep-alive时,可搭配组件name进行缓存过滤
- dom做递归组件时需要调用自身name
- vue-devtoos调试工具里显示的组件名称是由vue组件中name决定的
vue-router两种模式
- hash模式,使用url的hash来模拟一个完整的url,于是当url改变的时候,页面不会重新加载,也就是单页面应用了,当#后面的hash发生变化,不会导致浏览器向服务器发出请求,浏览器不发出请求就不会刷新页面,并且会触发hashChange这个事件,通过监听hash值来实现更新页面部分内容的操作
- history模式,主要使用html5的pushState和replaceState()这两个api来实现的,pushState()可以改变url地址并且不会发送请求,replaceState()可以读取历史记录栈,还可以对浏览器记录进行修改
- 区别: 前面的hashChange,你只能改变后面的url片段,而pushState设置的新的url可以是与当前url同源的任意url,history模式则会将url修改得就和正常请求后端得url一样,如后端没有配置对应/user/id得路由处理,则会返回404错误
vue-router有哪些钩子函数
- 全局前置守卫 router.beforeEach
- 全局解析守卫 router.beforeResolve
- 全局后置守卫 router.afterEach
- 路由独享守卫 beforeEnter
- 组件内的守卫 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
详情见
route和router区别
- router是“路由实例对象”,包括路由的跳转方法(push、replace)钩子函数
- route是“路由信息对象”,包括path、params、hash、query、fullpath、name等路由信息参数
vue的nextTick的原理是什么
- 为什么需要nextTick
vue是异步修改dom的,并且不鼓励开发者直接接触dom。但有时候业务需要必须对数据更改——刷新后的dom做相应的处理,这时就可以使用vue.nextTick(callback)这个Api了 - 理解nextTick
nextTick的原理正是vue通过异步队列控制dom更新和nextTick回调函数先后执行的方式
详情见
vue通信
- props / $emit
- $emit / $on
- vuex
- $attrs / $listeners
- provide / inject
- $parent / $children 与ref
vue组件中的data为什么是个函数
声明式渲染中的data只是在当前页面挂载的节点上使用,但是对于组件有个很明显的特征就是在于复用
vue.$set视图更新
向响应式对象添加一个属性,并确保这个新属性同样也是响应式的,并且触发视图更新
vue中怎么重置data
使用object.assign(),vm.$ data可以获取当前状态下的data,vm.$option.data(this),可以获取到组件初始化状态下data
object.assgin(this. $data,this. $option.data(this))
vuex有哪几种属性
state、getter、mutation、action、module
vue首屏加载优化
- 把不常用的库放到index.html中,通过cdn引入
- vue路由的懒加载
- vue组件尽量不要全局引入
vue常用的修饰符有哪些
在程序世界里,修饰符适用于限定类型以及类型成员的声明的一种符号
在vue中,修饰符处理了许多dom事件的细节,让我们不再需要花大量的时间去处理这些烦恼的事情,而能有更多的精力专注于程序的逻辑处理
vue中修饰符分为一下五种
- 表单修饰符
- 事件修饰符
- 鼠标按键修饰符
- 键值修饰符
- v-bind修饰符
详情见
react与vue区别
- vue使用的是template模板编写,react使用的是jsx语法
- 状态管理:react中的状态全部存入state中,vue中的state对象不是必须的,vue是通过data属性在vue对象中进行管理
- 监听数据的变化,vue劫持一些函数,能精确的知道数据的变化,react中默认是通过比较引用的方式去进行的,如果不优化使用shouldComponentUpdate/PureComponent方法优化,那会导致大量的虚拟dom重新渲染
- 数据流不同,vue可以进行组件与dom之间v-model双向绑定,react从始至终都只有单向数据流
- vue中使用的是mixins,react使用的是Hoc高阶组件
setState同步和异步
- setState只是在合成事件和生命周期函数中是异步更新
- 在setTimeout、原生事件、async函数中是同步更新
react请求与生命周期
react的异步请求,放入compontentDidMount中才是正确的操作
因为WillMount中请求发送,react的执行机制是不会等到数据返回之后才继续往下执行,而是继续向下执行并render,不会’暂停‘以等待数据到达
问题:服务器渲染时,如果在WillMount中请求数据,fetch data会执行两次,一次在服务端一次在客户端,造成了多余的请求,而且16版本之后,WillMount可能在一次渲染中多次调用
react通信
- 子传父:父组件通过props的方式传递
- 子传父:props+回调函数方式
- 兄弟组件:找到这两个共同的父节点,结合props和回调函数进行通信
- 跨层级通信:context通信
- store
react的渲染原理
- 单项数据流,只能通过数据层的变化去影响视图层变化
- 数据驱动视图,无需关注dom,只用关注数据即可
- 渲染过程,生命周期函数
- diff算法,对照两次dom不用的部分渲染
useEffect和useLayoutEffect区别
useEffect是异步,useLayoutEffect是同步的
react中key的作用
key是给每一个虚拟节点的唯一id,可以依靠key,更准确,更快的拿到oldVnode中对应的vnode节点,利用key的唯一性生成map对象来获取对应节点,比遍历方式更快
react有哪些组件
- 无状态组件
函数或无状态组件是一个纯函数,它可以接受参数,并返回react元素,这些都是没有任何副作用的纯函数,这些组件没有状态或生命周期方法 - 有状态组件
类或有状态组件具有状态和生命周期方可能通过setState()方法更改组件的状态,类组件是通过扩展react创建的,它在构造函数中初始化,也可能有子组件 - 受控组件
受控组件是在react中处理输入表单的一种技术,表单元素通常维护它们自己的状态,而react则在组件的状态属性中维护状态,我们可以将两者结合起来控制输入表单,这称为受控组件,因此,在受控组件表单中,数据由react组件处理 - 非受控组件
大多数情况下,建议使用受控组件,有一种称为非受控组件的方法可以通过使用ref来处理表单数据,在非受控组件中,ref用于直接从dom访问表单值,而不是事件处理程序 - 容器组件
容器组件是处理获取数据,订阅redux存储等的组件,它们包含展示组件和其他容器组件,但是里面从来没有html - 高阶组件
高阶组件是将组件作为参数并生成另一个组件的组件,redux connect是高阶组件的实例,这是一种用于生成可重用组件的强大技术
小程序相关
文件类型
- WXML —— 模板文件
- JSON —— 配置/设置文件,如标题、tabbar、页面注册
- WXSS —— 样式文件,样式可直接用import导入
- JS —— 脚本逻辑文件,逻辑处理,网络请求
- app.json —— 配置文件入口,整个小程序的全局配置,网络超时时间、底部tab、页面路径,window字段是小程序所有页面的顶部背景颜色、文字颜色
- app.js —— 可以没有内容,可以在里边监听声明周期函数、声明全局变量
- app.wxss —— 全局配置样式文件
数据请求怎么封装
- 将所有的接口放在统一的js文件中并导出(或者将请求地址、头、方法在一个js文件里统一定义为一个常量输出)
- 在app.js创建封装请求数据的方法
- 在子页面中调用封装的方法请求数据
参数传值的方法
- 给html元素中添加data-*属性来传递需要的值,之后通过e.currentTarget.dataset或onload的param参数获取,注意不能有大小写字母,不可存放对象
- 跳转页面时通过navigator传递需要的参数值
- 设置id的方法标识,通过e.currentTarget.id获取设置的id值,然后通过设置全局变量的方法来传递数值
页面间传值的方式
- url
- storag(wx.storageSync)
- 全局变量(getApp)
提高小程序的应用程序速度的方法
- 减少默认data的大小
- 组件化方案,封装公用组件,减少代码量
小程序的优点
- 无需下载
- 打开速度快
- 开发成本低
- 为用户提供良好的安全保障,发布有一套严格的审查流程,不能通过审查的程序无法发布上线
- 服务请求快
小程序缺点
- 依托微信,不能开发后台管理功能
- 大小不能超过2M,不能打开超过5个层级的页面
简述小程序原理
小程序分为两个部分webview和appService,webview用来展现ui,appService用来处理业务逻辑、数据及接口调用,它们在两个进程中运行,通过系统层JSBridge实现通信,完成ui渲染、事件处理
小程序和vue写法的区别
- 循环遍历:小程序是wx:for=“list”,vue是v-for=“list”
- 调用data模型:小程序是this.data.info,vue是this.info
- 给模型赋值:小程序是this.setData({info:1}),vue是直接this.info = 1
小程序的双向绑定和vue哪里不一样
小程序直接this.data的属性是不可以同步到视图的,必须调用this.setData({})
1px = 2rpx
生命周期函数
- onload —— 页面加载,调一次
- onShow —— 页面显示,每次打开页面都调用(路由变化就调用)
- onReady —— 初次渲染完成,调一次
- onHide —— 页面隐藏,当navigateTo或底部tab切换时调用
- onUnload —— 页面卸载,当redirectTo或navigateBack时调用
几种跳转、小程序内的页面跳转
- wx.navigateTo —— 保留当前页面,跳转到应用内的某个页面,但是不能跳到tabbar页面(参数必须为字符串)
- wx.redirectTo —— 关闭当前页面,跳转到应用内的某个页面,但是不允许跳转到tabbar页面
- wx.switchTab —— 跳转到tabbar页面,并关闭其他所有非tabbar页面,路径后不能带参数
- wx.navigateBack —— 关闭当前页面,返回上一页面或多级页面,可通过getCurrentPages()获取当前的页面栈,决定需要返回几层
- wx.reLaunch —— 关闭所有页面,打开到应用内的某个页面
- 通过navigator跳转
如何实现下拉刷新
- 先在app.json或page.json中配置enablePullDownRefresh:true
- page里用onPullDownRefresh函数,在下拉刷新时执行
- 在下拉函数执行时发起数据请求,请求返回后,调用wx.stopPullDownRefresh停止下拉刷新的状态
bindtap和catchtap的区别是什么
- bindtap不会阻止事件冒泡
- catchtap阻止事件冒泡
setData的回调函数
微信小程序的setData实现是和react的setState实现类似的,所以它也是一个异步函数,并且有回调函数的参数,当然平时小量数据我们可能并没有感觉到它的异步,但是为了确保逻辑的正确执行,在需要用到setData后,data里的数据的步骤,请写入setData的回调函数中,如
this.setData({
a: this.data.a++
},()=>{
})
小程序和小程序的跳转
- 在同一主体公众号上关联2个小程序appid
- 用navigator,对应设置一些属性即可
- target:miniProgram —— 其他小程序
- target:self —— 当前小程序
分包的操作,发布的时候是选择某个包来发吗
- 分包:主包添加跳转路径,分包放内容,在app.json配置subpakeages声明项目分包结构,代码包总包大小为12M,单个主包/分包大小不能超过2M
- 按照功能划分的打包原则:可以按照功能的划分,拆分为几个分包,当需要用到某个功能是,才加载这个功能对应的分包,公用逻辑,组件放在主包内
- 首次启动时,先下载小程序主包,显示主包内的页面,如果进入了某个分包的页面,在下载这个对应的分包,下载完毕后,显示分包的页面
- 总结:首先配置好打包路径,tabbar页面必须在主包内,各分包之间不能相互调用,能调用主包内的
- 分包加载,与分包加载
小程序的微信支付是哪个api,参数是哪些及怎么获取
wx.requestPayment
授权验证登录怎么做,用户退出后下次进入还需要再次授权吗
- wx.login获取到一个code,拿到code去请求后台得到openId, sessionKey, unionId
- 调用wx.getUserInfo
- 一次性授权
- 永久授权:调取授权登录接口并把获取到的用户公开信息存入数据库
验证授权是自动弹出还是触发的
按钮触发的,open-type指定为getUserInfo类型
小程序关联公众号如何确定用户的唯一性
unionid是相同唯一的
浏览器相关
tcp三次握手、四次挥手
三次握手
- 第一次握手:建立连接时,客户端A发送SYN包(syn=j)到服务器B,并进入到SYN_SEND状态,等待服务器B确认
- 第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时给自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN_SEND状态
- 第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=j+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手
四次挥手
由于tcp连接是全双工的,因此每个方向都必须单独进行关闭,这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接,收到一个FIN只意味着这一方向上没有数据流动,一个tcp连接在收到一个FIN后仍能发送数据,首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭
- 客户端A发送一个FIN,用来关闭客户端A到服务器B的数据传送
- 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1,和SYN一样,一个FIN将占用一个序号
- 服务器B关闭与客户端A的连接,发送一个FIN给客户端A
- 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1
为什么建立连接协议是三次握手,而关闭连接却是四次握手
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的连接请求后,它可以把ACK和SYN(ACK起应答作用,SYN起同步作用)房子啊一个报文里来发送,但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了,但未必你所有的数据都发送给对方了,所以你可能未必会马上关闭SOCKET,即你可能还需要发送一个数据给对方之后,再发送DIN报文给对方来表示你同意现在可以关闭连接了,所以他这里的ACK报文和FIN报文多数情况下都是分开发送的
浏览器内核的理解
- 主要分为两个部分:渲染引擎、js引擎
- 渲染引擎:负责取得网页的内容(html css img …),以及计算网页的显示方式,然后会输出至显示器或打印机,浏览器的内核不同对于网页的语法解释也不同,所以渲染的效果也不一样
- js引擎:解析和执行javascript来实现网页的动态效果
- 最开始渲染引擎和js引擎并没有区分的很明确,后来js引擎越来越独立,内核就倾向于渲染引擎
- IE:trident内核
- Firefox:gecko内核
- Safari:webkit内核
- Opera:以前是presto内核,Opera现已改用Google - chrome的Blink内核
- Chrome:Blink(基于webkit,Boogle与Opera Software共同开发)
http和https区别
http | https |
---|---|
80端口 | 443端口 |
无需申请证书 | 需要申请证书 |
超文本传输协议 | ssl加密协议 |
快 | 慢(因为会有一个ssl包需要传输) |
ssl加密
分为对称和非对称加密
- 对称加密,客户端和服务端公用一个密钥对消息加解密,(客户端和服务器端约定好一个加密钥匙,客户端在发消息浅用该密钥对消息加密,发送给服务器,服务器在用该密钥进行解密拿到消息)
- 非对称加密,客户端和服务端都有公钥和私钥。公钥加密的内容只有对应的私钥解密,私钥自己留着,公钥发给对方,这样发送消息之前,对方的公钥对消息进行加密,收到后在用自己的私钥进行解密
简述cookie、localstorage、sessionstorage
名称 | 大小 | 网络请求 | 生命周期 |
---|---|---|---|
cookie | 4kb | 每次都会携带在http头中,如果使用cookie保存过多数据会带来性能问题 | 默认是关闭浏览器后失效,但是也可以设置过期时间 |
localstorage | 5M | 仅在浏览器中保存,不参与和服务器的通信 | 除非手动被清除,否则永久保存 |
sessionstorage | 5M | 仅在浏览器中保存,不参与和服务器的通信 | 仅在当前会话(窗口)下有效,关闭窗口或浏览器后被清除,不能设置过期时间 |
http请求+作用?
- get方法:发送一个请求来获取服务器资源
- post方法:向服务器提交数据
- put方法:与post方法很像,也是提交数据,但put制定了资源在服务器上的位置,常用在修改数据
- head方法:只i请求页面的首部信息
- delete方法:删除服务器上的资源
- options方法:用于获取当前url支持的请求方式
- trace方法:用于激活一个远程的应用层请求消息回路
- connect方法:把请求链接转换到透明的tcp/ip的通道
http状态码
- 1xx:信息状态码
- 2xx:成功状态
- 3xx:重定向
- 4xx:客户端错误
- 5xx:服务器错误
从浏览器地址栏输入url后发生了什么?
(简化版)
- 浏览器根据请求的url交给dns域名解析,找到真是ip,向服务器发起请求
- 服务器交给后台处理完成后返回数据,浏览器接受文件(html、js、css、图像等)
- 浏览器对加载到的资源(html、js、css等)进行语法解析,建立相应的内部数据结构(如html的dom)
- 载入解析到的资源文件,渲染页面,完成
详情见图,自行了解
get和post请求的区别
get | post |
---|---|
参数长度有限制 | 参数长度无限制 |
get会把请求的数据附在url上 | post请求会把数据附加在请求体中 |
get是明文传输 | post不是明文传输 |
请求能缓存 | 不能缓存 |
DNS是如何解析的?
浏览器缓存 -> 本地缓存 -> hosts文件 -> 路由器缓存 -> ISP DNS缓存 -> DNS递归查询
强缓存和协商缓存
强缓存和协商缓存,强缓存通过响应头实现:expires和cache-control,它表示在缓存期间不需要在发起请求,协商缓存:如果缓存过期,可以使用协商缓存解决问题
协商缓存是需要发起请求,协商缓存需要客户端和服务端共同实现
浏览器禁用cookie该如何处理
一般会用到url重写的技术来进行会话跟踪,每一次的交互,都会在url后面加上sid=xxx类似的参数,服务端根据这种方式来识别用户
优化相关
SEO优化
- 合理的title、description、keywords:搜索对着三项的权重逐个减小,title值强调重点即可,重点关键词出现不要超过2次,而且排名要靠前,不同页面的title要有所不同,description把页面内容高度概括,长度合适,不可过分堆砌关键词,不同页面description有所不同,keywords列举出重要关键词即可
- 语义化的html代码,符合W3C规范:语义代码让搜索引擎容易理解网页
- 重要内容html代码放在最前面:搜索引擎抓取html顺序是从上到下,有的搜索引擎对抓取长度有限制,保证重要内容一定会抓取
- 重要内容不要用js输出:爬虫不会执行js获取内容
- 少用iframe:搜索引擎不会抓取iframe中的内容
- 非装饰性图片必须加alt
- 提高网站速度:网站速度是搜索引擎排序的一个重要指标
server优化
- 减少http请求,合并文件,雪碧图
- 减少dns查询,使用缓存
- 减少dom元素的数量
- 使用cdn
- 配置ETag、http缓存的手段
- 对组件使用Gzip压缩
- 减少cookie的大小
css优化
- 将样式表放在页面顶部
- 使用less、scss表达式
- 使用link不适用@import引入样式
- 压缩css
- 禁止使用css3代码代替js动画(尽可能避免重绘重排以及回流)
- 对于一些小图标,可以使用base64位编码,以减少网络请求
- 页面头部的style、script会阻塞页面(因为Render进程中js线程和渲染线程是互斥的)
- 当需要设置的样式很多时设置className而不是直接操作style
js方面
- 将脚本放到页面底部
- 将js外部引入
- 压缩js
- 使用ESlint语法检测
- 减少dom的操作
- 熟练使用设计模式
- 禁止使用iframe(阻塞父文档onload事件)
- 页面中空的href和src会阻塞页面其他资源的加载
- 网页gzip、cdn托管、data缓存、图片服务器
webpack优化点
- 代码合并压缩插件UglifyJsPlugin
- 服务器启用gzip压缩
- 按需加载资源文件require.ensure
- 优化devtool中的source-map
- 剥离css文件,单独打包
- 去除不必要的插件,通常就是开发环境与生产环境用同一套配置文件导致
- 开发环境不做无意义的工作如提取css计算文焕hash等
- 配置devtool
- 优化构建时的搜索路径 指明需要构建目录及不需要构建目录
性能优化的方法
- dns预解析
- 浏览器缓存,强缓存和协商缓存
- 预加载,将一些不影响首屏但重要的文件延后加载preload
- 预渲染prerender
- 懒加载
- 文件优化
- webpack优化,使用到tree shaking,各种loader等等
其他
深度优先和广度优先
- 广度优先:尝试访问尽可能靠近它的目标节点,然后逐层向下遍历,直至最远的节点层级
- 深度优先:从起始节点开始,一直向下找到最后一个节点,然后返回。又继续下一条路径,直到找遍所有的节点
渐进增强和优雅降级
- 渐进增强:针对低版本浏览器进行构建页面,保证最基本的功能,然后针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验
- 优雅降级:一开始就构建完整的功能,然后再针对低版本的浏览器进行兼容
CommonJS,AMD,CMD
CommonJS、AMD、CMD都是js模块化的规范
CommonJS是服务器端js模块化的规范,nodejs是这种规范的实现
AMD(异步模块定义)和CMD(通用模块定义)都是浏览器端js模块化的规范,requireJS遵循的是AMD,SeaJS遵循的是CMD
commonjs与es6的moduled的区别
commonjs | modules |
---|---|
运行时加载 | 编译时输出接口 |
输出的是值拷贝 | 值得引用 |
导入模块的路径可以是表达式 | 字符串 |
this指向当前模块 | undefined |
Common、AMD、CMD区别
Common | AMD | CMD |
---|---|---|
同步 | 异步 | 异步 |
依赖前置 | 就近依赖 |
fiber是什么
解决同步阻塞方法,异步和任务分割
任务分割调度算法,主要是将原先同步更新渲染的任务分割成一个个独立的小人物,根据优先级,将小任务分散到浏览器的空间时间执行,充分利用主进程的时间循环机制
面向对象编程思想
- 基本思想是使用对象、类、继承、封装等基本概念来进行程序设计
- 易维护
- 易扩展
- 开发工作的重用性、继承性高、降低重复工作量
- 缩短了开发周期
webpack proxy跨域
首先需要明白webpack proxy跨域只能用作与开发阶段,临时解决本地请求服务器产生的跨域问题,并不适合线上环境,配置在webpack的devServer属性中,webpack中的devSever配置后,打包阶段在本地临时生成了一个node服务器,浏览器请求服务器相当于请求本地服务
webpack热更新原理
核心就是客户端从服务端拉取更新后的文件,进行一个替换
实际上WDS与浏览器之间维护了一个Websocket,当本地资源发生变化时,WDS会向浏览器推送更新,并带上构建时的hash,让客户端与上一次资源进行对比,客户端对比出差异后会向WDS发起ajax请求来获取更改内容(文件列表、hash)这样客户端就可以再借助这些信息继续向WDS发起jsonp请求获取该chunk的增量更新
webpack的构建过程
- 初始化参数:从配置文件和Shell语句中读取与合并参数,得出最终的参数
- 开始编译,用上一步得到的参数初始化Compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译
- 确定入口:根据配置中的entry找出所有入口文件
- 编译模块:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,在递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
- 完成模块编译:在经过第4步使用Loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
- 输出完成:再确定好输出内容后,根据之前确定输出的路径和文件名,把文件内容写入到文件系统
提升webpack打包速度
- happypack
- dll采用webpack的DllPlugin和DllReferencePlugin引入dll,让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间
了解Service Worker吗
Service Worker是运行在浏览器背后的独立线程,一般可以用来实现缓存功能,使用Service Worker的话,传输协议必须为https,因为Service Worker中涉及到请求拦截,所以必须使用https协议来保障安全
常见的网站漏洞有哪些?
- 有跨站脚本攻击(XSS)
- 跨站请求伪造(CSRF)
- 点击劫持
- SQL注入
- DDOS攻击
- DNS劫持