深入浅出React(一)
1、create-react-app工具使用
-
安装create-react-app@H_404_7@
npm install create-react-app -g
-
创建项目@H_404_7@
creact-react-app demos cd demos npm start
- 分解应用
package.json@H_404_7@
"scripts": { "start": "react-scripts start","build": "react-scripts build","test": "react-scripts test --env=jsdom","eject": "react-scripts eject" }
npm start
启动开发环境,npm run build
创建生产环境优化代码,npm test
用于测试单元,npm run eject
把潜藏在react-scripts中的一序列技术栈“弹射”到
应用的顶端,此命令不可逆且会改变和增加一些文件。@H_404_7@
2、react新的前端思维模式
- 数据驱动渲染
- 开发者不需要像jQuery一样详细的操作DOM着重于‘如何去做’,只需要着重于“我要显示什么”,而不用操心“怎样去做”;
- react理念UI = reader(data)
- 3、Virtual DOM
- 每次render函数被调用,都要把整个组件重新渲染一遍会浪费,而react对此利用Virtual DOM,让每次渲染都只从新渲染最少的DOM;
- DOM树:HTML是结构化文本,而DOM是结构化文本的抽象表达形式,浏览器在渲染HTML格式网页时,会先将HTML文本解析以构建DOM树,然后根据DOM树渲渲染出用户看到界面,当改变内容时,就去改变DOM树上的节点;
- 虽然DOM树只是一些简单的JavaScript语句,但DOM操作会引起浏览器对网页的从新布局和绘制,所以Web前端开发优化原则之一: 尽量较少DOM操作 ;
- react开发会中jsx语句,将被Babel解析为创建React组件或HTML元素的语句,但React并不会通过其直接构建或操作DOM树,而是先构建Virtual DOM;
- DOM树是对HTML的抽象,而Virtual DOM是对DOM树的抽象;
- Vritual DOM不触及浏览器,只存在于JavaScript空间的树形结构,每次自上而下的渲染React组件时,都会对比此次产生的Vritual DOM和上一次产生的,然后真正的DOM树只需要操作有差别的部分。
- 4、JSX
- JSX: 是JavaScript的语法扩展,允许我们在JavaScript中编写HTML一样的代码,最终会编译成普通的JavaScript语句;
-
JavaScript表达式使用@H_404_7@
-
样式@H_404_7@
-
注释@H_404_7@
- 标签内注意需要写在{}中。
-
数组@H_404_7@
-
事件挂载@H_404_7@
- JSX中可以通过onClick(HTML原生为onclick)
-
HTML直接使用onclick缺点:@H_404_7@
-
JSX中的onClick事件(不存在以上问题)@H_404_7@
- onClick挂载的每个函数都可以控制在组件中,不会污染全局空间;
- JSX中onClick没有产生直接使用onclick的HTML,而是使用了 事件委托 方式处理,无论有多少个onClick出现,其实最后都只在DOM树上添加了一个事件处理函数,挂在最顶层的DOM节点上。
- 所有的点击事件都被这个事件处理函数捕获,然后根据具体组件分配给特定函数,所以性能较高;
- 因为React控制了组件的生命周期,在unmount的时候能够清除相关的所有事件处理函数,内存泄漏问题解决。
function Demo(){ const name = 'jsx'; const arr = [ <h3 key = {0}>数组</h3> <p key = {1}>数组会自动展开,注意添加key属性</p> ]; const func = () => { let result = 'hello'; if (name){ result += name; } else { result += 'world'; } return result; }; return ( <div> <h2></h2> {/*注释*/} <p style = {{color: 'red',fontSize: '14px'}}>hello {name || 'world'}</p> <p className = {name ? 'classA' : 'classB'}> hello {name && 'world'} </p> <p> {func()} </p> {arr} </div> ) }
3. React数据
@H_404_7@
-
React的prop@H_404_7@
- prop(property的简写)是从外部传递给组件的数据,一个组件通过定义自己能够接受的prop就定义了自己的对外公共接口;
- 每个React组件都是独立存在的模块,组件之外的一切都是外部世界,外部世界就是通过prop来和组件对话的。
-
给prop赋值@H_404_7@
class Demo extends Component{ render(){ return( <div> <Child caption = "toProp" initValue = {0}/>//给子组件<Child />传入caption和initValue信息,子组件需定义相关prop接口 </div> ) } }
-
读取prop值@H_404_7@
class Child extends Component{ constructor(props){ super(props); this.state = { //获取外部传入的prop,并用于state初始化 count: props.initValue || 0,caption: props.caption } } }
-
propTypes检查@H_404_7@
- prop是组件的对外接口,所以一个组件该声明自己的接口规范,规范组件支持哪些prop,每个prop该是什么样的格式;
- React通过propTypes来规范,因为propTypes已经从React包中分离出来,所以新版React中无法使用
React.PropTypes.*
,需导入prop-types
即安装:npm install prop-type --save
导入import PropTypes from ('prop-types')
-
propTypes验证器@H_404_7@
- JavaScript基本类型:
PropTypes.array@H_404_7@
PropTypes.bool@H_404_7@
PropTypes.func@H_404_7@
PropTypes.number@H_404_7@
PropTypes.object@H_404_7@
PropTypes.string@H_404_7@
- 可以被渲染为子节点的对象,包括数值、字符串ReactElement(指的是JSX中的闭合标签)或数组:
PropTypes.node
- ReactElement
PropTypes.element
@H_404_7@ - 指定类的实例
PropTypes.instanceOf(Message)
@H_404_7@ - 只接受指定的值:
PropTypes.oneOf(['News','Photos'])
@H_404_7@ - 多个对象类型中的一个:
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
])
@H_404_7@ - 指定类型组成的数组:
PropTypes.arrayOf(PropTypes.number)
@H_404_7@ - 指定类型的属性构成的对象:
PropTypes.objectOf(PropTypes.number)
@H_404_7@ - 符合指定格式的对象:
PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
})@H_404_7@ - 在任意类型上加上isrequired使其不为空:
- JavaScript基本类型:
eg: Child.propTypes = { initValue: PropTypes.number,caption: PropTypes.string }
- React的state
注意:不要直接修改this.state的值,虽然能够改变组件的内部状态,但只是野蛮的修改了state,却不会驱动组件从新渲染,所以变化不会反应到界面
而,this.setState()
所做的事是先改变this.state
的值,然后驱动组件更新@H_404_7@
-
prop和state对比@H_404_7@
- prop用于定义外部接口,state用于记录内部状态;
- prop的赋值在外部世界使用组件时,state的赋值在组件内部;
- 组件不应该改变prop的值,而state的存在就是为了让组件来改变。
- React的context
- 使用prop给内部子组件传递数据时需要一层一层的传递,即使中间有组件不需要使用,这样比较麻烦;
-
使用context可以实现跨级传递。@H_404_7@
eg:
父组件@H_404_7@
class Parent extends React.Component{ getChildContext(){ return {color: "red"} } render(){ return( <div> <Child /> </div> ) } } Parents.childContextTypes = { color: PropTypes.string.isrequired }
(有状态)子组件:
class Child extends React.Component{ render(){ return( <div> <p style = {{color:{this.context.color}}}>有状态的组件可以通过this.context获取</p> <Grandchild /> </div> ) } } Child.contextTypes = { color: PropTypes.string.isrequired }
(无状态)孙子组件:
function Grandchild(context){ return( <p style = {{color: {context.color}}}>无状态组件可以直接在函数的参数中获取</p> ) } Grandchild.contextTypes = { color:PropTypes.string.isrequired }
不积跬步,何以行千里
- 持续加载中.....