安装依赖
使用@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元素,props
和children
合并为第一个调用参数, (存在第二个参数, 推测是父组件的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); }