React基础

React

1.使用脚手架创建项目 npx create-react-app my-app

2.cd my-app

3.npm start

JSX表达式使用

使用{}识别变量

const name = 'ashin'
const getAge =  ()=>{
  return 22
}
const flag = true

function App() {
  return (
    <div className="App">
      {name}
      {getAge()}
      {flag?'很厉害':'很菜'}
    </div>
  );
}

使用JSX实现列表渲染

技术方案 map-------------------vue里面使用v-for

const songs = [
  {id:1,name:'ashin',age:22,},
  {id:2,name:'john',age:23,},
  {id:3,name:'Tom',age:24,}
]
<ul>
       {songs.map(song=><li key={song.id}>{song.name}</li>)}
      </ul>

JSX条件渲染

技术方案:三元表达式或者逻辑&&运算----- vue里面使用v-if或v-show

const flag = true
 { flag ? <div><span>三元表达式实现条件渲染</span></div>:null }
{ flag && <div><span>逻辑与实现条件渲染 </span></div> }

如果条件渲染逻辑比较复杂使用函数来写分支逻辑,模板只负责调用

const getHdom = (type)=>{
  if(type===1) return <h1>this is h1</h1>
  if(type===2) return <h2>this is h2</h2>
  if(type ===3 ) return <h3>this is h3</h3>
}

function App() {
  return (
  <div className="App">
   { getHdom(2) }
    </div>
    
  );
}

JSX样式控制

1.行内样式—在元素身上绑定一个style属性即可
2.类名样式—在元素身上绑定className属性

// 1.直接写在行内
<span style={{color:'red',fontSize:'30px'}}>行内样式控制</span>

//2.把样式抽离出来
const sty = {
    color:'red',
    fontSize:'30px'
}
<span style={sty}>行内样式控制</span>

// 3.类名样式
<span className='active'>类名样式</span>

动态类名控制

const activeFlag = true
<span className={activeFlag?'active':''}>动态控制类名样式</span>

JSX注意事项

1. JSX必须只有一个根节点 或者使用幽灵节点<>包裹
2.所以标签必须形成闭合,成对闭合或者自闭合都可以
3.JSX中的语法更加贴近JS语法,属性名采用驼峰命名法 class–>className 、for–>htmlFor
4.JSX支持多行(换行),如果需要换行,需使用()包裹,防止bug出现

函数组件的创建与渲染

约定说明:
1.组件的名称必须首字母大写,react内部会根据这个来判断是组件还是普通的HTML标签
2.函数组件必须有返回值,表示该组件的UI结构;如果不需要渲染任何内容,则返回null
3.组件就像HTML标签一样可以被渲染到页面中,组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值就是对应的内容
4.使用函数名称作为组件标签名称,可以成对出现也可以自闭合

function Hello(){
    return <div>Hello</div>
}

function App(){
    return (
        <div>
            <Hello/>
        </div>
    )
}

类组件的创建和渲染

class HelloComponent extends React.component{
    render(){
        return <div>this is class component</div>
    }
}

function App(){
    return (
        <div>
            <HelloComponent/>
        </div>
    )
}
函数组件与类组件的区别:

1.函数组件:没有状态,也叫无状态组件;负责静态结构的展示
2.类组件:有状态,也叫有状态组件;提供状态,提供交互
3.状态也叫数据,是私有的。
4.状态也就像vue2里面的data,vue3里面的ref、reactive

事件绑定

语法: on + 事件名称 = {事件处理程序}

// 1.函数组件
function HelloFn(){
    const clickHandle = ()=>{
        console.log('被点击了!')
    }
    return <div onClick={clickHandle}>点击我</div>
}

// 2.类组件
class HelloComponent extends React.component{
    const clickHandle = ()=>{
        console.log('被点击了!')
    }
    render(){
        return <div onClick={this.clickHandle}>this is class component</div>
    }
}

事件对象e

function HelloFn(){
    const clickHandle = (e)=>{
    console.log(e)
        console.log('被点击了!')
        e.preventDefault() // 阻止默认行为
    }
    return <div><a onClick={clickHandle} href="http://www.baidu.com">百度</a></div>
}

如何传递自定义参数

1.只需要一个额外参数:{}里改变为箭头函数即可

