01 webpack流程化安装

主要是为了熟悉react的目录结构,指令什么的,以及一些基础的webpack,会有很多配置的东西

01创建目录

mkdir react-demo // 新建项目文件夹
cd react-demo // cd到项目目录下
npm init // npm初始化

webpack 的一些操作

npm i  webpack webpack-cli --save--dev
touch webpack.config.js

webpack配置文件

/*
 * @Descripttion:  webpack.config.js 
 * @version:  ON||FOR
 * @@Company: DCIT-SH
 * @Author: Oneself
 * @Date: 2020-11-27 16:37:06
 * @LastEditors: Oneself
 * @LastEditTime: 2020-11-27 16:39:44
 * @Statu: TODO:    webpack配置文件 
 */
const path = require('path');
module.exports = {
  entry: './src/app.js', // 入口文件
  output: {
    path: path.resolve(__dirname, 'dist'), // 定义输出目录
    filename: 'bundle.js'  // 定义输出文件名称
  }
};

package.json 添加webpack执行命令

"scripts": {
  "build": "webpack --config webpack.config.js"
}

因为我们的入口文件是'./src/app.js',而我们执行build的时候会先到入口文件,我感觉的这个就像是thinkphp或者一些后端框架的 index.php,或者admin.php (意思上)而现在我们并没有入口文件,创建src目录,创建app.js

npm run build

在我们的根目下会生成一个 dist目录,一个 bundle.js

02webpack-dev-server
webpack-dev-server是一个小型的node.js Express服务器,它使用webpack-dev-middleware中间件来为通过webpack打包生成的资源文件提供Web服务。

npm install --save-dev webpack-dev-server

package.json 更新命令

"scripts": {
    "dev":"webpack-dev-server"
}

添加配置
webpack.config.js新增devServer配置

devServer: {
  hot: true, // 热替换
  contentBase: path.join(__dirname, 'dist'), // server文件的根目录
  progress:true,//开启进度条
  compress: true, // 开启gzip
 //open:true, //自动打开浏览器,
  port: 8080, // 端口
},

报错 Cannot find module 'webpack-cli/bin/config-yargs'
这个错误格外的眼熟,刚学webpack时候就遇到过,
package.json 改成如下版本
方法1:前提是你知道你依赖的版本

"webpack-dev-server": "^依赖版本"
删除node_modules文件夹
npm i 

方法2:最新版本

npm uninstall webpack-dev-server -g       卸载全局
npm uninstall webpack-dev-server -D      卸载局部(本地) 
npm install webpack-dev-server --save-dev      最新

方法3 看下以前项目的启动版本

 npm i webpack@4.43.0 webpack-cli@3.3.12 webpack-dev-server@3.11.0 webpack-dev-server -D

麻蛋的又报错了

Error: spawn cmd.exe ENOENT

看到这个错误的时候,我们就要考虑下, cmd.exe
定位错误的话,应该是我们的电脑问题,一般是环境变量,
在后端的日常中,我们会调用一些exe程序进行加密,拆解,拼图,会因为你的exe文件没有配置到电脑的环境变量,而找不到程序,
我查了下 cmd.exe是用户环境变量中加入System32
添加环境变量
cmd.exe 在哪个文件夹

C:\Windows\System32
还是不行,我百度了下,需要重启下电脑。我坲了

OK重启后可以正常启动

03安装 HtmlWebPackPlugin
说白就是html生成器,HTML模板插件 让webpack 简化了HTML文件的创建
HtmlWebPackPlugin
为html文件中引入的外部资源如script、link动态添加每次compile后的hash,防止引用缓存的外部文件问题
可以生成创建html入口文件,比如单页面可以生成一个html文件入口,配置N个html-webpack-plugin可以生成N个页面入口

npm install --save-dev html-webpack-plugin

webpack.config.js 添加配置 添加在 module.exports

// 引入html插件文件
const HtmlWebPackPlugin = require('html-webpack-plugin');
---------------------------
plugins: [
  new HtmlWebPackPlugin({
    // template是模板文件需要我们创建 
    template: './public/index.html',
    filename: path.resolve(__dirname, 'dist/index.html'),
    minify: {
                //true不换行
                collapseWhitespace: true
            },
    hash: true //生产环境下生成hash戳
  })
]
 npm run dev 

多页面应用,我在网上看的时候,这个东西用的不是很多,但是有些面试题会问,多页面应用说白了就是多个入口,和后端那些框架的设计模式差不多,
webpack.config.js

