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;
效果如下: