Git Product home page Git Product logo

hefei00.github.io's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

hefei00.github.io's Issues

js原型继承

js的原型继承

原型链

在javascript中,长除了nullundefined外,一切都是对象。每个对象都有一个属性链接到另一个对象,称为它的原型,该原型对象
也有自己的原型,直到遇到一个以null为原型的对象。当试图访问一个对象上的属性时,它不仅在该对象上搜寻,还会搜寻该对象的原型,
以及该对象的原型的原型,以此层层向上搜索,直至找到该属性或者到达原型链的末尾。

fds

注:

  1. 构成原型链的时_proto_属性,而不是prototype
  2. 只有函数对象有prototype属性,普通对象没有

几种继承的实现

原型链

改变子类的prototype,使其指向父类的一个实例

function Super() {
	this.name = 'super';
	this.arr = [1,2,3,4];
}
Super.prototype.sayName = function(){
	console.log(this.name);
}
function Sub() {
	this.age = 12;
}
Sub.prototype = new Super();//Sub继承Super

var test = new Sub();
console.log(test.age);//=> 12
console.log(test.name);//=> super

原型链:test -> Super的一个实例 -> Super.prototype

缺点:

  • 父类中包含引用类型的变量会被所有的实例共享,比如Super里的arr
  • 不能使用字面量重写原型Sub.prototype = {//一些方法},这样会导致原型链关系中断
  • 子类不能传递参数给父类

借用构造函数

子类调用父类的构造函数

function Super() {
	this.name = 'super';
	this.arr = [1,2,3,4];
}
Super.prototype.sayName = function(){
	console.log(this.name);
}
function Sub() {
	Super.call(this);//调用父类构造函数
	this.age = 12;
}

var test = new Sub();
console.log(test.age);//=> 12
console.log(test.name);//=> super

原型链: test -> Sub.prototype

缺点: 每个实例都会生成一遍自己的方法,无法复用

组合继承

子类调用父类的构造函数继承属性,再通过改变prototype继承方法

function Super() {
	this.name = 'super';
	this.arr = [1,2,3,4];
}
Super.prototype.sayName = function(){
	console.log(this.name);
}
function Sub() {
	Super.call(this);//调用父类构造函数,继承属性,第二次调用Super
}
Sub.prototype = new Super();//继承方法, 第一次调用Super
var test = new Sub();
console.log(test.age);//=> 12
console.log(test.name);//=> super

原型链:test -> Super的一个实例 -> Super.prototype

缺点:组合式继承可以满足大部分需求,但是Super函数调用了两次,不适合一些对性能要求比较高的场景

原型继承

创建一个让子类和父类原型链相连的方法

//一个构造函数
function create(o){
	function F(){}
	F.prototype = o;
	return new F();
}
//父类
function Super() {
	this.name = 'super';
	this.arr = [1,2,3,4];
}
var test1 = create(Super);
test1.name = 'test1'
test1.arr.push('5');
var test2 = create(Super);
test2.name = 'test2';
test2.arr.push('6');
console.log(Super.arr);//=> [1,2,3,4,5,6]//被子类行为改变

注: es5中实现了一个类似的函数:Object.create

原型链: test1 -> Super
缺点: 原型中引用类型的变量会被所有实例共享

寄生式继承

创建一个用于封装继承过程的函数,在该函数内部增强对象

//一个构造函数
function create(o){
	function F(){}
	F.prototype = o;
	return new F();
}
//父类
function Super() {
	this.name = 'super';
	this.arr = [1,2,3,4];
}
function createObject(origin){
	var clone = create(origin);
	clone.sayName = function(){
		console.log(this.name);
	}
	return clone;
}
var test = createObject(Super);
test.sayName();// =>'super'
test.arr.push(5);
console.log(Super.arr);//=>[1,2,3,4,5]

原型链: test -> Super
缺点: 原型中引用类型的变量会被所有实例共享

组合寄生式继承

调用父类构造函数继承属性,再通过构造一个封装继承的函数,避免父类函数的二次调用

//一个构造函数
function create(o){
	function F(){}
	F.prototype = o;
	return new F();
}
function inheritPrototype(sub, super) {
	var prototype = create(super.prototype);    //创建对象
	prototype.constructor = sub;                //增强对象
	sub.prototype = prototype;                  //指定对象
}
//父类
function Super() {
	this.name = 'super';
	this.arr = [1,2,3,4];
}
Super.prototype.sayName = function(){
	console.log(this.name);
}
function Sub() {
	Super.call(this);//调用父类构造函数
}
Sub.prototype.subSayName = function(){
	console.log('sub' + this.name);
}
var test = new Sub();

组合寄生式继承是相对来说比较完美的方案

原型链: test -> prototype -> Super.prototype

CSS Module笔记

css module

一个CSS Module就是一个所有的class名和动画名被限定在本地“作用域”的csscss文件。

CSS Module内部通过ICSS来解决样式导入和导出的问题。分别对应:import:export两个新增的伪类。

当在一个JS Module里导入一个CSS Module时,它会导出一个对象,这个对象里包含了所有的本地类名和全局类名。

/* style.css */
.green {
  color: green;
}
import styles from "./style.css";
// import { style } from "./style.css";

element.innerHTML = '<div class="' + styles.green + '">';

使用

局部作用域

CSS Module默认时局部作用域,按照上面的例子使用即可,局部作用域的样式只对引入了该样式的文件生效,对其他文件不生效。需要注意的是:由于局部calss名会被编译成一个hash字符串,所以使用时要使用style.green,不能直接使用green.

全局作用域

使用:global(.className)的语法可以产生一个全局规则,这样的类名不会被便衣成hash字符串。

/* style.css */
:global(.green) {
  color: green;
}

使用时:使用正常的class写法,就会引入全局class

import styles from "./style.css";
// import { style } from "./style.css";
element.innerHTML = '<div class="' + green + '">';

组合

CSS Module中可以很方便的组合选择器。

.className {
  color: green;
  background: red;
}
//简单组合
.otherClassName {
  composes: className;
  color: yellow;
}
//多重组合
.otherClassName {
  composes: classNameA classNameB;
  color: yellow;
}
//引入依赖
.otherClassName {
  composes: className from "./style.css";
}

react-router学习笔记

react-router

reacr-router是一个react的一个声明式的路由解决方案。在学习react-router的过程中,官方的教程非常丰富。一边做下来,基本可以对react-router有个初步印象。这边文章是我在学习react-router时的一些纪录。

简单示例

...
<Router>
    <div>
      <ul>
        <li><Link to="/">Home</Link></li>//导航至Home
        <li><Link to="/about">About</Link></li>
        <li><Link to="/topics">Topics</Link></li>
      </ul>

      <hr/>

      <Route exact path="/" component={Home}/>
      <Route path="/about" component={About}/>
      <Route path="/topics" component={Topics}/>
    </div>
  </Router>
...

上面的代码中,出现了三个和react-router有关的自定义组件:Router,Link,Route。它们本身就是react组件。上面代码的意思是:定义了一组路由(使用Router包裹),点击Link,渲染对应组件。更丰富的示例

常见API

Router

<Router>一共有三种模式:

  1. <BrowserRouter>:使用HTML5提供的history API保持UI和url同步
  2. <HashRouter>:使用hash来保持UI和url同步,常用于不支持history API的浏览器
  3. <MemoryRouter>:在内存中保存url纪录,用于非浏览器环境,比如测试环境或者叫react-native环境
  4. <NativeRouter>:用于react-native
  5. <StaticRouter>:静态router,从不改变地址

在声明的时候常常这样写:

import {BrowserRouter as Router,} from 'react-router-dom'

router上可以设置一个basename,作为当前位置基准url,其子route都会带上这个基准url

<BrowserRouter basename="/calendar"/>
<Link to="/today"/> // 渲染为 <a href="/calendar/today">

Link

为您的应用提供一个声明式的导航

<Link to="/courses" replace />

//导航至'/courses',replace表明替换掉原地址,如果为false,则在记录里添加一个新地址

Redirect

使用<Redirect>会导航到一个新的地址。

<Redirect push to="/somewhere/else"/>//使用push,表明新地址会加入纪录,而不是替换掉就纪录

Route

当url匹配path时,渲染对应组件

<Router>
  <div>
    <Route exact path="/" component={Home}/>
    <Route path="/news" component={NewsFeed}/>
  </div>
</Router>
//当地址是'/'
<div>
  <Home/>
  <!-- react-empty: 2 -->
</div>
//当地址是'/news'
<div>
  <!-- react-empty: 1 -->
  <NewsFeed/>
</div>

route的渲染方法有三种,如果三个属性同时存在,则优先级顺序为:component,render,children

  1. :只有地址匹配的时候组件才被渲染
  2. :地址匹配是调用的函数,返回一个jsx组件
  3. :不管地址是否匹配都会渲染,其他和render相同

route的props

  • match: 包含了<Route path> 如何与URL匹配的信息
  • location: 当前位置信息
  • history:history对象
  • path:任何path-to-regexp能理解的有效URL,如果不声明path属性,匹配任何路径
  • exact:为真时,必需完全匹配才会渲染组件。比如‘/one/two’是否匹配‘/one’
  • strict:是否匹配结尾斜线。比如‘/one/’和‘/one’

参考

React Router 使用教程
react-touter doc

跨域资源共享(CROS)

跨域资源共享(CROS)

当web资源由其他域提供时,就会发起跨域http请求。处于安全考虑,浏览器会限制脚本中的跨域请求。使用XMLHttpRequestfetch发起的请求必须遵守
同源策略,跨域的请求可以发起,但是响应会被浏览器拦截。这样的限制虽然安全,但是却很不方便。为了提高web应用的可用性,跨域资源共享机制(CROS)允许web服务器进行跨域访问控制。目前所有的浏览器都支持CROS。

跨域应用场景

  1. XMLHttpRequestfetch发起的请求
  2. web字体
  3. WebGl贴图
  4. 使用 drawImage 将 Images/video 画面绘制到 canvas
  5. 使用CSSOM的样式表
  6. Scripts

跨域实现机制

跨域资源共享是通过一组新增的HTTP首部字段,让服务器声明哪些源站有权限访问哪些资源。规范要求,对那些可能对服务器产生副作用的请求,浏览器必须先使用
OPTIONS方法发起一个预检请求,询问服务器是否允许该跨域请求。服务器允许之后,才会发起实际的跨域请求。

简单请求

如果一个请求满足以下条件,则称之为“简单请求”,简单请求不会触发CROS的预检请求。

  • 使用下列方法之一
    • GET
    • HEAD
    • POST
  • 只包含下列首部字段
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (需要注意额外的限制)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type值属于下列之一
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

CROS涉及到的HTTP首部字段如下

响应字段

  • Access-Control-Allow-Origin: | * 该字段表明允许访问资源的外域url,或者允许所有外域请求。
  • Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Heade 该字段规定请求中允许可带哪些HTTP首部字段。
  • Access-Control-Max-Age: 该字段表明了预请求的响应时间,在此时间内,不用重发预请求。
  • Access-Control-Allow-Credentials: true 该字段表明服务器是否允许 credentials 标志设置为 true 的请求
  • Access-Control-Allow-Methods: POST, GET, OPTIONS 该字段表明请求所允许的HTP方法

请求字段

  • Access-Control-Request-Method: 用于预请求,正式请求将会用到的方法
  • Access-Control-Request-Headers: [, ]* 用于预请求,正式请求将会用到的请求头部

参考

MDN-CROS

javascript中的类型

javascript中的类型

动态类型

javascript是一种动态类型语言。在javascript中,变量没有类型,值有类型。变量可以指代任何类型。

var foo = 42 //Number
var bar = 'abc' //String

数据类型

在javascript中,一共有7种类型

  • 6种原始类型
    • Null
    • Undefined
    • Boolean
    • String
    • Number
    • Symbol
  • Object

原始类型的意味着值都是不可变的。例如对字符串的操作并不会改变原字符串,而是返回一个新的字符串。

包装类型

原始类型只能代表数据,没有可执行的方法。但是在js中,我们可以见到例如var str = 'abcde'.substr(0,3)这样的语句。
这是因为javascript引擎在执行这一句的时候,会自动为其包装上一个对应的String对象,执行完这个substr后,
再销毁这个对象。这一切都是自动完成的,就像原始类型也有方法一样。但其实相当于

var str
var temp = new String('abcde')
str = temp.substr(0,3)
destroy(temp)

这几种原始类型都有其对应的包装类型。

  • string ---> String
  • number ---> Number
  • boolean ---> Boolean
  • symbol ---> Symbol

内建对象

在js中,有一些对象是内置的。常见的内建对象列表如下

  • String()
  • Number()
  • Boolean()
  • Array()
  • Object()
  • Function()
  • Regexp()
  • Date()
  • Error()
  • Symbol()

类型判断

  1. typeof

使用typeof可以判断除了null以外的类型。

typeof 32  //number
var a = {}
typeof a //object
var b = [1,2,3]
typeof b   //object
typeof null //object. It's an bug in javascript
  1. Object.prototype.toString.call

使用Object.prototype.toString.call可以准确判定类型。

Object.prototype.toString.call(32)  //[object Number]
var a = {}
Object.prototype.toString.call(a) //[object Object]
var b = [1,2,3]
Object.prototype.toString.call(b)   //[object Array]
Object.prototype.toString.call(null) //[object Null]

类型转化

将一种类型的值转换为另外一种类型叫做类型转换。在javascript里,有两种类型转化。一种是显示的,还有一种是隐式的。

转换规则

转换为字符串

undefined    ---->    'undefined'
null         ---->    'null'
true,false   ---->    'true', 'false'
32           ---->    '32'
'abc'        ---->    'abc'
[1,2,3]      ---->    '1,2,3'
{a:1}        ---->    '[object Object]'

对象转换为字符串,规则如下:

  1. 调用对象的toString方法。如果返回原始类型的值,则对该值使用String函数,不再进行后续步骤
  2. 如果toString返回的是对象,则执行对象的valueOf方法,如果返回的是原始类型,则对该值使用String方法,不再进行后续步骤
  3. 如果valueOf返回的是对象,则报错

简单来说,对象会转换为[object Object]

转换为数值

undefined    ---->    NaN
null         ---->    0
true,false   ---->    1, 0
32           ---->    32
'abc'        ---->    NaN
‘’           ---->    0
'123abc'     ---->    NaN
[1,2,3]      ---->    NaN
{a:1}        ---->    NaN

对象转换为数值类型,规则如下:

  1. 调用对象的valueOf方法。如果返回原始类型的值,则对该值使用Number函数,不再进行后续步骤
  2. 如果valueOf返回的是对象,则执行对象的toString方法,如果返回的是原始类型,则对该值使用Number方法,不再进行后续步骤
  3. 如果toString返回的是对象,则报错

转换为布尔值

其他类型转换为布尔值,只有以下6种情况为false,其余都换转换为true

  • undefined
  • null
  • -0
  • +0
  • NaN
  • ''

显式转换

显式类型转换主要是值使用String(),Number(),Boolean()这三个构造函数来使其它类型的值转换为相应类型的值

var a = 42
String(a)//'42'
var b = '3.14'
Number(b)//3.14
Number('abc')//NaN NaN也是一种数字类型
var c = 'fa'
Boolean(c)//true

隐式转转

隐式转换是指在某些情况下一种类型会自动转换为另外一种类型

  1. 不同类型的数据互相运算
12 + 'ab'//'12ab'
12-'ab'//NaN
'5'-'2'//3

对于预期为字符串的地方,都会将非字符串转换为字符串。主要是二元+运算符。

  • 只要字符串出现在+左侧或者右侧,都会将另外一个非字符串操作数转换为字符串
'aaa' + 123//'aaa123'
123 + 'aaa'//'123aaa'
'aaa' + {a:2} //'aaa[object object]'
'aaa' + true 'aaatrue'
  • 如果有对象出现在一侧,则会执行对象的valueOf方法,如果结果是原始类型,则将其视为该原始类型参与运算,否则调用toString()方法,然后再参与运算
1 + []//'1'
5 * []//0

除了+两侧都出现字符串时会把操作符转换为字符串,其它情况下都会把两侧运算符都转换为数值类型。

'5' - '2' // 3
'5' * '2' // 10
true - 1  // 0
false - 1 // -1
'1' - 1   // 0
'5'*[]    // 0
false/'5' // 0
'abc'-1   // NaN
  1. 对非布尔值类型的数据求布尔值
    在预期为布尔值的地方(比如控制语句中的条件部分),非布尔值都会被转换为布尔值。
if('acg'){
  console.log(true)
}//true
  1. 对非数值类型的数据使用一元运算
    对非数值类型的数据使用一元运算符会将其转换为数值类型
+'43'//43
+'abc'//NaN
-'afa'//NaN

参考

数据类型转换-阮一峰

mdn

Preact原理解析

Preact是一个React的轻量化版本,大小仅有3kb,和React的API完全一致。Preact兼容React生态,同时提供了更小体积、更高性能。可以把Preact当作是使用es6写的极致优化版的React。

React作为一个流行的前端框架,了解其工作原理是有必要的。而Preact由于其小体积和es6的书写方式,阅读其代码是一个了解React很好的开始。

Virtual—Dom

在研究Preact的源码之前,先得了解几个概念。首先就是Virtual—Dom。
前端领域中,和DOM打交道是少不了的。DOM的API繁多,Virtual—Dom就是对真实DOM的一种抽象:使用JS对象来代表DOM,只保留关键有用信息。
比如:

var vnode = {
  nodeName: 'div',
  attributes: {
    className: 'container',
    id: 'root'
  },
  children: 'this is container'
}

vnode代表了一个虚拟node,对应的真实node是这样的

<div class="container" id="root">
  this is container
</div>

可以看到,在我们写前端代码的时候,实际上用到的node属性是很少的。所以可以使用JS对象来模拟真实DOM,需要渲染的时候,再通过虚拟DOM生成真实DOM。

关于虚拟DOM更详细的内容,如有兴趣,可以参阅:
如何实现一个Virtual Dom(一)
how-to-write-your-own-virtual-dom
深度剖析:如何实现一个 Virtual DOM 算法
简洁清晰的virtual dom实现:snabbdom源码阅读 - 简书

JSX

上一节我们初步了解了Virtual—Dom,直接手写Virtual-Dom的效率比较低,而且容易出错。虽然可以通过一个构造函数来生成Virtual-Dom,比如:

var vnode = new Vnode('div', {id: 'root'}, children: 'this is container')

这样看起来还是不够直观,所以有了JSX,通过JSX,我们可以这样写Virtual-Dom。

var vnode = <div id='root'>this is container</div>

使用JSX解析器,可以将上面代码解析出Virtual-Dom的nodeName,attributes和children属性,并且调用一个vnode生成函数,的到一个和上文结果一样的vnode
注意:需要babel环境。

JSX语法可以让我们使用类似html的方式写Virtual-Dom,JSX本质上是一种语法糖,方便我们写组件模版。
关于JSX,这里有一篇很详细的解释文章:
wft-is-jsx

Preact

到了这里,我们终于要进入到Preact部分了。在[src/preact.js]( preact/preact.js at master · developit/preact · GitHub )中,定义了几个最顶层的方法。

  • h
  • cloneElement
  • Component
  • render
  • reRender
  • options

在上面的方法中,我们现在主要关心hrender函数,h函数就是让Babel调用的helper函数,负责生成vnode。render函数负责将vnode渲染成真实node并且插入到对应的位置。

我们通过一个示例开始

// 告诉 Babel 将 JSX 转化成 h() 的函数调用,不然,Babel会使用默认的React的一个方法去构造vnode
/** @jsx h */
import { h, render } from 'preact';

render((
    <div id="foo">
        Hello, world!
    </div>
), document.body);

render函数位于src/render.js,在执行render函数的时候,JSX写法已经被编译成了vnode,此时可以直接把上面的render函数等同于:

render({
  nodeName: 'div',
  attributes: undefined,
  children: 'Hello, world!'
}, document.body);

render函数里,调用了diff函数去进行更新DOM的过程。diff函数位于src/vdom/diff.jsdiff函数大概如下:

//只保留整体逻辑,细节去掉很多
export function diff(dom, vnode, context, mountAll, parent, componentRoot) {
	let ret = idiff(dom, vnode, context, mountAll);
	// append the element if its a new parent
	if (parent && ret.parentNode!==parent) parent.appendChild(ret);
	return ret;
}

我们可以看到,diff的主要功能非常清晰:内部调用了一个idiff的方法去更新vnode,生成真实DOM节点,然后将其插入到对应节点上。而处理vnode的逻辑都在idiff里面,
下面重点来看看这个函数。

//dom是老的dom节点,如果有的话。在上面的例子里,传递到此处的dom为undefined,我们现在只需关注第二个参数vnode即可
function idiff(dom, vnode, context, mountAll) {

	let ref = vnode && vnode.attributes && vnode.attributes.ref;

  //我们知道,react里有两种方式定义Component。一种是函数式的,一种是继承自`React.Component的。
  //如果是函数式的,则执行该函数,得到一个vnode
	while (isFunctionalComponent(vnode)) {
		vnode = buildFunctionalComponent(vnode, context);
	}


  // 如果vnode未null,则此vnode代表一个空的textNode
	if (vnode==null) vnode = '';


  //针对vnode是一个字符串的特殊处理,更改老的textNode节点活着生成新的textNode节点
	if (isString(vnode)) {
		// update if it's already a Text node
		if (dom && dom instanceof Text && dom.parentNode) {
			if (dom.nodeValue!=vnode) {
				dom.nodeValue = vnode;
			}
		}
		else {
			// it wasn't a Text node: replace it with one and recycle the old Element
			if (dom) recollectNodeTree(dom);
			dom = document.createTextNode(vnode);
		}

		return dom;
	}

  //如果vnode为一个继承自`React.Component`的Component。则通过Component的方法去比对新旧vnode差异,得到真实DOM
	if (isFunction(vnode.nodeName)) {
		return buildComponentFromVNode(dom, vnode, context, mountAll);
	}

  //下面就是一个Virtual-Dom的diff算法。这里不做过多解释。
	...
}

上面我们的idiff函数中,提到了函数式的Component和继承自React.Component的Component。下面我们来具体看一看区别:

  1. vnode
    编译前
var Hello1 = <div id="foo">Hello, world!</div>

编译后

var Hello1 = React.createElement(
  "div",
  { id: "foo" },
  "Hello, world!"
);

React.createElement函数为babel创建vnode的默认函数,作用和上文中的h函数类似,后文不再特殊强调。

  1. 函数式的组件
    编译前
function Hello2 (){
  return <div id="foo">Hello, world!</div>
}

编译后

function Hello2() {
  return React.createElement(
    "div",
    { id: "foo" },
    "Hello, world!"
  );
}
  1. React.Component构造的组件

编译前

class Hello3 extends React.Component {
  render(){
    return <div id="hello">hello</div>
  }
}

编译后


var _createClass = (function () {...})();

var _get = function get(_x, _x2, _x3) {...};

function _classCallCheck(instance, Constructor) { ...}

function _inherits(subClass, superClass) {...}

var Hello3 = (function (_React$Component) {
  _inherits(Hello3, _React$Component);

  function Hello3() {
    _classCallCheck(this, Hello3);

    _get(Object.getPrototypeOf(Hello3.prototype), "constructor", this).apply(this, arguments);
  }

  _createClass(Hello3, [{
    key: "render",
    value: function render() {
      return React.createElement(
        "div",
        { id: "hello" },
        "hello"
      );
    }
  }]);

  return Hello3;
})(React.Component);

注:上面一些辅助函数内容过多,这里省略

通过以上的试验,我们可以看到,通过函数式组件和React.Component组件最后都是要生成vnode,他们的不同在于,函数式的组件可以理解为一个渲染函数,接受数据,然后渲染出对应的界面
它本身没有状态的,也没有生命周期方法。而React.Component组件自身可以有自己的state状态,也可以添加生命周期方法。在上面idiff代码里,真对这两种不同的组件,会有不同的处理方式。对于函数式的组建,处理就很简单,直接调用就得到了vnode,而React.component组件在生成vnode时也会注册一些方法,添加一些hook。主要是setState和一些生命周期方法。调用setState会自动组件的render函数。而改变组件的props并不会触发render,类似Redux的数据管理库都是自动将组件和数据绑定,监控数据变化,数据一变,就执行响应组建的render函数。

HTTP

HTTP

关于HTTP协议,一篇就够了 - 简书
HTTP 协议 · 笔试面试知识整理
超文本传输协议 - 维基百科,自由的百科全书

简介

HTTP协议是超文本传输协议(HyperText Transfer Protocol)的缩写,是客户端和浏览器之间的传输协议。通常,由客户端发起一个请求,建立一个到服务器指定端口(通常是80)的TCP链接。HTTP服务器监听到客户端的请求后,向客户端发送响应信息。

主要特点

  1. 简单灵活:可传输任意类型文件
  2. 无状态:HTTP是无状态协议,意味着两次请求之间是没有任何记忆的

请求信息

一个http请求格式如下:
请求行、请求头部、空行、请求数据

GET /562f25980001b1b106000338.jpg HTTP/1.1
Host    img.mukewang.com
Accept    image/webp,image/*,*/*;q=0.8

响应信息

响应信息格式和请求信息格式相似:
状态行、消息报头、空行、响应正文

HTTP/1.1 200 OK
Server:Apache Tomcat/5.0.12
Content-Length:112

<html>...

状态码

  • 1XX:提示信息
  • 2XX:成功
  • 3XX:重定向
  • 4XX:客户端错误
  • 5XX:服务器错误

持久链接(Keep-Alive)

通过在HTTP请求头部加入Connection: Keep-Alive,后开启Keep-Alive模式。在改模式下,链接持续有效。当油后即请求时,能避免重建链接。
在HTTP1.1中,默认情况下所有链接都被保持。除非显示声明:Connection: close才关闭
HTTP长链接不会一直保持,Keep-Alive:timeout=5, max=100,持续5s,100次链接

常见原生dom操作

常见dom操作

创建

document.createElement()//创建元素
document.createDocumentFragment()//创建文档碎片
someElement.cloneNode()//复制node

查询

document.querySelector()//查找匹配的node,返回找到的第一饿或者null
document.querySelectorAll()//查找所有匹配的node,返回一个NodeList
document.getElementById()//根据id查找
document.getElementsByClassName()//根据class查找
document.getElementsByTagName()//根据标签名查找
document.getElementsByName()//根据元素的name属性查找

操作

document.appendChild(child)//在末尾插入元素
document.removeChild(child)//删除子元素
document.replaceChild(newChild, oldChild)//替换子元素
document.insertBefore(newChild, olcChild)//在oldChild之前插入newChild

操作元素属性

someElement.getAttribute(key)//获取属性
someElement.setAttribute(key, value)//设置属性
someElement.hasAttribute(key)//判断是否具有属性

使用react搭建简单博客

使用react搭建简单博客

自己之前学习react的时候只是跟着教程走了一遍,粗略的了解了一些概念。什么virtual-dom,单向数据流之类的。但是一致缺乏深入的实践。最近想搭建一个博客程序,实际演练一下。

整体思路

如果要搭建一个完整的博客的话,要做很多的工作。数据存储和解析、图片的保存等等。在浏览react china论坛的时候,看到一种利用github issue作为数据源的方式。觉得这种方式
很实用,主要时解决了后端存储问题。前端只需要调用api获取数据,渲染数据就好了。

程序初始化的时候,会请求github的issues接口,数据请求完后,再初始化程序。

blog结构

项目是利用create-react-app搭建的,隐藏了很多配置,只需要写好业务代码。整个blog的框架比较简单,只有两个模块:首页和归档。主题是仿照hexo主题hexo-theme-apollo
markdown渲染使用了ReactMarkdown。整个架构比较简单,拆分好逻辑之后,基本上就是拼接各个component了。拆分好组件、复用起来页比较简单。项目结构如下:

blog
--src
  --component
    --header
    --footer
    --article
    --title
    --time
    --...
  --view
  --index.js
  --app.js

使用react,感受最深的是component的拆分。比如一个Article,要不要拆分出title和Body两个组件,组件如何复用等等。拆分好component之后,剩下的工作就是针对各个页面
拼接component了。

在考虑要不要使用redux或者mobx时,尝试了一下,感觉太繁琐。这个blog项目的结构也比较简单,没有复杂交互。所以就是使用了一个全局的对象去管理数据。

不足

  1. 首先是github的issue接口是限制访问的,好像是60/h/ip,如果想要提高接口限制,就需要登录授权,而登录授权需要把token或者帐户密码放在前端代码里,这样做不安全。
    所以现在可能会出现请求失败的情况。而且github的issue是一次将所有数据返回的,如果博客内容过长的话,返回速度很慢。我测试了一下,请求3个比较长的issue,需要3s左右。
    这在体验上太不好了。后面有精力的话,可能会自己搭建一个后台去解决。
  2. 其次是样式还不太美观,很多细节没有处理好,移动端也没有适配

HTTP协议概述

HTTP

超文本传输协议(HTTP)是互联网使用的最广泛的网络协议。

HTTP是一个客户端终端(用户)请求到服务器上指定端口(默认为80)。我们称这个客户端为用户代理程序(user agent)。应答的服务器上村粗者一些资源。我们称这个应答服务器为源服务器(origin server)。在用户代理和源服务器中间可能存在多个“中间层”,比如代理服务器、网关或者隧道(tunnel)。

URL

URL的全称是统一资源定位符,用来标示互联网上的资源地址。其格式如下:

协议/服务器/端口/路径/查询

http://www.aaa.com:8080/w/index.html?title=hahaha#name

HTTP请求格式

Request

GET /images/nav_logo242.png HTTP/1.1  请求行:包含方法、URL、版本
Host: www.google.com.hk		请求头部:指明服务器要使用的附加信息
User-Agent: Mozilla/5.0              
								 空行,不可以省略
some data                     请求数据:可为空

Response

HTTP/1.1 200 OK					状态行:版本、状态码、状态消息
Content-Length: 16786				响应头部:客户端摇使用的附加信息
Content-Type: image/png		    
								    空行,不可省略
some data							响应正文

详细头部字段表: HTTP头字段列表 - 维基百科,自由的百科全书

HTTP状态码

1xx ——--消息——请求已被服务器接收,继续处理
2xx———-成功——请求已成功被服务器接收、理解、并接受
3xx———-重定向——需要后续操作才能完成这一请求
4xx———-请求错误——请求含有词法错误或者无法被执行
5xx———-服务器错误——服务器在处理某个正确请求时发生错误
详细状态码说明见: HTTP状态码 - 维基百科,自由的百科全书

HTTP的发展

HTTP/0.9

HTTP0.9只有GET一种方法,且不支持请求头。所以无法和服务器进行太多交互。

HTTP/1.0

HTTP1新增了POSTHEAD方法,支持请求头,状态码等,缓存等。类似于上文的例子。

在HTTP1.0版本,默认是短链接,即每个TCP只能发送一个请求。一次请求完毕之后,就会断开链接。下一次请求会重新建立一个TP链接。为了解决这个问题,有些浏览器请求时,会在请求头部加一个非标准的Connection字段。
Connection: keep-alive
这个字段告诉服务器不要关闭TCP链接,以便于其他请求复用。

HTTP/1.1

HTTP1.1相比于1.0,主要是将1.0标准化并加入了很多改进。其中最重要的有:

  1. 链接默认时复用的。即Connection:keep-alive时默认的。
  2. 流水线操作。在统一个TCP里面,可以同时发送多个请求。
  3. 支持响应分块。

需要注意的是,在一个TCP链接里面,可以发出多个请求,但是响应还是有顺序的。服务器只有处理完前一个响应之后,才会去处理第二个响应。如果前面有一个响应特别慢,后面的响应就得排队等候。为了解决这个问题,要么减少请求,要么多开持久链接。这也就是在网页优化时,经常会提到合并脚本和样式(可以减少请求)和域名分片(同一个域名下可以开的TCP链接时又限制的,一般为6)。

HTTP/2

HTTP2完全兼容HTTP1.1的语义。并且实现了多路复用、头信息压缩、服务器推送等。

HTTP缓存

缓存是一种可以有效提高用户体验的手段。关于缓存控制,google developer网站有篇很好的教程:HTTP 缓存 -Google Developers

参考

超文本传输协议 - 维基百科,自由的百科全书

HTTP 协议入门

关于HTTP协议,一篇就够了 - 简书

一次完整的HTTP请求与响应涉及了哪些知识? - SegmentFault

HTTP协议详解(真的很经典) - Hundre - 博客园

HTTP头字段列表 - 维基百科,自由的百科全书

HTTP状态码 - 维基百科,自由的百科全书

HTTP 缓存 -Google Developers

git rebase

git rebase

在git中,整合不同分支的代码呦两种方法:mergerebase
本文示意图都来自于git

git merge

如下图,experiment分支是在c2提交从master分支产生。在这之后,experiment分支和master分支都有新的提交。现在需要将两次提交整合在一起。

最简单的办法是merge

git checkout master
git merge experiment

产生的提交记录如下

merge操作简便快捷,但是这样做每次都会产生一个merge操作的纪录,在文件改动特别频繁的时候,提交记录会比较乱。

git rebase

git rebase master

产生的提交记录如下

可以看到,提交记录干净了许多。

git rebase 冲突解决

git rebase虽然可以得到干净的git纪录,但是它并不是一个安全的操作,相比于merge的方式更容易产生文件冲突。产生冲突的时候,rebase过程会中止。
需要手动解决冲突。

  1. 解决冲突
  2. git add .
  3. git rebase --containue

此外,git rebase还有这两条命令

git rebase --abort 取消此次rebase

git rebase --skip  跳过当前补丁

参考

git

事件

事件

js和html是通过事件来交互的。

事件流

事件流描述的是从页面中接受事件的顺序。(事件冒泡、事件捕获)

DOM事件流

DOM2级事件规定事件流有三个阶段:

  • 事件捕获阶段
  • 处于目标阶段
  • 事件冒泡阶段

image

事件处理程序

事件处理程序是响应某个事件的函数

html事件处理

image

DOM0级事件处理程序

通过js将一个函数赋值给一个元素的事件处理程序属性。
image

DOM2级事件处理程序

addEventListener:对元素添加一个侦听器
removeEventListener:移除一个侦听器

事件对象

image

事件类型

  • UI事件
  • 焦点事件
  • 鼠标事件
  • 滚轮事件
  • 文本事件
  • 键盘事件
  • 合成事件
  • 变动事件

js模块化

js模块化笔记

由于历史原因,js在设计时并没有考虑到模块化的需求。后来随着业务逻辑的前移,前端的逻辑越来越重,代码也越来越多。
所以必须有一种模块化方案提升代码的可维护性和复用性。

命名空间

最开始的模块化是利用闭包的特性去实现的

var module1 = (function () {
  var name = 'fdasf'
  return {
    sayName: function () {
      console.log(name)
    }
    //...
  }
})()

module1倍挂载在全局变量,变量a对于全局是不可见的,只能通过闭包函数访问。但是这样本质上还是污染了全局变量,随着代码的增加,命名冲突还是会出现,只能手工管理这些模块。

cmd

node开始流行后,迫切需要一种模块化方案。CommonJs社区推出了commonjs规范。简要介绍如下:

  1. 在一个模块里,必需有一个全局函数require。该函数接受一个模块定义,返回模块暴露出的api
  2. 在一个模块里,有一个exports对象变量,模块可以将api作为属性绑定在上面
  3. exports时模块暴露api的唯一方法

一个官方的例子如下

//math.js
exports.add = function() {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
        sum += args[i++];
    }
    return sum;
};
//increment.js
var add = require('math').add;
exports.increment = function(val) {
    return add(val, 1);
};
//program.js
var inc = require('increment').increment;
var a = 1;
inc(a); // 2

