vue-koa 應用腳手架

小文醬發表於2018-06-22

專案地址: github.com/xxx-fe/mall…

mall-app

vue koa 應用腳手架

支援多語言,多頁應用,多種MOCK

Architecture

前端

  • 樣式:scss.
  • 庫管理:npm,bower
  • 框架:vue2.
  • 模板引擎:handlebars4.
  • 打包:webpack4.
  • 圖示:iconfont.
  • 元件庫:element-ui.

中臺

  • 框架:koa2, nodejs>=7

目錄結構

.
├── build                                       # 使用 vue-cli 2.9.3(有修改)
├── config                                      # 使用 vue-cli 2.9.3(有修改)
├── server                                      # 服務端(koa,nodejs)
│    ├── api                                    #     介面
│    ├── controller                             #     控制器
│    ├── lib                                    #     庫
│    ├── mock                                   #     模擬資料
│    ├── router                                 #     路由(koa-router,或前端用vue-router)
│    ├── view                                   #     檢視
│    ├── server.js                              #     服務端入口
├── dist                                        # 生產目錄
├── public                                      # 公共資源(例如訪問http://localhost:3333/public/img/bg.jpg)
│    ├── img                                    #     圖片
│    └── vendor                                 #     第三方外掛
├── web                                         # 前端(vue,js,css...)
│    ├── components                             #     元件
│    ├── directives                             #     指令
│    ├── entry                                  #     入口
│    ├── filters                                #     過濾
│    ├── global                                 #     全域性設定
│    ├── mock                                   #     模擬資料
│    ├── pages                                  #     頁面                  
│    ├── styles                                 #     樣式
│    ├── vendor                                 #     第三方外掛
│    ├── webpack.entry.conf.js                  #     入口配置檔案
│    ├── webpack.dev.conf.js                    #     開發模式配置檔案
│    └── webpack.pord.conf.js                   #     生產模式配置檔案
│   config.yml                                  #     通用配置檔案,整個腳手架很多功能都與它有關
複製程式碼

安裝

npm install    # npm 安裝
bower install  # bower 安裝
複製程式碼

命令

npm run dev    # 啟動開發模式(dev)
npm run build  # 構建專案
npm run prod   # 啟動生產模式(prod)
複製程式碼

example

1.新建應用路由

  • /server/router/app/index.js
const page = require('../controller/index');
module.exports = [
    {path: '', ctrl: page.home},
    {path: 'main', ctrl: page.home},
    {path: 'api/list', ctrl: page.list, method: 'post'}
];

複製程式碼

2.新建應用控制器

  • /server/controller/index.js
const api = require('../api/index');
class page {
    async home(ctx, _next) {
        let locals = {
            title: 'home-page'
        };
        await ctx.render('pages/home', locals);
    }

    async list(ctx, _next) {
        let locals = {
            list: await api.getList(ctx)
        };
        ctx.body = locals;
    }
}
module.exports = new page();
複製程式碼

