模塊與組件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'
願你成爲終身學習者