13

本期

因为需要写这方面的文章, 导致我现在工作中很期待有趣bug的出现, 看到测试给我提bug还有点高兴...

1:element-ui的form表单组件敲'Enter'默认刷新页面

bug现象:
如题所说, 我们需要做的无非是禁掉默认的提交事件, element本身没有禁用提交属性, 下面是一块有bug的代码.

<template>
<el-form ref="form" :model="form" label-width="80px">
  <el-form-item label="活动名称">
    <el-input v-model="form.name"></el-input>
  </el-form-item>
  <el-form-item>
    <el-button type="primary" @click="onSubmit">立即创建</el-button>
    <el-button>取消</el-button>
  </el-form-item>
</el-form>
</template>

<script>
  export default {
    data() {
      return {
        form: {
          name: '',
        }
      }
    },
    methods: {
      onSubmit() {
        console.log('submit!');
      }
    }
  }
</script>

解决方式:
在form层多了个onsubmit="return false;"
<el-form ref="form" :model="form" label-width="80px" onsubmit="return false;">

更好的解决方式:
这种默认提交功能项目中基本不会使用了, 所以基本是要禁止掉的, 我们如果全局有5个form表单就要改5个地方, 所以程序员肯定会想要做一个公共的处理方式, 第一时间想到的就是二次封装, 但是虽然二次封装, 我仍然希望这个组件叫el-form, 那么会遇到什么问题那?

  1. 必须还叫 <el-form>
  2. 我按需引入的element-ui组件, 那么我不把它的form组件放在全局定义
  3. 我是直接完全引入的element-ui组件, 我不想换成按需引入
  4. 避免循环引入的bug

main.js代码如下:

import Vue from 'vue';
import App from './App.vue';
import ElementUI from 'element-ui';
import Form from './components/form.vue';
import 'element-ui/lib/theme-chalk/index.css';

// 这里我们不改变原本的组件定义, 这里定义组件并不是立即渲染, 所以可以被顶掉.
Vue.use(ElementUI);
// 这里使用我们自己的组件, 名字还是使用element-ui原本的名字.
Vue.component(ElementUI.Form.name, Form);

new Vue({
  render: h => h(App),
}).$mount('#app')

./components/form.vue里面我们仅需包裹一下:

<template>
  <x-form onsubmit="return false;"  v-bind="$attrs" v-on="$listeners" >
    <slot />
  </x-form>
</template>

<script>
import { Form } from "element-ui";

export default {
  components: {
    XForm: Form
  }
};
</script>

上面要注意, 之所以我起名为XForm就是要避免循环调用, 因为当vue识别这个组件的时候,全局是有一个el-form组件的, 不改名会报错Uncaught RangeError: Maximum call stack size exceeded爆栈.

2: intersectionObserver元素是否出现在视野内

bug现象:
我之前写过一个图片懒加载的插件, 第七集: 从零开始实现一套pc端vue的ui组件库( 懒加载v-lazy )与'骨架屏模板' 组件, 这个里面我判断图片是否出现在屏幕视野内的方式是, 计算图片相对于窗口的位置, 比如说用图片的宽减去他距离左侧的距离就可以知道它左边是否出现以及出现的百分比, 但是... 当有了这个属性一切都不一样了, 可以说之前自己写的是'错'的了.
代码呈现
大家可以直接拿去玩一下, 简直太好用了.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #ks{
      position: absolute;
      border: 1px solid red;
      left: 10px;
      width: 100px;
      height: 100px;
    }
  </style>
