原有项目使用vue的痛点
前端技术的发展可谓日新月异,更有甚者,在deno开源项目的issue中发“求不要更新了,老子学不动了”,这新闻传遍中国的技术圈子,有人说这是中国人的耻辱。这里我就不评论好坏。但可见,前端发展的速度非常惊人。
但“存在即合理”,黑格尔的这句话,在技术领域更加是。能够发展的技术,一定更人们带来便利。扩大解决问题的范围,或者是提高解决需求的速度等等。
然而对于后台人员,面对遗留下来的后台系统,并没有完全前后端分离,也有公司只有后台人员,前后端都干。那么问题来了,上述这种情况下如何提高效率呢。引入Angular、React、Vue是能解决效率问题,得益于其MVVM技术将视图UI和业务逻辑分开等四大特性:
- 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的”View”上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
- 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
- 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel)。
- 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
上述特性是不是抽象,前端人员都觉得前端技术发展之快,后端人员就更难接触和体会。vue官网的例子更是以webpack等模块化例子来讲解,这样以来,如果想使用vue的好处而已,还要去了解webpack、AMD等等,这让后端人员再三地望而却步。
除了技术栈的限制,项目时间也是个门槛,虽然我觉得前端很多思想都是源自于后端的思想,如模块化,mvc,构建技术等,但学起来还是要时间呢。老板并不会给时间开发人员去把整个前端推倒了重干。推倒重干也不切实际,一成本高,二风险大(如能不能完成,或能不能赶在市场机会面前完成)。所以渐进地改变是比较稳妥的(保守主义)。
所以这里分享下我在老项目快速应用vue的经验。
vue经验
在原有有项目的html页面里,引入vue的依赖
<!-- Vue.js v2.5.16 -->
<script src="/js/vue/vue.min.js"></script>
<!-- vue-resource v1.5.1 用于ajax请求-->
<script src="/js/vue/vue-resource.min.js"></script>
本例子是一个添加用户表单的提交,就只有使用几个属性 v-model,v-for和v-on:click,以及{{}}占位符
此时html代码只需修改如下(专注于展示):
<div class="form-horizontal" method="post" id="userFrom">
<div class="form-group">
<input type="text" class="form-control" id="name" placeholder="请输入用户账号" v-model="name" >
</div>
<div class="form-group">
<input type="password" class="form-control" id="name" placeholder="请输入用户密码" v-model="name" >
</div>
<div class="form-group">
<button type="button" class="btn btn-success" data-toggle="modal" data-target="#myModal">选择角色</button>
<table class="table table-hover" >
<thead>
<tr>
<th>操作</th>
<th>角色ID</th>
<th>角色名称</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in nodeList">
<td>
<button type="button" class="btn btn-danger" v-on:click="remove($event,index)">移除</button>
</td>
<th scope="row">{{ item.id }}</th>
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
</div>
<div class="form-group">
<textarea placeholder="请输入备注" name="remark" id="remark" v-model="remark"></textarea>
</div>
<div class="form-group">
<div class="col-xs-2">
<button class="btn btn-primary btn-block btn-flat" v-on:click="createUser">添加</button>
</div>
</div>
</div>
<!-- 弹出框,选择角色,上面的选择角色按钮则弹出以下对话框,这个是boostra技术的范畴,但不影响vue的使用 -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×
</button>
<h4 class="modal-title" id="myModalLabel">
选择角色
</h4>
</div>
<div class="modal-body">
<table class="table table-hover" id="toChooseTable">
<thead>
<tr>
<th>操作</th>
<th>角色ID</th>
<th>角色名称</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in toChooseList">
<td>
<button type="button" class="btn btn-danger" v-on:click="choose($event,index)">选择</button>
</td>
<th scope="row">{{ item.id }}</th>
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭
</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal -->
</div>
script代码修改(专注于数据及其操作)
<script >
//提交表单中的数据及操作
var userFrom = new Vue({
el: '#userFrom',
data: {
name:'',
password:'',
remark:'',
roleList: [],
apiUrl: '/user/add'
},
// 在 `methods` 对象中定义方法
methods: {
remove: function (event,index) {
//移除角色
//1.将未被选择的角色数据放回弹出框内
pushToChooseList(this.roleList[index]);
//2.在已选择的角色table里移除这个角色数据
this.roleList.splice(index,1);
},
createUser: function() {
var params = {
name:this.name,
password:this.password,
remark:this.remark,
roleList:[]
};
for(var i=0;i<this.roleList.length;i++){
var role={};
role.id = this.roleList[i].id;
role.name = this.roleList[i].name;
params.roleList.push(node);
}
this.$http.post(this.apiUrl,JSON.stringify(params),{
emulateJSON:false
}).then(function(response){
// response.data中获取ResponseData实体
console.log(response);
},function(response){
// 发生错误
console.log(response);
});
}
}
})
//弹出框数据及操作
var toChooseTable = new Vue({
el: '#toChooseTable',
data: {
toChooseList: [
],
apiUrl: '/role/list'
},
// 在 `methods` 对象中定义方法
methods: {
loadData: function() {
//加载角色数据
this.$http.post(this.apiUrl, {}).then(function (response) {
var result = response.data;
for (var i = 0; i < result.data.length; i++) {
var role = {};
role.id = result.data[i].id;
role.name = result.data[i].name;
toChooseTable.toChooseList.push(node);
}
}, function () {
console.log('failed');
});
},
choose: function (event,index)
//选择角色
//1.将选择的角色数据放入被选中的table里
pushChoosedList(this.toChooseList[index]);
//2.移除已被选择的弹出框的角色数据
this.toChooseList.splice(index,1);
}
}
})
//加载角色数据
toChooseTable.loadData();
function pushChoosedList(data) {
for(j = 0,len=userForm.roleList.length; j < len; j++) {
if(userForm.roleList[j].id==data.id){
return;
}
}
userForm.roleList.push(data);
}
function pushToChooseList(data) {
for(j = 0,len=toChooseTable.toChooseList.length; j < len; j++) {
if(toChooseTable.toChooseList[j].id==data.id){
return;
}
}
toChooseTable.toChooseList.push(data);
}
</script>
上述例子很好的展示视图层和数据层的分离,视图和数据层都是单独分开写的,不知道你能不能从中体会到效率的提高。以前这种弹出框选中数据,在table列表中展示,可是要写好多js代码,需手工创建tr标签,然后再append进table标签等,还要遍历定位标签,反正写个页面都老半天。有了vue,这效率简直大大的提高了。
element-ui经验
上述例子中,我们体会到视图层和数据层的分离,但还不是很理解模块化复用,以下就举个模块化复用好处的例子,就是使用element-ui的列表及分页。
引入element-ui
在上述vue基础上,引入:
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入组件库_element-ui@2.4.4@element-ui,官网不提供直接下载,需用cnpm i element-ui -S下载,然后复制lib目录 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
只需要引入element-ui的table组件和分页组件,即可达到复用
修改html代码:
<div class="ibox-content" id="content">
<!-- table组件 参考:http://element-cn.eleme.io/#/zh-CN/component/table-->
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
label="日期"
width="180">
<template slot-scope="scope">
<i class="el-icon-time"></i>
<span style="margin-left: 10px">{{ scope.row.date }}</span>
</template>
</el-table-column>
<el-table-column
label="姓名"
width="180">
<template slot-scope="scope">
<el-popover trigger="hover" placement="top">
<p>姓名: {{ scope.row.name }}</p>
<p>住址: {{ scope.row.address }}</p>
<div slot="reference" class="name-wrapper">
<el-tag size="medium">{{ scope.row.name }}</el-tag>
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件-->
<div align="center">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="page"
:page-sizes="[10, 20, 30, 40]"
:page-size="rows"
layout="total, sizes, prev, pager, next, jumper"
:total="totalRecord">
</el-pagination>
</div>
</div>
修改script代码
<script >
/*<![CDATA[*/
var content = new Vue({
el: '#content',
data: {
tableData: [{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}, {
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
}, {
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}],
url:'/user/list',
//搜索条件
criteria: '',
//下拉菜单选项
select: '',
//默认每页数据量
rows: 10,
//当前页码
page: 1,
//查询的页码
start: 1,
//默认数据总数
totalRecord: 1000
},
// 在 `methods` 对象中定义方法
methods: {
loadData: function(criteria, pageNum, pageSize){
this.$http.post(this.url,{criteria:criteria, page:pageNum, rows:pageSize},{emulateJSON:true}).then(function(res){
var page = res.data;
this.tableData = page.data;
this.totalRecord = page.totalRecord;
},function(){
console.log('failed');
});
},
handleEdit: function (index, row) {
console.log(index, row);
},
handleDelete: function (index, row) {
console.log(index, row);
},
//每页显示数据量变更
handleSizeChange: function(val) {
this.rows = val;
this.loadData(this.criteria, this.page, this.rows);
},
//页码变更
handleCurrentChange: function(val) {
this.page = val;
this.loadData(this.criteria, this.page, this.rows);
}
}
})
//加载数据
content.loadData(content.criteria, content.page, content.rows);
/*]]>*/
</script>
虽然没有使用webpack更彻底的模块化。但上述的列子,也够给老项目带来复用的效率提升,后台人员不用花时间去调样式。