GithubHelp home page GithubHelp logo

joyeuxman.github.io's People

Contributors

joyeuxman avatar

Stargazers

 avatar

Watchers

 avatar

joyeuxman.github.io's Issues

inline-block间距

处理inline-block白间距

<div class="space">
    <span>白间距</span>
    <span>白间距</span>
    <span>白间距</span>
</div>

inline-block水平呈现的元素间,切换显示或者空格分隔的情况下会有白间距,相信你们都有遇到过。究其原因,就是元素标签段之间的空格。

下面展示几种解决方法:

  • font-size:0(推荐)
.space{
  font-size:0;
}
.space span{
  font-size:16px;
}
  • letter-spacing
.space{
  letter-spacing:-3px;//其值的大小与上下文字体和字体大小有关
}
.space span{
  letter-spacing:0px;
}
  • word-spacing
.space{
  word-spacing:-4px;//其值的大小与上下文字体和字体大小有关
}
.space span{
  word-spacing:0px;
}

或者

.space{
  display:inline-table;
  word-spacing:-999px;//其值可以无限小,也不会出现字体重叠
}
.space span{
  word-spacing:0px;
}
  • margin负值
.space span{
  margin-right:-3px;//其值的大小与上下文字体和字体大小有关
}
  • 借助HTML注释移除标签之间的空格
<div>
  <span>白间距</span><!--
  --><span>白间距</span><!--
  --><span>白间距</span>  
</div>

发布和更新安装包

发布一个安装包

  npm publish <package>

更新自己的安装包

  1. 在本地更新这个包的版本 ,使用命令
    npm version   <update_type>
    
  2. 提交到远端npm中,使用命令
    npm publish
    

例子:

包名:lgh 版本:1.0.0

npm view lgh versions
['1.0.0']

本地更新

  • 打补丁 1.0.0 --> 1.0.1

    npm version patch
    
    
  • 小修小改 1.0.0 --> 1.1.0

    npm version minor
    
    
  • 大改 1.0.0 --> 2.0.0

    npm version major
    
    

同步远端

npm publish

在HTML中使用JS

在HTML中使用JS

向HTML页面插入JS的主要方法就是使用<script>元素

<script>元素属性介绍

属性名 是否可选 详细解释
async 可选 表示应该立即下载脚本,但不应妨碍页面中的其它操作,比如下载其他资源或者等待加载其它脚本。只对外部脚本文件有效
charset 可选 表示通过src属性指定的代码的字符集。由于大多数浏览器会忽略它的值,因此这个属性很少有人用
defer 可选 表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本有效。IE7及更早版本对嵌入脚本也支持这个属性
language 已废弃 原来用于表示编写代码使用的脚本语言(JavaScript、JavaScript1.2、VBScript)。大多数浏览器会忽略这个属性,因此也没有必要使用了
src 可选 表示包含要执行代码的外部文件
type 可选 可以看成language的替代属性;表示编写代码使用的脚本语言的内容类型(也成MIME类型)。虽然text/javascripttext/ecmascript都已经不被推荐使用,但人们一直以来使用的都还是text/JavaScript。实际上,服务器在传送JavaScript文件时使用的MIME类型通常是application/x-javascript,但在type中设置这个值却可能导致脚本被忽略。另外,在非IE浏览器中还可以使用以下值:application/javascriptapplication/ecmascript。默认值是text/javascript

无论script标签如何包裹代码,只要不存在deferasync属性,浏览器就会按照<script>元素在页面中出现的先后顺序对他们进行依次解析。换句话说,在第一个script元素包含的代码解析完成后,第二个script包含的代码才会被解析,然后是第三个、第四个...

<script>设置type="text/template"或者type="text/html"的妙用

在js里面,经常需要使用js往页面中插入html内容,如果html很短还好说,但是遇到描述里面的这么大段,直接用字符串存储会很困难,因为不光要处理单引号,还需要很多「+」号把字符串一个个连接起来,十分的不方便。

<script>设置type="text/template",标签里面的内容不会被执行,也不会显示在页面上,但是可以在另一个script里面通过获取,然后再通过相关的模板工具进行处理,插入到页面中。这样就把大段的HTML操作从js里面分离开了。

延迟脚本

<script>中设置defer属性,相当于告诉浏览器立即下载,但延迟执行。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>延迟脚本</title>
  <script src="test1.js" defer="defer"></script>
  <script src="test2.js" defer="defer"></script>
</head>
<body>
  <!-- 这里放内容 -->
</body>
</html>

上面的例子中,虽然我们将<script>元素放在了文档的<head>中,但其中包含的脚本将延迟到浏览器遇到</html>标签后再执行。HTML5规范要求脚本按照他们出现的先后顺序执行,因此第一个延迟脚本会先于第二个延迟脚本执行,而这两个脚本都会先于DOMContentLoaded事件执行。但在现实中,延迟脚本并不一定会按照顺序执行,也不一定在DOMContentLoaded事件触发之前执行。

异步脚本

<script>中设置async属性,相当于告诉浏览器立即下载脚本文件,但延迟执行。与defer不同的是,标记为async的脚本并不保证按照他们的先后顺序执行。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>异步脚本</title>
  <script src="test1.js" async="async"></script>
  <script src="test2.js" async="async"></script>
</head>
<body>
  <!-- 这里放内容 -->
</body>
</html>

上面例子中,第二个脚本文件可能会在第一个脚本文件之前执行。因此,确保两者之间互不依赖非常重要。指定async属性的目的是不让页面等待两个脚本下载和执行,从而异步加载页面其它内容。为此,建议异步脚本不要在加载期间修改DOM。

参考文章理解同步,异步和延迟脚本

更新安装包

更新本地安装的npm包

  1. 进入package.json 的目录
  2. 执行npm outdated 列出需要更新的报的信息(名称、当前版本号、想要的版本号、最新的版本号)
  3. 更新
    • 全部更新 npm update
    • 部门更新

      如果你只是想更新某一个包到指定的版本,那么你可以直接通过命令修改。如果这个包在package.json 的dependencies 节点下,使用:

        npm install [email protected] --save
      
      如果在package.json 的devDependencies下使用:
        npm install [email protected] --save-dev
      
  4. 验证是否更新完毕
  npm outdated

更新全局包

  1. 检查全局包是否有更新
  npm outdated -g --depth=0
  1. 更新

    • 全部更新
      npm update -g 
    
    • 部门更新
      npm install webpack -g
    
  2. 验证是否更新完毕

  npm outdated

你所不知道的forEach循环

forEach

循环时,在不使用函数第三个参数的情况下,遍历的元素若为基本类型(string,number,boolean),不会对原数组做出修改;遍历的元素若为引用类型(Object,Array),会对原数组做出修改。

请看示例:

const obj = {
  arr:['a','b','c'],
}
console.log('循环之前arr===',obj.arr);
obj.arr.forEach(item=>item = item + item);
console.log('循环之后arr===',obj.arr);

//输出如下:
循环之前obj.arr===',['a','b','c']
循环之后obj.arr===',['a','b','c']
const obj = {
  arr:[{name:'张三'},{name:'张三'},{name:'张三'}],
}
console.log('循环之前arr===',obj.arr);
obj.arr.forEach(item=>item.name = '李四');
console.log('循环之后arr===',obj.arr);

//输出如下:
循环之前obj.arr===',[{name:'李四'},{name:'李四'},{name:'李四'}]
循环之后obj.arr===',[{name:'李四'},{name:'李四'},{name:'李四'}]

CSS层叠

层叠上下文

层叠水平

普通元素的层叠水平优先由层叠上下文决定,因此,层叠水平的比较只有在当前层叠上下文元素中才有意义。

层叠顺序 stacking order

表示元素发生层叠时候有着特定的垂直显示顺序
层叠上下文和层叠水平是概念,而这里的层叠顺序是规则。

层叠准则

  • 谁大谁上:当具有明显的层叠水平标示的时候,如识别的z-indx值,在同一个层叠上下文领域,层叠水平值大的那一个覆盖小的那一个。通俗讲就是官大的压死官小的。
  • 后来居上:当元素的层叠水平一致、层叠顺序相同的时候,在DOM流中处于后面的元素会覆盖前面的元素。

柯里化函数

    function currying(fn) {
      var slice = Array.prototype.slice;
      var _args = slice.call(arguments, 1);
      return function () {
        var _inargs = slice.call(arguments);
        return fn.apply(null, _args.concat(_inargs));
      }
    }

    function curry(fn) {
      const _args = [...arguments].slice(1);
      return function () {
        const _inargs = [...arguments];
        return fn.apply(null, [..._args, ..._inargs]);
      }
    }

    const test = function () {
      console.log('in test fn arguments ===', arguments);
    }
    currying(test, 1, 2, 3, 4)(5, 6);
    curry(test, 1, 2, 3, 4)(5, 6);



    var add = function () {
      var _args = arguments;
      return function () {
        if (!arguments.length) {
          var sum = 0;
          for (var i = 0, c; c = _args[i++];) {
            sum += c;
          }
          console.log('sum===', sum);
          return sum;
        } else {
          Array.prototype.push.apply(_args, arguments);
          return arguments.callee;
        }
      }
    }
    add(1)(2)(3)(4)();

    const curry_delay = function (fn) {
      var _args = [];
      return function cb() {
        if (arguments.length === 0) {
          return fn.apply(this, _args);
        }
        Array.prototype.push.apply(_args, arguments);
        return cb;
      }
    }
    function sum() {
      console.log('in sum fn arguments===', arguments);
      const _args = [...arguments];
      var sum = 0;
      for (var i = 0; i < _args.length; i++) {
        sum += _args[i];
      }
      console.log('sum===', sum);
      return sum;
    }
    curry_delay(sum)(1)(2)(3)();

    Function.prototype.bind = function(context){
      console.log('this===调用bind的函数',this);//sum
      console.log('context===',context);//[1,2,3,4]

      var _this = this;
      var _args = Array.prototype.slice.call(arguments,1);
      return function(){
        return _this.apply(context,_args.concat(Array.prototype.slice.call(arguments)));
      }
    }
    const arr = [1,2,3,4];
    sum.bind(arr); // ==> sum.apply(arr,_args.concat(Array.prototype.slice.call(arguments)))

理解JS中的null、undefined、NaN

null

null表示一个指向不存在或无效的对象或地址引用。

null的类型

typeof null       //object

null与Boolean

!null             //true
null == false     //false 此处为坑,码友莫入坑!
null === false    //false
null == true      //false
null === true     //false

null与Number

null的值会被转换为0

null + 5           //5
null * 23          //0

undefined

undefined表示一个没有被定义的值。

undefined的类型

typeof undefined       //undefined

undefined与Boolean

!undefined             //true
undefined == false     //false 此处为坑,码友莫入坑!
undefined === false    //false
undefined == true      //false
undefined === true     //false

undefined与Number

undefinedNumber类型的数值进行算术运算会得到NaN

undefined + 5           //NaN
undefined * 23          //NaN

null vs undefined

相同点

  • 当被否定时,两者的值都是true
  • 代表一些不存在的东西

不同点

  • null表示无,完全不存在;undefined表示变量未定义
  • null是一个对象,而undefined有自己的数据类型(undefined
  • 在基本算术运算中,null被视为0,而undefined返回NaN

需要指出的是:

undefined == null         //true
undefined === null        //false
!undefined == !null       //true
!undefined === !null      //true

NaN

NaN表示一个非数字的值。

NaN的类型

typeof NaN       //number 此处为坑,码友莫入坑!

NaN与Boolean

!NaN             //true
NaN == false     //false 此处为坑,码友莫入坑!
NaN === false    //false
NaN == true      //false
NaN === true     //false

NaN自身的比较

NaN == NaN      //false
NaN === NaN     //false
isNaN(NaN)      //true
isNaN(23)       //false

原型

对象的三大特性

  • 继承
  • 多态
  • 原型

创建对象的几种方法

对象的分类

对象分为普通对象object和函数对象function。凡是通过new Function()创建的对象都是函数对象,其余都是普通对象。以下示例中f1f2FunctionObject,归根结底都是通过new Function()的方式进行创建的。

var o1 = {}; 
var o2 =new Object();
var o3 = new f1();
var o4 = new Array();

function f1(){}; 
var f2 = function(){};
var f3 = new Function('str','console.log(str)');

console.log(typeof Object); //function 
console.log(typeof Function); //function  

console.log(typeof f1); //function 
console.log(typeof f2); //function 
console.log(typeof f3); //function   

console.log(typeof o1); //object 
console.log(typeof o2); //object 
console.log(typeof o3); //object
console.log(typeof o4); //object 

构造函数、实例

//Person为构造函数
function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function() { alert(this.name) } 
}

//person1、person2为实例 
//实例 = new + 构造函数
var person1 = new Person('Zaxlct', 28, 'Software Engineer');
var person2 = new Person('Mick', 23, 'Doctor');

console.log(person1.constructor === Person); //true
console.log(person2.constructor === Person); //true

结论: 实例.constructor = 构造函数

原型对象prototype

在JS中定义对象(函数对象或者普通对象)时,对象中都会包含一些预先定义好的属性。其中每个函数对象都有一个prototype属性,该属性是一个普通对象,指向函数的原型对象。原型对象中也会有个预先定义好的属性constructor,该属性(是一个指针)指向prototype属性所在的函数。

结论:

构造函数.prototype.constructor === 构造函数

构造函数.prototype === 构造函数.原型对象 === 一个特殊实例

通俗来讲,原型对象是构造函数的一个实例。

特殊情况(前面说的函数对象都有prototype属性)

Function.prototype是一个函数对象,它没有原型对象。

console.log(typeof Function.prototype)      //function
console.log(typeof Function.prototype.prototype) //undefined

隐式原型__proto__

JS在创建对象(不管是普通对象还是函数对象)的时候,都会有__proto__的内置属性,用于指向创建它的构造函数的原型对象。
请参考下图:

结论:A.__proto__ === A.constructor.prototype


可以表现为:

  • 实例.__proto__ === 构造函数.prototype
    实例.constructor = 构造函数
    
  • 函数对象.__proto__ === Function.prototype
    //Function.prototype是一个空函数(Empty function)
    //所有构造函数都继承了Function.prototype的属性及方法。如call、apply、bind
    //自定义的函数对象和以下JS内置对象的构造函数都为Function
    Person/Number/Boolean/String/Array/RegExp/Error/Date/Object.constructor === Function
    Function.constructor === Function //此处为坑,码友莫入!
    Function.prototype === Function.constructor.prototype
    

实践环节

问题

  1. person1.__proto__ 是什么?
  2. Person.__proto__ 是什么?
  3. Person.prototype.__proto__ 是什么?
  4. Object.__proto__ 是什么?
  5. Object.prototype__proto__ 是什么?
  6. Function.prototype.__proto__是什么?

答案:

  1. person1.__proto__ === Person.prototype
  2. Person.__proto__ === Function.prototype
  3. Person.prototype.__proto__ === Object.prototype
  4. Object.__proto__ === Function.prototype
  5. Object.prototype.__proto__ === null此处为坑,码友莫入!
    由此可见null处于原型链的顶端。
  6. Function.prototype.__proto__ === Object.prototype

疑问解答:

  • Function.prototype为什么是函数对象???
 var A = new Function(); 
 Function.prototype = A;
 //结合起来就是: 
 // Function.prototype === A === new Function() === 空函数(empty function)
 /由于其由 new Function()创建,所以为函数对象。
  • Object.__proto__ === Function.prototype // true
Object 是函数对象,是通过new Function()创建的,所以Object.__proto__指向Function.prototype。
  • Function.__proto__ === Function.prototype // true
Function 也是函数对象,也是通过new Function()创建,所以Function.__proto__指向Function.prototype。
  • Function.prototype.__proto__ === Object.prototype //true
其实这一点我也有点困惑,不过也可以试着解释一下。
Function.prototype是个函数对象,理论上他的__proto__应该指向 Function.prototype,就是他自己,自己指向自己,没有意义。
JS一直强调万物皆对象,函数对象也是对象,给他认个祖宗,指向Object.prototype。Object.prototype.__proto__ === null,保证原型链能够正常结束。

总结:

  • 原型和原型链是JS实现继承的一种模型。
  • 原型链的形成是真正是靠__proto__ 而非prototype

CSS盒模型

  • display:block会让元素尺寸扩充。自适应容器元素的可用宽度显示。
  • display:inline-block会让元素尺寸收缩。若元素自身定义了宽度,则收缩为元素自身宽度;若元素自身没有定义宽度,且元素下有子元素,则收缩为子元素的宽度,若没有子元素则收缩为。当子元素自身宽度超出元素自身宽度时,会发生宽度溢出。高度收缩暂时不明。
    let element_width;//元素显示宽度
    const self_width;//元素自身宽度
    const self_child;//子元素
    const self_child_width;////子元素自身宽度
    //设置样式
    display:inline-block;
    
    element_width = self_width || self_child_width || 0;
    
  • display:table-cell会让元素尺寸收缩。表现行为类似display:inline-block,所不同的是,当元素自身宽度超出容器宽度时,并不会发生宽度溢出。

清除浮动的通用类语句:

.clearfix{
  zoom:1;
}
.clearfix:after{
  content:'';
  display:table;
  clear:both;
}

创建BFC元素的最佳实践:

.BFC{
  display:table-cell;
  width:9999px;
}

BFC的定义

创建BFC的方式

BFC的使用场景

JS常用函数

/**
 * 收集JS常用函数
 * @author lighl
 */

const utils = {};

/**
 * 获取一段区间内的随机整数
 * @param {Number} max 获得随机整数的最大值
 * @param {Number} min 获得随机整数的最小值
 */
