Egg 實現一個 mTime 時光網

先放出項目地址:github.com/OrangeXC/mt…css

有一段時間沒更新博客了,今天的文章主要圍繞 egg 進行,長時間沉浸在前端框架中,遊離到傳統 MVC 的開發模式還真不太適應,很久不寫 MVC 項目了。html

說下今天的主角 egg,在前幾天的騰訊 IMweb Conf 2017 大會第一個演講就是 egg,egg 是一個 node 框架基於 koa,寓意孵化新生,本項目的 logo 就有點怪咖,是個煎蛋,這個嘛,沒有新生了,由於這個項目沒有發揮 egg 太多優點。前端

之全部這麼說是由於我調用的三方 api,說到這裏爲何不用 vue,react,angular 等直接請求接口呢,由於這裏涉及到一點點數據庫操做,算是沒白折騰 egg。vue

不寫科普文,簡單的文檔層面能夠直接到 egg官網node

說說項目的搭建,egg 提供了 cli,項目的目錄也遵循約定規範,不可隨意篡改。mysql

egg-project
├── package.json
├── app.js (可選)
├── agent.js (可選)
├── app
|   ├── router.js
│   ├── controller
│   |   └── home.js
│   ├── service (可選)
│   |   └── user.js
│   ├── middleware (可選)
│   |   └── response_time.js
│   ├── schedule (可選)
│   |   └── my_task.js
│   ├── public (可選)
│   |   └── reset.css
│   ├── view (可選)
│   |   └── home.tpl
│   └── extend (可選)
│       ├── helper.js (可選)
│       ├── request.js (可選)
│       ├── response.js (可選)
│       ├── context.js (可選)
│       ├── application.js (可選)
│       └── agent.js (可選)
├── config
|   ├── plugin.js
|   ├── config.default.js
│   ├── config.prod.js
|   ├── config.test.js (可選)
|   ├── config.local.js (可選)
|   └── config.unittest.js (可選)
└── test
    ├── middleware
    |   └── response_time.test.js
    └── controller
        └── home.test.js複製代碼

初始配置

這裏主要關注配置文件 config/config.default.jsapp 目錄react

先看下 config/config.default.js 文件jquery

module.exports = appInfo => {
  const config = {};

  // should change to your own
  config.keys = appInfo.name + '_1504252356337_1029';

  // view
  config.view = {
    defaultViewEngine: 'nunjucks',
    mapping: {
      '.tpl': 'nunjucks',
    },
  };

  config.sequelize = {
    dialect: 'mysql', // support: mysql, mariadb, postgres, mssql
    database: 'mtime',
    host: '127.0.0.1',
    port: '3306',
    username: 'root',
    password: '',
  };

  config.mysql = {
    client: {
      host: '127.0.0.1',
      port: '3306',
      user: 'root',
      password: '',
      database: 'mtime',
    },
    app: true,
    agent: false,
  };

  return config;
};複製代碼

這裏面指定了模板文件,數據庫的鏈接參數。注意這樣並不能生效,由於咱們沒有指定 plugin 對應的 npm 包,前提是要安裝這些依賴。ios

config/plugin.jsgit

exports.nunjucks = {
  enable: true,
  package: 'egg-view-nunjucks',
};

exports.sequelize = {
  enable: true,
  package: 'egg-sequelize',
};

exports.mysql = {
  enable: true,
  package: 'egg-mysql',
};複製代碼

在網頁的公共頭部有城市選擇,以下

這裏比較坑的是 api 是非官方的 api,只能本身整理城市列表,存到了本地的 init 目錄下的 location.json

那麼問題來了,每次調用本地文件明顯是不合情理的,之後還會涉及到城市信息變更,這裏做爲第一次導入的 init 數據寫入 mysql,以後統一從 myspl 獲取 location。

初始化代碼按約定放在 app.js 中,容許咱們進行初始化操做。

const fs = require('fs');

module.exports = app => {
  app.beforeStart(async () => {
    // 應用會等待這個函數執行完成才啓動
    await app.model.sync({ force: true });

    app.database = await app.mysql.createInstance(app.config.mysql.client);

    const locations = JSON.parse(fs.readFileSync('./init/location.json'));

    await app.mysql.insert('locations', locations.data);
  });
};複製代碼

雖然 egg-mysql 和 egg-sequelize 文檔都有介紹,這裏簡單說下

  • await app.model.sync({ force: true }); 是同步 model 到數據庫,主要是同步數據庫表和字段

  • app.database = await app.mysql.createInstance(app.config.mysql.client); 是在應用運行時動態的從配置中心獲取實際的參數,再來初始化一個實例。

注:這裏官網的代碼有點小坑,親測下面官網代碼的 configCenter 並無 fetch 方法,遇到相同坑的該用上面的代碼便可

const mysqlConfig = yield app.configCenter.fetch('mysql');
app.database = app.mysql.createInstance(mysqlConfig);複製代碼
  • const locations = JSON.parse(fs.readFileSync('./init/location.json')); 這句不解釋了,看不懂先學學 node 基礎

  • await app.mysql.insert('locations', locations.data); 這句是將 Array 直接存到數據庫 locations 表,這裏見官網 如何編寫 CRUD 語句部分,官網的例子是插入單條數據(以 Object 的格式),固然這裏 Array 建立多條也是能夠的

注:第一句咱們建立了 locations 表,表裏多了兩個默認字段分別是 created_atupdated_at,批量導入數據裏沒有這兩個字段,故報錯,解決辦法是在 model 的 location.js 裏面給這兩個字段 default 值,以下

created_at: {
  type: DATE,
  default: new Date(),
},
updated_at: {
  type: DATE,
  default: new Date(),
}複製代碼

上面的代碼分別依賴兩個庫 egg-sequelizeegg-mysql,二者均與操做 mysql 有關,固然根據業務須要選擇其一也可,至於兩個庫分別有哪些功能能夠直接轉到 Github 看 document

到這裏咱們去 mysql 看一眼

一切準備工做就緒

路由搭建

頁面總體分紅三部分

header
router
footer複製代碼

router 根據路由動態渲染,也是主要業務邏輯的區塊,主頁電影列表分爲三類 正在售票 正在熱映 即將上映 分別對應三個路由 / /hot /new,固然考慮到城市因素(不一樣城市上映電影有細微差異),在本項目裏寫到了 query 裏面,全部路由後面都帶着一個 query 看着着實不爽,下一步我會把它移到 cookie 或者 localStorage,代碼約定寫在 app/router.js 裏以下

app.get('/', 'home.index');
app.get('/hot', 'hot.index.index');
app.get('/new', 'new.index.index');複製代碼

解釋下路由對應 controller 的語法,規則是 文件名/函數名文件夾/文件名/函數名,固然這是我我的猜想,發現奏效,官網的寫法法能夠到 如何定義router

下面還有詳情頁 短評頁 熱評頁 劇照和海報頁 預告花絮頁,分別對應下面的路由

app.get('/movie/:id', 'movie.index.index');
app.get('/comment/:movieId', 'comment.normal.index');
app.get('/hot_comment/:movieId', 'comment.hot.index');
app.get('/stills/:movieId', 'stills.index.index');
app.get('/video/:movieId', 'video.index.index');複製代碼

路由到這裏介紹完了,沒什麼好講的,不看網站這個項目的概況也是一目瞭然,接下來的事情就是 controller 來調取 model 數據渲染到頁面了,下面不一一陳述 controller,抽離一點可講的。

controller 搭建

按照約定咱們直接找到 controller 目錄,看到所有的 controller

const locations = await ctx.model.Location.findAll(); 這一句找到全部的 location 數組。

細心看的人會發現每一個 controller 都有下面的代碼,咱們要獲取所有 location 列表,並找到 query 裏面的那條數據,默認是北京,這裏頁面公共部分 header 一直存在一個 location 下拉列表,因此每次都要將數據拋給頁面,更好的解法是點開下拉列表異步拉取全部 location 再渲染進去。

ctx.query.location
  ? location = locations.find(({ id }) => id === Number(ctx.query.location))
  : location = {
    id: 290,
    name: '北京',
  };複製代碼

這裏徹底的 get 數據,一個前端層面的 ajax 都沒有,原諒個人偷懶,這樣致使了全部 controller 的代碼冗餘。

剩下的就是去調用 mtime 的 api 了,感受很好的是 egg 爲咱們封裝了全局 http 方法,HttpClient

使用簡單,參數簡單,堪比 axios 的便捷。你們本身體會吧。

view 搭建

說到 view 層就到了大前端的天下,玩的 6 的話,這一層能夠無限延展,從最簡單的模板(pug,ejs,swig,nunjucks 等),到 (vue,react,angular 等),再到(Andriod, IOS),再再到(RN,weex),甚至是小程序接口。

服務層讓我喜歡的就是可渲染模板,可吐數據,原本是想搞個先後端分離,後來被本身氣到,調用人家的 api,居然不直接寫 view 層,搞個 egg 進來沒起到——卵用。

不自覺講起了段子,索性就回歸 10 年前的前端,拋開 MVVM,甚至擼起了 jquery。

牢騷一堆,這裏用的是 nunjucks,官網的例子用的就是這個模板。

到了這裏咱們的頁面完成了。。。雖然什麼也沒講,我默認你們都看得懂模板的哈,至於 Bulma 的初衷是不想用 Jquery(bootStrap 你們懂得),奈何找不到喜歡的輪播,找了許久的輪播居然還依賴 jquery。。。

不足

這裏聲明人家 mTime 的接口並非官方公開的,來自 github.com/jokermonn/-… 的服務器禁止跨域請求 MP4 資源,嘗試如下幾個方法解決這個問題

  • iframe,沒能成功,訪問失敗
  • <a target="_blank">,也沒解決問題,不過奇怪的是複製 mp4 的 url 到新 tab 回車能夠訪問,a 標籤跳轉新 tab 則失敗,js 的 window.open() 沒有嘗試
  • node 請求 mp4 的 buffer 轉成 stream 後再拋給前端,能力有限沒能解決問題。

一方面做者能力緣由,一方面違背 mTime 節省視頻服務器流量的想法,在視頻頁給了連接能夠跳到真正的 mTime 官網。

算是遺憾吧,若是有 node 端轉發視頻請求經驗的大神歡迎賜教。

總結

項目是在短時間內速成的,好多細節沒考慮到位,望你們多吐槽,寫這篇文章的目的是給想了解 egg 的開發者一個小 demo,真正的生產模式比這個複雜的多,本文也天然就不值一提。

最後

本文同步更新至個人我的博客 orangexc.xyz

相關文章
相關標籤/搜索