React Element /组件/JSX
React是一个JavaScript的UI库,通过Javascript来构建UI(用户界面(创建Html元素)),但使用者确不需要操作DOM,只需要描述DOM,React会帮你创建DOM,所以写React就是描述DOM长什么样子。在JS中,描述一个东西通常使用对象,有哪些属性,值是什么。React提供了方法来创建对象,React 17以前是createElement(),以后是Jsx(),它们都接受三个参数,type(创建什么类型元素), properties(元素有哪些属性),children(元素有哪些子元素)。创建一个h1元素,它有id属性,children是Hello World,
let h1Elem = React.createElement('h1', {id: 'recipe'}, 'Hello World');
使用React方法创建的对象称React Element。react-dom库的render方法创建真实的DOM,第一个参数是React element, 第二个参数是渲染到什么地方,
ReactDOM.render(h1Elem, document.getElementById('root'));
ReactDom render相当于执行以下操作
let h1 = document.createElement('h1');
h1.setAttribute('id', 'recipe');
h1.textContent = 'Hello World';
document.getElementById('root').appendChild(h1);
浏览器就可以渲染出h1,构建出UI。写React就是创建React Element,创建真实的DOM,React会负责,所以React开发需要引入两个库: react 和react-dom。最开始了解React,完全可以使用script标签引入的方式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>React</title> <script src="https://unpkg.com/react@17/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script> </head> <body> <div id="root"></div> <script> let h1Elem = React.createElement('h1', { id: 'recipe' }, 'Hello World'); ReactDOM.render(h1Elem, document.getElementById('root')); </script> </body> </html>
其实,createElement可以接受任意个参数,第三个参数及其以后的参数,都会当成创建出来的React Element的children,
let ulElem = React.createElement('ul', null, React.createElement('li', null, 'JS'), React.createElement('li', null, 'React'), React.createElement('li', null, 'Redux') ); ReactDOM.render(ulElem, document.getElementById('root'));
ul下面三个li,简单的树形结构,整个Web应用(html文档)也是一个树形结构,都可以通过这种方式创建。当页面其他地方也用ul li,可以封装起来,最简单的就是写个函数
const Content = () => { return React.createElement('ul', null, React.createElement('li', null, 'JS'), React.createElement('li', null, 'React'), React.createElement('li', null, 'Redux') ) }
这就形成了React组件。组件包含一段React element ,相当于自定义了一个type。自定义类型要求组件名大写,以进行区分,还要求返回一个单一的根节点,一个类型一个根节点,这和树形结构有关,一棵子树,插入到整棵树上,一个根节点,最好操作。把组件名传给createElement 函数中的第一个参数type,
// 组件名Content, 相当于createElement函数中的第一个参数type, let content = React.createElement(Content, null, null); ReactDOM.render(content, document.getElementById('root'));
创建React组件的方式还有点问题,一是数据和UI绑定,组件不够通用,二是语法繁琐。数据和UI分离相对好解决,createElement第二个参数是属性, 组件也能定义属性
const item = ['JS', 'React', 'Redux']; // 向组件中传递一个数组数据 item let content = React.createElement(Content, {item: item}, null);
组件获取属性,则是通过props参数。React DOM 渲染时,会给组件注入一个参数,包含组件定义的所有属性,所以组件的参数名通常定义为props。
// 自动获取props作为参数。 const Content = (props) => { return React.createElement('ul', null, props.item.map((item, index) => React.createElement('li', {key:index}, item) ) ) }
React函数组件和普通的函数有两个区别,一个是函数名大写,一个是只有一个参数props,如果不写props,只能使用解构赋值,也就是说,组件的参数要么是props,要么以大括号开始。
语法繁琐的问题,React element是描述html元素,html标签也是描述html元素,能不能在JS中直接写html标签?这就是JSX,直接在JS代码中写html标签。ReactElement中的type,属性和children,对应html中的标签、属性和children,<h1 id='recipe' >Hello World</h1>, <Content items={items} >Content</Content>。JSX只是对JS进行扩展,它始终都是JS语法(写在.js文件中),JS中的所有语法,它都支持,JS中所有要求,它也必须遵守。扩展还包括属性是任意的类型,不仅仅是字符串,不过要用大括号包起来。在JSX中, {}里面的内容都当作JS表达式,需要求值。num = {1},bool={false}。<Content item ={item} name="sam" bool={false} num={1}></Content>。传递属性还可以用{...obj}。const obj = {name:”sam”, num: 1} , …obj就相当于name=”sam” , num= 1
const Content = ({ items, name, bool, num }) => { console.log(items, 'items') return <ul> { items.map((item, index) => { return (<React.Fragment> {bool && <li key={index}>{name} - {item} - {num}</li>}</React.Fragment>) }) } </ul> } const items = ['JS', 'React', 'Redux']; let content = <Content items={items} name="sam" bool={true} num={1} />; // 或者 const obj = { items: ['JS', 'React', 'Redux'], name: 'sam', bool: true, num: 1} let content = <Content {...obj} />; ReactDOM.render(content, document.getElementById('root'));
要求就是html语法不能使用JS的保留字和关键字。html元素经常添加class属性,但class在js中是关键字,class要变成className. 还有for,要用htmlFor,<label htmlFor=”input” className=”text”></label>。还有style属性,必须是一个对象,一些属性带浏览器厂商前缀,是用-,但-在JS中表示减法,这时前缀首字母必须大写, 所有的样式都是字符串
const inlineStyle = { color: 'pink', WebkitFilter: 'blur(5px)' // -wibkit-filter } return <ul style={inlineStyle}> ...</ul>
JSX是JS的扩展,但浏览器并没有支持该扩展,需要把它转换成JS。引入babel库,在head 标签中引入
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
然后<script type="text/babel">告诉babel, 哪些代码需要转译。有了JSX,重写代码如下
<script type="text/babel"> const Content = ({ items, name, bool, num }) => { return <ul> { items.map((item, index) => { return (<React.Fragment> {bool && <li key={index} >{name} - {item} - {num}</li>}</React.Fragment>) }) } </ul> } const App = () => { const items = ['JS', 'React', 'Redux', 'Java']; return <div> <header>Header</header> <Content items={items} name="sam" bool={true} num={1} />; </div> } ReactDOM.render(<App />, document.getElementById('root')); </script>
但在大型应用程序中,这么写React肯定有问题,开发不友好,运行时编译慢,于是就要使用构建工具。好在官方推出脚手架工具create-react-app,直接创建项目,进行开发。它和我们上面的开发的最大不同就是,一个组件一个JS文件,通过export和import进行组合,其他没什么区别。App.js是整个项目的入口,只有能被App.js引入,不管是直接引入,还是间接引入,都能渲染到页面上。在src目录下新建Content.js
export function Content({ items, name, bool, num }) { // export出Content组件 return <ul> { items.map((item, index) => { return (<React.Fragment> {bool && <li key={index} >{name} - {item} - {num}</li>}</React.Fragment>) }) } </ul> }
App.js
import { Content } from './Content'; //引入Content组件 function App() { const items = ['JS', 'React', 'Redux', 'Java']; return ( <div> <header>Header</header> <Content items={items} name="sam" bool={true} num={1} />; </div> ); } export default App;