superdc / myblogs Goto Github PK
View Code? Open in Web Editor NEWstudy more, write more, understand more
study more, write more, understand more
心路历程:每次面试过后心情都久久不能平复,回想面试的过程,自己的回答,心理只有一句话:太烂了,没有亮点!内心有点崩溃,怀疑自己,我真的这么差吗?有很多借口可以说,准备的不足,状态不好等等,但,还是面对现实吧。找到问题,解决问题。
存在的问题:
写给自己的话:
写博客不要一开始就追求准确和完美。准确和完美可以在不断深入的学习和实践中慢慢的达到,错误的答案也是答案,最重要的是开始。
Q: 为什么使用React.createClass
创建组件可以实现自动绑定this
,继承React.Component
不可以?
A: 简单来说,React.createClass
创建的组件在实例化时帮我们进行了自动绑定,而继承React.Component
没有。
还是让我们从源码中看看究竟吧~~
其简易流程图如下:
createClass
实现了一个继承自ReactClassComponent
的构造函数,让我们一步一步来看~
Constructor
Constructor
主要做了如下两件事:
createClass中声明的构造函数
var Constructor = identity(function(props, context, updater) {
// ...
// 这里就是实现自动绑定
if (this.__reactAutoBindPairs.length) {
bindAutoBindMethods(this);
}
// ...
});
自动绑定的实现如下
function bindAutoBindMethods(component) {
var pairs = component.__reactAutoBindPairs;
for (var i = 0; i < pairs.length; i += 2) {
var autoBindKey = pairs[i];
var method = pairs[i + 1];
component[autoBindKey] = bindAutoBindMethod(
component,
method
);
}
}
// component为当前实例
function bindAutoBindMethod(component, method) {
var boundMethod = method.bind(component);
// ...
return boundMethod;
}
this.__reactAutoBindPairs在第③步合并属性的过程中产生
function mixSpecIntoComponent(Constructor, spec) {
// ...
var proto = Constructor.prototype;
var autoBindPairs = proto.__reactAutoBindPairs;
// 先处理mixins, 因为这里可能会重写React的内部方法
if (spec.hasOwnProperty(MIXINS_KEY)) {
RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins);
}
for (var name in spec) {
if (!spec.hasOwnProperty(name)) {
continue;
}
if (name === MIXINS_KEY) {
// We have already handled mixins in a special case above.
continue;
}
var property = spec[name];
var isAlreadyDefined = proto.hasOwnProperty(name);
validateMethodOverride(isAlreadyDefined, name);
if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) {
RESERVED_SPEC_KEYS[name](Constructor, property);
} else {
var isReactClassMethod = ReactClassInterface.hasOwnProperty(name);
var isFunction = typeof property === 'function';
/*
shouldAutoBind的判定如下:
1. 是函数
2. 不是ReactClassInterface借口中定义的内部方法
3. 没有定义过(已经在处理mixins的时候处理了)
4. 没有将方法的autobind方法显式定义为false
*/
var shouldAutoBind =
isFunction &&
!isReactClassMethod &&
!isAlreadyDefined &&
spec.autobind !== false;
if (shouldAutoBind) {
autoBindPairs.push(name, property);
proto[name] = property;
} else {
if (isAlreadyDefined) {
var specPolicy = ReactClassInterface[name];
// methods before calling the new property, merging if appropriate.
if (specPolicy === 'DEFINE_MANY_MERGED') {
proto[name] = createMergedResultFunction(proto[name], property);
} else if (specPolicy === 'DEFINE_MANY') {
proto[name] = createChainedFunction(proto[name], property);
}
} else {
proto[name] = property;
}
}
}
}
}
ReactClassComponent
让我们看下ReactClassComponent
是什么
var ReactClassComponent = function() {};
Object.assign(
ReactClassComponent.prototype,
ReactComponent.prototype, // 注意,我们看到`ReactComponent`的身影喽,先按下不表。
ReactClassMixin
);
var ReactClassMixin = {
replaceState: function(newState, callback) { //... }, // 要被废弃了
isMounted: function() { // ... },
};
这样我们创建的实例就可以调用ReactComponent
的原型方法和ReactClassMixin
中定义的方法
getDefaultProps
第一个生命周期函数getDefaultProps
出现了。调用该函数并把值赋在构造函数的defaultProps
属性上。
相比起React.createClass
,React.component
简单了许多
function ReactComponent(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
ReactComponent.prototype.isReactComponent = {};
ReactComponent.prototype.setState = function(partialState, callback) {
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
ReactComponent.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this);
if (callback) {
this.updater.enqueueCallback(this, callback, 'forceUpdate');
}
};
我们看到ReactComponent
就是一个简单的构造函数,没有其他的操作(比如自动绑定),也没有getInitialState
和getDefaultProps
方法了。但实质上两种写法基本上是一样的,只是用extend
继承还不支持mixins
,但是官方现在也不推荐使用mixins
了,而是建议使用组合。
es5
var Container = React.createClass({
// 调用后赋值到构造函数的静态属性defaultProps上
getDefaultProps: function() {
// 注意:这里拿不到组件实例
return { text: 'num' };
},
// 实例化时调用,赋值到实例的state属性上
getInitialState: function() {
return { num: 0 };
},
// 原型属性,this绑定了实例
handleClick: function() {
this.setState({
num: this.state.num + 1
});
},
// 原型属性
render: function() {
return (
<button onClick={this.handleClick}>{this.props.text}: {this.state.num}</button>
);
}
});
es6
class Container extends React.Component {
// 构造函数的静态属性
static defaultProps = {text: 'num'};
constructor(props) {
super(props);
this.state = {num: 0}; // 实例属性
this.handleClick = this.handleClick.bind(this);
}
// 原型属性,this未绑定实例
handleClick() {
this.setState({
num: this.state.num + 1
});
}
// 原型属性
render() {
return (
<button onClick={this.handleClick}>{this.props.text}: {this.state.num}</button>
);
}
}
Q: setState为什么有时候表现为同步,有时候表现为异步?
React组件根据props
和state
渲染UI,props
由父组件传递进子组件,state
由组件内部来维护。
setState() schedules an update to a component’s state object. When state changes, the component responds by re-rendering.
React官方文档的这句话解释了setState
的作用:setState
可以用来更新一个组件的state
对象,当state
变化时,组件会重新渲染UI。
There is no guarantee that calls to
setState
will run synchronously, as they may eventually be batched together.
源码中对setState
方法的注释:不能保证setState
会同步执行,它们可能会进行批量更新。可以看下这个demo。
记住一点:React为提升性能,采取批量更新的策略。
简单来说batchingStrategy.isBatchingUpdates
影响着setState
是同步还是异步。当组件处于批处理事务中时,其值为true
,setState
表现为异步;反之,为false
,setState
表现为同步。
图中的internalInstance
内部实例只组件在挂载过程中创建的ReactCompositeComponent
实例。调用setState
时,将partialState
加入internalInstance
的等待处理的状态列表,即_pendingStateQueue
;如果传入了callback
,则将callback
加入internalInstance
的等待处理的回调函数列表,即_pendingCallbacks
。当batchingStrategy.isBatchingUpdates
为true
即处理批处理事务中时,将内部实例加入dirtyComponents
队列。React会在批处理事务关闭时对dirtyComponents
中的组件进行批量更新,表现为异步;当batchingStrategy.isBatchingUpdates
为false
时,则会进行一个完整的更新过程,表现为同步。
React会在自己可以控制的情况下采用批处理策略,比如如下几种情况
拿demo
的中的代码来说:
componentDidMount() {
this.setState({
num: this.state.num + 1
});
this.setState({
num: this.state.num + 1
});
}
因为组件挂载采用了批处理策略,执行componentDidMount
时,批处理事务仍然是打开的,那么此时batchingStrategy.isBatchingUpdates
为true
,不会更新组件的state
。上面的代码即相当于
internalInstance._pendingStateQueue.push(1,1)
那么当更新组件state
时就把它更新为1了。
还有一种常见的setState
方式,就是在事件回调中,请看这个demo
当我们点击按钮时,state
仍然是每次只加1。
dispatchEvent: function(topLevelType, nativeEvent) {
if (!ReactEventListener._enabled) {
return;
}
var bookKeeping = TopLevelCallbackBookKeeping.getPooled(
topLevelType,
nativeEvent
);
try {
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
} finally {
TopLevelCallbackBookKeeping.release(bookKeeping);
}
}
React实现了自己的syntetic事件,当事件触发时会调用上面的方法,我们看到了ReactUpdates.batchedUpdates
,在这里会打开批处理事务,那么在这期间我们不管调用多少次setState
,都会将内部实例加入dirtyComponents
中进行批量更新,那么就和上面的demo是一样的情况了。
在浏览器的异步API和原生的DOM事件中setState
会表现为同步,如setTimeout, setInterval, promise.then, addEventListener
。
因为浏览器是单线程,非阻塞的,主线程执行的是函数调用栈顶的执行上下文中的代码,当遇到异步API时,会将其上下文加入到任务队列或微任务队列中。当执行栈为空时,会先将微任务队列中的代码执行完,然后执行任务队列中的代码。所以,当在这些异步API的回调中调用setState
时,组件不会处于批处理过程中,batchingStrategy.isBatchingUpdates
为false
,表现为同步。
事件是JavaScript和HTML交互的方式,比如常用的click
,blur
,DOMContentLoaded
,mousedown
,mouseup
事件等。我们会先订阅这些事件(被观察者),给事件指定事件处理程序,当事件发生时浏览器就会调用相应的处理程序进行处理。可以说,事件模型是观察者模式的具体实现。
图片引用自官方文档
标准的DOM Events把事件传播分为三个阶段:
Event Handler的名字:on<event>
,比如onclick
。
<input value="Click me" onclick="alert(this.value)" type="button" id="button">
a. 因为HTML属性时大小写不敏感的,这里onclick也可以写为onClick,ONCLICK,ONClick... 但还是建议使用lowercase.
b. 这种方式指定的事件处理程序会创建一个包含属性内容的函数,等价于
button.onclick = function(){
// this指向当前元素
alert(this.value);
// 可以取到event对象
alert(event.type);
}
// 这也提醒我们属性内容应该是一个函数调用
c. 这种方式是不被推荐的。
2. DOM property(DOM0级事件处理程序)
<input id="elem" type="button" value="Click me">
<script>
var elem = document.getElementById('elem');
// 指定事件处理程序
elem.onclick = function() {
// this指向当前元素
alert(this.value);
// 可以取到event对象
alert(event.type);
};
// 删除事件处理程序
elem.onclick = null;
</script>
a. DOM属性时大小写敏感的,所以属性只能写为onclick
b. 事件处理程序在冒泡阶段触发
c. 只能指定一个事件处理程序,否则会覆盖
3. addEventListener(DOM2级事件处理程序)
// 旧语法
target.addEventListener(type, listener[, useCapture]);
// 新语法
target.addEventListener(type, listener[, options]);
options
对象为可选,包括如下几个选项
capture
默认为false
,同上面的useCapture
once
默认为false
,表示listener最多调用一次,触发后即被自动移除(Firefox50, Chrome55开始支持)passive
默认为false
,表示不会禁止事件的默认行为,即不会调用preventDefault
方法(Firefox49,Chrome51开始支持)passive
是为了解决touch
和wheel
事件引起的滚动卡顿。因为touchstart
, touchmove
都是可以cancelable
的,它们的默认行为是滚动页面,可以通过preventDefault
阻止默认行为。不管是否阻止默认行为,浏览器会在执行事件处理程序后再执行默认行为,这就有可能造成滚动卡顿,即使事件处理程序是一个空函数。当设置{passive: true}
,浏览器在监听事件的时候就知道了事件处理程序不会调用preventDefault
,浏览器就可以毫无顾虑的执行默认行为了。a. 这种方式允许为同一个事件添加多个事件处理程序
b. 多个事件处理程序按照添加的顺序执行
removeEventListener(移除事件处理程序)
target.removeEventListener(type, listener[, options]);
target.removeEventListener(type, listener[, useCapture]);
移除事件处理程序只检查type, listener, capture/useCapture,匿名的事件处理程序无法被移除。
element.addEventListener("mousedown", handleMouseDown, { passive: true });
element.removeEventListener("mousedown", handleMouseDown, { passive: true }); // Succeed
element.removeEventListener("mousedown", handleMouseDown, { capture: false }); // Succeed
element.removeEventListener("mousedown", handleMouseDown, { capture: true }); // Fail
element.removeEventListener("mousedown", handleMouseDown, { passive: false }); // Succeed
element.removeEventListener("mousedown", handleMouseDown, false); // Succeed
element.removeEventListener("mousedown", handleMouseDown, true); // Fail
//添加事件处理程序
target.attachEvent(eventNameWithOn, callback);
elem.attchEvent('onclick', function(){
// this是window, 不是element
console.log(this===window); // true
});
//删除事件处理程序
target.detachEvent(eventNameWithOn, callback);
a. 可以为同一事件添加多个事件处理程序
b. 多个事件处理程序会以与添加顺序相反的顺序执行
机会只有一次,它总是留给有准备的人。被生活推着被动的前进,就算好运降临了,也不一定抓得住。
更新于 2020.4.5
工程化,其实贯穿在我们开发的整个生命周期中,开发中,开发后。目的是,增加开发规范化,提高开发效率,资源构建效率,资源构建优化等。并且,用自动化的形式代替人工操作。
特点:线性数据结构。只能在某一端添加或删除数据,先进后出(FILO)。
class Stack{
constructor(){
this.stack = [];
}
push(item){
this.stack.push(item);
}
pop(){
this.stack.pop(item);
}
// 返回栈顶元素,但不删除
peek(){
return this.stack[this.getCount()-1];
}
getCount(){
return this.stack.length;
}
isEmpty(){
return this.getCount().length === 0;
}
}
let isValid = function(s) {
const map = {
'(': ')',
'[': ']',
'{': '}'
}
let stack = [];
let last;
for(let i = 0,l = s.length; i < l; i++){
if(map[s[i]]){
stack.push(map[s[i]]);
}else{
last = stack.pop();
if(last != s[i]) return false;
}
}
if(stack.length != 0) return false;
return true;
};
这篇文章是对Codebase Overview的翻译,有不当之处敬请指出~~
版本v16.3.1
文档地址:Code Overview
这个章节将会概要呈现React代码库的组织,约定以及实现。
如果你想contribute to React,我们希望这个指南可以帮助你更好的做出改变。
我们不一定推荐React
的任何约定,因为许多都是由于历史原因而存在的并且会随着时间而变化。
React
几乎没有外部依赖。一个require()
通常会指向React
自己代码库中的文件。然而,也有一些很少的例外。
写在前面: 从头到尾分析一遍React源码太庞杂了,所以决定从点入手来理解源码
下面列出了使用React
的过程中遇到的问题或者有疑惑的地方,在此记录,持续更新~~
React.Component
创建组件需要自己绑定this
,而使用createClass
创建组件不需要?setState
为什么有时候表现为同步,有时候表现为异步?componentWillMount
中调用setState
为什么不会引起重新渲染?componentDidMount
中,而不是在componentWillMount
中?key
用在了哪里?diff
算法每次看javascript的继承都会被绕晕,这次决定静下心来好好捋一捋~
按原型链实现继承的方式有以下几种:
function Parent() {
this.colors = ['red', 'yellow'];
}
Parent.prototype.getColors = function() {
console.log(this.colors);
};
function Child() {}
Child.prototype = new Parent();
var child = new Child();
child.colors // ["red", "yellow"]
child.getColors() // ["red", "yellow"]
插播一下原型和原型链的概念~~
prototype
:每个函数都有一个prototype
属性,它指向一个对象,默认具有一个constructor
属性,指向该prototype
属性所在的函数__proto__
:当我们把函数当做构造函数调用时,生成的实例会默认具有一个内部属性[[prototype]]
,在Firefox, Safari和Chrome
中这个属性是__proto__
,指向其构造函数的原型对象。constructor
:实例上没有constructor
属性,但我们却能访问到它,是因为对象属性是按照原型链查找的。当实例对象上没有访问的属性时,会在该对象的原型上(也就是__proto__
所指向的对象)继续查找。child.toString()
时,首先在child
上查找该属性,发现没有child.__proto__
即由new Parent()
创建的实例上查找,发现没有__proto__
即Ojbect
的实例上查找,找到了我们打印看下child
,对象就是通过__proto__
连起来的原型链对属性和方法进行一层层查找了
插播完毕~~
这种方式的缺点:
child.colors.push('blue');
child.colors; // ['red,yellow,blue']
var child2 = new Child();
child2.colors; // ['red,yellow,blue']
function Parent(name) {
this.name = name;
this.colors = ['red', 'yellow'];
this.getName = function() {
console.log(this.name);
}
}
Parent.prototype.getColors = function() {
console.log(this.colors);
}
function Child(name, age) {
//调用Parent构造函数,将属性添加到了Child实例上,
Parent.call(this, name);
this.age = age;
}
var child = new Child('DC', 18);
child.name // 'DC'
child.age // 18
child.colors.push('blue');
child.colors //['red','yellow','blue']
child.getColors() //TypeError
var child2 = new Child('MM', 18);
child2.colors //['red', 'yellow']
优点:
缺点:
function Parent(name) {
this.name = name;
this.colors = ['red', 'yellow'];
}
Parent.prototype.getColors = function() {
console.log(this.colors);
};
function Child(name) {
Parent.call(this, name);
}
Child.prototype = new Parent();
var child = new Child();
child.colors // ["red", "yellow"]
child.getColors() // ["red", "yellow"]
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.