vue-koa 应用脚手架

闻人吕恭
2023-12-01

项目地址: 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判断.

转载于:https://juejin.im/post/5b2ca430e51d4558b923ff3e

 类似资料: