Free Code Camp 学习笔记

Yizhe Wang
34 min readJun 22, 2020

React

  1. React 可以使用JSX直接来写html 的code:
const JSXCODE = <h1>Hello World!</h1>如果想要在屏幕上看到这个JSXCODE的话,需要render这个react的codeReactDOM.render(JSXCODE, document.getElementById('root'));这就是把这个JSXCODE放到root里面去,root是body的根元素,所以就等于把这个<h1>的元素放到了body里面,这样就可以显示出来了。

2. 当然JSX也可以是多个元素组成的一个div,但是注意,一个JSX只能返回一个元素,可以是多重嵌套的,但是不能分开的:

false:const JSX = <p> hello world </p>
<p> some other text </p>
correct: const JSX = <div>
<p> hello world </p>
<p> some other text </p>
</div>

3. comment: {/* comment */}

4. 上文中提到,如果想让这个JSX表导出来,得把它在入道html文件中去,也就是一个render的过程: ReactDOM.render(componentToRender, targetNode)

5. 关于class:由于class是js的keyword,但是我们在写html是也会用到class property,解决方法:html中的class=》className

const JSX = (
<div className="some-class-name">
<h1> SOME TEXT </h1>
</div>
)
这样就解决了class关键字冲突的问题。还有就是JSX一定要close tag: <br> => <br />; <hr> => <hr />

6. 不一定就要用JSX的方式来创建html,也可以用React的component的方式来建立:

