vue,koa應用腳手架,3步創建應用頁面

項目地址: github.com/xxx-fe/mall…javascript

2019-9-11 更新css

mall-app

vue,koa應用腳手架,3步創建應用頁面html

支持多語言路由,多頁應用,Mock,babel7,動態按需加載.前端

3步創建應用頁面

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 = {
            appId: 'home',
            title: 'home-page'
        };
        //按需加載下必填,不然可忽略.
        ctx.state.appKey = 'home/index';
        //使用common通用視圖
        await ctx.render('pages/common', locals);
    }

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

3.新建應用頁面

  • /web/pages/app/home/index.vue
...
<script>
    export default {
        appId: 'home', //必填,入口根據此ID渲染
        data () {
            return {
                list: ''
            }
        },
        mounted(){
            this.$http.post('/api/list').then(response => {
                console.log(response);
                this.list = response.data.list
            }, response => {
                console.log(response);
            })
        }
    }
</script>
...
複製代碼

架構

前端

  • 樣式:scss.
  • 庫管理:npm
  • 框架:vue2.
  • 模板引擎:handlebars4.
  • 打包:webpack4.
  • 圖標:iconfont,svg-sprite-loader.
  • 組件庫:element-ui.

中臺

  • 框架:koa2, nodejs>=7.6.0

目錄結構

.
├── build                                       # 使用 vue-cli 2.9.3(有修改)
├── config                                      # 使用 vue-cli 2.9.3(有修改)
├── server                                      # 服務端(koa,nodejs)
│    ├── api                                    #     接口
│    ├── controller                             #     控制器
│    ├── lib                                    #     庫
│    │    ├── context                           #       上下文(動態加載文件)
│    │    ├── middleware                        #       中間件
│    │    ├── utils                             #       通用方法
│    │    └── vendor                            #       第三方插件
│    ├── mock                                   #     中臺Mock
│    ├── router                                 #     路由(動態加載文件)
│    ├── view                                   #     視圖
│    ├── server.js                              #     服務端入口
├── dist                                        # 生產目錄
├── public                                      # 公共資源
├── web                                         # 前端(vue,js,css...)
│    ├── components                             #     組件
│    ├── entry                                  #     入口
│    ├── filters                                #     過濾
│    ├── global                                 #     全局設置
│    ├── mixins                                 #     混入
│    ├── mock                                   #     前臺Mock
│    ├── pages                                  #     頁面
│    │     └── /**/index.js                     #     app入口 
│    ├── styles                                 #     樣式
│    ├── webpack.entry.conf.js                  #     通用入口配置
│    ├── webpack.dev.conf.js                    #     app入口配置
│    └── webpack.pord.conf.js                   #     其餘配置
└── config.yml                                  #     通用配置文件,整個腳手架不少功能都與它有關
複製代碼

安裝

npm install    # npm 安裝
複製代碼

命令

npm run dev    # 啓動開發模式(dev)
npm run build  # 構建項目
npm run prod   # 啓動生產模式(prod)
複製代碼

入口配置

  • /webpack.entry.conf.js

任何模式都有效,通用入口配置.vue

,處在不一樣位置.在開發,生產模式webapck構建時自動合併引入webpack.entry.(不作其餘屬性合併).通常狀況不做其餘屬性修改.java

module.exports ={
    header: './web/entry/header.js', //全局頭部通用文件(引用vue,全局樣式...)
    // footer: './web/entry/footer.js', //全局底部通用文件(好比統計數據...)
};
複製代碼

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

footer.js:支持刪除.webpack

  • /webpack.dev.conf.js

開發模式有效,app入口配置.構建會合並全部屬性.ios

全部的app入口都在webpack.dev.conf.js配置. 默認是按需加載.git

