React 入门(5): 引入JSX 研究JSX的createElement实现

安装依赖

使用@babel/core

babel从7.0 之后,包名升级为 @babel/core。 和 vue-cli 升级到 @vue/cli 一样, @babel是一个group标记, 该组织发布的包在一个子目录下:

这样一来, 要查询官方包, 只需要执行npm search @babel就可以了.

→ 最新的babel引入JSX (推荐)

npm i -S @babel/core @babel/preset-env @babel/preset-react babel-loader

→ 使用babel-core (即@babel/core<7.0.0 不推荐)

npm i -S babel-core babel-preset-es2015 babel-preset-react babel-loader

添加Webpack模块规则

babel提供了js编译能力, 那自然是把js传递给bebel提供的loader模块编译, 没错, 我们有babel-loader, 并指定presets[]查询参数以启用相应的babel预设.

// @babel/core 推荐
{ test: /.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=@babel/env&presets[]=@babel/react' },
// babel-core 不推荐
{ test: /.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' },

Babel提案插件

React的默认属性等功能最好使用类静态属性, 类共有字段, 类私有字段等ES提案, 这些提案不像类静态方法一样是ES6标准, 只是提案, 所以没有被预设.
安装Babel插件以支持类静态属性:

npm i -S @babel/plugin-proposal-class-properties

安装完成后需要设置webpack模块规则:

rules: [
    { test: /.css$/, use: ['style-loader', 'css-loader?modules'] },
    { test: /.(html|png|jpg|ico)$/, use: 'file-loader?context=src&name=[path][name].[ext]' },
    {
        test: /.js$/, exclude: /node_modules/, loader: {
            loader: 'babel-loader',
            options: {
                presets: ['@babel/env', '@babel/react'], // 预设
                plugins: ['@babel/plugin-proposal-class-properties'], // 插件
            }
        }
    }, // babel
],

研究JSX的createElement实现

JSX只是语法糖, 始终都会编译为JavaScript代码. 这也说明JSX只是可选的.

function render() {
    return <App><button onClick={() => console.log(this)}>研究JAX</button></App>;
}
console.log(render);

为什么这样做, 因为一个React标签代表一次createElement调用, 也就是返回的React对象, 因此要打印函数, 也可以是箭头函数:

console.log(() => <div>组件</div>);
console.log((() => <div>组件</div>).toString()); // toString()避免浏览器省略

关于createElement函数

createElement(el, props, [...children]) : ReactElement;

el

el 可以是一个:

  • ReactElement函数 → 即"函数式组件"
    函数执行后最终返回一个ReactElement元素, propschildren合并为第一个调用参数, (存在第二个参数, 推测是父组件的props或者状态).
  • Component子类 → 即"类组件"
    该类继承React.Component, 将创建一个该类的实例, 并调用render()函数.
  • 字符串
    必须是原生html标签.

children

children 可以是一个:

  • ReactElement对象
    通过调用createElement()函数生成. 有时也直接实例化Component对象并调用其render()函数(本质上还是调用createElement()函数).
  • 字符串
    相当于<span>标签.
  • 数组
    一个children参数也可以是上诉合法参数的数组. 避免了解构合并等繁琐操作. 不过每一个数组里的元素都要提供一个唯一的key属性, 否则有警告.

页面不是组件

页面不是组件, 不需要继承Component类, 所以可以导出一个返回ReactElement的函数:

export default (props, ?) => (<div>App</div>); // 导出函数

import App from './pages/App';
ReactDOM.render(<StrictMode><App /></StrictMode>, /* container */ window.app);

如果连参数也不需要, 那么可以直接导出ReactElement:

export default (<div>App</div>); // 导出元素

import App from './pages/App';
ReactDOM.render(<StrictMode>{App}</StrictMode>, /* container */ window.app);
// 等价于
ReactDOM.render(<StrictMode children={App}></StrictMode>, /* container */ window.app);

子组件<Child />{Child}的区别

子组件有以下两种编写方式:

<Father><Child /></Father>
<Father>{Child}</Father>

// 对应的js实现
function whatsJSX() { return /*#__PURE__*/React.createElement(Father, null, /*#__PURE__*/React.createElement(Child, null)); }
function whatsJSX2() { return /*#__PURE__*/React.createElement(Father, null, Child); }
原文地址:https://www.cnblogs.com/develon/p/13666642.html