</head>
<body>
  <div id="ks">我来了</div>
  <script>
    const ks = document.getElementById('ks');
    // IntersectionObserver是浏览器原生提供的构造函数,接受两个参数:callback是可见性变化时的回调函数,option是配置对象(该参数可选)。
    var io = new IntersectionObserver((res)=>{
      // intersectionRatio 是可见的范围, 都可见为1, 都不可见为0
      console.log('隐藏了么',res[0].intersectionRatio)
    });
    // 开始观察
    io.observe(ks);
    var n =0;
    ks.onclick=function(){
       this.style.left = n + 'px';
       n -=5
    }
    setInterval(()=>{
      ks.style.left = n + 'px';
       n -=5
    },100)
    // 停止观察
    // io.unobserve(element);

    // 关闭观察器
    // io.disconnect();
  </script>
</body>
</html>

具体的详细解读大家可以看看阮一峰老师对这个方法的介绍, 这条只能算是api知识点, 但是在不知道这个api的情况下确实对我造成了困扰.

3: 项目名也就是main对应的字段, 需要避免与内部的包名重复

bug现象
我创建了一个名为xxx的项目, 并且在项目里面引入了一个叫xxx的包, 这就会导致项目报错.

bug解决
改掉package.json文件中的name属性.

bug回味

  1. 项目起名也是需要有一定章法的, npm包的起名也是要有一定章法的, 比如bable的命名就挺棒的@babel/core @babel/preset-env 这两个包第一眼看去就是围绕着babel产生的包显得很专业.
  2. 我们发布包也要避免污染npm的整体环境, 命名分为核心包与围绕核心包的扩展功能包, 因为一旦发布就不好撤回了, 而且包的名字是不可重复的.

4: flow-root 清除浮动最友好的方式, 不会影响元素本身 也不多写元素

解决的主要问题
清除浮动dispaly:flow-root形成"BFC"

为什么要用它?

  1. 可以说以前用的方式都不是专门针对清除浮动的, 而它才是专业.
  2. 不用新增元素, 哪怕只是占用个伪元素, 没准伪元素需要去做其他的事.
  3. 不用新增属性, 也就是不用多写一条样式, 直接把自身的'display'改了就好.
  4. 不影响父级属性, 比如你用overflow: hidden;其实是影响了自身的属性,内部元素没法出去了.

意义何在
我看到了css的坚持, 现阶段用浮动布局的人不多了, 但css不会因为这样就不管, 它依然想做到更好的解决办法, 可以让我们感觉到前端会一直进步下去.

推荐:
推荐没看过的同学看下我写的布局相关的文章, 跟浮动说拜拜记一次grid布局实战应用分享会

5: 兼容ie11 报错 script1006: 缺少 ')'

别看已经到11了,兼容性还是不行

bug描述
这个问题是同组的同学遇到的, 临时决定兼容ie11出现了下面的错误:
30116AB5-917F-4ef8-9EF1-0B27DC6B8483.png

点击这个bug:
cw.png

bug分析
这个bug是由于ie11监测到了不懂的语法, 但是ie直接这样指出问题的所在开发人员也不好定位,那我们需要解决的核心问题就是把所有es5以后的语法转义为es5应该就能解决问题.
连let都不认识? 我几乎不玩ie.
ii.png

bug定位
zhaobao.png
细心观察的话就会发现, 这个出问题的包的名字在上面.
在配置文件里面没有搜索到这个包, 那么这个包很可能是在依赖的依赖中, 甚至更深!

bug解决
vue.config.js里面配置指定解析项:transpileDependencies: ['resize-detector'],

transpileDependencies:中文意思是跨文件依赖项, 官网的描述如下:
gwnb.png

6: 尝试使用postcss-bem插件

故事陈述
想要进步的前端同学一定不甘于固有的模式, 所以一个同学学习了这篇文章(友情链接):PostCSS深入学习: 结合BEM和SUIT方法使用PostCSS.
大家有时间可以看一看挺有意思的想法, 当时我们的第一印象就是可以玩一玩.

介绍插件
为了懒得看上面的文章的同学我来介绍一下, 这个插件可以使我们项目更好的使用'bem'的结构来写css, 注意:只是css, 比如声明一个hello的盒子, 里面有hello-header元素.
看起来是不是还挺舒服的.

