分类: js

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

  • Vue 3.0-beta.1 发布!

    Vue 3.0-beta.1 发布!

    经历一年多的推倒重来反复打磨,在众多开发者的千呼万唤之下,Vue 开发团队终于在今天发布了 3.0-beta.1 版本,也就是测试版。通常来说,从测试版到正式版,只会修复 bug,不会引入新功能,或者删改老功能。所以,如果你对新版本非常感兴趣,或者有新项目即将上马,不妨尝试一下新版本。

    按照官方路线图,原计划 Q1 末发布测试版,Q2 发布正式版。目前看来稍微有些延期,不过不多。相信正式版会很快到来,非常期待呀。

    在我看来,Vue 已经是一份杰作,而 3.0 的变化则让它更加优秀。

    起初,Vue 开创性的使用 Object.defineProperty 改写对象属性赋值运算,隐性收集依赖,把前端开发的难度降低了一个维度。从此,我们不需要考虑怎么绑定数据,怎么更新视图,只需要简简单单修改变量的值,界面就会自动变化,如魔法一般。

    如今,Vue 3.0 更进一步,使用 Proxy 拦截赋值操作,不仅实现了同样的功能,还大幅降低系统消耗、减少计算时间。——更加值得期待的是,因为新 API 更强大,之前困扰众多开发者的“修改数据,界面不更新”问题,应该会变得极为罕见。

    这会令 Vue 的入门门槛变得更低。在我看来,这件事善莫大焉。写代码并不仅仅是写代码,它是这个世界上成本最低的“创造型”工作。其它创造型工作,比如雕塑、美术,除了复杂的基础教育,还需要昂贵的生产成本。编程不需要,只要你有一台电脑,坐下来就可以写,想怎么写就怎么写,想写什么就写什么。而前端,又是其中成本最低的,你甚至不需要搭建开发环境,拿个记事本就能写,写完放到浏览器里就能跑。

    从这个角度出发,每次在知乎看到“我出身不好,能不能学编程”,或者“我学历不好,能不能学编程”,我都会建议他们学。因为学会编程,不仅仅是掌握一门手艺,可能养家糊口,更是给自己的未来增加了一种可行性。学会编程,你就可以把手伸到我这种专业程序员伸不到的地方,帮助自己、帮助他人、甚至帮助世界,同时也能给自己换来各种各样的回报。

    另外,新增的 Composition API——不管是一些人口中的“学 React hooks”,还是 Vue 作者尤雨溪说的“这不是 hooks,这比 hooks 强太多了”——大大增强了代码的可复用性,能很好的改进代码架构,为更大的项目、更强的架构,带来了更多的可能。

    如果说 jQuery 让更多的人可以学会开发,那么 Vue 就让更多的人可以开发中大型软件。它会给整个行业带来的巨大帮助,让更多的产品可以有更好的用户体验,让更多的产品可以迁移、升级到新平台、新架构。

    这个影响是潜移默化的,就像当年 Flash Player 集成 H.264 视频解码器一样,虽然很多人没注意,但是视频网站的春天就是那个时候到来的。如今,有了更好的基础设施,哪个新产品会崛起,我无法判断,但我觉得,对技术来说,更好的一天已经开始了。

    (更多…)
  • 使用 JS 模拟元素被 click

    使用 JS 模拟元素被 click

    需求来自于我厂的 QA 产品。在这个产品中,我需要在浏览器插件里模拟用的各种行为,比如:点击。

    click 事件前后发生了什么

    最初我觉得点击嘛,能有啥问题,就直接广播 click 事件呗。结果发现并非如此。实际上,一次 click 背后,其实有一大套的逻辑:

    1. 移动到按钮上时,会依次触发 mouseovermouseenter 事件,前者冒泡,后者不冒泡
    2. 鼠标按下时,广播 mousedown 事件
    3. 如果此时其它元素有焦点,那么该元素会先失去焦点,并广播 blur 事件
    4. (下一节补充)
    5. 按钮获得焦点,广播 focus 事件
    6. 如果因此影响到 DOM,那么会等待 DOM 变更
    7. 如果鼠标没有离开按钮,按钮广播 mouseup 事件
    8. 最后广播 click 事件
    9. 在移动设备上,可能会有 300ms 延迟

    其实(5)并不准确,每一次事件广播都是独立的 event loop ,所以上面每一步都可能产生 DOM 变化和其它次生变化,也可能导致一些操作或功能不符合预期。比如:

    1. 一个搜索框。输入后自动搜索,结果以 dropdown 形式展示在下面
    2. 点击 dropdown 里的条目可以跳转
    3. 搜索框 blur 时 dropdown 移除

    此时,dropdown 里的条目可能无法点击。因为点击时,输入框先 blur,之后 dropdown 隐藏,接下来 mouseup 事件触发在别的元素上,于是不会有 click 事件。

    最简单的解决方案,给 blur 事件加延迟,10ms 就够。

    (关于上面的事件触发顺序,可以用这个的 codepen 尝试)

    click 事件对 change 事件的影响

    接下来,回到 QA 产品。

    我真正踩的坑,是 change 事件没有按照预期触发。大家知道,Vue 组件通过 $emit('input') 可以更新绑定在 v-model 里的值。触发的时机一般通过侦听 DOM input 或者 change 来决定。如果 change 无法正确触发,那么 QA 产品自然而然就无法正常工作。

    实际上,在上一节的 click 逻辑中,第(4)步可以展开为:

    如果失去焦点的元素是 <input> 或者 <textarea>,则该元素会广播 change 事件。

    模拟 click 动作的代码

    最后,演示一下最终代码:

    const options = {
      bubbles: true,
      cancelable: true,
      view: window,
    };
    let event = new MouseEvent('mousedown', options);
    elem.dispatchEvent(event);
    elem.focus();
    event = new MouseEvent('mouseup', options);
    elem.dispatchEvent(event);
    elem.click();

    相关链接

  • 几步简单的操作解决 `npm audit` vulnerabilities

    几步简单的操作解决 `npm audit` vulnerabilities

    NPM 是 JavaScript 生态最重要的组成部分,我们的项目中会大量使用 NPM 安装第三方包(安装后就称为“项目依赖”)解决问题。这些第三方包也会带来他们的依赖,最终一个项目里可能安装成百上千个依赖。

    有道是:没有完美的代码,代码里一定藏有隐患。所以用的依赖多了,其中有问题的概率也提升了,三五不时的,npm 就会提示我们:found N low/medium/high severity vulnerabilities

    npm 提供命令 npm audit fix,理论上可以修复这些隐患,但在实际操作中,以我的经验来看,并不容易生效。我猜测可能是因为依赖间的复杂关系,想彻底解决并升级不太容易。所以我一般是这样做的:

    (更多…)
  • Vue 2020 年路线图,Vue 3.0 计划于 Q2 发布

    Vue 2020 年路线图,Vue 3.0 计划于 Q2 发布

    昨天 Vue 团队更新了 2020 年的路线图,里面包含了很多 Vue 3.0 的信息。建议大家一定要看原文,地址在:https://github.com/vuejs/vue/projects/6。下面我结合自己的理解翻译一下:

    FAQ

    问:3.0 啥时候能好?

    答:请往后看。另外请注意,这些日期仅供参考,我们团队的首要目标是发布生产级别的高质量代码,不是赶 deadline。

    问:3.0 里都有啥变化啊?

    答:请自行翻阅最新的 RFC。另外,也要注意核心团队提交的 RFC 草案。

    如果某个 RFC 里包含破坏性变更,那么里面一定会有“升级策略”章节,讨论迁移问题。

    对于现在的 2.x 用户,我们会提供:

    • 迁移向导
    • 能够兼容 2.x 的兼容性版本(如果能兼容的话),并且对该升级的地方给出提示和升级建议
    • 命令行迁移工具
      • 自动升级能升级的代码
      • 不能自动升级的,扫描出来提示手动升级

    问:我是新人,我现在该学 Vue 2.0 还是等 3.0?

    答:如果你刚刚开始学习框架,那么应该开始使用 Vue 2。我们没有对 Vue 3 进行巨大的重新设计,所以大部分 Vue 2 知识仍将适用于 Vue 3。 如果你打算学习框架,没有必要等待。

    如果你要为某个生产级别的项目选择技术栈:

    • 如果项目需要立即动工:我们仍然建议使用 Vue 2,以便获得完整的、框架级别的支持。 但是,也请留心 3.0 中即将发生的更改,不要使用将被移除的功能,最好也不要使用与 Vue 2 深度耦合的第三方依赖。
    • 如果项目可以等到 Q2 末:那我们建议你等等,用 3.0。

    问:以后 2.x 会咋样呢?

    答:接下来会有一个小版本(2.7)更新:

    • 将兼容的 3.x 功能反向移植回 2.x
    • 对 3.x 弃用的功能发出警告

    这是 2.x 最后一个小版本,并提供长达 18 个月的 LTS(长期支持)。即使在 LTS 结束之后,我们也会继续提供重要的安全更新。

    问:Vuex 方面有什么计划么?

    答:一方面,我们正在开发Vuex(4.0)版本,其 API与 当前版本(3.0)完全相同,但与 Vue 3 兼容。我们力求向下兼容,让用户可以在 Vue 3 项目中继续使用现有 Vuex 代码。

    另一方面,我们也在尝试新的设计,更多的利用 Vue 3 的响应式 API,也让 Vuex API 不那么冗长。 这个新版本暂定为“ vuex-next”,也就是 5.0。 眼下,我们只是在进行早期探索,最早也要到 2020 年第三季度才会发布。

    2020 一季度计划

    • 3.0 SSR
    • 3.0 迁移
      • 升级向导(施工中)
      • 2.x 兼容版本
      • 迁移工具
    • 3.0 框架
      • router(施工中)
      • Vuex(施工中)
      • 测试工具(施工中)
      • JSX babel 插件(施工中)(我以为不会有这个东西了呢)
      • CLI
      • Devtools
      • 其它(虽然最后三个没标施工中,不过我觉得多半也是在施工中咯)
    • 3.0 beta
      • Q1 末发布!
      • 3.0 核心现在其实已经完成了,我们希望 API 到这个时候也能稳定下来。
      • 我们还需要更多的时间才能更新周边的库和工具。 如果您的使用场景对 router 和 vuex 没有硬性要求,这个时候就可以开始使用 3.0 了,但最好是非关键性应用程序。

    2020 二季度计划

    • 继续之前未完成的 3.0 框架工作
    • 季度中,发布 3.0 RC
      • 冻结 API,不再有重大变化。进入 RC 之前,所有涉及到重大变更的 RFC 都要定案。
      • 全家桶能够和 3.0 版本协同工作。
      • 3.0 版本就绪,此阶段基本可用。仍然会有一些小错误和框架集成问题,在 RC 阶段都会慢慢被解决掉。
    • 3.0 发布管理
      • 回归测试
      • 自动化每晚发布
      • 正式确定版本生命周期
    • 3.0 IE11 兼容性版本
    • 3.0 官方正式版

    2020 三季度计划

    发布 2.7 版本

    • 反相迁移 3.x 功能到 2.x
    • 对 3.x 中弃用的功能发出警告
    • 2.x 最后的小版本,LTS
  • 解决“[ERR_PACKAGE_PATH_NOT_EXPORTED]: No “exports” main resolved”

    解决“[ERR_PACKAGE_PATH_NOT_EXPORTED]: No “exports” main resolved”

    周末例行升级系统,今天打开项目,npm run dev,就报这个错误。检查代码,没变化,依赖也没变化。因为错误位置在 main.js,尝试给它加上 exports,无果。

    Google 之,发现一个非常新的 issue:https://github.com/babel/babel/issues/11216,3天前,来自 @babel/babel 仓库,多半是了。

    点进去一看,原来 node.js 从 13.10.1 之后,对 package.json 里的 exports 属性解读出现问题,继而导致 Babel 抛出错误。最简单的解决方法就是升级 Babel 到 7.8.4。

    升级后问题解决。

  • 使用 Proxy 创建有魔术属性/方法的类

    使用 Proxy 创建有魔术属性/方法的类

    前几天在 SF 回答了这个问题:如何使用proxy,如何在内部拦截get方法,然后翻了翻以前写的博客:使用 Proxy 添加魔术属性/方法,发现上次写完代理对象就停笔了,所以今天补全一下:创建有魔术属性/方法的类。这样就比较完整了。

    JavaScript 构造函数的特点

    ES6 增加了 class 关键字,梳理了面向对象的语法,现在我们可以这样定义一个类:

    class Person {
      constructor(name) {
        this.name = name; 
      }
    
      hello() {
        return `Hello, my name is ${this.name}.`;
      }
    }

    如果你有其它面向对象语言的经验,应该很容易理解这段代码。

    不过 JS 的构造函数特别,它支持 return 一个其它对象,作为 new SomeClass() 的结果。(如果不 return 或者 return 一个空对象,那么 new SomeClass() 得到的就是 SomeClass 的实例。)

    也就是说,原则上,我们可以这么做:

    class Person {
      constructor(name) {
        this.name = name;
        return {name: 'Meathill'};
      }
    }
    
    const person = new Person('张三');
    console.log(person.name); // 'Meathill'

    结合 Proxy

    理解了上一节的内容,我们就很容易得到这样一个类:

    class Person {
      constructor(name) {
        this.name = name;
    
        return new Proxy(this, {
          get(target, property) {
            if (property in target) {
              return target[property];
            }
            console.warn('Sorry, I can't do that.');
          }
        }
      }
    }

    在这个类的构造函数里,我返回了一个 Proxy 实例,代理了对真正 Person 实例的访问。当访问的属性/方法在实例上时,就返回需要的属性/方法,否则的话,输出警告。

    实际上,Proxy 的 getset 的功能远不止如此,上面的代码只是一些演示。

    用途

    魔法属性/方法主要有以下用途:

    1. 对象 a 要使用一部分对象 b 的功能,但是又不方便直接用原型链。比如上一篇文章的场景,我提供类 VElement 作为接口,实际完成工作的是另一个沙箱中的 Element。
    2. 不知道会怎么访问对象,希望所有访问都照顾到。
    3. 希望捕获到对对象的修改,也就是 Vue 3.0 的核心修改。

    Vue 3.0

    Vue 1.x & 2.x 期间,都在使用 ES5 的 Object.defineProperty 拦截对对象的修改,实现响应式。这样的做法看起来很神奇,给 Vue 带来了巨大的成功。但是这样做也有坏处:

    1. 声明实例时需要很多预处理工作,而且数据量越大处理的时间越久
    2. 不支持某些数组操作
    3. 不支持其它数据类型,比如 Set、Map
    4. 不支持后续的数据观察

    使用 Proxy 之后,以上问题全部都迎刃而解,甚至,因为 Proxy 是原生 API,性能表现更好,取得了内存减半、速度加倍的效果。

    想了解更多 Vue 3.0 的新特性,可以去看我在 SF 的分享:迎接 Vue 3.0。(注:这是免费广告,我不会从新购用户取得收益。)


    参考文章:

    Constructor, operator “new”(构造函数和操作符 “new”)

  • 迎接 Vue 3.0

    迎接 Vue 3.0

    思否(SegmentFault)去年引入了新的运营合伙人,大张旗鼓,计划重新梳理视频教程体系。第一个举措就是推出新系列:思否编程公开课。这个系列的优势在于结合社区运营的知识与一线讲师的实战能力:即社区寻找大家关心的话题,请相关领域的讲师来讲解,达到受众明确、内容扎实的目标。

    很高兴能够得到邀请,做了一期《迎接 Vue 3.0》,我也趁机深入了解了一把 Vue 3.0。

    Vue 3.0 原计划 2019 年发布,但是开发过程有些波折,一些早期的设计被放弃了,因为不够好;引入了新的更好的设计,于是延期至今,仍然是 alpha 版。在新的设计下,Vue 3.0 更快,快到不需要时间切片;而且还能基本保持与 2.x 版本的兼容。

    至于新增的 Compisition API,其实是选修课,类似 Async 之于 Promise。有了它,我们能更好的复用代码、维护代码;不用它,也不耽误我们享受其它升级带来的便利。

    其实看过很多 Vue 3.0 的东西之后,我越发感激开发团队为这个框架做得一切,越发感激开源文化为我们带来的新世界。

    我备课一向认真充分,所以讲课的内容应该是现在最新最全面的,欢迎大家围观:【思否编程公开课】迎接Vue 3.0

    (更多…)
  • 利用 Web Speech API 实现语音阅读试题(TTS)

    利用 Web Speech API 实现语音阅读试题(TTS)

    孩子上小学一年级,寒假作业里有一项:每天做20个20以内的加减法。这个作业老师不直接布置,而是让家长负责,方式任意。那么,显而易见,这个工作就由我负责。然后我就顺手抄起 Vue 做了一个 Web App:http://mui.evereditor.com,源代码在:https://github.com/meathill/mui-teacher

    然后老婆说,别的教学应用都会把题目念出来,这样比较有上课的感觉,问我能不能也把题目念出来。我之前大略有些印象,可以直接调用浏览器的原生 API 实现 TTS(text to speech),于是就想试试看吧。

    使用“Web speech API”作为关键词,很容易找到 MDN 上的这个页面,继而了解到 SpeechSynthesisUtterance 这个类,接下来就简单了,直接参考 Using the Web Speech API 里的 Demo,就完成了下面代码:

    doRead(index) {
      const content = this.$refs.line[index].textContent;
      const msg = new SpeechSynthesisUtterance(content.replace('-', '减'));
      speechSynthesis.speak(msg);
    },

    这里有三个注意事项:

    1. 系统会把 - 读成“至”,但我这里是加减的“减”,所以我要手动把它替换一下
    2. 从某个版本开始,发音必须由用户主动触发,即放在交互性事件里,不能在页面打开时自动读
    3. 发音效果跟浏览器有关,目前 Chrome 和 Safari 效果比较好

    这样的 TTS,对于整段文字来说,效果一般,但是读一般的算式足够了,小朋友也挺喜欢。这个 API 还可以用作语音输入,不过考虑到模型的效果,没有尝试,想真正可用的话,还是用那些高级的在线版本吧。


    图文无关,想出去玩……

  • 解决新机不能 npm install 私有仓库的问题

    解决新机不能 npm install 私有仓库的问题

    前些天换了电脑,从 iMac 27 Retina 2014 late 升级到 iMac 27 2019。关于前因后果,我写在张大妈,感兴趣的同学可以看下:《肉山的电子产品 篇九:机不如新,人不如故——iMac 27 换新记》

    换机器要搭环境。有两个选择:

    1. 整盘复制,利用 Time Machine 之类的工具
    2. 自己慢慢搭

    考虑到老电脑已经工作了 5 年,经历了小十个系统版本,各种软件删装无数,里面的垃圾很多;再加上我的环境并不复杂,所以我决定自己慢慢搭。

    然后遇到一个问题:npm install 会卡住,没有明显的提示信息,只有一段比较可疑:

    The authenticity of host 'GitHub.com (1.1.1.1)' can't be established.
    ECDSA key fingerprint is SHA256:abcdefg.
    Are you sure you want to continue connecting (yes/no)? 

    因为项目中用到一些私有仓库的依赖,所以我怀疑是它们的问题。于是我把私有仓库从 package.json 里移除,再安装,成功。看来猜的没错。

    然后我手动 git clone 一个项目到本地,clone 的过程中,会看到上面的提示,手动输入 yes,记录目标机器的指纹,视它安全可靠。clone 完成。再把私有仓库依赖加回 package.json,安装,成功。问题解决。

    原因:使用 npm 安装 GitHub 仓库作为依赖,实际上等同于使用 git clone 将目标仓库 clone 到 node_modules 里。而 git 协议依托于 ssh 协议。在新机器上安装时,因为没有访问过目标机器,本地不信任目标机器,而 npm 又没有对这个异常做处理,所以就会卡住,不上不下。

  • 基于 @vue/cli 的项目配置 browserslist

    基于 @vue/cli 的项目配置 browserslist

    前些日子虽然写了 最近折腾 @babel/preset-env 的一些小心得,但其实没有正确的理解和配置 browserslist,所以今天问题又来了。

    (更多…)