React 19+Vite+TS学习基础-1

核心思想

UI=f(state)

用户界面UI 仅仅是应用走程序状态的一个函数(f)

第一代: 事件驱动

找到dom元素,去给元素绑定事件

js jquery

第二代 MVC MVVM

不在框架层去关注dom,而是关注数据

AnglarJs 早期vue

第三代 声明式编程

react心智模型

通过状态驱动视图更新

vite React ts

替代了webpack,颠覆性的前段构建工具,极大提升了前端开发体验,利用现在浏览器原生支持ES Module的特性,在开发阶段无需对所有代码进行打包,从而实现了几乎瞬时的服务器启动和快如

闪电的热模块更新(HMR),

安装

复制代码
npm create vite@latest

接下来选择React作为开发框架,TS作为变体

下面默认

快捷创建

复制代码
pnpm create vite name --template=react-ts

安装pnpm

复制代码
npm install -g pnpm

依赖初始化

进入项目目录

复制代码
pnpm i

启动

复制代码
npm run dev   或者
pnpm dev

JSX语法

项目结构

vite.config.ts

最重要,用来构建react相关项目内容

tsconfig.json

tsconfig.app.json

配置前端用层,比如jsx

tsconfig.node.json

配置打包构建用node命令

package.json

依赖

eslint.config.ts

当前项目 规范化

src

jsx

在js中写html代码,是js的一种特殊语法,会被转化为js函数调用

React.createElement()

比如代码

复制代码
<h1 className="title">Hello,React<h1>

会被转换成

复制代码
React.createElement('h1',{className:'title'},'Hello,React');

举例

上面的main.tsx是源码,下面的倾斜的main.tsx是编译后的

复制代码
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

编译后的,可以在里面看StrictMode,APP这个单词

会发现他们在jsxDEV这个函数中

复制代码
import __vite__cjsImport0_react_jsxDevRuntime from "/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=423528cb";
const jsxDEV = __vite__cjsImport0_react_jsxDevRuntime["jsxDEV"];
import __vite__cjsImport1_react from "/node_modules/.vite/deps/react.js?v=423528cb";
const StrictMode = __vite__cjsImport1_react["StrictMode"];
import __vite__cjsImport2_reactDom_client from "/node_modules/.vite/deps/react-dom_client.js?v=1fc37301";
const createRoot = __vite__cjsImport2_reactDom_client["createRoot"];
import "/src/index.css";
import App from "/src/App.tsx";
createRoot(document.getElementById("root")).render(/* @__PURE__ */
jsxDEV(StrictMode, {
    children: /* @__PURE__ */
    jsxDEV(App, {}, void 0, false, {
        fileName: "D:/learn/react1/basicreact1/src/main.tsx",
        lineNumber: 8,
        columnNumber: 5
    }, this)
}, void 0, false, {
    fileName: "D:/learn/react1/basicreact1/src/main.tsx",
    lineNumber: 7,
    columnNumber: 3
}, this));

//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBT0k7QUFQSixTQUFTQSxrQkFBa0I7QUFDM0IsU0FBU0Msa0JBQWtCO0FBQzNCLE9BQU87QUFDUCxPQUFPQyxTQUFTO0FBRWhCRCxXQUFXRSxTQUFTQyxlQUFlLE1BQU0sQ0FBRSxFQUFFQztBQUFBQSxFQUMzQyx1QkFBQyxjQUNDLGlDQUFDLFNBQUQ7QUFBQTtBQUFBO0FBQUE7QUFBQSxTQUFJLEtBRE47QUFBQTtBQUFBO0FBQUE7QUFBQSxTQUVBO0FBQ0YiLCJuYW1lcyI6WyJTdHJpY3RNb2RlIiwiY3JlYXRlUm9vdCIsIkFwcCIsImRvY3VtZW50IiwiZ2V0RWxlbWVudEJ5SWQiLCJyZW5kZXIiXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOlsibWFpbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgU3RyaWN0TW9kZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gJ3JlYWN0LWRvbS9jbGllbnQnXG5pbXBvcnQgJy4vaW5kZXguY3NzJ1xuaW1wb3J0IEFwcCBmcm9tICcuL0FwcC50c3gnXG5cbmNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Jvb3QnKSEpLnJlbmRlcihcbiAgPFN0cmljdE1vZGU+XG4gICAgPEFwcCAvPlxuICA8L1N0cmljdE1vZGU+LFxuKVxuIl0sImZpbGUiOiJEOi9sZWFybi9yZWFjdDEvYmFzaWNyZWFjdDEvc3JjL21haW4udHN4In0=

