React基础
本人学习的一些笔记
1、什么时候用react,什么时候用vue
react灵活性比较大,处理复杂业务时有更多技术方案的选择 。
vue提供了更丰富的api,实现功能简单,但也因api多会对灵活性有一定的限制。
做复杂度比较高的项目时使用react,面向用户端复杂度不高的使用vue 。
2、JSX
React发明了JSX,利用HTML语法来创建虚拟DOM。React的核心机制之一就是可以在内存中创建虚拟的DOM元素。以此来减少对实际DOM的操作从而提升性能。JSX即JavaScript XML,它是对JavaScript的语法扩展,React使用JSX来替代常规的JS。
JSX的优点
- JSX执行更快,因为它在编译为了JS代码进行了优化
- 它是类型安全的,在编译过程中就能发现错误
- React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击。
- 使用JSX编写模板更加简单快速
JSX语法
1、我们可以在代码中嵌套多个HTML标签,需要使用一个div元素包裹它。也可以用<Fragment></Fragment>
(相当于占位符,但不会增加元素)来包裹
2、实例中的p元素添加了自定义属性data-myattribute,添加自定义属性需要使用“data-”前缀
3、我们可以在JSX中使用JS表达式(不能适用于语句),表达式写在大括号“{}”中
-
{2+2}
{user.firstName}
{formatName(user)}
-
在JSX中不能使用if-else语句,但可以使用conditional(三元运算)表达式来替代
const show = true; {show ? <img src="xxx.png"/> : ''}
-
循环
const list = [1, 2, 3, 4, 5]; { list.map((item, index) => { return <li key={index}>{item}</li> }) }
4、样式
-
React推荐使用内联样式。我们可以使用camelCase语法设置内联样式。
React会在指定元素数字后自动添加px
var myStyle = { fontSize: 100, // css中为font-size color: '#FF0000' }; <h1 style={myStyle}>xxx</h1>
-
<h1 style = {{background: red;}}>xxx</h1> //两个大括号
-
.red-btn { background: red; } <h1 className='red-btn'>xxx</h1> // 使用className而不是class
5、注释
{/* ... */}
{
// (单行注释要换行)
}
6、数组
JSX允许在模板中插入数组,数组会自动展开所有成员
var arr = [
<h1>xxx</h1>
<h2>xxx</h2>
];
<div>{arr}</div>
3、向事件处理程序传递参数
在循环中,通常我们会为事件处理函数传递额外的参数。例如,若 id 是你要删除那一行的 ID,以下两种方式都可以向事件处理函数传递参数:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
上述两种方式是等价的,分别通过箭头函数和 Function.prototype.bind 来实现。
4、state
-
不要直接修改state
this.state.comment = 'hello'; // wrong this.setState({ comment: 'hello'; //right })
构造函数是唯一可以给this.state赋值的地方
-
State 的更新可能是异步的
出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。
因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。
例如,此代码可能会无法更新计数器:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
5、阻止默认行为
不能通过返回 false 的方式阻止默认行为。必须显式的使用 preventDefault 。
例如,传统的 HTML 中阻止链接默认打开一个新页面,你可以这样写:
<a href="#" onclick="console.log('The link was clicked.'); return false">
<a>Click me</a>
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
在这里,e 是一个合成事件。React 根据 W3C 规范来定义这些合成事件,所以你不需要担心跨浏览器的兼容性问题。
6、阻止组件渲染
在极少数情况下,你可能希望能隐藏组件,即使它已经被其他组件渲染。若要完成此操作,你可以让 render 方法直接返回 null,而不进行任何渲染。
下面的示例中, 会根据 prop 中 warn 的值来进行条件渲染。如果 warn 的值是 false,那么组件则不会渲染:
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true};
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
showWarning: !state.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);
7、一个元素的 key
最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用来自数据 id 来作为元素的 key:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key:
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
**循环中的key值最好不是index,原始虚拟DOM树和新的虚拟DOM树的key值一致能提升虚拟DOM比对的性能,而列表项目的顺序可能会变化,index是不稳定的,经常会改变。使用index做key值会导致性能变差,还可能引起组件状态的问题。**如果你选择不指定显式的 key 值,那么 React 将默认使用索引用作为列表项目的 key 值。
8、setState的过程
- 每个组件实例,都有renderComponent方法
- 执行renderComponent会重新执行执行的render
- render函数返回newVnode,然后拿到vnode
- 执行patch(vnode, newVnode)
9、React和Vue对比
两者本质区别
- Vue—本质是MVVM框架,由MVC发展而来
- React—本质是前端组件化框架,由后端组件化发展而来
模板的区别
- Vue—使用模板(最初由Angular提出)
- React—使用JSX
- 模板语法上,更倾向于JSX
- 模板分离上,更倾向于Vue(React模板与JS混在一起,未分离)
组件化的区别
- React本身就是组件化,没有组件化就不是React
- Vue也支持组件化,不过是在MVVM上的扩展
- 对于组件化,更倾向于React,做得彻底而清新
两者共同点
- 都支持组件化
- 都是数据驱动视图
10、PWA
什么是PWA
PWA全称Progressive Web App,即渐进式WEB应用。
写网页的形式写手机APP应用。
1、可以添加至主屏幕,点击主屏幕图标可以实现启动动画以及隐藏地址栏
2、实现离线缓存功能,即使用户手机没有网络,依然可以使用一些离线功能
3、实现了消息推送
registerServiceWorker
引用它,网页上线到支持https协议的服务器上。第一次访问时需联网才能看到,但突然断网,第二次访问时依然可以看到之前访问过的页面,因为registerServiceWorker会把之前的网页存储在浏览器内。
11、html元素转义
var item = `<h1>hello</h1>`
<li>
key={index}
onClick={this.handleItemDelete.bind(this, index)}
dangerouslySetInnerHTML={__html: item}
</li>
12、扩大点击区域
<label htmlFor="insert">输入内容</label>
<input id="insertArea"/>
13、react的思考
- 声名式开发
- 可以与其它框架并存
- 组件化
- 单向数据流
- 视图层框架
- 函数式编程
14、父子组件的通信
-
父组件通过属性形式向子组件传递参数,子组件通过props接收父组件传递过来的参数
无论是使用函数声明还是class声明,都绝不能改变自身的props,所有React组件都必须像纯函数一样保护它们的props不被改变
// 父组件 <TodoItem delete={this.handleDelete} key={index} content={item} index={index} /> // 子组件 handleDelete() { this.props.delete(this.props.index); } <li key={this.props.index} onClick={this.handleDelete}>{this.props.content}</li>
-
子组件如果想与父组件通信,子组件要调用父组件传过来的方法
-
子组件只能使用父组件传递过来的值,但不能改变值(单向数据流)
15、子组件PropTypes和DefaultProps
import PropTypes from 'prop-types';
// 属性类型校验
TodoItem.propTypes = {
test: PropTypes.string.isRequired,
content: PropTypes.string,
deleteItem: PropTypes.func,
index: PropTypes.number
}
// 定义属性默认值
TodoItem.defaultProps = {
test: 'hello world'
}
16、props,state与render函数
当组件的state或props发生改变的时候,render函数就会重新执行。
当父组件的render函数被运行时,它的子组件的render都将被重新运行一次。
17、虚拟DOM
创建真实DOM损耗的性能远大于创建虚拟DOM损耗的性能
1、state数据
2、JSX模板
3、数据+模板 生成虚拟DOM(虚拟DOM就是一个JS对象,用它来描述真实DOM)(损耗了性能)
['div', {id: 'abc'}, ['span', {}, 'hello world']]
4、用虚拟DOM的结构,生成真实的DOM来显示
<div id='abc'><span>hello world</span></div>
5、state发生变化
6、数据+模板 生成新的虚拟DOM(极大地提升了性能)
['div', {id: 'abc'}, ['span', {}, 'byebye']]
7、比较原始虚拟DOM和新的虚拟DOM的区别,找到区别是span中的内容(极大地提升了性能)
8、直接操作DOM,改变span中的内容
JSX => createElement => 虚拟DOM(JS对象)=> 真实DOM
return <div>item</div>
等价于 return ReactElement('div', {}, 'item')
虚拟DOM的优点:
1、性能提升
2、它使得跨端应用得以实现 (React Native)
18、React中ref的使用(不过尽量少用)
<input value={this.state.inputValue} ref={(input) => {this.input = input;}} onChange={this.handleInputChange} />
handleInputChange() {
const value = this.input.value; //替换e.target.value
this.setState(() => ({
inputValue: value
}))
}
19、生命周期
生命周期函数指在某一时刻组件会自动调用执行的函数。
- constructor:在组件一创建的时刻就被调用。但不归类在React的生命周期中,因为它是ES6里面的东西,不是React独有的。
- componentWillMount:在组件即将被挂载到页面的时刻自动执行。
- componentDidMount:在组件被挂载后自动执行。
- shouldComponentUpdate:组件被更新之前,自动被执行需要返回一个布尔值。true 更新 false 不会被更新
- componentWillUpdate:组件被更新之前,它会自动执行,但是它在shouldComponentUpdate之后被执行,如果返回true就执行,如果返回false,这个函数就不会被执行了。
- componentDidUpdate:组件被更新之后自动执行。
- componentWillReceiveProps:一个组件要从父组件接受参数。只要父组件的render函数被重新执行了,子组件的这个生命周期函数就会被执行(如果这个组件第一次存在与父组件中,不会执行;如果这个组件之前已经存在于父组件中,才会执行)
- componentWillUnmount:当这个组件即将被从页面中剔除的时候,会被执行。
20、性能提升
1、this.handleClick = this.handleClick.bind(this);
将这种作用域的修改放在constructor中,保证作用域绑定操作只执行一次。
2、setState异步函数:能将多个数据的改变结合成一次来做,降低虚拟DOM的比对频率。
3、虚拟DOM,同层比对
4、借助shouldComponentUpdate避免组件做多次无谓的render操作
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.content !== this.props.content) {
return true;
} else {
return false;
}
...
}
5、ajax请求放在componentDidMount里
componentDidMount() {
axios.get('/api/todolist')
.then(() => { alert('succ'); })
.catch(() => { alert('error') })
}
21、React的css过渡动画
.show {
opacity: 1;
transition: all 1s ease-in;
}
.hide {
animation: hide-item 2s ease-in forwords; // 保持动画最后一帧css的样式
}
@keyframes hide-item {
0% {
opacity: 1;
color: red;
}
50% {
opacity: 0.5;
color: green;
}
100% {
opacity: 0;
color: blue;
}
}
22、使用react-transition-group实现动画
import {CSSTransition} from 'react-transition-group';
<CSSTransition
onEntered={(el) => {el.style.color = 'blue';}} // 结束时为蓝色
in={this.state,show}
className='fade'
timeout={1000} // 动画执行时间
appear={true} // 第一次展现也有动画效果
unmountOnExit // DOM消失时被移除
>
<div>hello</div>
</CSSTransition>
.fade-enter, .fade-appear {
opacity: 0;
}
.fade-enter-active, .fade-appear-active {
opacity: 1;
transition: opacity 1s ease-in;
}
.fade-enter-done {
opacity: 1;
color: red; // 结束之后为红色
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 1s ease-in;
}
.fade-exit-done {
opacity: 0;
}
23、Redux
Redux = Reducer + Flux
工作流程
使用
// store/index.js
import {createStore} from 'redux';
import reducer from './reducer';
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__() // 用于redux调试
);
// store/reducer.js
const defaultState = {
inputValue: '',
list: []
};
// reducer 可以接收state,但绝不能修改state,所以要另外拷贝一个
export default (state = defaultState, action) => {
if (action.type === 'change_input_value') {
// 深拷贝
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
return state;
}
// Todolist.js (部分)
import store from './store';
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState()
this.handleInputChange = this.handleInputChange.bind(this);
this.handleStoreChange = this.handleStoreChange.bind(this);
store.subscribe(this.handleStoreChange); // 订阅方法设置更新数据
}
render() {
return (
<TodoListUI
inputValue={this.state.inputValue}
list={this.state.list}
handleInputChange={this.handleInputChange}
handleBtnClick={this.handleBtnClick}
handleItemDelete={this.handleItemDelete}
/>
)
}
handleInputChange(e) {
const action = getInputChangeAction(e.target.value);
store.dispatch(action);
}
handleStoreChange() {
this.setState(store.getState());
}
}
改变store里的数据
1、先派发一个action,通过dispatch方法传递给store
2、reducer中接收state和action进行处理,返回一个新的state返回给store,替换原来的store
3、store中数据改变react感知到store数据的改变,通过store.subscribe()订阅方法设置更新数据
Redux设计和使用的三项原则
1.store是唯一的
2.只有store能改变自己的内容
3.reducer必须是纯函数
纯函数:给定固定的输入,就一定会有固定的输出,而且不会有任何的副作用
funcition sum(a, b) {
return a + b;
}
// 这样的函数被称为纯函数,因为该函数不会尝试更改入参,且多次调用下相同的入参始终返回相同的参数。
// 下面不是,自己更改了入参
function withdraw(account, amount) {
account,total -= amount;
}
Redux核心API
1、createStore ——创建store
2、store.dispatch ——派发action,这个action会传递给store
3、store.getState ——获取store中所有的数据内容
4、store.subscribe ——订阅store的改变,只要store发生改变,subscribe中接收的回调函数就会被执行
24、Redux的中间件
对dispatch方法进行升级:
接收对象,和原来一样,直接传递对象给store
接收函数,先执行函数,执行完后需要调用store再操作
如:
redux-thunk中间件——改造store.dispatch使得后者可以接受函数作为参数
redux-saga——单独把逻辑拆分出来放到另一个文件中管理
ps:中间是指action和store的中间,中间件是Redux的中间件,而不是react
redux-thunk的使用
redux-saga的使用
25、React-Redux
核心API:
Provider:作用:连接store,内部组件都有能力获取store的内容
connect:组件与store作连接
mapStateToProps:把store中state映射成组件中的props
mapDispatchToProps:将store.dispatch挂载到props上
使用
26、style-components
是针对React写的一套css in js框架,简单来讲,就是在js中写css。相对于预处理器(sass,less)的好处是,css in js使用的是js语法,不用重新再学习新技术,也不会多一道编译步骤,加快网页速度。