1. 程式人生 > >QQ日跡Omi實戰開發,從0到1

QQ日跡Omi實戰開發,從0到1

寫在前面

相信大家對Omi應該都不陌生了,如果還有不瞭解的同學先看看這裡。瞭解並使用Omi之後你會發現真的回不去了~~~

先簡單說一下吧,Omi就是一個可以快速開發專案的元件化框架,和vue/react一樣為了節省生產力的。想了解Omi和vue還有react區別的,上面文件有講解,或者加入群256426170,可以面對面諮詢Omi作者dnt。我這篇文章將使用Omi從0到1來完成一個移動端的專案,讓大家瞭解Omi開發的方便快捷。

開發準備:

這次我們挑選了一個日跡發現頁來作為例子開發,如果有用手機QQ的同學,應該有知道“日跡”這個專案,這次我們就挑選了一個日跡的一個發現頁,入口在手機QQ -> 動態 -> 日跡 -> 右上角發現
發現頁如下

開發一個移動端頁面和PC上開發是一樣的,首先要分析頁面劃分模組,發現頁很簡單,可以看成一個列表,然後裡面每一塊是一個item
如果不用元件化的話,ul+li是不是就可以上手幹了~但我們要告別原始社會的開發方式,採用Omi框架進行開發,下面就正式開始開發~

開發過程:

1/ 腳手架

開發一個專案(一個頁面也是一個專案),首先我們需要腳手架,腳手架可以從歷史專案中複製過來,也可以自己重新搭建。使用Omi的話就方便很多啦,我們只需要下面兩步

    npm install omi-cli -g
    omi-cli init [project name]

然後腳手架就OK了,下面簡單的看一下腳手架,瞭解一下專案結構

下面那些.babelrc/.eslintrc/package.json等就不說了
先看目錄,config是配置目錄,裡面有基礎配置和專案配置,一般我們不需要修改
tools裡面是構建相關,webpack.base.js是基礎配置,然後測試環境和生產環境的區分就靠script.js了

src是開發的目錄,也是我們程式碼所在地,開啟src再看一下

應該還是很好理解的,page是頁面,這裡面每個目錄就意味著有一個頁面。頁面的入口是目錄下的main

component是元件,元件也是以資料夾為粒度來的,裡面一定有一個js檔案,然後元件相關的資原始檔,樣式檔案也都放在js的同一目錄下,比如這樣

元件的圖片/樣式和js都有了,那外面的css/img/js呢?是一些全域性資源和公共方法等,這樣一來複用就極為方便了。

2/ 正式開發

首先我們引入一下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就可以了

  1. 區域性css或者less檔名必須以_開頭,loader會針對進行操作,就像上面的程式碼一樣
  2. html抽離成檔案的話需要用模版引擎的方式,上面程式碼用的是ES6模版字串,這樣的話是無法抽離成檔案的。**

omi.js預設的模版引擎是soda,如果還有喜歡ejs、mustache語法的同學,雖然omi.js本身沒有內建該寫法,但是用omi.mustache.js卻將其預設為內建模版引擎
具體的情況如下:

  • omi.js 使用 sodajs 為內建指令系統
  • omi.art.js 使用 art-template 為內建模版引擎
  • omi.lite.js 不包含任何模板引擎
  • omi.mustache.js 使用 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中接受到自己的資料,然後將資料給展示出來
資料傳遞的方式有很多種,簡單的說一下

  • on-* 代表傳遞父元件向子元件注入的回撥函式,比on-page-change="pageChangeHandler"
  • data-* 代表直接傳遞字串
  • :data-* 代表傳遞javascript表示式,比如data-num="1" 代表傳遞數字1而非字串,data-num="1+1"可以傳遞2。
  • ::data-* 代表傳遞父元件的屬性,比如上面的::data-items="data.items"就代表傳遞this.data.items給子元件
  • data 代表傳遞父元件的屬性,比如data="user"代表傳遞this.user給子元件
  • :data 代表傳遞javascript表示式,比如data="{ name : 'dntzhang' , age : 18 }"代表傳遞{ name : 'dntzhang' , age : 18 }給子元件
  • group-data 代表傳遞父元件的陣列一一對映到子元件

我們採用的是第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;

3/ 構建相關

開發過程中我們只需要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地址:

有問題的話可以留言大家一起交流~

相關推薦

QQOmi實戰開發0到1

寫在前面 相信大家對Omi應該都不陌生了,如果還有不瞭解的同學先看看這裡。瞭解並使用Omi之後你會發現真的回不去了~~~ 先簡單說一下吧,Omi就是一個可以快速開發專案的元件化框架,和vue/react一樣為了節省生產力的。想了解Omi和vue還有react區別的,上面文件有講解,或者加入群25642617

開發需求出發 &#183; 之四 春天在這裏

ins 大型 features 讓我 查看 width package 代碼 tle 首先,我要大字標語表達立場: 你所使用的framework & non-core features,就跟女人穿在身上的衣服一樣,越少越好! 扯淡完成,說正經的。 讓我

Docker實戰Ubuntu系列換到CentOS7.X系列應該避免的坑

一、背景 在生產環境中部署、使用Docker已經有很長一段時間了。學習的時候大部分環境、資料都是在Ubuntu14.04、16.04及18.04中實現的。由於某些原因,需要在生產環境中的CentOS7.2和7.4中部署使用Docker。在這個過程中踩了不少坑,花了很多時間,走了很多彎

後臺開發基礎到高階有什麼好的書籍推薦?

這篇部落格原作者的部落格連結:https://blog.csdn.net/analogous_love   首先,我覺得你應該好好準備演算法和資料結構,做到常見的演算法和資料結構知識點都能非常熟悉,這樣的話你畢業求職的時候可以輕鬆拿一些大廠(BAT等)的offer。我本人非科班出身

php 7 實戰開發

      閒得週六,無事的時候,看了一下新版的php7  ,感覺進步了好多,把正也沒有什麼事,著手玩了一把php  開發一一個小專案 ,,著為練手,為了快束,所以就選擇微信小程式設計師,因為程式碼要簡潔,輕量, 安裝php 7  ,配置&n

Extjs上傳附件實戰開發實現批量上傳及線上預覽功能(二)

SWFUpload的使用:         SWFUpload採用czpae86的UploadPanel二次開發,在此鳴謝。         SWFUpload下載最新版本swfupload.swf.v2.5.0.beta3.2.zip,你會發現資料夾裡只有swfuploa

基於HTML5+WebSocket+JAVA的棋牌遊戲開發入門到放棄(三)

前言 之前我們已經完成了一個有房間的五指棋遊戲,現在我們將進一步來完善這個東西。這一次我們打算新增的功能有: 之前我們增加了房間,但並沒有限制房間只能進入2個人 增加一個守護執行緒,統計當前房間的數量,後面我們將繼續完善這個守護執行緒的功能。 展示上一

實戰開發使用 Spring Session 與 Spring security 完成網站登入改造!!