@component hello {
  @descendent header {
    border: 1px solid #ccc;
  }
}

bug1描述
他的文章里没说怎么配置进vue项目里面, 可能是因为文章是2015年写的太早了, 所以具体怎么弄进去大家可以自行研究, 网上资料挺少的就看大家的学习能力了, 我是写在了postcss.config.js文件里面.

module.exports = {
  plugins: [
    require('postcss-bem')({}),
    require('postcss-nested')({}),
  ],
};

运行就报错:
zhaobus.png

bug1解决
真是挺坑的问题, 但还是找到了解决方案:
老版本moveto方法变成append方法才管用, 所以我们需要把源码下载到本地替换掉所有的moveto方法....

bug2描述
问题基本解决了可以很爽的开始写了么?? no...
还是下面的结构, 比如我想取hello-header元素下面的p标签, 这样是取不到的, 必须给p标签一个名字, 并且这个名字必须是符合上面层级关系的规范'bem'命名...否则疯狂报错.

@component hello {
  @descendent header {
    border: 1px solid #ccc;
  }
}

bug2解决
没找到解决方法

问题分析
这个插件想法很有趣, 但是弊端也挺多, 总体来说思想值得respect, 相应的问题如下:

  1. 无人维护无法放心使用.
  2. 限制太多反而限制了css代码的厉害之处.

不会为了使用新技术而使用新技术, 这个技术并不是那么好, 那我们索性就学习他的思想与玩法, 并不一定要使用它,要懂得取舍.

7: 1.toString() 报错是因为 1. 相当于 1.0 ?

保持好奇心
有些时候吧, 书上或者视频上也不一定全对

起因
我们常说像1这种数字不能直接.toString(), 因为他是基本值, 只有进行了包装类变成object才能进行'.'的操作, 比如new Number(1).toString(), 这类说法很主流, 但是接下来的一段代码怎么解释?

hq.png

问题猜想...
数字不可以直接用'.'来取属性是因为系统认为这个'.'是小数点, 而不是操作符,那么我们只要不让引擎以为我们写的是小数点就ok了, 下面的方法我来解释一下:

  1. 1.123.toString() 一个普通的数字只能有一个小数点, 那么第二个'.'也就会被解析为取属性.
  2. 如果我们明确告诉系统我们在取属性, console.log(1.123['toString']())那么系统也就会正确解析.

llc.png

未来展望
我写代码时间太短能力还不足, 期待明年的自己可以从v8引擎的角度来分析问题.

8: element-ui表格table的懒加载封装

bug描述
后端同学一下给我返回3万条列表数据, 导致页面'假死', 无奈的是产品不允许做成分页的.

bug分析
显而易见dom太多了, 但是element-ui本身没有这种table的懒加载, 他只有table内部树形结构的懒加载, 那么我们自己封装一个可以懒加载的表格吧, 恰好element-ui本身有个懒加载组件(不记得的同学可以撸撸官网):
bs.png

bug解决
单独封装懒加载的table, 利用一个可以无限滚动的外壳dom,每次触底帮助用户处理一下显示的数据, 这里我就展示一下大体的核心代码.
./components/ScrollTable.vue文件

<template>
<!-- 可变的都当做参数传进来就好了, 这里我只演示一下思想 -->
  <div
    class="scroll-box"
    ref="scrollBox"
    v-infinite-scroll="load"
    :infinite-scroll-delay="200"
    :infinite-scroll-distance="100"
  >
    <el-table border style="width: 300px" :data="list">
      <slot></slot>
    </el-table>
  </div>
</template>

<script>
export default {
  props:{
    reslist:Array
  },
  data() {
    return {
      list: [],
      count: 0 // 初始值可传
    };
  },
  methods: {
    load() {
      if (this.$refs.scrollBox) {
        this.count += 10; // 这个10也可以让用户自己传, 每次触底加载几条
        this.$refs.scrollBox.scrollTop -= 30;
        // 这里可以做一下已到底的处理
        this.list = this.reslist.slice(0, this.count);
      }
    }
  }
};
</script>

