2018遇到的问题

I belong to : Experience


2018/2

火狐和IE下 javascript:void(0) 会弹出空白页

本来javascript:void(0);的用处是不用整体刷新网页且返回一个空值,但这儿由于DOM本身的冒泡事件所以会最后执行HREF属性内的javascript:void(0);导致执行函数返回了一个空值,所以覆盖掉了前面正常执行函数所返回的值引起的错误。

一般情况下,IE会先运行DOM本身绑定的事件,如ONCLICK;如果没有阻止冒泡,则会顺序执行HREF属性。如果想正确运行,可以在前面用RETURN FALSE终止冒泡,例如:

<a target="_blank" class="prev" onclick="return false;"   href="javascript:void(0);"></a>

或者直接删去也行,如:

<a target="_blank"  class="prev" ></a>

但是后者hover的时候没有手势的pointer

vue2父组件传递props异步数据到子组件,获取到的是undefined.

解决方法参考链接

  1. 最简单的解决方法:使用v-if
  2. 子组件使用watch来监听父组件改变的prop,使用methods来代替created
  3. 子组件watch computed data 相结合,有点麻烦
  4. 使用emit,on,bus相结合(使用了bus库)
  5. vuex结合computed、mapState或者合computed、mapGetters

对象的属性名称可以是变量吗?

可以,运用方括号法设置就是。如

var a = "name";
var b = 10;
var obj = {};
obj[a] = b;

后台给的服务链接直接插入html中img的src里

修改图片之后,因为src的内容一直都是后台服务链接,是个不变的值,所以不会重新去请求,图 片会一直不变。如果刷新页面这个链接返回的图片会改变,为了做到点击图片只重载这个src而整个页面不刷新,采用的方法是在后面加上 Math.random(),例如

`http:${this.baseURL}gw/app.get.logo?tenantId=${this.tenantId}&appId=${this.appId}?`+Math.random();

扩展运算符可以用于数组深拷贝

var arr2 = arr;
var arr3 = [...arr];
console.log(arr===arr2); //true, 说明arr和arr2指向同一个数组
console.log(arr===arr3); //false, 说明arr3和arr指向不同数组

.number 的修饰符

数字类型的验证需要在 v-model 处加上 .number 的修饰符,这是 Vue 自身提供的用于将绑定值转化为 number 类型的修饰符。 v-model.number只会截取数字部分,比如100ttt只会取前面的100。做修改操作的时候这种情况用自定义validator解决

2018/3

给点击路由的时候加上监听事件

<router-link class="my-link" :to="{name: 'main.provider.index', params: {providerId: scope.row.providerId}}">提供者主页</router-link>

解决方法:给组件绑定原生事件。有时候,你可能想在某个组件的根元素上监听一个原生事件。可以使用 v-on 的修饰符 .native。例如:

<my-component v-on:click.native="doTheThing"></my-component>

参考连接 给组件绑定原生事件

块内元素文字上下居中

line-height设置为height的值

用refs来组件通信

在讨论非父子通信的时候,并在没有用Vuex的情况下,我是用官网那种创建Vue实例的方法,但是同事提到了另一种方法 用refs来引用,不禁一想这好像也是一种解决方案,就是觉得怪怪的。在这里记录一下

关于第4点,今天工作用了this.$refs来传播事件,类似于$brocast,但是VUE2把$brocast废除了。

总结一下vue传播事件

(1) 子组件传播事件给父组件,用$on,$emit.

虽然说是这样说,但是不是直接在父组件里面写$on的,应该是这样

<parent>
    <child @eventname="event">
</parent>

然后在parent组件里面写 event函数.event(){}

子组件调用的时候用this.$emit('eventname',payload)

(2)父组件传播事件给子组件,用 $refs

<parent>
    <child ref="refname">
</parant>

调用的时候用this.$ref['refname'].eventname()

(3)上面有种都麻烦的话,创建实例bus

js的数组可以是这样的形式

(4) [{…}, {…}, {…}, {…}, total: 4, code: "core.ok"]

他的length是4

把字符串转换成数字可以用数字运算符,但是 + 不行

console.log("2">>>0)  //2 number 
console.log("2">>0)  //2 number 
console.log("2"<<0)  //2 number  没有(<<<)
console.log("2"- 0)    //2 number
console.log("2"+0)    //20 string

reduce函数好好用

今天写了一个有重大作用的reduce函数,好开心。在此纪念一下

//str {String}, "app.get.logo.list"
//ctx {Object},  this._$http
//return {Object}, this._$http.app.get.logo.list
 getBackend(str){
        const arr = this.loadBackend.split('.');
        let ctx= this._$http;
        let a;
        let b=arr.reduce((prev, cur, idx) => {                
              a = prev[cur] ;         
            return a;
        },ctx);              
          return b;      
      }

vue项目中全局引入scss文件的方法

npm install node-sass --save-dev
npm install sass-loader --save-dev
npm install sass-resources-loader --save-dev

修改build/utils.js

scss: generateLoaders('sass').concat(
      {
        loader: 'sass-resources-loader',
        options: {
          resources: path.resolve(__dirname, '../src/assets/your.scss')
        }
      }
    )

elementUI的layout问题

el-footer会直接fixed在窗口底部,不好看,

解决方法:el-footer放到el-main里面

<el-container style="height:100%;">
    <el-header class="header" style="height:100px">
        <cty-navbar :navs="navs"></cty-navbar>
    </el-header>

    <el-main>      
        <el-scrollbar wrap-style="overflow-x:hidden;" style="min-height: 800px">
            <div>
                <router-view></router-view>
            </div>           
        </el-scrollbar>

        <el-footer class="footer" style="height:70px">
            <cty-footer></cty-footer>
        </el-footer>
    </el-main>

</el-container>

vue路由刷新问题,两个路由对应一个组件,如何能让路由重载

https://segmentfault.com/q/1010000010844655

写在共用组件文件里

data(){
...
}
mounted(){...},
 watch: {          
            $route: {
                handler: function (val, oldVal) {
                    this.worksheet_type = this.$route.params.type;                     
                },
                deep: true
            }
        },

在共用组件上加上key <div :key="$route.path">

单行文本超出隐藏并显示省略号