module.exports ={
    entry: {
        'app': './web/pages/app/index.js',
        'app2': './web/pages/app2/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(熱加載): 開發模式時每一個入口自動加入.

  • /webpack.prod.conf.js

生產模式有效,其餘配置.構建會合並全部屬性.

module.exports ={
    ...
   new ManifestPlugin({
       publicPath: '/dist/'
   })
    ...
};
複製代碼

合併後的實際入口

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...等. 名字隨你.

腳手架默認按需加載,能知足大多數app開發,app這個入口通常一個便可.

項目只保留1個app,多app需另建.

視圖

引用

handlebars(模板引擎)

handlebars-layouts(模板引擎佈局helpers)

/server/view/layout/**.hbs 以文件名註冊爲handlebars partial.

通用視圖

  • /server/view/pages/common.hbs
{{#extend "layout-default"}} # 使用layout-default佈局 {{#content "head"}} {{{parseUrl 'app.css'}}} # app應用的css,直接引用 {{/content}} # 不須要新建,build時會抽取vue的style成獨立的文件.不然生產模式看不到樣式. {{#content "body"}} <div id="{{appId}}"></div> # 控制器帶過來的ctx.appId {{{parseUrl 'app.js'}}} # app應用的js(相應webpack.entry) {{/content}} {{/extend}} 複製代碼

parseUrl

解析url,handlebars自定義helpers.根據當前開發環境返回正確的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的名字'.不然讀取樣式會不正確.

默認按需加載.ctx.appKey填上相關vue的路徑便可.

瀏覽: 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:'',
    appName:'app'
}
</script>
複製代碼

查看頁面源代碼通常會看到以上代碼.

mock

動態引用

mockjs

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

isMockAPI:true

{{{parseUrl 'header.css' 'header.js'}}}
複製代碼

↓↓↓

<script src="/public/vendor/mockjs/dist/mock-min.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|5-10": [{
            'name': '@cname',
            'imageUrl': 'http://placehold.it/300x150/f69/fff',
            '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

從這些配置文件打包 /build/webpack.base.conf , /webpack.entry.conf.js , /webpack.dev.conf.js
主要從/webpack.dev.conf.js配置打包開發須要的entry.

prod

從這些配置文件打包 /build/webpack.base.conf , /webpack.entry.conf.js , /webpack.prod.conf , /web/pages/**/index.js
主要從/web/pages/**/index.js打包全部js.

多語言

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')
    }
}
...
複製代碼

路由則支持

動態加載文件

如下路徑的文件根據本來代碼邏輯,自動引用全部js,無需手動引入.

  • /server/lib/context/**.js

  • /server/router/**.js

例如

/test/a.js 返回foo1方法. /test/b.js 返回foo2方法.

  • /test/index.js
//動態加載文件,無需手動引入
module.exports = {
    foo1:function(){},
    foo2:function(){}
};
複製代碼
  • /test/index.js
//手動引入
let foo1 = require('./a');
let foo2 = require('./b');
module.exports = {
    foo1: a,
    foo2: b
};
複製代碼

中臺自定義屬性/方法

屬性

ctx.appKey

  • 類型: String
  • 默認值: ''
  • 用法:
ctx.appKey = 'home/index'
複製代碼

按需加載模式的頁面路由.require.context根據appKey渲染vue頁面.

ctx.appId

  • 類型: String
  • 默認值: ''
  • 用法:
let locals = {
    appId: 'home',
    ...
};
await ctx.render('pages/common', locals);
複製代碼

handlebar模版引擎根據appId返回頁面.

ctx.appName

  • 類型: String
  • 默認值: ''
  • 用法:
ctx.appName = 'app'
複製代碼

區分多入口app,避免讀取樣式不正確.一個app不須要設置.

ctx.router

  • 類型: Array<object>
  • 示例:
ctx.logger.info(ctx.router);
複製代碼

當前ctx所有匹配的路由.

方法

ctx.axios(ctx, options[,isReturnFullResponse])

  • 參數
    • {Object} ctx
    • {Object} options
    • {Boolean} isReturnFullResponse 是否返回所有參數, 默認res.data
  • 示例:
ctx.axios(ctx, {
    url: '/api/xxx',
    method: 'post',
    data: ctx.request.body
});
複製代碼

引用封裝 axios 的發起請求方法.

ctx.logger

引用 log4js 的日誌方法.

  • 示例:
ctx.logger.info('示例');
複製代碼

ctx.setState

  • 示例:
ctx.setState(ctx);
複製代碼

設置ctx.state自定義通用屬性.

路由屬性/方法

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

path

  • 類型: String
  • 默認值: ''

路由路徑.

ctrl

  • 類型: async Function
  • 默認值: undefined

路由控制器.

method

  • 類型: String
  • 默認值: get

路由方法.

isAuthenticated

  • 類型: Boolean
  • 默認值: undefined
  • 示例:
{path: 'user/profile', ctrl: page.profile, method: 'post', isAuthenticated: true}
複製代碼

路由是否須要權限. isAuthenticated:true, 重定向到登陸頁面(自定義).

noContactToRoute

  • 類型: Boolean
  • 默認值: undefined
  • 示例:
{path: 'captcha', ctrl: page.captcha, noContactToRoute: true} //普通驗證碼不須要權限驗證
複製代碼

不合併到ctx.router.

每一個請求都會通過/server/middleware/state-context.js中間件.但只會匹配不帶/api的頁面路由.
noContactToRoute:true表示不通過這個中間件.由於state-context中間件根據ctx.router判斷.

相關文章
相關標籤/搜索