React 项目记录

1、一些项目基础配置

1、@ 别名 安装  npm install -D @craco/craco

在根目录创建一个 crao.config.js 文件,代码如下

const path = require('path');

module.exports = {
  webpack: {
    alias: {
      // 路径别名
      '@': path.resolve(__dirname, 'src'),
    },
  },
}

2、自动联想,在根目录创建 jsconfig.json 文件,代码如下

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

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

3、mock数据

安装  npm install -D json-server@0.17.3

准备一个json文件

在package.json 文件的scripts 中增加一项,以server/data.json 为数据源

 "server": "json-server ./server/data.json --port 8888"

2、一些 React使用

2.1、useMemo

// 类似于 Vue 的计算属性 [] 中的是依赖的原始值
const monthGroup = useMemo(() => {
  // lodash 中的分组方法
  return _.groupBy(billList, item => dayjs(item.date).format('YYYY-MM'))
}, [billList])




const overview = useMemo(() => {
    if (!currentMonthList) return { income: 0, pay: 0, total: 0 }
    const income = currentMonthList.filter(item => item.type === 'income')
      .reduce((a, c) => a + c.money, 0)
    const pay = currentMonthList.filter(item => item.type === 'pay')
      .reduce((a, c) => a + c.money, 0)
    return {
      income,
      pay,
      total: income + pay
    }
  }, [currentMonthList])

2.2、memo

 React 默认的渲染机制是子组件跟着父组件一起渲染的,但是如果是,子组件,不需要着渲染,这时候就可以用上memo了,使用 demo 如下

// React.memo

import { memo, useState } from "react"

// 1. 验证默认的渲染机制  子跟着父一起渲染
// 2. memo进行缓存  只有props发生变化的时候才会重新渲染 (不考虑context)

const MemoSon = memo(function Son () {
  console.log('我是子组件,我重新渲染了')
  return <div>this is son</div>
})


function App () {
  const [count, setCount] = useState(0)
  return (
    <div className="App">
      <button onClick={() => setCount(count + 1)}>+{count}</button>
      <MemoSon />
    </div>
  )
}

export default App

memo的比较机制

针对简单类型,是认为只要只没变那props就是没变的,子组件就只会渲染一次

针对引用类型,由于每次父组件都是重新执行,那引用类型数据,也会重新创建,会认为每次都是变的,子组件就是每次度渲染

如果引用类型props,当父组件改变的时候,想让子组件不改变,那就得想办法,让引用类型只初始化一次,也就是需要缓存,这时候就可以用到 useMemo了,例子如下

// React.memo props比较机制

// 1. 传递一个简单类型的prop   prop变化时组件重新渲染

// 2. 传递一个引用类型的prop   比较的是新值和旧值的引用是否相等  当父组件的函数重新执行时,实际上形成的是新的数组引用

// 3. 保证引用稳定 -> useMemo 组件渲染的过程中缓存一个值

import { memo, useMemo, useState } from 'react'

const MemoSon = memo(function Son ({ list }) {
  console.log('子组件重新渲染了')
  return <div>this is Son {list}</div>
})


function App () {
  const [count, setCount] = useState(0)

  // const num = 100

  const list = useMemo(() => {
    return [1, 2, 3]
  }, [])

  return (
    <div className="App">
      <MemoSon list={list} />
      <button onClick={() => setCount(count + 1)}>change Count</button>
    </div>
  )
}

export default App

2.3、useCallback

保持函数引用稳定,,不会随着父组件每次更新而改变

// useCallback

import { memo, useCallback, useState } from "react"


const Input = memo(function Input ({ onChange }) {
  console.log('子组件重新渲染了')
  return <input type="text" onChange={(e) => onChange(e.target.value)} />
})

function App () {
  // 传给子组件的函数, 保持函数引用不变,结合子组件memo使用
  const changeHandler = useCallback((value) => console.log(value), [])
  // 触发父组件重新渲染的函数
  const [count, setCount] = useState(0)
  return (
    <div className="App">
      {/* 把函数作为prop传给子组件 */}
      <Input onChange={changeHandler} />
      <button onClick={() => setCount(count + 1)}>{count}</button>
    </div>
  )
}

export default App

2.4、forwardRef

获取子组件的dom元素

import { forwardRef, useRef } from "react"

const Son = forwardRef((props, ref) => {
  return <input type="text" ref={ref} />
})


// 父组件
function App () {
  const sonRef = useRef(null)
  const showRef = () => {
    console.log(sonRef)
    sonRef.current.focus()
  }
  return (
    <>
      <Son ref={sonRef} />
      <button onClick={showRef}>focus</button>
    </>
  )
}

export default App

2.5、useImperativeHandle

暴露方法给父组件使用

import { forwardRef, useImperativeHandle, useRef } from "react"