正式环境构建

复制代码
pnpm build
pnpm preview

查看

在js中正式环境使用的是jsx

jsx本质

本质是js,可以用{}在jsx中嵌入任何有效的js表达式,无论是变量,数学运算,还是函数调用

在JSX描述页面结构时,有几条核心规则:

一个组件返回的JSX必须拥有一个单一的根元素,如果想返回多个,外面需要用div包裹

也推荐<Fragment>不会生成实际元素

因为最终会生成js,避免关键字冲突,class要写成className, for要写成htmlFor

事件遵循驼峰命名onClick

函数式组件与Class组件

以后都用函数式,

最简App.tsx
复制代码
import './App.css'

function App() {

  return (
    <div>
      Hello, World!
    </div>
  )
}

export default App

下面是Class组件

复制代码
import React from 'react'
import './App.css'

// function App() {

//   return (
//     <div>
//       Hello, World!
//     </div>
//   )
// }


class App extends React.Component {
  render() {
    return (
      <div>
        Hello, World!
      </div>
    )
  }
}

export default App
点击增加的实现
复制代码
import React, { useState } from 'react'
import './App.css'

function App() {
// const count=0;
// 把count变成响应式的
const [count,setCount]=useState(0);

  return  <div>{count} </div>;

}

export default App

点击增加

复制代码
import React, { useState } from 'react'
import './App.css'

function App() {
// const count=0;
// 把count变成响应式的
const [count,setCount]=useState(0);

  return  <div onClick={()=>setCount(count+1)}>{count} </div>;

}

export default App

改为Class

复制代码
import React, { useState } from 'react'
import './App.css'

// function App() {
// // const count=0;
// // 把count变成响应式的
// const [count,setCount]=useState(0);

//   return  <div onClick={()=>setCount(count+1)}>{count} </div>;

// }

class App extends React.Component {
  state = {
    count: 0
  };
  render() {
    return <div onClick={() => this.setState({ count: this.state.count + 1 })}>{this.state.count} </div>;
  }
}
export default App

组件化开发核心

Props:外部给组件的传值

State:内部状态

Props

properties的缩写

相当于js中函数的参数

制作一个组件,无属性
复制代码
// 具名导出,就是使用的时候也要用这个名字
export const HelloWorld = () => {
  return <div>Hello, World!</div>;
}

// 默认导出

// const HelloWorld = () => {
//   return <div>Hello, World!</div>;
// }
// export default HelloWorld;

最简单的返回一个组件,在App.tsx中

复制代码
import React, { useState } from 'react'
import './App.css'
import { HelloWorld } from './components/1.hello'

// 函数式组件返回值就是实体内容
function App() {
  return <HelloWorld />
}

export default App
题目2 字符串属性

传给组件一个字符串属性

复制代码
import React, { useState } from 'react'
import './App.css'
import { HelloWorld } from './components/1.hello'

// 函数式组件返回值就是实体内容
function App() {
  return <HelloWorld title='hihihi' />
}

export default App

组件接受一个字符串形参

复制代码
//定义一个参数
interface HelloWorldProps {
  title: string;


}

// 具名导出,就是使用的时候也要用这个名字
//注意形参中引用了上面的定义
export const HelloWorld = (props:HelloWorldProps) => {
    //解构属性
    const {title} = props;
  return <div>Hello, World! {title}</div>;
}

// 默认导出

