BootStrap3.X+html5+CSS3+Vue2.X 项目问题总结

1.Vue.js无法直接使用Bootstrap的tooltip插件的解决方法

1.1 根据Ajax数据动态增加的tooltip在Vue.js中的初始化操作

因为Bootstrap依赖于JQuery库,tooltip需要使用JQuery代码进行初始化

$('[data-toggle="tooltip"]').tooltip()

http://v4-alpha.getbootstrap.com/components/tooltips/
这里写图片描述
在将框架由JQuery调整为Vue.js以后,发现Bootstrap.js的tooltip无法显示了。
但是依然想使用Bootstrap的CSS文件,因此尝试了下面几种将Bootstrap和Vue进行结合的库:
1.vue-strap Bootstrap components built with Vue.js.
GitHub地址:https://github.com/yuche/vue-strap

但是引入tooltip时浏览器报错:vue.js:465 [Vue warn]: Failed to resolve directive: el
发现是Vue版本的问题。
因为vue-strap要求使用Vue.js 的版本是1.0.8(required ^1.0.8, test with 1.0.8).
但是不想降低vue的版本,因此放弃vue-strap。

2.取而代之尝试了bootstrap-vue
Bootstrap-Vue provides one of the most comprehensive implementations of Bootstrap V4 components and grid system available for Vue.js 2.4+, complete with extensive and automated WAI-ARIA accessibility markup.
Github地址:https://github.com/bootstrap-vue/bootstrap-vue
但是它是基于Bootstrap4的,还是个内测版本,连同bootstrap.css也要换成boostrap-vue.css 也就是boostrap4,和bootstrap3的差距蛮大,因此弃用。

3.使用awesome的一些vue组件库,需要使用npm。

最后上stackoverflow发现了解答,因为原来的Boostrap.js中关于tooltip的代码是在更新DOM以后再插入文档流,由于我的项目中的tooltip是在v-for结构中遍历数据个数之后动态增加的,因此想要初始化所有的tooltip需要将初始化的代码写在增加了所有的包含tooltip的div这个过程之后,这可以通过nextTick在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
nextTick Vue.js官网讲解:https://cn.vuejs.org/v2/api/#Vue-nextTick

Vue.nextTick(function () {
$('[data-toggle="tooltip"]').tooltip()
});

tooltip的显示问题就此解决。

1.2关于如何让tooltip动态地显示和隐藏

就是tooltip不需要初始化之后一直是激活状态,而可以用代码动态地将它隐藏。
具体的应用场景是项目中输入框的内容如果不符合要求(比如必填项为空或者格式不正确),当点击了Start按钮后,tooltip显示,内容为显示错误提示;而当用户进行输入以后tooltip提示隐藏或销毁。
这里我写了一个自定义指令,v-tooltip,可以根据传入指令的值(就是错误提示的内容)对tooltip进行显示或者隐藏,可以指定显示方式,是鼠标放到输入框显示还是输入框获取焦点才显示,并且可以通过传入指令的值来动态修改提示的文字内容:
这里写图片描述
这里写图片描述

Vue.directive('tooltip', {
    bind: function(el, binding) {
        //hover tooltip
        if (binding.arg == "hover" || binding.arg == "focus") {
            Vue.nextTick(function() {
                $(el).prop("title", binding.value).tooltip({
                    trigger: binding.arg,
                    html: true
                });
            });
        }
    },
    update: function(el, binding) {
        if (binding.value != binding.oldValue) {
            if (binding.value == null) {
                Vue.nextTick(function() {
                    $(el).tooltip("dispose");
                });
            } else {
                //hover tooltip
                if (binding.arg == "hover" || binding.arg == "focus") {
                    Vue.nextTick(function() {
                        $(el).prop("title", binding.value).tooltip("_fixTitle").tooltip({
                            trigger: binding.arg,
                            html: true
                        });
                    });
                } else {
                    //manual tooltip
                    Vue.nextTick(function() {
                        $(el).prop("title", binding.value).tooltip("_fixTitle").tooltip({
                            trigger: 'manual',
                            html: true
                        }).tooltip("show");
                    });
                }
            }
        }
    }
});

这里隐藏的时候写tooltip(“hide”)并不能隐藏tooltip,原因尚未找到;但是可以用tooltip(“destroy”)函数来销毁tooltip,且并不影响后续的再次显示。动态修改提示内容是通过tooltip(“_fixTitle”).tooltip(“show”)链式调用函数来实现更新显示的。

使用的时候向v-tooltip传入错误提示内容即可:

<div :class="{'has-error':portTooltipMsg!=null}" class="input-group">
      <input :disabled="sServerStatusInfo.sServerStatusInfoRes.status=='started'" @input="portTooltipMsg=null" class="form-control text-left" data-placement="bottom" placeholder="Listening Port Number" required="required" type="text" v-model.trim="sServerInfo.sServerInfoRes.port" v-tooltip="portTooltipMsg"/>
