john's tech blog

hope is coming


  • 首页

  • 标签

  • 归档

http_etag

发表于 2017-03-11 | 更新于 2019-05-07

http etag

Etag[1] 是URL的Entity Tag,用于标示URL对象是否改变,区分不同语言和Session等等。具体内部含义是使服务器控制的,就像Cookie那样。
HTTP协议规格说明定义ETag为“被请求变量的实体值”。另一种说法是,ETag是一个可以与Web资源关联的记号(token)。典型的Web资源可以一个Web页,但也可能是JSON或XML文档。服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:ETag:”50b1c1d4f775c61:df3”客户端的查询更新格式是这样的:If-None-Match : W / “50b1c1d4f775c61:df3”如果ETag没改变,则返回状态304然后不返回,这也和Last-Modified一样。测试Etag主要在断点下载时比较有用。

scalatra cache

先看源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
trait CacheSupport { self: ScalatraBase =>
implicit val cacheBackend: Cache //在具体的servlet中实现
implicit val keyStrategy: KeyStrategy = DefaultKeyStrategy
implicit val headerStrategy: HeaderStrategy = DefaultHeaderStrategy //重写了HeaderStrategy的方法

def cache[A](key: String, ttl: Option[Duration])(value: => A): A = {
cacheBackend.get[A](key) match {
case Some(v) => v
case None => cacheBackend.put(key, value, ttl)
}
}

//用于网页设置etag用
def cached[A](ttl: Option[Duration])(result: => A)(implicit keyStrategy: KeyStrategy,
headerStrategy: HeaderStrategy,
request: HttpServletRequest,
response: HttpServletResponse): A = {

val key = keyStrategy.key

cacheBackend.get[(String, A)](key) match {
case Some(v) =>
if (headerStrategy.isUnchanged(v._1)) halt(304)
else {
headerStrategy.setRevision(v._1) // response.setHeader("ETag", revision)
v._2
}
case None =>
val res = result //网页内容
val rev = headerStrategy.getNewRevision() //默认获取当前时间
cacheBackend.put(key, (rev, res), ttl)
headerStrategy.setRevision(rev)
res
}
}
}

HTTP Header中的ETag

angular_源码分析之初始化

发表于 2017-02-24 | 更新于 2019-05-07

angular初始化过程

页面加载完成调用angularInit

跟jquery中的ready函数很像吧

1
2
3
jqLite(window.document).ready(function() {
angularInit(window.document, bootstrap);
});

jqLite.ready函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var JQLitePrototype = JQLite.prototype = {
ready: function(fn) {
var fired = false;
//trigger方法中再调用参数fn
function trigger() {
if (fired) return;
fired = true;
fn();//调用fn参数 函数
}

// check if document is already loaded
if (window.document.readyState === 'complete') {
window.setTimeout(trigger);
} else {
this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
// we can not use jqLite since we are not done loading and jQuery could be loaded later.
// jshint -W064
JQLite(window).on('load', trigger); // fallback to window.onload for others
// jshint +W064
}
}
}

angularInit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//element元素 ,从上文看就是window.document
//bootstrap函数
function angularInit(element, bootstrap) {
var appElement,
module,
config = {};

//element比其他元素优先级高
// The element `element` has priority over any other element.
forEach(ngAttrPrefixes, function(prefix) {
var name = prefix + 'app';

if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
appElement = element;
module = element.getAttribute(name);
}
});
forEach(ngAttrPrefixes, function(prefix) {
var name = prefix + 'app';
var candidate;
//从element元素中用querySelector方法查找带ng-app的元素
if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
appElement = candidate;
module = candidate.getAttribute(name);
}
});
//ng-app标记的元素,
//模块比如ng-app="oaApp",那么module就等于oaApp
if (appElement) {
config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
//启动
bootstrap(appElement, module ? [module] : [], config);
}
}

bootstrap

最终调用的还是bootstrap函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
function bootstrap(element, modules, config) {
if (!isObject(config)) config = {};
var defaultConfig = {
strictDi: false
};
//扩展默认配置
config = extend(defaultConfig, config);
var doBootstrap = function() {
element = jqLite(element);//用jqLite包装下

//判断injector方法调用的结果
//一开始调用element.injector()时是返回false的,当调用到
//element.data('$injector', injector);就为true了
if (element.injector()) {
var tag = (element[0] === window.document) ? 'document' : startingTag(element);
// Encode angle brackets to prevent input from being sanitized to empty string #8683.
throw ngMinErr(
'btstrpd',
"App already bootstrapped with this element '{0}'",
tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
}

modules = modules || [];
//unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
modules.unshift(['$provide', function($provide) {
$provide.value('$rootElement', element);
}]);

if (config.debugInfoEnabled) {
// Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
modules.push(['$compileProvider', function($compileProvider) {
$compileProvider.debugInfoEnabled(true);
}]);
}
//ng模块放入第一位
modules.unshift('ng');
var injector = createInjector(modules, config.strictDi);
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
function bootstrapApply(scope, element, compile, injector) {
scope.$apply(function() {
element.data('$injector', injector);
compile(element)(scope);
});
}]
);
return injector;
};

