带你初学React(-)

307 阅读8分钟

使用引进cdn的方式引入react(此种方式只用来演示,实际项目中基本不会使用cdn方式引入)

我们使用bootcdn网址获得react cdn网址;

输入react

使用cdn方式引进react,需要同时引入react预计react-dom两个库;

查看链接,可以看到连接有cjs与umd的区别;cjs全称CommonJS,是node支持的模块规范;umd是统一模块定义,兼容各种模块规范(包含浏览器),所以我们优先使用umd版本,可同时兼容node与浏览器;引入后,我启动的本地项目结构如下:使用的parcel index.html命令启动的项目;

查看react 与 react-dom

我们在main.js可打印出console.log(window.React); console.log(window.ReactDom);

展开React 对象

展开ReactDOM对象

使用react的第二种方式,使用webpack配置,在这里我们就直接使用create-react-app,可快速安装开发react项目所用的依赖包;

  • npx create-react-app my-app
    安装成功!
  • yarn start

看到上面页面,代表你已经启动成功!

分析react项目结构

使用两种方式实现点击按钮数字自增的效果

使用引进cdn react方式

const React = window.React; // React对象
const ReactDOM = window.ReactDOM; // ReactDOM对象
const root = document.querySelector('#app');
let n = 0;
// 使用React.createElement创建元素,createElement()有三个参数,第一个参数标签名字,第二个参数属性键值对象,第三个标签内容
const App = React.createElement('div',{className:"red"},[
  n,
  React.createElement('button', {
    onClick: ()=> {
      n += 1;
    }
  }, "+1")
])
ReactDOM.render(App, root);

当单机按钮时,页面没有反应,说明react不会跟进对n的操作自动更新视图;没有监听劫持任何东西,这就需要我们自己去刷新;那怎么刷新呢,就像我们第一次怎么渲染在页面,我们就再次调用render就可以;需要注意的是,如果代码改成下面的样子是不起作用的;

因为当我们第一次加载App = React.createElement代码时,n已经计算完毕,当我们再次使用App元素时,此时n还是刚开始已经完成计算的值;如果想要重新计算n就需要重新执行App = React.createElement(...)代码;根据这个思想我们可以把App就行改造,

把App对象改成了函数,这就实现了什么时候调用什么时候计算n的值,即延迟求值,且求值时才会读取n的最新值; 219.gif

这就形成了react的2个概念,React 元素和函数组件

const app1 = React.createElement('div',null,'app1');               

app1是React 元素,会立即执行(名字小写,约定俗称);

const App2 =()=> React.createElement('div',null,'app1');   

App2是函数组件,被调用的时候再执行(名字大写,约定俗称);

需要注意的是App1元素代表DOM对象,不是真正的DOM对象,是虚拟DOM对象;函数组件虽然不是元素但是返回的是元素,可以函数组件也可以代表DOM对象,这个函数可以多次执行,每次执行会得到最新的虚拟DOM对象;第一次的虚拟DOM对象与第二次不一样,是需要更新页面,React会对比两个虚拟DOM对象,找出不同,局部更新视图,这大大提高了渲染性能,找不同的算法叫做DOM Diff算法;

看到以上代码,你肯定感觉react太弱了,写法太复杂了,完全没有办法和vue相比呀,其实上面的代码是react最弱的用法,后面会慢慢增加更多的用法,上面的例子只是用来让我们来理解react运行的原理,在实际项目中基本上不会这样使用,而是使用工程项目的方式;

JSX登场了!X代表扩展的意思,意思是JS的扩展版;

vue有vue-loader可以吧vue单文件里面的template、script、style标签变成一个构造选项;在写法上,react有jsx,那这JSX怎么变成React可以识别的代码,这也需要一个loader,不是jsx-loader,而是babel-loader,因为babel-loader已经取代了jsx-loader的功能,而babel-loader又被webpack内置,所以只要你使用webpack来构建项目,就不需要另外安装loader;
JSX写法

<button onClick="add">+1</button>

经过loader编译后的代码