一个commonjs模块里,需要暴露的api绑定在exports对象上,像使用该模块的时候,使用require函数导入模块,这个函数返回该模块的exports对象。可以
简单的理解为:require函数读取文件并执行文件,然后返回该文件的exports对象。

需要注意的是:

  1. exports对象不能被赋予其他值。可以理解为:exports = module.exports = {}。可以看到,exports只是module.exports的一个索引,
    本质上模块对外暴露的是module.exports,所以对于exports的操作要小心。不可赋予新值。
  2. commonjs模块输出的上是值的拷贝,一旦输出,模块内部的变化不会反应在已经导出的值上。

amd

commonjs规范主要用于服务器端,但是在浏览器端没法直接使用。这是因为:

  1. 外层没有function的包裹,变量暴露在全局对象上。
  2. commonjs是同步加载,在服务器端,资源是从硬盘或者内存中覆去,加载时间可以忽略。但是在浏览器端,由于网络环境的复杂性,比较适合异步加载。

为了解决浏览器端模块化的问题,社区提出了AMD规范:
存在一个全局函数define。该函数如下:

  define(id?, dependencies?, factory)

其中:

  1. id为模块标示
  2. dependencies为模块的依赖,和facroty中的形参一一对应,如果facroty为函数的话。
  3. fancroty可以为函数或者对象