// const HelloWorld = () => {
//   return <div>Hello, World!</div>;
// }
// export default HelloWorld;
题目三 函数当属性 render props

相当于子组件提供了一个类似vue的插槽,本身不确定什么内容,由父组件定义

Render函数

父组件
复制代码
import React, { useState } from 'react'
import './App.css'
import { HelloWorld } from './components/1.hello'

// 函数式组件返回值就是实体内容
function App() {
  return <HelloWorld title="hihihi" render={()=><div>你好</div>}/>
}

export default App

也可以增加颜色

复制代码
import React, { useState } from 'react'
import './App.css'
import { HelloWorld } from './components/1.hello'

// 函数式组件返回值就是实体内容
function App() {
  return <HelloWorld title="hihihi" render={()=><div style={{color: "red"}}>你好</div>}/>
}

export default App

函数

在定义中加了一个render?

返回 render?.()

复制代码
import type React from "react";

//定义一个参数
interface HelloWorldProps {
  title: string;
//   声明函数参数
    render?:() => React.ReactNode;  //返回的类型是React节点


}

// 具名导出,就是使用的时候也要用这个名字
//注意形参中引用了上面的定义
export const HelloWorld = (props:HelloWorldProps) => {
    //解构属性
    const {title, render} = props;
  return <div>Hello, World! {title} {render?.()}</div>;
}

// 默认导出

// const HelloWorld = () => {
//   return <div>Hello, World!</div>;
// }
// export default HelloWorld;
父组件多次调用子组件

return后加(<> </>)包起来

复制代码
import React, { useState } from 'react'
import './App.css'
import { HelloWorld } from './components/1.hello'

// 函数式组件返回值就是实体内容
function App() {
  return(
  <>
  
    <HelloWorld title="hihihi" render={()=><div style={{color: "red"}}>你好</div>}/>
   <HelloWorld title="ok" render={()=><div style={{color: "red"}}>LOVE</div>}/>
  
  
  </>
  



  )
  


}

export default App

状态提升

上文中每个组件的count参数是独立的,如果需要共用就定义到

事件处理函数独立

复制代码
import type React from "react";
import { useState } from "react";

//定义一个参数
interface HelloWorldProps {
  title: string;
//   声明函数参数
    // render?:() => React.ReactNode;  //返回的类型是React节点
    //可以接受参数,这样父组件可以使用子组件的参数
    render?:(count: number) => React.ReactNode; 


}

// 具名导出,就是使用的时候也要用这个名字
//注意形参中引用了上面的定义
export const HelloWorld = (props:HelloWorldProps) => {
    //解构属性
    const {title, render} = props;
    const[count, setCount] = useState(0);
    //  事件处理函数
    const handleAdd=() => {
        setCount(count + 1);
    };





  return (<div>Hello, World! {title} ---{count}
  <button onClick={handleAdd}>+</button>
       {render?.(count)}
       </div>
       
  );
}

// 默认导出

// const HelloWorld = () => {
//   return <div>Hello, World!</div>;
// }
// export default HelloWorld;

回调父组件

当子组件值变化时,通过onChange回调父组件

复制代码
import type React from "react";
import { useState } from "react";

//定义一个参数
interface HelloWorldProps {
  title: string;
//   声明函数参数
    // render?:() => React.ReactNode;  //返回的类型是React节点
    //可以接受参数,这样父组件可以使用子组件的参数
    render?:(count: number) => React.ReactNode; 
    //当数据变化时,为了给父级传递一个回调函数
    onChange?: (count: number) => void;


}

// 具名导出,就是使用的时候也要用这个名字
//注意形参中引用了上面的定义
export const HelloWorld = (props:HelloWorldProps) => {
    //解构属性
    const {title, render, onChange} = props;
    const[count, setCount] = useState(0);
    //  事件处理函数
    const handleAdd=() => {
        setCount(count + 1);
        onChange?.(count + 1);
    };





  return (<div>Hello, World! {title} ---{count}
  <button onClick={handleAdd}>+</button>
       {render?.(count)}
       </div>
       
  );
}

