一套程式碼小程式&Web&Native執行的探索(1)
前言
前面我們對微信小程式進行了研究:ofollow,noindex" target="_blank">【微信小程式專案實踐總結】30分鐘從陌生到熟悉
並且用小程式翻寫了之前一個demo:【元件化開發】前端進階篇之如何編寫可維護可升級的程式碼
之前一直在跟業務方打交道後面研究了下後端,期間還做了一些運營、管理相關工作,哈哈,最近一年工作經歷十分豐富啊,生命在於不斷的嘗試嘛。
當然,不可避免的在前端技術一塊也稍微有點落後,對React&Vue沒有進行過深入一點的研究,這裡得空我們便來一起研究一番(回想起來寫程式碼的日子才是最快樂的),因為我們現在也慢慢在切React、想嘗試下React Native,但是我這邊對於到底React還是Vue還是比較疑惑,所以我這裡研究下,順便看看能不能解決小程式一套程式碼的問題
我們現在就來嘗試,是否可以用React或者Vue讓一套程式碼能在三端同時執行
我們這裡依舊使用這個我覺得還算複雜的例子,做一個首頁一個列表頁,後面嘗試將這套程式碼翻譯成React Native以及微信小程式,於是便開始我們這次的學習吧
PS:我這裡對React&Vue熟悉度一般,文中就是demo研究,有不妥的地方請各位指正
React的開發方式
工欲善其事必先利其器,我們這裡依舊先做UI元件,首先我們做一個彈出層提示元件alert,這裡我們儘量嘗試與小程式開發模式保持一致點
我們這裡先來建立一個元件:
class UIAlert extends React.Component { propType() { //name必須有,並且必須是字串 name: React.PropTypes.string.isRequired } render() { return ( <view>我是{this.props.name}</view> ); } }; React.render( <UIAlert name="alert"/>, document.getElementById('main') );
//輸出 我是alert
生成的HTML結構為:
<view data-reactid=".0"> <span data-reactid=".0.0">我是</span><span data-reactid=".0.1">alert</span> </view>
這裡view顯然不會被識別,我們簡單做下處理(這裡順便升級下我們React的版本):
class View extends React.Component { render() { return ( <div >{this.props.children}</div> ); } } class UIAlert extends React.Component { render() { return ( <View>我是{this.props.name}</View> ); } }; ReactDOM.render( <UIAlert name="alert" />, document.getElementById('root') );
於是我們生成了這個樣子的程式碼,沒有額外新增span也沒有新增id標識了:
<div id="root"><div>我是alert</div></div>
我們這裡依舊以一個實際的例子來說明React的各種細節,這裡我們索性來做一個提示框元件吧,這裡我們先實現一個遮蓋層:
class UIMask extends React.Component { constructor(props) { super(props); this.state = { }; this.onClick = this.onClick.bind(this); } onClick(e) { if(e.target.dataset.flag !== 'mask') return; this.props.onMaskClick(); } render() { return ( <div onClick={this.onClick} data-flag="mask" className="cm-overlay" style={{zIndex: this.props.uiIndex, display: this.props.isShow}} > {this.props.children} </div> ); } }
這裡簡單說下React中狀態以及屬性的區別(我理解下的區別):
React中的屬性是隻讀的,原則上部允許修改自己的屬性,他一般作為屬性由父元件傳入 state作為元件狀態機而存在,表示元件處於不同的狀態,所以是可變的,state是元件資料基礎
這句話說的好像比較抽象,這裡具體表達一下是:
① 屬性可以從父元件獲取,並且父元件賦值是元件的主要使用方式
② 一個元件內部不會有呼叫setProps類似的方法期望引起屬性的變化
③ 總之屬性便是元件的固有屬性,我們只能像函式一樣使用而不是想去改變
④ 如果你想改變一個屬性的值,那麼說明他該被定義為狀態
⑤ 反之如果一個變數可以從父元件中獲取,那麼他一定不是一個狀態
這裡以我們這裡的遮蓋層元件為例說明:
遮蓋層的z-index以及是否顯示,對於遮蓋層來說就是最小原子單元了,而且他們也是父元件通過屬性的方式傳入的,我們看看這裡提示框的程式碼:
class UIAlert extends React.Component { constructor(props) { super(props); this.state = { isShow: '', uiIndex: 3000, title: '', message: 'message', btns: [{ type: 'ok', name: '確定' }, { type: 'cancel', name: '取消' }] }; this.onMaskClick = this.onMaskClick.bind(this); } onMaskClick() { this.setState({ isShow: 'none' }); } render() { return ( <UIMask onMaskClick={this.onMaskClick} uiIndex={this.state.uiIndex} isShow={this.state.isShow}> <div className="cm-modal cm-modal--alert" style={{zIndex: this.state.uiIndex + 1, display: this.state.isShow}}> <div className="cm-modal-bd"> <div className="cm-alert-title">{this.state.title}</div> {this.state.message.length > 10 ? <div className="cm-mutil-lines">{this.state.message}</div> : <div>{this.state.message}</div>} </div> <div className={this.state.btns.length > 2 ? 'cm-actions cm-actions--full' : 'cm-actions '}> { this.state.btns.map(function(item) { return <span data-type={item.type} className={item.type == 'ok' ? 'cm-btns-ok cm-actions-btn ' : 'cm-actions-btn cm-btns-cancel'}>{item.name}</span> }) } </div> </div> </UIMask> ); } };
為了方便除錯,我們這裡給提示框元件定義了很多“狀態”,而這裡面的狀態可能有很多是“不合適”的,可以看到我們遮蓋層UIMask使用的幾個屬性全部是這裡傳入的,然後我們這裡想象下真實使用場景,肯定是全域性有一個變數,或者按鈕控制顯示隱藏,所以我們這個元件進行一輪改造:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="./static/css/global.css" rel="stylesheet" type="text/css"/> <script src="./libs/react.development.js"></script> <script src="./libs/react-dom.development.js"></script> <script src="./libs/JSXTransformer.js"></script> </head> <body> <div id="root">ddd</div> <script type="text/jsx"> class UIMask extends React.Component { constructor(props) { super(props); this.state = { }; this.onClick = this.onClick.bind(this); } onClick(e) { if(e.target.dataset.flag !== 'mask') return; this.props.onMaskClick(); } render() { return ( <div onClick={this.onClick} data-flag="mask" className="cm-overlay" style={{zIndex: this.props.uiIndex, display: this.props.isShow}} > {this.props.children} </div> ); } } class UIAlert extends React.Component { constructor(props) { super(props); this.state = { uiIndex: 3000 }; this.onMaskClick = this.onMaskClick.bind(this); this.onBtnClick = this.onBtnClick.bind(this); } onMaskClick() { this.props.hideMessage(); } onBtnClick(e) { let index = e.target.dataset.index; this.props.btns[index].callback(); } render() { let scope = this; return ( <UIMask onMaskClick={this.onMaskClick} uiIndex={this.state.uiIndex} isShow={this.props.isShow}> <div className="cm-modal cm-modal--alert" style={{zIndex: this.state.uiIndex + 1, display: this.props.isShow}}> <div className="cm-modal-bd"> <div className="cm-alert-title">{this.props.title}</div> {this.props.message.length > 10 ? <div className="cm-mutil-lines">{this.props.message}</div> : <div>{this.props.message}</div>} </div> <div className={this.props.btns.length > 2 ? 'cm-actions cm-actions--full' : 'cm-actions '}> { this.props.btns.map(function(item, index) { return <span onClick={scope.onBtnClick} data-index={index} data-type={item.type} className={item.type == 'ok' ? 'cm-btns-ok cm-actions-btn ' : 'cm-actions-btn cm-btns-cancel'}>{item.name}</span> }) } </div> </div> </UIMask> ); } }; //這裡是真正的呼叫者,頁面級別控制器 class MainPage extends React.Component { constructor(props) { super(props); this.showMessage = this.showMessage.bind(this); this.hideMessage = this.hideMessage.bind(this); let scope = this; this.state = { alertShow: 'none', btns: [{ type: 'ok', name: '確定', callback: function() { scope.hideMessage(); console.log('成功'); } }, { type: 'cancel', name: '取消', callback: function() { scope.hideMessage(); console.log('取消'); } }] }; } showMessage() { this.setState({ alertShow: '' }) } hideMessage() { this.setState({ alertShow: 'none' }) } render() { return ( <div> <input type="button" value="我是一個一般的按鈕" onClick={this.showMessage} /> <UIAlert title="title" message="點點滴滴" btns={this.state.btns} showMessage={this.showMessage} hideMessage={this.hideMessage} isShow={this.state.alertShow} name="alert" uiIndex="333" /> </div> ); } } ReactDOM.render( <MainPage />, document.getElementById('root') ); </script> </body> </html>