創(chuàng)建一個(gè)egg.js + next.js的應(yīng)用

github地址:github完整代碼

egg 初始化

首先全局安裝egg-init命令瞒斩,在本地新建文件夾粹懒,進(jìn)入文件夾執(zhí)行初始化命令。

npm install egg-init -g
mkdir eggNext && cd eggNext
egg-init --type=simple
npm install
npm run dev

初始化目錄關(guān)鍵結(jié)構(gòu)如下所示

+ app
   + controller
      |- home.js
    router.js
+ config
    |-config.default.js
    |-plugin.js
  1. app目錄就是egg服務(wù)的項(xiàng)目項(xiàng)目,其中router文件用以指定path對應(yīng)的處理函數(shù)堪澎,controller中就是path相關(guān)的處理函數(shù)。
    可以看到在router中把/路徑指向了controller中home文件來進(jìn)行處理味滞。
    后續(xù)啟動next項(xiàng)目時(shí)樱蛤,就需要在這里增加路徑
module.exports = app => {
  const { router, controller } = app;
  router.get('/', controller.home.index);
};
  1. config用以存放相關(guān)的配置文件, 完整配置應(yīng)該是有如下環(huán)境的區(qū)分的,默認(rèn)生成的項(xiàng)目目錄里只有config.default.js剑鞍。后續(xù)我們也會增加配置配置文件來對next進(jìn)行一些配置昨凡。