{()=>clickHandle(‘自定义参数’)}
2.既需要事件对象e又需要额外参数
{(e)=>clickHandle(e,‘自定义参数’)}

组件状态的定义使用

import React from 'react'
class TestComponent extends React.component{
    // 1.定义组件状态
    state = {
        // 在这里可以定义各种属性,全部都是当前组件的状态
        name:'ashin'
    }
    changeName = ()=>{
    // 2.修改state中的属性
        this.setState({
            name:'阿信'
        })
    }
    render(){
        return (
            <div>
            // 3.使用状态
                当前的name为:{this.state.name}
                <button onClick={this.changeName}>修改</button>
            </div>
            
        )
    }
}

小结

0.state相当于vue中的data,也就是用于存放数据的,但是在使用上和data很不一样
1.react编写组件其实就是写原生js类或者函数
2.定义状态必须通过state实例属性的方法提供一个对象,名称是固定的就叫做state
3.修改state中的任何属性都不可以直接赋值,必须使用setState方法。这个方法来自继承得到
4.这里的this关键词,很容易出现指向错误的问题,需要规范书写

组件this指向问题

第一种写法 箭头函数写法 推荐写法

class TestComponent extends React.Component {
 
 state = {
    name: 'ashin'
  }
  
 changeName = () => {
    this.setState({
     name: '阿信'
     })
   }
   
   render () {
    return (
      <div>
        当前的name为{this.state.name}
        <button onClick={this.changeName}>修改</button>
      </div>
    )
  }
  }

第二种写法 使用bind改变this指向-- 了解即可

class TestComponent extends React.Component {
 constructor(){
     super()
     this.changeName = this.changeName.bind(this)
 }
 
 state = {
    name: 'ashin'
  }
  
 changeName() {
    this.setState({
     name: '阿信'
     })
   }
   
   render () {
    return (
      <div>
        当前的name为{this.state.name}
        <button onClick={this.changeName}>修改</button>
      </div>
    )
  }
  }

第三种写法 在模板调用函数的时候使用箭头函数

class TestComponent extends React.Component {
 
 state = {
    name: 'ashin'
  }
  
 changeName () {
    this.setState({
     name: '阿信'
     })
   }
   
   render () {
    return (
      <div>
        当前的name为{this.state.name}
        <button onClick={()=>this.changeName()}>修改</button>
      </div>
    )
  }
  }

受控表单组件-----类似于vue中的v-model

步骤:
1.声明用来控制input value的react组件的状态
2.给value绑定react state
3.给input绑定change事件,为了拿到当前输入的数据
4.写回调函数修改state中绑定的value的值

class InputHand extends React.Component {
// 1.声明用来控制input value的react组件的状态
  state = {
    message: 'ashin'
  }
  
  // 4.写回调函数修改state中绑定的value的值
  inputChange = (e) => {
    console.log('触发了', e, this)
    this.setState({
      message: e.target.value
    })
  }
  
  render () {
    return (
    // 2. 给value绑定react state
    // 3.给input绑定change事件,为了拿到当前输入的数据
      <div>
        <input type="text" value={this.state.message} onChange={this.inputChange} />
      </div>
    )
  }
}

非受控组件 — 相当于vue中ref获取dom元素,然后拿到原生dom元素里面的value值

1.导入createRef函数
2.调用createRef函数,创建一个ref对象,存储名为msgRef的实例属性中
3.为input添加ref属性,值为msgRef
4.在按钮的事件处理函数中,通过msgRef.current即可拿到input对应的dom元素,而其中msgRef.current.value拿到的就是文本框的值

import React,{createRef} from 'react'
class Input extends React.Component{
    msgref = create()
    getValue = () => {
    console.log(this.msgref.current.value)
  }
  render () {
    return (
      <div>
        <input type="text" ref={this.msgref} />
        <button onClick={this.getValue}>获取</button>
      </div>
    )
  }
    
}

React组件通信

父传子:props

// 函数式的Son
function SonF (props) {
// peops是一个对象,里面存着通过父组件传递过来的所以数据
  return (
    <div>this is Sonc: {props.msg}</div>
  )
}

// 类组件的Son
class SonC extends React.Component {
// 类组件必须通过this关键词获取这里的props是固定的
  render () {
    return (
      <div>this is SonC:{this.props.msg}</div>
    )
  }
}

