react入门学习

初始配置

$ 1 创建基本的webpack项目

创建目录

react入门学习

  • npm init -y 创建package.json

  • cnpm i webpack webpack-cli -D 安装webpack webpack-cli

  • 在根目录创建webpack.config.js并写入相应代码module.exports={mode:'development'}
    react入门学习

  • 终端里输入webpack进行打包,之后dist文件夹里多出个main.js,打包成功

配置实时打包编译
  • cnpm i webpack-dev-server -D
    package.jsonscripts里写入"dev": "webpack-dev-server --open --port 3000 --hot --host 127.0.0.1 --progress --compress"webpack-dev-server之后的如--open,--hot等代码是功能性代码,可选择性写
`--open` 设置自动打开默认浏览器 `--oprn chrome`设置自动打开google浏览器
`--port 3000` 设置端口号为3000,若不写则默认是8080
`--host 127.0.0.1`设置域名为127.0.0.1
`--compress` 压缩
`http://loaclhost:8080/`
  • npm run dev 运行,文件可在http://localhost:8080打开(ctrl+c可以终止npm run dev)
    index.html 中<script src="../dist/main.js"></script> 路径改为/main.js
因为webpack-dev-server打包好的main.js是托管到内存中的,在内存中是看不到的,
但却实实在在的存在于根目录中,所以用/main.js引入根目录中的main.js
解决经常访问磁盘目录问题——将index页面也放入内存中即可
  • cnpm i html-webpack-plugin -D 安装在内存中自动生成html页面的插件,解决每保存一次就调用代码消耗磁盘性能的问题
  • webpack.config.js写入如下代码(dirname前是两个 _ )--注意,代码千万不要敲错

react入门学习

const path=require('path')
const HtmlWebPackPlugin = require('html-webpack-plugin')

const htmlPlugin = new HtmlWebPackPlugin({
  template: path.join(__dirname,'./src/index.html'),
  filename:'index.html'
})

module.exports = {
  mode: 'development',
  plugins:[
    htmlPlugin
  ]
}

在这运行之后,系统会自动把打包好的js注入到页面中去(如下图蓝色部分是系统自动注入的)。所以<script src="/main.js"></script>可以省去了!react入门学习

  • npm run dev即可(注,不要cnpm run dev, cnpm一般只用来装包)

———————————————以上,初步配置完成———————————————

$ 2 react起步(react中,一切都是用js来表现的)

安装react react-dom包
  • cnpm i react react-dom -S
  • 在index.js里导入包
    import React from 'react'
    import ReactDOM from 'react-dom'
  • 接下来可以根据格式来写代码了,在index.js里写
创建虚拟DOM元素: const myh1 = React.createElement("h1",{id:"myh1",title:"thish1"},"这是一个h1标签及其内容")
`参数1:标签类型:div,h1,input等:
参数2:标签属性,{attr:"aa",attr:"bb"},不设置就写null`
`参数3:标签子节点,或者说内容
参数n:其他更多子节点`
调用render函数渲染: ReactDOM.render(myh1,document.getElementById("list1"))
`参数1:要渲染的那个虚拟DOM元素,如myh1;
参数2:指定html页面上的一个容器(必须写成dom元素的形式,就是这里的document.getElementById)
如<div ></div>`

标签嵌套写法
react入门学习

JSX
  • React.createElement形式写起来太繁琐
  • 于是react开发出了在js中,混合写入类似于html的语法,我们称之为JSX
  • 写好之后通过babel转换成React.createElement形式执行便可
  • jsx 语法的本质:并不是直接把 jsx 渲染到页面上,而是内部先转换成了 createElement 形式,再渲染的
配置babel

