2018年第29周-快速地在老项目中使用vue的经验

原有项目使用vue的痛点

前端技术的发展可谓日新月异,更有甚者,在deno开源项目的issue中发“求不要更新了,老子学不动了”,这新闻传遍中国的技术圈子,有人说这是中国人的耻辱。这里我就不评论好坏。但可见,前端发展的速度非常惊人。
但“存在即合理”,黑格尔的这句话,在技术领域更加是。能够发展的技术,一定更人们带来便利。扩大解决问题的范围,或者是提高解决需求的速度等等。
然而对于后台人员,面对遗留下来的后台系统,并没有完全前后端分离,也有公司只有后台人员,前后端都干。那么问题来了,上述这种情况下如何提高效率呢。引入Angular、React、Vue是能解决效率问题,得益于其MVVM技术将视图UI和业务逻辑分开等四大特性:

  1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的”View”上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
  2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
  3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel)。
  4. 可测试。界面素来是比较难于测试的,而现在测试可以针对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">
                                        &times;
                                    </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更彻底的模块化。但上述的列子,也够给老项目带来复用的效率提升,后台人员不用花时间去调样式。

    原文作者:黄小数
    原文地址: https://segmentfault.com/a/1190000015722712
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