〇、看前准备
1.自行clone react最新代码
2.自行搭建一个能跑react的test项目
一、看表面:那些插件 如何解析JSX
有如下一段代码:
// ---- hearder.jsx 组件 import React,{Component} from 'react'; export default (props)=>( <h1 ref="h1">我是header.{props.kk}</h1> ); // ---- Home.jsx 页面级组件 import React,{Component} from 'react'; import Header from '../components/header'; class Home extends Component { constructor(props){ super(props); } componentWillMount(){ console.log('willMount'); } render(){ let {name} = this.props; console.log(this); return ( <div ref="home"> <Header kk="jsx"/> <div>主页</div> <div> <p>哈哈哈哈</p> </div> </div> ) } } export default Home; // React入口文件 app.js import ReactDOM from 'react-dom'; import React from 'react'; import Home from './page/home'; let abc = ReactDOM.render( <div> <Home name="home"/> </div> , document.getElementById('app') );
发现每一个出现jsx语法的地方都要出现import React from 'react';
其实react 的引入就是为了将解析完的jsx 能有createElement方法被执行,在浏览器里打开控制台,我们发现代码被整成了这个样子:
// ----header.jsx var _default = function _default(props) { return _react2.default.createElement( "h1", { ref: "h1" }, "u6211u662Fheader.", props.kk ); }; exports.default = _default; // home.jsx var Home = function (_Component) { _inherits(Home, _Component); function Home(props) { _classCallCheck(this, Home); return _possibleConstructorReturn(this, (Home.__proto__ || Object.getPrototypeOf(Home)).call(this, props)); } _createClass(Home, [{ key: 'componentWillMount', value: function componentWillMount() { console.log('willMount'); } }, { key: 'render', value: function render() { var name = this.props.name; console.log(this); return _react2.default.createElement( 'div', { ref: 'home' }, _react2.default.createElement(_header2.default, { kk: 'js' }), _react2.default.createElement( 'div', null, 'u4E3Bu9875' ), _react2.default.createElement( 'div', null, _react2.default.createElement( 'p', null, 'u54C8u54C8u54C8u54C8' ) ) ); } }, { key: '__reactstandin__regenerateByEval', // @ts-ignore value: function __reactstandin__regenerateByEval(key, code) { // @ts-ignore this[key] = eval(code); } }]); return Home; }(_react.Component); var _default = Home; exports.default = _default; // app.js _reactDom2.default.render(_react2.default.createElement( 'div', null, _react2.default.createElement(_home2.default, { name: 'home' }) ), document.getElementById('app'));
二、为啥外面非得包一个
我们知道我们在写react的时候,不管你用的是webpack也好还是fis3 、gulp 或者直接撸js+html,都需要引入jsx语法转义插件,这些插件把jsx语法的字符转变成了我们在浏览器看到的样子。
每个组件都有render方法,每个render方法都会return一个React.createElement 执行后的东西
我将header.jsx改为
export default (props)=>( <h1 ref="h1">我是header.{props.kk}</h1> <p>hahaha</p> );
当我们在jsx中写若干个标签而外面不包东西的话,以babel-loader为例,丫会提示:
SyntaxError: Adjacent JSX elements must be wrapped in an enclosing tag
我们通过追溯得到jsx语法的解析其实是在 baabel-core/node_modules/babylon/index.js 中解析的
我们注释掉出现的报错信息,因能力有限水平一般且对babylon有任何了解,所以只通过粗浅的改动以下代码让webpack能够编译通过(当然js语法肯定是不成功的)
Tokenizer.prototype.readRegexp = function readRegexp() { ...... for (;;) { // if (this.state.pos >= this.input.length) this.raise(start, "Unterminated regular expression"); var ch = this.input.charAt(this.state.pos); if (lineBreak.test(ch)) { // this.raise(start, "Unterminated regular expression"); break; // 增加一个break } .............. return this.finishToken(types.regexp, { pattern: content, flags: mods }); }; pp$9.jsxParseElementAt = function (startPos, startLoc) { ............ // if (this.match(types.relational) && this.state.value === "<") { // this.raise(this.state.start, "Adjacent JSX elements must be wrapped in an enclosing tag"); // } return this.finishNode(node, "JSXElement"); };
编译出来的代码结构如下:
var _default = function _default(props) { return _react2.default.createElement( "h1", { ref: "h1" }, "u6211u662Fheader.", props.kk ) < p > hahaha < /p>/; }; exports.default = _default;
可见babylon在解析jsx的时候会默认将第一个闭合标签 return 出去,就算第二个咱们改babylon改的再成功能正确解析p标签这个jsx,岂不是return 两个东西出去了,函数里怎么可能有两个return呢!
所以react的所有组件必须放在一个闭合标签里(当然了16 版本也可以是一个数组,不用放在闭合标签里,以后再说)。
看到
SyntaxError: Adjacent JSX elements must be wrapped in an enclosing tag
和
SyntaxError: Unterminated regular expression
的报错
一定要检查自己的jsx是否在一个闭合标签中,同时是否语法正确