文章开始前,先划划水~
今日份开心:
今天看之前发布的按钮npm包下载量有162次,早知道好好做了
今日份不开心:
爬岗位看到一个整体都挺满意的岗位,公司位置和发展大方向都好喜欢!!!不过他的技术栈是react和nextjs的优先,这两个好像都没学过。QAQ~~~~这就是传说中的书到用时方恨少么?还有hybird跨段经验的优先,这块也没有学过,啊!我怎么那么菜!!(狗叫+emo中!!!)
现在学估计也来不及了,但是为了防止以后错失良机,还是学学吧(最近其实超级想去学游戏引擎!)
先学学react吧,直接用黑马的教案好了,前端我好像主要都是看的黑马的教程,黑马的主要特点就是讲的比较细,教案比较完整,比较适合没基础的新人,有基础的老人看的话可能会觉得又臭又长哈哈。
正片开始~!!
好几个月没学大的新技术栈了。学习启动!!!!!(当写到这个时候我还是个react小白)
其实以上的优势我觉得vue也具备···
项目开始
npx create-react-app react-basic
用惯了Vue看这个确实有点不习惯
知识点1.什么是JSX
JS和XML(HTML)的缩写,是react中创建UI的方式,指的是在JS中编写HTML
应该就是用JS创建实例吧,并不是标准的JS写法,其实还是每太理解作用
但看到下一个例子的时候就有点恍然大明白了,真的是吧JS和html融合了哈哈
基础实例渲染+条件渲染(相当于vue中的v-for和v-if吧)
知识点2.react事件绑定
讲了基本用法和如何传递事件对象和参数
function App(){const clickHandler = ()=>{console.log('button按钮点击了')}return (<button onClick={clickHandler}>click me</button>)
}function App(){const clickHandler = (e)=>{console.log('button按钮点击了', e)}return (<button onClick={clickHandler}>click me</button>)
}function App(){const clickHandler = (name)=>{console.log('button按钮点击了', name)}return (<button onClick={()=>clickHandler('jack')}>click me</button>)
}function App(){const clickHandler = (name,e)=>{console.log('button按钮点击了', name,e)}return (<button onClick={(e)=>clickHandler('jack',e)}>click me</button>)
}
知识点3.组件使用+小案例
useState,这段代码我看着有点蒙。哈哈,先往下继续看
从状态修改规则来看,要替换才能重新渲染,只用count++这种修改而非替换,无法重新渲染
现在明白了,定义时第二个为替换函数,专门用来替换状态变量,即set个全新的对象
下面有个实例,npm run start 启动下项目
不得不说启动的速度相比vue慢了不少、、
只看文档不看视频学还真有点费劲。火速去看了眼,发现是忘记了npm install下载依赖了·····
厘一下代码······主要是在app.js完成结构,在app.scss完成样式,json就是模拟数据库使用
import { useState } from 'react'
import './App.scss'
import avatar from './images/bozai.png'
import orderBy from 'lodash/orderBy'/*** 评论列表的渲染和操作** 1. 根据状态渲染评论列表* 2. 删除评论*/// 评论列表数据
const defaultList = [{// 评论idrpid: 3,// 用户信息user: {uid: '13258165',avatar: '',uname: '周杰伦',},// 评论内容content: '哎哟,不错哦',// 评论时间ctime: '10-18 08:15',like: 88,},{rpid: 2,user: {uid: '36080105',avatar: '',uname: '许嵩',},content: '我寻你千百度 日出到迟暮',ctime: '11-13 11:29',like: 88,},{rpid: 1,user: {uid: '30009257',avatar,uname: '黑马前端',},content: '学前端就来黑马',ctime: '10-19 09:00',like: 66,},
]
// 当前登录用户信息
const user = {// 用户iduid: '30009257',// 用户头像avatar,// 用户昵称uname: '黑马前端',
}/*** 导航 Tab 的渲染和操作** 1. 渲染导航 Tab 和高亮* 2. 评论列表排序* 最热 => 喜欢数量降序* 最新 => 创建时间降序*/// 导航 Tab 数组
const tabs = [{ type: 'hot', text: '最热' },{ type: 'time', text: '最新' },
]const App = () => {// 导航 Tab 高亮的状态const [activeTab, setActiveTab] = useState('hot')const [list, setList] = useState(defaultList)// 删除评论const onDelete = rpid => {// 如果要删除数组中的元素,需要调用 filter 方法,并且一定要调用 setList 才能更新状态setList(list.filter(item => item.rpid !== rpid))}// tab 高亮切换const onToggle = type => {setActiveTab(type)let newListif (type === 'time') {// 按照时间降序排序// orderBy(对谁进行排序, 按照谁来排, 顺序)newList = orderBy(list, 'ctime', 'desc')} else {// 按照喜欢数量降序排序newList = orderBy(list, 'like', 'desc')}setList(newList)}return (<div className="app">{/* 导航 Tab */}<div className="reply-navigation"><ul className="nav-bar"><li className="nav-title"><span className="nav-title-text">评论</span>{/* 评论数量 */}<span className="total-reply">{list.length}</span></li><li className="nav-sort">{/* 高亮类名: active */}{tabs.map(item => {return (<divkey={item.type}className={item.type === activeTab ? 'nav-item active' : 'nav-item'}onClick={() => onToggle(item.type)}>{item.text}</div>)})}</li></ul></div><div className="reply-wrap">{/* 发表评论 */}<div className="box-normal">{/* 当前用户头像 */}<div className="reply-box-avatar"><div className="bili-avatar"><img className="bili-avatar-img" src={avatar} alt="用户头像" /></div></div><div className="reply-box-wrap">{/* 评论框 */}<textareaclassName="reply-box-textarea"placeholder="发一条友善的评论"/>{/* 发布按钮 */}<div className="reply-box-send"><div className="send-text">发布</div></div></div></div>{/* 评论列表 */}<div className="reply-list">{/* 评论项 */}{list.map(item => {return (<div key={item.rpid} className="reply-item">{/* 头像 */}<div className="root-reply-avatar"><div className="bili-avatar"><imgclassName="bili-avatar-img"src={item.user.avatar}alt=""/></div></div><div className="content-wrap">{/* 用户名 */}<div className="user-info"><div className="user-name">{item.user.uname}</div></div>{/* 评论内容 */}<div className="root-reply"><span className="reply-content">{item.content}</span><div className="reply-info">{/* 评论时间 */}<span className="reply-time">{item.ctime}</span>{/* 评论数量 */}<span className="reply-time">点赞数:{item.like}</span>{user.uid === item.user.uid && (<spanclassName="delete-btn"onClick={() => onDelete(item.rpid)}>删除</span>)}</div></div></div></div>)})}</div></div></div>)
}export default App
知识点4.数据绑定
可以通过绑定Dom或者绑定值来实现实时渲染
知识点5.组件通信
父传子
function Son(props){return <div>{ props.name }</div>
}function App(){const name = 'this is app name'return (<div><Son name={name}/></div>)
}
子传父
function Son({ onGetMsg }){const sonMsg = 'this is son msg'return (<div>{/* 在子组件中执行父组件传递过来的函数 */}<button onClick={()=>onGetMsg(sonMsg)}>send</button></div>)
}function App(){const getMsg = (msg)=>console.log(msg)return (<div>{/* 传递父组件中的函数到子组件 */}<Son onGetMsg={ getMsg }/></div>)
}
兄弟组件:通过共同的父组件进行传递
// 1. 通过子传父 A -> App
// 2. 通过父传子 App -> Bimport { useState } from "react"function A ({ onGetAName }) {// Son组件中的数据const name = 'this is A name'return (<div>this is A compnent,<button onClick={() => onGetAName(name)}>send</button></div>)
}function B ({ name }) {return (<div>this is B compnent,{name}</div>)
}function App () {const [name, setName] = useState('')const getAName = (name) => {setName(name)}return (<div>this is App<A onGetAName={getAName} /><B name={name} /></div>)
}export default App
跨层传递
使用的createContext方法创建了一个上下文对象Ctx
顶层组件App通过Ctx.Provider组件提供数据
底层组件B用useContext钩子获取数据
// App -> A -> Bimport { createContext, useContext } from "react"// 1. createContext方法创建一个上下文对象const MsgContext = createContext()function A () {return (<div>this is A component<B /></div>)
}function B () {// 3. 在底层组件 通过useContext钩子函数使用数据const msg = useContext(MsgContext)return (<div>this is B compnent,{msg}</div>)
}function App () {const msg = 'this is app msg'return (<div>{/* 2. 在顶层组件 通过Provider组件提供数据 */}<MsgContext.Provider value={msg}>this is App<A /></MsgContext.Provider></div>)
}export default App
知识点6.副作用管理器 useEffect
用于Ajax请求 更改Dom操作等
在组件渲染完成后会从服务端获取数据并显示到页面
useEffect(()=>{},[])
import { useEffect, useState } from "react"function Son () {// 1. 渲染时开启一个定时器useEffect(() => {const timer = setInterval(() => {console.log('定时器执行中...')}, 1000)return () => {// 清除副作用(组件卸载时)clearInterval(timer)}}, [])return <div>this is son</div>
}function App () {// 通过条件渲染模拟组件卸载const [show, setShow] = useState(true)return (<div>{show && <Son />}<button onClick={() => setShow(false)}>卸载Son组件</button></div>)
}export default App
知识点7.自定义hook实现
自定义hook之前在学爬虫的时候了解过一些,就是自定义函数
// 封装自定义Hook// 问题: 布尔切换的逻辑 当前组件耦合在一起的 不方便复用// 解决思路: 自定义hookimport { useState } from "react"function useToggle () {// 可复用的逻辑代码const [value, setValue] = useState(true)const toggle = () => setValue(!value)// 哪些状态和回调函数需要在其他组件中使用 returnreturn {value,toggle}
}// 封装自定义hook通用思路// 1. 声明一个以use打头的函数
// 2. 在函数体内封装可复用的逻辑(只要是可复用的逻辑)
// 3. 把组件中用到的状态或者回调return出去(以对象或者数组)
// 4. 在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用function App () {const { value, toggle } = useToggle()return (<div>{value && <div>this is div</div>}<button onClick={toggle}>toggle</button></div>)
}export default App
规则:只能在组件或者其他自定义hook中实现,不能嵌套在if for 中
知识点8.redux
集中状态管理工具,应该和Vuex和pinia一个东西咯
npm i @reduxjs/toolkit react-redux
<button id="decrement">-</button>
<span id="count">0</span>
<button id="increment">+</button><script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script><script>// 定义reducer函数 // 内部主要的工作是根据不同的action 返回不同的statefunction counterReducer (state = { count: 0 }, action) {switch (action.type) {case 'INCREMENT':return { count: state.count + 1 }case 'DECREMENT':return { count: state.count - 1 }default:return state}}// 使用reducer函数生成store实例const store = Redux.createStore(counterReducer)// 订阅数据变化store.subscribe(() => {console.log(store.getState())document.getElementById('count').innerText = store.getState().count})// 增const inBtn = document.getElementById('increment')inBtn.addEventListener('click', () => {store.dispatch({type: 'INCREMENT'})})// 减const dBtn = document.getElementById('decrement')dBtn.addEventListener('click', () => {store.dispatch({type: 'DECREMENT'})})
</script>
import { createSlice } from '@reduxjs/toolkit'const counterStore = createSlice({// 模块名称独一无二name: 'counter',// 初始数据initialState: {count: 1},// 修改数据的同步方法reducers: {increment (state) {state.count++},decrement(state){state.count--}}
})
// 结构出actionCreater
const { increment,decrement } = counter.actions// 获取reducer函数
const counterReducer = counterStore.reducer// 导出
export { increment, decrement }
export default counterReducer
import { configureStore } from '@reduxjs/toolkit'import counterReducer from './modules/counterStore'export default configureStore({reducer: {// 注册子模块counter: counterReducer}
})
为react注入store
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
// 导入store
import store from './store'
// 导入store提供组件Provider
import { Provider } from 'react-redux'ReactDOM.createRoot(document.getElementById('root')).render(// 提供store数据<Provider store={store}><App /></Provider>
)
react组件使用store
修改store数据