專案地址: 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判斷.