React 學習筆記(1) 基礎語法

參看:視頻地址css

1. ReactDOM.render()

import ReactDOM from 'react-dom'

/**
 * ReactDOM.render() 將模板轉化爲 HTML 語言 render
 * 參數1: 要渲染的模板
 * 參數2: 掛載到的dom元素
 */
ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
);

2. React.createElement()

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'))

3. jsx 語法

3.1 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']
  }
}

 

3.2 {}語法

  • 標籤必須封閉 <img src="" />node

  • class要寫成classNamefor要寫成htmlForreact

  • 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'))
  • 能夠運行函數:render() { {this.func('參數')} }bootstrap

  • 樣式使用雙大括號react-native

<div style={{"width":"100px","height":20 + 30 + "px","backgroundColor" : "red"}}>這是一個標籤</div>

4. 建立組件

  • 組件名必須大寫數組

  • 組件方法必須return

4.1 構造函數建立組件

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'))

4.2 class關鍵字建立組件

// 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

不一樣點:

  1. 構造函數建立出來的組件:專業的名字叫作「無狀態組件」

  2. class關鍵字建立出來的組件:專業的名字叫作「有狀態組件」

用構造函數建立出來的組件,和用class建立出來的組件,這兩種不一樣的組件之間的本質區別就是有無state屬性 和 生命週期函數! 有狀態組件和無狀態組件之間的本質區別就是:有無state屬性!

 

5. react的樣式

5.1 渲染評論列表樣式

  • 行內樣式

  • css樣式寫成js對象的形式

// 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>
    )
  }
}

 

6. react中的事件

模擬實現 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

 

7. react組件的生命週期函數

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

8. react中綁定this傳遞參數的3中方式

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

9. context多重組件傳值

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
相關文章
相關標籤/搜索