Giter Club home page Giter Club logo

myblogs's People

Contributors

superdc avatar

Watchers

 avatar

myblogs's Issues

面试总结

心路历程:每次面试过后心情都久久不能平复,回想面试的过程,自己的回答,心理只有一句话:太烂了,没有亮点!内心有点崩溃,怀疑自己,我真的这么差吗?有很多借口可以说,准备的不足,状态不好等等,但,还是面对现实吧。找到问题,解决问题。

存在的问题:

  1. 平时缺少思考和总结。
  2. 虽然会写笔记,但不够深入。
    所以,多思考多学习多总结吧。
    下面列出回答的不好的问题。

写给自己的话:
写博客不要一开始就追求准确和完美。准确和完美可以在不断深入的学习和实践中慢慢的达到,错误的答案也是答案,最重要的是开始。

  1. 如何理解工程化?项目中是如何做的?
  2. 开发过程中,用到了哪些设计模式?
  3. 部署架构是怎样的?
  4. 如何避免组件多次渲染?
  5. CSS:link 和 import 引入 css 的区别?
  6. 插入排序 vs 冒泡排序

为什么使用React.createClass创建组件可以实现自动绑定this,继承React.Component不可以?

Q: 为什么使用React.createClass创建组件可以实现自动绑定this,继承React.Component不可以?
A: 简单来说,React.createClass创建的组件在实例化时帮我们进行了自动绑定,而继承React.Component没有。

还是让我们从源码中看看究竟吧~~

React.createClass

其简易流程图如下:

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.component

相比起React.createClassReact.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就是一个简单的构造函数,没有其他的操作(比如自动绑定),也没有getInitialStategetDefaultProps方法了。但实质上两种写法基本上是一样的,只是用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>
    );
  }
}

前端性能优化

网站的性能优化是一个重要的课题,它关系到用户体验,用户留存,商业利益。作为前端工程师,需要了解性能瓶颈出现在哪里以及优化的思路。

下面的思维导图是对性能优化的一些总结,也是自己学习的一个思路,后面会再针对性的详细总结一下:

default

setState为什么有时候表现为同步,有时候表现为异步?

Q: setState为什么有时候表现为同步,有时候表现为异步?

setState

React组件根据propsstate渲染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为提升性能,采取批量更新的策略

setState流程

简单来说batchingStrategy.isBatchingUpdates影响着setState是同步还是异步。当组件处于批处理事务中时,其值为truesetState表现为异步;反之,为falsesetState表现为同步。

图中的internalInstance内部实例只组件在挂载过程中创建的ReactCompositeComponent实例。调用setState时,将partialState加入internalInstance的等待处理的状态列表,即_pendingStateQueue;如果传入了callback,则将callback加入internalInstance的等待处理的回调函数列表,即_pendingCallbacks。当batchingStrategy.isBatchingUpdatestrue即处理批处理事务中时,将内部实例加入dirtyComponents队列。React会在批处理事务关闭时对dirtyComponents中的组件进行批量更新,表现为异步;当batchingStrategy.isBatchingUpdatesfalse时,则会进行一个完整的更新过程,表现为同步。

setState表现为异步的情况

React会在自己可以控制的情况下采用批处理策略,比如如下几种情况

生命周期函数中调用setState

demo的中的代码来说:

  componentDidMount() {
    this.setState({
      num: this.state.num + 1
    });
    this.setState({
      num: this.state.num + 1
    });
  }

因为组件挂载采用了批处理策略,执行componentDidMount时,批处理事务仍然是打开的,那么此时batchingStrategy.isBatchingUpdatestrue,不会更新组件的state。上面的代码即相当于

internalInstance._pendingStateQueue.push(1,1)

那么当更新组件state时就把它更新为1了。

事件回调中调用setState

还有一种常见的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是一样的情况了。

setState表现为同步的情况

在浏览器的异步API和原生的DOM事件中setState会表现为同步,如setTimeout, setInterval, promise.then, addEventListener
因为浏览器是单线程,非阻塞的,主线程执行的是函数调用栈顶的执行上下文中的代码,当遇到异步API时,会将其上下文加入到任务队列或微任务队列中。当执行栈为空时,会先将微任务队列中的代码执行完,然后执行任务队列中的代码。所以,当在这些异步API的回调中调用setState时,组件不会处于批处理过程中,batchingStrategy.isBatchingUpdatesfalse,表现为同步。

名词解释

  1. REPL: Read-Eval-Print Loop 交互式编程环境
  2. SCM: Software Configuration Management 软件配置管理(版本控制软件)

事件梳理(一)

事件

事件模型

