React的学习1

React 是什么?

一个用于构建用户界面的 JavaScript 库
中文手册:https://react.docschina.org/
用户界面:WEB --- 网页 --- 用户可以看到并使用的视图界面

命令式编程 和 声明式编程

命令式编程:告诉计算机怎么做(How) - 过程

命令式编程:
命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。

举个例子:
var numbers = [1,2,3,4,5]
var doubled = []
for(var i = 0; i < numbers.length; i++) {
var newNumber = numbers[i] * 2
doubled.push (newNumber)
}
console.log (doubled) //=> [2,4,6,8,10]

声明式编程:告诉计算机我们要什么(What) - 结果

举个例子:
var numbers = [1,2,3,4,5]
var doubled = numbers.map (function (n) {
return n * 2
})
console.log (doubled) //=> [2,4,6,8,10]

能让我们站在更高的层面是思考,站在云端思考我们想要的是什么,而不是站在泥里思考事情该如何去做。

如何使用 React

  1. 基于浏览器的模式

  2. 基本的api使用:

  3. React.createElement(type,props,children);

React.createElement 
用于构建视图,返回值是一个用来描述视图的虚拟DOM(纯对象格式)
React.createElement(type,props,children,...childrenN);


- type 节点类型 (标签名|组件名)
- props 元素对应的属性 (object|null)
- children 内容或子节点(包括文本节点)  (可以有多个)
  1. ReactDOM.render(Vnode, container[, callback])

ReactDOM.render(VDom, container[, callback])
将构建好的视图(虚拟DOM)渲染成真实DOM

- VDom:要渲染的内容(要构建的视图)
- container:要渲染的内容存放容器(挂载节点、要构建的视图放在哪个真实元素中)
- callback:渲染后的回调函数(可选参数) 

举个例子:

<head>
  <script src="js/react.js"></script>
  <script src="js/react-dom.js"></script>
</head>
<body>
<div id="root"></div>
<script>

/*
react.js主要负责构建视图和构建组件
依赖顺序:react-dom.js依赖于react.js
所以得先引入react.js!!
*/

//$$typeof: Symbol(react.element)  
console.log(React,'react.js暴露了一个React对象(大写R)');



console.log(React.createElement('div',{className:'box'},'box'));
// 返回值是一个用来描述视图的虚拟DOM
// 对象格式 问题:为什么需要加入虚拟DOM?对比虚拟Dom的原理?


//创建虚拟DOM
let header = React.createElement("header",{className:"header div",id:"header"},"页面头部");



//console.log(ReactDOM,'react-dom.js暴露了一个ReactDOM对象');


//将虚拟DOM转换为真实的DOM元素
//被渲染为:<header class="header div" id="header">页面头部</header>
ReactDOM.render(
  header,
  document.querySelector("#root"),()=>{
    console.log("构建完成")
  }
);

</script>  
</body>

基于React的视图构建

  • 构建一个复杂点的视图
<head>
  <script src="js/react.js"></script>
  <script src="js/react-dom.js"></script>
</head>

<body>
<div id="root"></div>
<script>

//创建虚拟DOM
//React.createElement的第三个参数可以是子节点
let logo = React.createElement("h1",{id:"logo"},"林");
let a1 = React.createElement("a",{href:"/index"},"a1");
let a2 = React.createElement("a",{href:"/about"},"a2");
let nav = React.createElement("nav",{className:"nav"},a1,a2);
let header = React.createElement("header",{className:"header div",id:"header"},logo,nav); 


ReactDOM.render(
  header,
  document.querySelector("#root"),
  ()=>{
    console.log("构建完成")
  }
);
</script>  
</body>

复杂点的视图构建暴露出来的问题:构建试图过于麻烦!

用JSX就来解决这个问题

浏览器默认是不认识JSX语法的

  • 在不使用脚手架构建react项目之前,为了让浏览器认识JSX语法需要引入babel.js
  • babel.js链接
<head>
<script src="js/react.js"></script>
<script src="js/react-dom.js"></script>
<script src="js/babel.js"></script>
</head>
<body>
<div id="root"></div>


<script type="text/babel">

let header = <header className="header div" id="header">
<h1 id="logo">林吧</h1>
<nav>
<a href="/index">a1</a>
<a href="/about">a2</a>
</nav>
<br />
</header>

ReactDOM.render(
header,
document.querySelector("#root"),
()=>{
console.log("构建完成")
}
);
</script>  
</body>
  1. 注意点:jsx的标签全部是小写的
  2. 注意点:jsx只接收一个顶层元素标签
  3. 注意点:在html中的class在jsx中要写为className
  4. 注意点:jsx支持插值表达式 {} ,可以配合js的表达式一起使用
  5. 注意点:在jsx中在标签上书写行间样式的话style={{width:20}}是这么书写的
  6. 注意点:jsx只能有一个顶层元素标签,可以通过<></>空标签或者Fragment来解决
  7. 注意点:JSX expressions must have one parent element.
  8. 注意点:可以用大括号来加入JavaScript表达式。遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析。
  9. 注意点:在react中通常约定组件类的第一个字母必须大写XML标签都是小写
  10. 注意点:它并不是字符串 ,不要带上``,如果带上会默认为字符串,于是全部全部转义后渲染
  11. 它也不是HTML,所以很多写法与HTML有出入,例如 class 要写为 className、XML的标签全部必须要闭合 </> 、JSX支持插值表达式
  12. 它可以配合JavaScript 表达式一起使用
JavaScript有以下表达式类型:

1.变量
2.算术运算  || &&  ?:  ❗
3.函数调用后有合法的运算结果的  (fuunction(){return '1'})()
4.string | number 
5.bollean | null  |  undefined 
6.数组
7.对象


if、for、while 这些都是语句,JSX 不支持语句

JSX JavaScript for XML

引入三个库

  <script src="js/react.js"></script>
  <script src="js/react-dom.js"></script>
  <script src="js/babel.js"></script>

在 JXS 中可以使用 {表达式} 嵌入表达式

表达式是一组代码的集合,它返回一个值。

表达式:产生值的一组代码的集合,有运行结果的一段程序

在{表达式}里使用表达式

  1. 变量

<body>
<div id="root"></div>
<script type="text/babel">

const data={
    title:'欢迎来到react的世界',
    subTitle:'欢迎来到react的世界'
}

let header = <header className="main" >
    <h1 id="logo">{data.title}</h1>
    <nav>
      <a href="/index">{data.subTitle}</a><br/>
      <a href="/about">常量值:{1+1}</a><br/>
    </nav>
    <br />
</header>


ReactDOM.render(
  header,
  document.querySelector("#root"),
  ()=>{
    console.log("构建完成")
  }
);


</script>  
</body>
  1. 算术运算

<body>
<div id="root"></div>
<script type="text/babel">


let header = <header className="main" >
    <h1 id="logo">{1==1 && (1+3)}</h1>
    <a href="/about">&&运算:{true &&  2}</a>返回值2<br/>
    <a href="/about">||运算:{false ||  222}</a>返回值222<br/>
    <br />
</header>


ReactDOM.render(
  header,
  document.querySelector("#root"),
  ()=>{
    console.log("构建完成")
  }
);


</script>  
</body>
  1. 函数调用后有运算结果的

<body>
<div id="root"></div>
<script type="text/babel">


let header = <header className="main" >
      <a href="/about">函数没调用的话整体被忽略:{function(){console.log('1')}}</a><br/>
      <a href="/about">函数调用后的返回值合法就可以呈现:{(function(){return 1})()}</a><br/>
    <br />
</header>


ReactDOM.render(
  header,
  document.querySelector("#root"),
  ()=>{
    console.log("构建完成")
  }
);


</script>  
</body>
  1. 是一个ReactElement

<body>
<div id="root"></div>
<script type="text/babel">



let header = <header className="main" >
      <a href="/about">react element:{   <a href="/index">react element</a> }</a>有返回值<br/>
</header>


//1.其实这也是一个ReactElement
console.dir(header,'header'); //$$typeof: Symbol(react.element)

//2.{ <a href="/index">react element</a> } 是可以这么做的
//接收的是一个基于React构建的元素节点 狭义上的ReactElement


ReactDOM.render(
  header,
  document.querySelector("#root"),
  ()=>{
    console.log("构建完成")
  }
);
</script>  
</body>
<body>
<div id="root"></div>
<script type="text/babel">


let header = <header className="main" >
        <a href="/about">react element:{   <a href="/index">react element</a> }</a>有返回值
</header>


ReactDOM.render(
  header,
  document.querySelector("#root"),
  ()=>{
    console.log("构建完成")
  }
);


</script>  
</body>
  1. string | number 会原样输出

  2. bollean | null | undefined 都没有返回值 全部空

<body>
<div id="root"></div>
<script type="text/babel">



let header = <header className="main" >
      <a href="/about">null:{false ||  null}</a>无返回值<br/>
      <a href="/about">null:{ null }</a>无返回值<br/>
      <a href="/about">undefined:{ undefined }</a>无返回值<br/>
      <a href="/about">for语句不行:{ for(let i=0;i<10;i++){console.log(i)} }</a>无返回值<br/>
    </nav>
    <br />
</header>
ReactDOM.render(
  header,
  document.querySelector("#root"),
  ()=>{
    console.log("构建完成)
  }
);
</script>  
</body>
  1. 数组 数组会自动去掉连接符号进行输出

数组会自动去掉连接符号进行输出 有几位就输出为几个子节点
arr中一定不要写成字串  写成字符串是另一种效果

const arr=[
    <div>hello arr1</div>,
    <div>hello arr2</div>
]

<a href="/about">数组:{ arr }</a><br/>


被渲染为
<a href="/about">数组:<div>hello arr1</div><div>hello arr2</div></a>


-----------------------------------------


注意点: arr中一定不要写成字串  写成字符串是另一种效果

即
const arr1=[
    '<div>hello arr1</div>',
    '<div>hello arr2</div>'
]

<a href="/about">数组:{ arr1 }</a><br/>

被渲染为  全部给你转义了
<a href="/about">数组:&lt;div&gt;hello arr1&lt;/div&gt;&lt;div&gt;hello arr2&lt;/div&gt;</a>
  1. 对象 对象是不能直接输出的

const obj={
    title:'欢迎来到react的世界',
    subTitle:'欢迎来到react的世界'
}
const obj1={
    title:<div>欢迎来到react的世界</div>,
    subTitle:<div>'欢迎来到react的世界'</div>
}


对象配合数组使用
<a href="/about">对象是不能直接输出的:{ obj }</a>这样报错<br/>


<a href="/about">对象:{ Object.keys(obj).map(item=>{return obj[item]}) }</a><br/>
 <a href="/about">对象:{ Object.keys(obj1).map(item=>{return obj1[item]}) }</a><br/>
 
 被渲染为
 <a href="/about">对象:欢迎来到react的世界欢迎来到react的世界</a>
 
 -----------
 <a href="/about">对象:<div>欢迎来到react的世界</div><div>'欢迎来到react的世界'</div></a>
  1. JSX的特殊属性 ---> 行间样式:style

style
<body>
<div id="root"></div>
<script type="text/babel">

const data={
    title:'欢迎来到react的世界',
    subTitle:'欢迎来到react的世界'
}


const style={
  width:500,
  fontSize:100
}


let header = <header 
          className={'main-' + 'box'} 
          >
    <h1 id="logo" style={style}>{data.title}</h1>
    <nav>
    </nav>
    <br />
</header>
console.dir(header,'header'); //$$typeof: Symbol(react.element)


ReactDOM.render(
  header,
  document.querySelector("#root"),
  ()=>{
    console.log("构建完成")
  }
);

</script>  
</body>


简写为   外面是插值表达式 里面是一个对象   记住是针对style属性特有的 支持对象
 <h1 id="logo" style={{width:500,fontSize:100}}>{data.title}</h1>
  1. 在元素的属性中使用插值表达式

1.元素的属性中也可以使用插值表达式  

<body>
<div id="root"></div>
<script type="text/babel">



let header = <header className={'main'}>
    <br />
</header>
console.dir(header,'header'); //$$typeof: Symbol(react.element)


ReactDOM.render(
  header,
  document.querySelector("#root"),
  ()=>{
    console.log("构建完成")
  }
);
</script>  
</body>


被渲染为<header class="main"><br></header>



2.在属性上使用表达式 当在属性中使用 {} 的时候,不要使用引号包含
let header = <header className={'main-' + 'box'}>
    <br />
</header>

被渲染为<header class="main-box"></header>
  1. 注释

注释


{/*注释*/}


{/*
多行注释
*/}

总结各种类型内容在插值中的使用

  1. 注释
  2. 输出数据类型
    1. 字符串、数字:原样输出
    2. 布尔值、空、未定义 会被忽略
  3. 列表渲染
    1. 数组
    2. 对象
      1. 扩展:虚拟 DOM (virtualDOM) 和 diff
  4. 条件渲染
    1. 三元运算符
    2. 与或运算符
  5. 在属性上使用表达式
    1. JSX 中的表达式也可以使用在属性上,但是使用的时候需要注意
    2. 当在属性中使用 {} 的时候,不要使用引号包含
  6. JSX 使用注意事项
    1. 必须有且只有一个顶层的包含元素 - 除非使用React.Fragment或者空标签<></>
    2. JSX 不是html,很多属性在编写时不一样
      1. className
      2. 标签行间样式 style
    3. 列表渲染时,必须有 key 值
    4. 在 jsx 所有标签必须闭合
    5. 组件的首字母一定大写,标签一定要小写
  7. XSS:为了有效的防止 XSS 注入攻击,React DOM 会在渲染的时候把内容(字符串)进行转义,所以字符串形式的标签是不会作为 HTML 标签进行处理的

基于自动化的集成环境模式 create-react-app - 脚手架 简称cra

介绍

通过前面 script 的方式虽然也能完成 React.js 的开发,但是有一个现在前端很重要的特性 - 模块化,无法使用。

Create React App 是一个使用 Node.js 编写的命令行工具,通过它可以帮助我们快速生成 React.js 项目,并内置了 Babel、Webpack 等工具帮助我们实现 ES6+ 解析、模块化解析打包,也就是通过它,我们可以使用 模块化 以及 ES6+ 等更新的一些特性。同时它还内置 ESLint 语法检测工具、Jest 单元测试工具。还有一个基于 Node.js 的 WebServer 帮助我们更好的在本地预览应用,其实还有更多。

这些都通过 Create React App(CRA) 帮助我们安装并配置好了,开箱即用

安装与使用

通过 npm、yarn、npx 都可以

安装:

npm
npm i -g create-react-app

yarn
yarn global add create-react-app

使用:安装完成以后,即可使用 create-react-app 命令

create-react-app <项目名称>

npx create-react-app <项目名称>

项目目录结构说明

运行命令以后,就会在运行命令所在目录下面创建一个以项目名称为名的目录

my-app/
  README.md
  node_modules/
  package.json
  public/
    index.html    根节点  默认是#root
    favicon.ico
  src/
    App.css
    App.js
    App.test.js
    index.css
    index.js  项目的入口文件  通常从这里起书写代码
    logo.svg

命令脚本

1.npm run start
启动一个内置的本地 WebServer,根目录映射到 './public' 目录,默认端口:3000

2.npm run test
运行 Jest 测试

3.npm run build
打包应用(准备上线)

4. npm run eject 该命令慎用   
将webpack配置暴露出来 是不可逆的操作

5.react-scripts 中内置了webpack功能等配置

6.web-vitals  用于性能监测

通过create-react-app启动项目

实践一:在"react": "^17.0.1"版本及之后中直接书写JSX语法

/**
 * 版本区别   JSX-runtime
 * 
 * 在"react": "^17.0.1"版本及之后中直接书写JSX语法
 * React能够直接将其解析虚拟DOM 
 * 不需要通过React.createElement来创建虚拟DOM
 */


//在17之前的版本必须通过引入react库来将JSX语法转为虚拟DOM

//在17以及之后的版本无需通过react库来将JSX语法转为虚拟DOM,可以自动转为虚拟DOM
//在这里我们还是引入了react库 虽然当前的版本是"react": "^17.0.1"

import React from 'react';
import ReactDOM from 'react-dom';

// 项目入口文件  file:src/index.js
// 如何打包以及如何将打包后的文件运行在服务器端??


//最终被渲染为<a href="/about">hello world</a>
const data={
    title:'hello',
    subTitle:'world'
}




//17以及之后的版本可以直接在页面上书写JSX语法
//会被直接编译为虚拟DOM
let header = 
  <div className="header div" id="header">
      <h1 id="logo" style={{
        width:50,
        height:50,
        fontSize:10,
        backgroundColor:'red'
      }}>开课吧</h1>
      <nav>
        <a href="/index">a1</a>
        <a href="/about">{data.title + ' '+ data.subTitle}</a>
        <a href="/about">a2</a>
      </nav>
      <br />
      <input type="text" ></input>
  </div>

console.log(header); 
//$$typeof: Symbol(react.element)  直接被编译成虚拟DOM


//file:public/index.html的默认根节点是#root
ReactDOM.render(header,document.querySelector('#root'))

实际二:一个项目中只会运行src目录下的文件 其他目录下不参与运行

import React from 'react';
import ReactDOM from 'react-dom';

/**
 * 条件输出   react只关心视图构建
 * 
 * 但是可以使用下方语法来做到
 * ||  或
 * &&  与
 *  ? :   三目运算符
 * 函数调用后返回的合法值   (function(i){return i})(1)
 */


console.log('hello world');


let header = 
  <div className="header div" id="header">
      <nav>
        <a href="/about">||的使用:{ false || 2}</a><br/>
        <a href="/about">&&的使用:{ false && 2}</a><br/>
        <a href="/about">&&的使用:{ 3 && 2}</a><br/>
        <a href="/about">三目的使用:{ true ? 2: 0}</a><br/>
        <a href="/about">函数的使用必须有返回一个合法值:{ (function(i){return i ;})(2)}</a><br/>
      </nav>
      <br />
      <input type="text" ></input>
  </div>


//虚拟DOM
console.log(header); //$$typeof: Symbol(react.element)

ReactDOM.render(header,document.querySelector('#root'))

实践三:使用{表达式}实现列表输出渲染

import React from 'react';
import ReactDOM from 'react-dom';


console.log('hello world');



//数组中有几位就渲染为几位子节点
//这里是被渲染为三个子节点
// const arr=[
//   <div>1</div>,
//   <div>2</div>
// ]


//被渲染为<a href="/about">列表输出:列表-1列表-2列表-3</a>
const data1=[
  '列表-1',
  '列表-2',
  '列表-3',
]




// const list =<ul>
//   {<li>{data1}</li>}
// </ul>



//列表输出就是根据原有数据映射为一个数组
const dataArr=[
  <li>item1</li>,
  <li>item2</li>,
  <li>item3</li>
]

const newData=data1.map(item=>{
  return <li>{item}</li>
})

console.dir(newData); //每一项:$$typeof: Symbol(react.element)


const list =<ul>
  {newData}
</ul>

console.log(list); //$$typeof: Symbol(react.element)

ReactDOM.render(list,document.querySelector('#root'))

实践四:对象要怎么使用{}

通常情况下 对象不能像 {对象}  这样来使用

可以转换为数组的形式

import React from 'react';
import ReactDOM from 'react-dom';

/**
 * 对象
 
根据原有数据映射成数组

列表输出时每一项必须有key值
 */


console.log('hello world');




const data={
  a:{
    title:'列表一'
  },
  b:{
    title:'列表二'
  },
  c:{
    title:'列表三'
  }
}
console.log(Object.keys(data));

//data[item]是个对象 对象不能用在{}中
const newData=Object.keys(data).map((item,key)=><li key={key}>{data[item].title}</li>)

const list =<ul>
  {newData}
</ul>

console.log(list); //$$typeof: Symbol(react.element)

ReactDOM.render(list,document.querySelector('#root'))

总结实践:

  1. jsx不是html

  2. jsx中不是字符串

  3. jsx最终会被解析成一个虚拟DOM 也就是说 jsx会被解析为一个对象值

  4. jsx必须有一个且只有一个顶层父级

    1. 但是可以借助Fragment   它最终不会被渲染为一个真正的DOM
      Fragment 是包含容器
      
      另一种解决方法  17版本及之后可以使用 <></> 空标记来作为顶层父级
      而且这个空标记 不需要 额外去引入
      
      import React,{Fragment} from 'react';
      import ReactDOM from 'react-dom';
      
      
      console.log('hello world');
      
      
      
      const data={
        a:{
          title:'列表一'
        }
      }
      
      const newData=Object.keys(data).map((item,key)=><li key={key}>{data[item].title}</li>)
      
      const list =<Fragment><ul>
        {newData}
      </ul>
      <div>现在可以有多个父级了 
      Fragment标签不会被渲染为一个真正的DOM</div>
      </Fragment>
      console.log(list); //$$typeof: Symbol(react.element)
      
      ReactDOM.render(list,document.querySelector('#root'))
      
  5. jsx中区分大小写 标签名要全部小写 组件名字首字母大写

  6. 列表输出时必须添加key值

组件的概念

对具有一定独立功能的数据与方法的封装,对外暴露接口,有利于代码功能的复用,且不用担心冲突问题。

类式组件

  • 组件类必须继承 React.Component
  • 组件类必须有 render 方法
file: app.js

import React,{Component} from 'react';
class App extends Component{
    render(){
        return <h1>hello world</h1>
    } 
}

export default App


-------------------------------------------
file: index.js


import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import App from './app';
/**
 * 组件
 * 
 * 根据视图拆分为各个组件
 * 每一个组件都有封闭的数据等等
 * 
 * 类组件:  
 *     - 组件类必须继承 **React.Component**
 *     - 组件类必须有 **render** 方法  
 *        --render的返回值是该组件要构建的视图(虚拟DOM)
 */


console.log('hello world');


//组件实例对象 虚拟DOM
console.log(App,'APP');

ReactDOM.render(<App/>,document.querySelector('#root'))

函数式组件

  • 函数的名称就是组件的名称
  • 函数的返回值就是组件要渲染的内容

props 和 state

  • props 父组件传递过来的参数

  • state 组件自身状态

    • state组件状态: 
      当组件状态有修改时,会对组件进行更新,从而实现组件的视图更新
      
      修改组件状态:
      要通过调用组件的setState方法来完成
      
    • setState:修改组件状态

      • setState方法怎么使用???
        
        /*
        1.调用setState方法  传入newState  传入希望更新的值
        setState(newState)  异步的 完成组件更新
        原理:调用setState之后,会调用render生成新的VDOM完成组件更新
        */
        
        
        import React,{Component} from 'react';
        
        
        class App extends Component{
            state={ //组件自身状态 类似vue中的data属性 
                count:1 //当希望通过数据更新视图的时候可以添加使用状态
            }
            handleClick=()=>{
            
            	//1.先获取count	
                let {count} =this.state
                // console.log(this,'指向App组件实例');
                
                // 2.接收一个对象
                this.setState({
                    count:count+1
                })
            }
            render(){
                const {count}=this.state
                return <h1>hello world
                    <p>{count}</p>
                    <button onClick={this.handleClick}>递增</button>
                </h1>
            } 
        }
        
        export default App
        
    • 多个 setState 合并

  • props 与 state 的区别

    • state 的主要作用是用于组件保存、控制、修改自己的可变状态,在组件内部进行初始化,也可以在组件内部进行修改,但是组件外部不能修改组件的 state

    • props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件,它是外部传进来的配置参数,组件内部无法控制也无法修改

    • state 和 props 都可以决定组件的外观和显示状态。通常,props 做为不变数据或者初始化数据传递给组件,可变状态使用 state

使用事件时this的指向问题

  • 类在实例化的时候一定会执行constructor
  • new的过程不需要我们去书写 react会自动帮我们去new这个App实例
  • 组件在每次实例化的时候才会去调用constructor
  • class 语法 class App extends Component
  • 注意:事件名on之后 每个单词首字母大写

演示使用事件时this的指向问题:this指向undefined而不是该组件实例

import React,{Component} from 'react';


// class App extends Component{
//     state={ //组件自身状态 类似vue中的data属性 
//         count:1 //当希望通过数据更新视图的时候可以添加使用状态
//     }
//     render(){
//         const {count}=this.state
//         return <h1>hello world
//             <p>{count}</p>
//             <button onClick={function(){console.dir(this,'指向undefined')}}>递增</button>
//         </h1>
//     } 
// }

export default App

解决方法一:使用箭头函数

import React,{Component} from 'react';

// class App extends Component{
//     state={ //组件自身状态 类似vue中的data属性 
//         count:1 //当希望通过数据更新视图的时候可以添加使用状态
//     }
//     render(){
//         const {count}=this.state
//         return <h1>hello world
//             <p>{count}</p>
//             <button onClick={()=>{
//                 console.dir(this,'指向App')
//             }}>递增</button>
//         </h1>
//     } 
// }

export default App
版本二:单独把函数抽离出来

// class App extends Component{
//     state={ //组件自身状态 类似vue中的data属性 
//         count:1 //当希望通过数据更新视图的时候可以添加使用状态
//     }
//     handleClick=()=>{
//         console.log(this,'指向App组件实例');
//     }
//     render(){
//         const {count}=this.state
//         return <h1>hello world
//             <p>{count}</p>
//             <button onClick={this.handleClick}>递增</button>
//         </h1>
//     } 
// }

解决方法二:使用bind重绑this指向

import React,{Component} from 'react';

class App extends Component{
 //可以将state写在构造函数中
 // state={ //组件自身状态 类似vue中的data属性 
 //     count:1 //当希望通过数据更新视图的时候可以添加使用状态
 // }


 //绑定this一般在构造函数中完成
 constructor(props){
     //一定要 继承至父类的构造函数
     //且要一定要接收 props 
     super(props)

     this.state={
         count:1
     }

     //在构造函数中进行this的硬绑定
     //最方便的写法是用箭头函数
     this.handleClick=this.handleClick.bind(this)
 }
 
 //写在render中的bind绑定 因为在每次更新视图时会重新触发render函数
 //所以会导致每一次都会重新绑定一次this指向!!
 //不推荐:<button onClick={this.handleClick.bind(this)}>递增</button>
 
 
 handleClick=function(){console.log(this)}
 render(){
     const {count}=this.state
     return <h1>hello world
         <p>{count}</p>
         <button onClick={this.handleClick}>递增</button>
     </h1>
 } 
}

export default App

案例演示一

第一步:构建静态视图 模板+样式

  • 分析数据
let rootdata = {
  React:["Component","Hooks","state&props"],
  Router:["Browsers","Hash","NavLink"],
  Redux:["reducer","action","dispatch"]
}; 

export default rootdata;
  • 使用数据构造视图
import React,{Component} from 'react';

//组件
import Menu from './menu';

//数据
import rootdata from './data';


//最终版本:
class App extends Component{
    render(){
        // const {count}=this.state
        // 为了构造[<Menu/>,<Menu/>,<Menu/> ]
        // <Menu  title={item} data={rootdata[item]}/>来传递props给子组件
        return <ul id="menu">
            {Object.keys(rootdata).map((item,index)=>{
                return <Menu 
                    key={index}
                    title={item}
                    data={rootdata[item]}
                    />
            })}
    </ul>
    } 
}

//版本二:
// class App extends Component{
//     render(){
//         // const {count}=this.state
//         return <ul id="menu">
//             <Menu></Menu>
//             <Menu></Menu>
//             <Menu></Menu>
//     </ul>
//     } 
// }

//版本一:
// class App extends Component{
//     render(){
//         // const {count}=this.state
//         return <ul id="menu">
//             {Object.keys(rootdata).map((item,index)=>{
//                 console.log(item,'item');
//                 console.log(rootdata[item],'子项');
//                 return <Menu 
//                     key={index}
//                     title={item}
//                     data={rootdata[item]}
//                     />
//             })}
//     </ul>
//     } 
// }

export default App

第二步:拆分组件 有三个调用数组生成三个组件

  • 实际工作中有几项数据就生成几个
  • 怎么在父级中传递给子项 props
    • 父组件调用子组件时 可以将数据添加在子组件的属性上
    • 在子组件中可以通过props属性接收父级传递的数据
import { Component } from "react";


//版本二:
class Menu extends Component{
    state={
        show:false
    }
    
    render(){
        const {title,data}=this.props;
        const {show}=this.state

        console.log(title,data,'title');
        return  <li className={show ? 'subList-show' :''}>
            <a onClick={()=>{
                this.setState({
                    show:!show
                })
            }}>{title}</a>
            <ul className="subList">
                {data.map((item,index)=><li key={index}>{item}</li>)}
            </ul>
        </li>
    }
}

// 版本一:
// class Menu extends Component{
//     render(){
//         const {title,data}=this.props;

//         console.log(title,data,'title');
//         return  <li className='subList-show'>
//             <a>React</a>
//             <ul className="subList">
//                 <li>Component</li>
//                 <li>Hooks</li>
//                 <li>state&props</li>
//             </ul>
//         </li>
//     }
// }
export default Menu;
样式文件:
body {
    margin: 0;
    background: #f3f3f3;
}
ul {
    margin: 0;
    padding: 0;
    list-style: none;
}

#menu {
    margin: 50px auto;
    display: flex;
    width: 360px;
    height: 40px;
    border: 1px solid #000;
    border-left:none;
    background: #fff;
}
#menu>li {
    position: relative;
    width: 119px;
    border-left: 1px solid #000;
}
#menu>li:after {
    content: ">";
    position: absolute;
    left: 10px;
    top: 13px;
    font: 14px/1 "宋体";
}
#menu a {
    display: block;
    font: 14px/40px "宋体";
    text-align: center;
}
.subList {
    display: none;
    position: absolute;
    left: -1px;
    top: 30px;
    width: 109px;
    padding: 5px;
    background: #fff;
    border: 1px solid #000;
    border-top: none;
}
.subList li {
    font: 12px/30px "宋体";
    text-indent: 20px;
    border-bottom: 1px solid #ccc;
}
#menu .subList-show .subList{
    display: block;
}
#menu .subList-show:after {
    transform: rotate(90deg);
}
模板文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link href="css/index.css" rel="stylesheet" />
</head>
<body>
    <ul id="menu">
        <li>
            <a>React</a>
            <ul class="subList">
                <li>Component</li>
                <li>Hooks</li>
                <li>state&props</li>
            </ul>
        </li>
        <li class="subList-show">
            <a>Router</a>
            <ul class="subList">
                <li>Browsers</li>
                <li>Hash</li>
                <li>NavLink</li>
            </ul>
        </li>
        <li>
            <a>Redux</a>
            <ul class="subList">
                <li>reducer</li>
                <li>action</li>
                <li>dispatch</li>
            </ul>
        </li>
    </ul>
</body>
</html>
posted @ 2021-01-07 15:35  林子酱  阅读(222)  评论(0)    收藏  举报