第三期前端九条bug分享

8次阅读

共计 7304 个字符,预计需要花费 19 分钟才能阅读完成。

本期

因为需要写这方面的文章, 导致我现在工作中很期待有趣 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 出现了下面的错误:

点击这个 bug:

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

bug 定位

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

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

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

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')({}),
  ],
};

运行就报错:

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(), 这类说法很主流, 但是接下来的一段代码怎么解释?

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

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

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

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

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

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

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

正文完
 0