上一節提完了構思和大體實現下面來看具體的
配置檔案config
配置檔案主要是是用來讓後端開發自主新增頁面,並通過該配置生成route和載入對應的store,這樣就省去了後端去了解vue-router和vuex的用法了;
配置檔案格式如下
這裡採用umd格式開頭是為了後續nodejs進行呼叫
(function(global, factory) {
typeof exports === `object` && typeof module !== `undefined` ? module.exports = factory() :
(global.__config__ = factory());
})(this, function() {
var __config__ = {
//規則說明
/**
* route : 路由
* path : 模組路徑
* store : 是否載入對應store
* sync : 是否同步載入
*/
modules: [
{ route: `/`, path: `Login`, store: true, sync:true },
{ route: `/Main`, path: `Main` },
...
]
}
});
路由router
有了上面的config檔案,我們就可以通過配置來來生成router了
程式碼如下
在define前根據config動態define一些模組用來給業務模組動態返回template和mixin一些公用方法
;
(function () {
var businessModules = [`vue`, `store/index`, `vue-router`].concat(__config__.map(function (o) {
var module = o
.route
.replace(//:[^\/]*/g, ``)
.replace(///g, `_`)
if (o.sync) {
var func = ";define(`business/base/" + module + "`,[`__module__`,`business/" + o.path + "/index`,`text!business/" + o.path + "/tpl.html`],function(factory,businessModule,template){ return factory(`" + module + "`, businessModule(`" + module + "`),template)})"
__config__.dynamic(func)
return `business/base/` + module
}
}))
define(businessModules, function (Vue, store, VueRouter) {
Vue.use(VueRouter)
var m = []
.slice
.call(arguments, 3)
var routes = __config__.map(function (o, i) {
var clone = Object.assign({}, o)
clone.name = clone
.route
.replace(//:[^\/]*/g, ``)
.replace(///g, `_`)
delete clone.store
clone.path = clone.route
delete clone.route
clone.component = clone.sync
? m[i]
: function (resolve) {
require([`__module__`, `business/` + o.path + `/index`, `text!business/` + o.path + `/tpl.html`], function (factory, businessModule, template) {
resolve(factory(clone.name, businessModule(clone.name), template))
})
}
return clone
})
var router = new VueRouter({mode: `hash`, routes: routes})
var firstLoad = true
var goto = function (to, from, next) {
var tName = to.name || `_`
var fName = from.name || `_`
var toDepth = tName
.split(`_`)
.length
var fromDepth = fName
.split(`_`)
.length
toDepth += (tName === `_`
? -1
: 0)
fromDepth += (fName === `_`
? -1
: 0)
var direction = toDepth - fromDepth
if (firstLoad && toDepth > 0) {
firstLoad = false
next({path: `/`})
} else {
store.dispatch(`transition`, {
direction: direction,
to: tName,
from: fName
})
window.setTimeout(function () {
next()
})
firstLoad = false
}
}
router.beforeEach(function (to, from, next) {
var args = arguments
if (to.path === `/`) {
goto.apply(this, args)
return
}
store
.dispatch(`auth`)
.then(function () {
goto.apply(this, args)
}, function () {
Vue.$toast({message: `驗證資訊已失效,請重新登陸`, iconClass: `fa fa-close`})
window.setTimeout(function () {
next({path: `/`})
})
})
})
return {tpl: `<router-view></router-view>`, router: router}
})
})()
狀態管理store
在define前根據config動態define一些模組用來給store物件新增一些公用getter,mutations和action
(function() {
var storeModules = [
`vue`,
`vuex`,
`./transition`
].concat(__config__.modules.map(function(o) {
var module = o.route.replace(///g, `_`);
var func = (o.store == true ?
";define(`store/modules/base/" + module + "`,[`__store__factory__`,`store/modules/" + o.path + "/store`],function(factory,storeModule){ var mb = factory(`" + module + "`); var m = new storeModule(`" + module + "`); var c = $.extend(true,{},mb, m); return c; });" :
";define(`store/modules/base/" + module + "`,[`__store__factory__`],function(factory){ return factory(`" + module + "`);});");
__config__.dynamic(func);
return `store/modules/base/` + module;
}));
define(storeModules, function(Vue, Vuex, transition) {
Vue.use(Vuex);
var m = [].slice.call(arguments, 3);
var modules = {};
__config__.each(function(o, i) {
modules[o.route.replace(///g, `_`)] = m[i];
});
return new Vuex.Store({
state: {},
mutations: {},
actions: {
transition: transition
},
modules: modules
})
})
})();
vue主程式定義
define([
`vue`,
`vue-router`,
`store/index`,
`router/index`,
`emitter`,
`__install__` //這裡面主要是對公用控制元件的一些初始化 Vue.component({...})
], function(Vue, VueRouter, store, router, Emitter) {
window.Vue = Vue;
return {
run: function() {
Vue.config.silent = false;
Vue.config.devtools = true;
Vue.mixin(Emitter);
var $vm = new Vue({
el: `body > div`,
store: store,
template: router.tpl,
router: router.router
});
}
}
})
模組業務的寫法
以Login模組為例
檔案路徑/business/Login/index.js
同目錄下還有個tpl.html
define([`vue`, `vuex`], function(Vue, Vuex) {
return function module(moduleName) {
return {
data: function() {
return {
username: ``,
password: ``
}
},
methods: Object.assign(
Vuex.mapActions([
`verify`
]), {
sign: function() {
var that = this;
this.verify({ username: this.username, password: this.password }).then(function() {
that.$router.push(`/Main`);
}, function(mes) {
Vue.$toast({
message: mes || `帳號或者密碼錯誤`,
iconClass: `fa fa-close`
});
});
}
})
}
}
})
對應的store
檔案為/store/module/Login/store.js
define(function() {
return function storeModule(module) {
this.state = {
sign: true,
auth: `` //用於儲存登陸成功後的驗證碼,用於後繼登陸狀態驗證
}
this.getters = {
isLogin: function(state) {
return state.sign;
}
}
this.mutations = {
success: function(state, param) {
state.sign = true;
state.auth = param ? param : state.auth;
},
fail: function(state) {
state.sign = false;
state.auth = ``;
}
}
this.actions = {
//頁面跳轉過程中驗證用
verify: function(content, opt) {
return new Promise(function(resolve, reject) {
$.post(`/api/verify`, { username: opt.username, password: opt.password }).then(function(data) {
if (data.state) {
content.commit(`success`, data.auth);
resolve();
} else {
content.commit(`fail`);
reject();
}
}, function() {
content.commit(`fail`);
reject("伺服器錯誤!請聯絡管理員");
});
})
},
//登陸用
auth: function(content) {
return new Promise(function(resolve, reject) {
$.get(`/api/auth`, { auth: content.state.auth }).then(function(data) {
if (data) {
content.commit(`success`);
resolve(data);
} else {
content.commit(`fail`);
reject();
}
});
});
}
}
}
})
平時後端開發時不涉及全域性狀態控制時就可以不用store,ajax可以直接寫在模組內
以上就是基於requirejs的vue2專案的核心內容
該專案在不打包的情況下能夠正常執行,各模組都會在頁面載入時進行預載入;後繼還將進行所有檔案打。減少伺服器的請求對於store和router這兩個特殊寫發的檔案(因為requirejs的r.js打包不識別),要進行特殊處理