// 子组件

const Son = forwardRef((props, ref) => {
  // 实现聚焦逻辑
  const inputRef = useRef(null)
  const focusHandler = () => {
    inputRef.current.focus()
  }

  // 把聚焦方法暴露出去
  useImperativeHandle(ref, () => {
    return {
      // 暴露的方法
      focusHandler
    }
  })
  return <input type="text" ref={inputRef} />
})


// 父组件
function App () {
  const sonRef = useRef(null)
  const focusHandler = () => {
    console.log(sonRef.current)
    sonRef.current.focusHandler()
  }
  return (
    <>
      <Son ref={sonRef} />
      <button onClick={focusHandler}>focus</button>
    </>
  )
}

export default App

2.6、高阶组件

简单鉴权组件

3、打包配置相关

3.1、打包

npm run build

3.2、本地预览

npm i -g serve
serve -s ./build
// 访问http://localhost:3000

3.3、路由懒加载

点击那个页面加载对应的页面文件,减少首次加载项目,资源请求,点击那个页面就请求对应的文件

实现

* 使用 lazy 方法导入路由组件
* 使用内置的 Suspense 组件渲染路由组件

import { createBrowserRouter } from 'react-router-dom'
import { lazy, Suspense } from 'react'
import Login from '@/pages/Login'
import Layout from '@/pages/Layout'

import AuthRoute from '@/components/Auth'

const Publish = lazy(() => import('@/pages/Publish'))
const Article = lazy(() => import('@/pages/Article'))
const Home = lazy(() => import('@/pages/Article'))


const router = createBrowserRouter([
  {
    path: '/',
    element: (
      <AuthRoute>
        <Layout />
      </AuthRoute>
    ),
    children: [
      {
        index: true,
        element: (
          <Suspense fallback={'加载中'}>
            <Home />
          </Suspense>
        )
      },
      {
        path: 'article',
        element: (
          <Suspense fallback={'加载中'}>
            <Article />
          </Suspense>
        )
      },
      {
        path: 'publish',
        element: (
          <Suspense fallback={'加载中'}>
            <Publish />
          </Suspense>
        )
      },
    ],
  },
  {
    path: '/login',
    element: <Login />,
  },
])

export default router

3.4、打包体积分析

可视化分析工具

  1. 安装分析打包体积的包:npm i source-map-explorer
  2. 在 package.json 中的 scripts 标签中,添加分析打包体积的命令
  3. 对项目打包:npm run build(如果已经打过包,可省略这一步)
  4. 运行分析命令:npm run analyze
  5. 然后浏览器会自动打开下面页面,分析图表中的包体,就可以对一些大的包做优化处理了

3.5、优化配置-CDN

基于3.4的分析,可以看出 react、react-dom 是比较大的包,但是又不需要经常变的,我们就可以使用cdn的方式引入,让其不参与打包,减少打包后的体积

分析说明:通过 craco 来修改 webpack 配置,从而实现 CDN 优化
核心代码
craco.config.js

// 添加自定义对于webpack的配置

const path = require('path')
const { whenProd, getPlugin, pluginByName } = require('@craco/craco')

module.exports = {
  // webpack 配置
  webpack: {
    // 配置别名
    alias: {
      // 约定:使用 @ 表示 src 文件所在路径
      '@': path.resolve(__dirname, 'src')
    },
    // 配置webpack
    // 配置CDN
    configure: (webpackConfig) => {
      let cdn = {
        js:[]
      }
      whenProd(() => {
        // key: 不参与打包的包(由dependencies依赖项中的key决定)
        // value: cdn文件中 挂载于全局的变量名称 为了替换之前在开发环境下
        webpackConfig.externals = {
          react: 'React',
          'react-dom': 'ReactDOM'
        }
        // 配置现成的cdn资源地址
        // 实际开发的时候 用公司自己花钱买的cdn服务器
        cdn = {
          js: [
            'https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js',
            'https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js',
          ]
        }
      })

      // 通过 htmlWebpackPlugin插件 在public/index.html注入cdn资源url
      const { isFound, match } = getPlugin(
        webpackConfig,
        pluginByName('HtmlWebpackPlugin')
      )

      if (isFound) {
        // 找到了HtmlWebpackPlugin的插件
        match.options.files = cdn
      }

      return webpackConfig
    }
  }
}

public/index.html 

<body>
  <div id="root"></div>
  <!-- 加载第三发包的 CDN 链接 -->
  <% htmlWebpackPlugin.options.files.js.forEach(cdnURL => { %>
    <script src="<%= cdnURL %>"></script>
  <% }) %>
</body>

注意:上面两个得注意一致,否则会下面报这个下面这个错误

Template execution failed: TypeError: Cannot read properties of undefined (reading 'js')

  • 23
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值