差不多先生

差不多先生 查看完整档案

北京编辑大连海事大学  |  软件工程 编辑求职  |  前端开发 编辑 weibo.com 编辑
编辑

自信、自洽、自在

微信公众号: 一步前端

个人动态

差不多先生 提出了问题 · 1月7日

eslint的extends和plugins的区别是什么?

eslint的extends和plugins的区别是什么?

关注 1 回答 0

差不多先生 关注了问题 · 2020-10-10

前端频繁请求接口

在做一个vue项目的时候发现一个问题,就是有一个根据input搜索框的值来查询数据的页面。
在快速点击搜索按钮2次或者多次之后。由于网咯的原因。可能导致最后一次点击查询按钮返回的值被前面的请求结果给覆盖了。。这种情况应该怎么解决呢。。只想保留最新一次发起的请求。

如果没有这个搜索按钮。只有一个input框(实时搜索)呢?即便是函数截流也会导致这个覆盖的问题(网络延迟)

关注 19 回答 14

差不多先生 赞了问题 · 2020-10-10

前端频繁请求接口

在做一个vue项目的时候发现一个问题,就是有一个根据input搜索框的值来查询数据的页面。
在快速点击搜索按钮2次或者多次之后。由于网咯的原因。可能导致最后一次点击查询按钮返回的值被前面的请求结果给覆盖了。。这种情况应该怎么解决呢。。只想保留最新一次发起的请求。

如果没有这个搜索按钮。只有一个input框(实时搜索)呢?即便是函数截流也会导致这个覆盖的问题(网络延迟)

关注 19 回答 14

差不多先生 赞了文章 · 2020-10-10

史上最清晰易懂的babel配置解析

标题党了哈哈哈~~~

原文地址

相信很多人和笔者从前一样,babel的配置都是从网上复制黏贴或者使用现成的脚手架,虽然这能够工作但还是希望大家能够知其所以然,因此本文将对babel(babel@7)的配置做一次较为完整的梳理。

语法和api

es6增加的内容可以分为语法和api两部分,搞清楚这点很重要,新语法比如箭头函数、解构等:

const fn = () => {}

const arr2 = [...arr1]

新的api比如Map、Promise等:

const m = new Map()

const p = new Promise(() => {})

@babel/core

@babel/core,看名字就知道这是babel的核心,没他不行,所以首先安装这个包

npm install @babel/core

它的作用就是根据我们的配置文件转换代码,配置文件通常为.babelrc(静态文件)或者babel.config.js(可编程),这里以.babelrc为例,在项目的根目录下创建一个空文件命名为.babelrc,然后创建一个js文件(test.js)测试用:

/* test.js */
const fn = () => {}

这里我们安装下@babel/cli以便能够在命令行使用babel

npm install @babel/cli

安装完成后执行babel编译,命令行输入

npx babel test.js --watch --out-file test-compiled.js

结果发现test-compiled.js的内容依然是es6的箭头函数,不用着急,我们的.babelrc还没有写配置呢

Plugins和Presets

Now, out of the box Babel doesn't do anything. It basically acts like const babel = code => code; by parsing the code and then generating the same code back out again. You will need to add plugins for Babel to do anything.

上面是babel官网的一段话,可以理解为babel是基于插件架构的,假如你什么插件也不提供,那么babel什么也不会做,即你输入什么输出的依然是什么。那么我们现在想要把剪头函数转换为es5函数只需要提供一个箭头函数插件就可以了:

/* .babelrc */
{
  "plugins": ["@babel/plugin-transform-arrow-functions"]    
}

转换后的test-compiled.js为:

/* test.js */
const fn = () => {}

/* test-compiled.js */
const fn = function () {}

那我想使用es6的解构语法怎么办?很简单,添加解构插件就行了:

/* .babelrc */
{
  "plugins": [
    "@babel/plugin-transform-arrow-functions",
    "@babel/plugin-transform-destructuring"
  ]    
}

问题是有那么多的语法需要转换,一个个的添加插件也太麻烦了,幸好babel提供了presets,他可以理解为插件的集合,省去了我们一个个引入插件的麻烦,官方提供了很多presets,比如preset-env(处理es6+规范语法的插件集合)、preset-stage(处理尚处在提案语法的插件集合)、preset-react(处理react语法的插件集合)等,这里我们主要介绍下preset-env

