React是什麼?React的官網是這樣介紹的:
React-用於構建用戶界面的 JavaScript 庫。
看起來十分簡潔,React僅僅是想提供一個構建用戶界面的工具,也就是隻關心UI層面,不關心數據層面,數據層面的東西交給專門的人(react-redux)來作。因此有些人會說React就是一個ui框架。
React 認爲渲染邏輯本質上與其餘 UI 邏輯內在耦合,好比,在 UI 中須要綁定處理事件、在某些時刻狀態發生變化時須要通知到 UI,以及須要在 UI 中展現準備好的數據。因此,React提供了jsx語法,也能夠理解爲讓你在ul組件裏寫不少東西。jsx最終是經過babel將組件轉化爲React.createElement()函數來調用。組件化開發的樂趣須要慢慢體會。既然是入門,下面經過搭建項目-小例子-引入路由/狀態管理簡單介紹下react,有關複雜的概念這裏暫不說起。html
const name = 'Josh Perez'; const element = <h1>Hello, {name}</h1>; ReactDOM.render( element, document.getElementById('root') );
上面說了,react的組件開發是須要babel進行編譯的,瀏覽器是不能直接解析的,因此react項目通常都是須要作一些簡單的配置的。固然,有不少的配置完善的腳手架可使用,好比官方腳手架creat-react-app或者螞蟻金服的dva框架,使用起來都是很方便上手。可是我們我以爲經過簡單搭建一個項目能夠更好的理解react。下面將會經過webpack一步一步搭建一個可使用的項目,可是具體的優化這裏就先不考慮了。node
1.打開終端、建立項目react
mkdir react-demo && cd react-demo //建立文件夾,並進入文件夾
2.初始化構建webpack
npm init //爲了後面下載npm包和node配置使用 //一路回車鍵就能夠了!項目中多出來一個package.json文件
3.建立項目入口git
新建app.js文件,引入react和react-dom,新建一個index.html,包含<div id='app'></div>。github
import React from 'react'; // 終端執行 npm i react 下載react import ReactDom from 'react-dom' // 終端執行 npm i react-dom 下載react-dom function App(){ //以函數的方式建立react組件 return <div>welcom,react-app</div> } ReactDom.render( //將組件App渲染掛載到頁面的根節點app <App />, document.getElementById('app')//因此須要新建一個html文件提供app節點供組件掛載 )
3.webpack配置web
建立好入口文件app.js後,須要將jsx語法文件經過babel編譯,因此先引入webpack,並新建js文件webpack.config.js作相關的配置以下:npm
npm i webpack webpack-cli --save //安裝webpack及cli依賴 webpack.config.js文件內容: const path = require("path"); const {CleanWebpackPlugin} = require('clean-webpack-plugin');//經過 npm 安裝 const HtmlWebpackPlugin = require('html-webpack-plugin'); //經過 npm 安裝 module.exports = { entry:{ app:'./app.js' }, output: { path: path.resolve(__dirname, './dist'), filename: '[name].bundel.[hash].js' }, module:{ rules: [{ test: /\.js$/, exclude: /(node_modules|bower_components)/, use:[{ loader: 'babel-loader', options: { presets: [ '@babel/preset-env',//引入babel '@babel/preset-react' //引入babel-react ] } }] }] }, plugins: [ new CleanWebpackPlugin(),//清空dist文件夾 new HtmlWebpackPlugin({ template: './index.html' }),//添加html模版插件 ] } 安裝依賴: npm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env @babel/preset-react --save npm i html-webpack-plugin clean-webpack-plugin --save
4.配置npm/script啓動項json
打開package.json文件,添加一行script代碼,以下:redux
此時咱們的代碼結構以下:
當咱們在終端執行npm run build命令後,能夠看到多出來打包後的dist文件夾,以及打包後的js文件,以下:
在瀏覽器裏打開dist下的index.html文件,OK,第一行react代碼閃亮出現!
4.修改npm/script啓動項
上面代碼雖然顯示出來了,可是不能每次更改代碼都執行一次打包吧,太麻煩了?那就搭建一個server吧,在搭配上局部熱更新(hmr)使用起來非常舒暢。須要修改3個地方:
1.在webpack.config.js文件中添加如下代代碼: devServer: {//開啓一個本地服務 port: 9000,//端口 host:"0.0.0.0",//配置後可經過本機ip訪問,方便在手機上調試 hot:true,// } 2.修改入口文件app.js: import React from 'react'; import ReactDOM from 'react-dom' import {hot} from 'react-hot-loader/root'//添加依賴,經過npm下載 function AppCont(){ return <div>welcom,react-app hot</div> } const App = hot(() =>{//根組件外層經過高階組件包裹 return <AppCont /> }) ReactDOM.render( <App />, document.getElementById('app') ) 3.修改package.json文件:增長一個新的script命令dev "dev": "webpack-dev-server --mode=development --config webpack.config.js"
執行npm run dev命令,打開http://localhost:9000/,能夠看到頁面內容出現了,在文字後面添加hot後,發現頁面內容更新了,可是沒有刷新!ok到這裏,簡單的項目已經搭建完成,能夠進行開發了。
這裏經過一個簡單的例子來描述一下react的一些概念。輸入框輸入數字、點擊後面add+數據會改變並求和。
import React from 'react'; function Item (props){ const {count,onChange,onAdd} = props; return <div> <input value={count} onChange={onChange} /> <span onClick={onAdd}>add+</span> </div> } class Add extends React.Component{ constructor(){ super(); this.state = { addList : [0,0,0,0] } this.onChange = this.onChange.bind(this) this.onAdd = this.onAdd.bind(this) } onChange(e,index){ this.state.addList.splice(index,1,e.target.value) this.setState({ addList:this.state.addList }) } onAdd(e){ this.state.addList.splice(e,1,Number(this.state.addList[e]) + 1 ) this.setState({ addList:this.state.addList }) } render (){ const { addList } = this.state; return <div> { addList.map((item,index)=>{ return <Item key={index} count={item} onChange={(e)=>{this.onChange(e,index)}} onAdd={()=>{this.onAdd(index)}} /> }) } <div>{addList[0]*1 + addList[1]*1 + addList[2]*1 + addList[3]*1}</div> </div> } } export default Add;
麻雀雖小...用來入門也是夠用了!
1.React.createClass
經常使用的是class繼承和無狀態函數的方式,還有一種React.createClass過於繁瑣並且會致使沒必要要的性能開銷因此基本上算是被廢棄了。
2.無狀態組件
直接經過函數聲明編寫,通常做爲純展現組件使用,內部沒有state(能夠經過hooks使用),不會實例化,也就不須要分配多餘的內存,從而性能獲得必定的提高。可是內部沒法使用this,也沒有生命週期
3.React.Component
React目前極爲推寵的建立有狀態組件的方式。內部可使用this和生命週期。可使用state。
React.Component建立的組件,其成員函數不會自動綁定this,須要手動綁,不然this不能獲取當前組件實例對象。綁定方式有3種:能夠在構造函數中完成綁定(constructor推薦),也能夠在調用時使用method.bind(this)來完成綁定,還可使用arrow function來綁定。
通常就是父向子傳遞參數經過props傳遞。
子向父傳遞則要經過父組件傳遞的函數來傳遞。
跨組件傳遞後面會提到使用redux想怎麼傳就怎麼傳。
表單元素除了收到state控制,還會受到用戶輸入控制。有兩個來源,使得內容不可預測。React使用state 成爲「惟一數據源」。渲染表單的 React 組件還控制着用戶輸入過程當中表單發生的操做。被 React 以這種方式控制取值的表單輸入元素就叫作「受控組件」。
能夠看到要給受控組件提供onChange函數來保證state數據的統一性。
上面看到了父子組件間的傳參還很方便的,可是若是要在Add組件外(也就是跨組件傳遞)使用求和後的數據怎麼辦?
固然,可使用增長中間組件、context等方法,這裏講一下大型項目經常使用的redux狀態管理,redux自己的原理也很簡單。
1.提供createStore建立store庫,將數據都存放在一塊兒,單向數據流。
2.數據只讀,修改數據只能經過dispatch方法,傳遞action對象(必須含有type屬性)在純函數reduce裏修改,每一次都是拷貝一個新的數據,不會修改原來的數據源,保證了數據的來源可溯性。
修改app.js import React from 'react'; import ReactDOM from 'react-dom'; import {createStore} from 'redux'; //添加redux依賴後,引入建立store的方法createStore import {Provider} from 'react-redux';//添加react-redux依賴,引入改節組件 import reduce from './model/reduce';//createStore方法第一個參數是reduce import {hot} from 'react-hot-loader/root'; import AddItem from './component/addItem'; const store = createStore(reduce) function AppCont(){ return <Provider store={store}><AddItem /></Provider>//根組件外面包裹高階組件 } const App = hot(() =>{ return <AppCont /> }) ReactDOM.render( <App />, document.getElementById('app') )
新建reduce文件,處理reduce函數。因爲後面項目龐大後,reduce函都寫在一塊兒確定太臃腫,redux提供了拆分的方法,能夠經過業務進行拆分,最後經過combineReducers來合併。
import {combineReducers} from 'redux'; const initState = { addList:[0,0,0,0] } function addSum(state=initState,action){ switch (action.type){ case 'add' : return { ...state, addList:[...action.payload] } default : return {...state} } } export default combineReducers({ addSum })
addItem文件須要作一些修改,將原來寫在state的數據,經過store來存取。修改方法跟下面新增長Sum組件同樣:經過connect函數來鏈接組件和store。
import React from 'react'; import {connect} from 'react-redux'; function Sum(props){ const {addList} = props return <div>{ Number(addList[0]) + Number(addList[1]) + Number(addList[2]) + Number(addList[3]) }</div> } export default connect(data=>data.addSum)(Sum);
react單頁面模式下,路由顯得很重要了,react將router單獨剔除,造成一個單獨的組件,使用起來更加的方便,代碼的耦合程度也下降很多。
提供HashRouter、BrowserRouter兩種根路由方式,重要的區別是HashRouter是經過hash路徑訪問,在瀏覽器直接輸入地址能夠訪問,BrowserRouter經過h5的history api訪問,輸入地址時會像服務器查詢,由於是單頁面,沒有歷史因此查詢不到,不過經過咱們使用webpack的devserver配置historyApiFallback: true,能夠訪問,服務端渲染也能夠訪問。
npm i react-router-dom --save <Router> <Route path="/" component={AddItem}></Route> <Link to="/tab1/1">tab1</Link> <Link to="/tab2/2">tab2</Link> <Link to="/tab3/3">tab3</Link> <Route path="/tab1/:num" component={PageItem}></Route> <Route path="/tab2/:num" component={PageItem}></Route> <Route path="/tab3/:num" component={PageItem}></Route> </Router>
OK,到這裏一個簡單的react開發框架搭建完畢,看到這裏應該入門了吧。
咱們說reduce要寫成純函數,寫成沒有反作用的函數,那麼有反作用的事件怎麼辦?最多見的就是接口的調用。不要慌!redux提供了不少中間件供咱們處理反作用函數,也能夠本身寫,原理就是在dispath(action)前面進行反作用的操做,拿到數據後再進行dispatch。下面引入一個比較好用的處理接口的中間件saga:
給store添加第二個參數: import { watchIncrementAsync } from './saga/index' const sagaMiddleware = createSagaMiddleware() const store = createStore(addSum, applyMiddleware(sagaMiddleware)); saga/index.js import { put, takeEvery,delay } from 'redux-saga/effects' export function* incrementAsync() { console.log(delay) yield delay(2000) yield put({ type: 'add',payload:[0,0,0,0] }) } export function* watchIncrementAsync() { yield takeEvery('INCREMENT_ASYNC', incrementAsync) }
須要注意的是,引入redux-saga依賴,由於saga是基於generator語法編寫的異步處理方案,因此須要對babel進行配置,npm i @babel/plugin-transform-runtime --save引入runtime依賴。並作如下配置修改:
options:{ "plugins": ["@babel/plugin-transform-runtime"] }
對store等文件作了一些修改,完整代碼,放到git上了代碼地址 。
以前都是先在git端新建項目,而後clone到本地在開發,此次是直接在本地建立的項目,而後在提交的,中間除了一點小問題,就當在這記錄一下吧。
1.githab新建倉庫,獲取地址。
2.打開終端,進入項目目錄下,git init //初始化
3.git add * //添加全部更改
4.git commit -m "提交備註"
5.git remote add origin https://github.com/wyczmy/rea...
//連接遠程倉庫
6.git pull --rebase origin master //遠程若是建立時有README.md文件,須要執行
7.git push -u origin master //推送到遠程 -f 強制推送
若有不妥,歡迎指正!