React基础

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、生命周期

生命周期函数指在某一时刻组件会自动调用执行的函数。

react生命周期

  • 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

工作流程
redux的工作流程

使用

// 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语法,不用重新再学习新技术,也不会多一道编译步骤,加快网页速度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章