React.createElement('button',{"onClick":()=>{add()},"+1")

现在让我们试用一下JSX,就在我们刚才启动的本地项目里,babel-loader也可以使用cdn方式引入;代码如下

index.jsx代码如下

let n = 0;
const App = ()=> (
  <div>
    {n}
    <button
      onClick={()=>{
        n += 1;
        render()
      }}
    >+1</button>
  </div>
  )

const render = () => (ReactDOM.render(<App/>, document.querySelector("#app")));
render()

JSX写法总结就是:标签按照html标签的写法,js变量与方法都使用{}花括号包起来;

注意:永远不要在生成环境使用cdn方式引进babel-loader,因为这样效率太低了,因为在客户端,babel要先编译浏览器才能识别代码,那为什么不先在我们本地编译完成,让客户端直接使用呢?这使用我们之前使用create-react-app命令启动的my-app项目,就可以实现;

使用项目工程方式实现点击按钮自增1的效果

删除项目现在不需要的文件

public文件夹下只留下index.html,src文件夹下只留下index.js;
index.js代码如下

import React from 'react';
import ReactDOM from 'react-dom';
import App from './app.js'

ReactDOM.render(
    <App/>,
  document.getElementById('root')
);

新建一个app.js,代码如下

import React from 'react'
const app = ()=>{
  return (
    <div>
      你好呀,我是app组件
    </div>
  )
}
export default app;

页面展示为

如何在jsx里面使用条件判断

第一种写法:

import React from 'react'
let n = 2;
// 条件判断,写法一
const app = ()=>{
  return n%2 === 0 ? <div>我是偶数</div> : <div>我是奇数</div>
}
export default app;

第二种:

import React from 'react'
let n = 2;
// 条件判断,写法一
const app = ()=>{
  return (
    n%2 === 0 ? <div>我是偶数--第二种方式</div> : <div>我是奇数--第二种方式</div>
  )
}
export default app;

第三种:

import React from 'react'
let n = 2;
// 条件判断,写法三
const app = ()=>{
  const nText = n%2 === 0 ? '我是偶数--第三种方式' : '我是奇数--第三种方式';
  return <div>{nText}</div>
}
export default app;

可以看出react写法比较自由,没有固定的形式,只要记住两点:在标签里面使用变量和方法要使用花括号包起来;

如何在jsx里面使用循环

页面展示:

react中的组件

到底什么是组件?
能和其他物件组合起来的物件就是组件;组件并没有明确的定义,靠感觉就可以;就目前而言,一个返回React元素的函数就是组件;在vue中,一个构造选项就可以是一个组件; React中有两种组件:一是:函数组件;二是:类组件
函数组件

function Dog(props){
    return <div>dog是忠实的动物!它的名字是:{props.name}</div>
}
// 使用方法:
<Dog name="Alice"/>

类组件

class Dog extend React.Component {
    render(){
        return <div>dog是忠实的动物!它的名字是:{this.props.name}</div>
    }
}
// 使用方法:
<Dog name="Alice"/>

需要注意的是:当我们在jsx里面使用标签时,要时刻记住所写的标签不是html标签,所写的标签会被翻译成React.createElement()
React.createElement的逻辑 第一种情况:直接使用原生标签,在babel编译时会编译成React.createElement('div'...),此时属于传给函数字符串'div',则会创建一个div;

const app = ()=>{
  return <div>我是直接使用div</div>
}
export default app;

第二种情况:使用函数组件,比如以下loop,在babel编译时会编译成React.createElement(loop),此时属于传给createElement一个函数,则会调用该函数,获取其返回值;

// 循环测试
const app = ()=>{
  return <div><Loop numbers={numbers}/></div>
}
const loop = (props)=>{
  return <div>{props.numbers}</div>
}
export default app;

第二种情况:使用类组件,比如以下Dog,在babel编译时会编译成React.createElement(loop),此时属于传给createElement一个类,则在类前面加个new(会执行constructor),获取一个组件对象,然后调用对象的render方法,获取其返回值;

class Dog extend React.Component {
    render(){
        return <div>dog是忠实的动物!它的名字是:{this.props.name}</div>
    }
}

你可以到babel online来验证以上内容!

使用react实现点击按钮增加1的效果

app.js代码

import React from 'react'
import Son from './son.js'
import './style.css'

const app = ()=>{
  return (<div className='bagRed'>
    我是父元素
    <Son name='儿子'/>
  </div>)
}
export default app;

son.js代码,此组件是函数组件,变量的声明、读取与设置要使用const [n,setN] = React.useState(0);以下方式;

import React from 'react';
import SonChild from './sonChild'
const Son = (props)=>{
  const [n,setN] = React.useState(0);
  return (
    <div className='sonBag'>
      我是{props.name}我的初始值是{n}<button onClick={()=>setN(n+1)}>+1</button>
      <SonChild name='孙子'/>
    </div>
  )
}
export default Son;

sonChild.js代码如下,此组件是类组件,变量的声明、读取与设置要使用this.state = { n:0 } this.props.name this.setState({n:this.state.n+1})

import React from 'react';
class SonChild extends React.Component {
  constructor(){
    super();
    // 初始化
    this.state = {
      n:0
    }
  }
  add(){
    this.setState({n:this.state.n+1})
  }
  render(){
    return(
      <div className='sonChildBag'>我是{this.props.name},我的初始值是{this.state.n}<button onClick={()=>this.add()}>+1</button></div>
    )
  }

}
export default  SonChild;