首页
统计
留言
友链
关于我
Search
1
git常用命令
6 阅读
2
docker常用命令整理
6 阅读
3
西门子S7-200 SMART在仿真软件运行
6 阅读
4
电脑高清壁纸网站
6 阅读
5
iframe父子窗口通信
4 阅读
前端
JavaScript
React
Vue
Nuxt3
后端
移动端
开发工具
VSCode
版本控制
WebStorm
运维
Docker
电气工程
登录
Search
标签搜索
前端
JavaScript
西门子S7-200 SMART
Vue
React
vscode
Git
运维
Docker
nuxt
Svg
WebStorm
壁纸
Flutter
小熊维尼
累计撰写
19
篇文章
累计收到
3
条评论
首页
栏目
前端
JavaScript
React
Vue
Nuxt3
后端
移动端
开发工具
VSCode
版本控制
WebStorm
运维
Docker
电气工程
页面
统计
留言
友链
关于我
搜索到
9
篇与
的结果
2024-12-06
JavaScript中的防抖和节流函数
防抖函数防抖是指在事件被触发n秒后再执行回调,如果在这n秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次重复请求。应用场景按钮提交场景:防止多次提交按钮,只执行最后提交的一次服务端验证场景:表单验证需要服务端配合,只窒执行一段连续的输入事件的最后一次,还有搜索联想词功能类似生存环境请用lodash.debounce防抖函数的实现function debounce(fn, wait) { let timer = null; return function() { let context = this,args = [...arguments]; // 如果此时存在定时器的话,则取消之前的定时器重新计时 if (timer) { clearTimeout(timer); timer = null; } // 设置定时器,使事件间隔指定时间后执行 timer = setTimeout(() => { fn.apply(context, args); }, wait) } }节流函数节流是指规定一个单位事件,在这个单位时间内,只能有一次触发事件的回调函数执行,如果同一个单位时间内某事件被触发多次,只能由一次能生效。节流可以使用在scroll函数的事件监听上,通过事件节流来降低事件调用频率。应用场景拖拽场景:固定时间内只执行一次,防止超高频次触发位置变动缩放场景:监控浏览器resize动画场景:避免短时间内多次触发动画引起性能问题节流函数的实现时间戳版function throttle(fn, delay) { let preTime = Date.now(); return function() { let context = this,args = [...arguments],nowTime = Date.now(); // 如果两次时间间隔超过了指定时间,则执行函数。 if (nowTime - preTime >= delay) { preTime = Date.now(); return fn.apply(context, args); } } }定时器版function throttle(fun, wait) { let timeout = null; return function() { let context = this; let args = [...arguments]; if (!timeout) { timeout = setTimeout(() => { fun.apply(context, args) timeout = null }, wait) } } }
2024年12月06日
1 阅读
0 评论
0 点赞
2024-12-06
探讨前端开发技术:从流行框架到实际应用
一、现在流行的前端框架React.js由 Facebook 开发和维护,是一个用于构建用户界面的 JavaScript 库。强调组件化开发,使用虚拟 DOM 提高渲染效率。社区活跃,有大量的第三方库支持。Vue.js一个渐进式的 JavaScript 框架,易于学习,适合快速开发。支持双向数据绑定,简化了数据管理和视图更新。社区支持良好,文档详尽。Angular由 Google 开发,是一个完整的前端开发框架。提供了丰富的功能,如依赖注入、双向数据绑定等。适合大型企业级应用开发,但学习曲线相对较陡。Svelte一种新型的前端框架,实际上是一个编译器,可以将框架级别的代码编译成高效的原生 JavaScript。无需在客户端加载框架,减少了初始加载时间。社区正在成长,但对于初学者来说可能不如其他框架成熟。Next.js一个基于 React 的轻量级服务端渲染应用框架。支持静态网站导出,适合 SEO 友好的应用开发。简化了 React 应用的开发流程,特别适合构建多页应用。Nuxt.js一个基于 Vue.js 的通用应用框架。支持服务端渲染 (SSR),适合 SEO 友好的应用开发。简化了 Vue 应用的开发流程,特别适合构建多页应用和静态站点。uni-app一个基于 Vue.js 的跨平台应用开发框架。支持多端渲染,适合开发一次、多端运行的应用。简化了跨平台应用的开发流程,特别适合构建移动应用和小程序。二、我们公司使用的框架Nuxt.js 2在早期的网页代下单项目中,我们选择了 Nuxt.js 2 进行开发(当时 Vue 3 还没有推出)。Nuxt.js 是一个基于 Vue.js 的通用应用框架,特别适合需要良好 SEO 支持的项目。以下是我们在该项目中使用 Nuxt.js 2 的主要原因:服务端渲染 (SSR) :Nuxt.js 2 支持服务端渲染,能够显著提高首屏加载速度,优化搜索引擎抓取效果,确保我们的网页在搜索引擎中的排名更高。自动路由:通过 pages 目录自动生成路由配置,简化了开发流程,减少了手动配置的复杂性和出错概率。组件化开发:继承了 Vue.js 的组件化开发模式,使代码结构清晰,便于维护和扩展。静态站点生成:支持将应用生成为静态站点,适合内容驱动的网站,可以轻松部署到 CDN 或静态托管平台,提高访问速度和可靠性。内置状态管理:内置了 Vuex 状态管理库,方便管理应用的状态,确保数据的一致性和可预测性。开发体验:提供了热重载功能,开发过程中修改代码后会自动刷新页面,提高了开发效率。内置了开发服务器,支持代理、静态资源服务等功能,简化了开发环境的搭建。生态系统丰富:拥有活跃的社区和丰富的插件生态,可以轻松集成各种第三方库和工具,满足不同项目的需求。Vue.js 2网页代下单项目用到了 Nuxt.js 2 框架进行开发,该框架是基于 Vue.js 2 的,因此开发者需要掌握 Vue.js 2 的相关知识。Vue.js 3自 2024 年起,公司中所有的 H5 页面以及供应商订单系统等不需要 SEO 的项目都采用了 Vue.js 3 进行开发。Vue.js 3 带来了许多性能和功能上的改进,以下是我们在新项目中选择 Vue.js 3 的主要原因:性能提升:Vue 3 通过优化内部算法和数据结构,显著提高了应用的初始化速度和运行性能。Composition API: Composition API 允许开发者将逻辑按功能模块组织,提高了代码的模块化和复用性。更好的 TypeScript 支持:Vue 3 对 TypeScript 的支持更加完善,提供了更好的类型推断和类型检查,减少了类型错误,提高了代码的健壮性。更强大的模板语法:引入了新的 <script setup> 语法,简化了组件的编写,使得代码更加简洁和易读。更好的工具和生态系统:Vue 3 的 Devtools 提供了更强大的调试和性能分析工具,帮助开发者更好地理解和优化应用。同时,Vue 3 拥有活跃的社区和丰富的插件生态,可以轻松集成各种第三方库和工具,满足不同项目的需求。更好的长期支持:Vue 3 是 Vue 的最新版本,将持续获得官方的支持和更新,确保项目能够跟上最新的技术发展。Nest.js公司中的 PDF 生成采用后端实现,使用 Nest.js 框架。Nest.js 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的框架。以下是我们在该项目中使用 Nest.js 的主要原因:模块化架构:Nest.js 采用模块化架构,使代码结构清晰,便于维护和扩展。依赖注入:内置了强大的依赖注入机制,简化了模块间的依赖管理和代码复用。丰富的装饰器:提供了丰富的装饰器,简化了控制器、服务、管道、守卫等的定义和使用。HTTP 客户端:内置了 HTTP 客户端模块,方便进行 HTTP 请求。WebSocket 支持:支持 WebSocket 通信,适用于实时应用开发。社区支持:拥有活跃的社区和丰富的插件生态,可以轻松集成各种第三方库和工具。三、用框架之前的前导知识和需要了解的生态基础技术HTML:掌握 HTML 的基本语法和语义化标签的使用。CSS:熟悉 CSS 的基本语法,了解盒模型、布局、选择器等。JavaScript:熟练掌握 JavaScript 的基本语法和常用 API,了解 ES6+ 新增的语法特性,如箭头函数、解构赋值、模板字符串、Promise、Async/Await 等。TypeScript:了解 TypeScript 的基本语法和类型系统,能够编写类型安全的代码。CSS 预处理器Less:熟悉 Less 的语法和变量、混合、嵌套等高级特性。Sass:了解 Sass 的语法和变量、混合、嵌套等高级特性。Tailwind CSS:熟悉 Tailwind CSS 的实用优先设计理念,能够快速构建自定义设计。Stylus:了解 Stylus 的语法和高级特性,能够编写简洁的样式代码。UI 框架Element UI:了解 Element UI 的基本用法,能够快速搭建美观的用户界面。Ant Design Vue:熟悉 Ant Design Vue 的组件库,能够高效地开发企业级应用。请求库Axios:熟练使用 Axios 进行 HTTP 请求,了解拦截器、错误处理等高级特性。Fetch API:了解 Fetch API 的基本用法,能够进行网络请求。WebSocket:熟悉 WebSocket 的基本用法,能够实现实时通信。国际化i18n:了解 Vue I18n 的基本用法,能够实现多语言支持和国际化。图表库Chart.js:熟悉 Chart.js 的基本用法,能够绘制各种图表,如折线图、柱状图、饼图等。ECharts:了解 ECharts 的基本用法,能够绘制复杂的图表和数据可视化。3D 渲染Three.js:了解 Three.js 的基本用法,能够进行 3D 图形和动画的创建。开发理念组件化思想组件化开发:理解组件化开发的基本概念,能够创建和使用可复用的组件。路由管理Vue Router:熟悉 Vue Router 的基本用法,了解如何配置和使用路由。Nuxt.js 路由:了解 Nuxt.js 的自动路由机制,能够根据 pages 目录自动生成路由。状态管理Vuex:了解 Vuex 的基本概念和用法,能够进行状态管理。Pinia:熟悉 Pinia 的基本用法,了解其与 Vuex 的区别和优势。代码质量和工具ESLint:熟练使用 ESLint 进行代码规范检查,确保代码风格一致。Prettier:了解 Prettier 的基本用法,能够自动格式化代码。版本控制Git:熟练使用 Git 进行版本控制,了解分支管理、合并冲突解决等操作。服务器和操作系统Linux:了解 Linux 基本命令,能够进行文件操作、权限管理等,能够独立部署前端项目。Nginx:熟悉 Nginx 的基本配置,能够进行反向代理、负载均衡等操作。Docker:了解 Docker 的基本用法,能够进行容器化部署和管理。构建工具Webpack:熟悉 Webpack 的基本配置,了解如何进行模块打包和优化。Vite:了解 Vite 的基本用法,能够快速启动开发服务器,提高开发效率。测试工具Jest:熟悉 Jest 的基本用法,能够进行单元测试和集成测试。后端开发(Nest.js)Node.js:熟悉 Node.js 的基本用法,能够进行服务器端开发。Express:了解 Express 的基本用法,能够进行简单的服务器端开发。TypeScript:熟悉 TypeScript 的高级用法,能够编写类型安全的后端代码。依赖注入:理解依赖注入的基本概念,能够使用 Nest.js 的依赖注入机制。模块化架构:了解模块化架构的基本概念,能够设计和实现模块化的后端应用。HTTP 客户端:熟悉 HTTP 客户端的用法,能够进行外部 API 调用。WebSocket:了解 WebSocket 的基本用法,能够实现实时通信。数据库:了解常见数据库(如 MySQL、MongoDB)的基本用法,能够进行数据存储和查询。ORM:熟悉 ORM(如 TypeORM、Sequelize)的基本用法,能够进行数据库操作。
2024年12月06日
2 阅读
0 评论
0 点赞
2024-12-06
React Hooks详解
useStateimport { useState } from 'react'; function Example() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }useState会返回一个数组,一个state,一个用于更新state的函数useState唯一的参数就是初始state,在初始化渲染期间,返回的状态与传入的第一个值相同注意事项React假设当你多次调用useState的时候,你能保证每次渲染时它们的调用顺序是不变的。因为react是根据每个useState定义时的顺序来确定你在更新state值时更新的是哪个state你可以在事件处理函数中或其他一些地方调用这个函数。它类似class组件的this.setState,但是它不会把新的state和旧的state进行合并,而是直接替换initialState参数只会在组件的初始化渲染中起作用,后续渲染时会被忽略Hook内部使用Object.is来比较新/旧state是否相等,与class组件中的setState方法不同,如果你修改状态的时候,传的状态值没有变化,则不重新渲染useState不能直接存储函数或函数组件,他会调用该函数并且将函数的返回值作为最终state值进行存储或更新。如果必须这么做可以作为一个数组的元素或对象的某个属性进行存储useState没有设置类似class组件中的setState回调函数来拿到最新state然后进行后续操作,可以通过useEffect并设置相应依赖来实现。因为useEffect就是在渲染完成后调用的useState在异步操作中的状态不同步的问题函数的运行时独立的,每个函数都有一份独立的作用域。函数的变量时保存在运行时的作用域里面。当我们有一部操作的时候,经常会碰到异步回调的变量引用是之前的,也就是旧的(这里也可以理解成闭包)。比如下面的一个例子:import React, { useState } from "react"; const Counter = () => { const [counter, setCounter] = useState(0); const onAlertButtonClick = () => { setTimeout(() => { alert("Value: " + counter); }, 3000); }; return ( <div> <p>You clicked {counter} times.</p> <button onClick={() => setCounter(counter + 1)}>Click me</button> <button onClick={onAlertButtonClick}> Show me the value in 3 seconds </button> </div> ); }; export default Counter;当你点击Show me the value in 3 secondes后,紧接着点击Click me使得counter的值从0变成1.三秒后,定时器触发,但alert出来的是0(旧值),但我们希望的结果是当前的状态1这个问题在class component不会出现,因为class component的属性和方法都存在一个instance上,调用方式是:this.state.xxx和this.method()。因为每次都是从一个不变的instance上进行取值,所以不存在引用是旧的问题。除了setTimout这种异步,还有类似事件监听函数(比如滚动监听的回调函数)中访问State也会是旧的这个问题目前的普遍解决方案是使用useRefuseEffect什么是react中的副作用操作?指那些没有发生在数据向视图转换过程中的逻辑,如ajax请求、访问原生dom元素、本地持久化缓存、绑定/解绑事件、添加订阅、设置定时器、记录日志等。副作用操作可以分两类:需要清除的(例如事件绑定/解绑)和不需要清除的。原先在函数组件内(这里指在react渲染阶段)改变dom、发送ajax请求以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的bug并破坏UI的一致性一个需求实现:需要实时让document.title显示你最新的点击次数(count)class组件实现:class Counter extends React.Component{ state = {number:0}; add = ()=>{ this.setState({number:this.state.number+1}); }; componentDidMount(){ this.changeTitle(); } componentDidUpdate(){ this.changeTitle(); } changeTitle = ()=>{ document.title = `你已经点击了${this.state.number}次`; }; render(){ return ( <> <p>{this.state.number}</p> <button onClick={this.add}>+</button> </> ) } }因为需要实时让document.title显示你最新的点击次数(count),所以就必须在componentDidMount或componentDidUpdate中编写重复的代码来重新设置document.titlehooks实现:import React,{Component,useState,useEffect} from 'react'; import ReactDOM from 'react-dom'; function Counter(){ const [number,setNumber] = useState(0); // useEffect里面的这个函数会在第一次渲染之后和更新完成后执行 // 相当于 componentDidMount 和 componentDidUpdate: useEffect(() => { document.title = `你点击了${number}次`; }); return ( <> <p>{number}</p> <button onClick={()=>setNumber(number+1)}>+</button> </> ) } ReactDOM.render(<Counter />, document.getElementById('root'));useEffect就是一个Effect Hook,给函数组件增加了操作副作用的能力。它跟class组件中的componentDidMount、componentDidUpdate和ComponentWillUnmount具有相同的用途,只不过被合并成了一个API与componentDidMount或componentDidUpdate不同,使用useEffect调度的effect不会阻塞浏览器更新屏幕,这让你的应用看起来相应更快。大多数情况下,effect不需要同步地执行。在个别情况下(例如测量布局),有单独的useLayoutEffect Hook供你使用,其API与useEffect相同清除副作用function Counter(){ let [number,setNumber] = useState(0); let [text,setText] = useState(''); // 相当于componentDidMount 和 componentDidUpdate useEffect(()=>{ console.log('开启一个新的定时器') let $timer = setInterval(()=>{ setNumber(number=>number+1); },1000); // useEffect 如果返回一个函数的话,该函数会在组件卸载和更新时调用 // useEffect 在执行副作用函数之前,会先调用上一次返回的函数 // 如果要清除副作用,要么返回一个清除副作用的函数 return ()=>{ console.log('destroy effect'); clearInterval($timer); } }); // },[]);//要么在这里传入一个空的依赖项数组,这样就不会去重复执行 return ( <> <input value={text} onChange={(event)=>setText(event.target.value)}/> <p>{number}</p> <button>+</button> </> ) }useEffect接收一个函数,该函数会在组件渲染到屏幕之后才执行,该函数有要求:要么返回一个能清除副作用的函数,要么旧不返回任何内容默认情况下,useEffect在第一次渲染之后和每次更新之后都会执行。而useEffect接收的函数参数所返回的清除副作用的函数则会在组件更新和卸载前执行,然后更新后执行useEffect接收的函数。然后等待下一次组件更新或卸载,执行副作用的函数...如此循环往复跳过effect进行性能优化默认情况下,useEffect在每次更新之后都会执行有时候,我们只想useEffect只在组件挂在时执行,然后卸载时执行清除副作用函数,不想更新时也执行useEffect(比如原生事件的绑定/解绑)或者只想让执行state发生更新时才执行useEffect(如果某些state更新后拿到最新state进行后续操作)此时,你可以通知react跳过对effect的调用,只要传递数组作为useEffect的第二个可选参数即可如果想执行只运行一次的effect(仅在组件挂在和卸载时执行),可以传递一个空数组作为第二个参数。这就告诉react你的effect不依赖props或state中的任何值,所以它永远都不需要重复执行如果指定state发生更新时才执行useEffect,可以传递一个包含执行state元素的数组作为第二个参数function Counter(){ let [number,setNumber] = useState(0); let [text,setText] = useState(''); // 相当于componentDidMount 和 componentDidUpdate useEffect(()=>{ console.log('useEffect'); let $timer = setInterval(()=>{ setNumber(number=>number+1); },1000); },[text]);// 数组表示 effect 依赖的变量,只有当这个变量发生改变之后才会重新执行 efffect 函数 return ( <> <input value={text} onChange={(event)=>setText(event.target.value)}/> <p>{number}</p> <button>+</button> </> ) }注意:无论你是否制定了useEffct的第二个参数,useEffect永远都会在组件挂载时执行一次使用多个useEffectuseEffect可以声明多个,react将按照effect声明的顺序依次调用组件中的每一个effect我们可以根据具体副作用操作的性质分类将不同种类的操作放到多个useEffect中// Hooks 版 function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); //dom操作相关的effect useEffect(() => { document.title = `You clicked ${count} times`; }); const [isOnline, setIsOnline] = useState(null); //订阅/取消订阅的相关effect useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); // ... }useEffect不能接收async作为回调函数在useEffect中发起异步请求是很常见的场景,由于异步请求通常都是封装好的异步方法,所以新手很容易写成下面这样function App() { const [data, setData] = useState({ hits: [] }); // 注意 async 的位置 // 这种写法,虽然可以运行,但是会发出警告 // 每个带有 async 修饰的函数都返回一个隐含的 promise // 但是 useEffect 函数有要求:要么返回清除副作用函数,要么就不返回任何内容 useEffect(async () => { const result = await axios( 'https://hn.algolia.com/api/v1/search?query=redux', ); setData(result.data); }, []); return ( <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> ); }更优雅的写法:function App() { const [data, setData] = useState({ hits: [] }); useEffect(() => { // 更优雅的方式 //将异步请求封成一个独立async函数然后在useEffect中调用 const fetchData = async () => { const result = await axios( 'https://hn.algolia.com/api/v1/search?query=redux', ); setData(result.data); }; fetchData(); }, []); return ( <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> ); }useLayoutEffect与useEffect的区别useEffect是异步非阻塞调用,useLayoutEffect是同步阻塞调用useEffect在浏览器绘制后调用(即Renderer commit阶段结束后),useLayoutEffect在DOM变更(React的更新)后,浏览器绘制前完成所有操作(即Renderer commit阶段的Layout子阶段同步执行)使用场景:大部分情况下使用useEffect即可。针对小部分特殊情况如短时间内出发了多次状态更新导致渲染多次以致屏幕闪烁的情况,使用useLayoutEffect会在浏览器渲染之前同步更新DOM数据,哪怕是多次的操作,也会在渲染前一次性处理完,再交给浏览器绘制。这样不会导致闪屏现象发生。useReduceruseReducer和redux中的reducer很像。useState内部就是靠useReducer来实现的useReducer接收两个参数:reducer函数含(preState和action两个参数)和初始化的state // 官方 useReducer Demo // 第一个参数:应用的初始化 const initialState = {count: 0}; // 第二个参数:state的reducer处理函数 function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; default: throw new Error(); } } function Counter() { // 返回值:最新的state和dispatch函数 const [state, dispatch] = useReducer(reducer, initialState); return ( <> // useReducer会根据dispatch的action,返回最终的state,并触发rerender Count: {state.count} // dispatch 用来接收一个 action参数「reducer中的action」,用来触发reducer函数,更新最新的状态 <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); }选择useReducer还是useState如果你的页面state很简单,可以直接使用useState如果你的页面state比较复杂(state是一个对象或者state非常多散落在各处)请使用useReducer对于复杂的state操作逻辑(比如某项操作同时操作或更新多个state值),嵌套的state的对象,推荐使用useReducer如果你的页面组件层级比较深,并且需要子组件触发state的变化,可以考虑useReducer+useContextuseContext接收一个context对象(React.createContext的返回值)并返回context的当前值。当前的context值由上层组件中距离当前组件最近的<MyContext.Provider>的value prop决定当组件上层最近的<MyContext.Provider>更新时,该Hook会触发重渲染,并使用最新传递给MyContext provider的context value值useContext(MyContext)相当于class组件中的static contextType = MyContext或者<MyContext.Consumer>const themes = { light: { foreground: "#000000", background: "#eeeeee" }, dark: { foreground: "#ffffff", background: "#222222" } }; const ThemeContext = React.createContext(themes.light); function App() { return ( <ThemeContext.Provider value={themes.dark}> <Toolbar /> </ThemeContext.Provider> ); } function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } function ThemedButton() { const theme = useContext(ThemeContext); return ( <button style={{ background: theme.background, color: theme.foreground }}> I am styled by theme context! </button> ); }使用useContext和useReducer模拟实现简易ReduxProvider组件import React, { useReducer } from 'react' import './App.css' import ComponentA from './components/ComponentA' import ComponentB from './components/ComponentB' import ComponentC from './components/ComponentC' const initialState = 0 const reducer = (state, action) => { switch (action) { case 'increment': return state + 1 case 'decrement': return state - 1 case 'reset': return initialState default: return state } } export const CountContext = React.createContext() function App() { const [count, dispatch] = useReducer(reducer, initialState) return ( <CountContext.Provider value={{ countState: count, countDispatch: dispatch }} > <div className="App"> {count} <ComponentA /> <ComponentB /> <ComponentC /> </div> </CountContext.Provider> ) } export default AppComponent A://组件A function ComponentA() { const countContext = useContext(CountContext) return ( <div> Component A {countContext.countState} <button onClick={() => countContext.countDispatch('increment')}>Increment</button> <button onClick={() => countContext.countDispatch('decrement')}>Decrement</button> <button onClick={() => countContext.countDispatch('reset')}>Reset</button> </div> ) }Component B:function ComponentB() { const countContext = useContext(CountContext) return ( <div> Component B {countContext.countState} <button onClick={() => countContext.countDispatch('increment')}>Increment</button> <button onClick={() => countContext.countDispatch('decrement')}>Decrement</button> <button onClick={() => countContext.countDispatch('reset')}>Reset</button> </div> ) }useRefuseRef返回一个可变的ref对象,其.current属性被初始化入被传入的参数(initialValue)。返回的ref对象在组件的整个生命周期内不变useRef返回的ref对象在组件的整个生命周期内保持不变,也就是说每次重新渲染函数组件时,返回的ref对象都是同一个。也就是说,useRef的更新不会引起当前组件或子组件的更新渲染但使用React.createRef,每次重新渲染组件都会重新创建ref示例:用useRef存储dom节点function Child() { const inputRef = useRef(); console.log('input===inputRef', input === inputRef); input = inputRef; function getFocus() { inputRef.current.focus(); } return ( <> <input type="text" ref={inputRef} /> <button onClick={getFocus}>获得焦点</button> </> ) }用useRef解决useState异步更新不同步的问题:针对上面useState谈到的异步更新不同步的问题,用useRef返回的immutable RefObject(把值保存在current属性上)来保存state,你可以把useRef存储的值堪称class组件实例中通过this存储的属性。然后取值方式从counter变成了->counterRef.current。如下:import React, { useState, useRef, useEffect } from "react"; const Counter = () => { const [counter, setCounter] = useState(0); const counterRef = useRef(counter); const onAlertButtonClick = () => { setTimeout(() => { alert("Value: " + counterRef.current); }, 3000); }; useEffect(() => { counterRef.current = counter; }); return ( <div> <p>You clicked {counter} times.</p> <button onClick={() => setCounter(counter + 1)}>Click me</button> <button onClick={onAlertButtonClick}> Show me the value in 3 seconds </button> </div> ); }; export default CounterReact.forwardRef在useRef出来之前,由于函数组件是没有实力的,所以函数组件无法使用ref属性来获取dom引用,而对应的解决方法就是React.forwardRef:TextInput函数组件:const TextInput = forwardRef((props,ref) => { //设置input标签node节点作为TextInput组件的ref引用 return <input ref={ref}></input> })function TextInputWithFocusButton() { // 关键代码 const inputEl = useRef(null); const onButtonClick = () => { // 关键代码,`current` 指向已挂载到 DOM 上的文本输入元素 inputEl.current.focus(); }; return ( <> // 用useRef存储TextInput设置的ref引用 <TextInput ref={inputEl}></TextInput> <button onClick={onButtonClick}>Focus the input</button> </> );上面例子说明forwardRef和useRef配合,可以在父组件中操作子组件的ref对象useCallbackuseCallback缓存一个函数,这个函数如果是由父组件作为props传递给子组件,或者自定义hooks里面的函数(通常自定义hooks里面的函数不会依赖于引用它的组件里面的数据),这时候我们可以考虑缓存这个函数,好处:不用每次重新声明新的函数,避免释放内存,分配的计算资源浪费子组件不会因为这个函数的变动重新渲染。(和React.memo搭配使用)function Example(){ const [count, setCount]= useState(1); const getNum = useCallback(() => { return (555 * 666666 )+count //只有count值改变时,才会重新计算 },[count]) const Child = React.memo(({getNum}) =>{ return <h4>总和{getNum()}</h4> }) return <div> <Child getNum={getNum}/> <button onClick={() => setCount(count + 1)}>+1</button> </div> }上面例子,将一个函数交给useCallBack处理并且作为props传递给memo包裹的子组件并子组件调用该方法,定义只有当coutn变化时才会触发子组件重新渲染因为通过useCallBack包裹后的函数通过props传递给子组件的永远是该函数的调用useMemouseMemo主要用于渲染过程优化,两个参数依次是计算函数(通常是组件函数)和依赖状态列表,当依赖的状态发生改变时,才会触发计算函数的执行。如果没有指定依赖,则每一次渲染过程都会执行该计算函数。useMemo的返回值就是计算函数的返回值function Example(){ const [count, setCount]= useState(1); const getNum = useMemo(() => { return (555 * 666666 )+count //只有count值改变时,才会重新计算 },[count]) return <div> <h4>总和:{getNum}</h4> <button onClick={() => setCount(count + 1)}>+1</button> </div> } export default Example;上面例子只有当count变化时才会触发getNum函数的重新计算和渲染。如果不使用useMemo则任何一个state发生变化都会导致组件重新渲染进而导致getNum重新计算,耗费性能useMemo和useCallback的区别useMemo和useCallback接收的参数都是一样,第一个参数为回调,第二个参数为要依赖的数据共同作用:仅仅依赖数据发生变化,才会重新计算结果,也就是起到缓存的作用。两者区别:useMemo计算结果是计算函数返回来的值,主要用于缓存计算结果的值,应用场景如:需要计算的状态useCallback计算结果是计算函数,主要用于缓存函数,应用场景如:需要缓存的函数,因为函数式组件每次任何一个state的变化,整个组件都会被重新刷新,一些函数是没有必要重新刷新的,此时就应该缓存起来,提高性能,和减少资源浪费。注意:当父组件更新渲染(state和props变化)时,无论子组件的props是否改变都会默认更新渲染子组件。所以这种情况下,若你的useMemo或useCallback时用来传给子组件的props时,都必须借助React memo来包裹子组件完成缓存,才能避免子组件的无效多余更新自定义Hook自定义的Hook必须以use开头,这个约定非常重要,不遵循的话,由于无法判断某个函数是否包含对其内部Hook的调用,React将无法自动检查你的Hook是否违反了Hook的规则。useDidMountimport { useEffect } from 'react'; const useDidMount = fn => useEffect(() => fn && fn(), []); export default useDidMount;useWillUnmountuseEffect时已经提起过,其允许返回一个清除副作用的函数,当依赖项为[]时,其相当与componentWillUnMountimport { useEffect } from 'react'; const useWillUnmount = fn => useEffect(() => () => fn && fn(), []); export default useWillUnmount;实现类似class组件可支持回调的setState方法class组件更新状态时,setState可以通过第二个参数拿到更新完毕后的回调函数。很遗憾,虽然hooks函数的useState第二个参数回调支持类似class组件的setState第一个参数的用法(通过传入一个函数并将函数的返回值作为新的state进行更新),但不支持第二个参数回调,但是很多业务场景中我们又希望hooks组件能支持更新后的回调这一方法。借助useRef和useEffect配合useState来实现这一功能:const useXState = (initState) => { const [state, setState] = useState(initState) //表示有state值更新了 let isUpdate = useRef() const setXState = (state, cb) => { //这里setState是使用了函数参数的方式更新useState的值,而不是直接更新成指定的参数值 setState(prev => { isUpdate.current = cb return typeof state === 'function' ? state(prev) : state }) } useEffect(() => { if(isUpdate.current) { //存在更新state,则执行回调 isUpdate.current() } }) return [state, setXState] } export default useXState说明:利用useRef的特性来作为标识区分挂载还是更新,当执行setState时,会传入和setState一摸一样的参数,并且将回调赋值给useRef的current属性,这样在更新完成时,我们手动调用current即可实现更新后的回调这一功能Hooks vs Render Props vs HOC没有hooks之前,高阶组件和Render Props本质上都是将复用逻辑提升到父组件中。这样就能够避免HOC和Render Props带来的【嵌套地狱】。但是,像Context的和这样有父子层级关系(树状结构关系)的,还是只能使用Render Props或者HOC。对于Hooks、Render Props和告诫组件来说,它们都有各自的使用场景Hooks:替代Class的大部分用例,除了getSnapshotBeforeUpdate和componentDidCatch还不支持。可提取复用逻辑。除了有明确父子关系的,其他场景都可以使用Hooks。Render Props:在组件渲染上拥有更高的自由度,可以根据父组件提供的数据进行动态渲染。适合有明确父子关系的场景高阶组件:适合用来注入,并且生成一个新的可复用组件。适合用来写插件不过,能使用Hooks的场景还是应该优先使用Hooks,其次才是Render Props和HOC。当然,Hooks、Render Props和HOC不是对立的关系。我们既可以用Hook来写Render Props和HOC,也可以在HOC中使用Render Props和Hooks。参考文章:https://blog.csdn.net/qq_43293207/article/details/117631126
2024年12月06日
2 阅读
0 评论
1 点赞
2024-12-06
iframe父子窗口通信
子窗口向父窗口发送消息window.parent.postMessage(参数1为发送的消息数据,参数2为可以接受到消息的源) window.parent.postMessage({ 'type': '自定义消息类型', 'value':JSON.stringify(发送的数据字段,只能是字符串类型) }, '*')父窗口接受消息window.addEventListener('message', 事件, false);const iframe = document.getElementById('iframe') window.addEventListener( 'message', (e) => { try { if (e.source === iframe.contentWindow) { if (e.data?.type === '自定义事件类型') { console.log('查看发送过来的数据', e.data) } } } catch {} }, false )父窗口向子窗口发送消息Iframe.contentWindow.postMessage(消息,源)注意:只有当iframe加载完毕,即onLoad完成后,才能接收到消息,所以当load完成后父窗口在去发送消息,不然发了也是白发!<template> <iframe class="map" src="xxxx" @load="iframeLoad"/> </template> <script> export default { data(){ return { loadFinish:false } }, methods:{ postMessage() { if(!this.loadFinish)return this.$el.querySelector('.map').contentWindow.postMessage('主页面消息', '*'); }, iframeLoad(){ this.loadFinish = true } } } </script>子窗口接收消息子窗口接收消息和父窗口接收消息一样的 window.addEventListener('message', 事件名, false);window.addEventListener( 'message', (e) => { try { if (e.data?.type === '自定义事件类型') { console.log('查看发送过来的数据', e.data) } } catch {} }, false )参考文章:https://www.cnblogs.com/grow-up-up/p/16981279.html
2024年12月06日
4 阅读
0 评论
0 点赞
1
2