class Father extends React.Component {
  state = {
    message: 'data'
  }
  render () {
    return (
      <div>
        <SonC msg={this.state.message} />
        <SonF msg={this.state.message} />
      </div>
    )
  }
}

子传父: 子组件调用父组件传递过来的参数,并且把想要传递的数据当成函数的实参传入即可

function Son(props){
const { getMsg } = props
    return (
        <div>
            <span>this is Son:</span>
            <button onClick={()=>getMsg('子组件传递的数据')}>click</button>
        
        </div>
    )
}

class Father extends React.Component{
    getMessage = (sonMsg)=>{
        console.log(sonMsg)
    }
    <Son getMsg={getMessage} />
}

兄弟组件的通信 先传给父组件,在由父组件传给兄弟组件

// SonA 传递数据给 SonB
function SonA (props) {
  return (
    <>
      <div>this is SonA:</div>
      <button onClick={() => props.getAMsg('A发送过去的数据')}>发送数据</button>
    </>
  )
}

function SonB (props) {
  return (
    <div>this is SonB:{props.sendBMsg}</div>
  )
}

class App extends React.Component{
state = {
    msg: '默认数据'
  }
  getA = (data) => {
    this.setState({
      msg: data
    })
  }
    <SonA getAMsg={this.getA}></SonA>
    <SonB sendMsg={this.state.msg}></SonB>
}

Context跨组件传递数据

例如: App --> A–> C
App数据 --> C
注意事项:
1.上层组件和下层组件是相对的,只要存在就可以使用,通常我们会通过App作为数据提供方
2.这里涉及到的语法都是固定的,有两处,第一处是提供方:必须是value={传递的数据};第二处是接收方:必须是{value=>使用value做什么都可以}

// 1.导入createContext方法并执行,结构是提供者Provider和消费者Comsumer分别包裹,与vue中的provider和inject类似
const {Provider,Consumer} = createContext()

function C(){
    return (
    <>
    <div>this is C</div>
    <Consumer>
        {value=><span>{value}</span>}
    </Consumer>
    </>
    )
}

function A(){
    return (
        <div>
        this is A
        <C />
        </div>
    )
}

class App extends React.Component{
state = {
    msg:'this is data'
}
    render(){
        return (
        <Provider value={this.state.msg}>
        <div>
            <A />
        </div>
        </Provider>
        )
    }
}

特殊的children属性

在子组件中写的东西都被存储在children中,可以写普通文本、普通标签元素、函数、JSX

function ListItem({children}){
    return (
        <div>ListItem,{children}</div>
    )
}

class App extends React.Component{
    render(){
        return (
            <div>
                <ListItem>
                 this is children
                 
                </ListItem>
            
            </div>
        )
    }
}

props类型校验

1.函数组件
function List({pageSize = 10}) {
  return (
    <div>
      此处展示props的默认值:{ pageSize }
    </div>
  )
}

// 不传入pageSize属性
<List />
2.类组件
class List extends Component {
  static defaultProps = {
    pageSize: 10
  }

  render() {
    return (
      <div>
        此处展示props的默认值:{this.props.pageSize}
      </div>
    )
  }
}

<List />

组件生命周期

只有类组件才有生命周期,函数组件是没有的(类组件实例化,函数组件不需要实例化)

挂载阶段:

  1. constructor:创建组件时最先执行,初始化的时候只执行一次,作用:一般用来初始化state(现在一般不这么用了),创建Ref,使用bind解决this问题
    2.render:每次组件渲染时都会触发,作用:渲染UI(不能在里面调用setState())
    3.componentDidMount:组件挂载(完成DOM渲染)后执行,初始化时只执行一次。作用:一般用来发送网络请求,DOM操作;类似于vue中的mounted
    更新阶段:
    1.render:与挂载阶段是同一个render
    2.componentDidUpdate:组件更新后(DOM渲染完毕),作用:DOM操作,可以获取到更新后的DOM内容,不要直接调用setState

卸载阶段:
componentWillUnmount:组件卸载(从页面中消失),作用:执行清理工作(比如:清理定时器)

Hook

