vue、react这种前端渲染的框架,比较适合做SPA(Single Page Application)。如果用ejs做SPA,js代码控制好全局变量冲突不算严重,但dom元素用jquery操作会遇到很多的名称上的冲突(tag、id、name)。
SPA要解决的问题:
(1)业务组件用什么文件格式?如果使用*.vue文件,需要在部署前build转换。使用*.js文件,部署前不需要buid转换。本来js的初心就是“即改即用”,我不太喜欢ts,jsx这些需要build的东西。
(2)业务组件如何加载?业务组件不可能写的时候全部知道(根据用户权限决定),也不可能一次性全部加载(影响首屏效率),应该是需要的时候,才从服务器加载。vue为此提供了异步组件,可以用Vue.defineAsyncComponent来创建。
demo.html
<html><header></header><head><!-- <script src="/js/jquery-1.11.1/jquery-1.11.1.min.js"></script> --><!-- <script src="/js/vue-3.3.4/dist/vue.global.js"></script> --><script type="importmap">{"imports": {"vue": "/js/vue-3.3.4/dist/vue.esm-browser.js","easyui":"/js/v3-easyui-3.0.14/dist/v3-easyui.js"}}</script><style>@import '/js/v3-easyui-3.0.14/dist/themes/default/easyui.css';@import '/js/v3-easyui-3.0.14/dist/themes/icon.css';@import '/js/v3-easyui-3.0.14/dist/themes/color.css';@import '/js/v3-easyui-3.0.14/dist/themes/vue.css';</style></head><body><div id="app"></div><script type="module">import * as Vue from 'vue';//console.log(Vue);import EasyUI from 'easyui';//console.log(EasyUI);import main from './com.main.js';let app=Vue.createApp(main);app.use(EasyUI);app.config.globalProperties.t=function(DDKey){return DDKey};//console.log(app);app.mount('#app');</script></body>
</html>
页面划分为上中下三层,中间划分为左右两部分,左边是功能树,右边是功能区。
com.main.js
import * as Vue from 'vue';
import EasyUI from 'easyui';
//console.log(EasyUI);
import Com_Header from './com.header.js';
import Com_Left from './com.left.js';export default {components: {Com_Header,Com_Left},data() {return { tabFile:null,tabs:[]}},created(){this.$messager.ok=this.t('OK');this.$messager.cancel=this.t('Cancel');},methods:{switchTab(name,file){console.log(name,file);console.log(this.$refs.tabs);let tab=null;for(let i=0;i<this.tabs.length;i++){if (this.tabs[i].name==name){tab=this.tabs[i];this.$refs.tabs.select(i);break;}}if (!tab){let component=Vue.defineAsyncComponent(function(){let com=import(file);let comMark=Vue.markRaw(com);return comMark;});component=Vue.shallowRef(component);this.tabs.push({name,component});this.$nextTick(function(){this.$refs.tabs.select(this.tabs.length-1);});}},onCloseTab(tab){console.log(tab);console.log(tab.title);for(let i=0;i<this.tabs.length;i++){if (this.tabs[i].name==tab.title){this.tabs.splice(i,1);break;}}}},template: `<a href="/">{{t('Home')}}</a><h1>{{t('Demo:translate at frontend browser,translate needed(vue)')}}</h1><span>SPA:Single Page Application</span><div className='layout-header2' style="background-color:bisque"><Com_Header></Com_Header></div><div className='layout-middle'><div v-Resizable="{minWidth:200,handles:'e'}" className='layout-left' style="width:200px;float:left;overflow:hidden;background-color:aquamarine"><Com_Left :switchTab=switchTab></Com_Left></div><div className='layout-right' style="margin-left:200px;overflow:hidden"><Tabs ref=tabs :scrollable="true" :plain=true @tabClose=onCloseTab><TabPanel v-for="tab in tabs" :key="tab.name" :title="tab.name" :closable=true><component :is="tab.component"></component></TabPanel></Tabs></div><div style="clear:both"></div></div><div className='layout-footer' style="background-color:brown;text-align:center"><span>copyright© Acroprise Inc. 2001-2023</span></div>`
}
这里要注意Vue.markRaw和Vue.shallowRef两个函数,如果不写,会有警告:
[Vue warn]: Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with `markRaw` or using `shallowRef` instead of `ref`.
com.left.js
let Com_Left={props:['switchTab'],data(){return{ }},methods:{menu_click(e){//console.log(e);let name=e.target.innerHTML;//console.log(name);let file=e.target.getAttribute('file');//console.log(file);this.switchTab(name,file);e.preventDefault();}},template:`<div><a href='/'>{{t('Home')}}</a><br/><a href='DDEditor' @click=menu_click file='/vue/app/DDEditor/page.DDEditor.js'>{{t('Data Dictionary Editor')}}</a><br/><a href='likeButton' @click=menu_click file='/vue/app/likeButton/page.likeButton.js'>{{t('Like Button')}}</a><br/><a href='About' onClick={{this.menu_click}} file=''>{{t('&About')}}</a></div>`
}
export default Com_Left;
效果如下: