React 会创建一个虚拟 DOM(virtual DOM)。当一个组件中的状态改变时,React 首先会通过 "diffing" 算法来标记虚拟 DOM 中的改变,第二步是调节(reconciliation),会用 diff 的结果来更新 DOM。
当 Facebook 第一次发布 React 时,他们还引入了一种新的 JS 方言 JSX
,将原始 HTML 模板嵌入到 JS 代码中。JSX 代码本身不能被浏览器读取,必须使用Babel
和webpack
等工具将其转换为传统的JS。很多开发人员就能无意识使用 JSX,因为它已经与 React 结合在一直了。
class MyComponent extends React.Component {
render() {
let props = this.props;
return (
<div className="my-component">
<a href={props.url}>{props.name}</a>
</div>
);
}
}
State 和 props 类似,但它是私有的,并且完全由组件自身控制。State 本质上是一个持有数据,并决定组件如何渲染的对象。
import React, { Component } from 'react';
import { is, fromJS } from 'immutable';
import ReactDOM from 'react-dom';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import './dialog.css';
let defaultState = {
alertStatus:false,
alertTip:"提示",
closeDialog:function(){},
childs:''
}
class Dialog extends Component{
state = {
...defaultState
};
// css动画组件设置为目标组件
FirstChild = props => {
const childrenArray = React.Children.toArray(props.children);
return childrenArray[0] || null;
}
//打开弹窗
open =(options)=>{
options = options || {};
options.alertStatus = true;
var props = options.props || {};
var childs = this.renderChildren(props,options.childrens) || '';
console.log(childs);
this.setState({
...defaultState,
...options,
childs
})
}
//关闭弹窗
close(){
this.state.closeDialog();
this.setState({
...defaultState
})
}
renderChildren(props,childrens) {
//遍历所有子组件
var childs = [];
childrens = childrens || [];
var ps = {
...props, //给子组件绑定props
_close:this.close //给子组件也绑定一个关闭弹窗的事件
};
childrens.forEach((currentItem,index) => {
childs.push(React.createElement(
currentItem,
{
...ps,
key:index
}
));
})
return childs;
}
shouldComponentUpdate(nextProps, nextState){
return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
}
render(){
return (
<ReactCSSTransitionGroup
component={this.FirstChild}
transitionName='hide'
transitionEnterTimeout={300}
transitionLeaveTimeout={300}>
<div className="dialog-con" style={this.state.alertStatus? {display:'block'}:{display:'none'}}>
{this.state.childs} </div>
</ReactCSSTransitionGroup>
);
}
}
let div = document.createElement('div');
let props = {
};
document.body.appendChild(div);
let Box = ReactD
子类:
//子类jsx
import React, { Component } from 'react';
class Child extends Component {
constructor(props){
super(props);
this.state = {date: new Date()};
}
showValue=()=>{
this.props.showValue && this.props.showValue()
}
render() {
return (
<div className="Child">
<div className="content">
Child <button onClick={this.showValue}>调用父的方法</button>
</div>
</div>
);
}
}
export default Child;
css:
.dialog-con{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.3);
}
权限控制
利用高阶组件的 条件渲染 特性可以对页面进行权限控制,权限控制一般分为两个维度:页面级别 和 页面元素级别
// HOC.js
function withAdminAuth(WrappedComponent) {
return class extends React.Component {
state = {
isAdmin: false,
}
async componentWillMount() {
const currentRole = await getCurrentUserRole();
this.setState({
isAdmin: currentRole === 'Admin',
});
}
render() {
if (this.state.isAdmin) {
return <WrappedComponent {...this.props} />;
} else {
return (<div>您没有权限查看该页面,请联系管理员!</div>);
}
}
};
}
// 使用
// pages/page-a.js
class PageA extends React.Component {
constructor(props) {
super(props);
// something here...
}
componentWillMount() {
// fetching data
}
render() {
// render page with data
}
}
export default withAdminAuth(PageA);
可能你已经发现了,高阶组件其实就是装饰器模式在 React 中的实现:通过给函数传入一个组件(函数或类)后在函数内部对该组件(函数或类)进行功能的增强(不修改传入参数的前提下),最后返回这个组件(函数或类),即允许向一个现有的组件添加新的功能,同时又不去修改该组件,属于 包装模式(Wrapper Pattern) 的一种。
什么是装饰者模式:在不改变对象自身的前提下在程序运行期间动态的给对象添加一些额外的属性或行为
可以提高代码的复用性和灵活性。
再对高阶组件进行一个小小的总结:
封装组件的原则
封装原则
1、单一原则:负责单一的页面渲染
2、多重职责:负责多重职责,获取数据,复用逻辑,页面渲染等
3、明确接受参数:必选,非必选,参数尽量设置以_开头,避免变量重复
4、可扩展:需求变动能够及时调整,不影响之前代码
5、代码逻辑清晰
6、封装的组件必须具有高性能,低耦合的特性
7、组件具有单一职责:封装业务组件或者基础组件,如果不能给这个组件起一个有意义的名字,证明这个组件承担的职责可能不够单一,需要继续抽组件,直到它可以是一个独立的组件即可
React 16.x的三大新特性 Time Slicing、Suspense、 hooks
(1)React16.8 加入hooks,让React函数式组件更加灵活,hooks之前,React存在很多问题:
hooks很好的解决了上述问题,hooks提供了很多方法
(2)React16.9
(3)React16.13.0
参考 前端进阶面试题详细解答
async/await是ES7标准中的新特性。如果是使用React官方的脚手架创建的项目,就可以直接使用。如果是在自己搭建的webpack配置的项目中使用,可能会遇到 regeneratorRuntime is not defined 的异常错误。那么我们就需要引入babel,并在babel中配置使用async/await。可以利用babel的 transform-async-to-module-method 插件来转换其成为浏览器支持的语法,虽然没有性能的提升,但对于代码编写体验要更好。
本质上来说JSX是React.createElement(component, props, ...children)
方法的语法糖。在React 17之前,如果使用了JSX,其实就是在使用React, babel
会把组件转换为 CreateElement
形式。在React 17之后,就不再需要引入,因为 babel
已经可以帮我们自动引入react。
父传子
在父组件中用useState声明数据
const [ data, setData ] = useState(false)
把数据传递给子组件
<Child data={data} />
子组件接收
export default function (props) {
const { data } = props
console.log(data)
}
子传父
子传父可以通过事件方法传值,和父传子有点类似。
在父组件中用useState声明数据
const [ data, setData ] = useState(false)
把更新数据的函数传递给子组件
<Child setData={setData} />
子组件中触发函数更新数据,就会直接传递给父组件
export default function (props) {
const { setData } = props
setData(true)
}
如果存在多个层级的数据传递,也可依照此方法依次传递
// 多层级用useContext
const User = () => {
// 直接获取,不用回调
const { user, setUser } = useContext(UserContext);
return <Avatar user={user} setUser={setUser} />;
};
一个 reducer 是一个纯函数,该函数以先前的 state 和一个 action 作为参数,并返回下一个 state。
高阶函数:如果一个函数接受一个或多个函数作为参数或者返回一个函数就可称之为高阶函数。
高阶组件:如果一个函数 接受一个或多个组件作为参数并且返回一个组件 就可称之为 高阶组件。
react 中的高阶组件
React 中的高阶组件主要有两种形式:属性代理和反向继承。
属性代理 Proxy
props
state
ref
访问到组件实例WrappedComponent
反向继承
会发现其属性代理和反向继承的实现有些类似的地方,都是返回一个继承了某个父类的子类,只不过属性代理中继承的是 React.Component
,反向继承中继承的是传入的组件 WrappedComponent
。
反向继承可以用来做什么:
1.操作 state
高阶组件中可以读取、编辑和删除WrappedComponent
组件实例中的state
。甚至可以增加更多的state
项,但是非常不建议这么做因为这可能会导致state
难以维护及管理。
function withLogging(WrappedComponent) {
return class extends WrappedComponent {
render() {
return (
<div>;
<h2>;Debugger Component Logging...<h2>;
<p>;state:<p>;
<pre>;{JSON.stringify(this.state, null, 4)}<pre>;
<p>props:<p>;
<pre>{JSON.stringify(this.props, null, 4)}<pre>;
{super.render()}
<div>;
);
}
};
}
2.渲染劫持(Render Highjacking)
条件渲染通过 props.isLoading 这个条件来判断渲染哪个组件。
修改由 render() 输出的 React 元素树
父传子——在调用子组件上绑定,子组件中获取this.props
子传父——引用子组件的时候传过去一个方法,子组件通过this.props.methed()传过去参数
connection
展示组件关心组件看起来是什么。展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只关心 UI 状态而不是数据的状态。
容器组件则更关心组件是如何运作的。容器组件会为展示组件或者其它容器组件提供数据和行为(behavior),它们会调用 Flux actions
,并将其作为回调提供给展示组件。容器组件经常是有状态的,因为它们是(其它组件的)数据源。
refs
属性不能透传(如果你向一个由高阶组件创建的组件的元素添加ref
引用,那么ref
指向的是最外层容器组件实例的,而不是被包裹的WrappedComponent
组件。)我们知道反向继承的渲染劫持可以控制 WrappedComponent
的渲染过程,也就是说这个过程中我们可以对 elements tree
、 state
、 props
或 render()
的结果做各种操作。
但是如果渲染 elements tree
中包含了 function 类型的组件的话,这时候就不能操作组件的子组件了。
JSX 是 JavaScript 语法的一种语法扩展,并拥有 JavaScript 的全部功能。JSX 生产 React "元素",你可以将任何的 JavaScript 表达式封装在花括号里,然后将其嵌入到 JSX 中。在编译完成之后,JSX 表达式就变成了常规的 JavaScript 对象,这意味着你可以在 if
语句和 for
循环内部使用 JSX,将它赋值给变量,接受它作为参数,并从函数中返回它。
在调用 super() 方法之前,子类构造函数无法使用this引用,ES6 子类也是如此。
将 props 参数传递给 super() 调用的主要原因是在子构造函数中能够通过this.props来获取传入的 props
传递了props
class MyComponent extends React.Component {
constructor(props) {
super(props);
console.log(this.props); // { name: 'sudheer',age: 30 }
}
}
没传递 props
class MyComponent extends React.Component {
constructor(props) {
super();
console.log(this.props); // undefined
// 但是 Props 参数仍然可用
console.log(props); // Prints { name: 'sudheer',age: 30 }
}
render() {
// 构造函数外部不受影响
console.log(this.props); // { name: 'sudheer',age: 30 }
}
}
this
值(在构造函数中是新对象;在严格模式下,函数调用中的 this
是未定义的;如果函数被称为“对象方法”,则为基础对象等),但箭头函数不会,它会使用封闭执行上下文的 this
值。this
的值增加dangerouslySetInnerHTML属性,并且传入对象的属性名叫_html
function Component(props){
return <div dangerouslySetInnerHTML={{_html:'<span>你好</span>'}}>
</div>
}
Refs 可以用于获取一个 DOM 节点或者 React 组件的引用。何时使用 refs 的好的示例有管理焦点/文本选择,触发命令动画,或者和第三方 DOM 库集成。你应该避免使用 String 类型的 Refs 和内联的 ref 回调。Refs 回调是 React 所推荐的。
<Route path="/home" component={App} onEnter={(nexState,replace)=>{
if(nexState.location.pathname!=='/'){
var sid = UtilsMoudle.getSidFromUrl(nexState);
if(!sid){
replace("/")
}else{
console.log(sid);
}
}
}}>
// 需要在入口处添加自定义权限指令v-auth,显示可操作组件
Vue.directive('auth', {
bind: function (el, binding, vnode) {
// 用户权限表
const rules = auths
for (let i = 0; i < rules.length; i++) {
const item = rules[i]
if(!binding.value || (binding.value == item.auth)){
// 权限允许则显示组件
return true
}
}
// 移除组件
el.parentNode.removeChild(el)
}
})
// 使用
<template>
<div>
<Button v-auth="admin_user_add">添加用户</Button>
<Button v-auth="admin_user_del">删除用户</Button>
<Button v-auth="admin_user_edit">编辑用户</Button>
</div>
</template>
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。