React的设计理念:UI = f(data)
1.什么是hook?
Hook的本质:一套能够使函数组件更加强大,更灵活的钩子
2.Hook解决了什么问题?
1.解决了状态逻辑复用。2.class组件自身的问题
3.Hook的优势:
1.告别难以理解的class 2.解决业务逻辑难以拆分的问题 3.使状态逻辑复用变的简单可行 4.函数组件在设计思想上,更加契合React的设计理念

useState

注意事项:1.只能出现在函数组件中;2.不能嵌套在if/for/其他函数中(react按照hooks的调用顺序识别每一个hook)

1.导入useState函数
2.执行这个函数并且传入初始值,必须在函数组件中
3.[数据,修改数据的方法]
4.使用数据 修改数据

import {useState} from 'react'
function App(){
    const [count.setCount] = useState(0)  //解构赋值写法,名字可以自定义,setCount函数作用用来修改count 不能修改原值而是生成新值替换
    return (
        <div>
            <button onClick={()=>setCount(count++)}>{count}</button>
        </div>
    )
}

useEffect

1.理解函数的副作用

副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于React组件来说,主作用就是根据数据渲染UI。除此之外都是副作用(比如:数据请求ajax,手动修改dom,localstorage操作);useEffect函数就是为函数组件提供副作用处理的
使用:
1.导入useEffect函数
2.在函数组件中执行 传入回调 并且定义副作用
3.当我们通过修改状态更新组件时,副作用也会不断执行

import {useState,useEffect} from 'react'
function App(){
    const [count,setCount] = useState(0)
    useEffect(()=>{
        // 定义副作用
        document.title = count
        
    })
    return (
        <div>
            <button onClick={()=>setCount(count+1)>{count}</button>
        </div>
    )
}

useEffect添加依赖项控制执行时机

1.不添加依赖项: 组件首次渲染执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行

useEffect(()=>{
    console.log('副作用执行了')
})

2.添加空数组: 组件只在首次渲染时执行一次

useEffect(()=>{
     console.log('副作用执行了')
},[])

3.添加特定依赖项: 首次渲染时执行,在依赖项发生变化时重新执行

function App() {  
    const [count, setCount] = useState(0)  
    const [name, setName] = useState('zs') 

    useEffect(() => {    
        console.log('副作用执行了')  
    }, [count])  

    return (    
        <>      
         <button onClick={() => { setCount(count + 1) }}>{count}</button>      
         <button onClick={() => { setName('cp') }}>{name}</button>    
        </>  
    )
}

自定义Hook函数 ,实现自动获取滚动距离Y

import { useState } from "react"

export function useWindowScroll () {
  const [y, setY] = useState(0)
  window.addEventListener('scroll', () => {
    const h = document.documentElement.scrollTop
    setY(h)
  })
  return [y]
}

自定义Hook函数,实现自动同步到localStorage中

import {useState,useEffect} from 'react'
export function useLocalStorage(key,defaultValue){
    const [message,setMessage] = useState(defaultValue)
    useEffect(()=>{
        window.localStorage.setItem(key,message)
        
    },[key,message])
    return [message,setMessage]
}
import {useLocalStorage} from './'
function App(){
    const [message,setMessage] = useLocalStorage('hook-key','ashin')
    setTimeOut(()=>{
        setMessage('阿信')
    },3000)
    return (
    <div>{message}</div>
    )
}

useState—函数作为参数

如果要初始化的数据无法直接得到需要通过计算才能获取到,使用useState(()=>{})

import { useState } from 'react'

function Counter(props) {
  const [count, setCount] = useState(() => {
    return props.count
  })
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>{count}</button>
    </div>
  )
}

function App() {
  return (
    <>
      <Counter count={10} />
      <Counter count={20} />
    </>
  )
}

export default App

useEffect清除副作用

例如清除定时器:如果不清理,组件虽然已经不显示了,但是定时器依旧在运行,在return函数中清理副作用

function Test(){
    useEffect(()=>{
    
       let timer = setInterval(()=>{
            console.log('定时器开启')
        },1000)
        
        return ()=>{
            //添加清除副作用函数
            clearInterval(timer)
        }
    })
}

useEffect–发送网络请求

在类组件中是在生命周期componentUnMounted里发送请求,而在hook中,是用useEffect中

