# myvue **Repository Path**: wujizhou/myvue ## Basic Information - **Project Name**: myvue - **Description**: 类似小程序的开源前端开发框架 myvue - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-03-12 - **Last Updated**: 2021-09-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: JavaScript ## README ## 说明 > Just be simple and elegant ! 特点: 轻量(只要nginx) 快速(受够了vue) 模块根据依赖加载(layui) 模板带缓存 即开即用 即时渲染 易于控制 ### 开发的初衷: 我是一个java,php,js开发者 ,随着技术的日渐累积 和 市场环境的日益更新(前端机器性能越来越高,前端日益发展), 逐渐萌生了站在巨人(layui,nginx,springboot,...)的肩膀上 做自己的前后端开发框架的决心, 从jquery/zeptjs/一众ui库到 avalonjs/layuijs/vue,从java/delphi/php/go再到java,发现最适合自己的开发为 : java做后端,php/...中间件,js前端 ,这里只讲下本次的主题:后台的前端开发框架. ### 开发环境 开发环境下, 及时查看需配置nginx且开启ssi 或者 构建为纯静态 查看 , 打包后为纯静态 主要采用 layui(模块化)+nginx(容器 + ssi,为了代码复用) + 自定义layui插件等,推一波layui(好看又实用) ,采用自定义的模板解析更新机制,并考虑到模板缓存问题 . 大体流程为: 加载后台主体js和html,需要加载页面时(例如点击菜单)先查看模板缓存(没有就去nginx请求模板), 再去后端(如java)请求初始化数据,填充模板,最后渲染数据 . 所有的一切追求都是轻且快,而实际表现也很惊喜 ### 打包上线 ``` # 预处理ssi标签等 gulp dist # 生成纯静态文件 gulp prod ``` ### 页面模板 > 分为3大标签: tpl , lay , js - tpl: html模板/layui模板 - lay: 部分layui模板 - js : 当前页面的逻辑,这里为核心,主体加载后执行详见下面的 解析机制: ``` exports = { data: { //初始化数据 ... } use:[], //依赖的layui模板 init: //渲染模板前的逻辑 after: //渲染模板后的逻辑 } ``` > 菜单管理页 /menu/index.html ```
{{ d.headTip }}
``` 上面html片段只是一些html,这里不看了 > /menu/index.js ``` exports = { data: { title: '菜单管理', parent: 0, headTip: "最多3级菜单,一级菜单名字不要改会自动国际化,点击带 .列单元格编辑", }, use:["rb","form","table2","moment","jquery"], init: function(rb) { rb.path = "menu"; rb.controller = "api_menu"; // 模板解析前的运行代码 rb.log("index init"); if( this.data.headTip ) { this.data.headTip = rb.getHtmlHeadTip( this.data.headTip ); } console.log(this); // 获取后端数据 // this.param 地址栏请求参数 // 封装数据到 }, after: function(rb,form,table) { // 页面渲染完后执行的代码 // form init rb.setWhere = function() { rb.where.kword = $("#jsf-kword").val() || ''; } // table init rb.where = { parent : this.data.parent ,field: 'sort' ,order: 'asc' }; // table render rb.table = table.render($.extend(rb.table_config,{ url: layui.cache.apiPath + rb.controller + "/index" ,where: rb.where ,initSort: { field: 'sort',type: 'asc' } ,cols: [[ {checkbox: true} //,LAY_CHECKED: true ,{field: 'id', title: 'ID', minWidth: 60,templet: '#nameTpl',style:'cursor:pointer',event:'go',sort:true} ,{field: 'name', title: L('menu')+' .',edit: 'text'} ,{field: 'icon', title: L('icon')+' .',edit: 'text',templet: '#iconTpl'} ,{field: 'url', title: L('url')+' .',edit: 'text'} ,{field: 'params', title: L('url-para')+' .',edit: 'text'} ,{field: 'show', title: L('show'), width: 60,templet:'#showTpl'} ,{field: 'sort',title: L('sort')+' .',align:'right',minWidth:60,edit: 'text',sort:true} ,{align:'left',title: L('op'),width:185,toolbar: '#barDemo'} ]] })); // table a go $("#body").on("click",".js-parent",function(e) { rb.where.parent = $(this).data('parent') || 0; rb.reloadTable(rb.where); }); table.on('tool(fDemo)', function(obj) { // console.log(obj); var data = obj.data; if(obj.event === 'go') { var params = obj.tr.find('a').data('params') || ''; if(params) { rb.where = $.extend(rb.where,JSON.parse('{' + params + '}')); rb.reloadTable(rb.where); } } }); } } ``` 上面js/html片段只是一些html/js,这里不看了 > rb.js rb是自定义的一些快捷方法 ``` ;layui.define(['layer','scojs','util','notice','table2excel'],function (exports){ "use strict"; var layer = layui.layer ,$ = layui.jquery ,notice = layui.notice ,device = layui.device(); // 外部接口 var rb = { alert : function (msg,type){ //scojs message box msg = undefined == msg ? '' : msg; type = type || false; $.scojs_message(msg, type ? $.scojs_message.TYPE_OK : $.scojs_message.TYPE_ERROR); }, … 其他方法 }; rb.log('rb',L('init')); exports('rb',rb); }); ``` ### 自定义解析机制 加载主体(如首页/登陆/..)后: 主体js大体如下: > index.js ``` // use strict; var loading = false; var loadingIdx; var initTabs = []; var initPages = []; var themes = { 'df' : '#23262e', 'blue': '#29d', 'cyan':'#6f7c85', 'gray':'#eee', 'green' :'#009688', 'orange':'#FFB800', 'red':'#ff5722', }; layui.cache.tab_welcome = { title:'', id:'iframe0' }; layui.use(['table2','layer','form','laytpl','element','tabrightmenu','rb'],function (){ var layer = layui.layer ,$ = layui.jquery ,el = layui.element ,form = layui.form ,laytpl = layui.laytpl ,rb = layui.rb ,table = layui.table2 ; … rb.check(); var apiPath = layui.cache.apiPath; var index = rb.loading(); rb.log("index","init"); // 请求菜单 + 添加菜单 rb.log("get menu ----------> "); $.post(apiPath+'api_menu/ajax', function(data) { … //获取模板代码 , 如点击菜单触发 $.get(url, function(tpl,status,xhr) { if(layui.cache.rainbow.cacheTpl){ rb.setCacheTpl(url,tpl); } handleMenu(tpl,params,id,ops); }); ... // 处理前端服务器url 请求 function handleMenu(tpl,params,id,ops){ var lay = rb.getTplByTagName(tpl,"lay"); var js = rb.getTplByTagName(tpl,"js"); tpl = rb.getTplByTagName(tpl,"tpl"); lay = lay ? rb.replaceByTagName(lay,'lay') : ''; js = js ? rb.replaceByTagName(js,'js') : ''; tpl = tpl ? rb.replaceByTagName(tpl,'tpl') : ''; if($.trim(js)){ //有js代码 try { // js = js.replaceAll('&',"&"); // console.log("js",js); var exports = eval(js); exports = $.extend(true, exports, {data:{},_d:{},_params:""}); } catch(e) { rb.log("模板js解析出错",e); } // console.log("exports: ", exports); if(exports.data) { // 请求的参数 exports.params = params; if('string' == typeof exports.use) { exports.use = [ exports.use ]; } layui.use(exports.use,function() { var modules = []; $.each(exports.use,function(i,el) { modules.push(layui[el]); }); // console.log("modules",modules); try{ exports.init.apply(exports,modules); } catch(e) { rb.log("模板js运行出错",e);return; } try{ laytpl(tpl).render(exports.data, function(html) { ops.content = html + lay; console.log(ops); rb.tabChangePage(id,ops); exports.after.apply(exports,modules); }); } catch (e) { rb.log("模板渲染出错",e);return; } }); } }else{ ops.content = tpl; rb.tabChangePage(id,ops); } } ``` ### 国际化 _lang.js ``` // app table2 rb...ok sort by alpha var _langs = { // un type // ADMIN MENU "CMS":"CMS", "IMG":"图库", "MALL":"商城", "OAUTH":"OAUTH", "OTHER":"其他", "SYS":"系统", "USER":"用户", …. // 要替换的话里面必须要有{key},否则不认,{key}在两头的可用LL function L(k,key=''){ var ret = _langs[k],reg; if(ret == undefined){ //未设置 ret = k; }else{ if(/\{key\}/.test(ret)){ // 需要替换 if(typeof key == 'string' || typeof key == 'number'){ reg= RegExp('{key}' , "g" ); ret = ret.replace(reg,L(key)); }else{ for(var k in key){ reg= RegExp('{'+k+'}' , "g" ); ret = ret.replace(reg,L(key[k])); } } } } return ret; // return _langs[k] || k; } function Lnull(k){ return _langs[k] || ''; } function LL(k,dif=' ',add=''){ // if(rainbow.lang == 'en') add = ' '; return k.trim().split(dif).map(function(item){ return L(item); }).join(add); } ``` ### 部分页面预览(暗黑版,眼睛不好,见谅) ![登陆实例](/preview/登陆实例.png) ![用户列表](/preview/用户列表.png) ![自定义插件1](/preview/自定义插件1.png) ![tab页面内操作](/preview/tab页面内操作.png) ![菜单编辑](/preview/菜单编辑.png) ### 关于更新 项目还在不断更新状态,但大体逻辑基本不会变动,由于工作等原因,更新较慢,望谅解! 所有的一切都是轻且快,而实际表现也很惊喜,开发本该如此优雅 > 希望大家玩的愉快,不玩初心!