WarpPrism / Blog

Do Epic Things
52 stars 7 forks source link

9月OKR:工作中遇到的一些前端问题及解决方案 #32

Open WarpPrism opened 7 years ago

WarpPrism commented 7 years ago

前端知识薄弱点:

$.ajax方法常用参数:

// jQuery.ajax( url [, settings ] ) 
$.ajax({
    async: true,
    type: 'post/get',
    url: '',
    headers: {},
    beforeSend: function(jqXHR, settings) {},   //发送ajax请求前调用
    success: function( Anything data, String textStatus, jqXHR jqXHR ) {},
    complete: function(jqXHR, settings) {},    //success,error回调触发后调用
    contentType: 'application/x-www-form-urlencoded; charset=UTF-8',    //指定发送数据的content type
    xhrFields: {
      withCredentials: true
   }
});

jQuery中的事件代理

事件代理利用了事件冒泡的原理,通过在父级元素绑定handler来监听子元素上触发的事件,从而减少了事件绑定的次数,也能够为动态添加的元素绑定事件:

// 普通绑定,当页面渲染完成时,为所有btn1添加hanlder,之后添加的btn1则没有效果
$('.btn1').click(function() {});
// 事件代理,所有btn2上触发的事件会冒泡到document上触发
$(document).on('click', '.btn2', function() {});

页面元素宽高的定义

Height Height

ajax中FormData的概念

FormData类型其实是在XMLHttpRequest 2级定义的,它是为序列化表以及创建与表单格式相同的数据(当然是用于XHR传输)提供便利。 FormData中的数据是以键值对的形式存储的,其中key是唯一的,一个key可以对应value,不同的value存储在一个数组中,比如:

var data = new FormData();
data.append('student', 'xiaoming');
data.append('student', 'gangang');
data.append('teacher', 'Mr Wang');
data.getAll('student'); // ['xiaoming', 'gangang']

FormData还有一个重要作用是ajax上传文件,例如:

var formData = new FormData();
var name = $("input").val();
formData.append("file",$("#upload")[0].files[0]);
formData.append("name",name);
$.ajax({  
        url : Url,  
        type : 'POST',  
        data : formData,  
        // 告诉jQuery不要去处理发送的数据
        processData : false, 
        // 告诉jQuery不要去设置Content-Type请求头
        contentType : false,
        beforeSend:function(){
               console.log("正在进行,请稍候");
                },
        success : function(responseStr) { 
            if(responseStr.status===0){
                console.log("成功"+responseStr);
            }else{
                console.log("失败");
            }
        },  
        error : function(responseStr) { 
            console.log("error");
        }  
    }); 

Fetch API

FetchAPI 是基于 Promise 设计的资源请求API,用于在未来的标准中代替ajaxAPI,以promise的转态改变来驱动请求的整个过程,而不是传统ajax的事件驱动。也有利于和其他标准,如async/await,generator等新型API结合,使得未来的代码更加简洁,可读性更好。标准请参照: MDN Doc

// 基本语法
fetch(url, options).then(function(response) {
  // handle HTTP response
}, function(error) {
  // handle network error
})

//兼容包
require('babel-polyfill')
require('es6-promise').polyfill()

import 'whatwg-fetch'

fetch(url, {
  method: "POST",
  body: JSON.stringify(data),
  headers: {
    "Content-Type": "application/json"
  },
  credentials: "same-origin"
}).then(function(response) {
  response.status     //=> number 100–599
  response.statusText //=> String
  response.headers    //=> Headers
  response.url        //=> String

  response.text().then(function(responseText) { ... })
}, function(error) {
  error.message //=> String
})

fetch接受2个参数,第一个是url或者Request对象,一般用来指定资源地址,第二个是options对象,指定配置参数,如method,headers, cache等等。fetch的返回值是一个promise对象,在promise resolve的时候,会将Response对象传入resolve函数中。 参考链接

_ lodash throttle vs debounce

// 二者参数是相同的,但作用不同,throttle保证在func函数在Xms内执行一次,
// 比如连续滚动窗口,每隔200ms执行一次回调,就像水龙头滴水的场景,尽管水流一直在水龙头汇集(连续事件),但水滴(cb)还是一滴一滴的往下滴,这就是所谓的节流
// debounce则是把突然涌进的事件(键盘事件)归为一个
// options.leading     事件在 wait ms delay 之前触发
// options.trailing    事件在 wait ms delay 之后触发
_.throttle(func, [wait=0], [options])
_.debounce(func, [wait=0], [options])

常用正则表达式

'正整数' /^[1-9]\d$/ '负整数' /^-[1-9]\d$/ '整数' /^-?[1-9]\d$/ '浮点数' /^-?([1-9]\d.\d|0.\d[1-9]\d|0?.0+|0)$/ '非负浮点数' /[1-9]\d.\d|0.\d[1-9]\d|0?.0+|0$/ '中文字符' /[\u4e00-\u9fa5]/ '空白行' /\n\s\r/ 'HTML标签' /<(\S?)[^>]>.?</\1>|<.? />/ '匹配首尾空白字符' /^\s|\s$/ 'Email' /\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)/ '简单URL' /[a-zA-Z]+://[^\s]/ '15位或18位身份证' /\d{15}|\d{18}/ 'ip格式' /\d+.\d+.\d+.\d/ '验证是否含有^%&',;=?$\ "等字符' /[^%&',;=?$\x22]+/

target vs currentTarget

事件流,捕获阶段 - 处于目标阶段 - 冒泡阶段 target: 触发事件的dom元素,也就是事件处于目标阶段时所在的dom元素。 currentTarget: 绑定事件的dom元素,可以存在于事件的捕获阶段,目标阶段或者冒泡阶段。也就是指事件处理函数执行时,event所指向的对象。

<div id="one">
   <div id="two"></div>
</div>

// 点击two

one.addEventListener('click',function(e){
    console.log(e.target);  //two
    console.log(e.currentTarget);  //one
},false);

函数柯里化

function currying(fn) {
  var slice = Array.prototype.slice,
  __args = slice.call(arguments, 1);
  return function () {
      var __inargs = slice.call(arguments);
      return fn.apply(null, __args.concat(__inargs));
  };
}

getBoundingClientRect()

getBoundingClientRect用于获取某个元素相对于视窗的位置集合。集合中有top, right, bottom, left, width, height等属性。

top: 元素上边界到视窗顶部的距离 bottom: 元素下边界到视窗顶部的距离 left:元素左边界到视窗左侧的距离 right:元素右边界。。。

embed 元素

embed是HTML5中定义的新规范, 标签定义嵌入的媒体,且embed标签必须指定src属性,格式可以是 Midi、Wav、AIFF、AU、MP3等等,embed元素有很多属性,用来定义embed标签的格式,比如:src,loop,hidden,volume,width,height,controls,name等等

<embed src="your.mid" autostart=true loop=false>

SVN版本管理系统

svn是subversion的简称,是一个开放源代码的版本管理系统。 SVN checkout(迁出代码版本库):在checkout时需要指定svn仓库的url地址,同时指定Revision(及仓库版本号),默认为HEAD最新版本,最后需要输入svn服务器的用户名和密码,这样才能checkout到本地指定目录。 SVN update:从服务器上获取最新文件的过程,类似git的pull操作。 SVN commit:提交操作,更新本地修改到服务器,需要填写本次更新的日志(log message) SVN add/delete:添加、删除文件,执行commit操作后将同步到服务器。 SVN revert:未同步至远端时,放弃本地更改,回到修改前的状态(慎用)

SVN VS Git

Dom元素的children属性 VS childNodes

前者忽略子节点中的空白文本节点(text nodeType: 3)

nodeType

nodeType属性表示节点的类型,如: 1 | ELEMENT_NODE 2 | ATTRIBUTE_NODE 3 | TEXT_NODE 8 | COMMENT_NODE 9 | DOCUMENT_NODE

oAuth2.0

oAuth 第三方登录遵循的协议,一般流程如下:

  1. 第三方应用请求用户授权。
  2. 用户同意授权,并返回一个凭证(code)
  3. 第三方应用通过第二步的凭证(code)向授权服务器请求授权
  4. 授权服务器验证凭证(code)通过后,同意授权,并返回一个资源访问的凭证(Access Token)。
  5. 第三方应用通过第四步的凭证(Access Token)向资源服务器请求相关资源。
  6. 资源服务器验证凭证(Access Token)通过后,将第三方应用请求的资源返回。

解释下闭包

闭包可以理解为函数中定义的函数,由于存在作用域链,内层函数可以访问外层函数的变量,那么内层函数就可以实时的对那个变量进行操作,而如果把这个内层函数当作返回值的话,那么外层函数的外部就可以突破作用域限制访问那个变量。 通俗的解释就是 function return function,就是由于作用域链的原因,内层函数可以访问外层函数中的变量,那个内层函数就是我们通常所说的闭包,它构成了函数内部和外部的桥梁。 闭包有以下两个作用:

  1. 访问函数内部的变量
  2. 可以使这些变量始终保存在内存中

闭包会使函数中的变量保存在内存当中,内存的消耗很大,所以我们不要滥用闭包,在适当的时候才去用

二分搜索算法

function binarySearch(arr, target) {
  if (Object.prototype.toString.call(arr) != "[object Array]") {
    return;
  }
  // arr前提要是从小到大排列的数组
  arr = quickSort(arr);
  var bottom = 0;
  var top = arr.length - 1;
  var position;

  while (bottom < top) {
    var middle = Math.floor((bottom + top) / 2);
    if (arr[middle] == target) {
      position = middle;
      console.log("Find target at position: " + position);
      return position;
    } else if (arr[middle] < target) {
      bottom = middle + 1;
    } else if (arr[middle] > target) {
      top = middle;
    }
  }

  return position;
}

如何判断一个Object是否为空

// jQuery isEmptyObject()
function isEmptyObject(e) {  
    var t;  
    for (t in e)  
        return !1;  
    return !0;
}  

雅虎14条性能优化规则总结:

优化方向 优化手段
请求数量 合并脚本和样式表,CSS Sprites,拆分初始化负载,划分主域,字体图标,雪碧图片等
请求带宽 开启服务器GZip,精简JavaScript,移除重复脚本,图像优化(包括图片大小kb)
缓存利用 使用CDN,使用外部JavaScript和CSS,添加Expires头,减少DNS查找,配置ETag,使AjaX可缓存
页面结构 将样式表放在顶部,将脚本放在底部,尽早刷新文档的输出
代码校验 避免CSS表达式,避免重定向

前端静态资源部署方案

知乎:张云龙的回答

如何设置CSS3(@media)横竖屏样式

    //竖屏时使用的样式
    @media all and (orientation:portrait) {
        code here ...
    }

    //横屏时使用的样式
    @media all and (orientation:landscape) {
            code here ...
    }

async vs defer

没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。

有 async,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。

有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。

gzip压缩

HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。这一般是指WWW服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来.一般对纯文本内容可压缩到原大小的40%.这样传输就快了,效果就是你点击网址后会很快的显示出来.当然这也会增加服务器的负载. 一般服务器中都安装有这个功能模块的。

什么是webp

WebP格式,谷歌(google)开发的一种旨在加快图片加载速度的图片格式。图片压缩体积大约只有JPEG的2/3,并能节省大量的服务器带宽资源和数据空间。

但WebP是一种有损压缩。相较编码JPEG文件,编码同样质量的WebP文件需要占用更多的计算资源。

css如何显示超出文字省略号

.text-wrap {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

css设置为inline-block的元素之间会出现间隙

产生原因:代码中dom元素间的换行符,空格等 解决方法:

  1. 不进行换行,即不同元素之间写成一行,再标签符内进行换行则不会产生空白符,但是不建议这种方法,代码可读性差
  2. 使用margin-left的负值,但是该方法适用情况不同,不同字体大小需要调整不同的值
  3. 不写闭合标签,这种方法兼容不太好,再HTML当中是可以这么写的,但是对于XHTML是不行的,而且适用的元素也不多
  4. 父级元素的letter-spacing设置为负值,当前元素letter-spacing设置为默认值即可
  5. 父级元素的word-spacing设置为负值,同上
  6. -webkit-white-space-collapse:discard; 兼容性不好

box-sizing的作用:

box-sizing 属性允许您以特定的方式定义匹配某个区域的特定元素,其默认值是 content-box,当使用border-box的时候,css属性设置 width的时候,会将padding 还有 border都算作 width的操作范围内

outline vs border

轮廓通常都能应用在指示焦点所在地方,比如input输入的时候,显示outline,便是当前正在这里操作。

  1. 轮廓不像边框那样参与到文档流中,即不会占据任何空间,不会影响到文档流的重新布局
  2. 默认情况下,轮廓的出现是在边框外面,但显示在 margin 之上
  3. 整个轮廓的宽度只能是一样的,但是 border 可以上下左右都不同的width

css cursor的用法:

cursor: url | auto | pointer | crosshair | help | text | wait | default | ... 常用的一些就是 pointer | text | help | crosshair(交叉十字) 如果要自定义图标,url 必须跟有一个逗号和某个通用关键字 a { cursor: url(global.cur), pointer; }

如何实现H5拖放

主要有4个步骤

设置元素可拖放 draggable = "true" 拖动什么 ondragstart + dataTransfer.setData("Text", ev.target.id) 放到何处 ondragover 规定在何处放置被拖动的数据,因为默认地是无法将数据/元素防止到其他元素中 进行放置 ondrop + dataTransfer.getData("Text") + appendChild

<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
    function allowDrop(ev) {
        ev.preventDefault();
    }
    function drag(ev) {
        ev.dataTransfer.setData("Text",ev.target.id);
    }
    function drop(ev) {
        ev.preventDefault();
        var data=ev.dataTransfer.getData("Text");
        ev.target.appendChild(document.getElementById(data));
    }
</script>
</head>
<body>
<div id="div1" ondrop="drop(event)"
ondragover="allowDrop(event)"></div>
<img id="drag1" src="img_logo.gif" draggable="true"
ondragstart="drag(event)" width="336" height="69" />
</body>
</html>

HTML5 为什么只需要写 !DOCTYPE HTML?

HTML5 不基于 SGML,因此不需要对DTD进行引用,但是需要doctype来规范浏览器的行为(让浏览器按照它们应该的方式来运行);而HTML4.01基于SGML,所以需要对DTD进行引用,才能告知浏览器文档所使用的文档类型。

window.onload 事件和 jQuery ready 函数有何不同?

jQuery ready() 函数只需对 DOM 树的等待,而无需对图像或外部资源加载的等待,从而执行起来更快。

渐进增强和优雅降级

渐进增强 :针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。

优雅降级 :一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。

SQL 注入

SQL Injection:就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。

SQL注入产生的原因,和栈溢出、XSS等很多其他的攻击方法类似,就是未经检查或者未经充分检查的用户输入数据,意外变成了代码被执行。针对于SQL注入,则是用户提交的数据,被数据库系统编译而产生了开发者预期之外的动作。也就是,SQL注入是用户输入的数据,在拼接SQL语句的过程中,超越了数据本身,成为了SQL语句查询逻辑的一部分,然后这样被拼接出来的SQL语句被数据库执行,产生了开发者预期之外的动作。所以从根本上防止上述类型攻击的手段,还是避免数据变成代码被执行,时刻分清代码和数据的界限。而具体到SQL注入来说,被执行的恶意代码是通过数据库的SQL解释引擎编译得到的,所以只要避免用户输入的数据被数据库系统编译就可以了。

浏览器缓存有哪些,通常缓存有哪几种方式

强缓存 强缓存如果命中,浏览器直接从自己的缓存中读取资源,不会发请求到服务器。

协商缓存 当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,通过服务器端依据资源的另外一些http header验证这个资源是否命中协商缓存,如果协商缓存命中,服务器会将这个请求返回(304),若未命中请求,则将资源返回客户端,并更新本地缓存数据(200)。

HTTP头信息控制缓存

Expires(强缓存)+过期时间 Expires是HTTP1.0提出的一个表示资源过期时间的header,它描述的是一个绝对时间

Cache-control(强缓存) 描述的是一个相对时间,在进行缓存命中的时候,都是利用客户端时间进行判断 管理更有效,安全一些 Cache-Control: max-age=3600

Last-Modified/If-Modified-Since(协商缓存) 标示这个响应资源的最后修改时间。Last-Modified是服务器相应给客户端的,If-Modified-Sinces是客户端发给服务器,服务器判断这个缓存时间是否是最新的,是的话拿缓存。

Etag/If-None-Match(协商缓存) etag和last-modified类似,他是发送一个字符串来标识版本。

堆栈的定义

stack为自动分配的内存空间,它由系统自动释放;而heap则是动态分配的内存,大小不定也不会自动释放。

基本数据类型存放在栈中

引用类型 存放在堆内存中,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据

H5中audio、vedio load()方法的作用

load() 方法重新加载音频/视频元素。 load() 方法用于在更改来源或其他设置后对音频/视频元素进行更新。

var vedioEle = document.getElementById('myvedio');
var sourceEle = document.getElementById('mysource');
sourceEle.src = 'https://www.w3cschool.cn/statics/demosource/movie.mp4';
vedioEle.load();
vedioEle.play();

如何封装coockie操作

var cookie = {

    //根据key值获取对应的cookie
    get:function(key){

        //获取cookie
        var data = document.cookie;
        //获取key第一次出现的位置    pwd=
        var startIndex = data.indexOf(key+'=');
        //  name=123;pwd=abc
        //如果开始索引值大于0表示有cookie
        if(startIndex>-1) {

            //key的起始位置等于出现的位置加key的长度+1
            startIndex = startIndex+key.length+1;

            //结束位置等于从key开始的位置之后第一次;号所出现的位置

            var endIndex = data.indexOf(';',startIndex);

            //如果未找到结尾位置则结尾位置等于cookie长度,之后的内容全部获取
            endIndex = endIndex<0 ? data.length:endIndex;

            return decodeURIComponent(data.substring(startIndex,endIndex));

        }else {

            return '';
        }

    },

    set:function(key,value,time){
        //默认保存时间
        var time = time;
        //获取当前时间
        var cur = new Date();

        var undefined;

        //设置指定时间
        cur.setTime(cur.getTime()+time*24*3600*1000);

        //创建cookie  并且设置生存周期为GMT时间
        document.cookie = key+'='+encodeURIComponent(value)+';expires='+(time===undefined?'':cur.toGMTString());

    },

    del:function(key){

        //获取cookie
        var data = this.get(key);

        //如果获取到cookie则重新设置cookie的生存周期为过去时间
        if(data!==false){

            this.set(key,data,-1);

        }

    }

};