一个典型的例子如下:

define(['a','b','c'], function (a, b, c) {//a,b,c分别对应几个模块的输出值
  ...
})

可以看出,在一个amd模块里,一个模块使用到的依赖需要提前被声明,而不像commonjs一样可以需要时再引入。

在amd模块里,依赖是被提前下载并执行的,如果依赖很多的话,一些暂时用不到的依赖也会提前被下载执行,会带来一定的性能损耗。虽然amd模块里也可以
不提前声明依赖,等到需要的时候使用require来引入,但是这样会带来另一个问题:后续代码的执行必须等到依赖被下载执行完后才能进行。所以后来又提出了cmd规范,和amd的主要区别是:cmd模块的依赖是预先下载,需要的时候再执行。

es2015 modules

在es之前,js的模块化主要有两种方案:commonjs和amd,分别应用于服务器和浏览器。在es6里,从语言标准层面实现了模块功能。在es6之前,由于语言的
不支持,各种模块化方案都是运行时加载,而es6的模块化,在编译时就确定了模块的依赖关系和输入输出变量。和commonjs不同的是es6的模块输出的始终是值的引用,而不是值的复制。关于es6的module语法,推荐阮一峰:module

test

a test issue created by api

chrome开发者工具使用简介

chrome开发者工具使用简介

elements

在Elements选项下,可以看到Elements面板内容。左侧为结构化html文档,右侧为样式、计算后样式等。
右键任意一节点,可以对该节点进行操作,包括修改、删除等。在右侧,可以编辑对应节点的样式,也可以切换到Computed选项,查看计算后的样式。

console

console面板主要提供一个执行脚本、显示脚本输入输出的环境。

Network

打开chrome的开发者工具,切换到network选项下,就可以看到chrome的网络面板

上面是功能区域,下面是网络请求内容。

功能区域从左到右依次为:

  • 纪录/停止纪录,是否纪录网络请求的开关
  • 清空纪录,清空所有纪录
  • 捕获屏幕截图,在发出请求过车个闹钟功能纪录屏幕截图,方便比对不同时刻的页面状态和网络请求状态
  • 调整纪录外观,使用大/小预览图,整体预览等
  • 是否保存纪录,如果选择是,在刷新页面后依然保存之前的纪录
  • 是否禁止缓存,如果选择是,则禁止浏览器缓存,在调试时很有用
  • 网速调整栏,设置不同网速,方便模拟在不同网络环境下的网络请求情况

功能区域之下的很多行就是具体的网络请求了。在每一行里,显示了该网络请求大大致信息:名称、请求状态、类型、发出请求的页面、大小、请求耗时、瀑布图等。
点击名称,会显示该请求的详情。
从左到右,依次是:

  • headers,显示该请求的头部字段,可以看到请求使用的头部字段和返回的响应头部字段
  • preview,提供资源的预览
  • response,返回结果
  • cooikes,使用的cookies
  • timing,请求耗时

