React学习笔记---React脚手架

React脚手架

  • 一、使用create-react-app创建react应用
    • 1、react脚手架
    • 2、创建项目并启动
    • 3、react脚手架项目结构
    • 4、功能界面的组件化编码流程(通用)
    • 5、用脚手架写一个简单示例
  • 二、组件的组合使用(综合实例)

一、使用create-react-app创建react应用

1、react脚手架

  1. xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
    1). 包含了所有需要的配置(语法检查、jsx编译、devServer…)
    2). 下载好了所有相关的依赖
    3). 可以直接运行一个简单效果
  2. react提供了一个用于创建react项目的脚手架库: create-react-app
  3. 项目的整体技术架构为: react + webpack + es6 + eslint
  4. 使用脚手架开发的项目的特点: 模块化, 组件化, 工程化

2、创建项目并启动

第一步,全局安装:npm i -g create-react-app
第二步,切换到想创项目的目录,使用命令:create-react-app hello-react
第三步,进入项目文件夹:cd hello-react
第四步,启动项目:npm start

实例演示
在这里插入图片描述
然后就等待一会儿就创建出来了
React学习笔记---React脚手架_第1张图片
如果发现你创建的时间特别长,几分钟甚至十几分钟,那么可能是你的npm没有使用国内镜像,因为在创建react项目的时候,它有许多包需要从外网下载,所以会导致很慢,那怎么解决呢?运行下面两行命令就可以了。

打开你的cmd
首先运行npm config set registry https://registry.npm.taobao.org
然后运行npm config get registry验证是否成功
如果出现https://registry.npm.taobao.org/就表示成功了
然后你重新create-react-app 项目名就发现很快了

安装成功后,启动项目
出现下面这个的同时会自动在浏览器打开一个窗口
React学习笔记---React脚手架_第2张图片
React学习笔记---React脚手架_第3张图片

3、react脚手架项目结构

React学习笔记---React脚手架_第4张图片
React学习笔记---React脚手架_第5张图片
index.html页面的解析

DOCTYPE html>
<html lang="en">
  <head>
		<meta charset="utf-8" />
		
		<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
		
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		
    <meta name="theme-color" content="red" />
    
    <meta
      name="description"
      content="Web site created using create-react-app"
		/>
		
		<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
		
		<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React Apptitle>
  head>
  <body>
		
    <noscript>You need to enable JavaScript to run this app.noscript>
    <div id="root">div>
  body>
html>

index.js文件解析

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  //在App外侧包裹React.StrictMode,用来检查各个组件是否符合react的规范
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

reportWebVitals();

4、功能界面的组件化编码流程(通用)

  1. 拆分组件: 拆分界面,抽取组件
  2. 实现静态组件: 使用组件实现静态页面效果
  3. 实现动态组件
    3.1 动态显示初始化数据
      1) 数据类型
      2) 数据名称
      3) 保存在哪个组件?
    3.2 交互(从绑定事件监听开始)

5、用脚手架写一个简单示例

目录结构
React学习笔记---React脚手架_第6张图片

App.jsx

//创建“外壳”组件App
import React,{Component} from 'react'
import Hello from './components/Hello'
import Welcome from './components/Welcome'

//创建并暴露App组件
export default class App extends Component{
	render(){
		return (
			<div>
				<Hello/>
				<Welcome/>
			</div>
		)
	}
}


index.js

//引入react核心库
import React from 'react'
//引入ReactDOM
import ReactDOM from 'react-dom'
//引入App组件
import App from './App'

//渲染App到页面
ReactDOM.render(<App/>,document.getElementById('root'))

Hello 下的index.jsx

import React, { Component } from 'react'
import hello from './index.module.css'

export default class Hello extends Component {
  render () {
    return (
      <h1 className={hello.backColor}>hello-react</h1>
    )
  }
}

Hello 下的index.module.css
注意:这里这样搞是为了搞css模块化,避免类名相同,引起样式冲突覆盖,也可以直接使用less或者sass写css,也可以避免这样的问题
使用了css模块化就可以这样引入样式
import hello from './index.module.css'

.backColor {
  background-color: skyblue;
}

Welcome 下的index.jsx

import React, { Component } from 'react'
import './index.css'

export default class Hello extends Component {
  render () {
    return (
      <h1 className="backColor">hello-react</h1>
    )
  }
}

Welcome 下的index.css

.backColor {
  background-color: skyblue;
}

二、组件的组合使用(综合实例)

目录结构
React学习笔记---React脚手架_第7张图片
效果图
React学习笔记---React脚手架_第8张图片
index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

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

App.jsx

import React, { Component } from 'react'
import './App.css'

import Header from './components/Header'
import List from './components/List'
import Footer from './components/Footer'
export default class App extends Component {
  state = {
      todos: [
        { id: 1, name: '吃饭', done: true },
        { id: 2, name: '睡觉', done: true },
        { id: 3, name: '敲代码', done: false }
      ]
    }

  addTodo = (todoObj) => {
    const { todos } = this.state
    const newTodos = [todoObj, ...todos]
    this.setState({ todos: newTodos })
  }

  updateTodo = (id, done) => {
    const { todos } = this.state
    const newTodos = todos.map(item => {
      if (id === item.id) {
        return { ...item, done: done }
      } else {
        return item
      }
    })
    this.setState({ todos: newTodos })
  }

  deleteTodo = (id) => {
    const { todos } = this.state
    const newTodos = todos.filter(item => {
      return id !== item.id
    })
    this.setState({ todos: newTodos })
  }

  checkedAll = (doneStatu) => {
    const { todos } = this.state
    const newTodos = todos.map((item) => {
      return { ...item, done: doneStatu }
    })
    this.setState({ todos: newTodos })
  }

  deleteChecked = () => {
    const { todos } = this.state
    const newTodos = todos.filter(item => {
      return item.done === false
    })
    this.setState({ todos: newTodos })
  }

  render () {
    const { todos } = this.state
    return (
      <div className="todo-container">
        <div className="todo-wrap">
          <Header addTodo={this.addTodo} />
          <List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo} />
          <Footer todos={todos} checkedAll={this.checkedAll} deleteChecked={this.deleteChecked} />
        </div>
      </div>
    )
  }
}

App.css

/*base*/
body {
  background: #fff;
}

.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}

Header中的index.jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { nanoid } from 'nanoid'
import './index.css'

export default class Header extends Component {

  static propTypes = {
    addTodo: PropTypes.func.isRequired
  }

  handleKeyUp = (event) => {
    console.log("ddd");
    const { keyCode, target } = event
    if (keyCode !== 13) return
    if (target.value.trim() === '') {
      alert('输入不能为空')
      return
    }
    //准备好一个todo对象
    const todoObj = { id: nanoid(), name: target.value, done: false }
    //将todoObj传递给App
    this.props.addTodo(todoObj)
    target.value = ''
  }

  render () {
    return (
      <div className="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认" onKeyUp={this.handleKeyUp} />
      </div>
    )
  }
}

Header中的index.css

/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}

.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}

List中的index.jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'

export default class List extends Component {
  static propTypes = {
    todos: PropTypes.array.isRequired,
    updateTodo: PropTypes.func.isRequired,
    deleteTodo: PropTypes.func.isRequired
  }
  render () {
    const { todos, updateTodo, deleteTodo } = this.props
    return (
      <ul className="todo-main">
        {
          todos.map((item) => {
            return <Item key={item.id} {...item} updateTodo={updateTodo} deleteTodo={deleteTodo} />
          })
        }
      </ul>
    )
  }
}

List中的index.css

/*main*/
.todo-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}

.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}

Item中的index.jsx

import React, { Component } from 'react'

import './index.css'

export default class Item extends Component {

  state = {
    mouse: false
  }

  handleMouse = (flag) => {
    return () => {
      console.log(flag)
      this.setState({ mouse: flag })
    }
  }

  handleChecked = (id) => {
    return (event) => {
      this.props.updateTodo(id, event.target.checked)
    }
  }

  deleteTodo = (id) => {
    if (window.confirm('确定删除吗?')) {
      this.props.deleteTodo(id)
    }
  }

  render () {
    console.log(this.props);
    const { mouse } = this.state
    const { id, name, done } = this.props
    return (
      <li style={{ backgroundColor: mouse ? '#ddd' : 'white' }} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}>
        <label>
          <input type="checkbox" checked={done} onChange={this.handleChecked(id)} />
          <span>{name}</span>
        </label>
        <button onClick={() => this.deleteTodo(id)} className="btn btn-danger" style={{ display: mouse ? 'block' : 'none' }}>删除</button>
      </li>
    )
  }
}

Item中的index.css

/*item*/
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}

li label {
  float: left;
  cursor: pointer;
}

li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}

li button {
  float: right;
  display: none;
  margin-top: 3px;
}

li:before {
  content: initial;
}

li:last-child {
  border-bottom: none;
}

Footer中的index.jsx

import React, { Component } from 'react'

import './index.css'

export default class Footer extends Component {
  checkedAll = (event) => {
    console.log(event.target.checked);
    this.props.checkedAll(event.target.checked)
  }

  deleteSelected = () => {
    this.props.deleteChecked()
  }

  render () {
    const { todos } = this.props
    // const newTodos = todos.filter(item => {
    //   return item.done === true
    // })
    // const selectCount = newTodos.length
    const doneCount = todos.reduce((pre, current) => {
      if (current.done === true) {
        return pre + 1
      } else {
        return pre + 0
      }
    }, 0)
    const sum = todos.length
    return (
      <div className="todo-footer">
        <label>
          <input type="checkbox" checked={doneCount === sum && sum !== 0 ? true : false} onChange={this.checkedAll} />
        </label>
        <span>
          <span>已完成{doneCount}</span> / 全部{sum}
        </span>
        <button className="btn btn-danger" onClick={this.deleteSelected}>清除已完成任务</button>
      </div>
    )
  }
}

Footer中的index.css

/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}

.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}

.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}

.todo-footer button {
  float: right;
  margin-top: 5px;
}

案例总结以及注意点

1.拆分组件、实现静态组件,注意:className、style的写法

2.动态初始化列表,如何确定将数据放在哪个组件的state中?

1)、某个组件使用:放在其自身的state中
2)、某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)

3.关于父子之间通信:
1).【父组件】给【子组件】传递数据:通过props传递
2).【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数

4.注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value

input的checked和defaultChecked的区别,defaultChecked只在第一次有效,如果页面初始化之后,需要进行一些操作(例如勾选未勾选的复选框,当全部已经勾选的时候,希望这个全部的复选框也勾选,这个时候就不能使用defaultChecked,因为defaultChecked只在页面初始化时起作用)
但是用了checked就必须用onChange

5.状态在哪里,操作状态的方法就在哪里

下一篇 脚手架配置代理在这里
脚手架配置代理

你可能感兴趣的:(React专栏,react.js,javascript,webpack)