JS部分
HTML + CSS
React
Vue
ES6
webpack,node.js,Git等
单一数据来源
:整个应用的状态存储在单个 store 中的对象/状态树里。单一状态树可以更容易地跟踪随时间的变化,并调试或检查应用程序。状态是只读的
:改变状态的唯一方法是去触发一个动作。动作是描述变化的普通 JS 对象。就像 state 是数据的最小表示一样,该操作是对数据更改的最小表示。使用纯函数进行更改
:为了指定状态树如何通过操作进行转换,需要用纯函数。纯函数是那些返回值仅取决于其参数值的函数。首先,UI组件发出 Action。然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action。 Reducer 会返回新的 State 。State 一旦有变化,Store 就会调用监听函数。用户可以通过store.getState()得到当前状态。
//伪代码,仅供参考
() => store.dispatch({
type: 'CHANGE_VISIBLE' ,payload : text}) //发出Action
//reducer接受并处理数据
const reducer = (state = {
visible : false,
attritube : [],
}, action) => {
switch (action.type) {
case 'CHANGE_VISIBLE':
return {
...state, visible: !state.visible , attritube : action.payload }
default:
return state
}
}
//react类组件通过装饰器获取state数据
@connect(state => ({
visible: state.visible , attritube : state.attritube}), dispatch => ({
}))
//函数式组件
export default connect((state => ({
visible: state.visible , attritube : state.attritube})))('组价名')
Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系。
React中key的使用
React生命周期文档
React生命周期图解
constructor()
: 如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。constructor(props) {
super(props);
// 不要在这里调用 this.setState()
this.state = {
counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
Render()
: render() 方法是 class 组件中唯一必须实现的方法。
render() 函数应该为纯函数,这意味着在不修改组件 state 的情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。
componentDidMount()
: 会在组件挂载后(插入 DOM 树中)立即调用。如需通过网络请求获取数据,此处是实例化请求的好地方。可以在 componentDidMount() 里直接调用 setState()。
componentDidUpdate()
: componentDidUpdate(prevProps, prevState, snapshot)会在更新后会被立即调用。首次渲染不会执行此方法
componentWillUnmount()
: 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。componentWillUnmount() 中不应调用 setState(),因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。
React
Vue
this.setState({
data: '666'
}, () => {
console.log('加载完成')
});
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
async myfun(){
await this.setState({
data: '666'
});
}
Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象
Immutable 实现的原理是 Persistent Data Structure (持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变
TIP:当然,通过这一点,我们可以去做时间旅行,也就是以前调用之前存在过的旧数据。
同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗, Immutable 使用了 Structural Sharing···· (结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
A
/ \
A B
/ \ / \
C D E F
//例如我们现在数据F发生改变,那么他只会影响到B与A,其他不会受到影响的数据直接复制过来就好了无需重新进行操作,性能有所提升。
JSX是javascript的语法扩展。它就像一个拥有javascript全部功能的模板语言 在里面可以使用js代码
它实现的原理就是react封装了createElement(第一个参数是 标签名,第二个参数是对象 包含了所有的属性,第三个是子节点)。
React Diff 算法的差异查找实质是对两个JavaScript对象(虚拟DOM和真实DOM)的差异查找,所以React更新阶段才会有Diff算法的运用。
更多
在React种有三个部分 :
(1)Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。
(2)View:与 State 一一对应,可以看作 State 的视觉层,也不合适承担其他功能。
(3)Action:存放数据的对象,即消息的载体,只能被别人操作,自己不能进行任何操作。
而想要在React种异步请求数据,在Action中请求无疑是最完美的。即store.dispatch()方法,可以添加请求数据功能。而中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。
redux-thunk
中间件,改造了store.dispatch,使得后者可以接受函数作为参数。与之相对应的redux-promise
返回一个 Promise 对象。
React-thunk的使用
Decorator 是 ES7 的一个新语法,他可以对一些对象进行装饰包装然后返回一个被包装过的对象,
可以装饰的对象包括:类,属性,方法等。
装饰器的作用就是为已经存在的函数或对象添加额外的功能。 装饰器应用场景及理解:
装饰器本质上是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能 。
这里主要介绍一下类装饰器,使用类装饰器可以减少一些代码的重复编写。
此时装饰器看起来更像是一个父类,但它又不是一个父类,因为被装饰的类重写一些生命周期函数的时候
装饰器里面的生命周期函数并不会被覆盖执行。对于componentDidMount 来说,先执被装饰类的
componentDidMount 再执行 装饰器内的componentDidMount;对于componentWillUnmount 来讲
先执行装饰器的componentWillUnmount 再执行被装饰的类的componentWillUnmount
关于装饰器的安装及引用在这里并不做赘述,只介绍如何在React中使用装饰器
@connect(state => ({
banner: state.banner}), dispatch => ({
getBanner() {
dispatch(getBannerAction)
},
}))
class PicturesWall extends React.Component {
componentDidMount() {
this.props.getBanner()
}
//使用 banner 变量
}
在这个类组件中,调用 componentDidMount 生命周期钩子,触发了connect 中的 getBanner 函数,并获取了 redux 中的 banner 变量。在这里 装饰器connect 包裹了 PicturesWall 组件 并触发一个异步函数,将store中的 banner变量传给组件
//函数式组件,换了种写法,作用都一样
const commodityclass = () => {
...
}
export default connect((state => ({
model_payload: state.model_payload})))(commodityclass)
props 是一个从外部传进组件的参数,主要作为就是从父组件向子组件传递数据,它具有可读性和不变性,只能通过外部组件主动传入新的 props 来重新渲染子组件,否则子组件的 props 以及展现形式不会改变。
state 是组件自身的状态,的主要作用是用于组件保存、控制以及修改自己的状态的一些数据,它只能在 constructor 中初始化,它算是组件的私有属性,不可通过外部访问和修改,只能通过组件内部的 this.setState 来修改,修改 state 属性会导致组件的重新渲染。
使用传统的 import 的方式。可以在函数内 return 一个 import, 添加 webpackChunkName 的注释,这样 webpack 打包时就会将对应的文件单独打包,当函数执行时再去加载这个包。
return import( /* webpackChunkName: "lodash" */ 'lodash').then(_ => {}
使用 React.lazy 实现。导入时调用 React.lazy 并给他一个函数参数,函数中返回 import React.lazy(() => import("./XXX"))。配合 Suspense 可以实现在模块未加载完成的时候给用户一个提示。
类组件可以使用其他特性,如状态 state 和生命周期钩子。
当组件只是接收 props 渲染到页面时,就是无状态组件,就属于函数组件
函数组件的性能比类组件的性能要高, 因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。
函数组件没有this,没有生命周期,没有状态state,
类组件有this,有生命周期,有状态state。
高阶组件
是个函数,输出结果是个新组件,可以对输入的组件进行加工,并返回一个具有特定功能的组件。
受控组件
相当于input中的value值通过state值获取,onChange事件改变state中的value值。实现了双向绑定,任意一方的数据发生变化,另一方也会随之改变 。
非受控组件
不需要设置对应的state属性,可通过ref来直接操作真实的dom。
表面区别就是vuex是通过将store注入到组件实例中,通过dispatch和commit来维护state的状态,并可以通过mapstate和this.$store来读取state数据。而redux则是需要通过connect将state和dispatch连接来映射state并操作state。redux没有commit,直接通过dispatch派发一个action来维护state的数据。并且只能通过reducer一个函数来操作state。
rudex使用的是不可变数据;vuex是可变的数据。
rudex每次都是返回一个新的state;而vuex是直接改变state。
原因就是 React会将存储的多个 setState进行合并 ,如果你想立即使用上次 setState后的结果进行下一次 setState,可以让 setState 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数。
redux-thunk
和redux-saga
都是redux的中间件,都是用来处理异步请求的。中间件是指在action
与store
之间实现某种功能的函数
使用redux-thunk处理异步等副作用操作,在action中处理异步等副作用操作,此时的action是一个函数,以dispatch,getState作为形参,函数体内的部分可以执行异步。通过redux-thunk来处理异步,action可谓是多种多样,不利于维护。
在redux-saga中,action是plain object(原始对象)
,并且集中处理了所有的异步操作。使用saga,我们生成一个集中处理异步的saga.js文件,
saga优点:
(1)集中处理了所有的异步操作,异步接口部分一目了然
(2)action是普通对象,这跟redux同步的action一模一样
(3)通过Effect,方便异步接口的测试
(4)通过worker 和watcher可以实现非阻塞异步调用,并且同时可以实 现非阻塞调用下的事件监听
(5) 异步操作的流程是可以控制的,可以随时取消相应的异步操作。 redux-saga使用了ES6中的Generator功能,避免了像redux-thunk的回调地狱。
缺点:太复杂,学习成本较高
• React本身就并不非常认可MVC开发模式
• React被认为是视图层的框架是因为它整个结构就是基于组件的,一切都是组件,然后组件就是渲染页面的基础。
• 不论组件中包含的jsx,methods,state,props,都是属于组件内部的。所以从这个角度看的话,react就是视图层框架。
定义:一个函数的返回结果只依赖于它的参数,并且在执行的过程中没有副作用,我们就把该函数称作纯函数。
纯函数的特点 :
纯函数的优点 : 纯函数非常“靠谱”,执行一个纯函数你不用担心它会干什么坏事,它不会产生不可预料的行为,也不会对外部产生影响。不管何时何地,你给它什么它就会乖乖地吐出什么。如果你的应用程序大多数函数都是由纯函数组成,那么你的程序测试、调试起来会非常方便。
在代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和
过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面。在React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在Diff算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
用 JavaScript 对象来表现一个 DOM 元素的结构JavaScript 写起来太长了,结构看起来又不清晰,用 HTML 的方式写起来就方便很多了。
于是 React.js 就把 JavaScript 的语法扩展了一下,让 JavaScript 语言能够支持这种直接在 JavaScript 代码里面编写类似 HTML 标签结构的语法,这样写起来就方便很多了。编译的过程会把类似 HTML 的 JSX 结构转换成 JavaScript 的对象结构。
React.createElement 会构建一个 JavaScript 对象来描述你 HTML 结构的信息,包括标签名、属性、还有子元素等。
jsx实际上就是一种javascript对象,他经过react语法的构造,还有编译转化,最后得到dom元素,可以插到页面中:所谓的 JSX 其实就是 JavaScript 对象,所以使用 React 和 JSX 的时候一定要经过编译的过程:
JSX —使用react构造组件,bable进行编译—> JavaScript对象 — ReactDOM.render()—>DOM元素 —>插入页面
React.createElement():JSX 语法就是用 React.createElement()来构建 React 元素的。它接受三个参数,第一个参数可以是一个标签名。如 div、span,或者 React 组件。第二个参数为传入的属性。第三个以及之后的参数,皆作为组件的子组件。
React.cloneElement()与 React.createElement()相似,不同的是它传入的第一个参数是一个 React 元素,而不是标签名或组件。新添加的属性会并入原有的属性,传入到返回的新元素中,而旧的子元素将被替换,旧元素的key和ref会保留。