var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;

if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
config.debugInfoEnabled = true;
window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
}

if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
return doBootstrap();
}

window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
angular.resumeBootstrap = function(extraModules) {
forEach(extraModules, function(module) {
modules.push(module);
});
return doBootstrap();
};

if (isFunction(angular.resumeDeferredBootstrap)) {
angular.resumeDeferredBootstrap();
}
}

element.injector在哪边定义的呢?

通过forEach遍历函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//line 3347
forEach({
data: jqLiteData,
inheritedData: jqLiteInheritedData,

scope: function(element) {
// Can't use jqLiteData here directly so we stay compatible with jQuery!
return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
},

isolateScope: function(element) {
// Can't use jqLiteData here directly so we stay compatible with jQuery!
return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
},

controller: jqLiteController,

injector: function(element) {
return jqLiteInheritedData(element, '$injector');
}}, function(fn, name) {
//fn相当于value name相当于key
//jQLite.prototype['injector'] =
JQLite.prototype[name] = function(arg1, arg2) {
var i, key;
var nodeCount = this.length;

//jqLiteHasClass是个只读函数,所以需要特殊处理
// jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
// in a way that survives minification.
//缩小范围
// jqLiteEmpty takes no arguments but is a setter.
//jqLiteEmpty 无参函数 但是 是一个setter方法

if (fn !== jqLiteEmpty &&
(isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
if (isObject(arg1)) {

// we are a write, but the object properties are the key/values
for (i = 0; i < nodeCount; i++) {
if (fn === jqLiteData) {
// data() takes the whole object in jQuery
//this[i] 是通过jqLiteAddNodes函数往jqLite对象中加入索引
fn(this[i], arg1);
} else {
for (key in arg1) {
fn(this[i], key, arg1[key]);
}
}
}
// return self for chaining
return this;
} else {
//element.injector() 走到这里
// we are a read, so read the first child.
// TODO: do we still need this?
var value = fn.$dv;
// Only if we have $dv do we iterate over all, otherwise it is just the first element.
var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
for (var j = 0; j < jj; j++) {
var nodeValue = fn(this[j], arg1, arg2);
value = value ? value + nodeValue : nodeValue;
}
return value;
}
} else {
// we are a write, so apply to all children
for (i = 0; i < nodeCount; i++) {
fn(this[i], arg1, arg2);
}
// return self for chaining
return this;
}
};
});

jqLiteInheritedData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function jqLiteInheritedData(element, name, value) {
// if element is the document object work with the html element instead
// this makes $(document).scope() possible
if (element.nodeType == NODE_TYPE_DOCUMENT) {
element = element.documentElement;
}
var names = isArray(name) ? name : [name];

while (element) {
for (var i = 0, ii = names.length; i < ii; i++) {
if (isDefined(value = jqLite.data(element, names[i]))) return value;
}

// If dealing with a document fragment node with a host element, and no parent, use the host
// element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
// to lookup parent controllers.
element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
}
}

jqLite.data(jqLiteData):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  function jqLiteData(element, key, value) {
if (jqLiteAcceptsData(element)) {

var isSimpleSetter = isDefined(value);
//element.injector() 就是 获取$injector
//那么就是isSimpleGetter
var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
var massGetter = !key;

//element.injector()
//!isSimpleGetter传false
var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
var data = expandoStore && expandoStore.data;

if (isSimpleSetter) { // data('key', value)
data[key] = value;
} else {
if (massGetter) { // data()
return data;
} else {
if (isSimpleGetter) { // data('key')
// don't force creation of expandoStore if it doesn't exist yet
return data && data[key];
} else { // mass-setter: data({key1: val1, key2: val2})
extend(data, key);
}
}
}
}
}

jqLiteExpandoStore函数

1
2
3
4
5
6
7
8
9
10
11
function jqLiteExpandoStore(element, createIfNecessary) {
var expandoId = element.ng339,
expandoStore = expandoId && jqCache[expandoId];

if (createIfNecessary && !expandoStore) {
element.ng339 = expandoId = jqNextId();
expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
}

return expandoStore;
}

jqCache对象

var jqCache = JQLite.cache = {};

angular源码剖析之模块加载器

发表于 2017-02-22 | 更新于 2019-07-16

angular1.5.8源码分析

setupModuleLoader方法

模仿setupModuleLoader方法看看如何angular是如何构造angular.module的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function setupModuleLoader(window) {

//函数内部声明函数
function ensure(obj, name, factory) {
return obj[name] || (obj[name] = factory());
}

//定义全局angular变量
var angular = ensure(window, 'angular', Object);

//返回angular.module变量
return ensure(angular, 'module', function() {

var modules = {};

return function module(name, requires, configFn) {

return ensure(modules, name, function() {

var moduleInstance = {
hello : 'hello angular'
};

return moduleInstance;
});
};

})
}

var window = {};

angularModule = setupModuleLoader(window);

angularModule('ng', ['ngLocale'], ['$provide', function ngModule($provide) {}]);

//已经存在ng这个module
console.log(window.angular.module('ng').hello)

控制台输出hello angular

angular1.5.8 源码分析之依赖注入--模块创建

发表于 2017-02-21 | 更新于 2020-03-21

模块创建主要是用到了setupModuleLoader函数.

setupModuleLoader函数

angular的模块是在依赖注入时必不可少的一个功能单元,所以要弄懂它的依赖注入,那必须讲它的模块设计。
总的来说通过angular.module的实现源码我们可以看到js闭包的强大功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/*
angular 模块加载器
*/

function setupModuleLoader(window) {

//省略部分代码
function ensure(obj, name, factory) {
//比如下方的ensure(angular,'module',function(){})
return obj[name] || (obj[name] = factory());
}
var angular = ensure(window, 'angular', Object);
//此处定义了angular.module方法
return ensure(angular, 'module', function() {
/** @type {Object.<string, angular.Module>} */
var modules = {};

/* 注释掉了 函数的描述*/
//调用angular.module() 时执行的函数
//requires 必须传递 ,比如[]数组
return function module(name, requires, configFn) {
var assertNotHasOwnProperty = function(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
}
};
//判断moduleName 是不是 hasOwnProperty
assertNotHasOwnProperty(name, 'module');
if (requires && modules.hasOwnProperty(name)) {
modules[name] = null;
}
return ensure(modules, name, function() {
if (!requires) {
throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
"the module name or forgot to load it. If registering a module ensure that you " +
"specify the dependencies as the second argument.", name);
}

/** @type {!Array.<Array.<*>>} */
//数组里也是数组
//调用队列
var invokeQueue = [];
//数组里是函数
//配置块
var configBlocks = [];
//数组里是函数
//运行块
var runBlocks = [];

/*
返回一个方法
方法内部调用configBlocks['push'](['$injector','invoke',arguments])
然后返回moduleInstance
config就是一个返回moduleInstance的函数
*/

//调用config方法会把参数放入configBlocks中
var config = invokeLater('$injector', 'invoke', 'push', configBlocks);

var moduleInstance = {
// Private state
//私有变量
_invokeQueue: invokeQueue,
_configBlocks: configBlocks,
_runBlocks: runBlocks,
//传入的依赖
requires: requires,

name: name,
//返回调用$provide的provider方法
provider: invokeLaterAndSetModuleName('$provide', 'provider'),
//返回调用$provide的factory方法
factory: invokeLaterAndSetModuleName('$provide', 'factory'),
//返回调用$provide的service方法
service: invokeLaterAndSetModuleName('$provide', 'service'),
//返回调用$provide的value方法
value: invokeLater('$provide', 'value'),
//返回调用$provide的constant方法
constant: invokeLater('$provide', 'constant', 'unshift'),
//返回调用$provide的decorator方法
decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
//返回调用$animateProvider的register方法
animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
//返回调用$filterProvider的register方法
filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
//返回调用$controllProvider的register方法
controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
//返回调用$compileProvider的directive方法
directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
//返回调用$compileProvider的component方法
component: invokeLaterAndSetModuleName('$compileProvider', 'component'),

config: config,

//调用run方法会把参数block加入runBlocks队列。
run: function(block) {
runBlocks.push(block);
return this;
}
};

//configFn有可能就是['$provide',function($provide){}]
//config 就是调用configBlocks[insertMethod || 'push']([provider, method, arguments]);
//这里的arguments就是configFn
if (configFn) {
//如果有configFn方法,那么就放入configBlocks队列
config(configFn);
}

return moduleInstance;
});
};
});

}