module.exports = {
// 多入文件
    entry: {
        index: "./src/index.js", // 前台入口 
        admin: "./src/admin.js"  // 后台入口
    },
    output: {
        path: path.resolve(__dirname, 'dist'), // 定义输出目录
        filename: '[name].js',  // 定义输出文件名称  [name]自动获取入口的home和admin, 将entry中的键提取出来
        publicPath: "/"  //build之后的公共路径
    },
     plugins: [
        new HtmlWebPackPlugin({
            // template是模板文件需要我们创建 
            template: './public/index.html',
            filename: path.resolve(__dirname, 'dist/index.html'),
            chunks:['index'],//只引用index.js,解决index.html里面有index.js和admin.js的问题
            minify: {
                //折叠换行true不换行
                collapseWhitespace: true
            },
            hash: true //生产环境下生成hash戳

        }),
        new HtmlWebPackPlugin({
            // template是模板文件需要我们创建 
            template: './public/admin.html',
            filename: path.resolve(__dirname, 'dist/admin.html'),
            chunks:['admin'],//只引用index.js,解决index.html里面有index.js和admin.js的问题
            minify: {
                //折叠换行true不换行
                collapseWhitespace: true
            },
            hash: true //生产环境下生成hash戳
        })
    ]
}

感觉我注释写的很清楚了,简单的说就是添加几个辨别项,将单页面转化为多页面,记得根据入口文件新建文件 entry下现在是两个入口文件

 npm run build

在我们指定的目录下会生成 dist index.js index.html ,admin.js admin.js 但是我看到了警告,

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