3.新建應用檢視

  • /server/view/pages/home.hbs
{{#extend "layout-default"}}          # 使用layout-default佈局
    {{#content "head"}}
        {{{parseUrl 'app.css'}}}      # app應用的css,直接引用
    {{/content}}                      # 不需要新建,build時會抽取vue的style成獨立的檔案.否則生產模式看不到樣式.
    {{#content "body"}}
        <div id="home-app"></div>
        {{{parseUrl 'app.js'}}}       # app應用的js(相應webpack.entry)
    {{/content}}
{{/extend}}
複製程式碼
  • /server/view/layout/**.hbs 以檔名註冊為handlebars partial.

引用:

parseUrl

解析url, handlebars自定義helpers.結合ctx.state.appName,根據當前開發環境返回正確的url.

dev

ctx.state.appName='app'

{{{parseUrl 'app.css' 'app.js'}}}
複製程式碼

↓↓↓

<script web="app.js"></script>
複製程式碼

ctx.state.appName=''; 或不設定

↓↓↓

<link href="/dist/static/css/app.[chunkhash].css" type="text/css" rel="stylesheet">
<script web="app.js"></script>
複製程式碼

prod

<link href="/dist/static/css/app.[chunkhash].css" type="text/css" rel="stylesheet">
<script web="/dist/static/js/app.[chunkhash].js"></script>
複製程式碼

有這種場景

如果存在多個app如app1,app2.在控制器就需要設定ctx.state.appName ='app的名字'.否則讀取樣式會不正確.

4.新建應用頁面

  • /web/pages/app/home.vue
...
<script>
    export default {
        data () {
            return {
                list: ''
            }
        },
        mounted(){
            this.$http.post('/api/list').then(response => {
                console.log(response);
                this.list = response.data.list
            }, response => {
                console.log(response);
            })
        }
    }
</script>
...
複製程式碼

5.新建應用入口

  • /web/pages/app/index.js
import homeApp from './home.vue';
if(document.getElementById('home-app')) {
    new Vue({
        render: h => h(homeApp)
    }).$mount('#home-app');
}
複製程式碼

瀏覽: http://localhost:3333/

APPSTATE

整個app的傳遞資訊(ctx.state封裝),部分由 /config.yml合成.

  • /server/view/layout/layout-default.hbs
<!doctype html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{title}}</title>
    {{{mountState}}}
    {{{parseUrl 'header.css' 'header.js' }}}
    {{#block "head"}}
    {{/block}}
</head>
<body>
{{#block "body"}}{{/block}}
</body>
</html>
複製程式碼
{{{mountState}}}   //將ctx.state掛載到window.APPSTATE
複製程式碼

↓↓↓

<script type="text/javascript">
    window.APPSTATE = {"locale":"zh","publicServer":"","isMockAPI":true,"appName":"app"}
</script>
複製程式碼

檢視頁面原始碼一般會看到以上程式碼.

webpack配置檔案

  • /webpack.entry.conf.js

任何模式都引用的配置檔案

作為全域性通用的入口檔案,處在不同位置.在開發,生產模式webapck構建時自動合併引入webpack.entry.(不做其他屬性合併).一般情況不作修改.

module.exports ={
    header: './web/entry/header.js', //全域性頭部通用檔案(引用vue,全域性樣式...)
    footer: './web/entry/footer.js', //全域性底部通用檔案(比如統計資料...)
};
複製程式碼

header.js:不支援刪除,在生產模式時,緊接著插入manifest.js,vendor.js.

footer.js:支援刪除.

  • /webpack.dev.conf.js

開發模式時所引用的配置檔案,構建會合並所有屬性.

module.exports ={
    entry: {
        'app': './web/pages/app/index.js',
    },
    //devtool: '#cheap-module-eval-source-map'
    ...
};
複製程式碼

合併後的實際入口(多入口)

entry: {
    'app': [
        './web/entry/header.js', 
        './web/entry/footer.js' , 
        './web/pages/app/index.js' , 
        'webpack-hot-client/client'
    ],
    'app2': [
        './web/entry/header.js', 
        './web/entry/footer.js' , 
        './web/pages/app/index.js' , 
        'webpack-hot-client/client'
    ]
}
複製程式碼

webpack-hot-client/client(hot-reload): 開發模式時每個入口自動加入.

  • /webpack.prod.conf.js

生產模式時所引用的配置檔案,構建會合並所有屬性.

module.exports ={
    ...
    new ManifestPlugin({
        publicPath: 'http://localhost:3333/app'
    })
    ...
};
複製程式碼

合併後的實際入口(多入口)

entry: {
    'app': ['./web/pages/app/index.js'],
    'app2': ['./web/pages/app2/index.js'],
    header: ['./web/entry/header.js'],
    footer: ['./web/entry/footer.js']
}
複製程式碼

/web/pages/**/index.js 都是app. 這裡,app, app2 2個app,甚至更多,即多頁應用.

app, app2,分別叫主app,其他app,還可以有另外app...等. 名字隨你.

專案只保留1個app,多app需另建.

mock

  • /config.yml
...
# 是否使用模擬資料api(dev模式有效)
isMockAPI : true
# apiServer api伺服器
apiServer : 'http://localhost:3334'
...
複製程式碼

isMockAPI:true

引用:

在頁面渲染時在/header/index.js前插入/public/vender/mock-min.js.

<script src="/public/vendor/mockjs/dist/mock-min.js"></script><script src="header.js"></script>
複製程式碼

1.服務端Mock

1.1 編寫/server/mock/**/.json檔案.

  • /server/mock/api/list.json

現在請求 /api/list

isMockAPI:true

服務端返回 /server/mock/api/list.json.

isMockAPI:false

服務端返回 http://localhost:3334/api/list.

2.前端Mock

2.1 編寫/web/mock/**/index.js檔案.

  • /web/mock/index.js
Mock.mock('/api/list', 'post', function () {
    return Mock.mock({
        "list|1-10": [{
            'name': '@cname',
            'imageUrl': 'https://placeholdit.imgix.net/~text?txtsize=50&bg=323232&txtclr=ffffff&txt=150%C3%97300&w=300&h=150',
            'description': '@cname'
        }
        ]
    });
});
複製程式碼

優先順序:前端Mock檔案>後端Mock檔案.否則報500.

打包

  • /config.yml
...
#webpack構建路徑(prod模式有效)
buildPath:
    # name entry路徑
    # isIndexEntry 是否使用index.js作為webpack.entry.
    # isIndexEntry = true
    # './web/pages/app/index.js'  --> /dist/static/js/app[chunkhash].js
    # 使用index.js上一級目錄名作為打包檔名(example.js).

    # isIndexEntry = false
    # './web/locale/zh.js'           --> /dist/static/js/zh[chunkhash].js
    # 使用當前檔案作為打包檔名(zh.js).
     -
       name: './web/pages'
       isIndexEntry : 'true'
     -
       name: './web/locale'
...
複製程式碼

一般情況每一個應用都建立在 /web/pages/**/index.js,以index.js作為打包入口.

否則,如果有/web/pages/app/index.js,/web/pages/app2/index.js,/web/pages/app3/index.js.就會最終構建出以排序最後的index.js.

所以,/web/pages/**,只要目錄不重名,並且以index.js作為入口.就不會衝突.

dev

從這些配置檔案打包 /webpack.base.conf , /webpack.entry.conf.js , /webpack.dev.conf.js
主要從/webpack.dev.conf.js配置打包開發需要的entry.

prod

從這些配置檔案打包 /webpack.base.conf , /webpack.entry.conf.js , /webpack.prod.conf , /web/pages/**/index.js
主要從/web/pages/**/index.js打包所有js.

多語言方案(locales)

1.配置引數

  • /config.yml
...
#多語言路由字首
locales: ['zh', 'en'[,.]]
#webpack構建路徑(entry)
buildPath:
     -
       #多語言入口
       name: './web/locale'
...
複製程式碼

缺一不可

2.建立多語言檔案

  • /web/locale/zh.js
window.locale = {
    'desc': 'vue koa 多頁應用腳手架'
};
複製程式碼
  • /web/locale/en.js
window.locale = {
    'desc': 'vue koa scaffold'
};
複製程式碼

多語言檔案會在header.js之前插入.

3.建立全域性方法

  • /web/utils/locale.js
/**
 * 獲取locale對應的值 
 */
window.getLocale = function (key) {
    if (window.locale) {
        return window.locale[key] || '';
    }
    else {
        return key;
    }
};
複製程式碼

4.呼叫全域性方法

...
data() {
    return {
        list: '',
        desc: getLocale('desc')
    }
}
...
複製程式碼

路由則支援

  • http://localhost:3333/
  • http://localhost:3333/zh/
  • http://localhost:3333/en/

中臺自定義屬性

ctx.axios

發起請求方法.

ctx.logger

日誌方法.

ctx.setState

設定ctx.state通用屬性.

ctx.router

全部路由.

ctx其他屬性

根據開發環境合併所有config.yml的屬性.

路由

根據 * /server/router/**/**.js 配置生成路由.

  • obj.path 路由路徑
  • obj.ctrl 路由控制器
  • obj.method 路由方法
  • obj.isAuthenticated 路由是否需要許可權
    假設邏輯為真重定向到登入頁面
  • obj.noContactToRoute 不合併到ctx.router
    每個請求都會經過/server/middleware/state-context.js中介軟體.但只會匹配不帶/api的頁面路由.
    noContactToRoute:true表示不經過這個中介軟體.因為state-context中介軟體根據ctx.router判斷.

相關文章