Sources

Sources面板主要是调试用的。最左侧是资源文件结构,中间是代码区域,可以设置断点、修改脚本,右侧上方是调试功能区域,下一步、进入函数、推出函数等功能。右侧下方展示了当前调用栈、当前作用域等信息,还可以添加watch变量,实时监听变量的变化。

Audits

Audits面板可以分析页面载入速度,然后提供优化建议。点击run按钮,就可以对页面进行分析,分析结果会显示页面有哪些可以优化的地方

Security

Security面板显示网站安全信息、证书等。

Application

Application面板展示了页面使用的所有资源,包括存储、数据库、缓存等。在次面板下,可以修改本地存储、会话存储,可以查看cookie,操作web sql等。

Performance和Memory

关于performance和Memory,由于自己使用的不适很熟练,建议直接查看chrome官方的开发者文档

chrome dev tool

BOM

BOM(浏览器对象模型)

BOM主要处理浏览器窗口和框架。用来处理除了网页文档内容、和浏览器本身的交互。

Window

BOM的核心是Window对象,它代表浏览器的一个实例。在浏览器中,window即使js访问浏览器窗口的一个接口,又是EVCMAScript规定的Global对象,所有在全局作用域中声明的变量、函数都会变成window的属性和方法。

窗口关系及框架