(版本配置问题,避免踩坑从我做起 https://blog.csdn.net/tj310/article/details/88134512 )

  • 安装 babel 插件
    • 运行cnpm i babel-loader @babel/core @babel/plugin-transform-runtime @babel/runtime -D
    • 运行 cnpm i @babel/preset-env @babel/plugin-proposal-class-properties -D
  • 安装能够识别转换jsx语法的包,就是将jsx转化为js @babel/preset-react
    • 运行cnpm i @babel/preset-react -D
  • 在根目录添加 .babelrc 配置文件
{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": ["@babel/plugin-transform-runtime", "@babel/plugin-proposal-class-properties"]
}
  • 在webpack.config.js的module.exports中添加babel-loader配置项:
module: { //要打包的第三方模块
    rules: [
      { test: /.js|jsx$/, use: 'babel-loader', exclude: /node_modules/ }  //exclude一定不能丢
    ]
}

  • 后缀名的省略及改变路径
在以上,在webpack.config.js之中的module下方添加如下代码
 resolve: {
    extensions: ['.js', '.jsx', '.json', '.vue'], // 表示,这几个文件的后缀名,可以省略不写
    alias: { // 表示别名
      '@': path.join(__dirname, './src') // 这样,@ 就表示 项目根目录中 src 的这一层路径
    }
  }
创建组件(两种方法,本质区别是有无state属性)

方法1:普通function模式创建组件(用的不多)

1、 src目录下创建components文件夹,里面创建组件文件(如comp1.jsx),在里面写入组件
    `如`
        import React from "react"     `*****必须引入React包*****`
        function Hello(props) {          `***组件名称首字母大写***`
          return <div>这是一个Hello组件----{props.name}----{props.age}----{props.weight}</div>
        }
        export default Hello           `*****必须暴露组件*****`

2、在index.js文件里引入组件
        import Hello(组件名称) from "./components/comp1.jsx"   `*****必须引入组件*****`

        const arrStr = ["haha", "heihei", "hehe", "xixi"]

        const dog={
            name:"金毛",
            age:"2",
            sex:"公",
            weight:"50kg"
        }

        ReactDOM.render(<div>

          <Hello {...dog}></Hello>      `***使用组件,以组件做标签名来使用,记住要和组件名一模一样***`
                                                      `{...dog}`,es6语法---展开运算符

          {arrStr.map(item => < div key = {item.id}><h3>{item}</h3></div > )}   //map方法映射数组,箭头函数,key

         </div>, document.getElementById("list1"))
     
方法2:class关键字创建组件
  • 先了解什么是class关键字
创建一个动物类
//在class的{}区间内只能写构造器(内部包含实例属性),静态属性,实例方法,静态方法
//class关键字内部,还是原来的js方法来写,只不过更方便了,因此把class关键字称作 语法糖
const Animal {    // 这是class关键字
构造器://class关键字内必须要有。每当执行new这个类的时候,会优先执行构造器中的代码
    constructor("name","age"){
        this.name=name      
        this.age=age
    }
静态属性://在class内部,通过static修饰的属性
    static info = "aaaaa"
实例方法(经常用到)
    yaoweiba(){
        console.log("这是一个实例方法")
    }
静态方法(用的不多)
    static yaoweiba(){
        console.log("这是Animal的静态方法")
    }
}

    const a1 = new Animal("金毛", 2)
    console.log(a1)
    a1.yaoweiba()   这是实例方法的调用
    Animal.yaoweiba()   这是静态方法的调用
  • class关键字的继承
class Person{     //父类
    constructor(name,age){     //这里是公用样式
        this.name=name
        this.age=age
       }
    sayHello(){
        console.log("大家好")
        }
  }

class American extends Person{    `// class 子类名 extends 父类名`
    constructor(name,age){
    super(name,age)                 `//如果写构造器了,则super()必须要有,而且必须放在构造器里的第一行`
        }
  }
const am1 = new American("jack",20)
console.log(am1)
am1.sayHello()

class Chinese extends Person{
     constructor(name,age,IdNumber){
        super(name,age)   
        this.IdNumber = IdNumber
      }
  }
const ch1  = new Chinese("张三",22,"342422************")
console.log(ch1)
ch1.sayHello()
  • class关键字创建组件
// 如果要使用 class 定义组件,必须 让自己的组件,继承自 React.Component
class Person extends React.Component {
   constructor() {   //给class创建的组件绑定私有数据(普通创建不支持)
      super()
      // 只有调用了 super() 以后,才能使用 this 关键字
      this.state = { // 这个 this.state = {} 就相当于 Vue 中的 data() { return { } }
        msg: '大家好,我是 class 创建的 Person组件'
      }
    }
      // 在 组件内部,必须有 render 函数,作用:渲染当前组件对应的 虚拟DOM结构
      render(){
          // render 函数中,必须 返回合法的 JSX 虚拟DOM结构
        this.state.msg="msg的值被我修改了"   //state私有数据的值是可以修改的,而props是不可修改的
        return <div>
        <h5>这是 class 创建的组件---{this.props.name}---{this.props.age}---{this.props.wife}</h5>
        <h2>{this.state.msg}</h2></div>
//在 class 关键字创建的组件中,如果想使用外界传递过来的 props 参数,不需接收,直接通过 this.props.属性名 访问
     }
}

const person={
    name:"林子闲",
    age:22,
    wife:"乔韵"
 }

ReactDOM.render(<div>
  <Person name={person.name} age={person.age} wife={person.wife}></Person>     
 //必须用组件名以标签的形式调用
   <Person {...person}></Person>
</div>,document.getElementById("list1"))
使用CSS样式美化组件

详细教程请看 https://www.bilibili.com/video/av25294556/?p=42 (第42集)

1、先下载相关包
      cnpm i style-loader css-loader -D
2、在webpack.config.js内的rules里配置如下代码
     { test: /.css$/, use: ['style-loader', 'css-loader']} 
3、在src文件夹下创建css文件夹,再在里面创建css文件,然后写样式
      如 src > css > style1.css
      在style1.css里写样式
         .mystyle{
              color:red;
              font-size: 14px;
          }
4、在相关文件中引入css文件
      import cssobj from "@/css/style1.css"    //这里固定写法cssobj,而非css
5、接下来可以在相关文件中通过className给定样式了
      如<div className="mystyle">哈哈</div>
CSS模块化——避免css样式污染全局代码问题,让其只在当前区域生效
* css模块化只针对 `类选择器` 和 `id选择器` 生效
1、 将webpack.config.js内的rules里的以下代码
     { test: /.css$/, use: ['style-loader', 'css-loader']} 改为
     { test: /.css$/, use: ['style-loader', 'css-loader?modules']} 
2、 给定样式的方式改成
   如  <div className={cssobj.mystyle} id={css.obj.mystyle2}>哈哈</div>
如果有某些css元素不想被模块化,想全局生效
1、将webpack.config.js内的rules里的一下代码
     { test: /.css$/, use: ['style-loader', 'css-loader?modules']} 改为
     { test: /.css$/, use: ['style-loader', 'css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]'] }
//[path]  表示样式表 相对于项目根目录 所在路径
//[name]  表示 样式表文件名称
//[local]  表示样式的类名定义名称
//[hash:length]  表示最多32位的hash值,length可以指定具体数值  
2、  :global(.allstyle){    //这样写就不会被模块化了 :local(属性名)
      font-size: 12px;
      padding: 0;
  }
3、然后在相关文件里引用
  <div className={cssobj.mystyle} className={.allstyle}>哈哈</div>
也可以这么写
<div className={cssobj.mystyle+" allstyle"}>哈哈</div>   //allstyle前面有空格哦
也可以这么写
<div className={[cssobj.mystyle,"allstyle"].join(" ")}>哈哈</div>   //空格哦
在项目中启用模块化并同时使用bootstrap

原理:第三方的样式表是以.css为后缀名,而我们将自己的样式用scss或less来做后缀名, 这样就可以区分开来自己的样式和第三方样式,从而可以给scss,less启用模块化,而不 影响第三方样式

1、下载bootstrap包   cnpm i bootstrap -S
2、下载包处理字体文件的url包   cnpm i url-loader -D    cnpm i file-loader -D
3、webpack.config.js的rules里添加   { test:/.ttf|woff|woff2|eot|svg$/,use:'url-loader'}
4、下载sass的包   cnpm i sass-loader node-sass -D
5、webpack.config.js的rules里添加   { test:/.scss$/,use:['style-loader','css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]','sass-loader']}
6、将{ test: /.css$/, use: ['style-loader', 'css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]','sass-loader'] }
   改为{ test: /.css$/, use: ['style-loader', 'css-loader'] }
7、在相应jsx或js文件里引入   import "bootstrap/dist/css/bootstrap.css"  
   然后尽情发挥吧

webpack.config.js里本阶段rules最终版如下

rules: [
      {test: /.js|jsx$/,use: 'babel-loader',exclude: /node_modules/},
      { test: /.css$/, use: ['style-loader', 'css-loader'] },
      { test:/.ttf|woff|woff2|eot|svg$/,use:'url-loader'},
      { test:/.scss$/,use:['style-loader','css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]','sass-loader']}
    ] 
为按钮绑定事件及修改私有数据

onClick onMouseenter onMouseover等 要采用小驼峰命名

import React from "react"
import "bootstrap/dist/css/bootstrap.css"

export default class Hello extends React.Component{
  constructor(){
    super()
    this.state={
        msg: '我没被修改呢'
    }
  }
  render(){
    /*this.state.msg="我被一this.state.msg的方式修改了"*/
    return <div>
            <h5>这是一个Hello组件</h5><br/>
            <h5>{this.state.msg}</h5>
            <button className="btn btn-primary" onClick={ ()=>{this.myclickHandler(参数1,参数2)}} >按钮</button>         
//函数调用用这种形式来写,可以不写参数  onClick={()=>{this.myclickHandler()}}
          </div>

  }
  myclickHandler=(a,b)=>{     //函数部分用箭头函数来写,可以不写参数a,b   myclickHandler=()=>{ }
    alert("我被点击了"+a+b);
    this.setState({       //若要修改私有数据state,react给定了一个方法 this.setState({ }),同时记住this.state.msg在这里不可用
      msg:'我被以this.setState的方式修改了'
    },function(){this.state.msg})   //此处为回调函数,可不加
  }
}
文本框与state值之间的双向绑定
import React from 'react'

export default class BindEvent extends React.Component {
    constructor() {
      super()
      this.state = {
        msg: '哈哈',
        age: 22,
      }
    }

    render() {
      return <div>
        {/* 需求:点击按钮,把修改 msg 的值 */}
        <button onClick={() => this.show('aa', 'bb')}>按钮</button>
        <h3>{this.state.msg}</h3>


        /* 如果 我们只是把 文本框的 value 属性,绑定到了 state 状态,但是,如果不提供   onChagne 处理函数的话,得到的文本框,将会是一个只读的文本框 */
        /* 当为文本框绑定 value 值以后,要么同时提供一个 readOnly, 要么,提供一个   onChange 处理函数 */
        /* <input type="text" style={{  '100%' }} value={this.state.msg} readOnly /> */
        <input type="text" style={{  '100%' }} value={this.state.msg} onChange={(e) => this.txtChanged(e)} />
              </div>
    }
  
    // 每当文本框的内容变化了,必然会调用 这个 txtChanged
    txtChanged = (e) => {
      // 在 onChange 事件中,获取 文本框的值,通过事件参数 e 来获取
      // console.log(e.target.value);

      const newVal = e.target.value

      this.setState({
        msg: newVal
      })
  }

快速搭建react项目

1、电脑里新建文件夹
2、npm create react-app 项目名称    如 npm create react-app huihuidemo / yarn create react-app huihuidemo
3、cd 项目名称    如 cd huihuidemo
4、npm run start
5、安装antd    cnpm i antd -S / yarn add antd 
6、在css文件中最顶部引入  @import '~antd/dist/antd.css';   //分号不能丢
    (也可在js文件里直接引入,  import 'antd/dist/antd.css';  )
7、在js jsx文件中引入需要使用的组件   import {Button,Icon...} from 'antd';
8、(以后必要)安装路由和ajax请求插件   yarn add react-router-dom axios 

具体可见antd官网   https://ant.design/docs/react/use-with-create-react-app-cn
解决react脚手架不支持less的问题
1、安装less相关包    cnpm i less less-loader -S / yarn add less less-loader 
2、暴露webpack    npm run eject / yarn eject   (此语句执行后会有config和scripts文件夹生成)   
      若出现以下错误: (Remove untracked files, stash or commit any changes, and try again.)
      是因为我们使用脚手架创建一个项目的时候,自动给我们增加了一个.gitignore文件,而我们本地却没有文件仓库
      这样解决: 先输入  git add .      再输入  git commit -m 'up' 
3、找到webpack.config.js的const cssRegex = /.css$/; (第38行) 做出如下改动
    - const cssRegex = /.css$/;
    + const cssRegex = /.(css|less)$/;
4、找到webpack.config.js的const loaders ,在里面最后一组后面添加
    { loader: require.resolve('less-loader') },
添加之后是这样的:
const loaders = [
      isEnvDevelopment && require.resolve('style-loader'),
      isEnvProduction && {...
       },
      {...
      },
      {...
      },
      {
        loader: require.resolve('less-loader')
      },
    ].filter(Boolean);
5、大功告成

ps:若npm start失败,出现这个问题(Build fails after eject: Cannot find module '@babel/plugin-transform-react-jsx' )
则可以
1、删除 node_modules 文件夹
2、运行 yarn
3、cnpm i less less-loader -S / yarn add less less-loader
3、重新 npm start / yarn start