一,對組件化的理解css
1,組件的封裝html
-視圖前端
-數據vue
-變化邏輯(數據驅動視圖變化)node
例:react
import React, { Component } from 'react'; import List from './list/index.js'; import Input from './input/index.js'; class Todo extends Component { constructor(props) { // 數據 super(props) this.state = {//保存當前組件的變量 list:[] } } render() { return ( // 視圖 <div> <Input addTitle={this.addTitle.bind(this)}/> <List data={this.state.list} /> <List data={[1,2,3]} /> </div> ) } // 變化邏輯 addTitle(title) { const currentList = this.state.list; this.setState({ list:currentList.concat(title) }) } } export default Todo
2,組件的複用npm
-props傳遞後端
-複用瀏覽器
例:babel
import React, { Component } from 'react'; class List extends Component { constructor(props) { super(props) } render(){ const list = this.props.data; //props傳遞 return ( <ul> { list.map((item,index) => { return <li key={index}>{item}</li> }) } </ul> ) } } export default List
二,JSX本質
1,JSX語法:
例:
// html形式 // 引入JS變量和表達式 // if...else... // 循環 // style和className // 事件 render() { const name = '12'; const show = true; const list = [1,2,3,4,5]; const styleConfig = { fontSize:'40px', color:'blue' } return ( <div className='container'> {/* <Todo /> */} <p>{name}</p> <p>{name ? 1 : 0}</p> <p>{(name === '12').toString()}</p> <p>{name || 'lisi'}</p> {show ? <img src='./logo.svg' /> : ''} <ul> {list.map(function(item,index) { return <li key={index}>{item}</li> })} </ul> <p style={styleConfig}>this is a p</p> <p style={{fontSize:'40px',color:'blue'}}>this is a p</p> </div> );
2,JSX解析成JS:
JSX語法根本沒法被瀏覽器所解析,那麼它如何在瀏覽器運行?-轉換成JS運行
思考:爲什麼react組件中,React 沒有顯示地使用,但卻必需要引入?(去掉的話會報錯) 'React' must be in scope when using JSX
import React, {
Component
} from 'react';
看個例子:
// JSX代碼 var profile = <div> <img src="avatar.png" className="profile" /> <h3>{[user.firstName,user.lastName].join('')}</h3> </div>;
解析結果:
// 解析結果 有沒有跟vdom的h函數、vue的_c函數很像?yes! var profile = React.createElement("div",null, React.createElement("img",{src:"avatar.png",className:"profile"}), React.createElement("h3",null,[user.firstName,user.lastName].join('')), );
從上面得知,咱們所寫的JSX代碼,都是須要經過 React.createElement 函數來解析
ReactElement createElement( string/ReactClass type, [object props], [children ...] )
(相似 vdom 的 h 函數、vue 的 _c 函數)例:
React.createElement('div',{id:'div1'},child1,child2,child3)
React.createElement('div',{id:'div1'},[...])
再看:
render(){ const list = this.props.data; return ( <ul> { list.map((item,index) => { return <li key={index}>{item}</li> }) } </ul> ) }
解析以下:
function render() { const list = this.props.data; return React.createElement( 'ul', null, list.map((item,index) => { return React.createElement( 'li', {key:index}, item ) }) ) }
JSX的寫法大大下降了學習成本和編碼工做量
同時,JSX也會增長debug成本
3,獨立的標準
JSX 是 React 引入的,但不是 React 獨有的,已是一個標準
React 已經將它做爲一個獨立標準開放,其餘項目也可使用
React.createElement 是能夠自定義修改的
說明:自己功能已經完備;和其餘標準兼容和擴展性沒問題
使用插件編譯jsx代碼,測試:
新建文件夾 jsx-test
mkdir jsx-test
進入文件夾
cd jsx-test
初始化:
npm init
新建文件 demo.jsx
touch demo.jsx
全局安裝babel
sudo npm i babel -g
安裝依賴插件
npm install --sav-dev babel-plugin-transform-react-jsx
建立.babelrc文件,配置:
{ "plugins":["transform-react-jsx"] }
babel編譯命令:
babel --plugins transform-react-jsx demo.jsx
demo.jsx代碼以下:
class Input extends Component { constructor(props) { super(props) this.state = { title:'' } } render(){ return ( <div> <input value={this.state.title} onChange={this.changeHandle.bind(this)} /> <button onClick={this.clickHandle.bind(this)}>submit</button> </div> ) } changeHandle(event){ this.setState({ title:event.target.value }) } }
編譯結果:
class Input extends Component { constructor(props) { super(props); this.state = { title: '' }; } render() { return React.createElement( 'div', null, React.createElement('input', { value: this.state.title, onChange: this.changeHandle.bind(this) }), React.createElement( 'button', { onClick: this.clickHandle.bind(this) }, 'submit' ) ); } changeHandle(event) { this.setState({ title: event.target.value }); } }
嘗試把 React.createElement 改爲 h 在文件頂部加入
/* @jsx h */
運行編譯以下:
class Input extends Component { constructor(props) { super(props); this.state = { title: '' }; } render() { return h( 'div', null, h('input', { value: this.state.title, onChange: this.changeHandle.bind(this) }), h( 'button', { onClick: this.clickHandle.bind(this) }, 'submit' ) ); } changeHandle(event) { this.setState({ title: event.target.value }); } }
總結:
js語法(標籤,js表達式,判斷,循環,事件綁定)
JSX實際上是語法糖(微創新),需被解析成JS才能運行,React.createElement
已經成爲獨立的標準
三,JSX 和 vdom 關係
1,分析:爲什麼須要vdom
vdom是 React 初次推廣開來的,結合 JSX
JSX 就是模版,最終要渲染成 html
初次渲染 + 修改 state 後的 re-render
正好符合 vdom 的應用場景
2,React.createElement 和 h(本質同樣)
全部的JSX都要解析成JS,執行最終返回vnode
3,什麼時候 patch ?
(1),初次渲染 ReactDOM.render(<App />, container),會觸發 patch(container,vnode)
例:
ReactDOM.render(<App />, document.getElementById('root'));
(2),re-render - setState,會觸發 patch(vnode,newVnode)
// patch(vnode,newVnode) this.setState({ list:currentList.concat(title) })
4,自定義組件的解析
'div',直接渲染<div>便可,vdom能夠作到
Input和List等自定義組件(class),vdom默認不認識
所以Input和List定義的時候必須聲明render函數
根據props初始化實例,而後執行實例的render函數
render函數返回的仍是vnode對象
var list = new List({ data: this.state.list }); var vnode = list.render();
例(demo.jsx)
import List from './list/index.js'; import Input from './input/index.js'; function render() { return ( // 視圖 <div> <p> this is demo </p> <Input addTitle={this.addTitle.bind(this)}/> <List data={this.state.list} /> <List data={[1,2,3]} /> </div> ) }
解析以下:
import List from './list/index.js'; import Input from './input/index.js'; function render() { return ( // 視圖 React.createElement( 'div', null, React.createElement( 'p', null, ' this is demo ' ), React.createElement(Input, { addTitle: this.addTitle.bind(this) }), React.createElement(List, { data: this.state.list }), React.createElement(List, { data: [1, 2, 3] }) ) ); }
總結:
爲什麼須要vdom:JSX須要渲染成html,數據驅動視圖
React.createElement 和 h,都生成vnode (React.createElement 既能夠建立 html 默認的標籤-字符串形式,能夠建立自定義組件名稱)
什麼時候patch:ReactDOM.render(...) 和 setState()
自定義組件的解析:初始化實例,而後執行render
四,說下 setState 的過程 ( setState 核心函數)
1,setState 的異步
addTitle(title) { const currentList = this.state.list; // patch(vnode,newVnode) console.log(this.state.list); //[1,2] this.setState({ list: currentList.concat(title) //3 }) console.log(this.state.list); //[1,2] 由於上面的setState是異步操做,因此這裏仍舊是[1,2] }
緣由:
01,可能會一次執行屢次 setState
02,你沒法規定、限制用戶如何使用 setState
03,不必每次 setState 都從新渲染,考慮性能
04,即使是每次從新渲染,用戶也看不到中間的效果(js執行和DOM渲染是單線程的)
05,只看到最後的結果便可
addTitle(title) { const currentList = this.state.list; // 初次想增長 title this.setState({ list: currentList.concat(title) }) // 改變注意,想增長 title + 1 this.setState({ list: currentList.concat(title + 1) }) // 又改變注意,想增長 title + 2 this.setState({ list: currentList.concat(title + 2) }) }
用戶操做後,只會看到最後的結果,即 list: currentList.concat(title + 2)
2,vue 修改屬性也是異步
01,效果、緣由和 setState 同樣
02,對比記憶,印象深入
03,複習下vue 的實現流程
第一步:解析模版成render函數
第二步:響應式開始監聽
第三步:首次渲染,顯示頁面,且綁定依賴
第四步:data 屬性變化,觸發 rerender ( set中執行updateComponent 是異步的)
3,setState 的過程
01,每一個組件實例,都有 renderComponent 方法 (在Component 組件中定義的)
02,執行 renderComponent 會從新執行實例的 render )
03,render 函數返回 newVnode,而後拿到 preVnode (上次執行的 newVnode)
04,執行 patch(preVnode,newVnode)
addTitle(title) { const currentList = this.state.list; // patch(vnode,newVnode) console.log(this.state.list); //[1,2] this.setState({ list: currentList.concat(title) //3 },() => { console.log(this.state.list); //這裏能拿到實時結果 [1,2,3] // this.renderComponent() }) console.log(this.state.list); //[1,2] 由於上面的setState是異步操做,因此這裏仍舊是[1,2] }
模擬 Component
class Component { constructor(props) { } renderComponent() { const preVnode = this._vnode; const newVnode = this.render(); patch(preVnode,newVnode) this._vnode = newVnode; } }
五,對 React 和 vue 的認識
國內使用,首推vue,文檔更易讀、易學,社區夠大
若是團隊水平較高,推薦使用React,組件化和JSX作的更好
1,二者的本質區別
vue-本質是MVVM框架,由MVC發展而來
React-本質是前端組件化框架,由後端組件化發展而來
但這並不妨礙它們二者都能實現相同的功能
2,模版的區別:
vue-使用模版(最初由 angular 提出)
React-使用JSX
模版語法上,更傾向於JSX
模版分離上,更傾向於Vue
3,組件化的區別:
React自己就是組件化,作的更完全
vue也支持組件化,不過是在MVVM上的擴展
4,二者共同點
都支持組件化
都是數據驅動視圖
六,總結:
組件化的理解:
1,組件的封裝:封裝視圖,數據,變化邏輯
2,組件的複用:props傳遞,複用
JSX本質:
1,語法
2,語法糖,需被解析成JS才能運行
3,JSX是獨立的標準,可被其餘項目使用
JSX和vdom的關係
1,爲什麼須要vdom:JSX須要渲染成html,還有rerender
2,React.createElement 和 h,都生成vnode
3,什麼時候patch:ReactDOM.render() 和 setState
4,自定義組件的解析:初始化實例,而後執行render
setState過程:
異步:效果、緣由 (vue修改屬性也是異步,效果、緣由)
setState的過程:最終走到 patch(preVnode,newVnode)
附:用 React 實現 todo-list
建立 React 開發環境
全局安裝
sudo npm i create-react-app -g --registry=https://registry.npm.taobao.org
運行
create-react-app react-rest
成功以後:
Inside that directory, you can run several commands: yarn start Starts the development server. yarn build Bundles the app into static files for production. yarn test Starts the test runner. yarn eject Removes this tool and copies build dependencies, configuration files and scripts into the app directory. If you do this, you can’t go back! We suggest that you begin by typing: cd react-rest yarn start Happy hacking!
進入文件夾
cd react-rest
運行
yarn start
實現todo組件
import React, { Component } from 'react'; import List from './list/index.js'; import Input from './input/index.js'; class Todo extends Component { constructor(props) { super(props) this.state = {//保存當前組件的變量 list:[] } } render() { return ( <div> <Input addTitle={this.addTitle.bind(this)}/> <List data={this.state.list} /> </div> ) } addTitle(title) { const currentList = this.state.list; this.setState({ list:currentList.concat(title) }) } } export default Todo
todo中使用的Input組件:
import React, { Component } from 'react'; class Input extends Component { constructor(props) { super(props) this.state = { title:'' } } render(){ return ( <div> <input value={this.state.title} onChange={this.changeHandle.bind(this)} /> <button onClick={this.clickHandle.bind(this)}>submit</button> </div> ) } changeHandle(event){ this.setState({ title:event.target.value }) } clickHandle(){ const title = this.state.title; // 把title添加進列表 const addTitle = this.props.addTitle; addTitle(title);//重點 this.setState({ title:'' }) } } export default Input
todo中使用的List組件
import React, { Component } from 'react'; class List extends Component { constructor(props) { super(props) } render(){ const list = this.props.data;//list組建指望別人傳入的列表 return ( <ul> { list.map((item,index) => { return <li key={index}>{item}</li> }) } </ul> ) } } export default List
最後,修改App.js
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; import Todo from './components/todo/index.js' class App extends Component { render() { return ( <div> <Todo /> </div> ); } } export default App;
附:
react 父子組件互相通訊
1,父組件向子組件傳遞 在引用子組件的時候傳遞,至關於一個屬性
<Input addTitle={this.addTitle.bind(this)} /> <List data={this.state.list} />
2,子組件向父組件傳遞 子組件經過 調用父組件傳遞到子組件的方法 向父組件傳遞消息的。
class Input extends Component { constructor(props) { super(props) this.state = { title:'' } // console.log(this.props) } render(){ return ( <div> <input value={this.state.title} onChange={this.changeHandle.bind(this)} /> <button onClick={this.clickHandle.bind(this)}>submit</button> </div> ) } changeHandle(event){ this.setState({ title:event.target.value }) } clickHandle(){ const title = this.state.title; // 把title添加進列表 const addTitle = this.props.addTitle; addTitle(title);//重點 調用父組件方法 this.setState({ title:'' }) } }
附:
React:Rethinking Web App Development at Facebook 只負責view層,不是完整的MVVM框架
React 特色:輕,組件化開發,高度可重用
React應用場景:
複雜場景下的高性能
重用組件庫,組件組合
React組件的生命週期和事件處理
React Components Lifecycle
Mounted: React Components 被render解析,生成對應的DOM節點,並被插入瀏覽器的DOM結構的一個過程( React.renderComponent() )
Update:一個 mounted 的 React Components 被從新render 的過程 (setState() 或者 setProps() ----> render)
Unmounted:一個mounted的 React Components 對應的 DOM 節點被從DOM結構中移除的過程
每個狀態 React 都封裝了對應的 hook 函數
未完待續...