封装组件,各个组件维护自己的状态和UI,当状态变更,自动重新渲染整个组件
(JavaScript XML)实际上,它只是为 React.createElement() 函数提供语法糖,为我们提供了在 JavaScript 中使用类 HTML 模板语法的能力。
原理:jsx语法最终会被babel
编译成为React.createElement()
方法,createElement方法会生成虚拟的DOM节点对象,再由ReactDOM.render()
函数生成真实DOM,插入到对应节点中去
//jsx
<div className="wang.haoyu">hellodiv>
经过bale
编译之后的:
React.createElement("div", {
className:'wang.haoyu'
}, "hello");
render()
生成真实Dom
ReactDOM.render(<App />, document.getElementById('root'));
// 真正渲染方法
function render(vDom, el) {
const newDom = createDom(vDom);
el.appendChild(newDom);
}
// 先不考虑自定义组件
function createDom(vDom) {
const { type, props } = vDom;
let dom;
// 文本节点
if (type === REACT_TEXT) {
dom = document.createTextNode(props.content);
} else {
dom = document.createElement(type);
}
// 更新属性
if (props) {
// 更新跟节点Dom属性
....
}
// 记录挂载节点
vDom.__dom = dom;
return dom;
}
React.Component
需要更多的代码;setSate()
React.Component
中;new
操作符将其实例化,然后调用 render
方法// 函数组件
function SayHi() {
return <p>Hello, React</p>
}
// React 内部
const result = SayHi(props) // » Hello, React
// 类组件
class SayHi extends React.Component {
render() {
return <p>Hello, React</p>
}
}
// React 内部
const instance = new SayHi(props) // » SayHi {}
const result = instance.render()
HTML | REACT | |
---|---|---|
写法 | 全小写onclick | 小驼峰onClick |
阻止默认行为 | 可以直接 return false |
必须用 event.preventDefault(); |
方法调用 | οnclick=“activateLasers()” | onClick={activateLasers} |
传参 | activateLasers(abc) | onClick={(e)=>this.handleClick(e, ‘aa’)} / onClick={this.handleClick.bind(this, ‘aa’)} |
SyntheticEvent
是基于浏览器本地事件的跨浏览器包装。它的 API 与浏览器的本地事件相同,包括 stopPropagation()
和 preventDefault()
,但事件在所有浏览器中的表现均一致
虚拟dom: 数据发生更改时,会计算旧的 DOM 与新的 DOM 之间的差异,更新真正改变的那部分到真实的 DOM。
作用: 虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。
实现步骤:
1. 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中;
2. 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异;
3. 把新旧Dom树之间差异,应用到真正的 DOM 树上,视图就更新了。
在constructor 方法中,您可以对组件进行初始化,为属性设置默认值,并绑定事件处理程序
调用 super()
时传递 props
主要是为了拿到 this 的引用
,在子类的构造器中访问 this.props
class MyComponent extends React.Component {
constructor(props) {
//1.
super(props);
console.log(this.props); // 打印 { name: 'John', age: 42 }
// 2.但是 props 参数依然可以访问
super();
console.log(this.props); // 打印 undefined
console.log(props); // 打印 { name: 'John', age: 42 }
}
//3.在 constructor 之外没有影响
render() {
console.log(this.props); // 打印 { name: 'John', age: 42 }
}
}
state
是一种数据结构,用于组件挂载时所需数据的默认值。state
是可变的,可以被修改,多数时候作为用户时间行为的结果。props
是组件的配置,由父组件传递给子组件,props
是不可变的,不能被修改钩子函数 | 触发时机 | 作用 |
---|---|---|
constructor () | 创建组件时 | 1,初始化state; 2.为事件处理程序绑定this |
render () | 每次组件渲染都会触发 | 渲染UI( 注意不能调用setState() ) |
componentDidMount () | 组件挂载(完成DOM渲染)后 | 1.发送网络请求; 2.DOM操作 |
render() | 每次组件渲染都会触发 | 渲染UI |
componetDidUpdate() | 组件更新(完成Dom渲染)后 | 1.发送网络请求;2.Dom操作 |
componentWillUnMount() | 组件卸载之前 | 完成组件的卸载和数据的销毁 |
其他钩子:
shouldCompontentUpdate(nextProps, nextState)
是否要更新组件时触发的函数。
父组件的重新渲染会导致组件重新渲染,可以通过此钩子函数限制子组件是否需要更新,return false
可以阻止组件更新
useState
状态管理useEffect
生命周期管理(函数组件Didmount、DidUpdate、WillUnmount时会触发;也可以监听某个state)useContext
共享状态数据useMemo
缓存值useRef
获取Dom 操作seCallback
缓存函数useReducer
redux 相似useImperativeHandle
子组件暴露值/方法useLayoutEffect
完成副作用操作,会阻塞浏览器绘制// 1.状态钩子 useState
const [data, setData] = useState('微信公众号: 前端自学社区')
// 2.生命周期钩子 useEffect
useEffect(() => {
//默认会执行
// 这块相当于 class 组件 生命周期的 compoentDidmount compoentDidUpdate
console.log(`num: ${num}`)
console.log(`count: ${count}`)
// 组件在卸载时,将会执行 return 中内容
return () => {
// 相当于 class 组件生命周期的 componentWillUnMount
console.log('测试')
}
}, [num])
// 3.传值钩子-数据共享 useContext
// Context.js
import React from 'react';
export const MyContext = React.createContext();
// index.js
const Son = () => {
const res = useContext(MyContext)
return (<h1>{res.code}</h1>)
}
export default () => {
return (
<MyContext.Provider value={{code:200,title:'添加数据成功'}}>
<Son/>
</MyContext.Provider>
)
}
// 4.useMemo
React ,只支持把数据从 state 上传输到页面,但是无法实现数据自动从页面传输到 state 中。
可以通过change
事件修改target.value
,实现双向绑定的方法:
const [msg, setMsg] = useState('xxxxx')
<input type="text" value={msg} onChange={(e) => {
setMsg(e.target.value)
}} />
flux
最大的特点就是单向数据流
1. 用户访问view
2. view发出用户的action
3. dispatcher收到action,更新store
4. store更新后,触发change事件
5. view收到change事件后,更新页面
服务端渲染, SSR (Server-side Rendering)
,顾名思义,就是在浏览器发起页面请求后由服务端完成页面的 HTML 结构拼接,返回给浏览器解析后能直接构建出有内容的页面。
流程:
1、服务器端使用 renderToString 直接渲染出包含页面信息的静态 html。
2、客户端根据渲染出的静态 html 进行二次渲染,做一些绑定事件等操作。
shouldComponentUpdate
来避免不必要的 dom 操作。组件复用问题、性能问题、兼容性问题
React遵循的协议是“BSD许可证 + 专利开源协议”,是否可以自由的使用React,取决于你的产品与FaceBook是否有竞争关系
在比较新旧虚拟Dom时,如果我们不希望某个组件刷新,或者刷后和之前的一样,可以用这个函数直接告诉React,省去Diff
操作,进一步提高效率 。
在组件的render
方法中返回null
。(不会影响触发生命周期方法)
npm install react-router-dom
安装后,直接引入,并用路由组件包裹根组件
// index.js
import ReactDOM from 'react-dom/client'
// 1. 从 react-router-dom 中引入 HashRouter
import {HashRouter} from 'react-router-dom'
import App from './App'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
// 2. 包裹根组件,给整个应用开启路由功能,就能监听路径的改变,并且将相应的路径传递给子组件
<HashRouter>
<App />
</HashRouter>
)
//js传参
this.props.history.push('/discover/playlist');
this.props.match.params.xxx(此处为id)
this.props.location.query
//umi
import { history } from 'umi';
history.push('/' + e.key);
//标签
<Routes><Route path='/about' element={<About/>} /></Routes>
<Link to='/about'>关于</Link>
<NavLink to='/about' className='about-link'>关于</NavLink>
<Navigate to='/home' /> //重定向
history
模式:
hash
模式:
PS:两种模式的区别
hash
模式背后的原理是onhashchange
事件,可以在window
对象上监听这个事件。history
利用了 H5 中新增的 pushState()
和 replaceState()
方法。hash
模式#
后面的变化并不会导致浏览器向服务器发出请求,不会刷新请求、重新加载页面; history
模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致hash
兼容性会比history
模式好,因为history
模式是依赖HTML5
的Web History API
,特定浏览器才会支持;hash
模式不利SEO
,爬虫不会读取到#
后的内容,因为会把 #
当作是锚点,这也导致会与网站里的锚点功能产生冲突hash
模式,页面刷新后会导致路由state参数
丢失
和
的用法相似,都用于设置路径的跳转,最终会被渲染成
元素.
是在
的基础上增加了一些样式属性,被选中时会自动添加上 class='active'
用于路由的重定向。只要这个组件一显示,就会自动跳转到 to 属性指定的路径中。useNavigate()
:用于在函数组件中进行手动跳转。
useParams()
:用于在函数组件中获取动态路由的参数。
useSearchParams()
:用于在函数组件中获取路径中的search(?号后面的)参数。
useLocation()
:用于在函数组件中获取 location。
useRoutes()
:用于在函数组件中通过配置的方式使用路由 {useRoutes(routers)}
路由懒加载不是 React Router
提供的功能,而是借助 React 提供的 React.lazy()
来实现。 React.lazy()
需要传入一个函数作为参数,在函数内部可以使用 import()
来异步加载文件。
const About = React.lazy(() => import('./components/About'))
function App() {
return (
<div>
<div>
<Link to='/home'>首页</Link>
<Link to='/about?userId=123'>关于</Link>
</div>
<Routes>
<Route path='/home' element={<Home />} />
<Route path='/about' element={<About />} />
</Routes>
</div>
)
}
export default App
React Router 基础、组件、钩子函数
访问:useSelector
钩子函数
调用:在Redux Toolkit
的 reducers
中写计算逻辑,在组件中用useDispatch()
钩子函数调用
// 访问
const count = useSelector(state => state.counter.value)
// 调用
// 1.在Redux Toolkit中配置
export const counterSlice = createSlice({
name: 'counter',
initialState: {value: 0},
reducers: {
increment: (state, action) => {
state.value += 1
},
}
})
//2.全局注册 store
import {configureStore} from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
export default configureStore({
reducer: {
counter: counterReducer
}
})
// 3.引入 调用dispatch方法,并传入一个action对象
import {increment} from './counterSlice'
onClick={
() => dispatch(increment({ name: '参数' }))
}
view
:基于当前状态的视图声明性描述state
:驱动应用的真实数据源头actions
:根据用户输入在应用程序中发生的事件,并触发状态更新reducer
: reducer 是一个函数,可以将 reducer 视为一个事件监听器,它根据接收到的 action(事件)类型处理事件。Redux 中文官网