基於requirejs的vue2專案(二)

heruiwoniou發表於2017-03-08

上一節提完了構思和大體實現下面來看具體的

配置檔案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打包不識別),要進行特殊處理

相關文章