分类: js

有关 JavaScript 的技术文章和行业分析文章。

  • Nuxt.js 支持 core-js 3

    Nuxt.js 支持 core-js 3

    Vue CLI 升级到 v4 之后,将内部的 core-js 依赖升级到 v3,关于 core-js v3 和 core-js v2 之间的区别,我在 最近折腾 @babel/preset-env 的一些小心得 里简单介绍过。

    升级完 Vue CLI 之后,在调用 nuxt generate 生成静态页,就会报错,因为 Nuxt.js 默认使用 core-js 2。这个时候,如果 Nuxt.js 版本在 2.6.0 之后,就只需要修改 nuxt.config.js 里的配置,指定 core-js 的版本:

    module.exports = {
      build: {
        babel: {
          presets({ isServer }) {
            return [
              [
                require.resolve('@nuxt/babel-preset-app'),
                // require.resolve('@nuxt/babel-preset-app-edge'), // For nuxt-edge users
                {
                  buildTarget: isServer ? 'server' : 'client',
                  corejs: { version: 3 }
                }
              ]
            ]
          }
        }
      }
    }

    参考文档:https://nuxtjs.org/guide/release-notes#v2.6.0

    说起来 Nuxt.js,用它发布静态页比想象中复杂,如果你想快速掌握这个技能,不妨看下我的这本小书:

  • 解决 Firefox 下的 race 问题

    解决 Firefox 下的 race 问题

    我厂有几个产品,需要从后端获取大量的信息,为了让用户能够近乎实时的看到这些信息,大部分数据都是通过 WebSocket 发给前端。这些产品在 Chrome 下表现正常,但是在 Firefox 下经常把数据格式搞乱,最终渲染失败。

    因为 Firefox DevTools 没法解析 WebSocket 数据,而且市场占有率比较低,所以我一直没有解决这个问题。前几天终于把最小可复现实例搞出来,正准备研究,结果同事已经修好了。

    预览版的 Firefox 终于可以在 DevTools 里查看 WebSocket 每一帧的数据,所以她尝试看了一下,发现从解析二进制数据的角度来看,Firefox 应该没问题。于是又回到代码,发现了一个可能产生 race 的点:

    if (data instanceof Blob) {
      data = await new Response(data).arrayBuffer();
    }

    因为服务器返回的数据是二进制,所以我需要进行一次转换,把它变成 ArrayBuffer,然后再通过 TextDecoder 转换成文本,然后处理。Response.arrayBuffer 返回的是 Promise,所以我就很自然的用 await,并且在 Chrome 上运行良好。

    但是在 Firefox 里,某些帧会后发先完成转换,a b c 变成 a c b,于是数据格式错乱,无法正常解析。我怀疑 Chrome 并没有真的把这一步保留到用户,而是同时存了两份数据,这样转换的时候直接给出数据就好,所以是微任务,不走 Event loop,不会产生 race。而 Firefox 则是实时转换,所以是宏任务,所以出问题。

    我尝试去翻了一下源码,无奈平时没看过,所以没能找到证据。如果有哪位同学刚好知道,可以在评论里告诉我。

  • 在 Chart.js 的线图里增加竖线

    在 Chart.js 的线图里增加竖线

    我经常使用 Chart.js 生成图表,无它,名字好记+免费+持续更新。现在主要基于 Vue 开发项目,所以经常使用 vue-chart.js。前阵子遇到一个需求:

    1. 把一组数据画成线图
    2. 用户可以任意点击时间
    3. 用数据生成饼图

    那么就需要在线图中画一条竖线,标识出当前时间点。本以为这个需求不复杂,没想到不仅 Chart.js 不支持,包含这个功能的插件因为 Chart.js 升级的关系,暂时没法用。所以只好自己实现。

    (更多…)
  • 三道前端编程面试题

    三道前端编程面试题

    面试题要有区分度。不能太容易,让对方有屈辱感(“看不起人么?让我做这个?”);也不能太难,把所有候选人都干掉,对自己的时间也是一种浪费。其实挺难选的。

    很多大公司,因为买方市场,大把候选人排队等着挑,所以干脆把面试题弄得难一些,目的是筛选,只要好的,合适不合适另说。而一些小公司,比如我厂,得到优秀简历的机会本就不多,如果因为面试题设计不好,没法很好的考察候选人的水平,或者让候选人感到不舒服拒接 offer,都是损失。

    这里分享我近期总结的三道编程题,对应初中高三档候选人,我觉得很有区分度,大家也可以试试。

    (更多…)
  • 在 Node.js 12 中使用 ESM

    在 Node.js 12 中使用 ESM

    Node.js 12 之后开始支持 ECMAScript Modules(简称ESM),不过并不是默认开启或者自动切换。坦率地说我也卡了一阵子才搞清楚怎么直接使用。简单记一下吧。

    (更多…)
  • Intersection Observer 笔记

    Intersection Observer 笔记

    有时候我们需要根据一个元素的位置来修改它的属性,比如图片的 lazyload,比如视频离开视窗之后停止播放。

    以前的做法通常是:

    1. 侦听 window.scroll 事件
    2. scroll 触发,遍历每个要检查的 DOM Element,执行 .getBoundingClientRect() 取出它的 widthheighttopleft,然后根据 viewport 和宽高 scrollTop scrollLeft 计算对象是否应该出现
    3. 然后做处理

    这样做会产生一些问题:

    1. 如果漏掉移除侦听器,可能造成内存泄漏
    2. 不同组件之间,很难共享侦听器
    3. 每一次都计算所有 Element,成本很高

    于是现在我们有了 Intersection Observer,专门用来观察一个 Element 是否出现在 viewport 或者父容器里。它可以很好的解决这些问题:

    1. API 更清晰
    2. 逻辑原生,速度更快,消耗更少
    3. 可以自定义阈值,更加可控

    关于 Intersection Observer 的详细知识,建议大家认真阅读 MDN – Intersection Observer,我就不抄文档了。

    使用 Intersection Observer 大体上分为三步:

    1. 声明一个 Intersection Observer 实例,这一步最关键的是要确定显隐依据哪个元素,也就是 root
    2. 将需要检查的元素加入侦听队列
    3. 在回调函数里处理元素状态

    写成代码大概是这样的:

    // 声明一个实例
    // 因为我的视口即当前 viewport,所以这里不需要 `options`
    const observer = new IntersectionObserver(entries => {
      // 遍历所有实例,如果它显示出来,即 intersectionRatio 显示比例大于 0
      // 那么就让它 `dispatch('visible')`
      entries.forEach(({target, intersectionRatio}) => {
        const event = new CustomEvent('visible', {
          detail: {
            isVisible: intersectionRatio > 0,
          },
        });
        target.dispatchEvent(event);
      });
    });
    
    // 然后可以在 Vue 里侦听这个事件
    export default {
      template: '<div @visible="onVisible"></div>',
      mounted() {
        observer.observe(this.$el);
      },
      beforeDestroy() {
        observer.unobserve(this.$el);
      },
    }

    Intersection Observer API 公布一段时间之后,又进行了升级,现在支持更丰富的参数,比如我们可以定义一个函数去判断更复杂情况下的显示状态。不过大部分场景下,我们并不需要很精准的判断,所以我觉得这个只是保障选择的机会。


    参考阅读:

    Proposed Updates for Intersection Observer

  • Node.js 里使用 Promise 的小技巧

    Node.js 里使用 Promise 的小技巧

    Node.js 8 的时候,引入了 util.promisify() 方法,可以把 node-like 的回调函数改造成返回 Promise 实例的方法,我当时还写了篇博文《Node.js 8 中的 util.promisify》小记。

    所以我现在写 Node.js 基本都是这种风格:

    const fs = require('fs');
    const {promisify} = require('util');
    
    const readFile = promisify(fs.readFile);
    const writeFile = promisify(fs.writeFile);

    刚才在推上看到两位大佬聊起这个话题,发现可以这么搞:

    去查了一下 Node.js 的文档,发现这是 v10 新增的 API,升级之后即可使用。

    我比较喜欢这么做:

    const {promises: {readFile, writeFile}} = require('fs');
    
    (async () => {
      let content = await readFile('1.txt', 'utf8');
      content = doSthToContent(content);
      await writeFile('2.txt', content, 'utf8');
      console.log('ok');
    })();
  • 最近折腾 @babel/preset-env 的一些小心得

    最近折腾 @babel/preset-env 的一些小心得

    近来厂里的项目越来越多,代码共享必不可少。我现在采取的方案是:

    1. 把公共组件拿出来,开一个新仓库
    2. 使用 webpack 进行打包编译,libraryTarget: 'umd'
    3. 将打包编译的代码一起提交到仓库
    4. 使用 npm i <owner>/<repo> -S 安装依赖,因为我厂的仓库均为私有,所以不能发布到 NPM

    这套方案简单好用,实操效果良好。接下来我希望优化打包结果,于是研究了打包配置项,下面是我的一点心得。

    [2021-04-07] 更新:

    我们目前采用 GitHub Registry 托管私有 packages 的方案,比上面直接安装仓库的方案更好,想了解的同学可以看 使用 GitHub Registry 托管私有 NPM 源

    (更多…)
  • Chat:Vue 进阶必经之路

    Chat:Vue 进阶必经之路

    Vue 进阶必经之路

    Vue 的学习曲线平缓,入门很简单,所以后发夺人,争取到一大批支持者,我也是其中之一。但是因为隐蔽了很多细节,所以我也没少踩坑,挠头拍桌,不厌其烦。如今,我终于突破屏障,遇到大部分问题,都能举一反三,快速定位、快速解决。这段过程,难免追本溯源,穷究细节。我希望通过这次分享,把我的经验和收获传达给大家。

    面向听众:

    1. 有一定 Vue 开发经验,希望进一步提升;
    2. 平时业务代码写得多,想学习技术细节。

    写完了,不得不说,没有预先想象中写得好。原因嘛,当然是工作太忙,能够写文的时间不太够。以后还是平时多写,Chat 当汇总比较好。

    不过还是希望大家都来看,毕竟我这两年踩坑得到的 Vue 经验都在里面了。

    (更多…)
  • 用 `resize` 和 MutationObserver 实现缩放 DOM 并记录尺寸

    用 `resize` 和 MutationObserver 实现缩放 DOM 并记录尺寸

    我厂既然号称“机器编程”,DSL 便属于基础配置。于是,各个产品几乎都需要用到 CodeMirror,用于在线写代码。大家的屏幕分辨率不同,对编辑器的大小要求也不同,最简单的办法就是让大家自己调整,并且将尺寸保存在本地。

    以前的做法会复杂一些:

    1. 拿一个元素作为 handle 放在角落里
    2. mousedown 时开始侦听 mousemove,动态调整文本框大小
    3. mouseup 时保存尺寸

    如今大家应该都有注意到,<textarea> 右下角多了一个缩放用的 handle。这个功能自然不是 <textarea> 独享,而是受 CSS 样式 resize 控制,任何块级元素都适用。MDN 文档在此,建议大家先好好看一遍。

    (更多…)