參看:視頻地址css
import ReactDOM from 'react-dom' /** * ReactDOM.render() 將模板轉化爲 HTML 語言 render * 參數1: 要渲染的模板 * 參數2: 掛載到的dom元素 */ ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') );
import React from 'react' import ReactDOM from 'react-dom' /** * React.createElement() * 參數1: 標籤名 * 參數2: 標籤屬性 * 參數3: 標籤內容 * 參數4: 其餘節點 */ // 須要 babel loader 解析jsx語法 const myH1 = <div title='h1'>哈哈哈</div> const myDiv = React.createElement('div', { title: 'this is a div', id: 'mydiv' }, '這是一個div', myH1) // 將 myH1 放在 myDiv中,渲染到 #app 標籤中 ReactDOM.render(myDiv, document.getElementById('app'))
jsx
jsx語法不能直接使用,須要@babel/preset-react翻譯。html
// package.json "devDependencies": { "@babel/core": "^7.6.2", "@babel/plugin-proposal-class-properties": "^7.5.5", "@babel/plugin-transform-runtime": "^7.6.2", "@babel/preset-env": "^7.6.2", "@babel/preset-react": "^7.0.0", "@babel/runtime": "^7.6.2", "babel-loader": "^8.0.6", "html-webpack-plugin": "^3.2.0", "webpack": "^4.41.2", "webpack-cli": "^3.3.9", "webpack-dev-server": "^3.8.2" }, "dependencies": { "react": "^16.10.2", "react-dom": "^16.10.2" }
// .babelrc { "presets": ["@babel/preset-env", "@babel/preset-react"], "plugins": ["@babel/transform-runtime", "@babel/plugin-proposal-class-properties"] }
// webpack.config.js const path = require('path') const HtmlWebPackPlugin = require('html-webpack-plugin') const htmlPlugin = new HtmlWebPackPlugin({ template: path.join(__dirname, './src/index.html'), filename: 'index.html' }) module.exports = { mode: 'development', // development production plugins: [ htmlPlugin ], module: { rules: [ // 解析 jsx 語法 { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader" }, exclude: /node_modules/ } ] }, resolve: { extensions: ['.js', '.jsx', '.json'] } }
{}
語法標籤必須封閉 <img src="" />
node
class
要寫成className
,for
要寫成htmlFor
react
HTML註釋不能使用,只能使用註釋{/* 註釋 */}
webpack
原生標籤好比p、li、div
若是要使用自定義屬性,必須用data-
前綴web
{}
中執行js
json
const num = 100 // 數字 const str = 'react 學習' // 字符串 const bool = true // 布爾值 const unde = undefined // undefined const nu = null // null const h4dom = <h4>這是一個h4標籤</h4> // jsx 語法 // 遍歷數組 const arrDom = [ <h5 key="1">這是一個h5標籤1</h5>, <h5 key="2">這是一個h5標籤2</h5>, <h5 key="3">這是一個h5標籤3</h5> ] // 字符串數組 const arr = ["白板","幺雞","二條","三餅"] ReactDOM.render(<div> { num + 1 } - { str } - { bool.toString() } - { unde } - { nu } <hr/> <h3 title={ str }>這是一個h3標籤</h3> <hr/> {/* 數組會自動展開 */} { h4dom } { arrDom } <hr/> <ul> { arr.map((item,index)=> <li key={ index }>{ item }</li>) } </ul> </div>, document.getElementById('app'))
bootstrap
樣式使用雙大括號react-native
<div style={{"width":"100px","height":20 + 30 + "px","backgroundColor" : "red"}}>這是一個標籤</div>
組件名必須大寫數組
組件方法必須return
import React from 'react' import ReactDOM from 'react-dom' // 將 person 傳入 HolleWorld 組件中 // 使用 props 接收傳進來的參數; props 只讀,不可更改 function HolleWorld(props) { return <h1>Holle World: { props.name } - { props.age} - { props.gender }</h1> } // 父組件傳遞 person 對象給 HolleWorld 子組件,必要時 HolleWorld 組件要抽離出去 const person = { name: 'Danny', age: 23, gender: 'boy' } ReactDOM.render(<div> {/* 組件傳參 props */} <HolleWorld { ...person }></HolleWorld> </div>, document.getElementById('app'))
// main.js import React from 'react' import ReactDOM from 'react-dom' // 導入組件 import App from "@/App/App.jsx" // 傳遞的 對象 const person = { name: 'Danny', age: 23, gender: 'boy' } // <App { ...person }></App> 是 Class 的實例對象 ReactDOM.render(<div><App { ...person }></App></div>, document.getElementById('app'))
// ./App/App.jsx 抽離出來的 App 組件 import React from "react" class App extends React.Component{ // render函數 是渲染當前組件對應的虛擬dom元素, props 接收 父組件 傳遞過來的值 render(props){ // 經過 this.props.*** 就能夠接收數據 //返回一個jsx語法 return <h1>Holle World { this.props.name } - { this.props.age} - { this.props.gender }</h1> } } export default App
不一樣點:
用構造函數建立出來的組件:專業的名字叫作「無狀態組件」
用class關鍵字建立出來的組件:專業的名字叫作「有狀態組件」
用構造函數建立出來的組件,和用class建立出來的組件,這兩種不一樣的組件之間的本質區別就是:有無state屬性 和 生命週期函數! 有狀態組件和無狀態組件之間的本質區別就是:有無state屬性!
行內樣式
// main.js import React from 'react' import ReactDOM from 'react-dom' import CommentList from "@/conpenents/CommentList" ReactDOM.render(<div> <CommentList></CommentList> </div>, document.getElementById('app'))
// src\conpenents\CommentList.jsx import React from "react" import CommentDetail from "@/conpenents/CommentDetail" class CommentList extends React.Component { constructor(params) { super() this.state = { CommentList: [ { id: 1, user: '張三', content: '哈哈,沙發' }, { id: 2, user: '張三2', content: '哈哈,板凳' }, { id: 3, user: '張三3', content: '哈哈,涼蓆' }, { id: 4, user: '張三4', content: '哈哈,磚頭' }, { id: 5, user: '張三5', content: '哈哈,樓下山炮' } ] } } render(props){ return ( <div> { /* 行內樣式 */} <h1 style={{ color: 'red', fontSize: '24px', backgroundColor: 'yellow', fontWeight: 700 }}>這是評論列表組件</h1> { this.state.CommentList.map(item => <CommentDetail key={ item.id } { ...item }></CommentDetail> )} </div> ) } } export default CommentList
// src\conpenents\CommentDetail.jsx import React from "react" // 將 CSS 樣式寫成 js 對象的形式 import styles from '@/conpenents/styles' function CommentDetail(props) { console.log(props) return ( <div style={ styles.itemStyle }> <h1 style={ styles.userStyle }>評論人:{ props.user }</h1> <p style={ styles.contentStyle }>評論內容:{ props.content }</p> </div> ) } export default CommentDetail
// src\conpenents/styles const styles = { itemStyle: { border: '1px dashed #ddd', margin: '10px', padding: '10px', boxShadow: '0 0 10px #ddd' }, userStyle: { fontSize: '14px' }, contentStyle: { fontSize: '12px' } } export default styles
sass
第三方使用css
本身的樣式使用sass
配置loader
{ test: /\.css$/, use: [ 'style-loader', 'css-loader'] }, { test: /\.scss$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader', options: { modules: { localIdentName: '[path][name]-[local]-[hash:base64:5]', }, } }, { loader: 'sass-loader' } ] }
使用:
// .jsx 文件 // 本身的 sass 文件,須要使用 sass 配置規則解析 import cssobj from '@/css/commentList.scss' console.log('commentList.css', cssobj) import 'bootstrap/dist/css/bootstrap.css' class CommentList extends React.Component { constructor(params) { super() } render(props){ return ( <div> {/* 本身的 css */} <h1 className={ [cssobj.title, 'test'].join(' ') }>這是評論列表組件</h1>. {/* 第三方的 css */} <button className="btn btn-primary">按鈕</button> </div> ) } }
模擬實現 VUE 中的雙向綁定數據原理
//#region 介紹 react 中綁定事件的標準格式 import React from "react" class BindEvent extends React.Component { constructor(params) { super() this.state = { msg: '哈哈', name: 'LiMing', color: '#' + Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, '0') } } render(props){ return ( <div> <h1>BindEvent 組件</h1> {/* 綁定點擊事件 onClick */} <button onClick={ () => this.myclickHandler(this.state.color) }>按鈕</button> {/* 顯示 state 中的數據 */} <h3>{ this.state.msg }</h3> {/* 模擬實現 VUE 中的雙向綁定數據原理 */} <input type="text" ref="txt" style={{ width: '100%'}} value={ this.state.msg } onChange={e => this.txtChange(e) }/> </div> ) } txtChange = (e) => { // 獲取 input 中 value 的兩種方法 // console.log(e.target.value === this.refs.txt.value) // true this.setState({ msg: e.target.value // this.refs.txt.value }) } myclickHandler = color => { // this.setState({}, callbcak) 是異步的,須要在 callbcak 獲取最新的值 this.setState({ msg: color },() => { console.log( '2', this.state.msg ) }) // 仍是未修改的值 console.log( '1', this.state.msg ) } } export default BindEvent //#endregion
組件建立階段:
componentWillMount:組件將要被掛載,此時尚未開始渲染虛擬DOM
render:第一次開始渲染真正的虛擬DOM,當render執行完,內存中就有了完整的虛擬DOM了
componentDidMount:組件完成掛載,此時,組件已經渲染到頁面上了,數據和頁面達到同步,當這個方法執行完,組件進入 運行中 的狀態
componentWillReceivrProps:組件將要接受新的屬性,此時,只要這個方法被觸發,就證實父組件爲當前子組件傳遞了新的屬性值
shouldComponenUpdate:組件是否須要被更新,此時,組件還沒有開始更新,可是 state 和 props 中的數據是最新的
componentWillUpdate:組件將要被更新,此時還沒有開始更新,內存中的虛擬DOM樹仍是舊的
render:此時,從新根據最新的 state 和 props 從新渲染一顆存在內存中的 虛擬DOM樹,當 render 調用完畢,內存中舊的DOM樹 替換 成新的 DOM樹了,可是頁面仍是舊的
componentDidUpdate:此時,頁面又被從新渲染了,state 和 虛擬DOM 頁面都是最新的,而且保持同步
componentDidUpdate:組件將要被卸載,此時組件還能夠正常使用
使用計數器學習react的生命週期:
import React from "react" import ReactTypes from 'prop-types' class Counter extends React.Component { constructor(props) { super(props) this.state = { msg: 'ok', count: props.initcount } } // 父組件沒有傳遞值是,設置默認值 static defaultProps = { initcount: 0 } // 建立靜態的 propTypes 對象,能夠校驗 父組件傳遞過來的值得類型 static propTypes = { initcount: ReactTypes.number // prop-types 指定數據類型 } // 組件將要被掛載到頁面上,此時,數據已經存在,可是尚未開始渲染虛擬DOM = VUE: created componentWillMount() { console.log('componentWillMount 函數') // 沒法獲取DOM,頁面 和 虛擬DOM 尚未渲染 console.log(document.querySelector('.myh3')) // null // props 和 state 能夠獲取到 console.log(this.props.initcount) // 100 console.log(this.state.msg) // ok this.myselfFunc() } // 渲染內存中的虛擬DOM,可是頁面還沒有真正顯示DOM元素 render() { // 每當調用 render 函數時,頁面上的元素仍是舊的 console.log('render 函數') // return 以前,虛擬DOM尚未建立, return 以後,虛擬DOM建立,可是尚未掛載到頁面上 return ( <div> <h1>這是計數器組件</h1> <input id="btn" type="button" value="+1" onClick={this.increment} /> <hr/> <h3 ref="h3" className="myh3">當前數量爲:{ this.state.count }</h3> <h3 className="myh3">當前數量爲:{ this.props.initcount }</h3> </div> ) } // 組件掛載到頁面上,會進入這個生命週期函數,此時頁面上渲染虛擬DOM了 == VUE mounted componentDidMount() { console.log('componentDidMount 函數') console.log(document.querySelector('.myh3')) // <h3 ... </h3> // 使用原生js方法,改變 count 值 // document.querySelector('#btn').onclick = () => { // this.setState({ // count: this.state.count + 2 // }) // } } // 從這裏判斷組件是否須要更新 shouldComponentUpdate(nextProps, nextState) { console.log('shouldComponentUpdate 函數') // 該週期中必須返回一個 boolean 值 // 返回 false,則不執行其餘生命週期函數,可是 state 的數據會被更改 // 返回 true,繼續執行其餘生命週期函數 // console.log(this.state.count) // 此時直接使用this.state是未更改的值 // console.log(nextProps, nextState) // 最新的 props 和 state // return nextState.count % 2 === 0? true: false return true } // 組件將要更新,此時還沒有更新,在進入這個生命週期函數的時候,內存中的虛擬DOM是舊的,頁面上的DOM元素也是舊的 componentWillUpdate() { console.log('componentWillUpdate 函數') console.log(document.querySelector('.myh3').innerText) // 舊的數據 console.log(this.refs.h3.innerText) } // 此時虛擬DOM,頁面,數據都是最新的 componentDidUpdate() { console.log('componentDidUpdate 函數') console.log(this.refs.h3.innerText) } myselfFunc() { console.log('本身定義的函數'); } increment = () => { this.setState({ count: this.state.count + 1 }) } } export default Counter
使用父子組件學習componentWillReceiveProps
周期函數
注意:第一次渲染子組件時,componentWillReceiveProps
不會傳遞props
屬性。
import React from "react" class Parent extends React.Component { constructor(props) { super(props) this.state = { msg: '父組件中的 msg 消息' } } render() { return (<div> <h1>這是父組件</h1> <input type="button" value="點擊修改msg的值" onClick={this.changeMsg}/> <hr/> <Son pmsg={ this.state.msg }></Son> </div>) } changeMsg = () => { this.setState({ msg: '更改了,變成新值了' }) } } class Son extends React.Component { constructor(props) { super(props) this.state = {} } render() { return (<div> <h3>這是子組件</h3> <p>{ this.props.pmsg }</p> </div>) } // 組件接受外界傳值觸發 // 子組件第一次被渲染到頁面上時候,不會觸發這個方法,第二次之後纔會觸發 componentWillReceiveProps(nextProps) { console.log('被觸發了') // 注意:在 componentWillReceiveProps 中拿到的值是未更改的值!! // console.log(this.props.pmsg) console.log(this.props.pmsg + '----' + nextProps.pmsg); } } export default Parent
import React from "react" class BindThis extends React.Component { constructor(props) { super(props) this.state = { msg: '這是 BindThis組件中的 msg 消息' } // 綁定 this 並傳參的方式 ② // bind 的返回值 是調用函數和參數的 copy // 注意:bind 不會修改原函數的 this 指向 this.changeMsg2Copy = this.changeMsg2.bind(this, '參數one', '參數two') } render() { return (<div> <h1>這是 BindThis 組件 </h1> {/* 方式 ① */} {/* bind 修改函數內部的 this 指向,指向 bind 參數列表中的第一個參數 */} <input type="button" value="react 中綁定 this 傳遞參數的3中方式1" onClick={ this.changeMsg1.bind(this, '參數1', '參數2')}/> <hr /> {/* 方式 ② */} <input type="button" value="react 中綁定 this 傳遞參數的3中方式2" onClick={ this.changeMsg2Copy }/> <hr /> {/* 方式 ③ */} <input type="button" value="react 中綁定 this 傳遞參數的3中方式3" onClick={ () => this.changeMsg3('參數1111', '參數2222') }/> <h2>{ this.state.msg }</h2> </div>) } changeMsg1(arg1, arg2) { console.log(this) // 指向實例 this.setState({ msg: '變成新值了' + arg1 + arg2 }) } changeMsg2(arg1, arg2) { console.log(this) // undefined this.setState({ msg: '變成新值了' + arg1 + arg2 }) } changeMsg3 = (arg1, arg2) => { console.log(this) // 指向實例 this.setState({ msg: '變成新值了' + arg1 + arg2 }) } } export default BindThis
import React from "react" import ReactTypes from 'prop-types' class Conm1 extends React.Component { constructor(params) { super() this.state = { color: "red" } } // 1. 在父組件定義 func,這個func固定的名字 getChildContext // 內部必須返回一個對象,這個對象就是要共給子孫組件的數據 getChildContext() { return { color: this.state.color } } // 2. 使用 屬性校驗,規定一下傳遞給子組件的數據類型,固定名字 childContextTypes static childContextTypes = { color: ReactTypes.string // 規定傳遞給子組件的數據類型 } render(props){ return ( <div> <h1>這是父組件</h1> <hr/> <Conm2></Conm2> <hr/> <Conm3></Conm3> </div> ) } } class Conm2 extends React.Component { render(props){ return ( <div><h3>這是子組件</h3></div> ) } } class Conm3 extends React.Component { constructor(params) { super() this.state = { } } // 3. 先來個屬性校驗,校驗父組件傳遞過來的參數類型 static contextTypes = { color: ReactTypes.string // 這裏,子組件必定要校驗一下父組件傳遞過來的 context 的數據類型 } render(props){ return ( <div> <h5 style={{ color: this.context.color }}>這是孫子組件 --- { this.context.color }</h5> </div> ) } } export default Conm1