joyeuxman / joyeuxman.github.io Goto Github PK
View Code? Open in Web Editor NEW个人博客
个人博客
<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;//其值的大小与上下文字体和字体大小有关
}
<div>
<span>白间距</span><!--
--><span>白间距</span><!--
--><span>白间距</span>
</div>
npm publish <package>
npm version <update_type>
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的主要方法就是使用<script>
元素
<script>
元素属性介绍属性名 | 是否可选 | 详细解释 |
---|---|---|
async | 可选 | 表示应该立即下载脚本,但不应妨碍页面中的其它操作,比如下载其他资源或者等待加载其它脚本。只对外部脚本文件有效 |
charset | 可选 | 表示通过src属性指定的代码的字符集。由于大多数浏览器会忽略它的值,因此这个属性很少有人用 |
defer | 可选 | 表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本有效。IE7及更早版本对嵌入脚本也支持这个属性 |
language | 已废弃 | 原来用于表示编写代码使用的脚本语言(JavaScript、JavaScript1.2、VBScript)。大多数浏览器会忽略这个属性,因此也没有必要使用了 |
src | 可选 | 表示包含要执行代码的外部文件 |
type | 可选 | 可以看成language的替代属性;表示编写代码使用的脚本语言的内容类型(也成MIME类型)。虽然text/javascript 和text/ecmascript 都已经不被推荐使用,但人们一直以来使用的都还是text/JavaScript 。实际上,服务器在传送JavaScript文件时使用的MIME类型通常是application/x-javascript ,但在type中设置这个值却可能导致脚本被忽略。另外,在非IE浏览器中还可以使用以下值:application/javascript 和application/ecmascript 。默认值是text/javascript |
无论script
标签如何包裹代码,只要不存在defer
和async
属性,浏览器就会按照<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 outdated
列出需要更新的报的信息(名称、当前版本号、想要的版本号、最新的版本号)npm update
如果你只是想更新某一个包到指定的版本,那么你可以直接通过命令修改。如果这个包在package.json 的dependencies 节点下,使用:
npm install [email protected] --save
npm install [email protected] --save-dev
npm outdated
npm outdated -g --depth=0
更新
npm update -g
npm install webpack -g
验证是否更新完毕
npm outdated
循环时,在不使用函数第三个参数的情况下,遍历的元素若为基本类型(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:'李四'}]
普通元素的层叠水平优先由层叠上下文决定,因此,层叠水平的比较只有在当前层叠上下文元素中才有意义。
表示元素发生层叠时候有着特定的垂直显示顺序
层叠上下文和层叠水平是概念,而这里的层叠顺序是规则。
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)))
null
表示一个指向不存在或无效的对象或地址引用。
typeof null //object
!null //true
null == false //false 此处为坑,码友莫入坑!
null === false //false
null == true //false
null === true //false
null
的值会被转换为0
null + 5 //5
null * 23 //0
undefined
表示一个没有被定义的值。
typeof undefined //undefined
!undefined //true
undefined == false //false 此处为坑,码友莫入坑!
undefined === false //false
undefined == true //false
undefined === true //false
undefined
与Number类型的数值进行算术运算会得到NaN
。
undefined + 5 //NaN
undefined * 23 //NaN
true
null
表示无,完全不存在;undefined
表示变量未定义null
是一个对象,而undefined
有自己的数据类型(undefined
)null
被视为0
,而undefined
返回NaN
需要指出的是:
undefined == null //true
undefined === null //false
!undefined == !null //true
!undefined === !null //true
NaN
表示一个非数字的值。
typeof NaN //number 此处为坑,码友莫入坑!
!NaN //true
NaN == false //false 此处为坑,码友莫入坑!
NaN === false //false
NaN == true //false
NaN === true //false
NaN == NaN //false
NaN === NaN //false
isNaN(NaN) //true
isNaN(23) //false
对象分为普通对象object
和函数对象function
。凡是通过new Function()
创建的对象都是函数对象,其余都是普通对象。以下示例中f1
,f2
,Function
,Object
,归根结底都是通过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 = 构造函数
在JS中定义对象(函数对象或者普通对象)时,对象中都会包含一些预先定义好的属性。其中每个函数对象都有一个prototype
属性,该属性是一个普通对象,指向函数的原型对象。原型对象中也会有个预先定义好的属性constructor
,该属性(是一个指针)指向prototype
属性所在的函数。
结论:
构造函数.prototype.constructor === 构造函数
构造函数.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
person1.__proto__
是什么?Person.__proto__
是什么?Person.prototype.__proto__
是什么?Object.__proto__
是什么?Object.prototype__proto__
是什么?Function.prototype.__proto__
是什么?person1.__proto__ === Person.prototype
Person.__proto__ === Function.prototype
Person.prototype.__proto__ === Object.prototype
Object.__proto__ === Function.prototype
Object.prototype.__proto__ === null
此处为坑,码友莫入!null
处于原型链的顶端。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,保证原型链能够正常结束。
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;
}
/**
* 收集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组件,供其它组件调用。
//声明高阶组件
高阶组件(低阶组件的配置信息){
return 低阶组件 =>{
return 低阶组件和低阶组件的配置信息的结合体
}
}
//调用高阶组件
高阶组件(低阶组件的配置信息)(低阶组件)
属性代理:通过做一些操作,将被包裹组件(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)
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 remote -v
//output
origin http://xxx.xxx.old.git
删除本地现在的远程仓库地址
git remote rm origin
验证本地现在的远程仓库地址是否删除成功
git remote -v
//没有出现origin,表示删除成功
将新的远程仓库地址添加到本地
git remote add origin http://xxx.xxx.new.git
验证新的的远程仓库地址是否添加成功
git remote -v
//output
origin http://xxx.xxx.new.git //出现origin内容表示添加成功
获取新的远程分支
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
查看所有的分支(本地旧分支+远程仓库新分支)
git branch -a
//output
oldBranch1
oldBranch2
oldBranch3
remotes/origin/newBranch1
remotes/origin/newBranch2
remotes/origin/newBranch3
依次删除本地旧分支
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
查看删除本地旧分支是否成功
git branch -a
//output success
remotes/origin/newBranch1
remotes/origin/newBranch2
remotes/origin/newBranch3
切换远程新分支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'.
...
查看本地新分支是否创建成功
git branch -a
//output success
newBranch1
newBranch2
newBranch3
remotes/origin/newBranch1
remotes/origin/newBranch2
remotes/origin/newBranch3
拉取最新代码
git pull
组件、它们的实例和元素之间的区别使许多初学者感到困惑。为什么有三种不同的术语来指代在屏幕上绘制的东西?
如果您刚接触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节点,而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元素要轻得多——它们只是对象!
但是,元素的类型也可以是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>
);
这种混合和匹配有助于使组件彼此分离,因为它们可以通过组合来表达isa
和hasa
关系。
按钮是一个带有特定属性的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作为输入,并将元素作为输出返回。
当你调用:
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-dom
或react-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()、JSX
或element 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
对组件从第一次实例化到最后销毁组件的整个生命周期的控制。
分为三阶段:
- 是组件第一次渲染阶段,在这里完成了组件的初始化和加载;(实例化)
- 是组件在运行和交互阶段,这个阶段组件可以处理用户交互,或者接收事件更新界面,以及因父组件的重新渲染而造成的变化;(存在期)
- 是组件卸载消亡的阶段,这里做一些组件的清理工作。(销毁期)
在组件创建之前,会先调用该方法,这是全局调用一次,严格地来说,这不是组件的生命周期的一部分。
在组件被创建并加载时,会调用该方法,来初始化组件的状态。这个函数在整个生命周期中只被调用一次。
组件第一次渲染之前,会调用该方法,这个函数在整个生命周期中只被调用一次。
可以:
1.可以加载服务器数据,并且如果使用了redux之类的数据服务,这里可以出发加载服务器数据的action;
2.这是唯一的会在服务端渲染调起的生命周期钩子函数。
3.调用setState()
,由于在render()之前调用,因此不会触发重渲(re-render)。
不可以:
1.不可以使用refs来获取dom节点进而对做dom节点进行操作,在该生命周期中,dom节点还未生成。
2.避免在该方法中引入任何的副作用或订阅。对于这些使用场景,我们推荐使用constructor()来替代。
渲染页面
不可以:
1.不可以调用setState()
,否则会死循环导致页面崩溃
在组件第一次渲染之后,会调用该方法,通知组件已经加载完成。这个函数在整个生命周期中只被调用一次。
可以:
1.可以加载服务器数据,并且如果使用了redux之类的数据服务,这里可以出发加载服务器数据的action;
2.可以使用refs来获取dom节点,进而操作dom节点。
3.发起任何订阅。如果你这么做了,别忘了在componentWillUnmount()退订。
4.调用setState()
,这样做将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了即使rende()将会调用两次,但用户不会看到中间状态。谨慎使用这一模式,因为它常导致性能问题。
如果组件收到新的属性(props),就会调用该方法,
可以:
1.基于this.props
和nextProps
中属性的变化,进而发请求还是调用setState()
,再或者调用你自己的定义的函数,达到更新的目。同理,由于在render()之前,调用setState()
是安全的,不会触发重渲(re-render)。
注意:
1.调用this.setState
时,该方法不会被调用。
2.在实例化期间,该方法不会被调用。
3.在已经实例化过的组件(mounted component)接收到新props时,该方法会被调用。
4.如果父组件导致了本组件的重新渲染,即使props没有更新,本组件的该方法也会被调用。因此若你想要当props发生变化时,去处理某些事情,请先对this.props
和nextProps
对比之后,再去处理。
当组件接收到新的props
或新的state
的话,或者说在componentWillReceiveProps(nextProps)
后,就会调用该方法。
这个函数的返回值决定是否需要重渲,如果true
表示需要,继续走后面的渲染流程。否者,则不渲染,直接进入等待状态。默认情况下,这个函数永远返回 true 用来保证数据变化的时候 UI 能够同步更新。在大型项目中,你可以自己重载这个函数,通过检查变化前后属性和状态,来决定 UI 是否需要更新,能有效提高应用性能。
注意:
1.在实例化期间,该方法不会被调用。
2.调用forceUpdate()
时,该方法不会被调用。
3.这个方法如果返回false
, 那么props
或state
发生改变的时候会阻止子组件发生重新渲染;
4.目前,如果shouldComponentUpdate(nextProps, nextState)
返回false
, 那么componentWillUpdate(nextProps, nextState)
, render()
, componentDidUpdate()
都不会被触发;
5.以后,React可能把shouldComponentUpdate()
当做一个小提示(hint)而不是一个指令(strict directive),并且它返回false仍然可能触发组件重新渲染(re-render);
如果组件状态或者属性改变,并且shouldComponentUpdate(...)
返回为 true ,就会调用该方法。这个函数调用之后,就会把 nextProps
和 nextState
分别设置到 this.props
和 this.state
中。紧接着这个函数,就会调用 render()
来更新界面了。
可以:
1.可以做一些在更新界面之前要做的事情
2.可以添加一些页面每次渲染时重复的操作
不可以:
1.不可以调用setState()
,否则会死循环导致页面崩溃
注意:
1.如果确实需要响应props
的改变,那么你可以在componentWillReceiveProps(nextProps)
中做响应操作;
调用了 render() 更新完成界面之后,会调用 componentDidUpdate() 来得到通知
可以:
1.可以加载服务器数据,并且如果使用了redux之类的数据服务,这里可以出发加载服务器数据的action;
2.可以使用refs来获取dom节点,进而操作dom节点。
3.可以对比this.props
和prevProps
,进而对props
的变化进行处理
4.调用setState()
,这样做将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了即使rende()将会调用两次,但用户不会看到中间状态。谨慎使用这一模式,因为它常导致性能问题。
注意:
若shouldComponentUpdate()返回false,componentDidUpdate()将不会被调用。
当组件要被从界面上移除的时候,就会调用。在这个函数中,可以做一些组件相关的清理工作,例如取消计时器、网络请求,清理任何在componentDidMount环节创建的DOM元素等。
componentWillMount -> render -> componentDidMount
componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate
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 | 否 | 否 |
管理本地安装的npm包的最佳方法是创建一个package.json
的文件。
一个package.json
文件必须有
-
和 _
是允许的x.x.x
的形式例子:
{
"name": "my-awesome-package",
"version": "1.0.0"
}
npm init
这将启动一个命令行问卷,以交互的形式创建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.0description
:来自readme的信息,或者是一个空字符串""
main
:总是 index.js
scripts
:默认创建一个空的测试脚本keywords
:空author
:空license
:ISCbugs
:来自于当前目录的信息,如果存在的话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.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文件中列出你需要的包。这里有两种类型的包你可以列出:
这些包是您的应用程序在生产环境中所需要的
npm install <package_name> --save
这些包仅用于开发和测试
npm install <package_name> --save-dev
跨站脚本攻击(Cross Site Scripting),缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该网页时,嵌入其中Web里面的Script代码就会执行,从而达到恶意攻击用户的目的。
发出请求时,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>
---引诱触发移除用户上传的DOM属性,如onerror(img-src=null)等,移除用户上传的style(body-display:none)节点,script(由于同源策略可以完全操作页面)节点,iframe,frame(各种引诱)节点等。
避免直接对HTML Entity编码(直接解码会直接执行,那么就白白过滤了),使用DOM Prase转换,校正不配对的DOM标签
评论区域最有可能发生XSS攻击
符合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();
<!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>
<html>
<head>
<link rel="stylesheet" href="main.css">
<script src="main.js" async></script>
</head>
<body>
<!-- body goes here -->
</body>
</html>
使用语义化标签有利于SEO。
- 语义化是指:根据元素其被创造出来时的初始意义来使用它。
- 意思就是用正确的标签干正确的事,而不是只有div和span。
<img>
标签的 alt 属性指定了替代文本,用于在图像无法显示或者用户禁用图像显示时,代替图像显示在浏览器中的内容。- 从SEO角度考虑,浏览器的爬虫爬不到图片的内容,所以我们要有文字告诉爬虫图片的内容
尽量在文档和模板中只包含结构性的 HTML;而将所有表现代码,移入样式表中;将所有动作行为,移入脚本之中。
在此之外,为使得它们之间的联系尽可能的小,在文档和模板中也尽量少地引入样式和脚本文件。
建议:
- 不使用超过一到两张样式表
- 不使用超过一到两个脚本
- 不在元素上使用 style 属性
代码规范: 遵循ESlint规范及风格。
建议使用 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');
}());
用三元操作符分配或返回语句。在比较简单的情况下使用,避免在复杂的情况下使用。
推荐:
return x === 10 ? 'valid' : 'invalid';
不推荐:
if(x === 10) {
return 'valid';
} else {
return 'invalid';
}
ID和class的名称总是使用可以反应元素目的和用途的名称,或其他通用的名称,代替表象和晦涩难懂的名称。
推荐:
.heavy {
font-weight: 800;
}
.important {
color: red;
}
不推荐:
.fw-800 {
font-weight: 800;
}
.red {
color: red;
}
示例:
<!-- good -->
<div class="sidebar"></div>
<!-- bad -->
<div class="left"></div>
一般情况下ID不应该被用于样式,并且ID的权重很高,所以不使用ID解决样式的问题,而是使用class。
推荐:
.content .title {
font-size: 2em;
}
不推荐:
#content .title {
font-size: 2em;
}
从结构、表现、行为分离的原则来看,应该尽量避免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;
}
版本格式:主版本号.次版本号.修订号,版本号递增规则如下:
在软件管理的领域里存在着被称作"依赖地狱"的死亡之谷,系统规模越大,加入的套件越多,你就越有可能在未来的某一天发现自己已深陷绝望之中。
在依赖高的系统中发布新版本套件可能很快会成为噩梦。如果依赖关系过高,可能面临版本控制被锁死的风险(必须对每一个相依套件改版才能完成某次升级)。而如果依赖关系过于松散,又将无法避免版本的混乱(假设兼容与未来的多个版本已超出了合理数量)。当你专案的进展因为版本相依被锁死或版本混乱变得不够简便和可靠,就意味着你正处于依赖地狱之中。
作为这个问题的解决方案之一,我提议用一组简单的规则及条件来约束版本号的配置和增长。这些规则是根据(但不限于)已经被各种封闭、开放源码软件所广泛使用的惯例所设计。为了让这套理论运作,你必须先有定义好的公共API。这可以透过文件定义或代码强制要求来实现。
如果您在尝试安装全局包时看到EACCES错误,请阅读本章。如果更改安装npm的目录,通常可以避免这个错误。要做到这一点:
这是避免权限问题的最好方法。这在第二章中描述。
在安装node版本管理器之前你不需要删除当前版本的npm和node。
本节不适用于windows系统。这章将在不久的将来更新,以解决windows的问题。
为了减少权限错误的机会,你可以将npm配置为使用不同的目录。在本例中,它将是主文件夹下的一个隐藏目录。
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
~/.profile
文件 export PATH=~/.npm-global/bin:$PATH
source ~/.profile
测试:在不使用sudo
的情况下,安装一个全局包
npm install -g jshint
与步骤4不同,您可以使用相应的ENV变量(例如,如果您不想修改~/.profile):
NPM_CONFIG_PREFIX=~/.npm-global
提示:考虑npx
如果您使用的是npm version 5.2或更高版本,请探索npx作为运行全局命令的替代方法,特别是如果您只是偶尔需要一个命令。
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.