/* .babelrc */
{
  "presets": ["@babel/preset-env"]    
}

preset-env

@babel/preset-env is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s).

以上是babel官网对preset-env的介绍,大致意思是说preset-env可以让你使用es6的语法去写代码,并且只转换需要转换的代码。默认情况下preset-env什么都不需要配置,此时他转换所有es6+的代码,然而我们可以提供一个targets配置项指定运行环境:

/* .babelrc */
{
  "presets": [
    ["@babel/preset-env", {
      "targets": "ie >= 8"
    }]
  ]    
}

此时只有ie8以上版本浏览器不支持的语法才会被转换,查看我们的test-compiled.js文件发现一切都很好:

/* test.js */
const fn = () => {}
const arr1 = [1, 2, 3]
const arr2 = [...arr1]


/* test-compiled.js */
var fn = function fn() {};
var arr1 = [1, 2, 3];
var arr2 = [].concat(arr1);

@babel/polyfill

现在我们稍微改一下test.js:

/* test.js */
const fn = () => {}
new Promise(() => {})


/* test-compiled.js */
var fn = function fn() {};
new Promise(function () {});

我们发现Promise并没有被转换,什么!ie8还支持Promise?那是不可能的...。还记得本文开头提到es6+规范增加的内容包括新的语法和新的api,新增的语法是可以用babel来transform的,但是新的api只能被polyfill,因此需要我们安装@babel/polyfill,再简单的修改下test.js如下:

/* test.js */
import '@babel/polyfill'

const fn = () => {}
new Promise(() => {})


/* test-compiled.js */
import '@babel/polyfill';

var fn = function fn() {};
new Promise(function () {});

现在代码可以完美的运行在ie8的环境了,但是还存在一个问题:@babel/polyfill这个包的体积太大了,我们只需要Promise就够了,假如能够按需polyfill就好了。真巧,preset-env刚好提供了这个功能:

/* .babelrc */
{
  "presets": [
    ["@babel/preset-env", {
      "modules": false,
      "useBuiltIns": "entry",
      "targets": "ie >= 8"
    }]
  ]    
}

我们只需给preset-env添加一个useBuiltIns配置项即可,值可以是entryusage,假如是entry,会在入口处把所有ie8以上浏览器不支持api的polyfill引入进来,如下:

/* test.js */
import '@babel/polyfill'

const fn = () => {}
new Promise(() => {})


/* test-compiled.js */
import "core-js/modules/es6.array.copy-within";
import "core-js/modules/es6.array.every";
import "core-js/modules/es6.array.fill";
...   //省略若干引入
import "core-js/modules/web.immediate";
import "core-js/modules/web.dom.iterable";
import "regenerator-runtime/runtime";

var fn = function fn() {};
new Promise(function () {});

细心的你会发现transform后,import '@babel/polyfill'消失了,反倒是多了一堆import 'core-js/...'的内容,事实上,@babel/polyfill这个包本身是没有内容的,它依赖于core-jsregenerator-runtime这两个包,这两个包提供了es6+规范的运行时环境。因此当我们不需要按需polyfill时直接引入@babel-polyfill就行了,它会把core-jsregenerator-runtime全部导入,当我们需要按需polyfill时只需配置下useBuiltIns就行了,它会根据目标环境自动按需引入core-jsregenerator-runtime

前面还提到useBuiltIns的值还可以是usage,其功能更为强大,它会扫描你的代码,只有你的代码用到了哪个新的api,它才会引入相应的polyfill:

/* .babelrc */
{
  "presets": [
    ["@babel/preset-env", {
      "modules": false,
      "useBuiltIns": "usage",
      "targets": "ie >= 8"
    }]
  ]    
}

transform后的test-compiled.js相应的会简化很多:

/* test.js */
const fn = () => {}
new Promise(() => {})

/* test-compiled.js */
import "core-js/modules/es6.promise";
import "core-js/modules/es6.object.to-string";

var fn = function fn() {};
new Promise(function () {});