function App(){
    useEffect(()=>{
    
        async function getData(){
    
            const data = await.axios.get('http://geek.itheima.net/v1_0/channels')
        },[])
}

错误写法

function App(){
    useEffect(async ()=>{
            const data = await.axios.get('http://geek.itheima.net/v1_0/channels')
        },[])
}

useRef

在函数组件中获取真实dom元素对象或者组件对象
使用步骤
1.导入useRef函数
2.执行useRef函数并且传入null值,返回值为一个对象,里面有一个current属性存放拿到的dom对象
3.通过ref绑定 要获取的元素或组件

const {useRef} from 'react'
function App(){
    const testRef = useRef(null)
    useEffect(()=>{
        // 拿到了dom元素
        console.log('testRef.current')
    },[])
    return (
      <div ref={testRef}>this is test</div>
    )
}

useContext

使用步骤:
1.使用createContext创建Context对象
2.在顶层组件中使用Provider提供数据
3.在底层组件中通过useContext函数获取数据

import { useState, useContext, createContext } from 'react'
const Context = createContext()
function ComA () {
  const num = useContext(Context)
  return (
    <div>
      this is ComA
      App组件传过来的数据:{num}
      <ComB />
    </div>
  )
}

function ComB () {
  const num = useContext(Context)
  return (
    <div>
      this is ComB
      App组件传过来的数据:{num}
    </div>
  )
}
function App(){
const [num,setNum] = useState(10)
    <Context.Provider value={num}>
    <div>
      <ComA></ComA>
      <button onClick={()=>{setNum(num+1)}}>+</button>
      </div>
    </Context.Provider>
}

路由

基础使用:
1.安装 yarn add react-router-dom@6
2.核心组件BrowerRouter

两种常用Router:HashRouter和BrowseRouter;

import {HashRouter} from 'react'
function App(){
    return (
    // 声明当前要用一个hash模式的路由
        <HashRouter>
        // 指定跳转的组件 to用来匹配路由的地址
            <link to="/">首页</link>
            <link to="/about">关于</link>
            // 路由出口 路由对应的组件会在这里进行渲染 相当于vue中router-view
            <Routes>
                // 指定路径和组件的对应关系 path代表路径 element代表组件
                <Route path='/' element={<Home />} >
                <Route path='/' element={<About />} >
            </Routes>
        </HashRouter>
    )
}

编程式导航—路由跳转
使用js编程的方式实现路由的跳转
语法使用:
1.导入useNavigate钩子函数
2.执行钩子函数得到跳转函数
3.执行跳转函数完成跳转
4.如果在跳转时不想加历史记录,可以添加额外参数replace为true

import {useNavigate} from 'react-router-dom'
const login = ()=>{
    const navigate = useNavigate()
    const getAbout = ()=>{
        navigate('/about',{replace:true})
    }
    return (
        <div>
          login
          <button onClick={getAbout}>切换到关于页</button>
        </div>
    )
}

路由跳转携带传参

两种传参数方式:
1.searchParams传参

// 传参
navigate('/about?id=101')
// 取参
let [params] = useSearchParams()
let id = params.get('id')

2.params传参

// 传参
navigate('/about/101')

// 取参
let params = useParams()
let id = params.id

嵌套路由的写法

function App(){
    return (
        <HashRouter>
            <link to="/">首页</link>
            <link to="/about">关于</link>
            // 路由出口 路由对应的组件会在这里进行渲染 相当于vue中router-view
            <Routes>
                <Route path='/' element={<Home />} >
                    // 定义二级路由嵌套
                    <Route path='article' element={<Article />} > </Route>
                    <Route path='board' element={<Board />} > </Route>
                </Route>
                <Route path='/' element={<About />} ></Route>
            </Routes>
        </HashRouter>
    )
}

// 在一级路由Home组件中定义出口
import {Outlet} from 'react-router-dom'
function Home(){
    return (
        <div>
          Home
          <Outlet/>
        </div>
    )
}

默认二级路由设置: 把要默认的那条路由不设置path 改为index, vue中是设置path为’/’

function App(){
    return (
        <HashRouter>
            <link to="/">首页</link>
            <link to="/about">关于</link>
            // 路由出口 路由对应的组件会在这里进行渲染 相当于vue中router-view
            <Routes>
                <Route path='/' element={<Home />} >
                    // 定义二级路由嵌套
                    <Route index element={<Article />} > </Route>
                    <Route path='board' element={<Board />} > </Route>
                </Route>
                <Route path='/' element={<About />} ></Route>
            </Routes>
        </HashRouter>
    )
}

404页配置

<Routes>
    <Route index element={<Article />} />
    <Route path='board' element={<Board />}></Route>
    // 当所有路径都没有匹配时渲染此路由
    <Route path="*" element={<NotFound />}></Route>
<Routes>

Mobx环境配置

1.安装mobx和mobx-react-lite

yarn add mobx mobx-react-lite
2 具体代码使用: 编写第一个mobx store小案例

import {makeAutoObservable} from 'mobx'
class CounterStore{
    // 1.定义数据
    count = 0
    constructor(){
        // 2.把数据弄成响应式
        makeAutoObservable(this)
    }
    //3.定义action函数(修改数据)
    addCount = ()=>{
        this.count++
    }
}
// 4.实例化
const counterStore = new CounterStore()
export { counterStore }

在App组件中使用mobx数据

import {counterStore} from ''
import {observer} from 'mobx-react-lite' //导入中间件连接mobx react完成响应式
function App(){
    return (
        <div>
            // 渲染store中的count
            {counterStore.count}
            // 点击事件触发action函数修改count
            <button onClick={counterStore.addcount}>+</button>
        </div>
    )
}
// 用observer包裹app
export default observer(App)

Mobx–computed计算属性

import {makeAutoObservable} from 'mobx'
class CounterStore{
    // 1.定义数据
    list = [1,2,3,4,5]
    constructor(){
        // 2.把数据弄成响应式
        makeAutoObservable(this)
    }
    //3.定义计算属性
    get filterList(){
        return this.list.filter(item=>item>2) 计算item大于2的值
    }
   
}
// 4.实例化
const counterStore = new CounterStore()
export { counterStore }

Mobx模块化–拆分子模块创建根模块

创建一个index.js文件合并所有子模块

// 1.导入子模块
import {CounterStore} from ''
import {ListStore} from ''
class RootStore {
    constructor(){
    // 2.对子模块进行实例化
        this.counterStore = new CounterStore()
        this.listStore = new ListStore()
    }
}
// 实例化操作
// 使用react context机制 完成统一方法封装
const rootStore = new RootStore()
// useContext查找机制:优先从Provider value查找,如果找不到就找从createContext方法传递过来的默认参数
const context = React.CreateContext(rootStore)
const useStore = ()=> React.useContext(context)
export {useStore}

在组件中使用数据时

function App(){
    const rootStore = useStore() // 或者使用解构
    return (
        <div>
          {rootStore.counterStore.count}
        </div>
    )
}

Mobx基础使用总结

1.初始化Mobx的过程是怎样的?
声明数据–》响应式处理–》定义action函数–》实例化导出
2.Mobx如何配合react,需要依赖什么包?
mobx-react-list作为连接react,导入observer方法,包裹组件(只能和函数组件配合);如果是类组件,使用mobx-react
3.模块化解决了什么问题?
维护性问题
4.如何实现mobx模块化?
按照功能拆分store模块,根模块组合子模块,利用context机制依赖注入(全局数据传递)

配置别名路径

1.安装修改CRA配置的包:yarn add -D @craco/craco
2.在项目根目录中创建craco的配置文件:craco.config.js,并在配置文件中配置路径别名

// 添加自定义对于webpack的配置
const path = require('path')
module.exports = {
  // webpack配置
  webpack: {
    // 配置别名
    alias: {
      // 约定使用‘@’表示src文件所有路径
      '@': path.resolve(__dirname, 'src')
    }
  }
}

3.修改package.json中的脚本命令

"scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "craco eject"
  },

4.在代码中,可以通过@来表示src目录下的相对路径
5.重启项目,让配置生效

@别名路径提示

能够让vscode识别@路径并给出提示
实现步骤:
1.创建jsconfig.json配置文件
2.代码实现

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*": [
        "src/*"
      ]
    }
  }
}

vscode会自动读取jsconfig.json中的配置,让vscode知道@就是src目录

你可能感兴趣的:(前端攻城狮,笔记,react.js,javascript,前端)