上次小黑在文章中介紹了[四種分散式一致性 Session 的實現方式](https://mp.weixin.qq.com/s/8HgFYgrJDC3bi5MY0icJfg),在這四種中最常用的就是後端集中儲存方案,這樣即使 web 應用重啟或者擴容,Session 都沒有丟失的風險。 ![](https:/

QQ音樂開發探討如何利用騰訊雲SDK在直播中加入視頻動畫

根據 經理 主存 不存在 alloc 依然 騰訊遊戲 配置 目前 歡迎大家前往騰訊雲+社區,獲取更多騰訊海量技術實踐幹貨哦~ 本文由騰訊遊戲雲發表於雲+社區專欄 看著精彩的德甲賽事,突然裁判一聲口哨,球賽斷掉了,屏幕開始自動播放“吃麥趣雞盒,看德甲比賽”的視頻廣告 那麽

QQ音樂開發探討如何利用騰訊雲SDK在直播中加入視訊動畫

歡迎大家前往騰訊雲+社群,獲取更多騰訊海量技術實踐乾貨哦~ 本文由騰訊遊戲雲發表於雲+社群專欄 看著精彩的德甲賽事,突然裁判一聲口哨,球賽斷掉了,螢幕開始自動播放“吃麥趣雞盒,看德甲比賽”的視訊廣告 那麼問題來了,如何在直播流中,無縫的插入點播視訊檔案呢?

關於網站增加qq客服的開發iwebshop框架實例

iwebshop框架實例制作店鋪街,在店鋪街展示客服qq號碼!開發思路:數據庫設計上需要設置一個存放qq號的數據表,裏面包含店鋪id,表結構id自增長後臺開發思路:需要通過查詢把當下商家店鋪的客服qq從相對的數據庫表中讀出,(可以是json格式)如果是json格式 需要把取出的字符串轉換成php變量!這樣方可

Nuxt.js服務端渲染實踐開發到部署

atd 格式 驗證 for replace 註冊 ear coo java 感悟 經過幾個周六周日的嘗試,終於解決了服務端渲染中的常見問題,也成功說服了公司新項目采用前後端分離的解決方案,當SEO不在是問題的時候,或許才是我們搞前端的真正的春天,其中也遇到了一些小坑,Nux

武漢微信開發之小程序開發應該哪些方面做起?

如果 不同 你是 實踐 可能 了解 上帝 內容 簡單的 很多武漢微信開發的從業者不知道微信小程序開發應該從哪些方面做起,究其原因,大概是大多數武漢微信開發從業者都是技術出身,而不知道微信小程序開發也是應該從運營角度去考慮的。技術不懂運營,這個是所有開發類行業中的硬傷。所以,

移動web:原生開發打包嵌入h5頁面 webApp:全部都是H5開發的應用 混合APP:使用第三方開發平臺apicloudappcanhbuilder等開發cordova技術打包 原生APP:就是eclipse開發或者studio等工具開發

應用 手機 .com net ack 自動連接 pan 經驗 使用 論壇43213 移動端webApp兼容問題解決 談談App混合開發 Hybrid APP混合開發的一些經驗和總結 PhoneGap是一個采用HTML,CSS和JavaScript的技術,創建

妙味課堂Jquery入門到插件開發到模擬視頻教程 Jquery實戰開發 Jquery UI

htm com http val targe 入門到 設計 pre 一課 <jQuery課程 初級到高級到模擬>├<第一課:jQuery初級>│ ├01 妙味雲課堂-jQuery簡介.mp4│ ├02 妙味雲課堂-jQuery設計思想之選擇元素.

行業新生態區塊鏈系統開發開始

區塊鏈技術區塊鏈技術的特征使其可以在互聯網的各個領域大展拳腳,為各行各業提供一種全新的解決方案,如金融、信貸、物聯、溯源等等。不少區塊鏈項目針對特定行業與領域,基於可信公鏈打造商業應用,形成了全新的行業生態。在文化行業,基於區塊鏈構建生態平臺,已經有不少優秀的項目。 針對文化資產交易領域,一套基於全球文化產

產品經理的角度幸運快三源碼開發看手機端語音助手

理解 以及 體驗 意圖 參考 自然語言理解 入門 意思 文章 本文從PM的角度對手機端語音助手進行了思考,包括當前市場情況、PM在設計產品時的思路等。 從產品經理的角度,看手機端語音助手 一、手機端語音助手的現狀 蘋果siri的出現,帶動了手機端智能助手的發展,如今,蘋果s

【免費公開課】7月31晚8點韓立剛老師帶你走進IT運維零開始成長為IT專家~

計算 sof 數據 系統 初中 eight 路線 ESS 數據庫設計 直播主題:如何從零開始成長為IT運維專家直播時間:7月31日晚8點-9點主講講師介紹:51CTO金牌講師:韓立剛<<點擊進入講師主頁河北師大軟件學院網絡教研室主任,河北地質大學客座教授,微軟認

Python3 視頻教程全網最全的視頻教程爬蟲入門到實戰

python3 python基礎 入門到 分享 視頻 pst https size 分布式爬蟲 需要聯系我:QQ:1844912514 最新Python基礎班+就業班視頻教程 鏈接: python分布式爬蟲打造搜索引擎鏈接: https://pan.baidu.com/

開發一款即時通訊App這幾步開始

聯系 多功能 數據監控 live 裁剪 ren vertica super 聯系人 歡迎大家前往騰訊雲+社區,獲取更多騰訊海量技術實踐幹貨哦~ 本文由騰訊雲視頻發表於雲+社區專欄 關註公眾號“騰訊雲視頻”,一鍵獲取 技術幹貨 | 優惠活動 | 視頻方案 “晚上去哪吃