</div>

2.ing状态和功能按钮js组件

这里写图片描述这里写图片描述这里写图片描述这里写图片描述
图中所示分别是两个按钮,一个是启动按钮(Start),一个是停止按钮(Stop)
当点击Start按钮后,如果配置输入框的内容符合要求,就调用连接api,同时按钮的显示内容更改为ing正在连接状态(前面是转动的圆圈,文字变成ing状态,并且末尾添加省略号),当监听到后台发送的Ajax数据中显示已经启动成功后,Start按钮隐藏,Stop按钮显示,点击Stop按钮后也是将其内容更改为ing正在停止状态,同Start;如果点击Start以后输入框验证不通过,不对Start按钮进行ing状态切换。
这个具有状态显示和发起请求功能的按钮存在于多个页面,为了提高代码复用性和简化实际编写代码时要考虑的状态切换(我能想到的还有一种状态切换显示方法是写两个按钮,一个按钮是第一个正常状态命名为按钮1,第二个是ing状态的按钮2,如果点击按钮1,输入内容验证通过则按钮1隐藏,按钮2显示,当后台提示Start成功则按钮2影藏,第三个停止按钮显示),我决定写一个封装这个具有状态切换显示的按钮组件:

//when click the button, the text add 'ing' and revolved circle, and call the callbackfun
Vue.component("gl-button", {
    template: '<button type="button" class="btn" :class="classType" @click="loading">\
    <revolved-circle-span :name="typeName" :isLoading="isLoading"></revolved-circle-span>\
    </button>',
    props: {
        classType: {
            required: true,
            type: [Object, Array]
        },
        name: {
            required: true,
            type: String
        },
        isReset: {
            type: Boolean
        }
    },
    data: function() {
        return {
            isLoading: false,
            typeName: this.name.toLowerCase()
        }
    },
    methods: {
        loading: function() {
            this.$emit("onclick");
            this.isLoading = true;
        }
    },
    watch: {
        //reset button if the button is hide
        classType: function(newVal, oldVal) {
            if (newVal.hide == true) {
                this.isLoading = false;
            }
            if (newVal.resetBtn == true) {
                this.isLoading = false;
            }
        },
        isReset: function(newVal, oldVal) {
            if (newVal == true) {
                this.isLoading = false;
                this.isReset = false;
            }
        }
    }
});

里面的文字切换:

//revolved circle msg
Vue.component('revolved-circle-span', {
    render: function(createElement) {
        if (this.isLoading == false) {
            return createElement("span", this.UpperName);
        } else {
            return createElement("span", [createElement('span', {
                'class': ["fa fa-spin fa-circle-o-notch"]
            }), " " + this.UpperName + "ing..."])
        }
    },
    props: {
        name: {
            type: String,
            required: true
        },
        isLoading: {
            type: Boolean,
            required: false
        }
    },
    computed: {
        UpperName: function() {
            return this.name[0].toUpperCase() + this.name.substring(1);
        }
    }
});

这个按钮有三个接收值,分别是classType,name以及isReset。classType指定了这个按钮的显示样式类,比如背景色等;name指定了这个按钮的功能名,比如start,stop;isReset属性用于控制它从ing状态复原到默认状态。
这个按钮有一个loading函数,当点击按钮后,默认会切换到ing状态,同时触发组件的onclick事件,以调用组件使用者想在按钮点击后调用的函数。
这个按钮还有两个属性,一个叫isLoading,保存当前按钮是正常状态还是ing状态;组件监听两个传入值classType和isReset,如果classType中有hide,意思是按钮需要隐藏,那么按钮就在隐藏后复原到默认显示,以便下次显示时又是默认状态;监听isReset属性,如果isReset属性为true,则改变按钮为默认状态。
组件使用代码:

<gl-button :class-type="{hide:sServerStatusInfo.sServerStatusInfoRes.status=='started'}" :is-reset="isResetBtn" @onclick="setServer" class="btn btn-success btn-rounded w-md waves-effect waves-light m-b-5" name="Start" v-show="sServerStatusInfo.sServerStatusInfoRes.status=='stopped'">

3.Vue.js组件命名约定

当注册组件 (或者 prop) 时,可以使用 kebab-case (短横线分隔命名)、camelCase (驼峰式命名) 或 PascalCase (单词首字母大写命名)。
这里写图片描述
这里写图片描述
这意味着对组件进行命名时,短横线小写命名法是最通用的。

4.字符串模板和非字符串模板

写在html中的 就是非字符串模板,写在js代码的template:”“,就是字符串模板。
同样的,在prop的命名规范中,也有如下要求:
HTML 特性是不区分大小写的,所以,当使用的不是字符串模板时,camelCase (驼峰式命名) 的 prop 需要转换为相对应的 kebab-case (短横线分隔式命名)。
也就是说如果注册组件时无论用哪种命名法为组件命名(比如命名为glButton,gl-button,GlButton),在html代码中使用该组件时都要用这种短横线小写命名法,否则代码报错,会提示组件未注册。
这里写图片描述
除了使用字符串模板(template)以类似html的形式写明节点内容,还可以使用render函数(https://cn.vuejs.org/v2/api/#render)来渲染节点,其中createElement函数的参数列表如下:
这里写图片描述
举例:要写一个span节点,包含一些文本节点以及strong标签,代码如下:

<span >Connecting...Please <strong>Do NOT refresh the page</strong>.</span>

用render函数进行节点渲染的代码如下:

render: function(createElement) {
    if (this.status == "connecting") {
        return createElement('span', ["Connecting...Please ",createElement("strong","Do NOT refresh the page.")])
    } else if (this.status == "connect") {
        return createElement('span', "<strong>Connection succeeds!</strong>")
    } else {
        return createElement('span', "<strong>Connecting failed!</strong>");
    }
},

5.v-bind:class传入对象时属性是否添加引号的问题

当绑定为对象时,如果是html标签固有的class属性,直接写;如果不是固有的,要写单引号或者双引号(与外层引号类型不同):

:class-type="{'btn':true,'gli-btn-highlight':true,hide:(vpnStatusClassObj==null?true:!(vpnStatusClassObj.active==false&&vpnStatusClassObj.waiting==false))}"

6.watch对象里不能使用this关键字

7.关于父子组件的props值更新

每次父组件更新时,子组件的所有 props 都会更新为最新值。这意味着你不应该在子组件内部改变 prop。如果你这么做了,Vue 会在控制台给出警告。如果想要在子组件中修改通过props传来的父组件的值,最好在Data中声明一个局部变量来保存,并用 prop 的值初始化它(另外,在组件中的Data是个函数,如果写成对象,会报错):

props: ['initialCounter'],
    data: function () {
    return { counter: this.initialCounter }
}

8.Vue.js中数据绑定方式

  • 文本插值或者说双大括号,形式为{{加引号的普通文本||解释为普通文本的vue变量||单个javascript表达式}}:注意这里表达式能访问的全局变量不包括用户定义的全局变量 (https://cn.vuejs.org/v2/guide/syntax.html#使用-JavaScript-表达式)

  • v-html=”html代码”:注意html代码中的属性的值不会被解析为普通文本,而是被当做纯html进行解析

9.计算属性VS方法 /侦听属性

  • 计算属性:在computed里设置属性的getter函数,该计算属性完全依赖于data对象里定义的属性的值,当其依赖的属性的值改变时,计算属性的值也会跟着改变。可以在模板中绑定计算属性。

  • 计算属性vs方法:计算属性也可以以函数的形式声明,结果完全一样:不同之处在于计算属性只有在其依赖改变时,对其进行的访问才会重新执行函数;但是以函数声明的方式,只要触发了这个部分的重新渲染就会执行函数。因此说计算属性是有缓存的。

以下是定义一个倒序文本为方法的形式,正确做法是将其定义为计算属性(computed),依赖于正常顺序文本,当正常顺序文本的值改变时,再触发对倒序文本的重新计算。
这里写图片描述这里写图片描述

  • 计算属性(computed)vs侦听属性(watch):如果一个属性依赖于两个及以上的属性,那么它最好声明为计算属性而不是侦听属性,否则你需要侦听其依赖的两个及以上的属性。

  • watch侦听属性封装操作:使用 watch 选项允许我们执行异步操作 (访问一个
    API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

10.数据中心对象用于缓存向api请求的数据

  • 在路由器配置页面这个项目中,存在一些api,既要在始终存在的主页面调用,也要在子页面调用,以请求状态信息数据。主页面调用的api固定i每隔5秒向后台发起Ajax请求,而子页面则是在需要时(比如点击了Connect按钮发起连接后)调用api以请求信息,那么有可能这两个请求前后相隔时间较短(小于半秒),而经验表明连接状态变化一般不可能在半秒内完成,因此可以认为这个状态信息在半秒内是有效的。
  • 为了减少向后台发起请求的次数,可以将每次api请求的数据都保存到一个对象里,即数据中心对象,用a比如主页面的定时器每隔5秒向api请求openVPN的连接状态以更新openVPN的连接状态图标,而openVPN的配置子页面在发起连接时也需要请求相同的api,那么如果请求相隔时间(通过lastSync属性值计算)小于500毫秒,且上一次请求是成功的(unsuccess=false),那么就使用上一次的数据,以减少对服务器的请求。
    数据中心对象的结构如下图,其中apiName属性是每请求一个新的api就动态增加的,它的值等于apiResults中与其对应的apiURL的值,这是为了在代码中访问数据时可以直接使用DataCenter[apiName]这种形式,而不用使用冗长的DataCenter.
    这里写图片描述
展开阅读全文

没有更多推荐了,返回首页