最近接手一個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="width: 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');
效果圖: