教你如何從零搭建一個Node.js 的 MVC 項目

1、準備工做

安裝 koa、熱更新用的 supervisor

PS:安裝 Node.js 就不介紹了,最新版本就 okcss

npm init -y
cnpm i koa --save
cnpm i supervisor --save-dev
複製代碼

創建入口文件 app.js

./app.jshtml

const Koa = require("koa");
const app = new Koa();

app.listen(3000,()=>{
    console.log("server is running..")
});
複製代碼

./package.jsonvue

···
  "scripts": {
    "start": "npm run dev",
    "dev": "supervisor -i ./node_modules ./app.js"
  },
···
複製代碼
注意 supervisor -i ./node_modules ./app.js 這條命令! 要是不把 ./node_modules 排除掉的話,supervisor 會吃光你的內存,風扇狂轉!

2、創建 controller

創建如圖所示目錄結構

controller 麼,先對路由下手就完了 ~node

因此,繼續下和路由有關的包吧~

cnpm i koa-simple-router --save
複製代碼

ok,咱們能夠愉快的寫 controller 的父類了

./controllers/indexControllers.jses6

class IndexControllers {
    constructor() {

    }

    actionIndex(ctx, next) {
        ctx.body = "koa2 is running~~";
    }
}

module.exports = IndexControllers;
複製代碼

繼承父類的 controller

./controllers/index.jsnpm

const router = require("koa-simple-router");
const IndexController = require("./indexControllers");
const indexController = new IndexController();

module.exports = app => {
    app.use(router(_ => {
        _.get("/", indexController.actionIndex);
        _.get('/index.html', indexController.actionIndex);
    }))
};
複製代碼

_.get("/", indexController.actionIndex);
_.get('/index.html', indexController.actionIndex); 僞靜態json

回到入口文件 app.js 註冊路由

const Koa = require("koa");
const app = new Koa();
// 註冊路由
require("./controllers/index")(app);

app.listen(3000,()=>{
    console.log("server is running..")
});

複製代碼

OK! 咱們如今應能夠啓動服務了,npm start 以後訪問本地服務,就能夠看到效果了,是否是還有點兒小激動?瀏覽器


3、創建配置文件優化項目

app.js 裏面的app.listen(3000,()=>{console.log("server is running..")});3000扔到入口文件不三不四,咱們能夠把它放到另外的位置緩存

項目根目錄創建 config 相關文件,如圖所示

./config/index.jsbash

const config={
    port:3000
};

module.exports(config);
複製代碼

./app.js

const Koa = require("koa");
const app = new Koa();
const config = require("./config");
// 註冊路由
require("./controllers/index")(app);

app.listen(config.port, () => {
    console.log("server is running..")
});
複製代碼

既然要優化端口,不如把生產環境的 80 端口開發環境的 3000 端口分開吧

安裝 cross-env

cnpm i cross-env --save-dev
複製代碼

改造 package.json 和 config.js

./package.json

···
  "scripts": {
    "start": "npm run dev",
    "dev": "cross-env NODE_ENV=development supervisor -i ./node_modules ./app.js"
  },
···
複製代碼

./config/index.js

const config = {};

if (process.env.NODE_ENV === "development") {
    config.port = 3000;
} else if (process.env.NODE_ENV === "production") {
    config.port = 80;
}

module.exports = config;
複製代碼

4、實現 view 層

老規矩,先創建目錄吧

那麼問題來了,這些靜態文件咱們得把它們加載進來呀

安裝 koa-static

cnpm i koa-static --save
複製代碼

繼續改造 config.js

./config/index.js

const path = require("path");

const config = {
    staticDir: path.join(__dirname, "..", "assets")
};

if (process.env.NODE_ENV === "development") {
    config.port = 3000;
} else if (process.env.NODE_ENV === "production") {
    config.port = 80;
}

module.exports = config;
複製代碼

把靜態文件加載進來

./app.js

const Koa = require("koa");
const app = new Koa();
const config = require("./config");
const serve = require("koa-static");
// 註冊路由
require("./controllers/index")(app);
// 加載靜態文件
app.use(serve(config.staticDir));

app.listen(config.port, () => {
    console.log("server is running..")
});
複製代碼

5、實現 Model 層

在這裏,咱們的模板引擎用的是 koa-swig.js

安裝模板引擎

cnpm i koa-swig --save
cnpm i co --save
複製代碼

注:co是歷史遺留問題,用來把koa1編譯成koa2

完善 view 層

./views/layouts/layout.html

這個將會是全部模板的「祖宗」

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock %}</title>

    {% block head %}
    {% endblock %}

    {% block link %}
    {% endblock %}
</head>
<body>
{% block content %}
{% endblock %}

{% block script %}
{% endblock %}
</body>
</html>
複製代碼

./views/index.html

繼承了 layout.html

{% extends './layout.html' %}
{% block title %} 圖書管理首頁 {% endblock %}

{% block link %}
<link rel="stylesheet" href="/styles/index.css">
{% endblock %}

{% block content %}
<div>
    <h1>{{content}}</h1>
</div>
{% endblock %}

{% block script %}
<script src="/scripts/index.js"></script>
{% endblock %}
複製代碼

./assets/styles/index.css

css

body{
    background: #000;
}
h1{
    color:greenyellow;
}
p{
    color:yellow;
}
複製代碼

./assets/scripts/index.js

js

const content = "你好koa~~";
console.log(content);
複製代碼

