Fragment是React中一个内置组件,允许将多个子元素分组渲染而不添加额外的DOM节点。它类似于一个透明的容器,不会在最终DOM结构中生成实际标签,仅用于逻辑上的包裹。Fragment的主要优势在于避免不必要的div嵌套,保持DOM结构简洁。
<>>
代替
,但不支持key属性。
可用于列表渲染时避免警告。避免额外DOM嵌套
当组件需要返回多个相邻元素但父组件要求单一根节点时,Fragment可以替代div包裹。
function Table() {
return (
Column 1
Column 2
);
}
列表渲染优化
在map循环中,Fragment的key属性可以避免元素被包裹在div中。
{items.map(item => (
{item.name}
{item.price}
))}
条件渲染组合
需要分组渲染条件分支中的多个元素时,Fragment能保持结构清晰。
{isLoading ? (
) : (
)}
<>>
不支持任何属性(如key或children)。
虚拟节点。React.forwardRef
是 React 提供的高阶组件(HOC),用于将 ref
属性自动向下传递到子组件中的特定 DOM 元素或组件实例。它解决了一个常见问题:在自定义组件中直接使用 ref
时,ref
会绑定到组件实例而非底层 DOM 节点。
跨组件传递 ref
允许父组件直接访问子组件内部的 DOM 节点或组件实例,无需通过 props
中转。
支持底层 DOM 操作
在封装输入框、模态框等需要直接操作 DOM 的组件时,forwardRef
提供了一种标准化的传递方式。
兼容第三方库
许多库(如 react-router
或 material-ui
)依赖 forwardRef
实现组件扩展,确保 ref
能正确传递到目标节点。
const ChildComponent = React.forwardRef((props, ref) => {
return ;
});
// 父组件中直接使用 ref
function ParentComponent() {
const inputRef = useRef();
return ;
}
const FancyInput = React.forwardRef((props, ref) => {
return ;
});
function App() {
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus(); // 直接操作子组件的 DOM
}, []);
return ;
}
若组件经过 HOC 包装,forwardRef
能确保 ref
穿透多层包装:
const EnhancedComponent = React.forwardRef((props, ref) => {
return ;
});
ref
参数,必须通过 forwardRef
包裹才能处理 ref
。displayName
以方便调试: ChildComponent.displayName = 'ForwardRef(ChildComponent)';
props
控制组件行为,仅在需要直接操作 DOM 或实例时使用。state
变化触发的生命周期当组件内部的 state
发生变化时,会触发以下生命周期方法(以类组件为例):
shouldComponentUpdate(nextProps, nextState) // 判断是否需要更新
componentWillUpdate(nextProps, nextState) // 即将更新(旧版 React)
render() // 重新渲染
componentDidUpdate(prevProps, prevState) // 更新完成
props
变化触发的生命周期当父组件传递的 props
发生变化时,会触发以下生命周期方法:
componentWillReceiveProps(nextProps) // 即将接收新 props(旧版 React)
shouldComponentUpdate(nextProps, nextState) // 判断是否需要更新
componentWillUpdate(nextProps, nextState) // 即将更新(旧版 React)
render() // 重新渲染
componentDidUpdate(prevProps, prevState) // 更新完成
props
变化会多触发 componentWillReceiveProps
(或新版 getDerivedStateFromProps
),而 state
变化不会。props
更新通常来自父组件,state
更新来自组件自身。对于函数组件,props
或 state
变化都会导致整个函数重新执行:
function MyComponent(props) {
const [state, setState] = useState();
// 任何 props 或 state 变化都会触发重新执行
return {props.value} - {state};
}
在 React 16.3+ 版本中:
componentWillReceiveProps
被 getDerivedStateFromProps
取代componentWillUpdate
被 getSnapshotBeforeUpdate
取代static getDerivedStateFromProps(props, state) {
// 返回新的 state 或 null
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// 返回 snapshot 或 null
}
在Vue中自定义指令分为全局注册和局部注册两种方式。全局注册通过Vue.directive
实现,局部注册在组件选项中通过directives
实现。
全局注册示例:
Vue.directive('focus', {
inserted: function(el) {
el.focus()
}
})
局部注册示例:
export default {
directives: {
focus: {
inserted: function(el) {
el.focus()
}
}
}
}
自定义指令提供多个生命周期钩子函数:
bind
:指令第一次绑定到元素时调用inserted
:被绑定元素插入父节点时调用update
:所在组件的VNode更新时调用componentUpdated
:所在组件及子组件VNode全部更新后调用unbind
:指令与元素解绑时调用Vue.directive('demo', {
bind: function(el, binding, vnode) {},
inserted: function(el, binding, vnode) {},
update: function(el, binding, vnode, oldVnode) {},
componentUpdated: function(el, binding, vnode, oldVnode) {},
unbind: function(el, binding, vnode) {}
})
钩子函数会接收到以下参数:
el
:指令绑定的DOM元素binding
:包含指令信息的对象vnode
:Vue编译生成的虚拟节点oldVnode
:上一个虚拟节点binding对象包含以下属性:
{
name: '指令名',
value: '指令的绑定值',
oldValue: '指令绑定的前一个值',
expression: '字符串形式的指令表达式',
arg: '传给指令的参数',
modifiers: '包含修饰符的对象'
}
表单自动聚焦
Vue.directive('focus', {
inserted: function(el) {
el.focus()
}
})
权限控制
Vue.directive('permission', {
inserted: function(el, binding) {
if(!hasPermission(binding.value)) {
el.parentNode.removeChild(el)
}
}
})
图片懒加载
Vue.directive('lazy', {
inserted: function(el, binding) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
el.src = binding.value
observer.unobserve(el)
}
})
})
observer.observe(el)
}
})
拖拽功能
Vue.directive('drag', {
bind: function(el) {
el.onmousedown = function(e) {
const disX = e.clientX - el.offsetLeft
const disY = e.clientY - el.offsetTop
document.onmousemove = function(e) {
el.style.left = e.clientX - disX + 'px'
el.style.top = e.clientY - disY + 'px'
}
document.onmouseup = function() {
document.onmousemove = null
document.onmouseup = null
}
}
}
})
指令的参数可以是动态的,通过方括号语法实现动态参数:
Vue.directive('demo', {
bind: function(el, binding) {
el.style.position = 'fixed'
const s = binding.arg === 'left' ? 'left' : 'top'
el.style[s] = binding.value + 'px'
}
})
指令可以通过组件实例访问数据和方法:
Vue.directive('demo', {
bind: function(el, binding, vnode) {
const vm = vnode.context
vm.$watch(binding.expression, function(value) {
el.innerHTML = value
})
}
})
$nextTick
是Vue提供的一个异步方法,用于在下次DOM更新循环结束之后执行延迟回调。其主要作用是确保在DOM更新完成后执行某些操作,避免因DOM未及时更新导致的问题。
this.$nextTick(() => {
// DOM更新完成后执行的操作
})
$nextTick
会在以下场景触发:
$nextTick
的回调会在DOM更新完成后执行。this.message = 'new message'
this.$nextTick(() => {
console.log('DOM updated')
})
mounted
等生命周期钩子中使用时,可以确保回调在初始DOM渲染完成后执行。mounted() {
this.$nextTick(() => {
console.log('DOM is fully rendered')
})
}
$nextTick
可以获取更新后的DOM状态。this.$refs.childComponent.someMethod()
this.$nextTick(() => {
// 子组件更新后的操作
})
this.showElement = true
this.$nextTick(() => {
const height = this.$refs.element.clientHeight
console.log(height)
})
this.$nextTick(() => {
new ThirdPartyLibrary(this.$refs.container)
})
$nextTick
利用了JavaScript的事件循环机制,通常基于微任务(如Promise)实现,确保回调在当前同步任务完成后、下一次DOM更新前执行。
React 中的 setState
和 replaceState
是用于更新组件状态的两种方法,但它们在行为上有显著的不同。
setState
是 React 中最常用的状态更新方法。它会将新状态 合并 到当前状态中,而不是完全替换。这意味着未在新状态中指定的属性会保留其当前值。
this.setState({ key: 'newValue' });
setState
是异步的,React 可能会批量处理多个 setState
调用以提高性能。this.setState({ count: this.state.count + 1 }, () => {
console.log('状态已更新');
});
replaceState
是 React 的旧 API,现已废弃(React 16+ 不再支持)。它的行为与 setState
不同,会 完全替换 当前状态,而不是合并。
this.replaceState({ newKey: 'newValue' });
replaceState
是同步的,会立即更新状态。replaceState
被移除,建议使用 setState
或其他状态管理方案。setState
合并状态,replaceState
完全替换状态。setState
是异步的,replaceState
是同步的(但已废弃)。replaceState
已废弃,setState
是推荐的方式。如果需要在现代 React 中完全替换状态,可以通过 setState
直接传递完整的新状态对象:
this.setState({ ...newState });
或者使用函数式 setState
确保完全替换:
this.setState(() => ({ ...newState }));