这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
什么是npx?
使用npx而不是npm安装react脚手架有一个好处:
npm会因为node版本不同可能会导致某个版本的node没有安装脚手架因此无法使用,如果每个版本都安装脚手架会导致没必要的内存空间浪费,使用npx 还能避免全局安装的模块。比如,create-react-app这个模块是全局安装,npx 可以运行它,而且不进行全局安装。
$ npx create-react-app my-react-app
上面代码运行时,npx 将create-react-app下载到一个临时目录,使用以后再删除。所以,以后再次执行上面的命令,会重新下载create-react-app。
npx 还可以执行 GitHub 上面的模块源码。
# 执行 Gist 代码
$ npx https://gist.github.com/zkat/4bc19503fe9e9309e2bfaa2c58074d32
# 执行仓库代码
$ npx github:piuccio/cowsay hello
注意,远程代码必须是一个模块,即必须包含package.json和入口脚本。
react初认识
两个最基本的包:
React:框架的核心包
ReactDOM:专门做渲染相关的包
ReactDOM.render(
<App/>,
document.getElementById('root')
)
一开始id为root的dom节点默认在index.html文件中
ps:严格节点应该去掉,因为会影响useEffect的执行时机
什么是jsx
jsx就是可以在js中写html结构
jsx中html结构中可以用{}包裹js表达式,可以使用的表达式有:
1.字符串、数值、布尔值、null、undefined、object([]/{})
2.1+2、'abc'.split('')、['a','b'].join('-')、三元表达式。。。
3.fn()
ps:if 语句/switch-case 语句/变量声明语句,这些叫做语句,不是表达式,不能出现在{}中!!
循环渲染列表
如果要渲染一组数据,应该使用数组的 map0方法
注意:渲染列表时应该添加 key 属性,key 属性的值要保证唯一
原则:map()遍历谁,就给谁添加 key 属性
const songs = [
{id:1,name:'痴心绝对'},
{id:2,name:'像我这样的人'},
{id:3,name:'南山南'},
]
function App() {
return(
<div>
<ul>
{songs.map(item=><li key={item.id} >{item.name}</li>)}
</ul>
</div>
)
}
条件渲染
作用:根据是否满足条件生成HTML结构,比如Loading
效果实现:可以使用三元运算符或逻辑与(&&)运算符
const flag = false
function App() {
return(
<div>
{
flag?(<div><span>this is span</span></div>) :null
}
{true&&<span>this is span</span>}
</div>
)
}
函数组件
1、组件的名称必须首字母大写,react内部会根据这个来判断是组件还是普通的HTML标签
2、函数组件必须有返回值,表示该组件的 U1 结构;如果不需要渲染任何内容,则返回 null
3、组件就像HTML标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值就是对应的内容
4、使用函数名称作为组件标签名称,可以成对出现也可以自闭合
例:
function Hello(){
return <div>hello</div>
}
//渲染<Hello/> <Hello></Hello>
function App(){
return (
<div>
<Hello/>
<Hello></Hello>
</div>
)
}
类组件
用es6的class创建的组件就叫类组件
1、类名称也必须以大写字母开头
2、类组件应该继承React.Component父类,从而使用父类中提供的方法或属性
3、类组件必须提供render方法render方法必须有返回值,表示该组件的u1结构
class HelloComponent extends React.Component {
render() {
return <div>this is class Component</div>
}
}
function App() {
return (
<div className="App">
<HelloComponent></HelloComponent>
</div>
)
}
事件绑定
语法
on+事件名称={事件处理程序},比如:<div onClick={()=>{}}></div>
注意:react事件采用驼峰命名法,比如:onMouseEnter、onFocus
class HelloComponent extends React.Component{
//事件回调函数(标准写法 避免this指向问题)
//这样写 回调函数中的this指向的是当前的组件实例对象
clickHandler =()=>{
console.log('类组件中的事件被触发了');
}
render(){
return <div onClick={this.clickHandler}>this is class Component</div>
}
function HelloFn (){
// 定义事件回调函数
const clickHandler = () => {
console.log('事件被触发了')
}
return (
<button onClick={clickHandler}>click me!</button>
)
}
获取事件对象
通过事件处理程序的参数获取事件对象e
function App() {
const clickHandler = (e, msg) => {
console.log('事件被触发了' + msg)
e.preventDefault()
}
return (
<div>
<a
href="http://baidu.com"
onClick={(e) => clickHandler(e, 'this is msg')}
>
clickme
</a>
</div>
)
}
类组件的状态
定义状态必须通过state 实例属性的方法 提供一个对象 名称是固定的就叫做state
修改state中的任何属性 都不可以通过直接赋值 必须走setstate方法 这个方法来自于继承得到
这里的this关键词 很容易出现指向错误的问题 上面的写法是最推荐和最规范的 没有this指向问题
class TestComponent extends React.Component {
state = {
name: 'teacher',
}
changename = (e) => {
console.log('事件被触发了')
e.preventDefault()
this.setState({
name: 'hgc',
})
}
render() {
return (
<div>
<div>当前name为:{this.state.name}</div>
<a href="http://baidu.com" onClick={(e) => this.changename(e)}>
修改名字
</a>
</div>
)
}
}
react的this指向问题
如果在jsx中直接用{}包裹this指针,那么this指针是undefined,必须要用箭头函数包裹直接沿用父函数中的this指向才能使this指针有效,也可以用另一种比较麻烦的办法进行修正:
class Test extends React.Component{
constructor(){
//这样会报错
handler
{console.log(this)
this.setstate}
//这样是正确的
super()
//使用bind强行修正我们的this指向
//相当于在类组件初始化的阶段 就可以把回调函数的this修正到
//永远指向当前组件实例对象
this.handler = this.handler.bind(this)
}
}
render () {
return (
<button onClick={ this. handler}>click</button>
)}
react的状态不可变
概念:不要直接修改状态的值,而是基于当前状态创建新的状态值
state = {
count : 0,
list: [1,2,3],
person: {name: 'jack',age:18}
}
//直接修改简单类型Number
this.state.count++
++this.state.count
this.state.count += 1
this.state.count = 1
this.setState({
count:this.state.count + 1
list:[...this.state.list,4],
person:{
...this.state.person,
//覆盖原来的属性 就可以达到修改对象中属性的目的
name:'rose'
})
react的表单处理(使用受控组件)
什么是受控组件?
受控组件就是可以 被react的状态控制的组件(input框自己的状态被React组件状态控制)
React组件的状态的地方是在state中,input表单元素也有自己的状态是在value中,React将state与表单元素的值(value)绑定到一起,由state的值来控制表单元素的值,从而保证单一数据源特性
实现步骤:
以获取文本框的值为例,受控组件的使用步骤如下:
1.在组件的state中声明一个组件的状态数据2,将状态数据设置为input标签元素的value属性的值
3.为input添加change事件
4,在事件处理程序中,通过事件对象e获取到当前文本框的值(即用户当前输入的值)
5,调用setState方法,将文本框的值作为state状态的最新值
export default class Four extends Component {
state = {
message: 'hello',
}
changeMessage = (e) => {
this.setState({
message: e.target.value,
})
}
render() {
let { message } = this.state
return (
<div className="four">
<p>
请输入:
<input type="text" value={message} onChange={this.changeMessage} />
</p>
</div>
)
}
}
react的表单处理(非受控方式)
非受控组件就是通过手动操作dom的方式获取文本框的值,文本框的状态不受react组件的state中的状态控制,直接通过原生dom获取输入框的值
实现步骤:
1.导入 createRef 函数
2.调用createRef函数,创建一个ref对象,存储到名为 msgRef 的实例属性中
3.为input添加ref属性,值为msgRef
4.在按钮的事件处理程序中,通过msgRef.current即可拿到input对应的dom元素,而其中msgRef.current.value拿到的就是文本框的值
import React, { Component, createRef } from 'react'
export default class Four extends Component {
msgRef = createRef() //使用create方法创建一个存放dom元素的容器
state = {
message2: '',
}
getmessage = () => {
this.setState({
message2: this.msgRef.current.value,
})
}
render() {
let { message } = this.state
return (
<div className="four">
<h2>非受控组件</h2>
<p>
请输入:
{/* 使用ref属性将dom元素跟ref对象进行关联,之后就可以通过操作ref对象的方式去操作dom元素 */}
<input type="text" ref={this.msgRef} />
<button onClick={this.getmessage}>获取数据</button>
<span>{this.state.message2}</span>
</p>
</div>
)
}
}
jsx样式控制
行内样式
在元素身上绑定一个style属性即可
function App() {
return(
<div className = "App">
<span style={{color:'red',fontSize:'30px'}}>this is span</span>
</div>
)
}
通常可以将style的对象另外定义,然后传一个对象给style属性就可以了
const style = {color:'red',fontSize:'30px'}
function App() {
return(
<div className = "App">
<span style={style}>this is span</span>
</div>
)
}
注意:这样传的话是一个{}不要和上面两个{}的情况弄混,react传数据都是一个{}
内联样式的优点:
1.内联样式,样式之间不会有冲突
2.可以动态获取当前state中的状态内联样式的缺点:
1.写法上都需要使用驼峰标识(因为js不支持用-连接的写法),驼峰就是每个单词首字母大写
2.某些样式没有提示
3.大量的样式,代码混乱04某些样式无法编写(比如伪类/伪元素)
类名样式
在元素身上绑定一个className属性即可,但是这样写需要另外写一个css文件然后再引入这个css文件
这样的编写方式和普通的网页开发中编写方式是一致的:
如果我们按照普通的网页标准去编写,那么也不会有太大的问题;
但是组件化开发中我们总是希望组件是一个独立的模块,即便是样式也只是在自己内部生效,不会相互影响;
但是普通的css都属于全局的css,样式之间会相互影响;
css modules
css modules并不是React特有的解决方案,而是所有使用了类似于webpack配置的环境下都可以使用的。
如果在其他项目中使用它,那么我们需要自己来进行配置,比如配置webpack.config.js中的modules:true等。
React的脚手架已经内置了css modules的配置:
xxx.css/.less/.scss等样式文件都需要修改成.module.css/.module.less/.module.scss 等;
之后就可以引用并且进行使用了;css modules确实解决了局部作用域的问题,也是很多人喜欢在React中使用的一种方案。
但是这种方案也有自己的缺陷:
1、引用的类名,不能使用连接符(.home-title),在JavaScript中是不识别的;
2、所有的className都必须使用(style.className)的形式来编写;
3、不方便动态来修改某些样式,依然需要使用内联样式的方式;
如果你觉得上面的缺陷还算OK,那么你在开发中完全可以选择使用css modules来编写,并且也是在React中很受欢迎的一种方式。
!!styled-components
styled-components是CSS-in-JS的一种库。
"CSS-in-JS”是指一种模式,其中 CSS 由 JavaScript 生成而不是在外部文件中定义;主意此功能并不是 React 的一部分,而是由第三方库提供;React 对样式如何定义并没有明确态度;
在传统的前端开发中,我们通常会将结构(HTML)、样式(CSS)、逻辑(JavaScript)进行分离。
但是在前面的学习中,我们就提到过,React的思想中认为逻辑本身和UI是无法分离的,所以才会有了JSX的语法。
样式呢?样式也是属于UI的一部分;
事实上CSS-in-JS的模式就是一种将样式(CSS)也写入到JavaScript中的方式,并且可以方便的使用JavaScript的状态;所以React有被人称之为 All in JS;
CSS-in-JS通过JavaScript来为CSS赋予一些能力,包括类似于CSS预处理器一样的样式嵌套、函数定义、逻辑复用、动态修改状态等等;虽然CSS预处理器也具备某些能力,但是获取动态状态依然是一个不好处理的点;所以,目前可以说CSS-in-JS是React编写CSS最为受欢迎的一种解决方案