完善 controller 層

./controllers/index.js

class IndexControllers {
    constructor() {

    }

    async actionIndex(ctx, next) {
        ctx.body = await ctx.render('index.html',{
            content:"hello node!"
        });
    }
}

module.exports = IndexControllers;
複製代碼

在入口文件註冊模板引擎

./config/index.js

const path = require("path");

const config = {
    viewDir: path.join(__dirname, "..", "views"), // 把視圖層加載引來
    staticDir: path.join(__dirname, "..", "assets")
};

if (process.env.NODE_ENV === "development") {
    config.port = 3000;
} else if (process.env.NODE_ENV === "production") {
    config.port = 80;
}

module.exports = config;
複製代碼

./app.js

const Koa = require("koa");
const app = new Koa();
const config = require("./config");
const serve = require("koa-static");
const render = require("koa-swig");
const co = require("co");
// 註冊路由
require("./controllers/index")(app);
// 加載靜態文件
app.use(serve(config.staticDir));
// 配置模板引擎
app.context.render = co.wrap(render({
    root: config.viewDir, // 把視圖層加載引來
    autoescape: true,
    cache: false, // 緩存
    ext: 'html',
    writeBody: false
}));
app.listen(config.port, () => {
    console.log("server is running..")
});
複製代碼

OK,咱們如今再看下

若是你作到這步了,那麼勝利的曙光就要降了!

6、安裝 babel

有人會問,如今的代碼有什麼問題嗎?,有,並且很多。

你們回到 ./assets/scripts/index.js文件,若是我新增了一行以下代碼,那會怎樣呢?

const content = "你好koa~~";
console.log(content);

export default content;
複製代碼

很正常麼,由於它不認識啊,另外若是瀏覽器不支持es6語法怎麼辦?

安裝 babel

cnpm i babel @babel-cli --save-dev
cnpm i babel @babel-core --save-dev
cnpm i babel @babel-preset-env --save-dev
複製代碼

項目根目錄新建 .babelrc 文件

./.babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": "systemjs"
      }
    ]
  ]
}
複製代碼

完善 package.json

···
  "scripts": {
    "start": "npm run dev",
    "dev": "cross-env NODE_ENV=development supervisor -i ./node_modules ./app.js",
    "build": "babel ./assets/scripts/index.js -o ./assets/scripts/index-bundle.js"
  },
···
複製代碼

npm run build 一下

完善 views/index.html

{% extends './layouts/layout.html' %}
{% block title %} 圖書管理首頁 {% endblock %}

{% block link %}
<link rel="stylesheet" href="/styles/index.css">
{% endblock %}

{% block content %}
<div>
    <h1>{{content}}</h1>
</div>
{% endblock %}

{% block script %}
<script type="module">
    import ("/scripts/index.js").then((_) => {
        console.log(_.default);
    })
</script>
<script type="nomodule" src="https://cdn.bootcss.com/systemjs/6.2.5/system.min.js"></script>
<script type="nomodule">
    System.import("/scripts/index-bundle.js").then((_) => {
        console.log(_.default);
    })
</script>
{% endblock %}
複製代碼

在這裏咱們引入了萬能木塊加載器 systemjs

OK,如今咱們的第一階段學習完成了~

7、CSR + SSR

好比咱們能夠把vuejQuery

./views/layouts/layout.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock %}</title>

    {% block head %}
    {% endblock %}

    {% block link %}
    {% endblock %}
</head>
<body>
{% block content %}
{% endblock %}
<script src="https://cdn.bootcss.com/vue/2.6.11/vue.min.js"></script>
{% block script %}
{% endblock %}
</body>
</html>
複製代碼

./assets/scripts/index.js

const content = "你好koa~~";
console.log(content);
const app6 = new Vue({
    el: '#app-6',
    data: {
        message: 'Hello Vue!'
    }
});
export default content;
複製代碼

./views/index.html

{% extends './layouts/layout.html' %}
{% block title %} 圖書管理首頁 {% endblock %}

{% block link %}
<link rel="stylesheet" href="/styles/index.css">
{% endblock %}

{% block content %}
<div>
    <h1>[[content]]</h1> // 注意!
</div>
<div id="app-6">
    <p>{{ message }}</p>
    <input v-model="message">
</div>
{% endblock %}

{% block script %}
<script type="module">
    import ("/scripts/index.js").then((_) => {
        console.log(_.default);
    })
</script>
<script type="nomodule" src="https://cdn.bootcss.com/systemjs/6.2.5/system.min.js"></script>
<script type="nomodule">
    System.import("/scripts/index-bundle.js").then((_) => {
        console.log(_.default);
    })
</script>
{% endblock %}

複製代碼
注意!vue 和 swig 的模板引擎的標識符不能衝突

因此咱們還要改下配置文件

./app.js

const Koa = require("koa");
const serve = require("koa-static");
const render = require("koa-swig");
const config = require("./config");
const co = require("co");
const app = new Koa();
app.use(serve(config.staticDir));
app.context.render = co.wrap(render({
    varControls:["[[","]]"],// 自定義模板語法
    root: config.viewDir,
    autoescape: true,
    cache: false, // 緩存
    ext: 'html',
    writeBody: false
}));
require("./controllers")(app);
app.listen(config.port, () => {
    console.log("圖書管理平臺啓動成功...");
});
複製代碼

大功告成!

相關文章
相關標籤/搜索