+ config
|- config.default.js
|- config.prod.js
`- config.local.js

next 初始化

全局安裝next react react-dom

npm install next react react-dom --save

egg + next

我們不采用next自帶的啟動方式,而是采用egg.js啟動next的方式蚁署。
參考next文檔中自定義服務(wù)器中的demo便脊,

// server.js
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
 createServer((req, res) => {
   // Be sure to pass `true` as the second argument to `url.parse`.
   // This tells it to parse the query portion of the URL.
   const parsedUrl = parse(req.url, true)
   const { pathname, query } = parsedUrl

   if (pathname === '/a') {
     app.render(req, res, '/b', query)
   } else if (pathname === '/b') {
     app.render(req, res, '/a', query)
   } else {
     handle(req, res, parsedUrl)
   }
 }).listen(3000, err => {
   if (err) throw err
   console.log('> Ready on http://localhost:3000')
 })
})

雖然服務(wù)端不是采用的egg.js的方式,但是我們可以提取出自定義服務(wù)器啟動next最關(guān)鍵的部分光戈。

const next = require('next');
app = next(config);
app.render(req, res, '/path', query);
  1. 傳入config參數(shù)對next進(jìn)行配置哪痰,可配置參數(shù)如下,


    config參數(shù).png
  2. 使用render函數(shù)來渲染對應(yīng)的頁面田度。/path對應(yīng)1中dir next項(xiàng)目下的pages里的路徑妒御。

根據(jù)以上分析,我們進(jìn)行如下操作

  1. 我們在 app的config下镇饺,創(chuàng)建config.local.js乎莉,config.beta.js, config.prod.js奸笤,增加如下配置惋啃,后續(xù)用來配置next參數(shù)。
module.exports = appInfo => {
  const config = {};
  config.next = {
    dev: true,    // config.prod.js中設(shè)置為false
    dir: './ssr',
  };
  return {
    ...config,
  };
};

  1. 創(chuàng)建srr/pages/index.js监右,這就是我們的next的頁面啦
// ssr/pages/index.js
export default function Home() {
    return (
      <div>
        test
      </div>
    )
  }

上述這些準(zhǔn)備工作做好了边灭,最最難的復(fù)雜的一點(diǎn)來了,我們?nèi)绾蝸碛胑gg啟動next項(xiàng)目呢健盒。

  1. 首先我們通過extend/application.js將next擴(kuò)展到app上绒瘦,在app目錄下新建extend/application.js 【 egg.js extend參考文檔】,之后你在app目錄下通過app.next就可以訪問到我們配置好的next了~
'use strict';
const Next = require('next');
const NEXT = Symbol('Application#next');
module.exports = {
  get next() {
    if (!this[NEXT]) {
      this[NEXT] = Next(this.config.next);
    }
    return this[NEXT];
  },
};
  1. 在router扣癣、controller里建立路徑的的處理函數(shù)惰帽,使用app.next.render來渲染next的頁面
// router.js
router.get('/ssr/*', controller.ssr.index);
// controller/ssr.js
const Controller = require('egg').Controller;

class SSRController extends Controller {
  async index() {
    const { ctx, app } = this;
    // ctx.body = 'hi, egg';
    const { req, res } = ctx;
    ctx.body = await app.next.render(req, res, '/');
  }
}
module.exports = SSRController;

滿心歡喜啟動....結(jié)果報(bào)了如下錯誤。??


error.png

我們重新回到next的自定義服務(wù)器啟動的demo父虑「眯铮可以看出,啟動服務(wù)器是在next準(zhǔn)備好了之后,才會啟動服務(wù)呜魄,而我們項(xiàng)目里是沒有等到next加載好服務(wù)就已經(jīng)啟動悔叽,導(dǎo)致資源加載可能存在問題。

// demo
app.prepare().then(() => {
  createServer(() => {})
})

大刀闊斧進(jìn)行再來改進(jìn)爵嗅!egg雖然幫我們把啟動都封裝好了娇澎,但是我們也可以進(jìn)行自定義啟動,我們在項(xiàng)目目錄下建立app.js操骡,寫入以下代碼九火,保證next prepare成功后再啟動服務(wù)【egg自啟動

// app.js
'use strict';
module.exports = app => {
  // NOTE: 這里一定要等next prepare好再啟動服務(wù)
  app.next.prepare().then(() => {
    app.beforeStart(() => {
      process.on('unhandledRejection', (reason, p) => {
      });
      process.on('uncaughtException', reason => {
      });
    });
  });
};

很好,我們再啟動一次册招! 這次頁面沒有報(bào)錯岔激,只是空白,打開控制臺是掰,發(fā)現(xiàn)請求了很多js資源虑鼎,都沒有找到。


error2.png

可以看到這些js資源全部都是_next目錄下的键痛,而我們的項(xiàng)目里沒有處理_next路徑炫彩。


error3.png

其實(shí)是next在啟動的時(shí)候會在ssr項(xiàng)目目錄下生成.next目錄,回到demo絮短,next里其實(shí)是handle這些_next請求的方法的江兢,當(dāng)請求路徑不滿足其他指定路徑時(shí),會把路徑交給handle來處理丁频。
const handle = app.getRequestHandler()
else {
  handle(req, res, parsedUrl)
}

根據(jù)這個(gè)杉允,我們增加一個(gè)中間件,讓路由先通過handle函數(shù)處理一下席里。
1 在app目錄下增加middleware文件夾叔磷,新建ssr.js

'use strict';
const { parse } = require('url');
module.exports = (options, app) => async (ctx, next) => {
 const { path, req, res } = ctx;
 if (/\/_next\//.test(path)) {
   const parsedUrl = parse(req.url, true);
   ctx.status = 200;
   if (/\.js$/.test(path)) {
     ctx.set('Content-Type', 'application/javascript');
   }
   if (/\.css$/.test(path)) {
     ctx.set('Content-Type', 'text/css');
   }
   const handle = app.next.getRequestHandler();
   await handle(req, res, parsedUrl);
 }
 await next();
}
  1. 在config.default.js里增加中間件配置
  config.middleware = ['ssr'];

再次啟動項(xiàng)目~ 成功啦~~


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奖磁,隨后出現(xiàn)的幾起案子改基,更是在濱河造成了極大的恐慌,老刑警劉巖咖为,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秕狰,死亡現(xiàn)場離奇詭異,居然都是意外死亡躁染,警方通過查閱死者的電腦和手機(jī)封恰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來褐啡,“玉大人,你說我怎么就攤上這事鳖昌”钙瑁” “怎么了低飒?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長懂盐。 經(jīng)常有香客問我褥赊,道長,這世上最難降的妖魔是什么莉恼? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任拌喉,我火速辦了婚禮,結(jié)果婚禮上俐银,老公的妹妹穿的比我還像新娘尿背。我一直安慰自己,他們只是感情好捶惜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布田藐。 她就那樣靜靜地躺著,像睡著了一般吱七。 火紅的嫁衣襯著肌膚如雪汽久。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天踊餐,我揣著相機(jī)與錄音景醇,去河邊找鬼。 笑死吝岭,一個(gè)胖子當(dāng)著我的面吹牛三痰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苍碟,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼酒觅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了微峰?” 一聲冷哼從身側(cè)響起舷丹,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蜓肆,沒想到半個(gè)月后颜凯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仗扬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年症概,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片早芭。...
    茶點(diǎn)故事閱讀 39,919評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡彼城,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情募壕,我是刑警寧澤调炬,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站舱馅,受9級特大地震影響缰泡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜代嗤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一棘钞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧干毅,春花似錦宜猜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至趴捅,卻和暖如春垫毙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拱绑。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工综芥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人猎拨。 一個(gè)月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓膀藐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親红省。 傳聞我的和親對象是個(gè)殘疾皇子额各,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評論 2 354