invokeLater

1
2
3
4
5
6
7
8
9
10
11
12
13

//延迟调用
//module.value 就是先调用的invokeLater返回的一个函数
//然后调用module.value()的时候就是把provider,method arguments放入 queue
function invokeLater(provider, method, insertMethod, queue) {
if (!queue) queue = invokeQueue;
return function() {

//就是调的数组的[].push方法
queue[insertMethod || 'push']([provider, method, arguments]);
return moduleInstance;
};
}

invokeLaterAndSetModuleName

从名字看来 很像懒加载模式, 返回函数用来稍后调用
把provider放入invokeQueue队列中

调用的时候如果factoryFunction是函数 那么就设置它的moduleName为当前模块名称

1
2
3
4
5
6
7
8
9
//比如controller是'$controllerProvider', 'register'
function invokeLaterAndSetModuleName(provider, method) {
//调用controller 后
return function(recipeName, factoryFunction) {
if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
invokeQueue.push([provider, method, arguments]);
return moduleInstance;
};
}

当调用var myModule = angular.module('myModule', []);的时候 就返回moduleInstance了。

angularjs1.5.8源码分析之foreach函数

发表于 2017-02-21 | 更新于 2020-03-27

foreach函数分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/*
* @param {Object|Array} obj Object to iterate over.
//obj是需要遍历的数组或者对象
* @param {Function} iterator Iterator function.
iterator是迭代器函数
* context是执行迭代器函数中的上下文对象
* @param {Object=} context Object to become context (`this`) for the iterator function.
* @returns {Object|Array} Reference to `obj`.
*返回obj对象的引用
*/