每个框架都有一个window对象
top: 最顶层框架
parent:当前框架父框架
self:window
frames: 子框架

常用方法

setTimeout
setInterval
parseInt
parseFloat

location

location对象提供了当前加载文档的信息,也又一些导航功能。修改location的属性(除了hash)都会改变当前加载的页面,并在浏览器历史记录中增加一条。

属性

hash,host,hostname,href,pathname,port,protocol,search

方法

assgin:打开一个新的url,增加历史记录
replace: 打开一个url,不会生成历史记录
reload:重载当前页面

navigator

表示浏览器信息

screen

标明客户端的能力,主要指像素尺寸

history

历史记录相关,处于安全策略,开发人员只能获取历史记录长度,而不能知道历史记录的具体url

test

a test issue created by api

js迭代器学习

迭代器是对数据进行遍历操作的一种模式。在es6之前,遍历数据主要有for(..),forEach,for...in三种方式。其中for循环和forEach都只用于数组,for循环写起来繁琐,forEach不支持breakfor...in循环用来遍历对象的可枚举属性,包括继承来的属性。主要缺点在于必须使用hasOwnProperty来排除原型链上属性,而且for...in循环的顺序是不确定的。

es6里,增加了Map和Set,这样一共就有Object,Array,Map,Set四种表示集合的结构。迭代器就是为不同的数据结构提供统一访问接口的一个方式。为了变成可迭代对象,一个对象必须实现@@Symbol.iterator属性。该属性是一个函数,执行该函数,就会得到一个迭代对象。