// 默认导出

// const HelloWorld = () => {
//   return <div>Hello, World!</div>;
// }
// export default HelloWorld;

父组件

在控制台输出count

复制代码
import React, { useState } from 'react'
import './App.css'
import { HelloWorld } from './components/1.hello'

// 函数式组件返回值就是实体内容
function App() {
  return(
  <>
  {/* 如下count其实是子组件内部的参数,但是父组件也可以使用了 */}
    <HelloWorld 
    title="hihihi" 
    render={(count)=><div style={{color: "red"}}>你好{count}</div>}
    // 当数据变化时,父组件应该做什么
    onChange={(count)=>console.log(count)}
    
    
    
    />
   <HelloWorld title="ok" render={(count)=><div style={{color: "red"}}>LOVE{count}</div>}/>
  
  
  </>
  



  )
  


}

export default App

状态管理

useState Hook

Props外部传入不可变,State则是组件内部自己管理的数据

useState接收一个参数,比如下面的0作为初始值,然后返回一个包含两个元素的数组,

通常通过js的数组解构来接收这两个值:属性(数据,状态变量)和方法(如何修改数据,更新函数)

复制代码
import {useState} from 'react';
const  Couter=()=>{
const[count,setCount]=useState(0);
}

当我们调用更新函数:

1)会计划一次对状态(上例中的状态)的更新,将新的状态值保存起来

2)触发该组件的一次重新渲染(re-rendor)

状态更新-->触发重新渲染-->使用新状态渲染UI 这个循环构成了React交互的核心

当页面点击时触发了handleAdd,进入触发setCount(),同时通过onChange把count暴露出去

复制代码
    const handleAdd=() => {
        setCount(count + 1);  //异步
        onChange?.(count + 1); //因为上一步是异步,所以这里依然用count+1,同步则可直接用count了
    };
对象
复制代码
import { useState } from "react";

export const BasicState = () => {
    //解构属性

    const[info, setInfo] = useState(
      {
      age:0
    }
  )
    //  事件处理函数
    const handleAdd=() => {
      // 这是错误的,不能直接修改
      // info.age++
      // setInfo(info);
      // 正确的做法。其实是创建了一个新的对象给setInfo,以下是对象的写法
      setInfo({
        ...info,
        age:info.age+1   //age是个新的
      })

      //函数写法,这样就拿到了age前值
      setInfo((prevInfo)=>({
        ...prevInfo,
        age:prevInfo.age+1
      }))
  

    };





  return (<div>{info.age}
  <button onClick={handleAdd}>+</button>

       </div>
       
  );
}

父组件

复制代码
import React, { useState } from 'react'
import './App.css'
import { HelloWorld } from './components/1.hello'
import { BasicState } from './components/2.basicSytate'

// 函数式组件返回值就是实体内容
function App() {
  return(
  <>
  {/* 如下count其实是子组件内部的参数,但是父组件也可以使用了
    <HelloWorld 
    title="hihihi" 
    render={(count)=><div style={{color: "red"}}>你好{count}</div>}
    // 当数据变化时,父组件应该做什么
    onChange={(count)=>console.log(count)}
    
    
    
    />
   <HelloWorld title="ok" render={(count)=><div style={{color: "red"}}>LOVE{count}</div>}/> */}

  <BasicState />
  
  </>
  



  )
  


}

export default App

列表渲染

复制代码
import { useState } from "react";

export const List=() => {
  
    //用数组的时候都用泛型定义下
    const [list, setList] = useState<number[]>([]);




    return (

        <div>
            {
                list.map((item) => (
                    <div key={item}>{item}</div>
                ))
            }

            <button 
            onClick={() => {
                //push比较传统
               // list.push(list.length)
                // setList([...list])
                //也可以用如下方法
                setList([...list, list.length])
            }
           }>追加元素</button>
        </div>
    )
}

父组件

复制代码
import './App.css'
import { List } from './components/3.List'