遗憾的是这个功能还处于试验状态,谨慎使用。

事实上假如你是在写一个app的话,以上关于babel的配置差不多已经够了,你可能需要添加一些特定用途的PluginPreset,比如react项目你需要在presets添加@babel/preset-react,假如你想使用动态导入功能你需要在plugins添加@babel/plugin-syntax-dynamic-import等等,这些不在赘述。假如你是在写一个公共的库或者框架,下面提到的点可能还需要你注意下。

@babel/runtime

有时候语法的转换相对复杂,可能需要一些helper函数,如转换es6的class:

/* test.js */
class Test {}


/* test-compiled.js */
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Test = function Test() {
  _classCallCheck(this, Test);
};

示例中es6的class需要一个_classCallCheck辅助函数,试想假如我们多个文件中都用到了es6的class,那么每个文件都需要定义一遍_classCallCheck函数,这也是一笔不小的浪费,假如将这些helper函数抽离到一个包中,由所有的文件共同引用则可以减少可观的代码量。而@babel/runtime做的正好是这件事,它提供了各种各样的helper函数,但是我们如何知道该引入哪一个helper函数呢?总不能自己手动引入吧,事实上babel提供了一个@babel/plugin-transform-runtime插件帮我们自动引入helper。我们首先安装@babel/runtime@babel/plugin-transform-runtime

npm install @babel/runtime @babel/plugin-transform-runtime

然后修改babel配置如下:

/* .babelrc */
{
  "presets": [
    ["@babel/preset-env", {
      "modules": false,
      "useBuiltIns": "usage",
      "targets": "ie >= 8"
    }]
  ],
  "plugins": [
    "@babel/plugin-transform-runtime"
  ]  
}

现在我们再来看test-compiled.js文件,里面的_classCallCheck辅助函数已经是从@babel/runtime引入的了:

/* test.js */
class Test {}


/* test-compiled.js */
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";

var Test = function Test() {
  _classCallCheck(this, Test);
};

看到这里你可能会说,这不扯淡嘛!几个helper函数能为我减少多少体积,我才懒得安装插件。事实上@babel/plugin-transform-runtime还有一个更重要的功能,它可以为你的代码创建一个sandboxed environment(沙箱环境),这在你编写一些类库等公共代码的时候尤其重要。

上文我们提到,对于Promise、Map等这些es6+规范的api我们是通过提供polyfill兼容低版本浏览器的,这样做会有一个副作用就是污染了全局变量,假如你是在写一个app还好,但如果你是在写一个公共的类库可能会导致一些问题,你的类库可能会把一些全局的api覆盖掉。幸好@babel/plugin-transform-runtime给我们提供了一个配置项corejs,它可以将这些变量隔离在局部作用域中:

/* .babelrc */

{
  "presets": [
    ["@babel/preset-env", {
      "modules": false,
      "targets": "ie >= 8"
    }]
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "corejs": 2
    }]
  ]  
}

注意:这里一定要配置corejs,同时安装@babel/runtime-corejs2,不配置的情况下@babel/plugin-transform-runtime默认是不引入这些polyfill的helper的。corejs的值现阶段一般指定为2,可以近似理解为是@babel/runtime的版本。我们现在再来看下test-compiled.js被转换成了什么:

/* test.js */
class Test {}
new Promise(() => {})


/* test-compiled.js */
import _Promise from "@babel/runtime-corejs2/core-js/promise";
import _classCallCheck from "@babel/runtime-corejs2/helpers/classCallCheck";

var Test = function Test() {
  _classCallCheck(this, Test);
};

new _Promise(function () {});

如我们所愿,已经为Promise的polyfill创建了一个沙箱环境。

最后我们再为test.js稍微添加点内容:

/*  test.js */
class Test {}
new Promise(() => {})

const b = [1, 2, 3].includes(1)


/* test-compiled.js */
import _Promise from "@babel/runtime-corejs2/core-js/promise";
import _classCallCheck from "@babel/runtime-corejs2/helpers/classCallCheck";

var Test = function Test() {
  _classCallCheck(this, Test);
};

new _Promise(function () {});
var b = [1, 2, 3].includes(1);

