相信你們對Omi應該都不陌生了,若是還有不瞭解的同窗先看看這裏。瞭解並使用Omi以後你會發現真的回不去了~~~javascript
先簡單說一下吧,Omi就是一個能夠快速開發項目的組件化框架,和vue/react同樣爲了節省生產力的。想了解Omi和vue還有react區別的,上面文檔有講解,或者加入羣256426170,能夠面對面諮詢Omi做者dnt。我這篇文章將使用Omi從0到1來完成一個移動端的項目,讓你們瞭解Omi開發的方便快捷。css
此次咱們挑選了一個日跡發現頁來做爲例子開發,若是有用手機QQ的同窗,應該有知道「日跡」這個項目,此次咱們就挑選了一個日跡的一個發現頁,入口在手機QQ -> 動態 -> 日跡 -> 右上角發現
發現頁以下html
開發一個移動端頁面和PC上開發是同樣的,首先要分析頁面劃分模塊,發現頁很簡單,能夠當作一個列表,而後裏面每一塊是一個item
若是不用組件化的話,ul+li是否是就能夠上手幹了~但咱們要告別原始社會的開發方式,採用Omi框架進行開發,下面就正式開始開發~
vue
開發一個項目(一個頁面也是一個項目),首先咱們須要腳手架,腳手架能夠從歷史項目中複製過來,也能夠本身從新搭建。使用Omi的話就方便不少啦,咱們只須要下面兩步java
npm install omi-cli -g omi-cli init [project name]
而後腳手架就OK了,下面簡單的看一下腳手架,瞭解一下項目結構react
下面那些.babelrc/.eslintrc/package.json等就不說了
先看目錄,config是配置目錄,裏面有基礎配置和項目配置,通常咱們不須要修改
tools裏面是構建相關,webpack.base.js是基礎配置,而後測試環境和生產環境的區分就靠script.js了jquery
src是開發的目錄,也是咱們代碼所在地,打開src再看一下webpack
應該仍是很好理解的,page是頁面,這裏面每一個目錄就意味着有一個頁面。頁面的入口是目錄下的maingit
component是組件,組件也是以文件夾爲粒度來的,裏面必定有一個js文件,而後組件相關的資源文件,樣式文件也都放在js的同一目錄下,好比這樣github
組件的圖片/樣式和js都有了,那外面的css/img/js呢?是一些全局資源和公共方法等,這樣一來複用就極爲方便了。
首先咱們引入一下rem統一的js代碼,如今來講用rem仍是比px方便不少的,代碼以下:
;(function(win) { var doc = win.document; var docEl = doc.documentElement; var tid; function refreshRem() { var width = docEl.getBoundingClientRect().width; if (width > 540) { // 最大寬度 width = 540; } var rem = width / 10; // 將屏幕寬度分紅10份, 1份爲1rem docEl.style.fontSize = rem + 'px'; } win.addEventListener('resize', function() { clearTimeout(tid); tid = setTimeout(refreshRem, 300); }, false); win.addEventListener('pageshow', function(e) { if (e.persisted) { clearTimeout(tid); tid = setTimeout(refreshRem, 300); } }, false); refreshRem(); })(window);
這樣咱們就將不一樣屏幕下的rem與px轉換統一了,視覺稿上面的px單位除以37.5就能夠了,這一步也能夠在構建的時候作
接下來咱們考慮到項目是一個長列表,說到長列表就確定離不開滾動,說到滾動就想到了安卓下局部滾動會很卡。那麼這裏能夠用全局滾動搞定麼?能夠的,由於頁面自己不復雜。
那麼複雜的情景下,必須是局部滾動的場景怎麼辦呢?AlloyTouch歡迎你~解決各種滾動問題,並且有Omi插件的無縫支持版本。
準備工做都考慮完善以後咱們就開始寫第一個組件了!第一個組件能夠當作是整個列表的一個包裹盒,盒子裏面不只有list,還有按鈕和一些其餘的玩意
先上一下代碼
import List from '../list/index'; Omi.tag('List', List); class Main extends Omi.Component { constructor(data) { super(data); this.inTouch = false; this.touchXY = []; this.data.loadWord = '正在加載中...'; } style() { return ` .record { position: fixed; bottom: 0.533333rem; left: 50%; -webkit-transform: translateX(-50%); transform: translateX(-50%); background-image: url(${require('./img/record.png')}); width: 2.000000rem; height: 2.000000rem; background-size: 100% 100%; } .isend { position: relative; text-align: center; margin: 0 auto; margin-left: -12px; padding: 12px 0; font-size: 14px; color: rgba(119, 119, 119, 1); } `; } render() { return ` <div class="main"> <List omi-id="list"></List> <div class="record" ontouchmove="handleTouchMove(this, event)" ontouchstart="handleTouchStart(this, event)" ontouchend="handleTouchEnd(this, event)"></div> <div class="isend">${this.data.loadWord}</div> </div>`; } handleTouchMove(dom, e) { this.inTouch = false; } handleTouchStart(dom, e) { this.inTouch = true; this.touchXY[0] = e.touches[0].screenX; this.touchXY[1] = e.touches[0].screenY; } handleTouchEnd(dom, e) { console.log(e.changedTouches[0]); var diffX = Math.abs(e.changedTouches[0].screenX - this.touchXY[0]); var diffY = Math.abs(e.changedTouches[0].screenY - this.touchXY[1]); if(this.inTouch && diffX < 30 && diffY < 30) { // handle tap event.... this.inTouch = false; } e.preventDefault(); } } export default Main;
超級簡單明瞭,constructor是組件的構造函數,也是生命週期的開始,由於咱們包裹盒的組件一直存在,因此沒有用上其餘生命週期的方法。但Omi對組件生命週期的控制但是很是強大的,以下圖
接着是style和render,這裏是用模版字符串寫css和html,很方便,但若是以爲麻煩也能夠用文件的形式,後面會說
下面三個是啥呢?是本身模擬的tap,由於移動端下onclick有300ms的延遲,因此咱們用的點擊都是模擬的。tap用語言描述就是一次點擊,咱們要保證touchend時候手指的位置不能距離touchstart的位置太遠,並且end和start期間不能觸發touchmove,這也就是本身實現tap的核心了。
若是有zepto的話自己能夠用ontap事件,沒必要本身去寫,可是我這裏沒有引入zepto,並且zepto自己是jquery相似的寫法,和框架開發仍是比較背馳的。那麼咱們就只能本身寫這麼多代碼去模擬麼??
固然不是!由於咱們有alloyfinger-omi版,咱們只須要這樣
安裝:
npm install omi-finger
使用:
import OmiFinger from 'omi-finger'; OmiFinger.init();
就能夠了!alloytouch裏面的手勢操做omi-finger均可以用,並且用起來也超級方便!
...... render() { return ` <div class="main"> <List omi-id="list"></List> <div class="record" omi-finger tap="handleTap"></div> <div class="isend">${this.data.loadWord}</div> </div>`; } handleTap() { // handle tap event.... } ......
這樣就能夠了,這就是Omi插件體系的好處,順帶一提alloytouch也能夠像finger這樣使用~
這樣最外層的包裹組件就已經ok了,咱們來看核心的list組件。
再上代碼
class List extends Omi.Component { constructor(data) { super(data); this.length = 0; this.data.leftList = []; this.data.rightList = []; } style() { return require('./_index.less'); } render() { return ` <div class="wrap clear" omi-finger tap="handleTap"> <div class="left"> ${ this.data.leftList.map((a, b) => `<Item data="data.leftList[${b}]"></Item>` ).join('') } </div> <div class="right"> ${ this.data.rightList.map((a, b) => `<Item data="data.rightList[${b}]"></Item>` ).join('') } </div> </div>`; } add(data) { for(let i = 0; i < data.length; i++) { // handle data if(i % 2 === 0) { this.data.leftList.push(info); } else { this.data.rightList.push(info); } } this.update(); } handleTap(e) { // handle tap; } reset() { this.data.leftList = []; this.data.rightList = []; } }
首先能夠看到和main不一樣的是,這裏咱們就把css給抽離成文件的形式了,純看我的喜愛。不過有一些須要注意的地方:
**1. 全局css只須要在文件中import就能夠了
omi.js默認的模版引擎是soda,若是還有喜歡ejs、mustache語法的同窗,雖然omi.js自己沒有內置該寫法,可是用omi.mustache.js卻將其默認爲內置模版引擎
具體的狀況以下:
接下來重點講的就是其中的循環生成子組件部分。
循環渲染有多種方式,剛剛代碼部分用的是ES6執行map,而後獲取到數組中每個元素,渲染
咱們也可使用omi中內置的soda模版的指令方式,以下代碼也能夠實現一樣的功能
render() { return ` <div class="wrap clear" omi-finger tap="handleTap"> <div class="left"> <Item o-repeat="item in leftList" group-data="data.leftList"></Item> </div> <div class="right"> <Item o-repeat="item in rightList" group-data="data.rightList"></Item> </div> </div>`; }
咱們在add方法中進行數據的處理,這裏組件的data下面有兩個數組,分別是左右兩邊的。注意這裏add方法最後有調用一個update()方法,omi自己沒有雙向綁定,將更新的操做交給了開發者。固然若是但願雙向綁定的話也能夠引入Mobx之類的第三方庫。
list組件裏面有一個item組件,這個item組件就是最後一個啦,它須要從list中接受到本身的數據,而後將數據給展現出來
數據傳遞的方式有不少種,簡單的說一下
咱們採用的是第x種,而後item中就是簡單的展現啦
class Item extends Omi.Component { constructor(data) { super(data); console.log('data', data); } style() { return require('./_index.less'); } render() { return ` <div class="item"> <div class="card" vid="${this.data.vid}" shoot="${this.data.shoot}" uin="${this.data.uin}"> <div class="pic" style="background-image: url(${this.data.pic})"></div> <div class="txt"> <div class="head" style="background-image: url(${this.data.head})"></div> <div class="other"> <div class="nick" data-content='${this.data.nick}'>${this.data.nick}</div> <div class="info"> <span class="watch"><i></i>${this.data.watch}</span> <span class="like"><i></i>${this.data.like}</span> </div> </div> </div> </div> </div> `; } } export default Item;
開發過程當中咱們只須要npm start,而後就能夠專一的擼代碼了
能夠用默認的localhost:9000端口進行訪問
也能夠修改config目錄下的config.js文件,用路由的方式訪問,好比我這樣
module.exports = { "webserver": "//xxx.qq.com/mobile/", "cdn": "", "port": "9000", "route": "/mobile/" };
固然我這裏是有配置代理的,將xxx.qq.com/mobile指向了本地的localhost:9000
當你開發完成後,只須要運行
**npm run dist**
生產環境的代碼就已經搞定了~接下來就是部署、提測...
文章一些cgi、util相關的代碼就省略掉了,主要目的是講解Omi的開發。雖然是一個很小的頁面,不過能夠看出來用omi+omi-cli開發仍是很簡單的哈!Omi的能力固然不止這一點點,我這篇文章只是拋磚引玉,你們想解放生產力的話,快來使用Omi吧~~
在線體驗地址,請使用手機QQ掃描下方二維碼
github地址:
有問題的話能夠留言你們一塊兒交流~