美文网首页
传统不带框架项目,引入requireJS和vue进行改造

传统不带框架项目,引入requireJS和vue进行改造

作者: 迪迪妮粑粑 | 来源:发表于2019-06-14 09:51 被阅读0次

先废话一下,本人做前端3年到4年,在很小的几家小公司待过,一直一个人做前端,前端水平自认为应该算基础入门了。这是第一篇文章,分享给有相似需求的伙伴。

本文使用场景:

1.新入职一家国企,要熟悉的一个项目是用原生手撸不带框架的,并且也没做过模块化和抽象,改样式都是用过js写 style.display === "none" 这样做的

2.领导要求让代码能模块化,提高可读性,至少知道哪里错了可以快速找到(纯原生dom获取,改值,每个元素起个id,用监听实现事件处理,阅读代码找bug真的很蛋疼)。

3.本项目是移动端用的(原生android套壳混合开发),重要的事情说三遍,本次改造不适用IE,不适用IE,不适用IE

4.本人就前端刚入门的水准,大神有更好的方法请指导。

5.这点很重要,这个例子不是说之前的项目作者写的不好,至少他在原生js代码基础上就比我好很多,这篇文章只为小部分朋友最小代价快速改造传统dom操作项目之用

处理方式简述:

1.提高可读性,首先代码不能插入内容,操作样式都用原生js写吧,于是引入了Vue。

2.由于在使用Vue动态组件的时候,发现渲染切换内容区时在移动端达到了2~3秒之多才出现内容,想了想,大概是由于组件不是异步加载导致的。(本项目是改造,不是推翻重来,所以不可以通过Vue脚手架+webpack打包这种形式做)所以引入requirejs进行模块化异步加载。

3.小结,基本就是vue做模板渲染,requirejs进行模块化异步加载,这样改动是最小最快的,并且代码可读性大大提高

看一下原来的项目写法:

其中一个页面是这样的(这里只是一部分截图,总的model大概有32个,太长了截不了),据说一个model就是一个流程操作页面,为什么这么做,因为流程中不做数据提交,如果多页面很难传递上一步页面的数据。所以用js操作dom显隐来控制数据不丢失,最后从页面元素拿数据。html模板长度723行,这里相当于把32个页面的流程做在一个页面上。

对应的js有2805行。我觉得除了用vue组件,模块化拆分这些步骤外,js的写法也有很大问题,举个例子,由于没有多页面,也就没有路由的概念。页面的前进和返回是这样的,一个物理返回监听,3个if语句里套了一个28个case的的switch,并且物理监听返回写过的东西,再在返回按钮的监听上再写一遍,头部也没有公用。

我改造的写法:

目录结构是这样的,vue基础库require基础库就不截图了,此外还包含主页面一个html,一个主入口js文件

目录结构

requirejs配置文件

require.config({
    baseUrl:'../js',
    paths: {
        "vue": "vue",
        "components":"../Scene/js/components"
    },
    shim: {
        
    }
});

主html只放了一个父组件标签,就不截图了
主入口js(我觉得注释很明白了,不解释了)

require(['require_config'],function(config){
    require([
        'vue',
        'components/HeaderBar',
        'components/ChooseType',
        'components/NewScene',
        'components/RuleType',
    ],
    function(Vue,headerBar,chooseType,newScene,ruleType){
        var rootPage = Vue.extend({
            name: 'rootPage',
            template: 
                `<div>
                    <!--以下为公共头部-->
                    <header-bar :title="currentTitle" @goBack="goBack">
                        <p v-if="currentScene==='newScene'" class="header_more_text" id="icon_confirm">保存</p>
                        <p v-if="currentScene==='ruleType'" class="header_more_text" id="rule_save">下一步</p>
                    </header-bar>
                    <!--以下为页面模块-->
                    <component v-model="datas" :is="currentScene" @changePage="changePage"></component>
                </div>`,
            components: {
                headerBar,
                chooseType,
                newScene,
                ruleType
            },
            data: function () {
                return {
                    datas:{
                        temp_rule:{
                            type:1
                        }
                    },
                    components:[
                        {title:'选择场景类型',type:'chooseType'}
                    ]
                }
            },
            computed: {
                //动态计算当前标题
                currentTitle() {
                    return this.components[this.components.length-1].title;
                },
                //动态计算当前显示的页面模块
                currentScene() {
                    return this.components[this.components.length-1].type;
                }
            },
            methods: {
                /*
                * 此处的changePage,和goBack分表实现页面模板的来回切换方法
                * 通过data中的components数组,
                * 模拟浏览历史栈数据结构的先进后出的特点,
                * 从而实现记录历史步骤
                */ 
                //返回
                goBack(){
                    if(this.components.length>1){
                        this.components.splice(this.components.length-1,1);
                        this.$set(this,'components',this.components);
                    }
                },
                //点击切换页面
                changePage(type,title) {
                    var obj = {
                        title:title,
                        type:type
                    }
                    this.$set(this,'components',[...this.components,obj]);
                    console.log(this.components)
                }
            },
            created(){
                var self = this;
                //监听物理返回
                history.pushState(null, null, location.href);
                window.addEventListener('popstate', function() {
                    self.goBack();
                })
            }
        });
        
        new Vue({
            el: '#scene_wapper',
            components: {
                rootPage
            }
        })
    })
})

公共头组件

define(['vue'],function(Vue){
    return Vue.extend({
        name: 'headerBar',
        props: ['title'],
        template: 
            `<div class="header bottom_border">
                <div class="header_back" id="choose_back" @click="goBack">
                    <img src="../image/Header_Back.png" alt="Arrow">
                </div>
                <div class="header_content">
                    <p class="header_content_text">{{title}}</p>
                </div>
                <div class="header_more">
                    <slot></slot>
                </div>
            </div>`,
        methods: {
            goBack(){
                this.$emit('goBack');
            }
        }
    });
})

带双向绑定的ruleType组件示例(其他都是类似模块,不展示了)

define(['vue'], function(Vue) {
    return Vue.extend({
        name: 'ruleType',
        model:{
            prop: 'datas',
            event: 'change'
        },
        props: ['datas'],
        template: 
        `<div class="scene_content">
            <div class="scene_margin"></div>
            <div class="scene_list bottom_border" @click="changeType(1)" :class="datas.temp_rule.type==1?'active':''">
                <div class="scene_list_name" style="width: 160px" id="local_rule_name">
                    本地<span class="scene_tips">(无网络时可用)</span>
                </div>
                <img class="scene_list_confirm" src="../image/Scene_confirm.png" alt="check">
            </div>
            <div class="scene_list bottom_border" @click="changeType(2)" :class="datas.temp_rule.type==2?'active':''">
                <div class="scene_list_name" id="cloud_rule_name">云端</div>
                <img class="scene_list_confirm" src="../image/Scene_confirm.png" alt="check">
            </div>
        </div>`,
        methods: {
            changePage(type, title) {
                this.$emit('changePage', type, title);
            },
            changeType(type){
                this.datas.temp_rule.type = type;
                this.$emit('change',this.datas);
            }
        },
        mounted() {

        }
    });
})

这样只要在最后一步的组件里直接提交父组件的datas属性就好了,每个子组件只需要关心自己步骤的逻辑,改造示例暂时就是这样,有指导意见请各位看官慷慨赐教,不好之处请提意见我会虚心改正。
我始终是前端界的一个小菜鸟。
开源demo

相关文章