可以发现,includes方法并没有引入辅助函数,可这明明也是es6里面的api啊。这是因为includes是数组的实例方法,要想polyfill必须修改Array的原型,这样一来就污染了全局环境,因此@babel/plugin-transform-runtime是处理不了这些es6+规范的实例方法的。

tips

以上基本是本文的全部内容了,最后再来个总结和需要注意的地方:

  1. 本文没有提到preset-stage,事实上babel@7已经不推荐使用它了,假如你需要使用尚在提案的语法,请直接添加相应的plugin。
  2. 对于普通项目,可以直接使用preset-env配置polyfill
  3. 对于类库项目,推荐使用@babel/runtime,需要注意一些实例方法的使用
  4. 本文内容是基于babel@7,项目中遇到问题可以尝试更新下babel-loader的版本

...待补充


全文完

查看原文

赞 26 收藏 17 评论 2

差不多先生 赞了文章 · 2020-09-17

仅限100 人!思否编程邀你成为「内容合伙人」

招募前言

SegmentFault 思否是国内领先的新一代开发者社区和技术媒体。也是中国领先的黑客马拉松组织者,目前已近覆盖和服务了上千万中国软件开发者和IT信息从业者。

思否编程依托 SegmentFault 思否在开发者行业的多年积淀重点发力IT新职业在线教育,以权威技术专家+优质教学服务打造行业领先课程,帮助更多人走进技术的世界。

现在思否编程公开招募「内容合伙人」,来「思否编程输出课程,与同行思维碰撞,带领开发者一起成长。

成为思否编程讲师你可以收获什么?

1,业内最专业的一对一的讲师辅导,无论你是行业大咖,还是素人小白,只要你技艺在身,辅导完下一个KOL就是你。

2,超精准的超前沿的后期制作,360度全方位制作,,绝对是是整条街最Liang的课程

3,一份持续的可观的收入,要想早日实现财富自由,睡后收入绝对不能少

4,思否编程认证讲师,你与日活几百万的平台还差一个证书

5,思否媒体专访机会(专属福利),听说被思否专访过的人都升职加薪啦

成为思否编程讲师应具备什么?

1,热爱分享,愿意帮助更多的开发者,思否社区高声望值用户,知名技术博主/图书作者

2,擅长某一技术方向感,一线研发/研发管理经验5年以上,具备突出项目经验者可适当放宽条件

3,每周2-3小时的空闲时间

4,如您此前有过授课经验,可在申请时提供相关课程链接

Q&A

Q:我没有高大上的学历和大厂背景,可以申请吗?

A:英雄不问出处,只要你有吸引人的且深度的内容,就大胆的报名吧。

Q:我没有讲过课可以报名吗?

A:如果你在某项技能上有自己的独到见解,热爱分享,就快来报名吧,我们有热门的选题,专业的课程策划团队,优质的课程后期,助你完成人生中的第一门课程。

Q:怎么录制呢,需要真人出镜吗?

A:远程在家录制就可以,不需要真人出镜。

Q:录用后的流程是什么样的呢?

A:策划选题、策划大纲、提交试录、签约录制、课程上线。

扫描下方二维码即可申请:

image

此招募贴长期有效,如需咨询,请添加小姐姐,暗号“讲师报名”

image

查看原文

赞 12 收藏 1 评论 10

差不多先生 收藏了文章 · 2020-09-07

构建自己的知识体系

差不多先生 收藏了文章 · 2020-09-07

构建自己的知识体系

差不多先生 发布了文章 · 2020-09-07

构建自己的知识体系

赞 5 收藏 4 评论 0

差不多先生 赞了文章 · 2020-09-04

JavaScript · 异步之async-await(摘)

写在前面

本文内容及代码均摘取出自一歩的相关教程。
本文仅作为个人笔记练习代码使用(所有的代码都需要自己手动敲上几遍),内容上也精简了很多。
相关知识还请阅读原文:异步神器 async-await

async-await
  • async-await是promise和generator的语法糖;
  • async返回的是一个promise对象,即可以使用then添加回调;
  • await只能在async的上下文中使用,不然会报错;
  • await可以等待两种返回值,promise或其他:

    • promise:造成异步函数停止执行,并等待promise的resolve;
    • 其他:立即执行
  • async-await的错误处理(try-catch);
  • async-await的并行处理(Promise.all);

