青训营笔记 | React 简单入门与 JavaScript 防抖与节流函数的使用 | 豆包MarsCode AI刷题

38 阅读4分钟

性能优化与调试技巧:探讨如何通过优化JavaScript代码来提高性能,包括减少重绘和重排、使用节流和防抖技术、使用性能分析工具等

React 快速入门

使用 React 与 Babel

通过以下语句可直接使用 React 和 ReactDOM 库, 并使用 Babel 进行编译, 从而快速进入开发环节

为什么要编译?

对于 ES6 某些特性与 JSX 而言, 需要编译才可使用

  <script src="https://unpkg.com/react@16.12.0/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16.12.0/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone@7.8.3/babel.js"></script>

使用 JSX

React 开发中常用的 JSX 语法,是一种类 HTML 的语法扩展,能够使创建 UI 组件更加直观和简洁。然而,浏览器并不原生支持 JSX,需要将其编译成标准的 JavaScript 函数调用(如 React.createElement())来正常运行。

例如,以下 JSX 代码:

const element = <h1>Hello, world!</h1>;

会被编译为:

const element = React.createElement('h1', null, 'Hello, world!');

React state hooks 入门

使用 useState 声明可以直接更新的状态变量

const [name, setName] = useState(0);

并且可以使用 setName 来更新状态 name

setName("jack"); // 设置 name 为 jack
为什么要"多此一举"地使用 useState 声明? 而不直接用变量声明呢?

当状态/变量发生改变时, 会触发再渲染, 对于使用普通变量声明的 HTML 元素会整块整块地刷新

而对于 React hooks 声明的状态只会更新相应的状态

例如

`<div>
    <input type="text" value=${name} />
<div>`

(<div>
    <input type="text" value={name} />
<div>

后者 input 不会因为 name 改变而失去聚焦

useEffect 快速入门

useEffect(fn[, dep]) 在每次渲染时会执行传入的函数 fn,

第二个参数 dep 是依赖项, 即保证依赖项中的状态变化后才执行传入的函数, 而不是每次渲染都执行,

当依赖项为 [] 时, 只有组件初始化(mount)和组件删除(unmount)时才会执行

什么是组件?
function MyApp() {
  return (
    <div>
      <h1>Welcome to my app</h1>
    </div>
  );
}

这就是一个组件 MyApp, 注意首字母大写, 我们可以是用 <MyApp /> 指这个组件

用以上 React 知识写一个简单的 demo

实现效果:

在输入框中, 输入时下面的文字同步更新, 并存储在本地, 刷新页面后不变 image.png

实现代码如下

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React Demo</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div class="root"></div>
    
    <script src="https://unpkg.com/react@16.12.0/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16.12.0/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone@7.8.3/babel.js"></script>
    <script type="text/babel">
      const root = document.querySelector(".root");

      function Form() {
        const [name, setName] = React.useState(() => window.localStorage.getItem("name") || "");

        React.useEffect(() => {
            window.localStorage.setItem("name", name);
        }, [name]);

        function handleNameInput(e) {
          e.preventDefault();
          setName(e.target.value);
        }

        return (
          <>
            <form className="name-form">
              <label htmlFor="name">Enter your name: </label>
              <br />
              <input value={name} type="text" id="name" onChange={handleNameInput} />
            </form>
            <div className="display-container">
              <div className="display-name">Here is your name: </div>
              <div className="display-name">{name}</div>
            </div>
          </>
        );
      }

      ReactDOM.render(<Form />, root);
    </script>
  </body>
</html>
  • HTML 和外部依赖:通过 <script> 标签引入了 React、ReactDOM 和 Babel。Babel 用于在浏览器中编译 JSX 代码。

  • React 组件结构

    • Form() 是一个 React 组件,用于渲染输入框和展示输入的内容。
    • 使用 React.useState 钩子创建了一个 name 状态,初始值通过 window.localStorage.getItem("name") 获取,保证页面刷新后输入仍然存在。
  • 状态和副作用

    • React.useEffect 钩子用于在 name 状态更新时,将其值保存到 localStorage,确保输入持久化
    • handleNameInput 函数响应输入事件,通过 setName 更新 name 状态。
  • 组件渲染

    • 初始渲染时,name 会从 localStorage 读取,如果没有值则为空。
    • 输入框的值通过 value={name} 实现双向绑定,用户输入触发 onChange 事件后,handleNameInput 更新 name 状态。
    • 组件重新渲染后,输入框和展示区域会同步显示 name 的最新值。
  • 输出显示

    • <form> 中包含输入框。
    • 输入的名字在 <div> 中实时显示,示例中使用了两行 <div> 进行展示,其中第二个 <div> 显示用户输入的内容。

加入防抖功能

什么是防抖?

特定时间内只执行一次, 不断触发则只会在特定时间后执行最后一次

function debounce(fn, dur) {
	dur = dur || 100;
	var timer;
	return function() {
		clearTimeout(timer);
		timer = setTimeout(() => {
			fn.apply(this, arguments);
		}, dur);
	}
}

dur时间内不断调用 debounce, 导致 setTimeout 不断刷新进而重置倒计时

代码实现

function debounce(fn, dur = 100) {
  let timer;
  return function () {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, arguments);
    }, dur);
  };
}

function handleNameInput(e) {
  e.preventDefault();
  const newName = e.target.value;
  debounce(() => setName(newName))(); 
}

实现效果: 用户连续输入时只取特定时间的最后一个字符, 哈哈哈哈反人类输入过快设计

加入节流功能

什么是节流?

特定时间内只执行一次, 不断触发会间隔特定时间依次执行

function throttle(fn, time = 500) {
	let timer;
	return function(...args) {
		if (timer == null) {
			fn.apply(this, args);
			timer = setTimeout(() =>{
				timer = null;
			}, time);
		}
	}
}

dur时间内, 不断调用只有当 timernull 时, 才会调用 fn

而只有当第一次或setTimeout结束时, timernull, 即达到节流效果

a2c81b50-8787-11eb-ab90-d9ae814b240d.png

一图胜千言

代码实现

function debounce(fn, dur = 500) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, dur);
  };
}

function throttle(fn, dur = 1000) {
  let timer;
  return function () {
    if (timer == null) {
      fn.apply(this, arguments);
      timer = setTimeout(() => {
        timer = null;
      }, dur);
    }
  };
}

const throttledSetName = throttle((newName) => setName(newName), 500);

function handleNameInput(e) {
  e.preventDefault();
  const newName = e.target.value;
  throttledSetName(newName);
}

不过实现效果好像有问题??? 🥲🥲🥲待会儿再来看看