utils.getRandomInt = (max, min = 0) => {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * url => 分层级的URL数组
 * /userinfo/2144/id => ['/userinfo','/useinfo/2144,'/userindo/2144/id']
 * @param {String} url URL地址
 */
utils.urlToList = (url) => {
  // 添加filter,过滤不存在的url,如:'/'  ''  '/a/b//c'等
  const urlList = url.split('/').filter(i => i);
  return urlList.map((item, index) => {
    return `/${urlList.slice(0, index + 1).join('/')}`;
  })
}

/**
 * 判断路径地址是否是一个完整的URL地址
 * '/userinfo/2144/id'  => false
 * 'https://www.baidu.com/s?ie=UTF-8&wd=input%20onkeydown'  => true
 * @param {String} path 路径地址
 */
utils.isUrl = (path) => {
  const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/g;
  return reg.test(path);
}

utils.isHTMLElement = (node) => {
  return typeof node === 'object' && node !== null && node.nodeType && node.nodeName;
}

/**
 * 剔除数组中的假值和重复的值
 * @param {Array} arr 
 * eg: [undefined,1,0,1,2,3,5,6,3] --> [1, 2, 3, 5, 6]
 */
utils.arrryToSingleTrue = (arr) => {
  return arr.filter((item, index, self) => {
    return item && self.indexOf(item) === index;
  })
}

utils.getParams = (url, param) => {
  if (!/\?/.test(url)) {
    return null;
  }
  var search = url.split('?')[1];
  var array = search.split('&');
  for (let i = 0; i < array.length; i++) {
    var tem = array[i].split('=');
    if (tem[0] === param) {
      return decodeURIComponent(tem[1]);
    }
    return null;
  }
}

utils.getRandomColor = ()=>{
  return '#' + (Math.random() + '').slice(-6)
}


export default utils;

React 高阶组件

概念

高阶函数:接受函数作为输入,输出另一个函数的一类函数。

高阶组件:通过包裹被传入的React组件,经过一系列处理,最终返回一个相对增强的React组件,供其它组件调用。

//声明高阶组件
高阶组件(低阶组件的配置信息){
	return 低阶组件 =>{
    		return 低阶组件和低阶组件的配置信息的结合体
	}
}
//调用高阶组件
高阶组件(低阶组件的配置信息)(低阶组件)

高阶组件的使用场景

  • 抽象逻辑,复用代码
  • 劫持渲染
  • 抽象和更改state
  • 更改props

实现高阶组件的方式

属性代理(props-proxy)

属性代理:通过做一些操作,将被包裹组件(Demo)的props()和新生成的props一起传递给此组件。

示例:

function withHeader(WrappedComponent) {
  return class HOC extends React.Component {
    static defaultProps = {
      desc_1: '来自增强组价的desc,增加props方式一',
    }
    static displayName = `HOC(${getDisplayName(WrappedComponent)})`;
    constructor(props) {
      super(props);
      this.state = {
        name_0: "HOC",
      }
    }

    render() {
      console.dir(WrappedComponent);//f Demo(props)
      console.log('this===', this);//HOC-->this.props-->{desc_1:...}
      return (
        <div>
          <h3>标题</h3>
          <WrappedComponent {...this.props} enhance={'来自增强组件的enhance,增加props的方式二'} />
        </div>
      )
    }
  }
}


@withHeader
export default class Demo extends React.Component {
  static defaultProps = {
    desc_0: '来自包裹组件的desc',
  }
  constructor(props) {
    super(props);
    this.state = {
      name_1: 'Demo',
    }
  }
  render() {
    console.log('Demo this===', this);//Demo-->this.props-->{desc_0:...,desc_1:...,enhance:...}
    return (
      <div>
        我是一个普通组件
      </div>
    )
  }
}

总结:withHeader是一个高阶函数,接受Demo组件,返回一个增强的Demo组件。本示例中,增加了一个标题,同时,增强了属性。
在Demo组件类实例化后的this中,其props属性中含有desc_0(来自增强组件)、desc_1(来自包裹组件自身),当被包裹组件和增强组价的属性名称相同时,包裹组件的属性会覆盖掉增强组件的同名属性。

###反向继承(inheritance inversion)

React生命周期实例

class Child extends React.Component {
static defaultProps = {
//1、设置子组件默认props,会作为constructor(props)参数的一部分属性,其余的部门属性是在其使用该组件的位置添加的
name: 'child',
age: 10,
}
constructor(props) {
// 注意 props === this.props
super(props);
this.state = { child: "child" };
console.log('2、child constructor');
// console.log('2、child props===', props);
// console.log('2、child this.state===', this.state);
// console.log('2、child this.props===', this.props);
}

componentWillMount() {
  console.log('3、child componentWillMount');
  // console.log('3、child this.state===', this.state);
  // console.log('3、child this.props===', this.props);
}

componentDidMount() {
  console.log('5、child componentDidMount');
  // console.log('5、child this.state===', this.state);
  // console.log('5、child this.props===', this.props);
}

componentWillReceiveProps(nextProps) {
  // nextProps为父组件更新后传递过来的属性
  // this.props为父组件更新之前传递过来的属性
  console.log('6、child componentWillReceiveProps');
  // console.log('6、child nextProps ===', nextProps);
  // console.log('6、child this.state===', this.state);
  // console.log('6、child this.props ===', this.props);
}

shouldComponentUpdate(nextProps, nextState) {
  // nextProps为父组件更新后传递过来的属性
  // this.props为父组件更新之前传递过来的属性
  console.log('7、child shouldComponentUpdate');
  // console.log('7、child nextProps ===', nextProps);
  // console.log('7、child nextState ===', nextState);
  // console.log('7、child this.state===', this.state);
  // console.log('7、child this.props ===', this.props);
  if (nextProps.number < 5) return true;
  return false
}

componentWillUpdate(nextProps, nextState) {
  // nextProps为父组件更新后传递过来的属性
  // this.props为父组件更新之前传递过来的属性
  console.log('8、child componentWillUpdate');
  // console.log('8、child nextProps ===', nextProps);
  // console.log('8、child nextState ===', nextState);
  // console.log('8、child this.state===', this.state);
  // console.log('8、child this.props ===', this.props);
}

componentDidUpdate(prevProps, prevState) {
  // this.props为父组件更新后传递过来的属性
  // prevProps为父组件更新之前传递过来的属性
  console.log('9、child componentDidUpdate');
  // console.log('9、child prevProps ===', prevProps);
  // console.log('9、child prevState ===', prevState);
  // console.log('9、child this.state===', this.state);
  // console.log('9、child this.props ===', this.props);
}

componentWillUnmount() {
  console.log('10、child componentWillUnmount');
  // console.log('10、child this.state===', this.state);
  // console.log('10、child this.props ===', this.props);
}

// static getDerivedStateFromProps() {
//   console.log('child getDerivedStateFromProps');
//   return null;
//   return { time: 1 }
// }
// getSnapshotBeforeUpdate() {
//   console.log('child getSnapshotBeforeUpdate');
// }
// componentDidCatch() {
//   console.log('child componentDidCatch');
// }

render() {
  console.log('4、child render');
  // console.log('4、child this.props ===', this.props);
  // console.log('4、child this.state ===', this.state);
  return (
    <p>{this.props.number}</p>
  )
}

}

class Parent extends React.Component {
static defaultProps = {
//1、设置父组件默认props,会作为constructor(props)参数的一部分属性,其余的部门属性是在其使用该组件的位置添加的
name: 'parent',
age: 36
};

constructor(props) {
  // 注意 props === this.props
  super(props);
  this.state = { number: 0, parent: "parent" };
  console.log('2、parent constructor');
  // console.log('2、parent props===', props);
  // console.log('2、parent this.state===', this.state);
  // console.log('2、parent this.props===', this.props);
}

// static getDerivedStateFromProps() {
//   console.log('parent getDerivedStateFromProps');
//   return { time: 1 }
// }
// getSnapshotBeforeUpdate() {
//   console.log('parent getSnapshotBeforeUpdate');
// }
// componentDidCatch() {
//   console.log('parent componentDidCatch');
// }
componentWillMount() {
  console.log('3、parent componentWillMount');
  // console.log('3、parent this.state===', this.state);
  // console.log('3、parent this.props===', this.props);
}

componentDidMount() {
  console.log('5、parent componentDidMount');
  // console.log('5、parent this.state===', this.state);
  // console.log('5、parent this.props===', this.props);
}

componentWillReceiveProps(nextProps) {
  console.log('6、parent componentWillReceiveProps');
  // console.log('6、parent nextProps ===', nextProps);
  // console.log('6、parent this.state===', this.state);
  // console.log('6、parent this.props ===', this.props);
}

shouldComponentUpdate(nextProps, nextState) {
  // this.setState 会直接触发该生命周期函数
  // nextProps、nextState均为更新后的属性和状态
  // this.props和this.state均为更新之前的属性和状态
  console.log('7、parent shouldComponentUpdate');
  // console.log('7、parent nextProps ===', nextProps);
  // console.log('7、parent nextState ===', nextState);
  // console.log('7、parent this.state===', this.state);
  // console.log('7、parent this.props ===', this.props);
  if (nextState.number < 15) return true;
  return false
}

componentWillUpdate(nextProps, nextState) {
  // nextProps、nextState均为更新后的属性和状态
  // this.props和this.state均为更新之前的属性和状态
  console.log('8、parent componentWillUpdate');
  // console.log('8、parent nextProps ===', nextProps);
  // console.log('8、parent nextState ===', nextState);
  // console.log('8、parent this.state===', this.state);
  // console.log('8、parent this.props ===', this.props);
}

componentDidUpdate(prevProps, prevState) {
  // this.props为更新后的属性
  // prevProps为更新之前的属性
  console.log('9、parent componentDidUpdate');
  // console.log('9、parent prevProps ===', prevProps);
  // console.log('9、parent prevState ===', prevState);
  // console.log('9、parent this.state===', this.state);
  // console.log('9、parent this.props ===', this.props);
}
componentWillUnmount() {
  console.log('10、parent componentWillUnmount');
  // console.log('10、parent this.state===', this.state);
  // console.log('10、parent this.props ===', this.props);
}
handleClick = () => {
  this.setState({
    number: this.state.number + 1
  })
};

render() {
  // this.props和this.state均为更新后的属性和状态
  console.log('4、parent render');
  // console.log('4、parent this.props ===', this.props);
  // console.log('4、parent this.state ===', this.state);
  return (
    <div>
      <p>{this.state.number}</p>
      <button onClick={this.handleClick}>+</button>
      {this.state.number < 10 ? <Child number={this.state.number} /> : null}
    </div>
  )
}

}

export default Parent;

/*
装配期(简化版)
2、parent constructor
3、parent componentWillMount
4、parent render
2、child constructor
3、child componentWillMount
4、child render
5、child componentDidMount
5、parent componentDidMount

*/

/*
更新期(简化版) this.setState触发
7、parent shouldComponentUpdate
8、parent componentWillUpdate
4、parent render
6、child componentWillReceiveProps
7、child shouldComponentUpdate
8、child componentWillUpdate
4、child render
9、child componentDidUpdate
9、parent componentDidUpdate
*/

/*
更新期(简化版) 按F5刷新页面触发 ???为什么重复了两遍过程
2、parent constructor
3、parent componentWillMount
4、parent render
2、child constructor
3、child componentWillMount
4、child render
5、child componentDidMount
5、parent componentDidMount
6、parent componentWillReceiveProps
7、parent shouldComponentUpdate
8、parent componentWillUpdate
4、parent render
6、child componentWillReceiveProps
7、child shouldComponentUpdate
8、child componentWillUpdate
4、child render
9、child componentDidUpdate
9、parent componentDidUpdate
6、parent componentWillReceiveProps
7、parent shouldComponentUpdate
8、parent componentWillUpdate
4、parent render
6、child componentWillReceiveProps
7、child shouldComponentUpdate
8、child componentWillUpdate
4、child render
9、child componentDidUpdate
9、parent componentDidUpdate
*/

/*
装配期
2、parent constructor
2、parent props=== {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
2、parent this.state=== {number: 0, parent: "parent"}
2、parent this.props=== {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
3、parent componentWillMount
3、parent this.state=== {number: 0, parent: "parent"}
3、parent this.props=== {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
4、parent render
4、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
4、parent this.state === {number: 0, parent: "parent"}
2、child constructor
2、child props=== {number: 0, name: "child", age: 10}
2、child this.state=== {child: "child"}
2、child this.props=== {number: 0, name: "child", age: 10}
3、child componentWillMount
3、child this.state=== {child: "child"}
3、child this.props=== {number: 0, name: "child", age: 10}
4、child render
4、child this.props === {number: 0, name: "child", age: 10}
4、child this.state === {child: "child"}
5、child componentDidMount
5、child this.state=== {child: "child"}
5、child this.props=== {number: 0, name: "child", age: 10}
5、parent componentDidMount
5、parent this.state=== {number: 0, parent: "parent"}
5、parent this.props=== {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
*/

/*
更新期 点击增加按钮,触发setState
7、parent shouldComponentUpdate
7、parent nextProps === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
7、parent nextState === {number: 1, parent: "parent"}
7、parent this.state=== {number: 0, parent: "parent"}
7、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
8、parent componentWillUpdate
8、parent nextProps === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
8、parent nextState === {number: 1, parent: "parent"}
8、parent this.state=== {number: 0, parent: "parent"}
8、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
4、parent render
4、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
4、parent this.state === {number: 1, parent: "parent"}
6、child componentWillReceiveProps
6、child nextProps === {number: 1, name: "child", age: 10}
6、child this.state=== {child: "child"}
6、child this.props === {number: 0, name: "child", age: 10}
7、child shouldComponentUpdate
7、child nextProps === {number: 1, name: "child", age: 10}
7、child nextState === {child: "child"}
7、child this.state=== {child: "child"}
7、child this.props === {number: 0, name: "child", age: 10}
8、child componentWillUpdate
8、child nextProps === {number: 1, name: "child", age: 10}
8、child nextState === {child: "child"}
8、child this.state=== {child: "child"}
8、child this.props === {number: 0, name: "child", age: 10}
4、child render
4、child this.props === {number: 1, name: "child", age: 10}
4、child this.state === {child: "child"}
9、child componentDidUpdate
9、child prevProps === {number: 0, name: "child", age: 10}
9、child prevState === {child: "child"}
9、child this.state=== {child: "child"}
9、child this.props === {number: 1, name: "child", age: 10}
9、parent componentDidUpdate
9、parent prevProps === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
9、parent prevState === {number: 0, parent: "parent"}
9、parent this.state=== {number: 1, parent: "parent"}
9、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
*/

/*
更新期 按F5刷新页面
2、parent constructor
2、parent props=== {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
2、parent this.state=== {number: 0, parent: "parent"}
2、parent this.props=== {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
3、parent componentWillMount
3、parent this.state=== {number: 0, parent: "parent"}
3、parent this.props=== {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
4、parent render
4、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
4、parent this.state === {number: 0, parent: "parent"}
2、child constructor
2、child props=== {number: 0, name: "child", age: 10}
2、child this.state=== {child: "child"}
2、child this.props=== {number: 0, name: "child", age: 10}
3、child componentWillMount
3、child this.state=== {child: "child"}
3、child this.props=== {number: 0, name: "child", age: 10}
4、child render
4、child this.props === {number: 0, name: "child", age: 10}
4、child this.state === {child: "child"}
5、child componentDidMount
5、child this.state=== {child: "child"}
5、child this.props=== {number: 0, name: "child", age: 10}
5、parent componentDidMount
5、parent this.state=== {number: 0, parent: "parent"}
5、parent this.props=== {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
6、parent componentWillReceiveProps
6、parent nextProps === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
6、parent this.state=== {number: 0, parent: "parent"}
6、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
7、parent shouldComponentUpdate
7、parent nextProps === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
7、parent nextState === {number: 0, parent: "parent"}
7、parent this.state=== {number: 0, parent: "parent"}
7、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
8、parent componentWillUpdate
8、parent nextProps === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
8、parent nextState === {number: 0, parent: "parent"}
8、parent this.state=== {number: 0, parent: "parent"}
8、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
4、parent render
4、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
4、parent this.state === {number: 0, parent: "parent"}
6、child componentWillReceiveProps
6、child nextProps === {number: 0, name: "child", age: 10}
6、child this.state=== {child: "child"}
6、child this.props === {number: 0, name: "child", age: 10}
7、child shouldComponentUpdate
7、child nextProps === {number: 0, name: "child", age: 10}
7、child nextState === {child: "child"}
7、child this.state=== {child: "child"}
7、child this.props === {number: 0, name: "child", age: 10}
8、child componentWillUpdate
8、child nextProps === {number: 0, name: "child", age: 10}
8、child nextState === {child: "child"}
8、child this.state=== {child: "child"}
8、child this.props === {number: 0, name: "child", age: 10}
4、child render
4、child this.props === {number: 0, name: "child", age: 10}
4、child this.state === {child: "child"}
9、child componentDidUpdate
9、child prevProps === {number: 0, name: "child", age: 10}
9、child prevState === {child: "child"}
9、child this.state=== {child: "child"}
9、child this.props === {number: 0, name: "child", age: 10}
9、parent componentDidUpdate
9、parent prevProps === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
9、parent prevState === {number: 0, parent: "parent"}
9、parent this.state=== {number: 0, parent: "parent"}
9、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
6、parent componentWillReceiveProps
6、parent nextProps === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
6、parent this.state=== {number: 0, parent: "parent"}
6、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
7、parent shouldComponentUpdate
7、parent nextProps === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
7、parent nextState === {number: 0, parent: "parent"}
7、parent this.state=== {number: 0, parent: "parent"}
7、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
8、parent componentWillUpdate
8、parent nextProps === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
8、parent nextState === {number: 0, parent: "parent"}
8、parent this.state=== {number: 0, parent: "parent"}
8、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
4、parent render
4、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
4、parent this.state === {number: 0, parent: "parent"}
6、child componentWillReceiveProps
6、child nextProps === {number: 0, name: "child", age: 10}
6、child this.state=== {child: "child"}
6、child this.props === {number: 0, name: "child", age: 10}
7、child shouldComponentUpdate
7、child nextProps === {number: 0, name: "child", age: 10}
7、child nextState === {child: "child"}
7、child this.state=== {child: "child"}
7、child this.props === {number: 0, name: "child", age: 10}
8、child componentWillUpdate
8、child nextProps === {number: 0, name: "child", age: 10}
8、child nextState === {child: "child"}
8、child this.state=== {child: "child"}
8、child this.props === {number: 0, name: "child", age: 10}
4、child render
4、child this.props === {number: 0, name: "child", age: 10}
4、child this.state === {child: "child"}
9、child componentDidUpdate
9、child prevProps === {number: 0, name: "child", age: 10}
9、child prevState === {child: "child"}
9、child this.state=== {child: "child"}
9、child this.props === {number: 0, name: "child", age: 10}
9、parent componentDidUpdate
9、parent prevProps === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
9、parent prevState === {number: 0, parent: "parent"}
9、parent this.state=== {number: 0, parent: "parent"}
9、parent this.props === {match: {…}, location: {…}, history: {…}, staticContext: undefined, routerData: {…}, …}
*/

/*
v16.3
装配期
2、parent constructor
parent getDerivedStateFromProps
4、parent render
2、child constructor
child getDerivedStateFromProps
4、child render
5、child componentDidMount
5、parent componentDidMount
*/

卸载安装包

卸载安装包

  • 如需删除 node_modules 目录下面的包(package)

      npm uninstall <package>
    
  • 如需删除 package.json 中 dependencies 对象下的包(package)

      npm uninstall <package> --save
    
  • 如需删除 package.json 中 devDependencies 对象下的包(package)

      npm uninstall <package> --save-dev
    
  • 如需删除全局包(package)

      npm uninstall <package> -g
    

Git远程仓库地址修改后你需要如何做

Git远程仓库地址修改后你需要如何做

  1. 查看本地现在的远程仓库地址

     git remote -v 
    
     //output
     origin http://xxx.xxx.old.git
    
  2. 删除本地现在的远程仓库地址

      git remote rm origin
    
  3. 验证本地现在的远程仓库地址是否删除成功

      git remote -v  
    
      //没有出现origin,表示删除成功
    
  4. 将新的远程仓库地址添加到本地

      git remote add origin http://xxx.xxx.new.git
    
  5. 验证新的的远程仓库地址是否添加成功

      git remote -v   
    
      //output
      origin http://xxx.xxx.new.git //出现origin内容表示添加成功
    
  6. 获取新的远程分支

      git fetch origin   
    
      //output
      From http://xxx.xxx.new.git
      * [new branch]        newBranch1     -> origin/newBranch1
      * [new branch]        newBranch2     -> origin/newBranch2
      * [new branch]        newBranch3     -> origin/newBranch3
    
  7. 查看所有的分支(本地旧分支+远程仓库新分支)

      git branch -a   
    
      //output
      oldBranch1
      oldBranch2
      oldBranch3
      remotes/origin/newBranch1
      remotes/origin/newBranch2
      remotes/origin/newBranch3
    
  8. 依次删除本地旧分支

      git branch -d oldBranch1  
      git branch -d oldBranch2
      git branch -d oldBranch3
    
      //or 强制删除
      git branch -D oldBranch1  
      git branch -D oldBranch2
      git branch -D oldBranch3
    
      // output
      Deleted branch oldBranch1
      Deleted branch oldBranch2
      Deleted branch oldBranch3
    
  9. 查看删除本地旧分支是否成功

      git branch -a   
    
      //output   success
      remotes/origin/newBranch1
      remotes/origin/newBranch2
      remotes/origin/newBranch3
    
  10. 切换远程新分支b并创建对应的本地分支

      // 格式 git checkout -b 本地分支名 远程分支名
      git checkout -b newBranch1 remotes/origin/newBranch1
      git checkout -b newBranch2 remotes/origin/newBranch2
      git checkout -b newBranch3 remotes/origin/newBranch3
    
      //output   success
      Switched to a new branch 'uat'
      Branch 'newBranch1' set up to track remote branch 'newBranch1' from 'origin'.
      ...
    
  11. 查看本地新分支是否创建成功

      git branch -a   
    
      //output   success
      newBranch1
      newBranch2
      newBranch3
      remotes/origin/newBranch1
      remotes/origin/newBranch2
      remotes/origin/newBranch3
    
  12. 拉取最新代码

      git pull   
    

React Components, Elements, and Instances

React Components, Elements, and Instances

组件、它们的实例和元素之间的区别使许多初学者感到困惑。为什么有三种不同的术语来指代在屏幕上绘制的东西?

管理实例

如果您刚接触React不久,您可能只与组件类、实例打过交道。例如,您可以通过创建一个类来声明一个按钮组件。当应用程序运行时,您可能在屏幕上有这个组件的几个实例,每个实例都有它自己的属性和本地状态。这是传统的面向对象的UI编程。为什么引入元素呢?
在这个传统的UI模型中,由您来负责创建和销毁子组件实例。如果表单组件想要呈现一个按钮组件,它需要创建它的实例,并手动保持它与任何新信息同步。

class Form extends TraditionalObjectOrientedView {
  render() {
    // Read some data passed to the view
    const { isSubmitted, buttonText } = this.attrs;

    if (!isSubmitted && !this.button) {
      // Form is not yet submitted. Create the button!
      this.button = new Button({
        children: buttonText,
        color: 'blue'
      });
      this.el.appendChild(this.button.el);
    }

    if (this.button) {
      // The button is visible. Update its text!
      this.button.attrs.children = buttonText;
      this.button.render();
    }

    if (isSubmitted && this.button) {
      // Form was submitted. Destroy the button!
      this.el.removeChild(this.button.el);
      this.button.destroy();
    }

    if (isSubmitted && !this.message) {
      // Form was submitted. Show the success message!
      this.message = new Message({ text: 'Success!' });
      this.el.appendChild(this.message.el);
    }
  }
}

这是伪代码,但它或多或少是您编写复合UI代码时所得到的结果,该代码使用像Backbone这样的库以面向对象的方式运行。

每个组件实例都必须保留对其DOM节点和子组件实例的引用,并在时机合适时创建、更新和销毁它们。当遇到有state的组件,代码的行数可能成倍的增长,并且父组件可以直接访问他们的子组件实例,这使得将来很难将它们解耦。

那么,React有什么不同呢?

元素描述了树

在React中,这是元素来拯救的地方。元素是一个描述组件实例或DOM节点及其所需属性的普通对象。它只包含有关组件类型的信息(例如,一个按钮),它的属性(例如,它的颜色),以及其中的任何子元素。
元素不是实例。相反,它是一种告诉React你想在屏幕上看到什么的方法。在元素上不能调用任何方法。它只是一个具有两个字段的不可变描述对象:type:(string |ReactClass)props:Object

DOM Elements

当一个元素的类型是一个字符串时,它表示一个带有该标签名称的DOM节点,而props对应它的属性。这就是React所呈现的。例如:

{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}

这个元素只是将以下HTML表示为普通对象的一种方法:

<button class='button button-blue'>
  <b>
    OK!
  </b>
</button>

注意元素是如何嵌套的。按照约定,当我们想要创建一个元素树时,我们指定一个或多个子元素作为子元素的元素。
重要的是,**子元素和父元素都只是描述,而不是实际的实例。当你创建它们时,它们不会指向屏幕上的任何东西。**你可以创造它们,把它们扔掉,这无关紧要。
React元素很容易遍历,不需要解析,当然它们比实际的DOM元素要轻得多——它们只是对象!

Component Elements

但是,元素的类型也可以是React component对应的函数或类:

{
  type: Button,
  props: {
    color: 'blue',
    children: 'OK!'
  }
}

这是React的核心**。
描述组件的元素也是一个元素,就像描述DOM节点的元素一样。它们可以相互嵌套和混合。
这个特性让您可以将“危险按钮”组件定义为具有特定颜色属性值的按钮,而不必担心按钮是否呈现给DOM < Button >、<div>或其他内容:

const DangerButton = ({ children }) => ({
  type: Button,
  props: {
    color: 'red',
    children: children
  }
});

您可以在单个元素树中混合和匹配DOM和组件元素:

const DeleteAccount = () => ({
  type: 'div',
  props: {
    children: [{
      type: 'p',
      props: {
        children: 'Are you sure?'
      }
    }, {
      type: DangerButton,
      props: {
        children: 'Yep'
      }
    }, {
      type: Button,
      props: {
        color: 'blue',
        children: 'Cancel'
      }
   }]
});

或者,如果您喜欢JSX:

const DeleteAccount = () => (
  <div>
    <p>Are you sure?</p>
    <DangerButton>Yep</DangerButton>
    <Button color='blue'>Cancel</Button>
  </div>
);

这种混合和匹配有助于使组件彼此分离,因为它们可以通过组合来表达isahasa关系。
按钮是一个带有特定属性的DOM < Button >。
“危险”按钮是一个具有特定属性的按钮。
DeleteAccount<div>中包含一个按钮和一个危险按钮。

组件封装元素树

当React看到一个带有函数或类类型的元素时,它知道要问这个组件渲染什么元素,并给定相应的props
当它看到这个元素:

{
  type: Button,
  props: {
    color: 'blue',
    children: 'OK!'
  }
}

React会询问按钮渲染的元素内容。该按钮将返回此元素:

{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}

React将重复这个过程,直到它知道页面上每个组件的底层DOM标签元素。
React就像一个孩子问“Y”是什么意思,你向他们解释,直到他们明白了世界上的每一件小事。
还记得上面的表单示例吗?应用React它可以写成如下:

const Form = ({ isSubmitted, buttonText }) => {
  if (isSubmitted) {
    // Form submitted! Return a message element.
    return {
      type: Message,
      props: {
        text: 'Success!'
      }
    };
  }

  // Form is still visible! Return a button element.
  return {
    type: Button,
    props: {
      children: buttonText,
      color: 'blue'
    }
  };
};

就是这样!对于React组件,props是输入,而元素树是输出。
返回的元素树可以包含描述DOM节点的元素和描述其他组件的元素。这使您可以在不依赖于其内部DOM结构的情况下组成UI的独立部分。
我们让React创建、更新和销毁实例。我们用从组件返回的元素来描述这些实例,React负责管理这些实例。

组件可以是类或函数。

在上面的代码中,Form、Message、Button都是React Component。它们可以被编写为函数,比如上面的函数,或者作为从React.Component继承的类。声明一个组件的这三种方法主要是等价的:

// 1) As a function of props
const Button = ({ children, color }) => ({
  type: 'button',
  props: {
    className: 'button button-' + color,
    children: {
      type: 'b',
      props: {
        children: children
      }
    }
  }
});

// 2) Using the React.createClass() factory
const Button = React.createClass({
  render() {
    const { children, color } = this.props;
    return {
      type: 'button',
      props: {
        className: 'button button-' + color,
        children: {
          type: 'b',
          props: {
            children: children
          }
        }
      }
    };
  }
});

// 3) As an ES6 class descending from React.Component
class Button extends React.Component {
  render() {
    const { children, color } = this.props;
    return {
      type: 'button',
      props: {
        className: 'button button-' + color,
        children: {
          type: 'b',
          props: {
            children: children
          }
        }
      }
    };
  }
}

当一个组件被定义为一个类时,它比功能组件更强大一些。当创建或销毁相应的DOM节点时,它可以存储一些本地state并执行自定义逻辑。
功能组件的功能不那么强大,但更简单,它的作用就像一个类组件,只有一个render()方法。除非您在类中只需要这个特性,否则我们鼓励您使用功能组件。
然而,无论是函数还是类,从根本上来说,它们都是React的组件。他们将这些props作为输入,并将元素作为输出返回。

Top-Down Reconciliation 自上而下的和解(解析)

当你调用:

ReactDOM.render({
  type: Form,
  props: {
    isSubmitted: false,
    buttonText: 'OK!'
  }
}, document.getElementById('root'));

考虑到这些props,React会问Form组件它返回什么元素树。它将逐步“精炼”对您的组件树的理解,以更简单的原语:

// React: You told me this...
{
  type: Form,
  props: {
    isSubmitted: false,
    buttonText: 'OK!'
  }
}

// React: ...And Form told me this...
{
  type: Button,
  props: {
    children: 'OK!',
    color: 'blue'
  }
}

// React: ...and Button told me this! I guess I'm done.
{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}

当您调用ReactDOM.render()setState()时,React会开始解析,这是React过程的一部分。在React解析结束时,它会知道要渲染DOM树,而一个类似于react-domreact-native的渲染器会应用最小的更改集来更新DOM节点。
这个逐渐精炼的过程也是React应用程序易于优化的原因。如果组件树的某些部分变得太大,导致React不能有效地访问,那么您可以告诉React跳过这个“细化”,如果相关的props没有改变的话,就把树的某些部分分散开来。如果它们是不可变的,那么就可以很快地计算出这些道具是否已经改变了,因此,React和 immutability的工作在一起很好,并且可以在最小的工作量下提供极大的优化。
您可能已经注意到,这个博客条目谈论了许多关于组件component和元素element的内容,而不是关于实例instance的很多内容。事实是,与大多数面向对象的UI框架相比,实例的作用要小得多。
只有被声明为类的组件才有实例,而您永远不会直接创建它们:对您React是这样的。当父组件实例访问子组件实例的机制存在时,它们只用于命令式操作(例如将焦点集中在字段上),并且通常应该避免。
React负责为每个类组件创建一个实例,这样您就可以以面向对象的方式编写组件,并使用方法和本地状态state,但除此之外,实例在React的编程模型中并不十分重要,并且是由React本身来管理的。

总结

元素是一个简单的对象,描述您希望在屏幕上显示的DOM节点或其他组件。元素可以包含其他元素。创建一个React元素很便宜。一旦创建了一个元素,它就不会发生突变。
组件可以以几种不同的方式声明。它可以是带有render()方法的类。或者,在简单的情况下,它可以被定义为一个函数。在这两种情况下,它都以支持作为输入,并返回一个元素树作为输出。
当一个组件接收到一些props作为输入时,这是因为某个特定的父组件返回了带有它的类型和这些props的元素。
实例是您在编写的组件类中this的引用。它对于存储本地状态和响应生命周期事件非常有用。
功能组件根本没有实例。类组件有实例,但您永远不需要创建一个组件实例,React会负责这个。
最后,要创建元素,请使用React.createelement()、JSXelement factory helper。不要将元素作为一个对象写入真正的代码中,只需要知道它们都是底层的普通对象。

操作符

前置递增与后置递增

前置递增

let num1 = 2;
let num2 = 20;
let num3 = --num1 + num2; // 21
let num4 = num1 + num2; // 21

后置递增

let num1 = 2;
let num2 = 20;
let num3 = num1-- + num2; // 22
let num4 = num1 + num2; // 21

位操作符(奇淫技巧,让阅读代码的人会困惑)

React生命周期

适用于React V16.3 版本之前, ES5写法

React组件三个阶段(ES5版本)

对组件从第一次实例化到最后销毁组件的整个生命周期的控制。
分为三阶段:

  1. 是组件第一次渲染阶段,在这里完成了组件的初始化和加载;(实例化)
  2. 是组件在运行和交互阶段,这个阶段组件可以处理用户交互,或者接收事件更新界面,以及因父组件的重新渲染而造成的变化;(存在期)
  3. 是组件卸载消亡的阶段,这里做一些组件的清理工作。(销毁期)

React组件相关的生命周期

getDefaultProps()

在组件创建之前,会先调用该方法,这是全局调用一次,严格地来说,这不是组件的生命周期的一部分。

实例化

getInitialState()

在组件被创建并加载时,会调用该方法,来初始化组件的状态。这个函数在整个生命周期中只被调用一次。

componentWillMount()

组件第一次渲染之前,会调用该方法,这个函数在整个生命周期中只被调用一次。

可以:

1.可以加载服务器数据,并且如果使用了redux之类的数据服务,这里可以出发加载服务器数据的action;
2.这是唯一的会在服务端渲染调起的生命周期钩子函数。
3.调用setState(),由于在render()之前调用,因此不会触发重渲(re-render)。
不可以:
1.不可以使用refs来获取dom节点进而对做dom节点进行操作,在该生命周期中,dom节点还未生成。
2.避免在该方法中引入任何的副作用或订阅。对于这些使用场景,我们推荐使用constructor()来替代。

render()

渲染页面
不可以:
1.不可以调用setState(),否则会死循环导致页面崩溃

componentDidMount()

在组件第一次渲染之后,会调用该方法,通知组件已经加载完成。这个函数在整个生命周期中只被调用一次。

可以:
1.可以加载服务器数据,并且如果使用了redux之类的数据服务,这里可以出发加载服务器数据的action;
2.可以使用refs来获取dom节点,进而操作dom节点。
3.发起任何订阅。如果你这么做了,别忘了在componentWillUnmount()退订。
4.调用setState(),这样做将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了即使rende()将会调用两次,但用户不会看到中间状态。谨慎使用这一模式,因为它常导致性能问题。

存在期

componentWillReceviceProps(nextProps)

如果组件收到新的属性(props),就会调用该方法,
可以:
1.基于this.propsnextProps中属性的变化,进而发请求还是调用setState(),再或者调用你自己的定义的函数,达到更新的目。同理,由于在render()之前,调用setState()是安全的,不会触发重渲(re-render)。

注意:
1.调用this.setState时,该方法不会被调用。
2.在实例化期间,该方法不会被调用。
3.在已经实例化过的组件(mounted component)接收到新props时,该方法会被调用。
4.如果父组件导致了本组件的重新渲染,即使props没有更新,本组件的该方法也会被调用。因此若你想要当props发生变化时,去处理某些事情,请先对this.propsnextProps对比之后,再去处理。

componentShouldUpdate(nextProps, nextState)

当组件接收到新的props或新的state的话,或者说在componentWillReceiveProps(nextProps)后,就会调用该方法。
这个函数的返回值决定是否需要重渲,如果true表示需要,继续走后面的渲染流程。否者,则不渲染,直接进入等待状态。默认情况下,这个函数永远返回 true 用来保证数据变化的时候 UI 能够同步更新。在大型项目中,你可以自己重载这个函数,通过检查变化前后属性和状态,来决定 UI 是否需要更新,能有效提高应用性能。
注意:
1.在实例化期间,该方法不会被调用。
2.调用forceUpdate()时,该方法不会被调用。
3.这个方法如果返回false, 那么propsstate发生改变的时候会阻止子组件发生重新渲染;
4.目前,如果shouldComponentUpdate(nextProps, nextState)返回false, 那么componentWillUpdate(nextProps, nextState), render(), componentDidUpdate()都不会被触发;
5.以后,React可能把shouldComponentUpdate()当做一个小提示(hint)而不是一个指令(strict directive),并且它返回false仍然可能触发组件重新渲染(re-render);

componentWillUpdate(nextProps, nextState)

如果组件状态或者属性改变,并且shouldComponentUpdate(...) 返回为 true ,就会调用该方法。这个函数调用之后,就会把 nextPropsnextState 分别设置到 this.propsthis.state 中。紧接着这个函数,就会调用 render()来更新界面了。
可以:
1.可以做一些在更新界面之前要做的事情
2.可以添加一些页面每次渲染时重复的操作
不可以:
1.不可以调用setState(),否则会死循环导致页面崩溃
注意:
1.如果确实需要响应props的改变,那么你可以在componentWillReceiveProps(nextProps)中做响应操作;

componentDidUpdate(prevProps, prevState)

调用了 render() 更新完成界面之后,会调用 componentDidUpdate() 来得到通知
可以:
1.可以加载服务器数据,并且如果使用了redux之类的数据服务,这里可以出发加载服务器数据的action;
2.可以使用refs来获取dom节点,进而操作dom节点。
3.可以对比this.propsprevProps,进而对props的变化进行处理
4.调用setState(),这样做将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了即使rende()将会调用两次,但用户不会看到中间状态。谨慎使用这一模式,因为它常导致性能问题。
注意:
若shouldComponentUpdate()返回false,componentDidUpdate()将不会被调用。

销毁期

componentWillUnmount()

当组件要被从界面上移除的时候,就会调用。在这个函数中,可以做一些组件相关的清理工作,例如取消计时器、网络请求,清理任何在componentDidMount环节创建的DOM元素等。

生命周期触发流程

组件初始化

componentWillMount -> render -> componentDidMount

组件更新 — props change

componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate

组件更新 — state change

shoudlComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate

生命周期 调用次数 能否使用 setSate() 是否可以发请求
getDefaultProps 1(全局调用一次)
getInitialState 1
componentWillMount 1
render >=1
componentDidMount 1
componentWillReceiveProps >=0
shouldComponentUpdate >=0
componentWillUpdate >=0
componentDidUpdate >=0
componentWillUnmount 1

处理package.json

处理package.json

管理本地安装的npm包的最佳方法是创建一个package.json的文件。

A package.json file:

  • 列出项目所依赖的npm包
  • 允许您使用语义版本控制规则指定包的版本
  • 使您的项目构建可重现,因此更容易与其它开发人员分享

requirements:

一个package.json文件必须有

  • name
    • 全部小写
    • 一个单词,没有空格
    • -_ 是允许的
  • version

例子:

{
  "name": "my-awesome-package",
  "version": "1.0.0"
}

创建package.json

  1. Run a CLI questionnaire
npm init

这将启动一个命令行问卷,以交互的形式创建package.json文件,创建的文件位于你运行命令的目录下。

  1. Create a default package.json
npm init --yes
// or
// npm init -y

{
  "name": "my_package",
  "description": "",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/ashleygwilliams/my_package.git"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/ashleygwilliams/my_package/issues"
  },
  "homepage": "https://github.com/ashleygwilliams/my_package"
}

这个方法将会生成一个默认package.json文件,该文件的内容信息从当前目录提取出来。

  • name :当前目录的名称
  • version : 总是 1.0.0
  • description :来自readme的信息,或者是一个空字符串""
  • main :总是 index.js
  • scripts :默认创建一个空的测试脚本
  • keywords :空
  • author :空
  • license :ISC
  • bugs :来自于当前目录的信息,如果存在的话
  • homepage :来自于当前目录的信息,如果存在的话

你可以为 npm init 命令设置几个有用的配置选项:

npm set init.author.email "[email protected]"
npm set init.author.name "liguanghui6"
npm set init.license "MIT"

说明:
如果在 package.json 文件中没有 description 字段,npm将会使用README.md文件的第一行内容作为替换。该描述可以帮助人们在搜索npm包时找到你的包。所以在 package.json 文件中定义一个通俗的 description 字段是十分有用的。

如何自定义 npm init 出来的配置选项

你需要创建一个定制的.npm-init.js文件在你的根目录下(~/.npm-init.js)

一个简单的.npm-init.js文件如下所示:

module.exports = {
  customField: 'Custom Field',
  otherCustomField: 'This field is really cool'
}

在根目录下运行npm init ,将会输出一个包含以下内容的package.json

module.exports = {
  customField: 'Custom Field',
  otherCustomField: 'This field is really cool'
}

您还可以使用prompt函数来定制问卷

module.exports = prompt("what's your favorite flavor of ice cream, buddy?", "I LIKE THEM ALL");

要了解关于如何创建高级定制的更多信息,请查阅有关init-package-json的文档

指定依赖项

为了指定你项目中所依赖的包,你需要在package.json文件中列出你需要的包。这里有两种类型的包你可以列出:

  • dependencies

    这些包是您的应用程序在生产环境中所需要的

    npm install <package_name> --save
    
  • devDependencies

    这些包仅用于开发和测试

    npm install <package_name> --save-dev
    

XSS攻击

XSS的定义

跨站脚本攻击(Cross Site Scripting),缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该网页时,嵌入其中Web里面的Script代码就会执行,从而达到恶意攻击用户的目的。

XSS的原理

  • 攻击者对含有漏洞的服务器发起XSS攻击(注入JS代码)。
  • 诱使受害者打开受到攻击的服务器URL。
  • 受害者在Web浏览器中打开URL,恶意脚本执行。

XSS的攻击方式

  • 反射型

发出请求时,XSS代码出现在URL中,作为输入提交到服务器端,服务器端解析后响应,XSS随响应内容一起返回给浏览器,最后浏览器解析执行XSS代码,这个过程就像一次发射,所以叫反射型XSS。

  • 存储型

存储型XSS和反射型的XSS差别就在于,存储型的XSS提交的代码会存储在服务器端(数据库,内存,文件系统等),下次请求目标页面时不用再提交XSS代码。

代码演示

//Node  Express 后台路由
const express = require('epress');
const router = express.Router();

router.get('/',function(req,res,next){
  //一般情况下,浏览器会对这种XSS攻击拦截
  //关闭浏览器的自动拦截XSS功能
  res.set('X-XSS-Protection':0);
  //反射型
  res.render('index',{title:'Express',xss:req.query.xss})

  //存储型
  res.render('index',{title:'Express',xss:sql()})
})

module.exports = router;
//index.ejs 前端展示页面(后端模板渲染的方式)
//<%- xss %>遇到html标签不会转义
//<%= xss %>遇到html标签会转义
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title><%= title %></title>
</head>
<body>
  <h1><%= title %></h1>
  <div>
    <%- xss %>
  </div>
</body>
</html>

在浏览器地址栏中输入(反射型)

  • localhost:3000/?xss=<img src="null" onerror="alert(1)"/> ---自动触发
  • localhost:3000/?xss=<img src="色情图片" onclick="alert(1)"/> ---引诱触发
  • localhost:3000/?xss=<iframe src="色情网站"></iframe> ---引诱触发

XSS的防御措施

  • 编码

对用户输入的数据进行HTML Entity编码

  • 过滤 (重中之重)

移除用户上传的DOM属性,如onerror(img-src=null)等,移除用户上传的style(body-display:none)节点,script(由于同源策略可以完全操作页面)节点,iframe,frame(各种引诱)节点等。

  • 校正

避免直接对HTML Entity编码(直接解码会直接执行,那么就白白过滤了),使用DOM Prase转换,校正不配对的DOM标签

评论区域最有可能发生XSS攻击

前端规范

前端开发规范:命名规范、html规范、css规范、js规范


基本准则

符合web标准, 语义化html, 结构、表现、行为分离, 兼容性优良. 页面性能方面, 代码要求简洁明了有序, 尽可能的减小服务器负载, 保证最快的解析速度.

命名

变量命名:

命名方式 : 小驼峰式命名方法
命名规范 : 类型+对象描述的方式,如果没有明确的类型,就可以使前缀为名词

类型 小写字母
array a
boolean b
function fn
string s
object o
推荐:
const aBtn = document.getElementByClass('btn');
const tableTitle = 'LoginTable';

不推荐:

const getTitle = 'LoginTable';

函数:

命名方式 : 小驼峰方式 ( 构造函数使用大驼峰命名法 )
命名规则 : 前缀为动词

动词 含义 返回值
can 判断是否可执行某个动作 ( 权限 ) 函数返回一个布尔值。true:可执行;false:不可执行
has 判断是否含有某个值 函数返回一个布尔值。true:含有此值;false:不含有此值
is 判断是否为某个值 函数返回一个布尔值。true:为某个值;false:不为某个值
get 获取某个值 函数返回一个非布尔值
set 设置某个值 无返回值、返回是否设置成功或者返回链式对象
推荐:
//是否可阅读
function canRead(){
    return true;
}

//获取姓名
function getName{
    return this.name
}

常量:

命名方法 : 全部大写
命名规范 : 使用大写字母和下划线来组合命名,下划线用以分割单词。
推荐:

 var MAX_COUNT = 10;
 var URL = 'http://www.baidu.com';

注释规范:

单行注释 ( // )

  • 单独一行://(双斜线)与注释文字之间保留一个空格
  • 在代码后面添加注释://(双斜线)与代码之间保留一个空格,并且//(双斜线)与注释文字之间保留一个空格。
  • 注释代码://(双斜线)与代码之间保留一个空格。

推荐:

// 调用了一个函数;1)单独在一行
setTitle();

var maxCount = 10; // 设置最大量;2)在代码后面注释

// setName(); // 3)注释代码

多行注释 ( / 注释说明 / )

  • 若开始(/和结束(/)都在一行,推荐采用单行注释
  • 若至少三行注释时,第一行为/,最后行为/,其他行以开始,并且注释文字与保留一个空格。

推荐:

/*
* 代码执行到这里后会调用setTitle()函数
* setTitle():设置title的值
*/
setTitle();

HTML规范

文档规范:

使用 HTML5 的文档声明类型 : <!DOCTYPE html>
  • DOCTYPE标签是一种标准通用标记语言的文档类型声明,它的目的是要告诉标准通用标记语言解析器,它应该使用什么样的文档类型定义(DTD)来解析文档。
  • 使用文档声明类型的作用是为了防止开启浏览器的怪异模式。
  • 没有DOCTYPE文档类型声明会开启浏览器的怪异模式,浏览器会按照自己的解析方式渲染页面,在不同的浏览器下面会有不同的样式。
  • 如果你的页面添加了<!DOCTYP>那么,那么就等同于开启了标准模式。浏览器会按照W3C标准解析渲染页面。

脚本加载:

所有浏览器中推荐:
<html>
  <head>
    <link rel="stylesheet" href="main.css">
  </head>
  <body>
    <!-- body goes here -->

    <script src="main.js" async></script>
  </body>
</html>
ie10+及移动端:
<html>
  <head>
    <link rel="stylesheet" href="main.css">
    <script src="main.js" async></script>
  </head>
  <body>
    <!-- body goes here -->
  </body>
</html>

语义化:

使用语义化标签有利于SEO。

  • 语义化是指:根据元素其被创造出来时的初始意义来使用它。
  • 意思就是用正确的标签干正确的事,而不是只有div和span。

alt标签不为空

  • <img>标签的 alt 属性指定了替代文本,用于在图像无法显示或者用户禁用图像显示时,代替图像显示在浏览器中的内容。
  • 从SEO角度考虑,浏览器的爬虫爬不到图片的内容,所以我们要有文字告诉爬虫图片的内容

结构、表现、行为三者分离

尽量在文档和模板中只包含结构性的 HTML;而将所有表现代码,移入样式表中;将所有动作行为,移入脚本之中。
在此之外,为使得它们之间的联系尽可能的小,在文档和模板中也尽量少地引入样式和脚本文件。
建议:

  • 不使用超过一到两张样式表
  • 不使用超过一到两个脚本
  • 不在元素上使用 style 属性

JS规范

代码规范

代码规范: 遵循ESlint规范及风格。

使用 ECMA Script 6

建议使用 ECMA Script 6 中新增的语法糖和函数。这将简化你的程序,并让你的代码更加灵活和可复用。

避免全局命名空间污染

尽量不用全局变量,推荐使用IIFE(立即执行的函数表达式):

(function(){
  'use strict';

  // Code goes here

}());

数组和对象字面量

用数组和对象字面量来代替数组和对象构造器。数组构造器很容易让人在它的参数上犯错,推荐使用数组及对象的字面量形式。
推荐:

// 数组
var a = [x1, x2, x3];
var a2 = [x1, x2];
var a3 = [x1];
var a4 = [];

// 对象
var o = {};
 
var o2 = {
  a: 0,
  b: 1,
  c: 2,
  'strange key': 3
};

不推荐:

// 数组
// Length is 3.
var a1 = new Array(x1, x2, x3);
 
// Length is 2.
var a2 = new Array(x1, x2);
 
var a3 = new Array(x1);
 
// Length is 0.
var a4 = new Array();
// 对象
var o = new Object();
 
var o2 = new Object();
o2.a = 0;
o2.b = 1;
o2.c = 2;
o2['strange key'] = 3;

使用全局等

总是使用 === 精确的比较操作符,避免在判断的过程中,由 JavaScript 的强制类型转换所造成的困扰。例如:

(function(){
  'use strict';

  log('0' == 0); // true
  log('' == false); // true
  log('1' == true); // true
  log(null == undefined); // true

  var x = {
    valueOf: function() {
      return 'X';
    }
  };

  log(x == 'X');

}());

三元条件判断(if 的快捷方法)

用三元操作符分配或返回语句。在比较简单的情况下使用,避免在复杂的情况下使用。

推荐:

return x === 10 ? 'valid' : 'invalid';

不推荐:

if(x === 10) {
  return 'valid';
} else {
  return 'invalid';
}

CSS规范

id和class的命名

ID和class的名称总是使用可以反应元素目的和用途的名称,或其他通用的名称,代替表象和晦涩难懂的名称。

推荐:

.heavy {
  font-weight: 800;
}

.important {
  color: red;
}

不推荐:

.fw-800 {
  font-weight: 800;
}

.red {
  color: red;
}

class 必须单词全字母小写,单词间以 - 分隔。

class 必须代表相应模块或部件的内容或功能,不得以样式信息进行命名。

示例:

<!-- good -->
<div class="sidebar"></div>

<!-- bad -->
<div class="left"></div>

合理的使用ID

一般情况下ID不应该被用于样式,并且ID的权重很高,所以不使用ID解决样式的问题,而是使用class。
推荐:
.content .title {
font-size: 2em;
}
不推荐:
#content .title {
font-size: 2em;
}

css选择器中避免使用标签名

从结构、表现、行为分离的原则来看,应该尽量避免css中出现HTML标签,并且在css选择器中出现标签名会存在潜在的问题。

使用子选择器

推荐:

.content > .title {
  font-size: 2rem;
}

不推荐:

.content .title {
  font-size: 2rem;
}

尽量使用缩写属性

尽量使用缩写属性对于代码效率和可读性是很有用的,比如font属性。

推荐:

.box {
      border: 1px solid #fff;
}

不推荐:

.box {
      border-color: #fff;
      border-width: 1px;
      border-style: solid;
}

属性书写顺序

结构性属性:

1.display
2.position, left, top, right
3.overflow, float, clear
4.margin, padding
表现性属性:
1.background, border
2.font, text

推荐:

.box {
  display: block;
  position: absolute;
  left: 30%;
  right: 30%;
  overflow: hidden;
  margin: 1em;
  padding: 1em;
  background-color: #eee;
  border: 3px solid #ddd;
  font-family: 'Arial', sans-serif;
  font-size: 1.5rem;
  text-transform: uppercase;
}

不推荐:

.box {
  font-family: 'Arial', sans-serif;
  border: 3px solid #ddd;
  left: 30%;
  position: absolute;
  text-transform: uppercase;
  background-color: #eee;
  right: 30%;
  isplay: block;
  font-size: 1.5rem;
  overflow: hidden;
  padding: 1em;
  margin: 1em;
}

语义化版本

语义化版本 2.0.0

摘要

版本格式:主版本号.次版本号.修订号,版本号递增规则如下:

  1. 主版本号:当你做了不兼容的API修改
  2. 次版本号:当你做了向下兼容的功能性新增
  3. 修订号:当你做了向下兼容的问题修正
    先行版本号及版本编译信息可以加到 主版本号.次版本号.修订号 的后面,作为延伸

简介

在软件管理的领域里存在着被称作"依赖地狱"的死亡之谷,系统规模越大,加入的套件越多,你就越有可能在未来的某一天发现自己已深陷绝望之中。

在依赖高的系统中发布新版本套件可能很快会成为噩梦。如果依赖关系过高,可能面临版本控制被锁死的风险(必须对每一个相依套件改版才能完成某次升级)。而如果依赖关系过于松散,又将无法避免版本的混乱(假设兼容与未来的多个版本已超出了合理数量)。当你专案的进展因为版本相依被锁死或版本混乱变得不够简便和可靠,就意味着你正处于依赖地狱之中。

作为这个问题的解决方案之一,我提议用一组简单的规则及条件来约束版本号的配置和增长。这些规则是根据(但不限于)已经被各种封闭、开放源码软件所广泛使用的惯例所设计。为了让这套理论运作,你必须先有定义好的公共API。这可以透过文件定义或代码强制要求来实现。

如何防止权限错误

如何防止权限错误

如果您在尝试安装全局包时看到EACCES错误,请阅读本章。如果更改安装npm的目录,通常可以避免这个错误。要做到这一点:

  • 重新安装npm
  • 手动更改npm的默认目录

重新安装npm

这是避免权限问题的最好方法。这在第二章中描述。

在安装node版本管理器之前你不需要删除当前版本的npm和node。

手动更改npm的默认目录

本节不适用于windows系统。这章将在不久的将来更新,以解决windows的问题。

为了减少权限错误的机会,你可以将npm配置为使用不同的目录。在本例中,它将是主文件夹下的一个隐藏目录。

  1. 在开始之前备份你的电脑
  2. 制作一个全局安装目录
    mkdir ~/.npm-global
  1. 配置npm使用新的目录路径
npm config set prefix '~/.npm-global'
  1. 添加这一行打开或者创建一个 ~/.profile文件
 export PATH=~/.npm-global/bin:$PATH
  1. 回到命令行,更新你的系统变量
source ~/.profile

测试:在不使用sudo的情况下,安装一个全局包

npm install -g jshint

与步骤4不同,您可以使用相应的ENV变量(例如,如果您不想修改~/.profile):

NPM_CONFIG_PREFIX=~/.npm-global

提示:考虑npx

如果您使用的是npm version 5.2或更高版本,请探索npx作为运行全局命令的替代方法,特别是如果您只是偶尔需要一个命令。

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.