两种方法, 1. javascript function:
const componentName = () => {
return (
<div>
<p>HTML body</p>
</div>
);
}
2. ES6 class:
class ComponentName extend React.Component {
constructor(props) {
super(props);
}
render() {
return (<div><p>HTML body</p></div>);
}

7. Component之间可以相互嵌套,但是要用< />套起来

const firstComponent=()=>{
return <div><h1>Some TEXT</h1></div>;
}
class SecondComponent extends React.Component {
constructor(props){super(props);}
render() { return <div><firstComponent /></div>;}
}
const thirdComponent=()=>{
return <div><h1>testing</h1><SecondComponent /></div>;
}
这样写是可以的
*************************
但是在采用reactDOM来render的时候要注意,和JSX不一样,JSX直接挂变量名,而这时候要用< />套起来
*************************
ReactDOM.render(<thirdComponent />, document.getElementById('target-id'));

8. Props=> properties,可以作为参数被pass到return的JSX中:

const someComponent = (props) => <div><p>{props.name}</p></div>
*****************
props 也可以是一个array
*****************
那如果我们要取出array的值,我们就需要用到joinconst TestComponent = (props) => <h1>{props.varname.join(", ")</h1>const SomeComp = () => <div><TestComponent varname={["格式", "要", "记住"]};这时候,varname中的array元素就会被join起来,做成一个string,返回给这个<h1>然后在表现层表达。*******************
我们也可以给这个props一个初始值,当不设定的的时候,我们自动取这个初始值
*******************
TestComponent.defaultProps = {key:value};使用的时候: <div>
<TestComponent key="new-val" />
</div>
*******************
特殊情况:当我们要设定的元素是一个数字的时候,要注意,这种key=“val”的形式只能传递string,传递int需要加{}, 让react知道,这是在传递一个javascript的object,而不是string了。
*******************
<div>
<TestComponent quantity={10} />
</div>

**********
propTypes: 我们可以要求传入数据的种类,这样会使系统更安全;要注意这个地方也是hash
**********
TestComponent.propTypes = {quantity:PropTypes.number(/func...)

9. this.props.data: 之前我们说的都是component之前传递props,如果是class怎么传递呢?其实也很简单,只需要在props前面加一个this就可以了:

class One extends React.Component {
constructor(props) {super(props);}
render() {return <div><h1>{this.props.words}</h1></div>;}
}
class Two extends React.Component {
constructor(props) {super(props);}
render() {return <div><h1>Hi, these are the words</h1><br /><One words="nothing" />;}
}

10. ********* STATE ************

state 是一个很重要的概念,就是这个component目前处于一个什么态,这个态是可以改变的。state这个元素在这个object内部是可以改变的,我不知道prop可不可以改变。class A extends React.Component {
constructor(props) {
super(props);
this.state = {key:value};
//必须是一个javascript的object
}
render() {
if (this.props.varname == "some_value") {
this.state = {key: "altered_value");
}
return <h1>{this.state.key}</h1>;
}
}
这种写法是可行的,也就是说,我们可以通过传入不同的props来影响render出来的html的状态。虽让上述的写法可行,但是react还是不允许这种写法,它要求修改state的行为一定要通过一个叫做setState的函数来进行:render() {
if(this.props.varname == "some_value") {
this.setState({key: "altered_value"});
}
}
这种setState的方法还是比较直接的。可以通过其他的function来完成这个setState的过程,但是略复杂,涉及到function 和 this 之间的关系class SomeComponent extends React.Component {
constructor(props) {
super(props);
this.state = {key:value};
this.funcName = this.funcName.bind(this);
//这样一来,这个function(funcName)就已经和这个component 绑定上了,否则会出现function undefined的这个情况。
}
funcName() {
this.setState({key:new_value});
}
render() {
<div>
<button onClick={this.funcName}>Submit</button>
</div>
}
}

**********************
bind(this)
**********************
javascript既是oop也是functional 的,它的function是可以传递给变量的,
const aFunc = (words) => console.log(words);
const bFunc = aFunc;
bFunc("hello world");

是可以执行的。但是如果我们处理的是对象中的method,而不是这种function的话,就会出现问题,再给一个variable赋值成instance method的时候,“this”就不再是instance/object 本身了,这个可能就变成了一个当下正在处理的某个元素了,这样很容易就出现了method undefined这种情况了。
dog = { name : "Paul",
bark : function(){ return `${this.name} said Whoof!`;}
}
let dogBark= dog.bark;
dogBark(); => undefined said Whoof! 因为这个时候的this已经不是dog了,处理这个问题,我们就需要:
dogBark = dogBark.bind(dog);就相当于把dogBark的this设定成dog了,就这么个操作。这个**********(bind(dog))的做法就会导致在全局作用域下,不管什么地方call这个function,他永远是针对于dog这个object的,不会再去根据“this”关键字的变化而去处理其他的object。************
https://blog.csdn.net/Jutal_ljt/article/details/53381670
  • *************
  • EVENTLISTENER
  • *************
事件就是类似于onclick,onhover这类的行为被触发,但是事件的触发并不是针对一个单独的object的,一个事件是在全局都可以感知到的,一个button 被触发,最外层的body也是知道的,那么可能我们只需要改变某个被触发的元素,但也有可能我们需要改变被触发元素的parent元素,这个时候就涉及到一个时间顺序问题,有的时候我们希望先改变parent level,有的时候我们希望先改变child level,这就涉及到一个capture还是bubble的问题,MicroSoft和netscape有不同的原则,但是现在的浏览器和javascript支持capture和bubble两种操作方式,只需要在addEventListener('click', funcName, true/false)的第三个parameter输入true false选项就可以了,true=》capture;false=〉bubble,default=》false;
https://www.youtube.com/watch?v=BtOrr7oTH_8

STATE中还有一个关键点:如果我们需要根据之前的state的值来得到一个新的值,我们需要用“function”

原state:

class MyComp extends React.Component {
constructor(props) {
super(props);
this.state = {key:true};
this.methodThatCanBeUsedByClass = this.methodThatCanBeUsedByClass.bind(this);
}
render() {
return (<div><h1>{this.state.key}</h1></div>)
}
}
如果我们想要根据this.state来返回目前this.state.key的相反值怎么办?一个toggle?this.setState({key: !this.state.key}) 是不行的,react对state的update不是一个个来的,很有可能是一起打包做一次,这样的话,如果是async的,我们在set的时候,很可能这个this.state和我们真正想得到的结果已经不一样了。所以不能使用this.state来做,方法如下:***********************
this.setState((state)=>({key: !state.key}))
***********************
注意,我们为setState带入了一个function,这个function take state作为参数,然后返回这个参数的新的key,这样做就避免了async造成的问题
https://juejin.im/post/599b8f066fb9a0247637d61b

看了掘金这篇文章我们会知道,其实setState可以接受两个参数,一个是“partialState"一个是“callback”, partialState,它是新的state用来更新之前的state。callback作为回调函数,会在更新结束之后执行。

也就是说:this.setState((state, prop)=>({manipulate state and prop to return a new state}));是一个固定写法,就是为了避免this.state和this.prop出现异步更新的问题。

************* 一定要注意this.setState,经常中招***************

onChange 这个是在JSX中的:

class Aform extends React.Component {
constructor(props) {
super(props);
this.state = {input: '', submit: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState(state=>({input: event.target.value}));
}
handleSubmit(event) {
this.setState(state=>({submit: state.input}));
}
render() {
return (
<div><form onSubmit={this.handleSubmit}>
<input value={this.state.input} onChange={this.handleChange} />
<button type="submit>SUBMIT</button>
</form></div>
);
}这个input的value也是在不停变化的,这个变化是来自于每次onChange的update,也就时说,每次我们多打一个字,this.state.input都发生一次变化,并不是append,而是重做。handleSubmit是一个一次性的工作,就是看当时的this.state.input是什么,是什么就把this.state.submit改成什么。
  • ****************
  • LIFECYCLE
  • ****************

就是可以在特定的时候做某件事,比如在render图像之前,之后。。。

componentWillMount() {} : 在render之前做某事

componentDidMount() {} :

this.state = {activeUsers: null}componentDidMount() {
setTimeout(()=>{this.setState({activeUser: 1273})}, 1000);
}
render() {
return <div><h1>Active Users: {this.state.activeUsers}</h1></div>
}
这段代码就将didMount的功能展示出来了,也就是说在render之后,didMount就会自动被call,然后会执行内部的代码,我们是让他在1秒后将activeUser改变成1273,我们也会清晰的从browser中发现,render一开始,Active User是空的,过了一秒变成1273.
还有就是在执行componentDidMount()之后再放置一个componentWillUnmount(),这个方法的作用就是将降一些在componentDidMount()方法中放置的一些eventListener的东西清除掉,否则会产生某些eventListener可能一直在执行的问题。life cycle来说也就是是一个删除后的清理工作。
**********如何unmount?***********

shouldComponentUpdate() {}:

react的原则就是当component收到新的state或者是props的时候,他就会开始re render,但是这对性能是不优化的,我们应该让他在发现props或者states和之前的counterpart不一样的时候才去做修改,也就是说,if(currentProps != previousProps || currentState != previousState) { render() {updated view}.shouldComponentUpdate(nextProps, nextState) {
return nextProps == this.props && nextState == this.state ? false : true; //true就是要update
}

componentDidUpdate() {}:

这个顾名思义,就是在render的东西被改变之后,但是这个和componentDidMount有什么区别???********************

setTimeout:

setTimeout(function(){return someValue}, 1000); 两个参数,一个function,一个毫秒数。

Style

React也可以inline的添加style:添加方式:style={{color: "red", fontSize: "75"}}
注意这种方式首先是value需要用引号套起来,其次就是不能有kabab-case, font-size就不OK了, 要用fontSize,还有就是px这类的可以省略。

**********
&&
***********

很重要: (a&&b) return的是什么? 
如果a==false,那么直接return false,如果a!=false,return b
|| 也是一个道理
a||b, 如果a为truthy value那么return a,否则return b。
所以说在我们做JSX内部的判断的时候,我们就可以{condition && <element>} 如果condition 为false,那么就直接一个falsy value,那么就不会return 任何东西,curly braces中间也就是套了一个falsy的value,那么也不会有任何的html返回去,如果前者condition为真,那么后面的这个element 就会被返回去,这个很有效,省去写if else和turnary statement

react 优先级概念,在碎片时间进行高优先级的操作,react fiber

react 与 vue的对比:

react 灵活性更好,处理复杂业务时选择多,

vue 灵活性差,api做的好,面对用户简单一些的可以用vue

搭建react js 开发环境

添加reactjs的方法:1.html 加script标签

2。用脚手架,react create app。。。

grunt,webpack编写脚手架。

脚手架也可以公司各自建立。

最好使用官方的,使用简单,定制性好。create-react-app

code:

npm install -g create-react-app
create-react-app my-app
cd my-app
npm start

脚手架会自动生成下面的文件

首先了解一下package.json,这其实是node的一部分,是node的一个包文件,使项目变为一个node的包。这个package.json应该是node的配置文件,内容如下。

PWA: progressive web application

register service worker: 可以帮助浏览器存储网页,用来以后离线观看。也就是app化。

app.test.js 适用于自动化测试的。

manifest.json里面存的也是关于register service worker 相关的内容,也就是app形式展示的问题,里面可以设置离线http app的icon啊这些东西。

REACT 就是一个个小的组件,前段组件化。

引用关系:

import React { Component } from ‘react’ 或者 const Component = React.Component

只要用了JSX,我们就一定要引用React。render函数中的标签都是JSX

如果使用JSX 标签语法,如果是自定义的组件,要大写。<App /> (<app />不认)

reactDom就是用于把react的组件连接到html文件的手段。

import {Fragment} from 'react'
这个代码的意义就在于避免了每个JSX组件都是一个div有时候会造成css排版上的问题,这样一来,<Fragment></Fragment>里面的代码就会既被看成是一个代码块,同时右可以不是div形式存在。

注意: 下面代码的第38行,我们要注意,onclick接受的是一个函数,并不是执行这个函数,我之前想deleteMessage(idx)并且直接带入idx进去,这时候其实就是执行一个函数了,而不是把函数作为变量带进去,这个有的时候会有一点confusing。

如果我想带入<h1>Hello World<h1>这种带有样式的数据,并且希望网页给予现实的话就要这么写:

<li key={idx(or uuid) onClick={()=>deleteMessage(idx)} dangerouslySetInnerHTML={{__html: ele}}
  • *****有一些关键字,react自留,for:htmlFor, class: className, onclick: onClick

destructuring assginment:

let array = [1,2,3]
[a, b, c] = array
// a=>1, b=>2, c=>3
let example = {hello: "world"}
let {hello} = example
//hello=>"world"
  • ****注意最后一行的return {messages: messages} 可以用 return {messages}来代替,es6的语法糖,也就是说如果hash keyName如果和value的varName一致,则可以用一个varname来代表整个的key value pair
  • ****react有单向数据流的特点,意思是,数据是可以从父组件传递到子组件的,但是不能在子组件中修改父组件。这个东西传下来就变成readonly了,只可以把改变父组件的function用props一路传下来,然后调用这个方法即可。
  • *** 可以通过强制类型来确定父传子的props的类型。

这种强类型的方式需要import proptypes的库

import PropTypes from 'prop-types';
Component.propTypes = {
test: PropTypes.string.isRequired, 这样一来就要求这个必须是string,而且必须存在。
message: PropTypes.string,
deleteMsg: PropTypes.func,
someOther: PropTypes.oneOfTypes([PropTypes.number, PropTypes.string])
//这个就表示someOther这个变量既可以是number,也可以是string};

defaultProps这个属性就是说如果在上面的propTypes中的某一项出现required这种值,而有的时候父组件没有向下传递相应值的时候就会出现报错,这个方式能给一个default值,避免报错

  • **** render()这个function就是保证在state或者是props的值有改变的时候,马上就刷新页面。父组件的render执行时,子组件的render也会执行。
  • *****虚拟DOM
  • react virtual dom的概念就是为了提升性能,因为如果用js来不断的生成real dom,并且比对后,在生成新的dom去替换原有dom的话,就会导致大量的性能损耗,因为生成任何一个dom的话,javascipt都要调用一个web application级别的api,然后去生成对象,如果我们以指定的形式去存储一个虚拟的dom,which is 一个js对象的话,就会非常的节省性能,js对象就是内存上的几个slot而已,init起来也简单,diff起来也简单,
function Item() {    return (
<div id="hello"><span>World</span></div>
)
}
这个Item function所给我们生成的<div>可以转化成一个js对象
['div', {id: "hello"}, ['span', {}, 'World']]
用createElement实现就是: createElement({'div', {id: "hello"}, createElement({'span', {}, 'World'})}
也就是说我们创建的这个每个JSX都可以翻译成: block 类型, block property, block的内容这三个东西的一个array,或是objectJSX只是为了看起来比较简单。
*****************虚拟dom使得性能提升很高
react native可以在原生应用里面识别虚拟dom,我们只需要翻译虚拟dom成为相应的android或是ios的组件。

DIFF virtual DOMS

首先setState是一个异步函数,也就是说并不是你调用了setState之后就直接执行的,而是可能调用之后,batch很多的setState一同执行。

同层比对,pre 的第一层和新的第一层比对,发现有diff,直接重新render,这个看上去有一些资源浪费,就是如果只有第一层有变化也会全局都替换。虽然有点耗性能,但是问题不大。

还有就是key值的问题,我们在做Virtual Dom比对的时候,其实我们是在做两个jsx对象的比对,如果没有key的话,就是两个数组进行比对,两个数组进行比对就要O(n²)的时间复杂度,如果有key的话,直接按key比对,那就是线性的。也就是array=>hash的转化。

?????????????????????????????????

key值要稳定,而且最好不要用index作为key值,因为一个在进行增减的时候,统一元素的index值是有可能发生变化的,如果变化,那么key值不匹配也会浪费性能,但是具体怎么确定key值还需要学习,因为如果直接以内容做key值,产生了同内容的jsx组件怎么办?

?????????????????????????????????

ref:

就是给一个JSX组件添加一个ref的prop,然后呢就把某一个DOM元素连接到class内部的某个variable,方便调用了,以前是用event.target来定位,用ref来做一个link之后,就不用写event.target,在创建函数的时候也不用在去take parameter了。

setState是异步函数,那么也就是说我们在调用他的时候,和其他命令同时执行,可能会引发数据和期待不一致的问题:

this.state = {smKey: "a"};
this.setState({smKey: "smVal"});
console.log(this.smKey)
很有可能该操作的log会是“a“而不是”smVal“,因为setState的异步操作特性,会导致console.log先执行,这样就会出错。
解决方法:callbacksetState可以接收两个paramthis.setState({smKey: "smVal"}, ()=>{console.log(this.state.smKey)});
这样就基本不会出错了。

以ref的方式获取DOM的方式不是特别推荐,因为setState的异步特性,会导致ref返回的DOM节点尚未被update或是render完成,会产生错误。

生命周期函数

在某一个时刻,组件会自动调用执行的函数。

render()在state,props发生改变时,他会自动执行。

constructor()也可以理解为一种生命周期函数

initialize=》mount

component will mount 即将被挂载的时候执行
component did mount 挂载之后执行
component will receive props子组件才存在的东西,而且在第一次接收到父组件传值的时候并不会执行,会在第2+次开始执行。
should component update 这个lifecycle hook是要求返回一个boolean的,如果返回false,之后的一系列包括render的生命周期就都不会执行执行
component will update 原理和mount是一样的
component did update 同理
mount相关的这两个hook是组件第一次在页面渲染的时候才有用。update相关的这些就是在发生改变的时候会触发prop相关的那个就是只有在新的节点已经形成之后,也就时说只有在第二+次接收到prop变化时才会执行

但是life cycle这个一套东西有什么意义呢?

通过这个方式可以尽量减少虚拟dom的re-render,正常我们虚拟dom在发现某个state或是props有变化,就会立即re-render,通过life-cycle 就可以控制在某些情况下不进行变化。

比如说shouldcomponentupdate可以决定上层数据的变化是不是要重新渲染子层的组件

ajax请求,应该在component did mount中加入,也就是说在mount之前去的ajax 数据,(如果放在render里面,render会不断被执行,ajax也会被来回执行,肯定是不合理的)component will mount也可以放,但是这个以后可能会有一些冲突。

axios 是一个发ajax请求的东西。里面有处理请求的一些东西,暂时不重要。

CSSTransition:这个很值得了解一下,就是可以给一些组件套一个CSSTransition,套上之后,转化成了一个嵌套css的组件,其中也有一些钩子,这些钩子会在

on enter
on entering
on entered
exit, exiting, exited 的情况下被call,然后

我们知道,如果是用的component来做组件,我们必须要有一个render函数,Component中没有对于render的default 方案。

  • *********************AJAX 就是异步请求

redux 工作流程

数据层框架

所有数据都放在一个store中

antd 是一个ui框架,比较适合开发sass的相关服务,画风整个比较偏商务一些。

可以直接npm install 进来,然后import相应的组件,比如说 import Input,Button这些,注意这个时候不是input了,而是Input,也就是说是组件了,不再是原装的JSX。

说回到redux

安装chrome的redux devtools

const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export store;

*************

  • ************
  • 非常重要的一个收获:
  • axios是一个轻量级的在react app中可以使用async的http request 的工具,语法就是axios.get…
这就是一个sample的axios请求,注意这个headers 一定要加上这一行access control allow origin这是防止csrf的一种方式,在这就是等于不去担忧csrf攻击,暂时用于dev可以这样,我们从对方服务器,取回data,然后data的data就是那个json文件,我们就有了数据。

还要注意一点,我们这个changeAlertAction function返回的是一个函数,而正常的redux只能接受返回对象的函数,但是我们用了thunk中间件之后,就可以让store去dispatch这种返回函数的function,也就是说有了thunk之后,store在dispatch之前会做判定,看这个action对应的返回的type然后决定是否执行一下这个结果,如果是对象本身则不执行,如果是函数就执行,这个的好处就在于可以在action中加入ajax的请求,这样也使得componentDidMount函数不那么臃肿。

store.dispatch(action) 的时候,如果action是一个函数,那么dispatch是可以被传递进到这个函数里面的,这是为什么呢?

看这段代码:export const getTodoList = () => {
return () => {
axis({
method: "get",
url: "https://jsonplaceholder.typicode.com/posts/1",
responseType: "stream",
headers: { "Access-Control-Allow-Origin": "*"}
}).then(res=>{
const data = res.data.title;
const action = {type: "loadInitMessage", value: data};
dispatch(action);
});};};

Object assign 这个方法也很厉害

Object.assign(target, source1, source2)这个做法就是会返回一个新的对象,对source1,source2,和target进行整合,source2 的priority最高,source1的第二,target最低,priority高的可以override priority低的object
举例:
let newTarget = ({a: 2, b: 3, c: 4}, {a: 3, b: 5, c: 2}, {a: 100, b: 1000, c: 10000, d: 1});
返回的newTarget值就应该是{a: 100, b: 1000, c: 10000, d: 1}
这个对于复制object很有好处。
thunk 这种中间件的执行就是在action和reducer之间加了一层缓冲层,也就是,在action被create之后,逐个遍历这些中间件,然后让中间件们对action(无论是对象还是function还是什么别的东西)进行操作,thunk就会把function转化成起返回的值,这就很有意思了。但是还是有一点混乱,就是为什么写thunk的时候最后一步并没有return任何东西,而是dispatch了一个(action)这个结果好像不会返回一个action,但是在调用的时候确实用dispatch(getTodoList())直接call,这是为什么?

redux saga

异步代码拆分的中间件,可以代替redux thunk

yarn add redux-saga

注意saga使用了es6 的generator函数,

function* funcname() {    yield 'first result';    yield 'second result';    return 'last result'};funcname.next();
{value: 'first result', done: false}
funcname.next();
{value: 'second result', done: false}
funcname.next();
{value: 'last result', done: true}
redux-saga相对来说比较复杂1. 创建safas.js, 然后import到store的index文件中
import todoSagas from './sagas';
2. 创建一个sagaMiddleware的对象
const sagaMiddleware = createSagaMiddleware;
3. 创建一个composeEnhancer
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__() : compose;
4. 创建一个enhancer把这几个中间件连起来
const enhancer = composeEnhancer(applyMiddleware(sagaMiddleware));
5. 创建store
const store = createStore(reducer, enhancer);
6. 直接run这个middleware
sagaMiddleware.run(todoSagas);

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Yizhe Wang
Yizhe Wang

Written by Yizhe Wang

Designer transforming into a developer

No responses yet

Write a response