在五一節之前和一網友討論前端技術時,對方提到vue、vue-route如果配合requirejs應用。當時的我沒有想得很明白,也沒能這位網友一個准確的回復,但我許諾於他五一研究后給他一個回復。本是一天的研究卻被我搞成了研究了一周,這拖延症。。。
閑話少數,進入正題
一、示例代碼說明
代碼結構說明:
- modules(存放的為組件,主要是模板與Js對象分成兩個文件來寫)
- route:測試子路由的組件
- title:就是一個簡單的顯示文字的組件
- app.js:核心類,提供vue的創建、以前modules組件加載的方法等
- chart.js:模擬的一個業務模塊
- index.html:頁面文件
- layout.css:整體樣式文件(測試require加入樣式文件)
- main.js:requirejs的配置文件(也是入口文件)
丑陋的效果圖:
此示例沒有樣式,只是為了驗證require如何加載一個vue組件,以子路由的動態注入的能力,示例代碼下載。
二、從.vue文件入手
一個.vue文件可以包含模板、Js類、樣式(可以不要)等三塊。但我們通過vue的官網可以知道,vue提供了compile對象方法,可以把模板編譯成VNode。並且我們通過webpack打包后生成的文件可以看出模板與Js類是混淆在一起了。這也就說明vue的組件就是一個Js對象。如下圖所示:
三、requirejs對vue、vuex和vue-route的引入
引入這三個都很容易,並將這三個注入到Vue對象也是相對簡單的,難道的是需要解決動態注入向vue-route實例注入路由,以及vuex的動態加入一個數據模塊的能力。好在這兩點官網都給出了解決方案:
- vue-route如何動態注入路由
根據官網幫助文檔說明,存在addRoutes方法,向路由實體動態注入路由 - vuex模塊動態注入
也是根據官網幫助文檔提示有registerModule方法實現。 - 實現的部分代碼:
初始化Vue對象
apt.init = function(){ this.store = Object.create({ modules:{} }); this.Vue.use(VueRouter); this.Vue.use(Vuex); this.router = new VueRouter(); this.store = new this.Vuex.Store(this.store); }
首先提供一個init方法,對Vue對象進行一些初始化,也就是把Vuex、vue-route都注入到Vue對象中。在這里我把創建的vuex和vue-route的實例都放到this對象,方便后面提供給組件注冊實使用。
創建Vue實例:
apt.createVue = function(){ this.vue = new this.Vue({ store: this.store, router: this.router, computed: { childs: function(){ if(!this.$store.state.router) return null; return this.$store.state.router.childs; } } }); return this.vue; }
只創建vue對象,沒有進行mount。
為其他模塊提供的上下文:
apt.createContext = function(){ return { Vue: this.Vue, router: this.router, $vue: this.vue }; }
四、如何通過require加載html和js方式的組件
從項目結構圖中可以看出在modules文件夾中定義了兩個組件,分別是:routet和title,而他們的模板則是一個html文件。以下是這類組件如何加載的代碼:
apt.acquire = function(path){ var arrayPath; if(!this.isArray(path)){ arrayPath = [path]; }else{ arrayPath = path; } var promise = this.dfd(function(dfd){ require(arrayPath,function(){ dfd.resolve(Array.prototype.slice.call(arguments)); },function(error){ dfd.reject(error); }); }).promise(); return promise; } apt.createComponent = function(componentName){ //可以重載,讀取.vue的文件 var path = this.$modulePrefix + componentName, html = 'text!' + path + '/index.html', js = path + '/index.js', self = this; var promise = this.acquire([html,js]); promise.done(function(result){ var obj = result[1], content = result[0]; obj.template = content; obj.__path__ = path; self.$components.push(obj); }); return promise; }
說明: acquire:提供通過require加載JS或者是html等文件的方法,並返回一個promise,這樣就方便調用者使用。 createComponet:會根據調用傳入的名稱在modules文件夾中找出對應的js和html文件,然后調用acquire加載組件。
五、main.js是這樣引用的
提供注冊全局組件方法
apt.registerGlobalComponents = function(componentNames){ var gloadComponet = componentNames, self = this; var promises = gloadComponet.map(function(data,index){ return self.createComponent(data); }); var dfd = this.dfd(); $.when.apply(null, promises).done(function(){ var _router = []; self.$components.forEach(function(data,index){ self.Vue.component(data.name, data); _router.push({ path: '/' + data.name, component: data }); }); self.router.addRoutes(_router); //全局注冊都注冊為路由 dfd.resolve(_router); }); return dfd.promise(); }
main.js中的引用
var _app = app.createApp(); _app.registerGlobalComponents(['title', 'route']).done(function(){ var vue = _app.createVue(); var cxt = app.getVue().createContext(); var r = { state: { childs: [] }, mutations: { childs: function(state, data){ state.childs = data; } }, actions: { childs: function(state, data){ state.commit('childs', data); } } } vue.$store.registerModule('router', r); vue.$mount('#app'); });
說明:
- 創建App的一個實例;
- 注冊全局的組件:title、route;
- 注冊完成后創建vue實例,並且向實例的vuex注入二級路由展示的模塊