.element{
width: 20em;/*不允许出现半汉字截断*/
overflow: hidden; /*自动隐藏文字*/
text-overflow: ellipsis;/*文字隐藏后添加省略号*/
white-space: nowrap;/*强制不换行*/

多行文字隐藏加省略号

.element{
    width: 20em;
    height: 3em;/*注意高度和宽度,不允许出现半汉字截断*/
    line-height: 1.5em;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
}

2018/4

实现QQ弹窗 和QQ号为qqnum聊天

<a target="_blank" :href="`http://wpa.qq.com/msgrd?v=3&uin=${qqnum}&site=qq&menu=yes`">
    <img border="0" :src="`http://wpa.qq.com/pa?p=2:1424314130:51`" alt="点击这里给我发消息" title="点击这里给我发消息"/>
</a>

Vue全局过滤器也可以当函数使用

this.$options.filters.dateformat

字符串转义问题

本地字符串不会自己转义,所以本地需要写成 \\d

后台请求返回的字符串会自动帮你转义,所以在后台数据库只需要写成\d

如何解决vue父组件模板slot到孙子组件中去

>每一个被继承的组件都需要有slot;

参考链接

记录一下第一次使用async ,await ,解决了困扰我一早上的异步问题,赞!!!

   //获取文件信息
    getFileInfo(item){
        let fileId= item.value;
        const downloadLink =`/gw/tenant/worksheet/extend/Download?worksheetInfoId=${item.worksheetInfoId}&blobId=offer.info`;
        return new Promise((resolve,reject)=>{
            if(fileId == 'undefined') resolve(null);
            this._$http.tenant.offerExtendGetFileInfo({fileId: fileId})
            .then(res=>{ 
                res={...res,downloadLink};
                resolve(res)
            }) 
        })           
    },
    //获取文件信息
    fileGetter: async function(item) {           
        const fileInfo = await this.getFileInfo(item);
        return Promise.resolve(fileInfo);         
    },

自定义指令---解决跟dom有关的问题

最大的用处就是可以引用一些第三方的代码插入到Vue项目中,比如有一个操作dom的函数:

//当然,真实情况第三方的代码要复杂的多
function changeColor(dom){
    dom.style.backgroundColor = "red";
}

我们可以注册一个全局的指令来为需要执行changeColor方法的dom提供指令:

Vue.directives('color',{
    bind:function(el){
        changeColor(el)
    }
})

这样,如果需要这个dom改变颜色的话,只需要这样即可: <div v-color>改变颜色</div>

当日常开发遇到跟dom有关的问题却一筹莫展时,可以想想自定义指令是否有功能可以解决为题

父组件给子组件传值,这个值还要从子组件传给它的子组件【inheritAttrs】和【attrs】

直接一层一层传prop非常麻烦. Vue提供了【inheritAttrs】和【attrs】两个功能来解决这样的问题

//父组件
<template>
   <div>
     <child :text="text"  :count="count"></child>
   </div>
</template>

<script>
    export default{
        data(){
            return {
                text:"父组件的值" count:123456,
            }
        }
    }
</script>
//子组件
<template>
    <div>{{text}}</div>
</template>
<script>
    export default{
        props:["text"]
    }
</script>

这里父组件的count属性仅仅挂在子组件上,并没有使用。此时我们打开浏览器,可以看到子组件的dom上显示的展示了<div class='child' count="123456"></div>

此时,我们可以通过设置inheritAttrs: false来取消这种默认行为:

data(){
  return{
    ......   
  }  
}

inheritAttrs: false,
mounted(){
  console.log(this.$attrs); //{count:123456}
}

这时再看dom上就没有count属性了。然后,我还打印了this.$attrs的值,值为一个包含着count键值对的Object。 也就是说,父组件没有props的属性值会被保存在一个名为$attrs中供子组件使用,然而这并没有解决开头子组件的子组件获取值的问题。 别急,我们只需要在子组件上加个东西就可以了:

<template>
    <div class="child">
        <my-child v-bind="$attrs"></my-child>
    </div>
</template>

这样,子组件的子组件也可以获取这个值了。

provide / inject -更适合用来做父组件给后代组件传值,

provide/inject方法要比inheritAttrs/attrs更适合用来做父组件给子组件或孙组件传值, 文档的链接

//父组件使用provide
<template>  
    <div class="parent">     
         <child-component></child-component>   
    </div>  
</template>

<script>
export default {  
    ......  
    provide: { parent: "父组件的值"},  
    components:{    child-component,  }, 
     ......
</script>


//此时可以在子组件通过这种方式获取父组件中“parent”的值:
//子组件中
export default {  
    mounted(){      
        console.log(this.parent); //"父组件的值"  
    },  
    inject: ['parent'],
}

2018/5

发现三个很厉害的库

htmlDocx (将html代码导出为doc文件)http://evidenceprime.github.io/html-docx-js/build/html-docx.js saveAs(将浏览器内存中的doc文件保存至硬盘)http://evidenceprime.github.io/html-docx-js/test/vendor/FileSaver.js domtoimage(将dom节点转化成为图片)https://cdnjs.cloudflare.com/ajax/libs/dom-to-image/2.6.0/dom-to-image.min.js

domtoimage.toPng(dom); //转出来是base64 saveAs(htmlDocx.asBlob(html), filename);

主要这三个库方法 domtoimage.toPng saveAs htmlDocx.asBlob

Vue的router的push传递参数有没有不把参数展示在url中的传递方式?

const userId = 123
router.push({ name: 'user', params: { userId }})
// 路由设置 path 为:'/user/:userId'
// 跳转路径:'/user/123'

// 路由设置 path 为:'/user'
// 跳转路径:'/user'
// this.$route.params.userId 获取userId

2018/6

使用keep-alive

在商品列表进入商品详情页之后 vue中使用keepAlive组件缓存遇到的坑

a标签里面不加download属性会下载下来一些莫名其妙的东西

<a :href="file.worksheetFileId|url"  :download="file.fileName">{{file.fileName}}</a>

html5对a标签新增的download属性用于下载文件,简单的理解是a标签如果添加了download属性,那么点击它的时候就不会跳转,而是会触发浏览器下载文件。 那么我不加download属性会下载下来的东西是什么??

答案:docx后缀的word文档用解压软件打开就是这个目录结构

$nextTick

Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。 $nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 。

router-link存在的问题是,手动输入改变url,下面路由没监听到

vue-router在IE 11下有bug,a href 里的路径,点击地址栏发生了变化,页面并不跳转 vm._route.path并没有发生变化,和地址栏不同步 要加上这段代码修正下:

function fixRouterIE(vm){
    //IE 11下
    if (
      '-ms-scroll-limit' in document.documentElement.style && 
      '-ms-ime-align' in document.documentElement.style
    ) { 
      window.addEventListener("hashchange", function(event) {
        const currentPath = window.location.hash.slice(1);

        if (vm._route.path !== currentPath) {
          vm._router.push(currentPath)
        }
      }, false)
    }
}

height设为100%要求父容器高度确定,否则无效。

el-scrollbar滚动到容器底部

<el-scrollbar style="height:100%" ref="elscrollbar">
var div = this.$refs['elscrollbar'].$refs['wrap'];
this.$nextTick(() => {
    div.scrollTop = div.scrollHeight
})

怎么应用vertical-align,才能生效?

怎么应用vertical-align,才能生效

  1. 父元素(inline-block\block)必须含有line-height(inline元素有无皆可),子元素(inline-block/inline元素)中的vertical-align才能起作用。 vertical-align不可继承,必须对子元素单独设置。

  2. 当父元素没有设置line-height时,vertical-align只对行内元素的兄弟元素对齐有用,无法子元素居中对齐父元素。 设置了vertical-align:middle的子元素的中线与兄弟元素的基线对齐。若兄弟元素都设置该项,则居中对齐。

Array.prototype.sort 兼容

IE9-- Array.prototype.sort 不能根据 对象属性 做排序的遗憾

//方法1冒泡排序
var mySort = function(fn){
    if(typeof fn != 'function'){
        fn = function(a,b){
            return a-b;
        }
    }

    for(var i=0; i < this.length-1;i++){
        for(var j=i+1;j<this.length;j++){
            var t = this[i];
            if(fn(this[i],this[j]) > 0){
                this[i] = this[j];
                this[j] = t;
            }
        }
    }
    return this;
}

if(typeof Array.prototype.sorts!= 'fucntion'){
    Array.prototype.sorts = mySort;
    mySort = null;
}


//方法2 插入排序
var mySort = function(fn){
    if(typeof fn != 'function'){
        fn = function(a,b){
            return a-b;
        }
    }
    for(var i=1;i<this.length;i++){
        var t = this[i];
        var j = i-1;
        while(j >= 0 && fn(this[j],t)> 0 ){
            this[j+1] = this[j];
            j--;
        }
        this[j+1] = t;

    }
    return this;

}
if(typeof Array.prototype.sorts!= 'fucntion'){
    Array.prototype.sorts = mySort;
    mySort = null;
}

safari 不支持函数参数

!function(window){ 
    var ua = window.navigator.userAgent.toLowerCase(), 
    reg = /msie|applewebkit.+safari/; 
    if(reg.test(ua)){ 
        var _sort = Array.prototype.sort; 
        Array.prototype.sort = function(fn){ 
            if(!!fn && typeof fn === 'function'){ 
                if(this.length < 2) return this; 
                var i = 0, j = i + 1, l = this.length, tmp, r = false, t = 0; 
                for(; i < l; i++){ 
                    for(j = i + 1; j < l; j++){ 
                        t = fn.call(this, this[i], this[j]); 
                        r = (typeof t === 'number' ? t : 
                        !!t ? 1 : 0) > 0 
                        ? true : false; 
                        if(r){ 
                            tmp = this[i]; 
                            this[i] = this[j]; 
                            this[j] = tmp; 
                        } 
                    } 
                } 
                return this; 
            }else{ 
                return _sort.call(this); 
            } 
        }; 
    } 
}(window); 

https://blog.csdn.net/lt3487928/article/details/53157817

https://www.cnblogs.com/Alucelx/archive/2011/07/13/2104381.html

2018/7

vue强制更新

用vue.$set不行,watch不行,试试$forceUpdate。 还有一种办法,再把值重新复制一遍也是可以的 this.obj = Object.assign({},this.obj)

在Vue中使用sass及sass图片路径问题

https://www.jianshu.com/p/5e81814f8d8c

CORS跨域请求

会先发option请求,如果server返回access-control-allow-origin头为*或者和当前域名一致的话,才会进入第二段的真正请求。

禁止缓存数据deactivated

防止下次选择数据不更新

  deactivated () {
    this.$destroy(true)
  }

深拷贝

arr.slice() 如果obj所有值都是非引用类型,那么obj.slice(0)与深浅拷贝没有差别;

如果obj有引用类型的元素的话,obj.slice(0)仅仅是复制了元素的地址,,obj.slice(0)可看作浅拷贝。

对象深拷贝 Object.assign({},obj),不要用JSON.parse(JSON.stringify(obj)),太慢

事件冒泡到父容器或某个祖先容器停止

在父容器或某个祖先容器上绑定一个@click.stop='stop' stop函数为一个空函数即可

2018/10

webpack-dev-server启动

并不是访问vue项目内某个文件, src/assets内的文件是不能在index.html直接引用的, 必须经过loader处理路径

plublic里放置的文件是不经过 webpack 处理的。在 index.html 里引用的时候,直接就是 <script src="/plublic/oidc-client.js"></script>。

scss export


 $title-width: 100;

:export {
  titleWidth: $title-width;
}

ie margin-top负值无效

https://www.cnblogs.com/chaozhang/p/4711282.html

加上display:inline-block

inline-block的元素设置over-flow:hidden会意外地增加元素总体高度,

用vertical-align:bottom解决

ie中img被拉伸

只要设置img为 height:auto,width:auto,就不会出现这种情况了

vue 路由query变化监测不到meta的

这样使用query,路由变化,meta并没有变化

{
    name: 'order.list.1',
    path: '/order/list/status=1',
    component: list,
    meta: {
      domain: 'order',
      key: 'order-list-1',
    },
  },

  {
    name: 'order.list.2',
    path: '/order/list/status=2',
    component: list,
    meta: {
      domain: 'order',
      key: 'order-list-2',
    },
  },

使用params是可以的

{
    name: 'order.list.1',
    path: '/order/list/1',
    component: list,
    meta: {
      domain: 'order',
      key: 'order-list-1',
    },
  },

  {
    name: 'order.list.2',
    path: '/order/list/2',
    component: list,
    meta: {
      domain: 'order',
      key: 'order-list-2',
    },
  },

使用函数模式也可以

const dict = {
  1: 'order-list-1',
  2: 'order-list-2',
  default: 'order-list',
};


export default{
  name: 'order',
  path: '/order',
  redirect: {
    name: 'order.list',
  },
  component: Container,
  children: [{
    name: 'order.list',
    path: '/order/list',
    component: list,
    meta: {
      domain: 'order',
      key(route) {
        return dict[route.query.status || 'default'];
      },
    },
  },

vue 指令使用模板,不再用template了,用el

//Marquee.js
import Marquee from './Marquee.vue';
const Tpl = Vue.extend(Marquee);//这个好漂亮
const Plugin = {
  install(vue, options) {
        Vue.directive('marquee', {
           inserted(el, binding, vnode) {
              const mask = new Tpl({
                  el: document.createElement('div'),
                  data: { name: binding.value }
              });
              el.appendChild(mask.$el);
           })
     }   
  }
}
export default Plugin;
//main.js
import Marquee from './Marquee.js'
Vue.use(Marquee)

2018/12

完美解决本地和服务器上资源url解析的问题

在 config/index.js 中修改 assetsPublicPath 为 ./ 在 build/utils.js 中的 ExtractTextPlugin.extract 传入参数 publicPath: '../../'

知识记录

for\for-in\for-each\for-of

for-in不仅可以遍历数组还可以遍历对象 for-in 遍历属性的顺序并不确定,即输出的结果顺序与属性在对象中的顺序无关,也与属性的字母顺序无关,与其他任何顺序也无关。 使用for-in在遍历的时候,它不仅遍历了对象上的属性,而且还遍历了对象父类原型上的属性。 所以for-in并不适合遍历Array中的元素,更适合遍历对象中的属性 for-in只会遍历存在的实体,以及对应的原型链上的属性.因此其速度相比较其他for循环,要慢一些。 所以除非明确要迭代一个属性数量未知的对象,否则应该避免使用for-in。 for-in遍历数组的缺点:数组的下标index值是数字,for-in遍历的index值"0","1","2"等是字符串

在不同浏览器下测试的结果都是 forEach 的速度不如 for。 forEach主要应用在遍历数组,但是它的性能并不如for,因此可以使用for就尽量不要使用forEach。 forEach 不能break 或者return

for-of不支持普通对象遍历,只返回具有数字索引的属性.

遍历数组:for-of > for > for-each 遍历对象:for-in(要用hasOwnProperty) await/async 要用for-of for循环、while循环、do-while循环比for in 要快

字符串 拼接性能

比较下四中字符串拼接方法的性能:

A:str = str + 'a'+'b'
B:str += 'a' + 'b'
C: arr.join('')
D:str.concat('b','c')

☆ Chrome65上测试的是A优于B优于C优于D

总结一些常用的

1、内容长短不固定的时候用min-height,而不要用height

2、

//显示一行,多余的用省略号表示
.one-line{
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: inline-block;
}
//最多显示2行,多余的用省略号表示
.two-line{     
    overflow: hidden;     
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    text-overflow: ellipsis;
}

3、 一般英文或者数字是默认不换行,所以要加上 css word-break: break-all;

4、placeholder

:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
    color: #C6C6C6;
}
::-moz-placeholder { /* Mozilla Firefox 19+ */
    color: #C6C6C6;
}
input:-ms-input-placeholder{
    color: #C6C6C6;
}
input::-webkit-input-placeholder{
    color: #C6C6C6;
}

vue源码研读

手拉手带你过一遍vue部分源码

当面试官问你Vue响应式原理,你可以这么回答他

如何解释vue的生命周期才能令面试官满意?

在使用.vue文件开发的过程当中,我们在里面写了template模板,在经过了vue-loader的处理之后,就变成了render function,最终放到了vue-loader解析过的文件里面。这样做有什么好处呢?原因是由于在解析template变成render function的过程,是一个非常耗时的过程,vue-loader帮我们处理了这些内容之后,当我们在页面上执行vue代码的时候,效率会变得更高。

Vue为我们提供了renderError方法,这个方法只有在开发的时候它才会被调用,在正式打包上线的过程当中,它是不会被调用的。它主要是帮助我们调试render里面的一些错误。

renderError (h, err) {
  return h('div', {}, err.stack)
}

有且只有当render方法里面报错了,才会执行renderError方法。 所以我们主动让render函数报个错:

render (h) {
  throw new TypeError('render error')
}

还有一点,renderError只有在本组件的render方法报错的情况下它才会被调用。

Cache-Control

no-cache: 告诉浏览器、缓存服务器,不管本地副本是否过期,使用资源副本前,一定要到源服务器进行副本有效性校验。
must-revalidate:告诉浏览器、缓存服务器,本地副本过期前,可以使用本地副本;本地副本一旦过期,必须去源服务器进行有效性校验。
max-age: 指缓存资源的缓存时间比指定的值小,那么客户端就接受缓存资源,且缓存服务器不对资源有效性进行再次确认

CSS像素

是web编程的概念,是相对的而不是绝对的单位. 用户的缩放比会影响单位CSS像素点对应的实际物理像素的多少

使用Flexible实现手淘H5页面的终端适配

https://github.com/amfe/article/issues/17

禁止使用document.write()

异步脚本禁止使用document.write() 延迟脚本禁止使用document.write()

export及export default的区别

在一个文件或模块中,export可以有多个,export default仅有一个。

(1) 导入的时候带花括号

```javascript
    // 写法一
    export var m = 1;

    // 写法二
    var m = 1;
    export {m}; //export m 或者export 1都错误

    // 写法三
    var n = 1;
    export {n as m};
```

(2) export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。

(1) 导入的时候没有花括号

```javascript
    import str from 'demo1' //导入的时候没有花括号
```

(2) export default命令的本质是将后面的值,赋给default变量,所以可以直接将一个值写在export default之后。