ε=(´ο`*)))唉,我给忘了一个mode,虽然不印象打包,但是还是看着挺难受的,在 module.exports 中配置

 //模式 默认两种production(生产环境:代码压缩) development(开发环境:代码不压缩)
    mode: "development",

04loaders 配置css

npm install --save-dev css-loader style-loader mini-css-extract-plugin

css-loader:解析@import这种语法
style-loader:把css插入到head标签中
mini-css-extract-plugin:抽离css样式让index.html里面的css样式变成link引入

配置 webpack.config.js

let MiniCssExtractPlugin = require("mini-css-extract-plugin"); 
// 在插件中引用 
plugins: [
 new MiniCssExtractPlugin({
      filename: "static/css/main.css",
    }),
]
// 在模块中,配置不同的规则 
  module: {
    //规则
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader, //都放到了上面的main.css里面
          {
            loader: "css-loader",
          },
        ],
      },
    ],
  },

然后我们测试下,看看是否将css引用了,在src目录下新建assets目录,新建两个css文件,在index.js中

@import '你放置css的位置'
npm run dev 
/*就会发现,你写的css已经加载到css中了, @import也可以在css中使用,将公共的类,和当前css*/
npm run build
/*就会生成对应配置的目录结构 .  static/css/main.css

css兼容性处理

npm install --save postcss-loader autoprefixer

05配置 css 兼容性 webpack.config.js

// 在css 规则 中添加 一个新的postcss-loader规则 写在css配置的use中 
{ loader: "postcss-loader" },

在根目录下新建 postcss.config.js 配置浏览器建兼容

module.exports = {
    plugins: {
        'autoprefixer': {
          overrideBrowserslist: [
            "Android 4.1",
            "iOS 7.1",
            "Chrome > 31",
            "ff > 31",
            "ie >= 8"
          ]
        }
    }
};

打包后查看我们的dist文件夹下css,自动生成css文件

    -webkit-transform: rotate(60deg);
        -ms-transform: rotate(60deg);
            transform: rotate(60deg);

webpack.config.js 配置会越来越大,位置会越来越多,建议学习下 webpack

06配置 css 压缩

npm install --save optimize-css-assets-webpack-plugin

//引入css压缩
let OptimizeCss = require("optimize-css-assets-webpack-plugin");
// 添加配置 
 optimization: {
    minimizer: [
      new OptimizeCss(), //优化css
    ],
  },

需要配置mode模式
07配置 js 压缩

npm install --save uglifyjs-webpack-plugin
//js压缩
let UglifyjsPlugin=require('uglifyjs-webpack-plugin');
minimizer: [
	    //压缩js  
	    new UglifyjsPlugin({
                cache:true, //是否用缓存
                parallel:true, //是否并发打包
                sourceMap:true //es6映射es5需要用
            }), 
]

08配置图片

npm install --save-dev url-loader

// 配置 
module:{
      rules: [     
         {
                test:/\.(png|jpg|gif|jpeg)$/,
                use:{
                    loader:"url-loader", //file-loader加载图片,url-loader图片小于多少k用base64显示
                    options: {
                        limit:100*1024, //小于100k用base64
                        //build之后的目录分类
                        outputPath:'static/images'                    },
                }
            },
       ]
}

09es6转es5 配置

npm install --save babel-loader @babel/core @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime @babel/runtime

10安装react

npm i react react-dom --save
npm i babel-preset-react --save-dev

安装babe

npm install babel-loader@next @babel/core @babel/preset-react @babel/runtime --save
Babel 是一个 JavaScript 编译器 (说白就是为了解析js代码)

Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。下面列出的是 Babel 能为你做的事情:

  • 语法转换
  • 通过 Polyfill 方式在目标环境中添加缺失的特性 (通过 @babel/polyfill 模块)
  • 源码转换 (codemods)
    更新 webpack.config.js
 module: {
    rules: [
      {
        test: /\.(js | jsx)$/, // 因为react是jsx,需要添加jsx
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },

{
    "presets": ["@babel/preset-env", "@babel/preset-react"]
}

更新 index.js

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

class App extends React.Component{
    render(){
        return(
            <div>Hello React!</div>
    )
    }
}
export default App;

ReactDOM.render(<App />, document.getElementById("app"));

将index.js ReactDOM.render(<App />, document.getElementById("app")); 抛出的节点绑定到文件流中
更新 public/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title> 首页 </title>
</head>
<body>
    <div id="app">
      
    </div>
   
</body>
</html>

代码
参考:
https://react.docschina.org/tutorial/tutorial.html
https://segmentfault.com/a/1190000017945643
https://blog.csdn.net/pippa134679/article/details/85322865

##安 01脚手架装

npm install -g create-react-app
create-react-app my-app
cd my-app 
npm run eject //将webpack配置文件暴露出来。
npm run start

npm run eject 可能会出错,如果遇到错误情况老方法删除

node_modules 文件夹 npm i

02 react基础知识点

01JSX
JSX是一种JavaScript的语法扩展,运用于React架构中,其格式比较像是模版语言,但事实上完全是在JavaScript内部实现的。元素是构成React应用的最小单位,JSX就是用来声明React当中的元素,React使用JSX来描述用户界面。
JSX 是 JavaScript 的一种语法扩展,并拥有 JavaScript 的全部功能。

打开我们app.js清空页面,调整下目录结构(具体看仓库)

import React from 'react';
class App extends  React.Component{
  componentDidMount(){
    console.log(document.getElementById('forData'))
  }
  render(){
    let name = '你好呀';
    let conter ="<span style='color:#FF0000'> 我是html </span>";
    return (
      <div className="App">
        {/* 01JSX语法 */}
        {name}
        {/* 02 输出html标签 */}
        <div dangerouslySetInnerHTML={{__html:conter}}></div>
        {/* 03 ref的使用 */}
        <div id="forData"> 获取数据 </div>
      </div>
    );
  }
}

export default App;

console.log(document.getElementById('forData','获取到真实的dom节点forData'))
获取到id为forData的html的节点,打印此节点,这就是我们最先开始接触的html和js的知识,当我们的html文件被允许在浏览器环境中执行时候,浏览器会将我们的html根据不同的文本标签,和数据流,进行由上至下的解析,为什么会要讲这些?? 在react中,将会对dom操作进行diff算法的操作,将以往的dom树,又添加了一个新虚拟的dom,我是这样理解的,这就是JQ爸爸没有的,也可以说是区别,
ref的使用

<div ref="forData"> 获取数据 </div>
// 方法1
console.log(this.refs['forData'])
// 方法2 
console.log(this.refs.forData)

获取组件的dom节点
创建一个节点,有种感觉,就自己感觉,react的写法,和thinkphp的写法。两者有点相识的感觉,这个继承,这个导出

// 头部子组件
import React from 'react'
class  HeaderComponents extends React.Component{
    render() {
        return(
            <div id="herder">
                页面头部组件 
            </div>
        )
    }
}
export default HeaderComponents

导出就可以导入了,在我们的主组件中导入
由于ref的直接作用空间是当前的组件中,所以我们想获取到子组件中的dom就不能使用ref了,所以要用id,但是原生的js效率会低,

import HeaderComponents from './components/header'

console.log(ReactDOM.findDOMNode(document.getElementById("herder")))

{/* 可以这样写成闭合式*/}
<HeaderComponents></HeaderComponents>
{/* 也可以写成单标签方式*/}
<HeaderComponents/>

组件最后代码

02React 组件生命周期 菜鸟教程

组件的生命周期可分成三个状态:
Mounting:已插入真实 DOM ,Updating:正在被重新渲染 ,Unmounting:已移出真实 DOM
生命周期的方法有:

  • componentWillMount 在渲染前调用,在客户端也在服务端。
  • componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。
  • componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
  • shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
    可以在你确认不需要更新组件时使用。
  • componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
  • componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。
  • componentWillUnmount在组件从 DOM 中移除之前立刻被调用。

03 React循环 && 判断
关闭Eslint 配置,package.json 项目中不介意关闭,学习就是为了理解代码

  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ],
    "rules": {
      "no-undef": "off",
      "no-restricted-globals": "off",
      "no-unused-vars": "off"
    }
  },

React组件中的constructor 概述
显示隐藏

import React from 'react';
import ReactDOM from 'react-dom'
import HeaderComponents from './components/header'
import './assets/css/app.css'

// 
class App extends  React.Component{
 constructor(){
   super();
   this.state = {
     isShow : false // 是否显示
   }
 }
 componentDidMount(){}
 render(){
   return (  
     <div className="App">
       <HeaderComponents></HeaderComponents>
       {/* 三元运算符   this.state.isShow 为true 显示 div 否则 为空 */}
       {this.state.isShow?<div className='judge'></div> :''}
       {/*逻辑运算的应用*/}
       {this.state.isShow && <div className='judge'></div>}
     </div>
   );
 }
}
export default App;

添加按钮显示隐藏

//改变this的指向 
 this.ClickIsShow = this.ClickIsShow.bind(this);
//方法 
ClickIsShow(){
      this.setState({isShow:!this.state.isShow})
  }
{/* 添加按钮 添加点击事件  */}
<button type='button' onClick={this.ClickIsShow}> 显示 \ 隐藏</button>

循环数据

 data: [
        { id: 1, title: "html" },
        { id: 2, title: "css" },
        { id: 3, title: "JS" },
      ],
<div>
     {this.state.data.map((item, index) => {
            return (
              <p key={index}>
                <span>{item.title}</span>
              </p>
            );
      })}
 </div>

全部代码

04 组件 && 传值

 {/* 父组件  */}
import HeaderComponents from "./components/header";

<HeaderComponents data='父组件的值'></HeaderComponents>

 {/* 子组件  */}
import React from 'react'
class  HeaderComponents extends React.Component{
    render() {
        return(
            <div id="herder">
                {this.props.data}
            </div>
        )
    }
}
HeaderComponents.defaultProps = {
    data:'默认值'
}
export default HeaderComponents

根据父组件的传值来判断值是否显示

父组件 
 <HeaderComponents data='父组件的值' isShow={false}></HeaderComponents>
子组件 
 <div id="herder" style={this.props.isShow ? {display:'block'} :{display:'none'}}>

05State的认识
什么是 state?

状态是 React 组件的核心,是数据的来源, React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
创建动态和交互式组件。可以通过 this.state访问它们。

(在构造函数中)调用 super(props) 的目的是什么?

在 super() 被调用之前,子类是不能使用 this 的,在 ES2015 中,子类必须在 constructor 中调用 super()。传递 props 给 super() 的原因则是便于(在子类中)能在 constructor 访问 props

06 事件处理
点击事件处理 ,鼠标事件处理

import React from "react";
import "./assets/css/app.css";

//
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isActive: false,
    };

    // this.ClickIsShow = this.ClickIsShow.bind(this);
  }
  componentDidMount() {}
  claickButton(e) {
    console.log(e);
  }
  claickButtonTwo = () => {
    console.log("点击事件2");
  };
  // 鼠标移入事件 
  MouseOver(){
    this.setState({ isActive: true})
  }
  // 鼠标移除 事件 
  MouseOut(){
    this.setState({ isActive: false})
  }
  render() {
    return (
      <div className="App">
        {/* 点击事件 */}
        <button type="button" onClick={this.claickButton.bind(this, "传参")}>
          1按钮点击
        </button>
        <button type="button" onClick={this.claickButtonTwo}>
          2点击事件
        </button>
        <button
          type="button"
          onClick={() => {
            this.claickButton("传参2");
          }}
        >
          3按钮点击
        </button>
          4鼠标事件 
        <div 
        className={this.state.isActive ? "mouse active" : "mouse"}
        onMouseOver = { this.MouseOver.bind(this) }
        onMouseOut = { this.MouseOut.bind(this) }
        ></div>
      </div>
    );
  }
}

export default App;

模拟双向数据绑定

 this.state = {
      isActive: false,
      number: 1,
    };

  onChangeNumber(e) {
      let number =e.target.value.replace(/[^\d]/g,''); // 使用正则
      this.setState({number:number});
    }
 <div className="inputName">
          <div className="iputdata">
            <span>数量:</span>
            <input
              type="text"
              value={this.state.number}
              onChange={(e) => {
                this.onChangeNumber(e);
              }}
            />
          </div>
          <div className="iputFoer">
            <span> 数量:{this.state.number}</span>
            <span> </span>
          </div>
        </div>