// 函数式组件返回值就是实体内容
function App() {
  return(
  <>
  <List/>
  
  </>
  



  )
  


}

export default App

条件渲染

可以使用三目运算,比如下面的偶数才显示

复制代码
import { useState } from "react";

export const List=() => {
  
    //用数组的时候都用泛型定义下
    const [list, setList] = useState<number[]>([]);
    //判断是不是奇数行odd是英文单词,作形容词时译为"奇怪的;奇数的;零散的;剩余的"
    const isOdd = (n:number) => n % 2 === 1;




    return (

        <div>
            {
                // list.map((item) => (
                //     <div key={item}>{item}</div>
                // ))
                // 偶数才渲染
                list.map((item)=>item%2===0?(
                    <div key={item}>{item}</div>
                ):null)
            }

            <button 
            onClick={() => {
                //push比较传统
               // list.push(list.length)
                // setList([...list])
                //也可以用如下方法
                setList([...list, list.length])
            }
           }>追加元素</button>
        </div>
    )
}

列表的key

key是React识别列表中各个元素的身份证,当列表数据发生变化时(增删改排序),React会协调算法(Reconciliation /rekənsɪliˈeɪʃn/ 和解;协调;调解;和谐一致 )会通过key来高效对比新旧虚拟DOM树,根据key来判断哪些是新创建的,删除的,移动位置的

一个稳定且唯一的key 能帮助React最大已有DOM元素和组件实例,从而极大提升性能

不提供key控制台会警告,列表更新时可能有UI BUG,性能问题

不要使用数组索引作为Key,这是反模式,因为比如插入一个元素,所有后续元素索引都会改变,会让React误以为元素自身内容发生了大规模变化,引发不必要的重新渲染,甚至丢失组件内部的状态

理想的key值是数据项本身就带有的,比如数据库中的id

Hooks与生命周期

hooks定义时尽量用泛型进行约束,以免填错值

useEffect

监控某个值,创建副作用

复制代码
import { useEffect, useState } from "react";
export const  Hooks=()=>{
    const [count,setCount] = useState(0);
    const handleAdd=()=>{
        // setCount(count+1)
        //使用就近取值
        setCount((prevCount)=>prevCount+1)
    }
// 创建副作用  [count] 数据变化后处理这个副作用
  useEffect(
    () =>{
        console.log(count),[count]
    }
  )


    return(
        <div>
            <div>当前计数{count}</div>
            <button onClick={handleAdd}>增加</button>



        </div>
    )
}

useEffect的三种写法

复制代码
import { useEffect, useState } from "react";
export const  Hooks=()=>{
    const [count,setCount] = useState(0);
    const handleAdd=()=>{
        // setCount(count+1)
        //使用就近取值
        setCount((prevCount)=>prevCount+1)
    }
// 第一种创建副作用  指定[count] 数据变化后处理这个副作用
  useEffect(
    () =>{
        console.log(count),[count]
        // 让标题变化
        document.title = `当前计数${count}`;
    }
  )
  // 第二种创建副作用  空数组表示只在组件挂载和卸载时执行---挂载就相当于订阅,组件卸载时取消订阅
  useEffect(() =>{
        console.log("空数组,订阅,组件挂载完成"); //类似于类组件的onComponentDidMount
        //函数式编程中,取消订阅就是订阅的return一个函数
    return ()=>{
        console.log("取消订阅,组件卸载时执行"); //类似于类组件的onComponentWillUnmount
    }},[]
  )
// 第三种创建副作用  不传参表示每次组件更新都执行

useEffect(
    () =>{
        console.log("无参数,每次组件更新都执行"); //类似于类组件的onComponentDidUpdate
    }
  )

  //订阅和取消订阅举例
// 在函数式编程,订阅和取消订阅就是标准化结构
// const unsubscribe =()=>{ 


//     //取消订阅就是return 一个函数
// return ()=>{    console.log("取消订阅,组件卸载时执行"); 
// }

//  }







    return(
        <div>
            <div>当前计数{count}</div>
            <button onClick={handleAdd}>增加</button>



        </div>
    )
}