var arr = [1,2,3];

var it = arr[Symbol.iterator]();

it.next();		// { value: 1, done: false }
it.next();		// { value: 2, done: false }
it.next();		// { value: 3, done: false }

it.next();		// { value: undefined, done: true }

当使用for...of循环时,会自动寻找迭代器借口,不用我们像上面一样自己去调用。

var arr = [1,2,3];
for(var item of arr){
  console.log(item)//1,2,3
}

在es6中,原生部署迭代器接口的有数组、某些类数组对象(比如String)、Set、Map。一般对象如果想要实现迭代器,可以自己部署此接口。

参考:Iterator和for...of循环

You Don't Know JS: ES6 & Beyond

浏览器工作原理

浏览器工作原理

浏览器的主要功能

浏览器的主要功能是向服务器发出请求,在浏览器窗口展示所选择的资源。这里的资源一般是指HTML文档,pdf,图片等。资源的位置由URI(统一资源标识符)指定。

浏览器的高层架构

浏览器主要组件为:

  1. 用户界面 - 地址栏,前进后退按钮等。
  2. 浏览器引擎 - 在用户界面和呈现引擎之间传递指令 。
  3. 呈现引擎 - 显示请求的内容。如果是HTML,就负责解析HTML和CSS内容,并将解析完后的内容显示在屏幕上。
  4. 网络 - 用于网络调用。
  5. 用户界面后段 - 用于绘制基本的窗口小部件,比如组合框和窗口。
  6. javascript解释器 - 解释和执行javascript相关代码
  7. 数据存储 - 浏览器需要在硬盘上保存数据
    [image:7AB8D3EE-6321-4128-A904-FB0137EA37B3-34261-00016501C8CC8D76/ABD649A2-F9DD-4748-87D8-2F864AB87A2F.png]
    image

呈现引擎

呈现引擎的主要作用是解析并渲染网页内容。

主流程

[image:70BEC576-5D17-4DBB-A82F-4B13E7DCB9B7-34261-00017BC4AE234561/50EA94E2-A1AD-4D66-AAC7-5ED1968DF414.png]
image

基本流程为:
解析html,生成dom树;解析css,生成呈现树;布局;绘制。
image

参考:

现代浏览器工作原理

google开发者

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.