模块与组件css
模块:html
组件:node
模块化与组件化react
模块化:webpack
组件化:ios
React的特色git
React高效的缘由程序员
* 导入相关js库文件(react.js, react-dom.js, babel.min.js) * 编码: ``` <div id="container"></div> <script type="text/babel"> var aa = 123 ReactDOM.render(<h1>{aa}</h1>, containerDOM); </script> ```
react定义的一种相似于XML的JS扩展语法: XML+JSes6
做用: 用来建立react虚拟DOM(元素)对象github
把数据的数组转换为标签的数组:
var liArr = dataArr.map(function(item, index){ return <li key={index}>{item}</li> })
注意:
* 基本理解和使用 * 自定义的标签: 组件类(函数)/标签 * 建立组件类 ``` //方式1: 无状态函数(最简洁, 推荐使用) function MyComponent1() { return <h1>自定义组件标题11111</h1>; } //方式2: ES6类语法(复杂组件, 推荐使用) class MyComponent3 extends React.Component { render () { return <h1>自定义组件标题33333</h1>; } } //方式3: ES5老语法(不推荐使用了) var MyComponent2 = React.createClass({ render () { return <h1>自定义组件标题22222</h1>; } }); ``` * 渲染组件标签 ``` ReactDOM.render(<MyComp />, cotainerEle); ``` * ReactDOM.render()渲染组件标签的基本流程 * React内部会建立组件实例对象/调用组件函数, 获得虚拟DOM对象 * 将虚拟DOM并解析为真实DOM * 插入到指定的页面元素内部 * props * 全部组件标签的属性的集合对象 * 给标签指定属性, 保存外部数据(多是一个function) * 在组件内部读取属性: this.props.propertyName * 做用: 从目标组件外部向组件内部传递数据 * 对props中的属性值进行类型限制和必要性限制 ``` Person.propTypes = { name: React.PropTypes.string.isRequired, age: React.PropTypes.number.isRequired } ``` * 扩展属性: 将对象的全部属性经过props传递 ``` <Person {...person}/> ``` * 组件的组合 * 组件标签中包含子组件标签 * 拆分组件: 拆分界面, 抽取组件 * 经过props传递数据 * refs * 组件内包含ref属性的标签元素的集合对象 * 给操做目标标签指定ref属性, 打一个标识 * 在组件内部得到标签对象: this.refs.refName(只是获得了标签元素对象) * 做用: 操做组件内部的真实标签dom元素对象 * 事件处理 * 给标签添加属性: onXxx={this.eventHandler} * 在组件中添加事件处理方法 ``` eventHandler(event) { } ``` * 使自定义方法中的this为组件对象 * 在constructor()中bind(this) * 使用箭头函数定义方法(ES6模块化编码时才能使用) * state * 组件被称为"状态机", 页面的显示是根据组件的state属性的数据来显示 * 初始化指定: ``` constructor() { super(); this.state = { stateName1 : stateValue1, stateName2 : stateValue2 }; } ``` * 读取显示: this.state.stateName1 * 更新状态-->更新界面 : this.setState({stateName1 : newValue}) * 实现一个双向绑定的组件 * React是单向数据流 * 须要经过onChange监听手动实现 * 组件生命周期 * 组件的三个生命周期状态: * Mount:插入真实 DOM * Update:被从新渲染 * Unmount:被移出真实 DOM * 生命周期流程: * 第一次初始化显示 ``` constructor() componentWillMount() : 将要插入回调 render() : 用于插入虚拟DOM回调 componentDidMount() : 已经插入回调 ``` * 每次更新state ``` componentWillReceiveProps(): 接收父组件新的属性 componentWillUpdate() : 将要更新回调 render() : 更新(从新渲染) componentDidUpdate() : 已经更新回调 ``` * 删除组件 ``` ReactDOM.unmountComponentAtNode(document.getElementById('example')) : 移除组件 componentWillUnmount() : 组件将要被移除回调 ``` * 经常使用的方法 ``` render(): 必须重写, 返回一个自定义的虚拟DOM constructor(): 初始化状态, 绑定this(能够箭头函数代替) componentDidMount() : 只执行一次, 已经在dom树中, 适合启动/设置一些监听 ```
* React没有ajax模块 * 集成其它的js库(如axios/fetch/jQuery/), 发送ajax请求 * axios * 封装XmlHttpRequest对象的ajax * promise * 能够用在浏览器端和服务器 * fetch * 再也不使用XmlHttpRequest对象提交ajax请求 * fetch就是用来提交ajax请求的函数, 只是新的浏览才内置了fetch * 为了兼容低版本的浏览器, 能够引入fetch.js * 在哪一个方法去发送ajax请求 * 只显示一次(请求一次): componentDidMount() * 显示屡次(请求屡次): componentWillReceiveProps()
虚拟DOM是什么?
Virtual DOM 算法的基本步骤
进一步理解
react脚手架
xxx脚手架: 用来帮助程序员快速建立一个基于xxx库的空项目的库
建立项目并启动
拆分组件:
肯定组件的state和props:
App:
CommentAdd
commentList
CommentItem
编写静态组件
实现动态组件
动态展现初始化数据
响应用户操做, 更新组件界面
拆分组件
肯定组件的state和props
App
Search
List
编写动态组件
方式一: 经过props传递
方式二: 使用消息订阅(subscribe)-发布(publish)机制: 自定义事件机制
使用:
import PubSub from 'pubsub-js' //引入 PubSub.subscribe('delete', function(data){ }); //订阅 PubSub.publish('delete', data) //发布消息
箭头函数:
优势:
扩展运算符(...)
项目编译打包并运行
react相关库
npm install react react-dom --save
babel相关库
npm install babel-core babel-preset-es2015 babel-preset-react --save-dev
webpack相关库
npm install webpack babel-loader --save-dev npm install webpack-dev-server
const path = require('path'); //path内置的模块,用来设置路径。 module.exports = { entry: './src/main.js', // 入口文件 output: { // 输出配置 filename: 'bundle.js', // 输出文件名 path: path.resolve(__dirname, 'dist') //输出文件路径配置 }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, //babel处理js { test: /\.js$/, exclude: /node_modules/, //排除此文件夹 use: [ 'babel-loader' ] } ] } };
{ "presets": ["es2015", "react"] }
src/js/App.js: 应用组件
import React from 'react' export default function App() { //暴露组件都得使用默认暴露 return <h1>Hello React Client Component</h1> }
src/js/main.js: 入口js
import React from 'react' import ReactDOM from 'react-dom' import App from './App' //渲染组件标签到页面元素 ReactDOM.render(<App />, document.getElementById('demo'))
npm install style-loader css-loader --save-dev 建立css文件 src/css/test.css body{ background : red }
devServer:{ contentBase: './',//内置服务器动态加载页面所在的目录 } `` ##20. 执行命令
构建任务:webpack 热加载任务: webpack-dev-server ```
"scripts": { "start": "webpack-dev-server", "build": "webpack" }
包含的相关组件:
Router: 路由器组件
Route: 路由组件
IndexRoute: 默认路由
hashHistory
Link: 路由连接
* webpack配置: webpack.config.js ``` module.exports = { //入口js entry: './index.js', //编译打包输出 output: { filename: 'bundle.js', publicPath: '' }, module: { //使用的loaders loaders: [ {test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react'} ] } } ``` * 包配置: package.json ``` { "name": "tutorial", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "webpack-dev-server --inline --content-base ." }, "author": "", "license": "ISC", "dependencies": { "react": "^0.14.7", "react-dom": "^0.14.7", "react-router": "^2.0.0" }, "devDependencies": { "babel-core": "^6.5.1", "babel-loader": "^6.2.2", "babel-preset-es2015": "^6.5.0", "babel-preset-react": "^6.5.0", "http-server": "^0.8.5", "webpack": "^1.12.13", "webpack-dev-server": "^1.14.1" } } ```
* 定义各个路由组件 * About.js ``` import React from 'react' function About() { return <div>About组件内容</div> } export default About ``` * Home.js ``` import React from 'react' function Home() { return <div>Home组件内容2</div> } export default Home ``` * Repos.js ``` import React, {Component} from 'react' export default class Repos extends Component { render() { return ( <div>Repos组件</div> ) } } ``` * 定义应用组件: App.js ``` import React, {Component} from 'react' import {Link} from 'react-router' export default class App extends Component { render() { return ( <div> <h2>Hello, React Router!</h2> <ul> <li><Link to="/about" activeClassName="active">About2</Link></li> <li><Link to="/repos" activeClassName="active">Repos2</Link></li> </ul> {this.props.children} </div> ) } } ``` * 定义入口JS: index.js-->渲染组件 ``` import React from 'react' import {render} from 'react-dom' import {Router, Route, IndexRoute, hashHistory} from 'react-router' import App from './modules/App' import About from './modules/About' import Repos from './modules/Repos' import Home from './modules/Home' render(( <Router history={hashHistory}> <Route path="/" component={App}> <IndexRoute component={Home}/> <Route path="/about" component={About}></Route> <Route path="/repos" component={Repos}></Route> </Route> </Router> ), document.getElementById('app')) ``` * 主页面: index.html ``` <style> .active { color: red; } </style> <div id=app></div> <script src="bundle.js"></script> ```
* repo.js: repos组件下的分路由组件 ``` import React from 'react' export default function ({params}) { let {username, repoName} = params return ( <div>用户名:{username}, 仓库名:{repoName}</div> ) } ``` * repos.js ``` import React from 'react' import NavLink from './NavLink' export default class Repos extends React.Component { constructor(props) { super(props); this.state = { repos: [ {username: 'faceback', repoName: 'react'}, {username: 'faceback', repoName: 'react-router'}, {username: 'Angular', repoName: 'angular'}, {username: 'Angular', repoName: 'angular-cli'} ] }; this.handleSubmit = this.handleSubmit.bind(this) } handleSubmit () { const repos = this.state.repos repos.push({ username: this.refs.username.value, repoName: this.refs.repoName.value }) this.setState({repos}) this.refs.username.value = '' this.refs.repoName.value = '' } render() { return ( <div> <h2>Repos</h2> <ul> { this.state.repos.map((repo, index) => { const to = `/repos/${repo.username}/${repo.repoName}` return ( <li key={index}> <Link to={to} activeClassName='active'>{repo.repoName}</Link> </li> ) }) } <li> <form onSubmit={this.handleSubmit}> <input type="text" placeholder="用户名" ref='username'/> / {' '} <input type="text" placeholder="仓库名" ref='repoName'/>{' '} <button type="submit">添加</button> </form> </li> </ul> {this.props.children} </div> ); } } ``` * index.js: 配置路由 ``` <Route path="/repos" component={Repos}> <Route path="/repos/:username/:repoName" component={Repo}/> </Route> ``` 6. 优化Link组件 * NavLink.js ``` import React from 'react' import {Link} from 'react-router' export default function NavLink(props) { return <Link {...props} activeClassName="active"/> } ``` * Repos.js ``` <NavLink to={to}>{repo.repoName}</NavLink> ```
material-ui(国外)
ant-design(国内蚂蚁金服)
npm install create-react-app -g create-react-app antd-demo cd antd-demo npm start
下载
npm install antd --save
src/App.js
import React, { Component } from 'react'; import { Button } from 'antd'; import './App.css'; class App extends Component { render() { return ( <div className="App"> <Button type="primary">Button</Button> </div> ); } } export default App;
src/App.css
@import '~antd/dist/antd.css'; .App { text-align: center; }
使用 eject 命令将全部内建的配置暴露出来
npm run eject
下载babel-plugin-import(用于按需加载组件代码和样式的 babel 插件)
npm install babel-plugin-import --save-dev
修改配置: config/webpack.config.dev.js
// Process JS with Babel. { test: /\.(js|jsx)$/, include: paths.appSrc, loader: 'babel', query: { + plugins: [ + ['import', [{ libraryName: "antd", style: 'css' }]], + ], // This is a feature of `babel-loader` for webpack (not Babel itself). // It enables caching results in ./node_modules/.cache/babel-loader/ // directory for faster rebuilds. cacheDirectory: true } },
去除引入全量样式的语句: src/App.css
@import '~antd/dist/antd.css'
愿你成为终身学习者