父组件

复制代码
import { useState } from 'react'
import './App.css'
import { Hooks } from './components/4.Hooks'

// 函数式组件返回值就是实体内容
function App() {
//为了隐藏组件,也是组件取消订阅
  const [isShow,setIsShow]=useState(true)
  const handleClick=()=>{
    setIsShow(!isShow)
  }


// 以下是三目运算符简写 {isShow?<Hooks/>:null} 
  return(
  <>
  {isShow && <Hooks/>} 
  
<button onClick={handleClick}>{isShow?'隐藏Hooks组件':'显示Hooks组件'}</button>
  </>
  



  )
  


}

export default App

useRef

获取DOM或者缓存某一个数据(不是状态值,不会引起试图更新)

ref很常用,DIV等都有,只是没有使用

获取DOM元素

获取后,可以进行应用元素的方法,比如focus()

复制代码
import { useEffect, useRef, useState } from "react";

export const UseRef=()=>{

 const [count,setCount] = useState(0);
//  const inputRef=useRef(null);   //直接使用null 下面的聚焦focus会报错,因为null没有focus方法,但也会生效
// 如果想避免报错,可以给useRef传入一个泛型参数,告诉TS当前ref对象的current属性是什么类型
const inputRef=useRef<HTMLInputElement>(null);



    const handleAdd=()=>{
        // setCount(count+1)
        //使用就近取值
        setCount((prevCount)=>prevCount+1)
    }

    // 获取输入框输入的内容
useEffect(()=>{
    // 获取输入这个dom元素
//    console.log(inputRef.current);
     // 获取输入这个dom元素并聚焦
   console.log(inputRef.current?.focus()); 

    })

    return(
        <div>  
           <div>当前计数{count}</div>
            <button onClick={handleAdd}> 增加</button>

            <input ref={inputRef}/>
        </div>
    )
}

存储值

不会引起数据更新

复制代码
import { use, useEffect, useRef, useState } from "react"

export const USEREF2=()=>{
    const [count,setCount]=useState<number>(0);
    // 用于缓存的值,不会引起数据更新,增加泛型更严谨,不会被赋予其他类型的值
    const  isMounted=useRef<boolean>(false);  //初始的时候肯定是没挂载


 useEffect(()=>{    
        console.log('组件更新了');
        console.log('isMounted',isMounted.current);
    }
    ,[count])






    useEffect(()=>{
      console.log('组件挂载完成');
    //   标志组件已经挂载了
      isMounted.current=true;

    },[]
    )


    return (
       
        <div>
            <div>当前计数{count}</div>
            <button onClick={()=>setCount(count+1)}>增加</button>
        </div>


    )
}
相关推荐
小白郭莫搞科技几秒前
鸿蒙跨端框架Flutter学习:CustomTween自定义Tween详解
学习·flutter·harmonyos
Halo_tjn1 小时前
基于封装的专项 知识点
java·前端·python·算法
阳光九叶草LXGZXJ1 小时前
达梦数据库-学习-47-DmDrs控制台命令(LSN、启停、装载)
linux·运维·数据库·sql·学习
摘星编程1 小时前
OpenHarmony环境下React Native:自定义useTruncate文本截断
javascript·react native·react.js
A9better2 小时前
嵌入式开发学习日志53——互斥量
stm32·嵌入式硬件·学习
进阶小白猿3 小时前
Java技术八股学习Day30
java·开发语言·学习
近津薪荼3 小时前
优选算法——双指针6(单调性)
c++·学习·算法
m0_748229993 小时前
Vue2 vs Vue3:核心差异全解析
前端·javascript·vue.js
2601_949593654 小时前
高级进阶React Native 鸿蒙跨平台开发:LinearGradient 背景渐变与主题切换
react native·react.js·harmonyos
C澒4 小时前
前端监控系统的最佳实践
前端·安全·运维开发