事件是JavaScript和HTML交互的方式,比如常用的clickblurDOMContentLoadedmousedownmouseup事件等。我们会先订阅这些事件(被观察者),给事件指定事件处理程序,当事件发生时浏览器就会调用相应的处理程序进行处理。可以说,事件模型是观察者模式的具体实现。

事件处理机制

图片引用自官方文档
wx20180430-221404 2x
标准的DOM Events把事件传播分为三个阶段:

  1. 捕获阶段(Capture Phase)
  2. 处理目标阶段(At Target Phase)
  3. 冒泡阶段(Bubbling Phase)
    由于IE在9以上才支持事件捕获,为了兼容性,我们更多让事件在冒泡阶段触发。

指定Event Handler(事件处理程序)的几种方式

Event Handler的名字:on<event>,比如onclick

  1. HTML attribute
    用元素与Event Handler同名的属性来指定,比如像下面这样
<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]);
  • listener是函数,必需
  • useCapture是可选值,默认为false。为true表示listener在捕获阶段触发,为false则表示在冒泡阶段触发
// 新语法
target.addEventListener(type, listener[, options]);
  • listener可以为函数,可以为对象(对象中实现了EventListener接口,即拥有handleEvent方法)
  • options对象为可选,包括如下几个选项
    • capture 默认为false,同上面的useCapture
    • once 默认为false,表示listener最多调用一次,触发后即被自动移除(Firefox50, Chrome55开始支持)
    • passive 默认为false,表示不会禁止事件的默认行为,即不会调用preventDefault方法(Firefox49,Chrome51开始支持)
      passive是为了解决touchwheel事件引起的滚动卡顿。因为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
  1. IE事件处理程序
//添加事件处理程序
target.attachEvent(eventNameWithOn, callback);

elem.attchEvent('onclick', function(){
  // this是window, 不是element
  console.log(this===window);  // true
});
//删除事件处理程序
target.detachEvent(eventNameWithOn, callback);

a. 可以为同一事件添加多个事件处理程序
b. 多个事件处理程序会以与添加顺序相反的顺序执行

参考资料

  1. 《JavaScript高级程序设计》
  2. http://javascript.info/introduction-browser-events
  3. https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
  4. https://www.cnblogs.com/ziyunfei/p/5545439.html
  5. https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener

如何理解工程化?

机会只有一次,它总是留给有准备的人。被生活推着被动的前进,就算好运降临了,也不一定抓得住。

什么是工程化呢?

我的理解

更新于 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;
  }
}

LeetCode 20(有效的括号)

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

这篇文章是对Codebase Overview的翻译,有不当之处敬请指出~~

版本v16.3.1
文档地址:Code Overview

Codebase Overview

这个章节将会概要呈现React代码库的组织,约定以及实现。

如果你想contribute to React,我们希望这个指南可以帮助你更好的做出改变。

我们不一定推荐React的任何约定,因为许多都是由于历史原因而存在的并且会随着时间而变化。

External Dependencies

React几乎没有外部依赖。一个require()通常会指向React自己代码库中的文件。然而,也有一些很少的例外。

React QA list

写在前面: 从头到尾分析一遍React源码太庞杂了,所以决定从点入手来理解源码

下面列出了使用React的过程中遇到的问题或者有疑惑的地方,在此记录,持续更新~~

  1. 为什么使用React.Component创建组件需要自己绑定this,而使用createClass创建组件不需要?
  2. setState为什么有时候表现为同步,有时候表现为异步?
  3. componentWillMount中调用setState为什么不会引起重新渲染?
  4. 为什么异步请求最好在componentDidMount中,而不是在componentWillMount中?
  5. key用在了哪里?
  6. 事件机制
  7. diff算法

学习javascript--继承

每次看javascript的继承都会被绕晕,这次决定静下心来好好捋一捋~

实现继承的思路

  1. 原型链
  2. 属性拷贝

按原型链实现继承的方式有以下几种:

原型链继承

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__连起来的原型链对属性和方法进行一层层查找了
wx20180407-112420 2x

插播完毕~~

这种方式的缺点:

  1. 无法向超类构造函数中传参
  2. 原型链上的属性是所有实例共享的,如果原型链上有引用类型的属性,那么实例上对该属性的更改会影响到原型链上的属性
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']

优点:

  1. 可以向Parent构造函数传值
  2. 每个实例上的引用类型属性有了自己的副本,不会互相影响

缺点:

  1. 方法定义在构造函数中,创建实例的时候方法也会被实例话一次,是没有必要的
  2. 子类无法访问父类原型上的属性和方法

组合继承

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"]

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.