function forEach(obj, iterator, context) {
var key, length;
//先判断obj是否存在
if (obj) {
//如果obj是函数
if (isFunction(obj)) {

for (key in obj) {
//检查hasOwnProperty是否存在,ie8上querySelectorAll的结果是不带
//hasOwnProperty函数的对象
// Need to check if hasOwnProperty exists,
// as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
//先传value再传key
iterator.call(context, obj[key], key, obj); //调用iterator 函数
}
}
} else if (isArray(obj) || isArrayLike(obj)) {
//如果obj是数组或类数组
var isPrimitive = typeof obj !== 'object';//是否是原生的数组
for (key = 0, length = obj.length; key < length; key++) {
if (isPrimitive || key in obj) {
//执行迭代器
iterator.call(context, obj[key], key, obj);
}
}
} else if (obj.forEach && obj.forEach !== forEach) {
//执行对象本身的foreach函数
obj.forEach(iterator, context, obj);
} else if (isBlankObject(obj)) {
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
for (key in obj) {
iterator.call(context, obj[key], key, obj);
}
} else if (typeof obj.hasOwnProperty === 'function') {

//存在hasOwnProperty方法
// Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
//继承自Object.prototype的对象
for (key in obj) {
if (obj.hasOwnProperty(key)) {
iterator.call(context, obj[key], key, obj);
}
}
} else {
// Slow path for objects which do not have a method `hasOwnProperty`
for (key in obj) {
if (hasOwnProperty.call(obj, key)) {//调用hasOwnProperty方法成功后
iterator.call(context, obj[key], key, obj);
}
}
}
}
return obj;
}
//自定义的hasOwnProperty是Object原型里的函数
var hasOwnProperty = Object.prototype.hasOwnProperty;
//根据对象的key来做排序
function forEachSorted(obj, iterator, context) {
var keys = Object.keys(obj).sort();
for (var i = 0; i < keys.length; i++) {
iterator.call(context, obj[keys[i]], keys[i]);
}
return keys;
}

我们可以看见里面有很多自定义的函数,比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
之后我们会逐个分析。

在```publishExternalAPI```方法中通过extend方法定义,然后就可以通过angular.foreach调用了。

### foreach函数用法

```js
var values = {name: 'misko', gender: 'male'};
var log = [];
//value在前,key在后
angular.forEach(values, function(value, key) {
this.push(key + ': ' + value);
}, log);
expect(log).toEqual(['name: misko', 'gender: male']);

angularjs内部很多地方用到了它自定义的foreach函数,通过研究它的实现我们就可以在平时的开发中运用它foreach的设计
来定义一个适合自己的遍历函数了。

1…303132…47

John

232 日志
43 标签
GitHub Twitter
欢迎关注我的公众号:沉迷Spring
© 2023 johnwonder
由 Hexo 强力驱动 v3.2.0
|
主题 – NexT.Pisces v7.1.1
|
0%