最近接手一个Web三维项目,前后端分离,前端是传统的前端项目,但又是模块化的开发方式,在修改的过程中,我需要做一些增删改查的功能,又想尽可能少的写css、尽可能少的直接操作DOM元素,所以引入了element-ui和vue,但是又不想依赖nodejs和webpack,不想引入相关配置文件,就实现了一个方案:
如何在传统前端项目中进行javascript模块化编程,并引入使用vue.js、element-ui,并且不依赖nodejs和webpack?
这里有一个问题,如果前端页面使用.vue扩展名,又没有相关配置,vscode不识别,没有代码着色,一片白,所以我是直接在html文件中写模块的css和template。
该方案开发的项目的源代码,直接扔到IIS就能跑,无需打包。
以 CameraRelation 这个模块为例:
一、在index.html中引用vue.js、element-ui的css、element-ui的js
在index.html中添加:
<link type="text/css" rel="stylesheet" href="build/element-ui/index.css"> <script type="text/javascript" src="build/vue/vue.js"></script> <script type="text/javascript" src="build/element-ui/index.js"></script>
二、编写模块的html
cameraRelation.html代码:
<html> <head> <style type="text/css"> .cameraRelation-rightContainer { line-height: 38px; } </style> </head> <body> <div id="cameraRelation"> <el-dialog title="摄像机设备关联模型" :visible.sync="dialogVisible" width="1300px" :before-close="handleClose" :close-on-press-escape="false" :close-on-click-modal="false"> <el-row :gutter="20"> <el-col :span="6" class="cameraRelation-rightContainer"> <span>模型ID:</span> <span>{{modelIds}}</span> </el-col> <el-col :span="12" class="cameraRelation-rightContainer"> <span>模型名称:</span> <span>{{modelNames}}</span> </el-col> <el-col :span="4"> <el-input placeholder="请输入内容" v-model="input" clearable> </el-input> </el-col> <el-col :span="2"> <el-button type="primary" @click="search()">查询</el-button> </el-col> </el-row> <el-row> <el-table ref="singleTable" :data="tableData" highlight-current-row @current-change="handleCurrentChange" style=" 100%" height="400"> <el-table-column type="index" width="50"> </el-table-column> <el-table-column property="id" label="摄像机ID" width="400"> </el-table-column> <el-table-column property="name" label="摄像机名称"> </el-table-column> <el-table-column property="modelId" label="关联模型ID集合" width="200"> </el-table-column> <el-table-column property="modelName" label="关联模型名称集合" width="200"> </el-table-column> </el-table> <!-- <div style="margin-top: 20px"> <el-button @click="setCurrent(tableData[1])">选中第二行</el-button> <el-button @click="setCurrent()">取消选择</el-button> </div> --> </el-row> <span slot="footer" class="dialog-footer"> <el-button type="primary" @click="deleteRelation">删除关联</el-button> <el-button type="primary" @click="ok">关 联</el-button> </span> </el-dialog> </div> </body> </html>
三、编写模块的js
CameraRelation.js代码:
/** * 平台摄像机关联模型 */ import { API } from '../js.my/API.js'; import { Msg } from '../js.my/Msg.js'; import { LoadHtml } from '../js.my/LoadHtml.js'; let api = new API(); let msg = new Msg(); let loadHtml = new LoadHtml(); loadHtml.load('./view/cameraRelation.html', 'cameraRelation-container'); let vue = new Vue({ el: "#cameraRelation", created() { }, data() { return { dialogVisible: false, input: '', modelIds: '', modelNames: '', tableData: [{ id: '', name: '', modelId: '', modelName: '' }], currentRow: null }; }, methods: { handleClose(done) { done(); }, ok() { if (this.currentRow) { msg.confirm("确定关联?", "提示", () => { let data = { id: this.currentRow.id, name: this.currentRow.name, model_id: this.modelIds, model_name: this.modelNames } api.updatePtCameraInfo(data, () => { this.search(); msg.show("模型关联摄像机设备成功"); }); }); } else { msg.show("请点击选择一条数据"); } }, deleteRelation() { if (this.currentRow) { msg.confirm("确定删除该摄像机设备的模型关联?", "提示", () => { let data = { id: this.currentRow.id, name: this.currentRow.name, model_id: '', model_name: '' } api.updatePtCameraInfo(data, () => { this.search(); msg.show("摄像机设备删除模型关联成功"); }); }); } else { msg.show("请点击选择一条数据"); } }, show(modelIds, modelNames) { this.modelIds = modelIds; this.modelNames = modelNames; this.dialogVisible = true; this.search(); }, search() { api.getPtCameraList(this.input.trim(), data => { this.tableData = data; }); }, setCurrent(row) { this.$refs.singleTable.setCurrentRow(row); }, handleCurrentChange(val) { this.currentRow = val; } } }); let CameraRelation = function () { this.show = vue.show; } CameraRelation.prototype.constructor = CameraRelation; export { CameraRelation }
CameraRelation.js代码中使用了Vue,使用了LoadHtml.js模块以同步的方式加载cameraRelation.html
四、同步方式加载html的工具类
该模块使用了jquery
代码中为什么不把请求到的html内容直接添加到body中,而是再包裹一层div?因为直接添加到body中导致其他功能报错,即使把内容中的html、head、body标签去除也不行。
/** * 同步加载html */ let LoadHtml = function () { /** * 同步加载html * @param {string} url html的url地址 * @param {string} containerId div容器id */ this.load = function (url, containerId) { $.ajax({ url: url, type: 'GET', dataType: 'html', async: false, //同步方式请求 success: function (data) { if ($('#' + containerId).length == 0) { $('body').append('<div id="' + containerId + '"></div>'); $('#' + containerId).html(data); } }, error: function (XMLHttpRequest, textStatus, errorThrown) { if (XMLHttpRequest.responseText) { console.log("网络请求出错:", XMLHttpRequest.responseText); } else { console.log("网络请求出错:", XMLHttpRequest); } } }); } } LoadHtml.prototype.constructor = LoadHtml; export { LoadHtml }
五、在CameraRelation.js模块中加载依赖的cameraRelation.html
首先导入模块:
import { LoadHtml } from '../js.my/LoadHtml.js';
然后加载cameraRelation.html:
let loadHtml = new LoadHtml(); loadHtml.load('./view/cameraRelation.html', 'cameraRelation-container');
六、index.html中引用show.js
show.js是和index.html对应的业务功能的主模块
<script type="module" src="./js/show.js"></script>
七、在show.js文件中引入并使用CameraRelation模块:
Ray2.js模块用于在三维场景中拾取三维模型,然后使用对话框模块CameraRelation.js显示编辑界面
import { CameraRelation } from '../js.my/CameraRelation.js'; import { Ray2 } from '../js.my/Ray2.js'; let cameraRelation; let ray2; //平台摄像机关联模型 let rayCallback = function (result) { if (result.length > 0) { let modelIds = ""; let modelNames = ""; let pos = result[0].name.lastIndexOf('_'); let namePre = result[0].name.substr(0, pos); objects.all.map(obj => { if (obj.name.indexOf(namePre) >= 0) { modelIds += obj.id + ","; modelNames += obj.name + ","; } }); if (!cameraRelation) { cameraRelation = new CameraRelation(); } cameraRelation.show(modelIds, modelNames); } } $("#ray-start").on("click", function (event) { if (!ray2) { ray2 = new Ray2(); ray2.config(objects, camera, scene, "GV", rayCallback); ray2.start(); } else { ray2.start(); } msg.show("请点击选择摄像机模型"); }); $("#ray-stop").on("click", function (event) { if (ray2) { ray2.stop(); msg.show("摄像机关联模型操作结束"); } });
如何支持.vue扩展名的文件?
在LoadHtml工具类中加入如下代码:
/** * 同步加载vue文件 * @param {string} url vue文件的url地址 * @param {string} containerId div容器id */ static loadVue = function (url, containerId) { $.ajax({ url: url, type: 'GET', dataType: 'html', async: false, success: function (data) { let regTmpl = /<template[^>]*>([\s\S]*)<\/template[^>]*>/; let regStyle = /<style[^>]*>([\s\S]*)<\/style[^>]*>/; let template = regTmpl.exec(data)[1]; let style = regStyle.exec(data)[1]; if ($('#' + containerId).length == 0) { $('body').append('<div id="' + containerId + '"></div>'); $('#' + containerId).html(template + '<style type="text/css">' + style + '</style>'); } }, error: function (XMLHttpRequest, textStatus, errorThrown) { if (XMLHttpRequest.responseText) { console.error("网络请求出错:", XMLHttpRequest.responseText); } else { console.error("网络请求出错:", XMLHttpRequest); } } }); }
使用示例:
LoadHtml.loadVue('../views/switchMapControl.vue', 'switchMapControlContainer');
效果图: