简介
JSX(Javascript Syntax Extension)是 JavaScrip 的一种扩展语法,使我们能用类似HTML的形式去编写JS。
作用
JSX并不是React等框架的必要条件。它能定义简洁且我们熟悉的包含属性的树状结构语法,相对于方法调用和对象字面量的书写形式,更易于阅读。
使用方法
自定义组件(classes)的标签名需以大写字母开头,除此以外所有标签都按照HTML标签(string)渲染。JSX本身还是JavaScript,所以class、for等标识符不能用作标签属性名。可以使用className``htmlFor来代替。
// 渲染HTML
const divEl = <div className="demo">demo</div>
React.render(divEl, document.body);
// 渲染自定义组件
const myComp = <MyComp sthProps={true} />
React.render(myComp, document.body);
复制代码
JavaScript表达式
// 属性表达式
const a = <div id={window.inited && "active"}></div>;
// 子节点表达式
const content = <div>{ window.inited ? <img src="" /> : <p>loading</p> }</div>;
// 遍历
const list = <ul>{ [0,1,2].map(it=><li>{it}</li>) }</ul>
复制代码
Babel与JSX
React发明了JSX,利用HTML语法来穿件虚拟DOM。当遇到<,JSX就当HTML解析,遇到{就当JavaScript解析。Babel 是React团队选择的在使用React过程中转换ES5以上和JSX为ES5语句的工具。
通过babel预编译和解析后,将结果通过React.createElement完成替换,继而创建成一个个包含节点所有信息的reactElement,即虚拟DOM。
React.createElement的源码中做了如下几件事
- 处理config,把除了保留属性外的其他config赋值给props
- 把children处理后赋值给props.children
- 处理defaultProps
- 调用ReactElement返回一个jsx对象(virtual-dom)
/**
React的创建元素方法
*/
export function createElement(type, config, children) {
// propName 变量用于储存后面需要用到的元素属性
let propName;
// props 变量用于储存元素属性的键值对集合
const props = {};
// key、ref、self、source 均为 React 元素的属性,此处不必深究
let key = null;
let ref = null;
let self = null;
let source = null;
// config 对象中存储的是元素的属性
if (config != null) {
// 进来之后做的第一件事,是依次对 ref、key、self 和 source 属性赋值
if (hasValidRef(config)) {
ref = config.ref;
}
// 此处将 key 值字符串化
if (hasValidKey(config)) {
key = "" + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// 接着就是要把 config 里面的属性都一个一个挪到 props 这个之前声明好的对象里面
for (propName in config) {
// 筛选出可以提进 props 对象里的属性
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
// childrenLength 指的是当前元素的子元素的个数,减去的 2 是 type 和 config 两个参数占用的长度
const childrenLength = arguments.length - 2;
// 如果抛去type和config,就只剩下一个参数,一般意味着文本节点出现了
if (childrenLength === 1) {
// 直接把这个参数的值赋给props.children
props.children = children;
// 处理嵌套多个子元素的情况
} else if (childrenLength > 1) {
// 声明一个子元素数组
const childArray = Array(childrenLength);
// 把子元素推进数组里
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
// 最后把这个数组赋值给props.children
props.children = childArray;
}
// 处理 defaultProps
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
// 最后返回一个调用ReactElement执行方法,并传入刚才处理过的参数
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props
);
}
function ReactElement(type, key, ref, self, source, owner, props) {
const element = {
$$typeof: REACT_ELEMENT_TYPE, //表示是ReactElement类型
type: type, //class或function
key: key, //key
ref: ref, //ref属性
props: props, //props
_owner: owner,
};
return element;
}
作者:想当厨子的小左
链接:juejin.cn/post/715942…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。