<style>
.scroll-box {
  overflow: auto;
  /* 这些当做参数传进来就好了 */
  width: 300px;
  height: 260px;
  padding-bottom: 20px;
}
</style>

使用:

<template>
  <div id="app">
    <el-scroll-table :reslist="list">
      <el-table-column type="index" width="50" label="序号" />
      <el-table-column prop="name" label="参数" />
    </el-scroll-table>
  </div>
</template>

<script>
import ElScrollTable from "./components/ScrollTable.vue";
export default {
  name: "App",
  components: {
    ElScrollTable
  },
  data() {
    return {
      list: []
    };
  },
  mounted(){
    for(let i=0;i<90;i++){
      this.list.push({
        name:'金毛'+ i
      })
    }
  }
};
</script>

问题回顾

  1. this.$refs.scrollBox.scrollTop -= 30这段莫名其妙的代码它解决了无限触底加载的bug, 因为每次触底之后虽然加载新数据, 但是滚动条位置还停留在最底部, 这样就会一直加载.
  2. 我们可以再加一段代码用来重置参数, 或者每次用户自己去出发重置方法, 或是监听用户传值的变化而重置.
  3. 这种方式比较适合后端把所有数据都给到我们, 比如我这次遇到的就是一次拿到3万条, 用户不可能每次下拉十条一直看一万条, 后端非要返给我那就我接到数据后进行了截断, 毕竟放内存里也是会占空间的.
  4. 使用起来很简单, 效果也还不错, 但是在pc端大部分还是用分页器做的, 这种需求挺少但不是没有, 完全体的组件时间关系我暂时没有去做, 有兴趣的同学根据自己的需求可以玩一玩.

9: vue项目icon错乱与引入scss文件失效

bug描述
突然有一天夜里同事发觉新上线的版本icon是错乱的, 只在少数电脑上可以复现, 并且icon并不是一开始就错乱而是正常显示2s后开始错乱.

bug解决
查了好多地方都没找到,因为他有2s钟是正常的, 这个现象影响了我的思路, 另一个同学发现打包出来的icon代码好像是混乱的, 那么我们就把vue里面的style标签上的lang="scss"去掉, 本来我不信但结果还真的好了....,罪魁祸首是dart-sass.

dart-sass是啥
引用官网的一段话:
Dart Sass 是 Sass 的主要实现版本,这意味着它集成新 功能要早于任何其它的实现版本。Dart Sass 速度快、易于安装,并且 可以被编译成纯 JavaScript 代码,这使得它很容易集成到现代 web 的开发流程中。, 比如我们平时下载node-sass会出现失败或者是缺少python2的报错, 而dart-sass就没有这样的问题, 并且速度很快.

bug2描述
我们之前引入的一个scss文件不生效了, 就是没有code格式报错,没有控制台报错, 就是文件里面的内容不生效了.

bug2解决
原来是由于我们取消了app.vue文件里面style的lang="scss", 再次引入scss文件并不会走scss解析器, 而这个scss文件里面的第一行我用// 这样的注释写明了这个文件的功能, 导致下面的代码全部失效, 解决方式就是把//更改为/**/下面的文件就ok了, 当然如果你下面的代码是scss规范书写的那也无法解析出来, 所以要谨慎使用dart-sass.
但我现在还是不明白为什么会有2s钟正常显示, 这个等我年底研究v8的时候再来从底层的角度搞定它.

end:

这次遇到的bug让我体会到我现在无法理解的问题不代表不会发生, 只是我能力还不够加油提高吧! 本次的分享就是这样,欢迎交流, 祝每天进步


lulu_up
5.7k 声望6.9k 粉丝

自信自律, 终身学习, 创业者