上述总结具体代码演示如下:

// async返回promise;
async function demo1() {
    var num = Math.random();
    return num; // 相当于Promise.resolve(num);
}
demo().then(value => {
    alert(value);
})
// await只能在async上下文中使用
function notAsyncFunc() {
    return await Math.random();
}
notAsyncFunc();
// Uncaught SyntaxError: await is only valid in async function 

async function demo2() {
    for(i=0;i<5;i++) {
        await console.log(Math.random());
    }
}
demo2(); // 可以正常执行,打印5个随机数

async function errorDemo2() {
    for(i=0;i<5;i++) {
        setTimeout(()=>{await console.log('test2')}))
    }
}
errorDemo2();
// Uncaught SyntaxError: await is only valid in async function
// await的两种等待对象;
function promiseObj() {
    return new Promise((resolve,reject) => {
        setTimeout(resolve,1000,'promise!')
    })
}
function otherObj() {
    setTimeout(()=>console.log('otherObj!'),2000)
    // 或者是一个表达式等任何非promise对象
}

async function awaitDemo() {
    await otherObj();
    console.log('other!');
    let result = await promiseObj();
    console.log(result);
    console.log('promiseObj!')
}
awaitDemo();
// other! 立即执行
// promise! 1s后执行
// promiseObj! 紧跟着上条执行
// otherObj! 2s后执行
// 异步等待的错误处理;
function sleep(second) {
    return new Promise((resolve,reject) => {
        setTimeout(() => {
            reject('want to sleep~');
        },second)
    })
}

async function errorDemo() {
    let result = await sleep(2000);
    console.log(result);
    // Uncaught (in promise) want to sleep~
}

async function correctDemo() {
    try {
        let result = await sleep(2000);
        console.log(result);
    } catch(err) {
        console.log(err);
        // want to sleep~
    }
}
// 异步等待的并行处理;
function sleep(second) {
    return new Promise((resolve,reject) => {
        setTimeout(() => {
            resolve('done!' + Math.random());
        },second);
    })
}

async function bugDemo() {
    await sleep(1000);
    await sleep(2000);
    await sleep(3000);
    console.log('clear the loading~');
}

async function correctDemo() {
    let p1 = sleep(1000);
    let p2 = sleep(2000);
    let p3 = sleep(3000);
    await Promise.all([p1,p2,p3]);
    console.log('clear the loading~');
}

bugDemo(); // 6s后输出 clear the loading~
correctDemo(); // 3s后输出 clear the loading~
promise 和 async-await的关系
  • async-await只是promise的语法糖;
  • async-await简化了promise的实现;
// 需求:以请求1的结果作为参数,发出请求2
function request(second,param) {
    return new Promise((resolve,reject) => {
        setTimeout(resolve,second,param)
    })
}
// promise实现
function promiseFunc() {
    let result1,result2,result3;
    request(2000,'req01').then(res => {
        result1 = res;
        return request(1000,'req02' + res)
    })
    .then(res => {
        result2 = res;
        return request(500,'req03' + res);
    })
    .then(res => {
        result3 = res;
    })
    .finally(() => {
        console.log(`
            ${result1}
            ${result2}
            ${result3}
        `)
    })
}
// async-await实现
async function asyncFunc() {
    let result1 = await request(2000,'req01');
    let result2 = await request(1000,'req02' + result1);
    let result3 = await request(500,'req03' + result2);
    console.log(`
        ${result1}
        ${result2}
        ${result3}
    `);
}
  • async-await的本质上还是promise;
// 见上面Promise.all那个例子
查看原文

赞 2 收藏 1 评论 0

差不多先生 关注了专栏 · 2020-08-07

终身学习者

我要先坚持分享20年,大家来一起见证吧。

关注 48718

认证与成就

  • SegmentFault 讲师
  • 获得 1181 次点赞
  • 获得 68 枚徽章 获得 2 枚金徽章, 获得 23 枚银徽章, 获得 43 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2015-03-29
个人主页被 14k 人浏览