function Person() { }
或者
var Person = function(){ }
对象(类的实例)
我们使用 new obj 创建对象 obj 的新实例, 将结果(obj 类型)赋值给一个变量方便稍后调用。
function Person() { ...... }
var person1 = new Person();
var person2 = new Person();
新增的 Object.create 方法来创建对象
const person = {
isHuman: false,
printIntroduction: function() {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};
const me = Object.create(person);
构造器 对象属性
function Person(firstName) {
this.firstName = firstName;
alert('Person instantiated');
}
var person1 = new Person('Alice');
var person2 = new Person('Bob');
@fnord: "I am fnord.";
@var: "fnord";
#wrap::after{
content: @@var; // 将@var替换为其值 content:@fnord;
}
生成的 CSS
#wrap::after{
content: "I am fnord.";
}
媒体查询
之前媒体查询的格式是, 先分屏然后在每一个媒体查询中设置样式。
@media only screen and {max-width:1200px;}{
Div{}
}
@media only screen and {min-width:992px;}{
Div{}
}
@media only screen and {min-width:768px}{
Div{}
}
// 报错
(function () {
let a = 10;
var a = 1;
console.log(a)
})()
// 报错
(function () {
let a = 10;
let a = 1;
console.log(a)
})()
// OK
(function () {
var a = 10;
var a = 1;
console.log(a) // 1
})()
const
const声明的是常量。一旦声明,常量的值就不能改变。
const PI = Math.PI
PI = 23 // Module build failed: SyntaxError: /es6/app.js: "PI" is read-only
当我们尝试去改变用const声明的常量时,浏览器就会报错。
下面的定义,试问这时候如果输出obj1 和 num的值,分别是多少呢?
let obj = {'num1' : 20, 'num2' : 30}
const obj1 = obj
const num = obj.num1
obj.num1 = 40
import { createStore } from 'redux';
const store = createStore(fn);
reateStore函数接受另一个函数作为参数,**返回新生成的 Store 对象**
### State
Store对象包含所有数据就是State。
如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State。
当前时刻的 State,可以通过store.getState()拿到。
import { createStore } from 'redux';
const store = createStore(fn);
const state = store.getState();
React
React关联的技术栈
JavaScript
JavaScript 面向对象
原型编程
基于原型的编程不是面向对象编程中体现的风格,且行为重用(在基于类的语言中也称为继承)是通过装饰它作为原型的现有对象的过程实现的。这种模式也被称为弱类化,原型化,或基于实例的编程。
命名空间
Javascript中的普通对象和命名空间在语言层面上没有区别。 比如,声明一个全局命名空间
自定义对象 类
JavaScript是一种基于原型的语言,它没类的声明语句(ES6 开始提供 Class声明) JavaScript可用方法作类。定义一个类跟定义一个函数一样简单。 在下面的例子中,我们定义了一个新类Person。
对象(类的实例)
我们使用 new obj 创建对象 obj 的新实例, 将结果(obj 类型)赋值给一个变量方便稍后调用。
新增的
Object.create
方法来创建对象构造器 对象属性
可以使用 关键字 this调用类中的属性, this是对当前对象的引用。
继承 (prototype继承)
创建一个或多个类的专门版本类方式称为继承(Javascript只支持单继承)。 创建的专门版本的类通常叫做子类,另外的类通常叫做父类。 在Javascript中,继承通过赋予子类一个父类的实例并专门化子类来实现。 在下面的例子中, 我们定义了 Student类作为 Person类的子类. 之后我们重定义了sayHello() 方法并添加了 sayGoodBye() 方法
JavaScript 中的值和类型
JavaScript提供两种数据类型: 基本数据类型和引用数据类型
基本数据类型有
引用数据类型有
JavaScript 中比较对象
对于两个非原始值,比如两个对象(包括函数和数组),== 和 === 比较都只是检查它们的引用是否匹配,并不会检查实际引用的内容
默认情况下,数组将被强制转型成字符串,并使用逗号将数组的所有元素连接起来。所以,两个具有相同内容的数组进行 == 比较时不会相等
原型设计模式
原型模式可用于创建新对象,但它创建的不是非初始化的对象,而是使用原型对象的值进行初始化的对象。原型模式也称为属性模式。 原型模式在初始化业务对象时非常有用,业务对象的值与数据库中的默认值相匹配。原型对象中的默认值被复制到新创建的业务对象中。 经典的编程语言很少使用原型模式,但作为原型语言的 JavaScript 在构造新对象及其原型时使用了这个模式
Javascript 匿名函数
有名函数
匿名函数: 有关键词 function, 有小括号,有大括号,就是没有函数名。
执行匿名函数 方式1:把它放进一个变量里,这个变量就相当于一个函数名了
方式2:干脆不要名字,直接执行
方式3:作为另一个函数的参数
Javascript 闭包
首先看一下变量的作用域 变量的作用域无非就是两种:全局变量和局部变量。 函数内部可以直接读取全局变量。
在函数外部自然无法读取函数内的局部变量。
这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
如何从外部读取局部变量?
出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。 那就是在函数的内部,再定义一个函数。
函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。 既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
闭包的概念
我的理解是,闭包就是能够读取其他函数内部变量的函数。 由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成定义在一个函数内部的函数。 所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的用途
它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制回收。
这段代码中另一个值得注意的地方,就是
nAdd=function(){n+=1}
这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。使用闭包的注意点
Less
Less是一门css预处理语言,他扩展了css语言,增加了变量、Mixin、函数等特性,使css更容易维护和扩展。他不是一个直接使用的语言,而是一个生成css的语言。Less可以运行在Node或浏览器端。
可以把less理解成 动态样式语言
引入解析less的less.js
普通变量
less 以@开头定义变量,并且使用时直接键入@名称
Less其变量只能定义一次,不能重复定义,否则后面的会类似与js的变量提升,覆盖前面的变量。 使用变量设置css,方便代码的维护。
选择器变量
选择器变量让选择器变成一个动态的。 作为选择器的变量在使用的时候需要添加大括号 {},变量的前面可以添加选择操作符。
生成的CSS
属性变量
生成的CSS
Url变量
项目结构改变时,可以方便我们修改,目的是为了方便项目的维护。
生成的CSS
声明变量
变量运算
用变量的定义变量
解析的顺序是从后向前逐层解析
生成的 CSS
媒体查询
之前媒体查询的格式是, 先分屏然后在每一个媒体查询中设置样式。
less写法
生成的 CSS
参数
Less可以传递参数
Less 可以使用默认参数,如果没有传参数,那么将使用默认参数。 @arguments 犹如 JS 中的 arguments 指代的是全部参数。传的参数中必须带着单位。
条件语句
Less没有if / else 但是less具有一个when,and,not与“,”语法。
生成后的
继承(扩展)
extend是less的一个伪类。它可以继承所匹配声明中的全部样式 & 符号,表示的是上1级选择器的名字。
生成后的 CSS
导入
在less文件中可以引入其他的less文件。使用关键字import。 导入less文件,可以省略后缀。
Less中使用JavaScript
less本身是使用js实现的,所以在less中可以使用js。 Js的代码写在字符串模板里
生成后的 CSS
Less的函数
Less 有很多的内置的函数 http://lesscss.cn/functions/#functions-overview 比如
ECMAScript 6
let
let与var一样是用来声明变量的,与var的区别是 let所声明的变量,只在let所在的代码块内有效
作用域
通过let定义的变量,作用域是在定义它的块级代码以及其中包括的子块中,并且无法在全局作用域添加变量。 通过var定义的变量,作用域为包括它的函数作用域或者全局作用域
var的作用域
let的作用域
let 无法在全局作用域中定义变量
let 也非常适合 for 循环,在循环中 i 的值只在循环语句中生效,在外边取不到的。
重复声明
另外let不允许在同一作用域下声明已经存在的变量,即在同一作用域下不可以声明两个变量名相同的变量,会直接报错,但是var不会,可见let比var更加规范化 let未先声明变量就用变量会编译出错,但是var不会,会输出undefined
const
const声明的是常量。一旦声明,常量的值就不能改变。
当我们尝试去改变用const声明的常量时,浏览器就会报错。
下面的定义,试问这时候如果输出obj1 和 num的值,分别是多少呢?
答案
也就是说 const定义的对象,当对象改变了之后,const定义的值也会跟着改变。 cosnt定义的变量是一个对象的一个属性值,但是当对象属性值改变了以后,const定义的这个值并不会改变。
那么这是为了什么呢? 在计算机中,常量是放在栈中的,而对象是放在堆中的。对于对象赋值,const指向的仅仅是他的地址,cosnt仅仅是保证这个地址不改变,至于地址对应的数据,是可以进行改变的。 而如果定义一个简单的数据类型,那这个数据他本身就是存在栈中的,所以不可以改变。
class
class, extends, super 这三个特性涉及了ES5中最令人头疼的的几个部分:原型、构造函数,继承... ES6提供了更接近传统语言的写法,引入了Class(类)这个概念。新的class写法让对象原型的写法更加清晰、更像面向对象编程的语法,也更加通俗易懂。
Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。上面定义了一个Cat类,该类通过extends关键字,继承了Animal类的所有属性和方法。
super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。 ES6的继承机制,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。
箭头函数
ES6最常用的一个新特性了,用它来写function比原来的写法要简洁清晰很多
如果方程比较复杂,则需要用 {} 把代码包起来
长期以来,JavaScript语言的this对象一直是一个令人头痛的问题,在对象方法中使用this,必须非常小心。例如
运行上面的代码会报错,这是因为
在Javascript里面,this指针代表的是执行当前代码的对象的所有者
传统的解决方法有两种 第一种是将this传给self,再用self来指代this
第二种方法是用bind(this)
但现在我们有了箭头函数,就不需要这么麻烦了
当我们使用箭头函数时,函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,它的this是继承外面的,因此内部的this就是外层代码块的this。
箭头函数最直观的三个特点
解构
ES6 中允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构 同名变量解构赋值
不同变量解构赋值
default
default很简单,意思就是默认值。 下面的例子,调用animal()方法时忘了传参数,传统的做法就是加上这一句
type = type || 'cat'
来指定默认值。如果用ES6我们而已直接这么写
扩展运算符 ...
对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中
扩展运算符可以与解构赋值结合起来,用于生成数组
import 和 export
export 加default和不加的区别
export default也可以用来输出类
Promise
Promise对象可以理解为一次执行的异步操作,使用promise对象之后可以使用一种链式调用的方式来组织代码;让代码更加的直观
比如我使用ajax发一个A请求后,成功后拿到数据,我们需要把数据传给B请求;那么我们需要如下编写代码
这种情况下,代码不够直观。
Promise 基本用法
创建一个 Promise
该构造函数接收两个函数作为参数,分别是resolve和reject。
然后,我们通过then方法,分别指定resolved状态和rejected状态的回调函数
then方法可以接收两个回调函数作为参数,第一个回调函数就是fulfilled状态时调用;第二个回调函数就是rejected时调用。这边的第二个参数是可选的,不一定要提供
Promise.all
传入的参数中任意一个promise返回失败时,那么整体立即返回失败,返回的错误信息是第一个失败的promise结果
Promise.prototype. catch()
推荐使用catch的写法 不要在then方法中定义rejected状态的回调函数;这是因为使用catch还可以捕获在then方法执行中存在的错误
Promise.prototype. finally()
返回一个Promsie。是指,在上一轮 promise 运行结束后,无论fulfilled还是 rejected,都会执行指定的回调函数。 该方法适合,无论结果如何都要进行的操作,例如清除数据
Promise.all 是并行处理,全部处理完成之后,到then里面处理
Promise.reduce
Promise.reduce 方法是一个顺序执行 Promise 的方法,所谓顺序执行,其实就是从左到右按顺序去创建 Promise ,并且始终只有一个 Promise 在运行。
async await
在async/await之前,我们有三种方式写异步代码
但是,这三种写起来都不够优雅,ES7做了优化改进,async/await应运而生,async/await相比较Promise 对象then 函数的嵌套,与 Generator 执行的繁琐(需要借助co才能自动执行,否则得手动调用next() ), Async/Await 可以让你轻松写出同步风格的代码同时又拥有异步机制,更加简洁,逻辑更加清晰。
async/await是一个用同步思维解决异步问题的方案(等结果出来之后,代码才会继续往下执行)
可以通过多层 async function 的同步写法代替传统的callback嵌套
async 函数返回的是一个 Promise 对象
改写成 async await 的方式
async 函数返回的是一个 Promise 对象,如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。 await 只会出现在 async 函数中,我们使用 async/await 时,几乎不需要 .then,因为 await 为我们处理等待;但是在代码的顶层,当我们在 async 函数的外部时,我们在语法上是不能使用 await 的,所以通常添加 .then/catch 去处理最终结果或者 error。
单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了 假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用 setTimeout 来模拟异步操作
如果用 async/await 来实现呢,会是这样
Object.assign
基本用法
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)
Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。 注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
Babel
babel是一个JS编译器,用来转换最新的JS语法,比如把ES6, ES7等语法转化成ES5语法,从而能够在大部分浏览器中运行。像箭头函数,就可以做转换。babel在执行过程中,分三步:先分析(parsing)、再转化、最后生成代码。
但babel只转换语法的话,一些最新的api是不转化的,比如Object.assign, Promise等。所以babel还提供了很多插件,也就是babel-ployfill。安装后,即可支持浏览器运行。 babel还可以转换JSX语法,对React支持比较好
一般与webpack结合使用 (babel-loader)
NPM / Yarn
NPM
npm 不需要单独安装。在安装 Node 的时候,会连带一起安装 npm 。但是,Node 附带的 npm 可能不 是最新版本,最后用下面的命令,更新到最新版本
npm init
npm init 用来初始化生成一个新的 package.json 文件。它会向用户提问一系列问题,如果你觉得不用修改默认配置,一路回车就可以了。 如果使用了 -f(代表 force)、-y(代表 yes),则跳过提问阶段,直接生成一个新的 package.json 文件。
npm search
npm search 命令用于搜索 npm 仓库,它后面可以跟字符串,也可以跟正则表达式。
npm list
npm list 命令以树形结构列出当前项目安装的所有模块,以及它们依赖的模块。
npm install
Node模块采用npm install命令安装。 每个模块可以全局安装,也可以本地安装。“全局安装”指的是将一个模块安装到系统目录中,各个项目都可以调用。一般来说,全局安装只适用于工具模块,比如eslint和gulp。“本地安装”指的是将一个模块下载到当前项目的node_modules子目录,然后只有在项目目录之中,才能调用这个模块。
install命令总是安装模块的最新版本,如果要安装模块的特定版本,可以在模块名后面加上@和版本号。
install命令可以使用不同参数,指定所安装的模块属于哪一种性质的依赖关系,即出现在packages.json文件的哪一项中。
比如
npm install默认会安装dependencies字段和devDependencies字段中的所有模块, 如果使用--production参数,可以只安装dependencies字段的模块。
一旦安装了某个模块,就可以在代码中用require命令加载这个模块。
npm update
npm update命令可以更新本地安装的模块。
使用-S或--save参数,可以在安装的时候更新package.json里面模块的版本号。
npm run
npm不仅可以用于模块管理,还可以用于执行脚本。package.json文件有一个scripts字段,可以用于指定脚本命令,供npm直接调用。
上面代码中,scripts字段指定了两项命令lint和test。 命令行输入 npm run lint,就会执行jshint **.js, 输入npm run test,就会执行mocha test/。 npm run是npm run-script的缩写,一般都使用前者,但是后者可以更好地反应这个命令的本质。
npm内置了两个命令简写,npm test等同于执行npm run test,npm start等同于执行npm run start。
YARN
yarn是由Facebook,Google,Exponent,Tilde联合推出的一个新的JS包管理工具,yarn是为了弥补npm的一些缺陷而出现的。
NPM VS Yarn
yarn性能更好一些 无论 npm 还是 Yarn 在执行包的安装时,都会执行一系列任务。npm 是按照队列执行每个 package,也就是说必须要等到当前 package 安装完成之后,才能继续后面的安装。而 Yarn 是同步执行所有任务,并行安装,提高了性能
yarn.lock 版本锁定 为了跨机器安装得到一致的结果,Yarn 需要比你配置在 package.json 中的依赖列表更多的信息。 Yarn 需要准确存储每个安装的依赖是哪个版本。 为了做到这样,Yarn 使用一个你项目根目录里的 yarn.lock 文件。
将可构建的yarn.lock文件push到代码仓库中, 来确保整个团队都有一个可返回构建运行的版本
比如 package.json
代表使用 3.x.x中最新的版本 在yarn.lock固定了你使用的版本
将yarn.lock放到代码仓库中, 来确保整个团队 yarn install 时,都使用 3.6.5 的版本。 当yarn upgrade更新包时候,yarn.lock文件才会更新了,安装包才会更新版本。
axios
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。 执行一个GET请求
执行一个POST请求
调用axios , axios返回的是 promise 对象
React
React的功能其实很单一,主要负责渲染的功能,现有的框架,比如angular是一个大而全的框架,用了angular几乎就不需要用其他工具辅助配合,但是react不一样,他只负责ui渲染,想要做好一个项目,往往需要其他库和工具的配合,比如用redux来管理数据,react-router管理路由,react已经全面拥抱es6,所以es6也得掌握,webpack就算是不会配置也要会用,要想提高性能,需要按需加载,immutable.js也得用上, 等等
使用组件化开发方式,符合现代Web开发的趋势
React中的核心概念
虚拟DOM
React将DOM抽象为虚拟DOM,虚拟DOM其实就是用一个对象来描述DOM,通过对比前后两个对象的差异,最终只把变化的部分重新渲染,提高渲染的效率
VituralDOM的处理方式
Diff算法
当你使用React的时候,在某个时间点 render() 函数创建了一棵React元素树, 在下一个state或者props更新的时候,render() 函数将创建一棵新的React元素树, React将对比这两棵树的不同之处,计算出如何高效的更新UI(只更新变化的地方)
key 属性
当在子节点的后面添加一个节点,这时候两棵树的转化工作执行的很好
执行过程 React会匹配新旧两个
<li>first</li>
,匹配两个<li>second</li>
,然后添加<li>third</li>
但是如果你在开始位置插入一个元素,那么问题就来了在没有key属性时执行过程 React将改变每一个子删除重新创建,而非保持
<li>Duke</li>
和<li>Villanova</li>
不变 为了解决以上问题,React提供了一个 key 属性。当子节点带有key属性,React会通过key来匹配原始树和后来的树。执行过程 现在 React 知道带有key '2014' 的元素是新的,对于 '2015' 和 '2016' 仅仅移动位置即可
注意
<li key={item.id}>{item.name}</li>
React的基本使用
安装
React元素
React元素 ≠ DOM元素 React元素表示应该如何创建浏览器DOM的一组指令 使用 React.createElement 创建一个React元素来表示 h1 标题元素
在渲染的过程中,React将会把这个元素转换成实际的DOM元素
因此来说,一个React元素只是一个JavaScript语法,用来告知React如何构造Dom元素 但 createElement() 方式,代码编写不友好,太复杂,推荐使用JSX
使用数据构造元素
使用React的一个优点是它可以将数据和UI元素有效隔离。 比如,将一个数组数据映射为li元素
注: map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值
React 组件
React 组件可以让你把UI分割为独立、可复用的片段,并将每一片段视为相互独立的部分。 组件是由一个个的HTML元素组成的
概念上来讲, 组件就像JS中的函数。它们接受用户输入(props),并且返回一个React对象,用来描述展示在页面中的内容
React创建组件的方式
主要使用 ES6 的方式 React.Component 是一个主要特性,兼容ES6规范,它是一个抽象类,用户可以通过它来构建新的组件
无状态组件是 通过 JavaScript函数创建的
无状态函数式组件,顾名思义,无状态,也就是你无法使用State,也无法使用组件的生命周期方法,这就决定了函数组件都是展示性组件,接收Props,渲染DOM,而不关注其他逻辑。
无状态组件的创建形式使代码的可读性更好,并且减少了大量冗余的代码,精简至只有一个render方法,大大的增强了编写一个组件的便利,除此之外无状态组件还有以下几个显著的特点
其实无状态函数式组件也是官方比较推荐的一种方式,尽量让底层的组件变成无状态函数式组件,也即组件尽量只用来显示数据,把数据操作的逻辑都放在顶层,然后从顶层把数据传到底层
JSX
JSX - JavaScript XML 允许用户使用类似HTML的语法来定义React元素 推荐使用 JSX 的方式创建组件 JSX语法,最终会被编译为 createElement() 方法
JSX的语法需要通过 babel-preset-react 编译后,才能被解析执行
注意
style={{}}
是两个大括号 属性名同样需要驼峰命名法。即margin-top要写成marginTopProps
组件中有一个 只读的对象 叫做 props,无法给props添加属性 作用是将传递给组件的属性转化为 props 对象中的属性 React把传递给组件的属性转化为一个对象并交给 props
props.children
它表示组件所有的子节点 在组件内部使用 this.props.children,可以拿到用户在组件里面放置的内容
this.props.children 的值有三种可能
所以,处理 this.props.children 的时候要小心。 官方建议使用React.Children.map来遍历子节点,而不用担心数据类型
PropTypes 类型检查
DefaultProps
类的静态属性
ES6提供类的静态属性,允许用户在类的声明中封装propTypes和defaultProps属性。
State
State是一种内置的数据管理机制,它可以修改组件内部的状态。 当应用程序状态变化时,UI会被重新渲染来反映这些变化。
状态即数据
注意
从外部获取数据后,使用setState来绑定状态,在 componentDidMount 里面做。
重新渲染时机
每当组件状态改变时,React 都会调度渲染。 更改状态意味着当我们调用 setState 函数时,React 触发更新。这不仅意味着将调用组件的 render 函数,而且还意味着将重新渲染其所有后续子组件,无论其 prop 是否已更改。
setState会触发子组件的render。但是并不一定会刷新DOM,有变化才会刷新DOM。 但是触发没必要的render就会触发虚拟Dom对比运算,也是有消耗的。 所以最好redux等状态管理库,来管理数据更新
react render渲染的几种情况
shouldComponentUpdate
这个功能是 React 生命周期的功能之一,它允许我们通过告诉 React 什么时候更新类组件来优化渲染性能。 它的参数是组件的下一个 props 和下一个状态
如果return false的话,组件将不会渲染
数据流和组件间的通信
React单向数据流
父子组件沟通
在React中,最为常见的组件沟通也就是父子了,一般有两种情况
父组件更新子组件状态 很容易理解,传递给子组件的props更新,子组件的render被触发。重新渲染。
第二种子组件更新父组件状态的情况。 一般情况下,只能由父组件通过props传递数据给子组件,使得子组件得到更新,那么现在,我们想实现 子组件更新父组件就需要父组件通过props传递一个回调函数到子组件中,这个回调函数可以更新父组件,子组件就是通过触发这个回调函数,从而使父组件得到更新
点击子组件的button,会调用父组件的回调函数search,回调函数search里面,通过setState来更新父组件的状态。
兄弟组件沟通
当两个组件处于同一级时,就称为兄弟组件。
按照React单向数据流方式,我们需要借助父组件进行传递,通过父组件回调函数改变兄弟组件的props。 其实这种实现方式与子组件更新父组件状态的方式是大同小异的。
context
除了props来传递回调函数的方式以外,React来提供的context上下文 来实现。 context 表示上下文,将好像组件里面的全局变量一样,指定 context 允许我们将变量从一个组件传递到另一个组件
Context 设计目的是为了让组件共享那些对于一个组件树而言是“全局”的数据
在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的(单向数据流),但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。 Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。
在父组件中 定义上下文类型
子组件,通过 this.context 访问 context 中的数据
模块
ES6 模块
import命令用于输入其他模块提供的功能 export命令用于规定模块的对外接口
CommonJS 模块化
require 与 module.exports
require 在 ES6 和 CommonJS 中都支持 即使我们使用了 ES6 的模块系统,如果借助 Babel 的转换,ES6 的模块系统最终还是会转换成 CommonJS 的规范。
Node 中默认支持 CommonJS 这个服务器端模块化规范,但是对 ES6 的模块化支持并不是太友好,所以需要通过 babel 这个第三方插件在 Node 中来体验高级的 ES6 特性。 nodejs12 开始支持es6模块加载
组件扩展
组件声明周期
组件生命周期函数的定义:从组件被创建,到组件挂载到页面上运行,再到页面关闭组件被卸载,这三个阶段总是伴随着组件各种各样的事件,那么这些事件,统称为组件的生命周期函数
组件的生命周期包含三个阶段:创建阶段(Mounting)、运行和交互阶段(Updating)、卸载阶段(Unmounting)
创建阶段 (Mounting)
该阶段的函数只执行一次
通过 constructor() 的参数props获取 设置state和props
componentWillMount 在render()之前被调用,因此在这方法里同步地设置状态将不会触发重渲染 无法获取页面中的DOM对象 可以调用 setState() 方法来初始化状态
render 渲染组件到页面中,无法获取页面中的DOM对象
componentDidMount
运行阶段(Updating)
该阶段的函数执行多次
每当组件的props或者state改变的时候,都会触发运行阶段的函数
可以通过对比this.props和nextProps并在该方法中使用this.setState()处理状态改变
shouldComponentUpdate
根据这个方法的返回值决定是否重新渲染组件,返回true重新渲染,否则不渲染, 如果返回值为false,那么,后续render()方法不会被调用
componentWillUpdate
组件将要更新
render 重新渲染组件,与Mounting阶段的render是同一个函数
componentDidUpdate 组件已经被更新
卸载阶段(Unmounting)
组件卸载期间,函数比较单一,只有一个函数,这个函数也有一个显著的特点:组件一辈子只能执行一次!
高阶组件 HOC
高阶组件(HOC)是 React 中用于重用组件逻辑的高级技术。 HOC 本身不是 React API 的一部分。 它们是从 React 构思本质中浮现出来的一种模式。 具体来说,高阶组件是一个函数,能够接受一个组件并返回一个新的组件。 构建一个简单的hoc
React中,一个高阶组件只是一个包装了另外一个 React 组件的 React 组件
实现高阶组件的方法有如下两种
属性代理
属性代理是我们react中常见高阶组件的实现方法,我们通过一个例子来说明
我们想要使用MyContainer这个高阶组件就变得非常容易
MyContainer这个高阶组件可以做什么?
你可以读取,添加,修改,删除将要传递给 WrappedComponent 的 props 渲染劫持 它被叫做渲染劫持是因为高阶组件控制了 WrappedComponent 生成的渲染结果,并且可以做各种操作。
你可以从外部取得数据,然后将得到的数据(state)作为组件的props传递
反向继承
总结
所有高阶组件都是函数 WrappedComponent是我们希望包装的组件,返回的类MyContainer,主要用于存储和管理state。 比如,在高阶组件MyContainer,取得数据,作为自己的state,当State发生变化并且数据载入完毕,WrappedComponent组件会被渲染,并且state数据,会作为属性props传递给他。
通常的用法是,WrappedComponent组件,负责得到数据,是一个接受props的无状态组件,高阶组件MyContainer负责取得数据,然后传递给WrappedComponent组件,渲染UI。
如果不使用高阶组件,WrappedComponent组件内部,需要取得数据,然后渲染UI。 使用了高阶组件,用户就可以构建更多的无状态函数组件,以便可以一心一意的管理UI。 而高阶组件MyContainer负责取得数据。
受控组件 非受控组件
受控组件
在HTML当中,像input,textarea和select这类表单元素会维持自身状态,并根据用户输入进行更新。 在React中,可变的状态通常保存在组件的state中,并且只能用 setState() 方法进行更新. React根据初始状态渲染表单组件,接受用户后续输入,改变表单组件内部的状态。 因此,将那些值由React控制的表单元素称为:受控组件。
受控组件的特点
// 当文本框内容改变的时候,触发这个事件,重新给state赋值 handleTextChange = event => { console.log(event.target.value) this.setState({ msg: event.target.value }) }
// 通过ref的方式来获取,即获取该节点(非受控组件)
// 直接取相关节点的值 this.refs.username.value
import { createStore } from 'redux'; const store = createStore(fn);
import { createStore } from 'redux'; const store = createStore(fn); const state = store.getState();
const action = { type: 'ADD_TODO', payload: 'Learn Redux' };
const ADD_TODO = 'ADD_TODO'; function addTodo(text) { return { type: ADD_TODO, text } }
const action = addTodo('Learn Redux');
import { createStore } from 'redux'; const store = createStore(fn);
store.dispatch({ type: 'ADD_TODO', payload: 'Learn Redux' });
store.dispatch(addTodo('Learn Redux'));
const reducer = function (state, action) { // 计算新的state return new_state; };
const defaultState = 0; const reducer = (state = defaultState, action) => { switch (action.type) { case 'ADD': return state + action.payload; default: return state; } };
const state = reducer(1, { type: 'ADD', payload: 2 });
import { createStore } from 'redux'; const store = createStore(reducer);
import { createStore } from 'redux'; const store = createStore(reducer); store.subscribe(listener);
let unsubscribe = store.subscribe(() => console.log(store.getState()) ); unsubscribe();
store.getState() store.dispatch() store.subscribe()
import { createStore } from 'redux'; let { subscribe, dispatch, getState } = createStore(reducer);
let store = createStore(todoApp, window.STATE_FROM_SERVER)
import { combineReducers } from 'redux'; const chatReducer = combineReducers({ chatLog, statusMessage, userName })
const reducer = combineReducers({ a: doSomethingWithA, b: processB, c: c })
import { combineReducers } from 'redux' import * as reducers from './reducers'
const reducer = combineReducers(reducers)
store.dispatch(action);
// 设置监听函数 store.subscribe(listener);
function listerner() { let newState = store.getState(); component.setState(newState);
}
// 定义了一个无状态的组件,没有state,只接受props const Counter = ({ value, onIncrement, onDecrement }) => (
{value}
);
// 定义了reducer,处理state的更新 const reducer = (state = 0, action) => { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } };
// 创建store const store = createStore(reducer);
// 渲染UI const render = () => { ReactDOM.render( <Counter value={store.getState()} onIncrement={() => store.dispatch({type: 'INCREMENT'})} // 点击,发出Action onDecrement={() => store.dispatch({type: 'DECREMENT'})} // 点击,发出Action />, document.getElementById('root') ); };
render(); // 设置监听函数render, 当state发生变化的时候,会调用render,重新渲染UI store.subscribe(render);
let next = store.dispatch; store.dispatch = function dispatchAndLog(action) { console.log('dispatching', action); next(action); console.log('next state', store.getState()); }
const store = createStore( reducer, initial_state, applyMiddleware(thunk, promise, logger) );
npm install --save redux-thunk
// store.js import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import rootReducer from '../reducers/root_reducer';
const store = () => createStore(rootReducer, applyMiddleware(thunk)); export default store;
export const initListAction = (list) => { return { type: constants.INIT_LIST, list } }
export const getTodoList = () => { return (dispatch) => { axios.get('/api/list.json').then(res => { const { data } = res; const action = initListAction(data); dispatch(action); }) } }
componentDidMount () { const action = getTodoList(); store.dispatch(action); }
const Title = value =>
{value}
;import { connect } from 'react-redux' const VisibleTodoList = connect()(TodoList);
import { connect } from 'react-redux' const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList)
const mapStateToProps = (state) => { return { todos: getVisibleTodos(state.todos, state.visibilityFilter) } }
const getVisibleTodos = (todos, filter) => { switch (filter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) default: throw new Error('Unknown filter: ' + filter) } }
const mapDispatchToProps = ( dispatch, ownProps ) => { return { onClick: () => { dispatch({ type: 'SET_VISIBILITY_FILTER', filter: ownProps.filter }); } }; }
const mapDispatchToProps = { onClick: (filter) => { type: 'SET_VISIBILITY_FILTER', filter: filter }; }
import { Provider } from 'react-redux' import { createStore } from 'redux' import todoApp from './reducers' import App from './components/App'
let store = createStore(todoApp);
render(
You clicked {count} times
AwesomeSite
{username}
Count: {state.count}
Loading ...
} returnYou're viewing: {person.name}
Height: {person.height}
Mass: {person.mass}