webpack 已采纳 的版本太高_webpack版本与webpack-dev-server版本 - CSDN
精华内容
参与话题
  • 带你用合理的姿势使用webpack4(下)

    千次阅读 2018-10-12 10:30:35
    推荐先阅读 webpack 入门教程之后再来阅读本文。 Webpack 4 和单页应用入门 手摸手,带你用合理的姿势使用 webpack4 (上) 本文为手摸手使用 webpack4(下),主要分为两部分: 怎么合理的运用浏览器缓存 怎么...

    原文https://segmentfault.com/a/1190000015919928

    推荐先阅读 webpack 入门教程之后再来阅读本文。

    本文为手摸手使用 webpack4(下),主要分为两部分:

    • 怎么合理的运用浏览器缓存
    • 怎么构建可靠的持久化缓存

    默认分包策略

    webpack 4 最大的改动就是废除了 CommonsChunkPlugin 引入了 optimization.splitChunks

    webpack 4 的Code Splitting 它最大的特点就是配置简单,如果你的 mode 是 production,那么 webpack 4 就会自动开启 Code Splitting

    以下内容都会以 vue-element-admin 为例子。 在线
    bundle-report

    如上图所示,在没配置任何东西的情况下,webpack 4 就智能的帮你做了代码分包。入口文件依赖的文件都被打包进了app.js,那些大于 30kb 的第三方包,如:echartsxlsxdropzone等都被单独打包成了一个个独立 bundle。

    它内置的代码分割策略是这样的:

    • 新的 chunk 是否被共享或者是来自 node_modules 的模块
    • 新的 chunk 体积在压缩之前是否大于 30kb
    • 按需加载 chunk 的并发请求数量小于等于 5 个
    • 页面初始加载时的并发请求数量小于等于 3 个

    但有一些小的组件,如上图:vue-count-to 在未压缩的情况下只有 5kb,虽然它被两个页面共用了,但 webpack 4 默认的情况下还是会将它和那些懒加载的页面代码打包到一起,并不会单独将它拆成一个独立的 bundle。(虽然被共用了,但因为体积没有大于 30kb)

    你可能会觉得 webpack 默认策略是不是有问题,我一个组件被多个页面,你每个页面都将这个组件打包进去了,岂不是会重复打包很多次这个组件?就拿vue-count-to来举例,你可以把共用两次以上的组件或者代码单独抽出来打包成一个 bundle,但你不要忘了vue-count-to未压缩的情况下就只有 5kb,gizp 压缩完可能只有 1.5kb 左右,你为了共用这 1.5kb 的代码,却要额外花费一次 http 请求的时间损耗,得不偿失。我个人认为 webpack 目前默认的打包规则是一个比较合理的策略了。

    但有些场景下这些规则可能就显得不怎么合理了。比如我有一个管理后台,它大部分的页面都是表单和 Table,我使用了一个第三方 table 组件,几乎后台每个页面都需要它,但它的体积也就 15kb,不具备单独拆包的标准,它就这样被打包到每个页面的 bundle 中了,这就很浪费资源了。这种情况下建议把大部分页面能共用的组件单独抽出来,合并成一个component-vendor.js的包(后面会介绍)。

    优化没有银弹,不同的业务,优化的侧重点是不同的。个人认为 webpack 4 默认拆包已经做得不错了,对于大部分简单的应用来说已经够用了。但作为一个通用打包工具,它是不可能满足所有的业务形态和场景的,所以接下来就需要我们自己稍微做一些优化了。

    优化分包策略

    就拿 vue-element-admin 来说,它是一个基于 Element-UI 的管理后台,所以它会用到如 echartsxlsxdropzone等各种第三方插件,同时又由于是管理后台,所以本身自己也会写很多共用组件,比如各种封装好的搜索查询组件,共用的业务模块等等,如果按照默认的拆包规则,结果就不怎么完美了。

    如第一张图所示,由于element-uientry入口文件中被引入并且被大量页面共用,所以它默认会被打包到 app.js 之中。这样做是不合理的,因为app.js里还含有你的router 路由声明store 全局状态utils 公共函数icons 图标等等这些全局共用的东西。

    但除了element-ui,其它这些又是平时开发中经常会修改的东西,比如我新增了一个全局功能函数,utils文件就会发生改变,或者我修改一个路由的 path,router文件就变了,这些都会导致app.js的 hash 发生改变:app.1.js => app.2.js。但由于 element-ui和 vue/react等也被打包在其中,虽然你没改变它们,但它们的缓存也会随着app.xxx.js变化而失效了,这就非常不合理的。所以我们需要自己来优化一下缓存策略。

    我们现在的策略是按照体积大小、共用率、更新频率重新划分我们的包,使其尽可能的利用浏览器缓存。

    我们根据上表来重新划分我们的代码就变成了这样。

    • 基础类库 chunk-libs

    它是构成我们项目必不可少的一些基础类库,比如 vue+vue-router+vuex+axios 这种标准的全家桶,它们的升级频率都不高,但每个页面都需要它们。(一些全局被共用的,体积不大的第三方库也可以放在其中:比如 nprogress、js-cookie、clipboard 等)

    • UI 组件库

    理论上 UI 组件库也可以放入 libs 中,但这里单独拿出来的原因是: 它实在是比较大,不管是 Element-UI还是Ant Design gizp 压缩完都可能要 200kb 左右,它可能比 libs 里面所有的库加起来还要大不少,而且 UI 组件库的更新频率也相对的比 libs 要更高一点。我们不时的会升级 UI 组件库来解决一些现有的 bugs 或使用它的一些新功能。所以建议将 UI 组件库也单独拆成一个包。

    • 自定义组件/函数 chunk-commons

    这里的 commons 主要分为 必要非必要

    必要组件是指那些项目里必须加载它们才能正常运行的组件或者函数。比如你的路由表、全局 state、全局侧边栏/Header/Footer 等组件、自定义 Svg 图标等等。这些其实就是你在入口文件中依赖的东西,它们都会默认打包到app.js中。

    非必要组件是指被大部分页面使用,但在入口文件 entry 中未被引入的模块。比如:一个管理后台,你封装了很多 select 或者 table 组件,由于它们的体积不会很大,它们都会被默认打包到到每一个懒加载页面的 chunk 中,这样会造成不少的浪费。你有十个页面引用了它,就会包重复打包十次。所以应该将那些被大量共用的组件单独打包成chunk-commons

    不过还是要结合具体情况来看。一般情况下,你也可以将那些非必要组件函数也在入口文件 entry 中引入,和必要组件函数一同打包到app.js之中也是没什么问题的。

    • 低频组件

    低频组件和上面的共用组件 chunk-commons 最大的区别是,它们只会在一些特定业务场景下使用,比如富文本编辑器、js-xlsx前端 excel 处理库等。一般这些库都是第三方的且大于 30kb,所以 webpack 4 会默认打包成一个独立的 bundle。也无需特别处理。小于 30kb 的情况下会被打包到具体使用它的页面 bundle 中。

    • 业务代码

    这部分就是我们平时经常写的业务代码。一般都是按照页面的划分来打包,比如在 vue 中,使用路由懒加载的方式加载页面 component: () => import('./Foo.vue') webpack 默认会将它打包成一个独立的 bundle。

    完整配置代码:

    splitChunks: {
      chunks: "all",
      cacheGroups: {
        libs: {
          name: "chunk-libs",
          test: /[\\/]node_modules[\\/]/,
          priority: 10,
          chunks: "initial" // 只打包初始时依赖的第三方
        },
        elementUI: {
          name: "chunk-elementUI", // 单独将 elementUI 拆包
          priority: 20, // 权重要大于 libs 和 app 不然会被打包进 libs 或者 app
          test: /[\\/]node_modules[\\/]element-ui[\\/]/
        },
        commons: {
          name: "chunk-commons",
          test: resolve("src/components"), // 可自定义拓展你的规则
          minChunks: 2, // 最小共用次数
          priority: 5,
          reuseExistingChunk: true
        }
      }
    };

    上图就是最终拆包结果概要,你可以 点我点我点我,在线查看拆包结果。

    这样就能尽可能的利用了浏览器缓存。当然这种优化还是需要因项目而异的。比如上图中的共用组件 chunk-commons,可能打包出来发现特别大,包含了很多组件,但又不是每一个页面或者大部分页面需要它。很可能出现这种状况:A 页面只需要 chunk-commons里面的 A 组件,
    但却要下载整个chunk-commons.js,这时候就需要考虑一下,目前的拆包策略是否合理,是否还需要chunk-commons?还是将这些组件打包到各自的 bundle 中?还是调整一下 minChunks: 2( 最小共用次数)?或者修改一下它的拆包规则?

    // 或者你可以把策略改为只提取那些你注册在全局的组件。
    
    - test: resolve("src/components")
    + test: resolve("src/components/global_components") //你注册全局组件的目录

    博弈

    其实优化就是一个博弈的过程,是让 a bundle 大一点还是 b? 是让首次加载快一点还是让 cache 的利用率高一点? 但有一点要切记,拆包的时候不要过分的追求颗粒化,什么都单独的打成一个 bundle,不然你一个页面可能需要加载十几个.js文件,如果你还不是HTTP/2的情况下,请求的阻塞还是很明显的(受限于浏览器并发请求数)。所以还是那句话资源的加载策略并没什么完全的方案,都需要结合自己的项目找到最合适的拆包策略。

    比如支持HTTP/2的情况下,你可以使用 webpack4.15.0 新增的 maxSize,它能将你的chunkminSize的范围内更加合理的拆分,这样可以更好地利用HTTP/2来进行长缓存(在HTTP/2的情况下,缓存策略就和之前又不太一样了)。

    Long term caching

    持久化缓存其实是一个老生常谈的问题,前端发展到现在,缓存方案已经很成熟了。简单原理:

    • 针对 html 文件:不开启缓存,把 html 放到自己的服务器上,关闭服务器的缓存
    • 针对静态的 js,css,图片等文件:开启 cdn 和缓存,将静态资源上传到 cdn 服务商,我们可以对资源开启长期缓存,因为每个资源的路径都是独一无二的,所以不会导致资源被覆盖,保证线上用户访问的稳定性。
    • 每次发布更新的时候,先将静态资源(js, css, img) 传到 cdn 服务上,然后再上传 html 文件,这样既保证了老用户能否正常访问,又能让新用户看到新的页面。

    相关文章 大公司里怎样开发和部署前端代码?

    所以我们现在要做的就是要让 webpack 给静态资源生产一个可靠的 hash,让它能自动在合适的时候更新资源的 hash,
    并且保证 hash 值的唯一性,即为每个打包后的资源生成一个独一无二的 hash 值,只要打包内容不一样,那么 hash 值就不一样。

    其实 webpack 4 在持久化缓存这一块已经做得非常的不错了,但还是有一些欠缺,下面我们将要从这几个方面讨论这个问题。

    • RuntimeChunk(manifest)
    • Module vs Chunk
    • HashedModuleIdsPlugin
    • NamedChunksPlugin

    RuntimeChunk(manifest)

    webpack 4 提供了 runtimeChunk 能让我们方便的提取 manifest,以前我们需要这样配置

    new webpack.optimize.CommonsChunkPlugin({
      name: "manifest",
      minChunks: Infinity
    });

    现在只要一行配置就可以了

    {
      runtimeChunk: true;
    }

    它的作用是将包含chunks 映射关系的 list单独从 app.js里提取出来,因为每一个 chunk 的 id 基本都是基于内容 hash 出来的,所以你每次改动都会影响它,如果不将它提取出来的话,等于app.js每次都会改变。缓存就失效了。

    单独抽离 runtimeChunk 之后,每次打包都会生成一个runtimeChunk.xxx.js。(默认叫这名字,可自行修改)

    优化

    其实我们发现打包生成的 runtime.js非常的小,gzip 之后一般只有几 kb,但这个文件又经常会改变,我们每次都需要重新请求它,它的 http 耗时远大于它的执行时间了,所以建议不要将它单独拆包,而是将它内联到我们的 index.html 之中(index.html 本来每次打包都会变)。

    这里我选用了 script-ext-html-webpack-plugin,主要是因为它还支持preload和 prefetch,正好需要就不想再多引用一个插件了,你完全可以使用 inline-manifest-webpack-plugin或者 assets-webpack-plugin等来实现相同的效果。

    const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");
    
    // 注意一定要在HtmlWebpackPlugin之后引用
    // inline 的name 和你 runtimeChunk 的 name保持一致
    new ScriptExtHtmlWebpackPlugin({
      //`runtime` must same as runtimeChunk name. default is `runtime`
      inline: /runtime\..*\.js$/
    });

    Module vs Chunk

    我们经常看到xxxModuleIdsPluginxxxChunksPlugin,所以在 webpack 中 module和 chunk到底是一个怎么样的关系呢?

    • chunk: 是指代码中引用的文件(如:js、css、图片等)会根据配置合并为一个或多个包,我们称一个包为 chunk。
    • module: 是指将代码按照功能拆分,分解成离散功能块。拆分后的代码块就叫做 module。可以简单的理解为一个 export/import 就是一个 module。

    每个 chunk 包可含多个 module。 比如:

    //9.xxxxxxxxx.js
    
    //chunk id为 9 ,包含了Vc2m和JFUb两个module
    (window.webpackJsonp = window.webpackJsonp || []).push([
      [9],
      {
        Vc2m: function(e, t, l) {},
        JFUb: function(e, t, l) {}
      }
    ]);

    一个module还能跨chunk引用另一个module,比如我想在app.js里面需要引用 chunkId13的模块2700可以这样引用:

    return n.e(13).then(n.bind(null, "27OO"));

    HashedModuleIdsPlugin

    了解了 modulechunk之后,我们来研究一下 moduleId

    首先要确定你的 filename 配置的是chunkhash(它与 hash 的区别可以看上篇文章)。

    output: {
      path: path.join(__dirname, 'dist'),
      filename: '[name].[chunkhash].js',
    }

    我们在入口文件中随便引入一个新文件test.js

    //main.js
    import "./test";
    
    //test.js
    console.log("apple");

    我们运行npm run build,发现了一件奇怪的事情,我只是多引入了一个文件,但发现有十几个文件发生了变化。这是为什么呢?

    我们随便挑一个文件 diff 一下,发现两个文件只有 module id 的不同。

    这是因为:
    webpack 内部维护了一个自增的 id,每个 module 都有一个 id。所以当增加或者删除 module 的时候,id 就会变化,导致其它文件虽然没有变化,但由于 id 被强占,只能自增或者自减,导致整个 id 的顺序都错乱了。

    虽然我们使用 [chunkhash] 作为输出名,但仍然是不够的。
    因为 chunk 内部的每个 module 都有一个 id,webpack 默认使用递增的数字作为 moduleId
    如果引入了一个新文件或删掉一个文件,都可能会导致其它文件的 moduleId 发生改变,
    那这样缓存失效了。如:

    本来是一个按序的 moduleId list,这时候我插入一个orange模块,插在第三个位置,这样就会导致它之后的所以 module id 都依次加了 1。

    这到了原因,解决方案就很简单了。我们就不要使用一个自增的 id 就好了,这里我们使用HashedModuleIdsPlugin

    或者使用optimization.moduleIds v4.16.0 新发布,文档还没有。查看 源码发现它有naturalnamedhashedsizetotal-size。这里我们设置为optimization.moduleIds='hash'等于HashedModuleIdsPlugin。源码了也写了webpack5会优化这部分代码。

    它的原理是使用文件路径的作为 id,并将它 hash 之后作为 moduleId。

    使用了 HashedModuleIdsPlugin`,我们再对比一下发现 module id 不再是简单的 id 了,而是一个四位 hash 过得字符串(不一定都是四位的,如果重复的情况下会增加位数,保证唯一性 源码)。
    这样就固定住了 module id 了。

    NamedModulesPlugin 和 HashedModuleIdsPlugin 原理是相同的,将文件路径作为 id,只不过没有把路径 hash 而已,适用于开发环境方便调试。不建议在生产环境配置,因为这样不仅会增加文件的大小(路径一般偶读比较长),更重要的是为暴露你的文件路径。

    NamedChunkPlugin

    我们在固定了 module id 之后同理也需要固定一下 chunk id,不然我们增加 chunk 或者减少 chunk 的时候会和 module id 一样,都可能会导致 chunk 的顺序发生错乱,从而让 chunk 的缓存都失效。

    作者也意识到了这个问题,提供了一个叫NamedChunkPlugin的插件,但在使用路由懒加载的情况下,你会发现NamedChunkPlugin并没什么用。
    供了一个线上demo,可以自行测一下。这里提就直接贴一下结果:

    产生的原因前面也讲了,使用自增 id 的情况下是不能保证你新添加或删除 chunk 的位置的,一旦它改变了,这个顺序就错乱了,就需要重排,就会导致它之后的所有 id 都发生改变了。

    接着我们 查看源码 还发现它只对有 name 的 chunk 才奏效!所以我们那些异步懒加载的页面都是无效的。这启不是坑爹!我们迭代业务肯定会不断的添加删除页面,这岂不是每新增一个页面都会让之前的缓存都失效?那我们之前还费这么大力优化什么拆包呢?

    其实这是一个古老的问题了 相关 issue: Vendor chunkhash changes when app code changes 早在 2015 年就有人提了这个问题,这个问题也一直讨论至今,'网友们'也提供了各种奇淫巧技,不过大部分随着 webpack 的迭代已经不适用或者是修复了。

    这里我就结合一下 timse(webpack 第二多贡献)写的持久缓存的文章(在 medium 上需要翻墙)
    总结一下目前能解决这个问题的三种方案。

    目前解决方案有三种

    • records
    • webpackChunkName
    • 自定义 nameResolver

    webpack records

    很多人可能连这个配置项都没有注意过,不过早在 2015 年就已经被设计出来让你更好的利用 cache。官方文档

    要使用它配置也很简单:

    recordsPath: path.join(__dirname, "records.json");

    对,只要这一行代码就能开启这个选项,并打包的时候会自动生成一个 JSON 文件。它含有 webpack 的 records 记录 - 即「用于存储跨多次构建(across multiple builds)的模块标识符」的数据片段。可以使用此文件来跟踪在每次构建之间的模块变化。

    大白话就是:等于每次构建都是基于上次构建的基础上进行的。它会先读取你上次的 chunk 和 module id 的信息之后再进行打包。所以这时候你再添加或者删除 chunk,并不会导致之前所说的乱序了。

    简单看一下构建出来的 JSON 长啥样。

    {
      "modules": {
        "byIdentifier": {
          "demo/vendor.js": 0,
          "demo/vendor-two.js": 1,
          "demo/index.js": 2,
          ....
        },
        "usedIds": {
          "0": 0,
          "1": 1,
          "2": 2,
          ...
        }
      },
      "chunks": {
        "byName": {
          "vendor-two": 0,
          "vendor": 1,
          "entry": 2,
          "runtime": 3
        },
        "byBlocks": {},
        "usedIds": [
          0,
          1,
          2
      }
    }

    我们和之前一样,在路由里面添加一个懒加载的页面,打包对比后发现 id 并不会像之前那样按照遍历到的顺序插入了,而是基于之前的 id 依次累加了。一般新增页面都会在末尾填写一个新 id,删除 chunk 的话,会将原来代表 chunk 的 id,保留,但不会再使用。

    但这个方案不被大家知晓主要原因就是维护这个records.json比较麻烦。如果你是在本地打包运行webpack的话,你只要将records.json当做普通文件上传到githubgitlab或其它版本控制仓库。

    但现在一般公司都会将打包放在 CI里面,用docker打包,这时候这份records.json存在哪里就是一个问题了。它不仅需要每次打包之前先读取你这份 json,打包完之后它还需要再更新这份 json,并且还要找地方存贮,为了下次构建再使用。你可以存在 git 中或者找一个服务器存,但存在什么地其它方都感觉怪怪的。

    如果你使用 Circle CI可以使用它的store_artifacts,相关教程

    本人在使用了之后还是放弃了这个方案,使用成本略高。前端打包应该更加的纯粹,不需要依赖太多其它的东西。

    webpackChunkName

    在 webpack2.4.0 版本之后可以自定义异步 chunk 的名字了,例如:

    import(/* webpackChunkName: "my-chunk-name" */ "module");

    我们在结合 vue 的懒加载可以这样写。

    {
        path: '/test',
        component: () => import(/* webpackChunkName: "test" */ '@/views/test')
      },

    打包之后就生成了名为 test的 chunk 文件。

    chunk 有了 name 之后就可以解决NamedChunksPlugin没有 name 的情况下的 bug 了。查看打包后的代码我们发现 chunkId 就不再是一个简单的自增 id 了。

    不过这种写法还是有弊端的,首先你需要手动编写每一个 chunk 的 name,同时还需要保证它的唯一性,当页面一多,维护起来还是很麻烦的。这就违背了程序员的原则:能偷懒就偷懒。

    所以有什么办法可以自动生成一个 name 给 chunk 么 ?查看 webpack 源码我们发现了NamedChunksPlugin其实可以自定义 nameResolver 的。

    自定义 nameResolver

    NamedChunksPlugin支持自己写 nameResolver 的规则的。但目前大部分相关的文章里的自定义函数是不适合 webpack4 ,而且在结合 vue 的情况下还会报错。

    社区旧方案:

    new webpack.NamedChunksPlugin(chunk => {
      if (chunk.name) {
        return chunk.name;
      }
      return chunk.modules.map(m => path.relative(m.context, m.request)).join("_");
    });

    适配 webpack4 和 vue 的新实现方案:

    new webpack.NamedChunksPlugin(chunk => {
      if (chunk.name) {
        return chunk.name;
      }
      return Array.from(chunk.modulesIterable, m => m.id).join("_");
    });

    当然这个方案还是有一些弊端的因为 id 会可能很长,如果一个 chunk 依赖了很多个 module 的话,id 可能有几十位,所以我们还需要缩短一下它的长度。我们首先将拼接起来的 id hash 以下,而且要保证 hash 的结果位数也能太长,浪费字节,但太短又容易发生碰撞,所以最后我们我们选择 4 位长度,并且手动用 Set 做一下碰撞校验,发生碰撞的情况下位数加 1,直到碰撞为止。详细代码如下:

    const seen = new Set();
    const nameLength = 4;
    
    new webpack.NamedChunksPlugin(chunk => {
      if (chunk.name) {
        return chunk.name;
      }
      const modules = Array.from(chunk.modulesIterable);
      if (modules.length > 1) {
        const hash = require("hash-sum");
        const joinedHash = hash(modules.map(m => m.id).join("_"));
        let len = nameLength;
        while (seen.has(joinedHash.substr(0, len))) len++;
        seen.add(joinedHash.substr(0, len));
        return `chunk-${joinedHash.substr(0, len)}`;
      } else {
        return modules[0].id;
      }
    });

    我给 vue-cli 官方也提了一个相关
    issue尤雨溪最后也采纳了这个方案。
    所以如果你现在下载最新 vue-cli@3上面啰嗦了半天的东西,其实都已经默认配置好了(但作者本人为了找到这个 hack 方法整整花了两天时间 o(╥﹏╥)o)。

    目前测试了一段时间没发现有什么问题。不过有一点不是很理解,不知道 webpack 出于什么样的原因,官方一直没有修复这个问题?可能是在等 webpack5 的时候放大招吧。

    总结

    拆包策略:

    • 基础类库 chunk-libs
    • UI 组件库 chunk-elementUI
    • 自定义共用组件/函数 chunk-commons
    • 低频组件 chunk-eachrts/chunk-xlsx
    • 业务代码 lazy-loading xxxx.js

    持久化缓存:

    • 使用 runtimeChunk 提取 manifest,使用 script-ext-html-webpack-plugin等插件内联到index.html减少请求
    • 使用 HashedModuleIdsPlugin 固定 moduleId
    • 使用 NamedChunkPlugin结合自定义 nameResolver 来固定 chunkId

    上述说的问题大部分在 webpack 官方文档都没明确指出,唯一可以参考的就是这份 cache 文档,在刚更新 webpack4 的时候,我以为官方已经将 id 不能固定的问题解决了,但现实是残酷的,结果并不理想。不过作者也在很多的 issue 中说他正在着手优化 long term caching

    We plan to add another way to assign module/chunk ids for long term caching, but this is not ready to be told yet.

    在 webpack 的 issue 和源码中也经常见到 Long term caching will be improved in webpack@5TODO webpack 5 xxxx这样的代码注释。这让我对webpack 5很期待。真心希望webpack 5能真正的解决前面几个问题,并且让它更加的out-of-the-box,更加的简单和智能,就像webpack 4optimization.splitChunks,你基本不用做什么,它就能很好的帮你拆分好bundle包,同时又给你非常的自由发挥空间。

    展望

    Whats next? 官方在这篇文章中展望了一下 webpack5 和讲述了一下未来的计划--持续改进用户体验、提升构建速度和性能,降低使用门槛,完善Persistent Caching等等。同时 webpack 也已经支持 Prefetching/Preloading modules,我相信之后也会有更多的网站会使用这一属性。

    同时 webpack 的团队已经承诺会通过投票的方式来决定一些功能。比如不久前发起的投票。

    大家可以关注 Tobias Koppers 的 twitter 进行投票。

    最后还是期待一下 webpack5 和它之后的发展吧。如果没有 webpack,也就不会有今天的前端。

    其实如一开始就讲的,vue 有vue-cli、react 有creat-react-app,现在新建项目基本都是基于脚手架的,很少有人从零开始写 webpack 配置文件的,而且一般开发中,一般程序员也不需要经常去修改 webpack 的配置。webpack 官方本身也在不断完善默认配置项,相信 webpack 的配置门槛也会越来低多。

    展开全文
  • (点击上方公众号,可快速关注)作者:华尔街见闻技术团队 - 花裤衩segmentfault.com/a/1190000015919928推荐先阅读 webpack 入门教...
        

    (点击上方公众号,可快速关注)

    作者:华尔街见闻技术团队 - 花裤衩

    segmentfault.com/a/1190000015919928


    推荐先阅读 webpack 入门教程之后再来阅读本文。



    本文为手摸手使用 webpack4(下),主要分为两部分:


    • 怎么合理的运用浏览器缓存

    • 怎么构建可靠的持久化缓存


    默认分包策略


    webpack 4 最大的改动就是废除了 CommonsChunkPlugin 引入了 optimization.splitChunks。


    webpack 4 的Code Splitting 它最大的特点就是配置简单,如果你的 mode 是 production,那么 webpack 4 就会自动开启 Code Splitting。


    640?wx_fmt=jpeg


    以下内容都会以 vue-element-admin 为例子。 在线

    bundle-report


    如上图所示,在没配置任何东西的情况下,webpack 4 就智能的帮你做了代码分包。入口文件依赖的文件都被打包进了app.js,那些大于 30kb 的第三方包,如:echarts、xlsx、dropzone等都被单独打包成了一个个独立 bundle。


    它内置的代码分割策略是这样的:


    • 新的 chunk 是否被共享或者是来自 node_modules 的模块

    • 新的 chunk 体积在压缩之前是否大于 30kb

    • 按需加载 chunk 的并发请求数量小于等于 5 个

    • 页面初始加载时的并发请求数量小于等于 3 个


    640?wx_fmt=jpeg


    但有一些小的组件,如上图:vue-count-to 在未压缩的情况下只有 5kb,虽然它被两个页面共用了,但 webpack 4 默认的情况下还是会将它和那些懒加载的页面代码打包到一起,并不会单独将它拆成一个独立的 bundle。(虽然被共用了,但因为体积没有大于 30kb)


    你可能会觉得 webpack 默认策略是不是有问题,我一个组件被多个页面,你每个页面都将这个组件打包进去了,岂不是会重复打包很多次这个组件?就拿vue-count-to来举例,你可以把共用两次以上的组件或者代码单独抽出来打包成一个 bundle,但你不要忘了vue-count-to未压缩的情况下就只有 5kb,gizp 压缩完可能只有 1.5kb 左右,你为了共用这 1.5kb 的代码,却要额外花费一次 http 请求的时间损耗,得不偿失。我个人认为 webpack 目前默认的打包规则是一个比较合理的策略了。


    但有些场景下这些规则可能就显得不怎么合理了。比如我有一个管理后台,它大部分的页面都是表单和 Table,我使用了一个第三方 table 组件,几乎后台每个页面都需要它,但它的体积也就 15kb,不具备单独拆包的标准,它就这样被打包到每个页面的 bundle 中了,这就很浪费资源了。这种情况下建议把大部分页面能共用的组件单独抽出来,合并成一个component-vendor.js的包(后面会介绍)。


    优化没有银弹,不同的业务,优化的侧重点是不同的。个人认为 webpack 4 默认拆包已经做得不错了,对于大部分简单的应用来说已经够用了。但作为一个通用打包工具,它是不可能满足所有的业务形态和场景的,所以接下来就需要我们自己稍微做一些优化了。


    优化分包策略


    就拿 vue-element-admin 来说,它是一个基于 Element-UI 的管理后台,所以它会用到如 echarts、xlsx、dropzone等各种第三方插件,同时又由于是管理后台,所以本身自己也会写很多共用组件,比如各种封装好的搜索查询组件,共用的业务模块等等,如果按照默认的拆包规则,结果就不怎么完美了。


    如第一张图所示,由于element-ui在entry入口文件中被引入并且被大量页面共用,所以它默认会被打包到 app.js 之中。这样做是不合理的,因为app.js里还含有你的router 路由声明、store 全局状态、utils 公共函数,icons 图标等等这些全局共用的东西。


    但除了element-ui,其它这些又是平时开发中经常会修改的东西,比如我新增了一个全局功能函数,utils文件就会发生改变,或者我修改一个路由的 path,router文件就变了,这些都会导致app.js的 hash 发生改变:app.1.js => app.2.js。但由于 element-ui和 vue/react等也被打包在其中,虽然你没改变它们,但它们的缓存也会随着app.xxx.js变化而失效了,这就非常不合理的。所以我们需要自己来优化一下缓存策略。


    我们现在的策略是按照体积大小、共用率、更新频率重新划分我们的包,使其尽可能的利用浏览器缓存。


    640?wx_fmt=jpeg


    我们根据上表来重新划分我们的代码就变成了这样。


    • 基础类库 chunk-libs


    它是构成我们项目必不可少的一些基础类库,比如 vue+vue-router+vuex+axios 这种标准的全家桶,它们的升级频率都不高,但每个页面都需要它们。(一些全局被共用的,体积不大的第三方库也可以放在其中:比如 nprogress、js-cookie、clipboard 等)


    • UI 组件库


    理论上 UI 组件库也可以放入 libs 中,但这里单独拿出来的原因是: 它实在是比较大,不管是 Element-UI还是Ant Design gizp 压缩完都可能要 200kb 左右,它可能比 libs 里面所有的库加起来还要大不少,而且 UI 组件库的更新频率也相对的比 libs 要更高一点。我们不时的会升级 UI 组件库来解决一些现有的 bugs 或使用它的一些新功能。所以建议将 UI 组件库也单独拆成一个包。


    • 自定义组件/函数 chunk-commons


    这里的 commons 主要分为 必要和非必要。


    必要组件是指那些项目里必须加载它们才能正常运行的组件或者函数。比如你的路由表、全局 state、全局侧边栏/Header/Footer 等组件、自定义 Svg 图标等等。这些其实就是你在入口文件中依赖的东西,它们都会默认打包到app.js中。


    非必要组件是指被大部分页面使用,但在入口文件 entry 中未被引入的模块。比如:一个管理后台,你封装了很多 select 或者 table 组件,由于它们的体积不会很大,它们都会被默认打包到到每一个懒加载页面的 chunk 中,这样会造成不少的浪费。你有十个页面引用了它,就会包重复打包十次。所以应该将那些被大量共用的组件单独打包成chunk-commons。


    不过还是要结合具体情况来看。一般情况下,你也可以将那些非必要组件函数也在入口文件 entry 中引入,和必要组件函数一同打包到app.js之中也是没什么问题的。


    • 低频组件


    低频组件和上面的共用组件 chunk-commons 最大的区别是,它们只会在一些特定业务场景下使用,比如富文本编辑器、js-xlsx前端 excel 处理库等。一般这些库都是第三方的且大于 30kb,所以 webpack 4 会默认打包成一个独立的 bundle。也无需特别处理。小于 30kb 的情况下会被打包到具体使用它的页面 bundle 中。


    • 业务代码


    这部分就是我们平时经常写的业务代码。一般都是按照页面的划分来打包,比如在 vue 中,使用路由懒加载的方式加载页面 component: () => import('./Foo.vue') webpack 默认会将它打包成一个独立的 bundle。


    完整配置代码:


    splitChunks: {

      chunks: "all",

      cacheGroups: {

        libs: {

          name: "chunk-libs",

          test: /[\/]node_modules[\/]/,

          priority: 10,

          chunks: "initial" // 只打包初始时依赖的第三方

        },

        elementUI: {

          name: "chunk-elementUI", // 单独将 elementUI 拆包

          priority: 20, // 权重要大于 libs 和 app 不然会被打包进 libs 或者 app

          test: /[\/]node_modules[\/]element-ui[\/]/

        },

        commons: {

          name: "chunk-comomns",

          test: resolve("src/components"), // 可自定义拓展你的规则

          minChunks: 2, // 最小共用次数

          priority: 5,

          reuseExistingChunk: true

        }

      }

    };


    640?wx_fmt=jpeg


    上图就是最终拆包结果概要,你可以 点我点我点我,在线查看拆包结果。


    这样就能尽可能的利用了浏览器缓存。当然这种优化还是需要因项目而异的。比如上图中的共用组件 chunk-commons,可能打包出来发现特别大,包含了很多组件,但又不是每一个页面或者大部分页面需要它。很可能出现这种状况:A 页面只需要 chunk-commons里面的 A 组件,

    但却要下载整个chunk-commons.js,这时候就需要考虑一下,目前的拆包策略是否合理,是否还需要chunk-commons?还是将这些组件打包到各自的 bundle 中?还是调整一下 minChunks: 2( 最小共用次数)?或者修改一下它的拆包规则?


    // 或者你可以把策略改为只提取那些你注册在全局的组件。

     

    - test: resolve("src/components")

    + test: resolve("src/components/global_components") //你注册全局组件的目录


    博弈


    其实优化就是一个博弈的过程,是让 a bundle 大一点还是 b? 是让首次加载快一点还是让 cache 的利用率高一点? 但有一点要切记,拆包的时候不要过分的追求颗粒化,什么都单独的打成一个 bundle,不然你一个页面可能需要加载十几个.js文件,如果你还不是HTTP/2的情况下,请求的阻塞还是很明显的(受限于浏览器并发请求数)。所以还是那句话资源的加载策略并没什么完全的方案,都需要结合自己的项目找到最合适的拆包策略。


    比如支持HTTP/2的情况下,你可以使用 webpack4.15.0 新增的 maxSize,它能将你的chunk在minSize的范围内更加合理的拆分,这样可以更好地利用HTTP/2来进行长缓存(在HTTP/2的情况下,缓存策略就和之前又不太一样了)。


    Long term caching


    持久化缓存其实是一个老生常谈的问题,前端发展到现在,缓存方案已经很成熟了。简单原理:


    • 针对 html 文件:不开启缓存,把 html 放到自己的服务器上,关闭服务器的缓存

    • 针对静态的 js,css,图片等文件:开启 cdn 和缓存,将静态资源上传到 cdn 服务商,我们可以对资源开启长期缓存,因为每个资源的路径都是独一无二的,所以不会导致资源被覆盖,保证线上用户访问的稳定性。

    • 每次发布更新的时候,先将静态资源(js, css, img) 传到 cdn 服务上,然后再上传 html 文件,这样既保证了老用户能否正常访问,又能让新用户看到新的页面。


    相关文章 大公司里怎样开发和部署前端代码?(https://www.zhihu.com/question/20790576/answer/32602154)


    所以我们现在要做的就是要让 webpack 给静态资源生产一个可靠的 hash,让它能自动在合适的时候更新资源的 hash,

    并且保证 hash 值的唯一性,即为每个打包后的资源生成一个独一无二的 hash 值,只要打包内容不一样,那么 hash 值就不一样。


    其实 webpack 4 在持久化缓存这一块已经做得非常的不错了,但还是有一些欠缺,下面我们将要从这几个方面讨论这个问题。


    • RuntimeChunk(manifest)

    • Module vs Chunk

    • HashedModuleIdsPlugin

    • NamedChunksPlugin


    RuntimeChunk(manifest)


    webpack 4 提供了 runtimeChunk 能让我们方便的提取 manifest,以前我们需要这样配置


    new webpack.optimize.CommonsChunkPlugin({

      name: "manifest",

      minChunks: Infinity

    });


    现在只要一行配置就可以了


    {

      runtimeChunk: true;

    }


    它的作用是将包含chunks 映射关系的 list单独从 app.js里提取出来,因为每一个 chunk 的 id 基本都是基于内容 hash 出来的,所以你每次改动都会影响它,如果不将它提取出来的话,等于app.js每次都会改变。缓存就失效了。


    单独抽离 runtimeChunk 之后,每次打包都会生成一个runtimeChunk.xxx.js。(默认叫这名字,可自行修改)


    640?wx_fmt=jpeg


    优化


    其实我们发现打包生成的 runtime.js非常的小,gzip 之后一般只有几 kb,但这个文件又经常会改变,我们每次都需要重新请求它,它的 http 耗时远大于它的执行时间了,所以建议不要将它单独拆包,而是将它内联到我们的 index.html 之中(index.html 本来每次打包都会变)。


    这里我选用了 script-ext-html-webpack-plugin,主要是因为它还支持preload和 prefetch,正好需要就不想再多引用一个插件了,你完全可以使用 inline-manifest-webpack-plugin或者 assets-webpack-plugin等来实现相同的效果。


    const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");

     

    // 注意一定要在HtmlWebpackPlugin之后引用

    // inline 的name 和你 runtimeChunk 的 name保持一致

    new ScriptExtHtmlWebpackPlugin({

      //`runtime` must same as runtimeChunk name. default is `runtime`

      inline: /runtime..*.js$/

    });


    Module vs Chunk


    我们经常看到xxxModuleIdsPlugin、xxxChunksPlugin,所以在 webpack 中 module和 chunk到底是一个怎么样的关系呢?


    • chunk: 是指代码中引用的文件(如:js、css、图片等)会根据配置合并为一个或多个包,我们称一个包为 chunk。

    • module: 是指将代码按照功能拆分,分解成离散功能块。拆分后的代码块就叫做 module。可以简单的理解为一个 export/import 就是一个 module。


    每个 chunk 包可含多个 module。 比如:


    //9.xxxxxxxxx.js

     

    //chunk id为 9 ,包含了Vc2m和JFUb两个module

    (window.webpackJsonp = window.webpackJsonp || []).push([

      [9],

      {

        Vc2m: function(e, t, l) {},

        JFUb: function(e, t, l) {}

      }

    ]);


    一个module还能跨chunk引用另一个module,比如我想在app.js里面需要引用 chunkId为13的模块2700可以这样引用:


    return n.e(13).then(n.bind(null, "27OO"));


    HashedModuleIdsPlugin


    了解了 module和chunk之后,我们来研究一下 moduleId。


    首先要确定你的 filename 配置的是chunkhash(它与 hash 的区别可以看上篇文章)。


    output: {

      path: path.join(__dirname, 'dist'),

      filename: '[name].[chunkhash].js',

    }


    我们在入口文件中随便引入一个新文件test.js


    //main.js

    import "./test";

     

    //test.js

    console.log("apple");


    我们运行npm run build,发现了一件奇怪的事情,我只是多引入了一个文件,但发现有十几个文件发生了变化。这是为什么呢?


    我们随便挑一个文件 diff 一下,发现两个文件只有 module id 的不同。


    640?wx_fmt=jpeg


    这是因为:


    webpack 内部维护了一个自增的 id,每个 module 都有一个 id。所以当增加或者删除 module 的时候,id 就会变化,导致其它文件虽然没有变化,但由于 id 被强占,只能自增或者自减,导致整个 id 的顺序都错乱了。


    虽然我们使用 [chunkhash] 作为输出名,但仍然是不够的。

    因为 chunk 内部的每个 module 都有一个 id,webpack 默认使用递增的数字作为 moduleId。

    如果引入了一个新文件或删掉一个文件,都可能会导致其它文件的 moduleId 发生改变,

    那这样缓存失效了。如:


    640?wx_fmt=jpeg


    本来是一个按序的 moduleId list,这时候我插入一个orange模块,插在第三个位置,这样就会导致它之后的所以 module id 都依次加了 1。


    这到了原因,解决方案就很简单了。我们就不要使用一个自增的 id 就好了,这里我们使用HashedModuleIdsPlugin。


    或者使用optimization.moduleIds v4.16.0 新发布,文档还没有。查看 源码发现它有natural、named、hashed、size、total-size。这里我们设置为optimization.moduleIds='hash'等于HashedModuleIdsPlugin。源码了也写了webpack5会优化这部分代码。


    它的原理是使用文件路径的作为 id,并将它 hash 之后作为 moduleId。


    640?wx_fmt=jpeg


    使用了 HashedModuleIdsPlugin`,我们再对比一下发现 module id 不再是简单的 id 了,而是一个四位 hash 过得字符串(不一定都是四位的,如果重复的情况下会增加位数,保证唯一性 源码)。

    这样就固定住了 module id 了。


    NamedModulesPlugin 和 HashedModuleIdsPlugin 原理是相同的,将文件路径作为 id,只不过没有把路径 hash 而已,适用于开发环境方便调试。不建议在生产环境配置,因为这样不仅会增加文件的大小(路径一般偶读比较长),更重要的是为暴露你的文件路径。


    NamedChunkPlugin


    我们在固定了 module id 之后同理也需要固定一下 chunk id,不然我们增加 chunk 或者减少 chunk 的时候会和 module id 一样,都可能会导致 chunk 的顺序发生错乱,从而让 chunk 的缓存都失效。


    作者也意识到了这个问题,提供了一个叫NamedChunkPlugin的插件,但在使用路由懒加载的情况下,你会发现NamedChunkPlugin并没什么用。

    供了一个线上demo,可以自行测一下。这里提就直接贴一下结果:


    640?wx_fmt=jpeg


    产生的原因前面也讲了,使用自增 id 的情况下是不能保证你新添加或删除 chunk 的位置的,一旦它改变了,这个顺序就错乱了,就需要重排,就会导致它之后的所有 id 都发生改变了。


    接着我们 查看源码 还发现它只对有 name 的 chunk 才奏效!所以我们那些异步懒加载的页面都是无效的。这启不是坑爹!我们迭代业务肯定会不断的添加删除页面,这岂不是每新增一个页面都会让之前的缓存都失效?那我们之前还费这么大力优化什么拆包呢?


    其实这是一个古老的问题了 相关 issue: Vendor chunkhash changes when app code changes 早在 2015 年就有人提了这个问题,这个问题也一直讨论至今,’网友们’也提供了各种奇淫巧技,不过大部分随着 webpack 的迭代已经不适用或者是修复了。


    这里我就结合一下 timse(webpack 第二多贡献)写的持久缓存的文章(在 medium 上需要翻墙)

    总结一下目前能解决这个问题的三种方案。


    目前解决方案有三种


    • records

    • webpackChunkName

    • 自定义 nameResolver


    webpack records


    很多人可能连这个配置项都没有注意过,不过早在 2015 年就已经被设计出来让你更好的利用 cache。官方文档


    要使用它配置也很简单:


    recordsPath: path.join(__dirname, "records.json");


    对,只要这一行代码就能开启这个选项,并打包的时候会自动生成一个 JSON 文件。它含有 webpack 的 records 记录 – 即「用于存储跨多次构建(across multiple builds)的模块标识符」的数据片段。可以使用此文件来跟踪在每次构建之间的模块变化。


    大白话就是:等于每次构建都是基于上次构建的基础上进行的。它会先读取你上次的 chunk 和 module id 的信息之后再进行打包。所以这时候你再添加或者删除 chunk,并不会导致之前所说的乱序了。


    简单看一下构建出来的 JSON 长啥样。


    {

      "modules": {

        "byIdentifier": {

          "demo/vendor.js": 0,

          "demo/vendor-two.js": 1,

          "demo/index.js": 2,

          ....

        },

        "usedIds": {

          "0": 0,

          "1": 1,

          "2": 2,

          ...

        }

      },

      "chunks": {

        "byName": {

          "vendor-two": 0,

          "vendor": 1,

          "entry": 2,

          "runtime": 3

        },

        "byBlocks": {},

        "usedIds": [

          0,

          1,

          2

      }

    }


    我们和之前一样,在路由里面添加一个懒加载的页面,打包对比后发现 id 并不会像之前那样按照遍历到的顺序插入了,而是基于之前的 id 依次累加了。一般新增页面都会在末尾填写一个新 id,删除 chunk 的话,会将原来代表 chunk 的 id,保留,但不会再使用。


    640?wx_fmt=jpeg


    但这个方案不被大家知晓主要原因就是维护这个records.json比较麻烦。如果你是在本地打包运行webpack的话,你只要将records.json当做普通文件上传到github、gitlab或其它版本控制仓库。


    但现在一般公司都会将打包放在 CI里面,用docker打包,这时候这份records.json存在哪里就是一个问题了。它不仅需要每次打包之前先读取你这份 json,打包完之后它还需要再更新这份 json,并且还要找地方存贮,为了下次构建再使用。你可以存在 git 中或者找一个服务器存,但存在什么地其它方都感觉怪怪的。


    如果你使用 Circle CI可以使用它的store_artifacts,相关教程。


    本人在使用了之后还是放弃了这个方案,使用成本略高。前端打包应该更加的纯粹,不需要依赖太多其它的东西。


    webpackChunkName


    在 webpack2.4.0 版本之后可以自定义异步 chunk 的名字了,例如:


    import(/* webpackChunkName: "my-chunk-name" */ "module");


    我们在结合 vue 的懒加载可以这样写。


    {

        path: '/test',

        component: () => import(/* webpackChunkName: "test" */ '@/views/test')

      },


    打包之后就生成了名为 test的 chunk 文件。


    640?wx_fmt=jpeg


    chunk 有了 name 之后就可以解决NamedChunksPlugin没有 name 的情况下的 bug 了。查看打包后的代码我们发现 chunkId 就不再是一个简单的自增 id 了。


    不过这种写法还是有弊端的,首先你需要手动编写每一个 chunk 的 name,同时还需要保证它的唯一性,当页面一多,维护起来还是很麻烦的。这就违背了程序员的原则:能偷懒就偷懒。


    所以有什么办法可以自动生成一个 name 给 chunk 么 ?查看 webpack 源码我们发现了NamedChunksPlugin其实可以自定义 nameResolver 的。


    自定义 nameResolver


    NamedChunksPlugin支持自己写 nameResolver 的规则的。但目前大部分相关的文章里的自定义函数是不适合 webpack4 ,而且在结合 vue 的情况下还会报错。


    社区旧方案:


    new webpack.NamedChunksPlugin(chunk => {

      if (chunk.name) {

        return chunk.name;

      }

      return chunk.modules.map(m => path.relative(m.context, m.request)).join("_");

    });


    适配 webpack4 和 vue 的新实现方案:


    new webpack.NamedChunksPlugin(chunk => {

      if (chunk.name) {

        return chunk.name;

      }

      return Array.from(chunk.modulesIterable, m => m.id).join("_");

    });


    当然这个方案还是有一些弊端的因为 id 会可能很长,如果一个 chunk 依赖了很多个 module 的话,id 可能有几十位,所以我们还需要缩短一下它的长度。我们首先将拼接起来的 id hash 以下,而且要保证 hash 的结果位数也能太长,浪费字节,但太短又容易发生碰撞,所以最后我们我们选择 4 位长度,并且手动用 Set 做一下碰撞校验,发生碰撞的情况下位数加 1,直到碰撞为止。详细代码如下:


    const seen = new Set();

    const nameLength = 4;

     

    new webpack.NamedChunksPlugin(chunk => {

      if (chunk.name) {

        return chunk.name;

      }

      const modules = Array.from(chunk.modulesIterable);

      if (modules.length > 1) {

        const hash = require("hash-sum");

        const joinedHash = hash(modules.map(m => m.id).join("_"));

        let len = nameLength;

        while (seen.has(joinedHash.substr(0, len))) len++;

        seen.add(joinedHash.substr(0, len));

        return `chunk-${joinedHash.substr(0, len)}`;

      } else {

        return modules[0].id;

      }

    });


    我给 vue-cli 官方也提了一个相关issue尤雨溪最后也采纳了这个方案。

    所以如果你现在下载最新 vue-cli@3上面啰嗦了半天的东西,其实都已经默认配置好了(但作者本人为了找到这个 hack 方法整整花了两天时间 o(╥﹏╥)o)。


    目前测试了一段时间没发现有什么问题。不过有一点不是很理解,不知道 webpack 出于什么样的原因,官方一直没有修复这个问题?可能是在等 webpack5 的时候放大招吧。


    总结


    拆包策略:


    • 基础类库 chunk-libs

    • UI 组件库 chunk-elementUI

    • 自定义共用组件/函数 chunk-commons

    • 低频组件 chunk-eachrts/chunk-xlsx等

    • 业务代码 lazy-loading xxxx.js


    持久化缓存:


    • 使用 runtimeChunk 提取 manifest,使用 script-ext-html-webpack-plugin等插件内联到index.html减少请求

    • 使用 HashedModuleIdsPlugin 固定 moduleId

    • 使用 NamedChunkPlugin结合自定义 nameResolver 来固定 chunkId


    上述说的问题大部分在 webpack 官方文档都没明确指出,唯一可以参考的就是这份 cache 文档,在刚更新 webpack4 的时候,我以为官方已经将 id 不能固定的问题解决了,但现实是残酷的,结果并不理想。不过作者也在很多的 issue 中说他正在着手优化 long term caching。


    We plan to add another way to assign module/chunk ids for long term caching, but this is not ready to be told yet.


    在 webpack 的 issue 和源码中也经常见到 Long term caching will be improved in webpack@5和TODO webpack 5 xxxx这样的代码注释。这让我对webpack 5很期待。真心希望webpack 5能真正的解决前面几个问题,并且让它更加的out-of-the-box,更加的简单和智能,就像webpack 4的optimization.splitChunks,你基本不用做什么,它就能很好的帮你拆分好bundle包,同时又给你非常的自由发挥空间。


    展望


    Whats next? 官方在这篇文章中展望了一下 webpack5 和讲述了一下未来的计划–持续改进用户体验、提升构建速度和性能,降低使用门槛,完善Persistent Caching等等。同时 webpack 也已经支持 Prefetching/Preloading modules,我相信之后也会有更多的网站会使用这一属性。


    同时 webpack 的团队已经承诺会通过投票的方式来决定一些功能。比如不久前发起的投票。


    640?wx_fmt=jpeg


    大家可以关注 Tobias Koppers 的 twitter 进行投票。


    最后还是期待一下 webpack5 和它之后的发展吧。如果没有 webpack,也就不会有今天的前端。


    其实如一开始就讲的,vue 有vue-cli、react 有creat-react-app,现在新建项目基本都是基于脚手架的,很少有人从零开始写 webpack 配置文件的,而且一般开发中,一般程序员也不需要经常去修改 webpack 的配置。webpack 官方本身也在不断完善默认配置项,相信 webpack 的配置门槛也会越来低多。


    愿世间再无 webpack 配置工程师。


    640?wx_fmt=jpeg




    【关于投稿】


    如果大家有原创好文投稿,请直接给公号发送留言。


    ① 留言格式:
    【投稿】+《 文章标题》+ 文章链接

    ② 示例:
    【投稿】《不要自称是程序员,我十多年的 IT 职场总结》:http://blog.jobbole.com/94148/

    ③ 最后请附上您的个人简介哈~



    觉得本文对你有帮助?请分享给更多人

    关注「前端大全」,提升前端技能

    640?wx_fmt=png

    640?wx_fmt=jpeg

    展开全文
  • 第一章 技术与规范与JavaScript库 1.Node Node.js 就是运行在服务端的 JavaScript。Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8...

    (此文档于2019年3月停止再更新,后续更新移步至:https://github.com/liuyuqin1991/polaris)

    学习路线

    这里写图片描述

    第一章 技术(核心单独列章节)

    1.Node

    Node.js 就是运行在服务端的 JavaScript。Node.js 是一个基于Chrome JavaScript
    运行时建立的一个平台。Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎。相当与JDK。

    Github:

    https://github.com/nodejs/node

    官网教程:

    https://nodejs.org/en/

    其他教程:

    http://nqdeng.github.io/7-days-nodejs/

    http://www.imooc.com/learn/348

    2.Npm

    NPM(node package
    manager)是 Node.js 的包管理和分发工具。它类似于PHP的Composer,Ruby的gem,Python的pip,Java的Maven……它可以让 JavaScript 开发者能够更加轻松的共享代码和共用代码片段,并且通过npm管理你分享的代码也很方便快捷和简单。截至目前
    最新的稳定版 npm 是 3.3.12。

    Github:

    https://github.com/npm/npm

    官方网站:

    https://docs.npmjs.com

    官方教程:

    https://docs.npmjs.com/getting-started

    其他教程:

    https://segmentfault.com/a/1190000005799797

    http://www.cnblogs.com/kelsen/p/4947859.html

    http://www.runoob.com/nodejs/nodejs-npm.html

    3.Gulp

    gulp是基于Nodejs的自动任务运行器, 能自动化地完成
    javascript、coffee、sass、less、html/image、css
    等文件的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成,并监听文件在改动后重复指定的这些步骤。在实现上,它借鉴了Unix操作系统的管道(pipe)思想,前一级的输出,直接变成后一级的输入,使得在操作上非常简单。

    Github:

    https://github.com/gulpjs/gulp

    官方网站:

    http://gulpjs.com/

    官方文档:

    https://github.com/gulpjs/gulp/blob/master/docs/API.md

    其他教程:

    http://www.cnblogs.com/2050/p/4198792.html

    http://www.tuicool.com/articles/FJVNZf

    https://www.cnblogs.com/Darren_code/p/gulp.html

    4.Bower

    Bower是一个客户端技术的软件包管理器,它可用于搜索、安装和卸载如JavaScript、HTML、CSS之类的网络资源。其他一些建立在Bower基础之上的开发工具,如YeoMan和Grunt。

    Bower与Npm:

    简单的说,npm是进行后端开发中,使用的模块安装工具,而bower,是前端的模块安装工具。比如,在安装express,socket.io时,当然使用的是npm,那么比如bootstrap,jquery等前端框架,需要使用bower,npm
    是伴随 Node.js 出现的一个包管理器,最开始只能支持 Node.js 的模块管理,但是后来,
    npm 官网经过一次改版,打出的口号是,javascript
    的包管理器,所以,其已经不在局限于是Node.js 的模块管理了,已经通用到了所有 js
    的包管理工具了,可以说,前后通吃了。bower
    的话,从一开始,就是专门为前端表现设计的包管理器,一切全部为前端考虑的。npm 和
    bower 的最大区别,就是 npm 支持嵌套地依赖管理,而
    bower只能支持扁平的依赖(嵌套的依赖,由程序员自己解决)。嵌套依赖,指的就是,你依赖的软件包,还有它自己的依赖,好像摘葡萄,一摘一大串。在服务器环境的时候,这并没什么关系,因为存储空间够大,一切代码都是本地运行,只要解决完依赖就行了,但是到了用户产品的浏览器里,就很成问题了,你不能让用户去下载好几M的js代码,那就太糟糕了。在这个情况下,就需要程序员自己手动解决用到的类库的嵌套依赖问题。比如确保各种各样的插件都依赖同一个版本的jQuery。为什么有很多项目
    bower 和 npm 都用呢,那是因为要用 bower 管理前端的包,而用 npm
    去管理一些后端的包和构建工具,例如,yeoman,grunt,gulp,jshint
    等等等等。所有的包管理器,都有自己的弊端,要视需要选用对自己的项目最合适的。

    最新更新于2018年1月17日:

    Bower已经过时,作者也不在进行维护,包管理工具已经是npm的天下,不过yarn也在快速崛起,值得研究。

    Github:

    https://github.com/bower/bower

    官方网站:

    https://bower.io/

    官方教程:

    https://bower.io/#getting-started

    其他教程:

    https://segmentfault.com/a/1190000002971135

    5.Browserify­

    browserify是一个编译工具,通过它可以在浏览器环境下像nodejs一样使用遵循commonjs规范的模块化编程。你可以使用browserify来组织代码,也可以使用第三方模块,不需要会nodejs,只需要用到node来编译,用到npm来安装包.browserify模块化的用法和node是一样的,所以npm上那些原本仅仅用于node环境的包,在浏览器环境里也一样能用。

    Github:

    https://github.com/browserify/browserify

    官方网站:

    http://browserify.org/

    官方教程:

    https://github.com/substack/node-browserify#usage

    学习资料

    http://www.tuicool.com/articles/IFvQ3qv

    https://segmentfault.com/a/1190000002941361

    http://www.cnblogs.com/liulangmao/p/4920534.html

    6.Browsersync

    Browsersync能让浏览器实时、快速响应您的文件更改(html、js、css、sass、less等)并自动刷新页面。更重要的是
    Browsersync可以同时在PC、平板、手机等设备下进项调试。您可以想象一下:“假设您的桌子上有pc、ipad、iphone、android等设备,同时打开了您需要调试的页面,当您使用browsersync后,您的任何一次代码保存,以上的设备都会同时显示您的改动”。无论您是前端还是后端工程师,使用它将提高您30%的工作效率。

    Github:

    https://github.com/BrowserSync/browser-sync

    教程:

    http://www.browsersync.cn/

    7.Less-.less,Saas-.scss

    Sass,Less 是一门 CSS 预处理语言,它扩展了 CSS
    语言,增加了变量、Mixin、函数等特性,使 CSS 更易维护和扩展。Less 可以运行在 Node
    或浏览器端。

    less 官网:

    http://lesscss.org/

    less中文网:

    http://lesscss.cn/

    sass官网:

    http://sass-lang.com/

    sass中文网:

    https://www.sass.hk/

    8.Es6

    ECMAScript 6
    (以下简称ES6)是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript
    2015.也就是说,ES6就是ES2015。

    教程:

    http://es6.ruanyifeng.com/

    https://segmentfault.com/a/1190000004365693#articleHeader6

    9.Babel

    Babel是一个广泛使用的转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。

    Github:

    https://github.com/babel/babel

    官方网站:

    http://babeljs.io/

    教程:

    http://babeljs.cn/

    其他:

    文章 链接
    【第1464期】babel7使用手册 https://mp.weixin.qq.com/s/AURDiWwspdfRExopNf4YLQ
    【第1378期】 一口(很长的)气了解 Babel https://github.com/easonyq/easonyq.github.io/blob/master/学习记录/others/babel.md

    10.Q.js

    Promise是抽象异步处理对象以及对其进行各种操作的组件。JavaScript的异步执行都是以回调函数来执行的。Promise可以简化回调并提供链式异步调用。

    Github:

    https://github.com/kriskowal/q

    教程:

    http://liubin.org/promises-book/

    Github:

    https://github.com/kriskowal/q

    11.Express.js

    基于 Node.js 平台,快速、开放、极简的 web 后端开发框架。

    Github:

    https://github.com/expressjs/express

    官方网站:

    http://expressjs.com/

    中文网:

    http://www.expressjs.com.cn/

    12.Fontawesome

    Font Awesome 字体为您提供可缩放矢量图标,它可以被定制大小、颜色、阴影以及任何可以用CSS的样式。

    Github:

    https://github.com/FortAwesome/Font-Awesome

    官方地址:

    http://www.fontawesome.com.cn/

    13.yarn

    Yarn 是一个依赖管理工具。它能够管理你的代码,并与全世界的开发者分享代码。Yarn
    是高效、安全和可靠的,你完全可以安心使用,与npm属于同一类型的库

    中文官方教程:

    https://yarn.bootcss.com/docs/getting-started.html

    其他教程:

    http://mp.weixin.qq.com/s/FnCa0wqdHKQN4syrPIPSAA

    14.ESLint

    提供一个插件化的javascript代码检测工具

    Github:

    https://github.com/eslint/eslint

    官网:

    https://eslint.org/

    教程:

    http://eslint.cn/

    15.typescript

    TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。编译出来的
    JavaScript 可以运行在任何浏览器上。TypeScript
    编译工具可以运行在任何服务器和任何系统上。TypeScript 是开源的。

    Github:

    https://github.com/Microsoft/TypeScript

    教程:

    https://www.tslang.cn/

    https://ts.xcatliu.com/introduction/what-is-typescript.html

    16.PWA

    第二章 插件库

    类别 名称 Github 说明
    相册图库切换 bootstrap-carousel Bootstrap 轮播(Carousel)插件是一种灵活的响应式的向站点添加滑块的方式。除此之外,内容也是足够灵活的,可以是图像、内嵌框架、视频或者其他您想要放置的任何类型的内容。
    jpicture 一款兼容主流浏览器的基于jQuery的图片切换插件。简单方便,轻松快捷,好用实用
    数学库 mathjs https://github.com/josdejong/mathjs 是一个广泛应用于JavaScript 和 Node.js的数学库,它的特点是灵活表达式解析器,支持符号计算,内置大量函数与常量,并提供集成解决方案来处理不同的数据类型,如数字,大数字,复数,分数,单位和矩阵。
    下拉框 bootstrap-select https://github.com/silviomoreto/bootstrap-select/ bootstrap风格的下拉框组件,提供丰富的下拉框功能
    加解密 crypto-js https://github.com/brix/crypto-js 一个加解密的JavaScript库
    发布/订阅库 PubSubJS https://github.com/mroderick/PubSubJS 可以订阅的息,发布消息(通过一个绑定项)和消息退订
    新手指引 Intro.js https://github.com/usablica/intro.js/ 新手指引的一个框架库, Step-by-step guide and feature introduction
    Driver.js https://github.com/kamranahmedse/driver.js 新手指引的一个框架库
    ajax 异步请求 api isomorphic-fetch https://github.com/matthew-andrews/isomorphic-fetch
    **fetch-jsonp ** https://github.com/camsong/fetch-jsonp
    axios https://github.com/axios/axios 类似于jquery的ajax
    fetch https://github.com/github/fetch
    验证 async-validator https://github.com/yiminghe/async-validator async-validator 是一个异步验证的库,需要传入要验证的数据和验证规则
    图片查看器 viewerjs https://github.com/fengyuanchen/viewerjs Viewer.js 是一款强大的图片查看器,像门户网站一般都会有各自的图片查看器,如果您正需要一款强大的图片查看器,也许 Viewer.js 是一个很好的选择 官方网站: http://fengyuanchen.github.io/viewerjs/
    视频播放器 video.js https://github.com/videojs/video.js Video.js 是一个通用的在网页上嵌入视频播放器的 JS 库,Video.js 自动检测浏览器对 HTML5 的支持情况,如果不支持 HTML5 则自动使用 Flash 播放器
    弹窗提示插件 **pnotify ** https://github.com/sciactive/pnotify 一个仿桌面弹窗的提示插件
    日期时间选择器插件 bootstrap-datepicker https://github.com/uxsolutions/bootstrap-datepicker bootstrap的日期开源组件 官方文档地址: http://bootstrap-datepicker.readthedocs.io/en/latest/index.html 中文网文档: http://www.bootcss.com/p/bootstrap-datetimepicker/index.htm
    datetimepicker github.com/smalot/bootstrap-datetimepicker bootstrap的时间开源组件 官方网站: http://www.bootcss.com/p/bootstrap-datetimepicker/
    Daterangepicker https://github.com/dangrossman/bootstrap-daterangepicker 时间区间范围选择插件 官方网站: http://www.daterangepicker.com/ 其他文档: option文档:http://bootstrap-datepicker.readthedocs.io/en/latest/options.html
    城市选择插件 City-Picker.js https://github.com/tshi0912/city-picker 城市三级联动选择器。
    3D three.js https://github.com/mrdoob/three.js JavaScript 3D library 官方网站: https://threejs.org/
    数字运算,格式化库 numeral https://github.com/adamwdraper/Numeral-js Numeral.js 是一个用于格式化和数字四则运算的js 库,支持包括中文在类的17种语言 官方网站: http://numeraljs.com/ 相关资料: https://www.cnblogs.com/chu888chu888/archive/2012/12/22/2828994.html
    数字滚动动画 countUp https://github.com/inorganik/countUp.js/ 数字滚动插件
    数据模拟 mock.js https://github.com/nuysoft/Mock 方便前端生成模拟数据,它基于 数据模板 生成模拟数据,基于 HTML模板 生成模拟数据,拦截并模拟 ajax 请求。 Github: https://github.com/nuysoft/Mock 官方地址: http://mockjs.com
    表格插件 Datatables.js https://github.com/DataTables/DataTables Datatables是一款基于jQuery表格插件。 Github: https://github.com/DataTables/DataTables 官方网站: https://www.datatables.net/
    时间插件 moment.js https://github.com/moment/moment JavaScript 日期处理类库。
    图表插件 echarts https://github.com/ecomfe/echarts 图标类控件。 官方地址: http://echarts.baidu.com/index.html
    模板引擎 Handlebars.js https://github.com/wycats/handlebars.js/ Handlebars.js是一套js模版引擎,是一款基于Jquery的插件,以json对象为数据源,支持逻辑判断、循环等操作,同时具有非常好的扩展性,体积60KB左右 教程: <http://www.cnblogs.com/iyangyuan/archive/2013/12/12/3471227.html http://keenwon.com/992.html http://www.ghostchina.com/introducing-the-handlebars-js-templating-engine/>
    jade(pug) https://github.com/pugjs/pug jade是一套js模版引擎,现已改名pug 官方网站: http://jade-lang.com/ 教程: <http://www.nooong.com/docs/jade_chinese.htm http://cnodejs.org/topic/5368adc5cf738dd6090060f2 https://www.w3cplus.com/html/how-to-use-jade.html>
    dot.js https://github.com/olado/doT 一个适用于混合开发的模板引擎
    npm相关 concurrently https://github.com/kimmobrunfeldt/concurrently A CLI tool to run multiple npm-scripts in parallel or sequential.
    npm-run-all https://github.com/mysticatea/npm-run-all A CLI tool to run multiple npm-scripts in parallel or sequential.
    动画视觉特效 rainyday.js https://github.com/mubaidr/rainyday.js Rainyday.js 是一个轻量的 JavaScript库,利用 HTML5 Canvas 实现雨滴下落在玻璃表面的动画效果。Rainyday.js尽可能的模拟现实的雨滴效果,几乎可以以假乱真了
    loading加载相关库 http://www.cnblogs.com/lhb25/p/loading-spinners-animated-with-css3.html 使用 CSS3 实现超炫的 Loading(加载)动画效果
    tooltip提示 popper.js https://github.com/FezVrasta/popper.js Popper.js 是一个扩展性较好的 tooltips 提示类 JS 插件,不需要依赖 jQuery 库,大小仅为 3.5KB 左右,使用与配置相当简单
    触摸滑动 swiper.js https://github.com/nolimits4web/swiper 开源、免费、强大的触摸滑动插件 中文官方网站: https://www.swiper.com.cn/
    下拉刷新,上拉加载 dropload.js https://github.com/ximan/dropload 移动端下拉刷新、上拉加载更多插件
    mescroll.js https://github.com/mescroll/mescroll 移动端下拉刷新、上拉加载更多插件 官方网站: http://www.mescroll.com/index.html

    第三章 gulp自动化构建组件(FEZ项目实践)

    名称 Github 说明 相关教程
    node-hbsfy https://github.com/epeli/node-hbsfy borwserify 支持 require handlebars模板
    jadeify https://github.com/domenic/jadeify jade模板
    node-glob https://github.com/isaacs/node-glob 使用glob来返回目录中所有子文件(文件或文件夹) http://www.cnblogs.com/liulangmao/p/4552339.html
    glob通配符释义:http://blog.csdn.net/zhuchuji/article/details/51297819
    gulp-inject https://github.com/klei/gulp-inject CSS和WebComponent 注入插件,将它们注入到index.html文件中(官方解释),把css,js这些静态文件注入到html文件中,不需要手动添加。
    main-bower-files https://github.com/ck86/main-bower-files 通过读取并分析bower.json文件里override属性里main路径下定义的插件及相关依赖,返回一个文件数组
    vinyl-buffer https://github.com/hughsk/vinyl-buffer 将虚拟的流文件转换为可以使用的的buffer文件
    gulp-if https://github.com/robrich/gulp-if 为功能执行添加条件判断,跟程序语言中的if是相同语义
    browserify-shim https://github.com/thlorenz/browserify-shim browserify-shim 是一个转换工具,它会读取 package.json 文件的 “browserify-shim” 属性,将不遵循node风格的commonjs的输出写法的模块包转换成browserify可以读懂的模块包
    babel-polyfill https://github.com/babel/babel/tree/master/packages/babel-polyfill IE8兼容包
    vueify https://github.com/babel/babel/tree/master/packages/babel-polyfill 在browserify中编辑vue代码
    watchify https://github.com/browserify/watchify
    gulp.spritesmith https://github.com/twolfson/gulp.spritesmith 小图(图标)合成精灵图,拼接图片并生成样式表,并且还能输出SASS,Stylus,LESS甚至是JSON
    gulp-filter https://github.com/sindresorhus/gulp-filter 在虚拟文件流中过滤特定的文件
    gulp-order https://github.com/sirlantis/gulp-order The gulp plugin gulp-order allows you to reorder a stream of files using the same syntax as of gulp.src,这是官方定义,简单来说,就是按给定的顺序整理需要处理的文件集。
    gulp-concat https://github.com/contra/gulp-concat 合并js文件,比如合并多个js到一个文件
    gulp-concat-css https://github.com/mariocasciaro/gulp-concat-css 合并css文件,比如合并多个css到一个文件
    rework https://github.com/reworkcss/rework
    del https://github.com/sindresorhus/del 使用glob匹配规则来删除文件或文件夹
    gulp-less https://github.com/stevelacy/gulp-less 编译less文件的gulp插件
    gulp-sass https://github.com/dlmanning/gulp-sass 编译sass文件的gulp插件
    gulp-postcss https://github.com/postcss/gulp-postcss PostCSS把扩展的语法和特性(比如变量,混入,未来css预发,内联图像等)转换成现代的浏览器友好的CSS
    gulp-usemin(不推荐使用) https://github.com/pursual/gulp-usemin 根据预先在html文件(或者其它模板/视图中的文件)中声明好的blocks来执行一系列任务(例如合并文件并重全名、排除一些只在开发过程中引入的脚本以及将css和js中的代码提取出来内嵌在html文件中)来处理未优化的样式和脚本
    gulp-htmlmin https://github.com/jonschlinkert/gulp-htmlmin 压缩html,可以压缩页面javascript、css,去除页面空格、注释,删除多余属性等操作
    gulp-imagemin https://github.com/sindresorhus/gulp-imagemin 压缩常见的图片格式,比如PNG,JPEG,GIF,甚至是SVG图片
    gulp-clean-css https://github.com/scniro/gulp-clean-css 压缩css文件,减小文件大小,并给引用url添加版本号避免缓存
    gulp-uglify https://github.com/terinjokes/gulp-uglify 专业压缩js文件,减小文件大小
    gulp-rename https://github.com/hparra/gulp-rename 修改文件名称,比如有时我们需要把app.js改成app.min.js
    gulp-util https://github.com/gulpjs/gulp-util gulp工具包,可以打log啊,提示语颜色啊,提示音啊等等一些操作,不过一般就是用来打印日志
    gulp-rev https://github.com/sindresorhus/gulp-rev 根据静态资源内容,生成md5签名,打包出来的文件名会加上md5签名,同时生成一个json用来保存文件名路径对应关系
    gulp-rev-replace https://github.com/jamesknelson/gulp-rev-replace 配合 gulp-rev 使用,拿到生成的 manifest。json 后替换对应的文件名称
    gulp-sourcemaps https://github.com/gulp-sourcemaps/gulp-sourcemaps 处理 JavaScript 时生成 SourceMap
    gulp-svg-symbols https://github.com/Hiswe/gulp-svg-symbols 合并SVG图标
    gulp-inject-string https://github.com/mikehazell/gulp-inject-string 在页面中插入内容
    gulp-replace https://github.com/lazd/gulp-replace 字符串替换的gulp 插件
    gulp-flatten https://github.com/armed/gulp-flatten 删除或替换文件的相对路径
    lazypipe https://github.com/OverZealous/lazypipe Lazypipe允许您创建一个不可变的、延迟初始化的管道。能在需要重用部分管道的环境中使用,比如gulp。它可以创建一些工厂来把你经常使用的功能 stream 链分离出来,变成一个通用插件功能链,然后放入需要这个链的链路上
    event-stream https://github.com/dominictarr/event-stream 流操作,还不大清楚其原理
    gulp-autoprefixer https://github.com/sindresorhus/gulp-autoprefixer 给CSS添加前缀,解决某些CSS属性不是标准属性,有各种浏览器前缀的情况,灰常有用
    gulp-posthtml https://github.com/posthtml/gulp-posthtml html预处理 html中的rem转换等
    postcss-pxtorem https://github.com/cuth/postcss-pxtorem CSS 转换 `px` 为 `rem`
    envify https://github.com/hughsk/envify
    vinyl-source-stream https://github.com/hughsk/vinyl-source-stream 在gulp或虚拟文件的流开始使用传统的文本流,对现有的npm流有更好的互操作性
    babelify https://github.com/babel/babelify
    cssify https://github.com/davidguttman/cssify borwserify 支持 require css样式
    lessify https://github.com/dstokes/lessify borwserify 支持 require less样式
    preprocessify https://github.com/bibliolabs/preprocessify 条件注释/主要区分开发和上线环境,是否加载mock数据
    lodash https://github.com/lodash/lodash 具有一致接口、模块化、高性能等特性的 JavaScript 扩展工具库 官方网站:https://lodash.com/
    gulp-size https://github.com/sindresorhus/gulp-size 压缩字体文件
    imagemin-pngquant 压缩png图片文件
    gulp-rev-delete-original https://github.com/nib-health-funds/gulp-rev-delete-original 删除由gulp-rev或gulp-rev-all重写的原始文件
    gulp-rev-css-url https://github.com/galkinrost/gulp-rev-css-url 提取样式中的相对路径
    gulp-strip-debug https://github.com/sindresorhus/gulp-strip-debug
    gulp-cdnify https://github.com/kaiqigong/gulp-cdnify CDN地址替换

    第四章 React

    组件 概念 标题 链接
    react react官方文档 https://reactjs.org/docs/hello-world.html
    react基础入门 http://www.ruanyifeng.com/blog/2015/03/react.html
    谈谈 react 中的 key https://juejin.im/post/5a7c04746fb9a063461fe700?utm_medium=fe&utm_source=weixinqun
    【第1200期】React 是怎样炼成的 https://segmentfault.com/a/1190000013365426
    【第1269期】基于React实战分享WeatherApp https://github.com/alivebao/weather_app
    【第1273期】React性能优化-虚拟Dom原理浅析 http://wuyuying.com/blog/archives/optimizing-react-virtual-dom-explained/
    【第1281期】React 16 加载性能优化指南 https://zhuanlan.zhihu.com/p/37148975
    【第1333期】图解 React https://zhuanlan.zhihu.com/p/39658720
    React 学习路线图 - 2018版 https://juejin.im/entry/5b49af6d6fb9a04fe25ec224
    【第1341期】React组件模式 https://segmentfault.com/a/1190000015710309
    【第1355期】组件、Prop 和 State https://zhuanlan.zhihu.com/p/41398296
    精读《React 八种条件渲染》 https://juejin.im/post/5b285c0d5188257494641d0b
    【第1386期】React从渲染原理到性能优化(一) https://zhuanlan.zhihu.com/p/43145754
    【第1392期】React从渲染原理到性能优化(二) https://zhuanlan.zhihu.com/p/43566956
    【第1409期】 React之深入理解 Props 和 State https://zhuanlan.zhihu.com/p/44784850
    【第1430期】以面试官的角度来看 React 工作面试 https://juejin.im/post/5bca74cfe51d450e9163351b
    React v16.6 发布 https://mp.weixin.qq.com/s/8cJwUj8_C-W9ZC0fTWq8YA
    【第1428期】React v16.7 “Hooks” - What to Expect https://zhuanlan.zhihu.com/p/47684983
    【第1439期】React 项目结构和组件命名之道 https://github.com/dawn-plex/translate/blob/master/articles/Structuring-projects-and-naming-components-in-react.md
    React 2019年路线图发布!Hooks明年第一季度上线 https://mp.weixin.qq.com/s/Kml_Tv-u0xpWq0TacfCzNw
    【第1472期】我们为什么要写 super(props)? https://juejin.im/post/5c04fea5f265da6133565696
    React中的五种组件形式 https://www.jianshu.com/p/1bc0a178be05
    【第1493期】 React 的今天和明天(图文版)第一部分 https://juejin.im/post/5be90d825188254b0917f180
    【第1494期】 React 的今天和明天(图文版)第二部分 https://juejin.im/post/5bfccbf8f265da61407e97b5
    【第1497期】React组件文档解决方案 https://zhuanlan.zhihu.com/p/53567112
    【第1507期】从 loading 的 9 种写法谈 React 业务开发 https://www.yuque.com/es2049/blog/xel32z
    react-route与react-router-dom 教程 https://reacttraining.com/react-router/web/example/basic
    基础入门 http://www.ruanyifeng.com/blog/2016/05/react_router.html
    react-dnd
    react-draggable
    react-redux 前面有一个Redux,我们去撩(聊)一下它 https://juejin.im/post/5adf0b4df265da0b8070620a?utm_medium=fe&utm_source=weixinqun
    【第1304期】聊一聊Redux的前身Flux https://zhuanlan.zhihu.com/p/38050036
    nerv 一个基于 Virtual DOM 的类 React 组件框架(jd自己的凹凸实验室研发的) 官方地址 https://nerv.aotu.io/
    react-native React-Native从零搭建App https://juejin.im/post/5a9f93d96fb9a028d2077c19?utm_medium=react&utm_source=weixinqun
    Airbnb 在 React Native 上下的赌注(一):概述 https://juejin.im/post/5b2c924ff265da59a401f050
    Airbnb 在 React Native 上下的赌注(二):技术细节 https://juejin.im/post/5b3b40a26fb9a04fab44e797
    Airbnb 在 React Native 上下的赌注(三):建立一个跨平台的移动端团队 https://juejin.im/post/5b446177f265da0f7c4faec8
    Airbnb 在 React Native 上下的赌注(四):React Native 退役 https://juejin.im/post/5b447b1e6fb9a04fd3437dad
    Airbnb 在 React Native 上下的赌注(五)(完结篇):Airbnb 移动端路在何方? https://juejin.im/post/5b46f92de51d45198e721cd7
    create-react-app create项目脚手架及框架 Creat React App 2.0 发布 https://mp.weixin.qq.com/s/sCPmYHMH2Dc3SZmQhlJCig

    react组件轮子集合(摘自antd)

    类型 组件
    UI库 antd
    路由 react-router
    布局 react-blocks
    拖拽 react-dnd
    react-draggable
    代码编辑器 react-codemirror2
    富文本编辑器 react-quill
    react-draft-wysiwyg
    react-lz-editor
    braft-editor
    拾色器 rc-color-picker
    react-color
    响应式 react-responsive
    react-media
    复制到剪贴板 react-copy-to-clipboard
    页面 meta 属性 react-helmet
    react-document-title
    图标 react-fa
    react-icons
    二维码 qrcode.react
    可视化图表 BizCharts
    recharts
    victory
    顶部进度条 nprogress
    应用国际化 react-intl
    代码高亮 react-syntax-highlighter
    Markdown 渲染 react-markdown
    无限滚动 react-virtualized
    地图 react-google-maps
    google-map-react
    react-amap 高德
    Emoji emoji-mart
    右键菜单 react-contextmenu

    第五章 Vue:

    标题 链接
    [译] Vue让我喜欢的10个方面 https://juejin.im/post/5a7bfbe8f265da4e8b2fcc50?utm_medium=fe&utm_source=weixinqun
    2018 年你需要知道的 Vue.js 组件库,完善你的应用开发 http://blog.csdn.net/VhWfR2u02Q/article/details/78859493
    基于vue的browserify的项目模板 https://github.com/keepfool/vue-tutorials/tree/master/05.OfficialTemplates/my-browserify-simple-demo
    手摸手,带你用vue撸后台 系列一 https://juejin.im/post/59097cd7a22b9d0065fb61d2
    【第1244期】详解vue中的 Object.create(null) https://mp.weixin.qq.com/s/bPpPnzmOJtBdEmdcLeyHQw
    VUE组件汇总 https://juejin.im/post/5af16a2cf265da0b8636353b
    【第1272期】从零开始搭建脚手架 https://mp.weixin.qq.com/s/m8atIJzGEGnOV_utpijG2g
    【第1313期】一个Vue页面的内存泄露分析 https://zhuanlan.zhihu.com/p/38448752
    【第1322期】Vue CLI 3 配置中 Modern mode 是什么 https://mp.weixin.qq.com/s/X_kl6AJ0Q_9BJk0_eRSM1w
    【第1385期】Vue高版本中一些新特性的使用 https://github.com/masterkong/blog/issues/7
    【第1410期】尤雨溪:Vue 3.0 计划 https://juejin.im/post/5bb719b9f265da0ab915dbdd
    【第1432期】megalo – 网易考拉小程序解决方案 https://mp.weixin.qq.com/s/n5nUGdOO-JxkPsM7G7UCAQ

    第六章 Webpack:

    标题 链接
    基础入门 http://www.jianshu.com/p/42e11515c10f
    中文详细文档 http://www.jqhtml.com/7626.html
    【第1186期】Webpack v4 beta 版本发布 https://mp.weixin.qq.com/s/ZWY_UDxaWvrlgHuIen7AMw
    【第1203期】webpack 4 发布了! http://www.zcfy.cc/article/x1f3bc-webpack-4-released-today-webpack-medium
    【第1206期】看清楚真正的 Webpack 插件 https://zoumiaojiang.com/article/what-is-real-webpack-plugin/
    【第1215期】基于Webpack4使用懒加载分离打包React代码 https://segmentfault.com/a/1190000013654180
    【第1241期】webpack4升级完全指南 https://segmentfault.com/a/1190000014247030
    带你走进webpack世界,成为webpack头号玩家 https://juejin.im/post/5ac9dc9af265da23884d5543
    【第1252期】Webpack基本架构浅析 http://blog.zxrcool.com/2018/04/19/Webpack基本架构浅析/
    【第1263期】我当初为什么写webpack https://zcfy.cc/article/interview-with-webpack-founder-tobias-koppers
    【第1271期】Webpack4+ 多入口程序构建 https://futu.im/posts/2018-05-07-webpack4/
    【第1303期】webpack4初探 https://blog.shenfq.com/2018/06/09/webpack4初探/
    手摸手,带你用合理的姿势使用webpack4(上) https://segmentfault.com/a/1190000015919863
    手摸手,带你用合理的姿势使用webpack4(下) https://segmentfault.com/a/1190000015919928
    【第1364期】Webpack之treeShaking https://github.com/easonyq/easonyq.github.io/blob/master/学习记录/webpack/treeShaking.md
    深度掌握webpack的5个关键点 https://mp.weixin.qq.com/s/ciyc9z8ZaVwSpriQoUCHHA
    【第1498期】webpack loader机制源码解析 https://hellogithub2014.github.io/2019/01/03/webpack-loader/
    你配置Webpack 4的方式可能是错的! https://mp.weixin.qq.com/s/cX7yuneDxDk8_NnMy3Bc8Q

    第七章 CSS:

    标题 链接
    箭头消息提示框样式 http://www.cssarrowplease.com/
    Flex 布局教程:语法篇 http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html?utm_source=tuicool
    Flex 布局教程:实例篇 http://www.ruanyifeng.com/blog/2015/07/flex-examples.html
    【第1195期】现代CSS进化史 https://segmentfault.com/a/1190000013191860
    【第1227期】关于 CSS 变量,你需要了解的一切 https://juejin.im/post/5ab835225188255572085af4
    CSS工程化演进 https://mp.weixin.qq.com/s/1QkGy9UbRF3FtyBllmJdmA
    【第1242期】 开始使用新的 CSS Typed Object Model https://zhuanlan.zhihu.com/p/35029796
    前端布局推进剂 - 间距规范化 https://juejin.im/post/5ad0a366f265da239b41dbaf?utm_medium=fe&utm_source=weixinqun
    【第1183期】这些 CSS 命名规范,将省下你大把调试时间 https://github.com/xitu/gold-miner/blob/master/TODO/css-naming-conventions-that-will-save-you-hours-of-debugging.md
    【第1288期】新的 CSS 特性正在改变网页设计 https://github.com/xitu/gold-miner/blob/master/TODO1/future-of-web-design.md
    【第1348期】理解伪元素:before和:after https://www.jianshu.com/p/cc14b72c870e
    BEM的定义 https://www.w3cplus.com/css/bem-definitions.html
    从10种现在流行的 CSS 解决方案谈谈我的最爱 (上) https://juejin.im/post/5b39e63ae51d4562aa017c81?utm_medium=fe&utm_source=weixinqun
    从10种现在流行的 CSS 解决方案谈谈我的最爱 (中) https://juejin.im/post/5b3c594fe51d45191252d6a2
    从10种现在流行的 CSS 解决方案谈谈我的最爱 (下) https://juejin.im/post/5b3dd2d25188251b193d2d7e
    【第1368期】视口相关单位的应用 http://wuyuying.com/blog/archives/css-in-depth-viewport-relative-units/
    【第1376期】CSS自定义属性 https://mp.weixin.qq.com/s/8mk7fo2PIo8JXIaZWtL7aQ
    【第1373期】无单位数字和行高 http://wuyuying.com/blog/archives/css-in-depth-unitless-number/
    【第1356期】如何更愉快地使用em https://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651229486&idx=1&sn=c4f2541df9332b62cdb4056c93fe6bc0&chksm= bd4954aa8a3eddbcaacd206251ab9b2f209106aa7bdd6a53a8d61b58ed9c73939648f253b677&scene=21#wechat_redirect
    【第1361期】如何更愉快地使用rem http://wuyuying.com/blog/archives/css-in-depth-stop-thinking-in-px/
    【第1433期】CSS3动画实战之多关键帧实现无限循环动效的时间间隔 https://juejin.im/post/5abc2d25f265da237b222797
    【第1440期】你所不知道的 CSS 阴影技巧与细节 https://www.cnblogs.com/coco1s/p/9913885.html
    【第1457期】CSS 与网络性能 https://juejin.im/post/5bf4bcbee51d4514e0512f72
    【第1488期】有限状态机在 CSS 动画中的应用 https://github.com/dawn-plex/translate/blob/master/articles/css-animations-with-finite-state-machines.md

    CSS样式库:

    类别 名称 Github 官网 说明
    按钮 buttons https://github.com/alexwolfe/Buttons Buttons 是一个高度可定制的、免费并且开源的按钮 CSS 样式库
    动画 animate.css https://github.com/daneden/animate.css https://daneden.github.io/animate.css/ css3动画库

    第八章 UI:

    分类 名称 资源
    react Ant Design of React alibaba的蚂蚁金服开发的开箱即用的高质量 React UI组件 官方教程: https://ant.design/docs/react/introduce-cn Github(star:30001 2018-6-26 ): https://github.com/ant-design/ant-design
    material-ui Material-UI是一个实现了Google’s Material Design设计规范的react组件库,开箱即用,使用它可以快速搭建出赏心悦目的应用界面 官网: https://material-ui.com/ Github(star:38027 2018-6-26 ): https://github.com/mui-org/material-ui/
    react-desktop 如果你对跨平台桌面应用程序的 UI 组件感兴趣,那么 React-Desktop 就是为你而设。你可以在上面找到 Mac OS 和 Windows 10 的均可用 UI 组件。 React-Desktop 可与 NW.jsElectron.js 完美结合,也可用于任何 JavaScript 驱动的项目 官网: http://reactdesktop.js.org Github(star:7556 2018-6-26 ): https://github.com/gabrielbull/react-desktop
    Semantic-UI-React React 最好的 UI 框架,Semantic-UI-React 几乎拥有 Semantic-UI 中所有有用的组件,同时也有一个非常好的 Declarative API ,和用于 React 组件的 shorthand props ,并且 jQuery-Free。 文档: https://react.semantic-ui.com/introduction Github(star:7222 2018-6-26 ): https://github.com/Semantic-Org/Semantic-UI-React
    Blueprint 它包含 30+ 的 React 组件,涵盖几乎所有的通用界面元素,从按钮到表单控件到工具提示等等。 它还包括每个组件的 CSS 样式和使用 Sass 和 Less 变量设计自己的组件和应用的工具, 以及一个优雅的调色板和两种尺寸的 300 多个 UI 图标,旨在让你专注于构建产品,针对构建复杂、数据密集的 Web 界面的桌面应用进行了最优化。如果你重度依赖移动互动,并且正在寻找 mobile-first 的 UI 工具包,它可能不适合你 官网: http://blueprintjs.com/ Github(star:9652 2018-6-26 ): https://github.com/palantir/blueprint
    React-Bootstrap React-Bootstrap 是一个可重复使用的前端组件库。你可以通过 Facebook 的 React.js 框架获得 Twitter Bootstrap 的体验,而且有更为清晰的代码。 简而言之,这是老牌的 Bootstrap 组件,用 React 重新编写。 官网: https://react-bootstrap.github.io/ Github(star:13271 2018-6-26 ): https://github.com/react-bootstrap/react-bootstrap
    React-Toolbox 一组使用 CSS 模块实现 Google Material Design 的 React 组件,它允许你只使用所需的 CSS ,而不用使用像 Purify-CSS 这样的工具。除此之外,React-Toolbox 是具有30 +组件,开箱即用的,高度可定制的框架。 官网: http://react-toolbox.com/ Github(star:7985 2018-6-26 ): https://github.com/react-toolbox/react-toolbox
    **Grommet ** Grommet 不仅仅是一个 UX 框架,它所包含的东西要比单纯的 UX 框架多得多。Grommet = 用 React 编写的一堆 UX 组件和工具 + 自有的 grommet-cli +“入门” 指南 + 预建模板 + 良好的文档+ 与 Sketch 集成 。 官网: http://grommet.io/ Github(star:3381 2018-6-26 ): https://github.com/grommet/grommet
    Fabric 用于构建与 Office 和 Office 365 界面相类似的 Web 应用的 React 组件,Fabric 是用 TypeScript 编写的官方 Office 库,包含“入门”指南、博客、官方调色板和字体,以及构建项目所需的所有组件 官网: https://developer.microsoft.com/en-us/fabric#/components Github(star:2822 2018-6-26 ): https://github.com/OfficeDev/office-ui-fabric-react
    element-react Element was initially written in Vue, which has many elegant UI components, but we also love React, so we forked it for the React community. 官方介绍,一眼就懂 官方教程: https://elemefe.github.io/element-react/#/zh-CN/quick-start Github(star:17062018-7-25 ): https://github.com/ElemeFE/element-react
    vue element Element,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库 官方教程: http://element-cn.eleme.io/#/zh-CN/component/installation Github(star:29138 2018-7-25 ): https://github.com/ElemeFE/element
    iview 一套基于 Vue.js 的高质量 UI 组件库 官方教程: https://www.iviewui.com/docs/guide/install Github(star:16217 2018-7-25 ): https://github.com/iview/iview
    通用 bootstrap 简洁、直观、强悍的前端开发框架,让web开发更迅速、简单 官网: http://www.bootcss.com/ Github(star:125581 2018-6-26 ): https://github.com/twbs/bootstrap
    wired-elements 手绘风格的一套很特别的组件 官网: wiredjs.com Github(star:5842 2018-6-26 ): https://github.com/wiredjs/wired-elements

    第九章 分散技术:

    1. __filename

    在Node.js中,预定义了两个变量:用于获取当前模块文件名的__filename变量与用于获取当前目录名的__dirname变量。在任何模块文件内部,可以使用__filename变量获取当前模块文件的带有完整绝对路径的文件名。

    2.fis

    3.PostCSS

    4.Prettier1.0

    JS程序员格式化代码神器!

    5.Swagger

    是一个简单但功能强大的API表达工具,使用Swagger生成API,我们可以得到交互式文档,自动生成代码的SDK以及API的发现特性等

    6.vs code

    一款免费,小巧,好看的编辑器,微软出品

    官方教程:

    https://code.visualstudio.com/docs

    7.canvas

    Canvas
    API(画布)是在HTML5中新增的标签用于在网页实时生成图像,并且可以操作图像内容,基本上它是一个可以用JavaScript操作的位图

    教程:

    http://caibaojian.com/canvas/about.html

    8.flutter

    9.Stetho

    第十章 前端技术文章:

    标题 链接
    下一代 Web 应用模型 — Progressive Web App https://zhuanlan.zhihu.com/p/25167289?from=timeline&isappinstalled=0
    最全的常用正则表达式大全 https://www.cnblogs.com/zxin/archive/2013/01/26/2877765.html
    antV 3.0(G2,F2,G6)发布 https://antv.alipay.com/zh-cn/vis/blog/antv-3.0-releasing.html
    【第1154期】2017 年 JavaScript 发展状况回顾 http://mp.weixin.qq.com/s/5phdFz4W3Q2cUYiyWgwK9A
    【第1152期】2017 JavaScript 调查报告概述 http://mp.weixin.qq.com/s/SqmFD0VKiP40q8iJzkE4kA
    【第1165期】H5动画:轨迹移动 https://mp.weixin.qq.com/s/ySC69C3lIIzRDwmOs-pGYw
    【第1164期】从前端技术到体验科技 http://mp.weixin.qq.com/s/cIM4KA-4EaT3wdxLuIpBBg
    【第1117期】萌新也能懂的现代 JavaScript 开发 https://zhuanlan.zhihu.com/p/31044340
    【第1167期】npm 2017 JavaScript 框架报告之 React 生态系统分析 https://mp.weixin.qq.com/s/Ltw1-SsW4IOHr0HcXtUqDQ
    【第1168期】字符编码的故事 http://mp.weixin.qq.com/s/P0PbNjajkILusn533mNqVA
    【第1171期】npm 2017 JavaScript 框架报告之后端框架 http://mp.weixin.qq.com/s/w5vccN2CnVO82PSOp75-KA
    解决ajax跨域的方法原理详解 http://blog.csdn.net/wuliyun88/article/details/50521725
    Chrome控制台使用详解 http://www.codeceo.com/article/chrome-console.html
    【第1172期】HTML 5.2 有哪些新内容? https://github.com/xitu/gold-miner/blob/master/TODO/whats-new-in-html-5-2.md
    【第1173期】npm 2017 JavaScript 框架报告之前端框架 https://mp.weixin.qq.com/s/AD_7nOCBBZxmfTKlF0fN2g
    【第790期】构建可靠的前端异常监控服务-采集篇 http://mp.weixin.qq.com/s/LGbMXauSuuGWcvqazjXWjQ
    【第1175期】2017年 JavaScript 明星项目 https://risingstars.js.org/2017/zh/#section-framework
    【第1037期】美团点评点餐前后端分离实践 https://zhuanlan.zhihu.com/p/28704974
    【第1178期】WebSocket:5分钟从入门到精通 http://mp.weixin.qq.com/s/rKvoevbSmtAUoCgOH2lZdQ
    【第1125期】GraphQL 技术栈揭秘 http://www.zcfy.cc/article/the-graphql-stack-how-everything-fits-together-apollo-graphql-4549.html
    【第362期】最简单实现跨域的方法:使用nginx反向代理 http://blog.jobbole.com/90975/
    【第1182期】Canvas or SVG?一张好图,两手准备,就在 ECharts 4.0 https://zhuanlan.zhihu.com/p/33093211
    2018 年最值得关注的 JavaScript 趋势 http://blog.csdn.net/VhWfR2u02Q/article/details/78974598
    【第1184期】滴滴后市场前端技术体系 https://juejin.im/post/5a3dd19b6fb9a0452846b159
    2018 前端性能优化清单 https://github.com/xitu/gold-miner/blob/master/TODO/front-end-performance-checklist-2018-1.md
    【第1158期】哔哩哔哩的前端之路 https://zhuanlan.zhihu.com/p/32487533
    【第1191期】你所忽略的js隐式转换 https://juejin.im/post/5a7172d9f265da3e3245cbca
    【第1194期】手把手教你用 SVG 符号和 CSS 变量做出彩色图标 https://zhuanlan.zhihu.com/p/33623303
    【第1192期】记“编写babel插件”与“通过语法解析替换小程序路由表”的经历 https://segmentfault.com/a/1190000013130489
    2018 Web 开发者路线图 https://zhuanlan.zhihu.com/p/33565551?utm_source=com.daimajia.gold&utm_medium=social
    前端每周清单第 51 期: React Context API 与模式变迁, Webpack 与 Web 优化, AI 界面生成 https://juejin.im/post/5a81ac8e6fb9a0635c0478fc
    【第1196期】原生JS数据绑定 http://zcfy.cc/article/native-javascript-data-binding
    JavaScript 进阶之深入理解数据双向绑定 http://mp.weixin.qq.com/s/CiLa2wjl4VriA-JYRS_o6w
    【第1199期】10 种最常见的 Javascript 错误 https://elevenbeans.github.io/2018/02/05/top-10-javascript-errors/
    【第1201期】Node 定时器详解 http://www.ruanyifeng.com/blog/2018/02/node-event-loop.html
    【第1202期】手淘过年项目中采用到的前端技术 https://www.w3cplus.com/css/taobao-2018-year.html
    【第999期】从 JavaScript 到 TypeScript http://muyunyun.cn/posts/66a54fc2/index.html
    【第1207期】TypeScript 实践分享 https://juejin.im/post/5a9c004a6fb9a028b92c9e91
    最受欢迎的五大前端框架比较 https://mp.weixin.qq.com/s/A1WM8uJD9p-rpip5SYMOmg
    什么是 PWA https://juejin.im/post/5a9e8ad5f265da23a40456d4?utm_medium=fe&utm_source=weixinqun
    【第1209期】关于 Promise 的 9 个提示 http://yanmeng.me/2018/03/02/about-promise/
    【第1210期】前端开发、交互、视觉是怎么合作完成工作的 https://zhuanlan.zhihu.com/p/34238597
    2018 如何玩转 JavaScript https://juejin.im/entry/5aa0e46d6fb9a028b54775cd?utm_medium=fe&utm_source=weixinqun
    用 JavaScript 实现人脸识别 http://mp.weixin.qq.com/s/YlpehhbtCnuK3lSt2unffg
    【第1214期】帆布指纹识别 https://www.cnblogs.com/xiezhengcai/p/4252008.html
    JS中浮点数精度问题 https://juejin.im/post/5aa1395c6fb9a028df223516?utm_medium=fe&utm_source=weixinqun
    【第1216期】最全最好用的动效落地方法、都帮你总结好了(上) https://zhuanlan.zhihu.com/p/34501702
    【第1223期】最全最好用的动效落地方法、都帮你总结好了(下) https://zhuanlan.zhihu.com/p/34815524
    JavaScript变量的生命周期:为什么let不被提升 https://juejin.im/post/5aa631ab5188255587233214?utm_medium=fe&utm_source=weixinqun
    【第1054期】高阶函数:利用Filter、Map和Reduce来编写更易维护的代码 https://zhuanlan.zhihu.com/p/28835709
    从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理 http://www.dailichun.com/2018/01/21/js_singlethread_eventloop.html
    【第1218期】DNS:为什么很重要&是如何工作的 http://mp.weixin.qq.com/s/jRTmRhAbga5PkgF7QLpHaw
    H5 分层屏幕适配 http://hai.li/2018/03/14/h5-screen-adaptation.html
    简单快速理解js中的this、call和apply https://juejin.im/post/5aab40bef265da23826dba61?utm_medium=fe&utm_source=weixinqun
    【第1222期】十倍效能提升——Web 基础研发体系的建立 http://www.cnblogs.com/sskyy/p/8613393.html
    WebSocket 学习:基于socket.io实现简单多人聊天室 https://segmentfault.com/a/1190000011538416
    【第729期】什么时候不该使用es6箭头函数 https://segmentfault.com/a/1190000007074846
    2017前端性能优化清单 https://segmentfault.com/a/1190000008132322
    2018年3月前端知识集锦 https://juejin.im/post/5abb22925188255c4c1050e0?utm_medium=fe&utm_source=weixinqun
    【第1230期】从头实现一个koa框架 https://zhuanlan.zhihu.com/p/35040744
    【第1231期】热爱 JavaScript,但是讨厌 CSS ? https://juejin.im/post/5aba0d37518825556a72708a
    【第1234期】前端布局基础概述 https://mp.weixin.qq.com/s/-LcNZWFFty2lWuND6uuNNA
    Chrome 调试工具使用小技巧 http://mp.weixin.qq.com/s/Nyrav5fx_pqgSDEXNRb6Ug
    【第1239期】关于Google发布的JS代码规范,你需要了解什么? https://github.com/WhiteYin/translation/issues/10
    【第1240期】passive 事件监听 https://futu.im/posts/2017-06-06-passive-event-listeners/
    【第1091期】JavaScript:理解同步、异步和事件循环 https://manxisuo.github.io/2016/01/18/understanding-async-and-event-loop/
    【第1243期】一次掌握 JavaScript ES5 到 ES8 数组内容 https://hufangyun.com/2017/array-learn/
    【第839期】JavaScript 数组方法对比 http://jinlong.github.io/2017/02/04/javascript-array-methods-mutating-vs-non-mutating/
    【第1071期】深入 JavaScript 数组:进化与性能 http://www.wemlion.com/post/javascript-array-evolution-performance
    聊聊 HTTPS 和 SSL/TLS 协议 http://mp.weixin.qq.com/s/vOFTcll3ditYuuVyESN-2A
    【第1248期】ECMAScript 2016, 2017, 和2018中新增功能 https://blog.hypers.io/2018/04/11/es2016-17-18/
    我是如何在公司项目中使用ESLint来提升代码质量的 https://juejin.im/post/5ad367695188255c9323bb30?utm_medium=be&utm_source=weixinqun
    【第1250期】彻底理解浏览器的缓存机制 https://heyingye.github.io/2018/04/16/%E5%BD%BB%E5%BA%95%E7%90%86%E8%A7%A3%E6%B5%8F%E8%A7%88%E5 %99%A8%E7%9A%84%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6/
    高阶函数,你怎么那么漂亮呢! https://juejin.im/post/5ad6b34a6fb9a028cc61bfb3?utm_medium=fe&utm_source=weixinqun
    微信小程序之-NBA在线直播小程序开发 https://juejin.im/post/5ad614c46fb9a028d7011e7f?utm_medium=fe&utm_source=weixinqun
    【第1251期】玩转HTML5 Video视频WebVTT字幕使用样式与制作 http://www.zhangxinxu.com/wordpress/2018/03/html5-video-webvtt-subtitle/
    微信小程序也要强行热更代码,鹅厂不服你来肛我呀 https://juejin.im/entry/5ad91eda6fb9a07acb3c67f2?utm_medium=fe&utm_source=weixinqun
    【第1255期】超大型 JavaScript 应用的设计哲学 https://zhuanlan.zhihu.com/p/35929167
    JS 中的设计模式了解一下? https://juejin.im/post/5adea689f265da0b873a212e?utm_medium=fe&utm_source=weixinqun
    【第1257期】YAML 语言教程 http://www.ruanyifeng.com/blog/2016/07/yaml.html
    【第1258期】从JS垃圾回收机制和词源来透视闭包 https://mp.weixin.qq.com/s/485GgpEt2c7uS-mY1cbA3w
    【第1259期】Nerv实战 - 京东首页改版小结 https://aotu.io/notes/2018/04/24/jdindex_2017/
    【第1260期】图说 ES Modules https://segmentfault.com/a/1190000014318751
    【第1261期】那些好玩却尚未被 ECMAScript 2017 采纳的提案 https://juejin.im/post/5ae920fd51882567127852e7
    【第1262期】Jenkins打造强大的前端自动化工作流 https://juejin.im/post/5ad1980e6fb9a028c42ea1be
    【第1265期】那些前端MVVM框架是如何诞生的 https://zhuanlan.zhihu.com/p/36453279
    【第1266期】基于Docker+Consul+Registrator+Nodejs实现服务治理(一) https://github.com/chenchunyong/blog/blob/master/microservice/serviceRegister.md
    【第1267期】基于Docker+Consul+Registrator+Nodejs实现服务治理(二) https://github.com/jasonGeng88/blog/blob/master/201704/service_discovery.md
    面试官:阮一峰版的快速排序完全是错的 https://juejin.im/post/5af4902a6fb9a07abf728c40
    如何加密传输和存储用户密码 https://juejin.im/post/5af5711e5188254267261e3b?utm_medium=fe&utm_source=weixinqun
    【第1274期】ES6之路之模块详解 https://mp.weixin.qq.com/s/bP-hhEm01V63R-hAYsKbpw
    console觉醒之路,打印个动画如何 https://juejin.im/post/5afafb0c6fb9a07ac65331fe
    手机web前端调试页面的几种方式 https://juejin.im/entry/5afd1b276fb9a07acc11ec81
    【第1277期】Google I/O 2018 —— Web 系列参会笔记 https://mp.weixin.qq.com/s/EiTQDFPoUBc97gQtwTNWCA
    【第1278期】上课啦!了解下 DDoS攻击方式 https://zhuanlan.zhihu.com/p/29784472
    【第1279期】无尽滚动的复杂度 https://www.jianshu.com/p/4e16b4211d84
    【第1280期】如何利用 Chrome 浏览器实现滚动截屏 https://weibo.com/ttarticle/p/show?id=2309404241869646237445
    【第1282期】页面可视化搭建工具前生今世 https://github.com/CntChen/cntchen.github.io/issues/15
    【第1283期】从0开始发布一个无依赖、高质量的npm https://github.com/simbawus/blog/issues/12
    【第1285期】我知道的HTTP请求 https://juejin.im/post/5a757d2f5188254e5c6c404a
    【第1286期】滑向未来:现代 JavaScript 与 CSS 滚动实现指南 https://www.zcfy.cc/article/scroll-to-the-future
    【第1287期】深入浅出 SVG https://juejin.im/post/5ad84f296fb9a045e8011be1
    【第1289期】W3C工作备忘 – 布局 https://cloud.tencent.com/developer/article/1138992
    【第1290期】 一个安卓设备管理操作平台-STF http://ju.outofmemory.cn/entry/199455
    【第1292期】GitHub 的用法与礼仪 https://zhuanlan.zhihu.com/p/37599617
    【第1293期】浏览器之美,你知道多少? https://juejin.im/post/5b0a085f6fb9a07aa048774e
    【第1294期】JS Linter 进化史 https://zhuanlan.zhihu.com/p/34656263
    【第1295期】浅谈 instanceof 和 typeof 的实现原理 https://tinycat2017.github.io/2018/05/28/浅谈-instanceof-和-typeof-的实现原理/
    【第1297期】HTTPS 的故事 https://juejin.im/post/5b10aa22e51d4506c5568f1d
    【第710期】HTTPS是如何工作的 https://www.zcfy.cc/article/how-does-https-work-1280.html
    【第1298期】宋小菜生鲜 B2B 的前端团队搭建 https://mp.weixin.qq.com/s/kl107B5j6VDIkNr_IdHu-Q
    【第1299期】浅谈混合应用的演进 https://mp.weixin.qq.com/s/Ui5TKoBWteDORx5CEswx5w
    【第1301期】如何阅读大型前端开源项目的源码 https://zhuanlan.zhihu.com/p/36996225
    谁说前端不需要懂-Nginx反向代理与负载均衡 https://juejin.im/post/5b01336af265da0b8a67e5c9?utm_medium=fe&utm_source=weixinqun
    一套比较完整的前端技术选型,需要规整哪些东西,你知道不? https://juejin.im/post/5b28d4fbe51d45587b47fd43?utm_medium=fe&utm_source=weixinqun
    如何利用vw+rem进行移动端布局 https://juejin.im/post/5b29f476e51d455892718380?utm_medium=fe&utm_source=weixinqun
    【第1312期】Node.js开发之父:十个Node.js的设计错误以及其终极解决办法 https://medium.com/m/global-identity?redirectUrl=https://m.oursky.com/node-js-開發之父-十個node-js-的設計錯誤-以及其終極解決辦法-f0db0afb496e
    【第1311期】浅析前端安全之 XSS https://zhuanlan.zhihu.com/p/38327058
    大型网站的 HTTPS 实践:协议层以外的实践 https://mp.weixin.qq.com/s/hy0qauQ_hvAJNMYOHvnxLg
    关于PWA,你需要注意这9点 https://mp.weixin.qq.com/s/3JgScJUbEPWL0wRDn28kkA
    【第1314期】JavaScript 引擎基础:Shapes 和 Inline Caches https://zhuanlan.zhihu.com/p/38202123
    【第1315期】GraphQL 基于 SPA 架构的工程实践 https://mp.weixin.qq.com/s/etnLUbok8BUZBAMr3bKWGg
    【第1316期】大前端时代前端监控的最佳实践 https://mp.weixin.qq.com/s/YiKRY_LDURY0uONtEhkUfg
    【第1317期】在 web 上使用 JavaScript 模块 https://zhuanlan.zhihu.com/p/38581901
    【第1318期】深入浅出 JavaScript 关键词 – this https://juejin.im/post/5aefe76e6fb9a07abc29d4a1
    【第1319期】deno深入揭秘及未来展望 https://www.cnblogs.com/accordion/p/9247016.html
    我看完掘金上的227篇文章,总结出一份Flutter入门教程 https://juejin.im/post/5b3c8a4be51d4519935860d5?utm_medium=fe&utm_source=weixinqun
    【第1326期】WebView缓存原理分析和应用 https://unclechen.github.io/2017/05/13/WebView缓存原理分析和应用/
    【第1327期】如何找到一份好的前端开发工作 https://mp.weixin.qq.com/s/Cs3BZEBZEV79hP7qFujOWg
    接口咋整?前端数据药神来也 https://juejin.im/post/5b4160f4f265da0f83646849
    微信小程序朋友圈分享图片生成方案实现 https://juejin.im/post/5b40b158e51d4518f543c7b0
    【第348期】JSON Web Token - 在Web应用间安全地传递信息 https://mp.weixin.qq.com/s/PY6xSf-3KKPHIjS9T5pufw
    【第1328期】八幅漫画理解使用JSON Web Token设计单点登录系统 http://blog.leapoahead.com/2015/09/07/user-authentication-with-jwt/
    小程序 LRU 存储设计 https://juejin.im/post/5b42d3ede51d4519277b6ce3?utm_medium=fe&utm_source=weixinqun
    【第1330期】洞察 video 超能力系列——玩转 mp4 https://techblog.toutiao.com/2018/07/09/untitled-51/
    【第1334期】组件开发的单元素模式 https://zhuanlan.zhihu.com/p/39814349
    【第1335期】这个控件叫:Skeleton Screen/加载占位图 https://zhuanlan.zhihu.com/p/26014116
    【第1337期】JavaScript 是如何工作的:用 MutationObserver 追踪 DOM 的变化 https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md
    【第1339期】如何不择手段提升scroll事件的性能 https://zhuanlan.zhihu.com/p/30078937
    射鸡狮说圆形的进度条不是我要的效果 https://segmentfault.com/a/1190000015719837
    手把手教你用原生JavaScript造轮子(1)——分页器 https://juejin.im/post/5b592635e51d4533d2043e15
    简易商城小程序全栈开发(mpvue+koa+mongodb) https://juejin.im/post/5b548ce8e51d45191d79f8a6
    2017年Web前端技术综述 https://blog.csdn.net/napolunyishi/article/details/79176176
    【第1351期】JavaScript 中的私有变量 https://github.com/xitu/gold-miner/blob/master/TODO/private-variables-in-javascript.md
    前端性能之JavaScript成本2018版 https://mp.weixin.qq.com/s/Ngj1bVkXRsXx0Ke6EdWkCg
    【第1362期】微信小程序30分钟从陌生到熟悉(上) https://mp.weixin.qq.com/s/t8HH4BgWNZ10gbFpAiv_1A
    【第1363期】微信小程序30分钟从陌生到熟悉(下) https://mp.weixin.qq.com/s/gy2xrKy9QJJtRiOy_6i6iA
    视频演示如何用纯 CSS 创作一只玉免 https://segmentfault.com/a/1190000015997373
    【第1363期】 用JS写一个同Excel表现的智能填充算法 https://segmentfault.com/a/1190000015951750
    前端程序员必须掌握之三角函数在前端动画中的应用 https://juejin.im/entry/5b79a1d6f265da4343459b7f?utm_medium=fe&utm_source=weixinqun
    【第1366期】如何精确统计页面停留时长 https://techblog.toutiao.com/2018/06/05/ru-he-jing-que-tong-ji-ye-mian-ting-liu-shi-chang/
    【第1368期】视口相关单位的应用 http://wuyuying.com/blog/archives/css-in-depth-viewport-relative-units/
    【第1369期】简单聊聊 GZIP 的压缩原理与日常应用 https://github.com/rccoder/blog/issues/32
    【第1370期】 验证码前端性能分析及优化实践 https://mp.weixin.qq.com/s/hF7vRXFeJDyOwle3fizSVQ
    【第1375期】JavaScript 引擎基础:原型优化 https://hijiangtao.github.io/2018/08/21/Prototypes/
    【第1377期】为什么我们应该关注下 PWA? https://mp.weixin.qq.com/s/6b479imf6UdIo1H7AC2PTw
    【第1378期】 一口(很长的)气了解 Babel https://github.com/easonyq/easonyq.github.io/blob/master/学习记录/others/babel.md
    【第1379期】鸽子传信解释 HTTPS https://blog.hhking.cn/2018/08/25/https-explained-with-carrier-pigeons/
    【第1383期】 微信小程序架构原理 http://eux.baidu.com//blog/fe/微信小程序架构原理
    近两万字小程序攻略发布了(超精华) https://juejin.im/post/5b8fd1416fb9a05cf3710690
    【第1396期】immer.js 简介及源码简析 https://zhangzhao.name/posts/immer-immutable/
    【第1394期】JavaScript 2018 中即将迎来的新功能 https://github.com/xitu/gold-miner/blob/master/TODO1/whats-coming-up-in-javascript-2018-async-generators-better-regex.md
    精准操控的滚动体验,浅谈新标准 Scroll Snap https://mp.weixin.qq.com/s/UP6ib3B39OOsEd-X9ZJ8UQ
    【第1397期】如何在 JavaScript 中更好地使用数组 https://juejin.im/post/5b8d0a74f265da431d0e7ec0
    【第1398期】一文读懂前端缓存 https://github.com/easonyq/easonyq.github.io/blob/master/学习记录/others/cache.md
    【第1421期】2018年如何写一个现代的JavaScript库 https://yanhaijing.com/javascript/2018/08/17/2020-js-lib/
    【第1426期】代码优化策略 — Idle Until Urgent https://www.yuque.com/es2049/blog/pbhvb1
    【第1431期】图解浏览器的基本工作原理 https://zhuanlan.zhihu.com/p/47407398
    【第1434期】渔人和Rxjs的故事 https://github.com/jackiewillen/blog/issues/1
    【第1436期】利用交叉观察器解锁懒加载新姿势 https://mp.weixin.qq.com/s/oOV4zmf0kku044Zhm3xP6Q
    【第1437期】深拷贝的终极探索 https://yanhaijing.com/javascript/2018/10/10/clone-deep/
    【第1438期】如何让你的网页“看起来”展现地更快 —— 骨架屏二三事 https://mp.weixin.qq.com/s/1v6MqN97xV0zyCph7pl5IA
    【第1441期】 Lighthouse的使用与Google的移动端最佳实践 http://www.rennaiqian.com/2018/02/10/lighthouse/
    每个Web开发者都应该知道的SOLID原则 https://mp.weixin.qq.com/s/GFABaqzGKloPDZZu-Tc1jQ
    SVG vs Image, SVG vs Iconfont https://aotu.io/notes/2018/11/23/SVG_vs_Image_vs_iconfont/index.html
    【第1460期】JavaScript 基础:Babel 转译 class 过程窥探 https://zhuanlan.zhihu.com/p/50113848
    【第1455期】新一代页面生命周期API:来自Chrome官方博客的介绍 https://mp.weixin.qq.com/s/sxbs2W8IkURo-agamUDivw
    【第1454期】JS箭头函数三连问:为何用、怎么用、何时用 https://juejin.im/post/5be599d4e51d451d23633ec8
    【第1465期】内存管理速成教程 https://zhaozhiming.github.io/blog/2017/06/20/a-crash-course-in-memory-management-zh/
    【第1466期】通俗漫画介绍 ArrayBuffers 和 SharedArrayBuffers https://zhaozhiming.github.io/blog/2017/06/20/a-cartoon-intro-to-arraybuffers-and-sharedarraybuffers-zh/
    【第1471期】AST抽象语法树——最基础的javascript重点知识 https://segmentfault.com/a/1190000016231512
    【第1473期】用代码做设计 https://zhuanlan.zhihu.com/p/50414690
    【第1474期】HTTP/3 要点 https://www.oschina.net/translate/some-notes-about-http3
    【第1475期】企鹅辅导课程详情页毫秒开的秘密 - PWA 直出 https://mp.weixin.qq.com/s/A6B7lyo_VrElJAiHNctDkA
    【第1477期】想写好前端,先练好内功 https://www.yuque.com/es2049/blog/al62bl
    【第1479期】2018 年前端开发回顾 https://juejin.im/post/5c13760ee51d4545ca722d45
    【第1481期】函数式编程:抽象与组合 https://juejin.im/post/5bfb989be51d45033627989a
    【深度长文】JavaScript数组所有API全解密 http://louiszhai.github.io/2017/04/28/array/
    深谈require和import https://blog.csdn.net/qq_28702545/article/details/54892562
    【第1489期】关于JavaScript, NPM官方发布了2018年的回顾以及2019年的预测 https://segmentfault.com/a/1190000017479339
    【第1491期】如何编写 Typescript 声明文件 https://juejin.im/post/5bc406795188255c451ed3b3
    【第1495期】 ESLint 工作原理探讨 https://zhuanlan.zhihu.com/p/53680918
    【第1499期】d.ts http://blog.ayqy.net/
    【第1500期】其实你并不懂 Unicode https://zhuanlan.zhihu.com/p/53714077
    【第1505期】谈谈代理 https://github.com/rainjay/blog/issues/9
    你不知道的“隐身斗篷” https://mp.weixin.qq.com/s/M_8wUHSnaHCEr7v8ic2N0Q
    【第1506期】JavaScript工程项目的一系列最佳实践策略 https://github.com/elsewhencode/project-guidelines/blob/master/README-zh.md
    【第1508期】深入浅出 Javascript Decorators 和 AOP 编程 https://github.com/rainjay/blog/issues/5
    【第1510期】动效不该难 https://www.zcool.com.cn/article/ZODI5NjYw.html

    第十一章 前端各种库:

    标题 链接
    【第1162期】2018 要学习的优秀 JavaScript 库与知识 http://mp.weixin.qq.com/s/qyyDZkfjy57njuncq3_i1w
    2018年值得关注的10大JavaScript动画库 https://www.zcfy.cc/article/10-javascript-animation-libraries-to-follow-in-2018
    【第1300期】多端统一开发框架 - Taro https://aotu.io/notes/2018/06/07/Taro/
    【本周项目】7.14-7.20 https://mp.weixin.qq.com/s/tZzPbTgYaLEGZ7svUfPwxg
    【本周项目】7.21-7.27 https://mp.weixin.qq.com/s/Mu8e1YeSI2vNSMcEelzyyg
    【本周项目】7.28-8.3 https://mp.weixin.qq.com/s/zLD3K_51YF-MKFguABHNQg
    【本周项目】8.4-8.10 https://mp.weixin.qq.com/s/QN1ZmIegSC9CmyKQ6dsQCg
    【本周项目】8.11-8.17 https://mp.weixin.qq.com/s/nvVx8TSkiS8YjdI_JXQtIw
    【本周项目】8.18-8.24 https://mp.weixin.qq.com/s/pMwoUgQORnA065hB_5v6Tw
    【本周项目】8.25-8.31 https://mp.weixin.qq.com/s/XEuq8z7U45J9BBYFx4vZPQ
    【本周项目】9.1-9.7 https://mp.weixin.qq.com/s/akeo02ESHHf2c7PNQl_FPA
    【本周项目】9.8-9.14 https://mp.weixin.qq.com/s/3TsCzbzixViOlgo8E08S-A
    【本周项目】9.15-9.21 https://mp.weixin.qq.com/s/23j_BPK7V89MQQnD1d0ZOA
    【本周项目】9.22-9.28 https://mp.weixin.qq.com/s/VB5qp3W_hGW1cTFq2d9nKA
    【本周项目】9.29-10.5 https://mp.weixin.qq.com/s/OOrXznxMzOd0AtIWwN_Ulw
    【本周项目】10.6-10.12 https://mp.weixin.qq.com/s/VUWwNiBIjnaBB9fFI-YStA
    【本周项目】10.13-10.19 https://mp.weixin.qq.com/s/dFybQOA6nzRUMCg1Yfebxw
    【本周项目】10.20-10.26 https://mp.weixin.qq.com/s/_EPE47lAwXeyndqtSQLS3Q
    【本周项目】10.27-11.2 https://mp.weixin.qq.com/s/L6td-hfqhu-VkSaK9Nfe-g
    【本周项目】11.3-11.9 https://mp.weixin.qq.com/s/anlpNjimjJdmfQKwtsdUgg
    【本周项目】11.10-11.16 https://mp.weixin.qq.com/s/Pfanz8lOV0mDZWatX5Nbxw
    【本周项目】11.17-11.23 https://mp.weixin.qq.com/s/YpKHCmsu-Rwr0W-8gb593g
    【本周项目】11.24-11.30 https://mp.weixin.qq.com/s/AP70ESKZy7sGQqbBdQ1r8Q
    【本周项目】12.1-12.7 https://mp.weixin.qq.com/s/grNRoPGa2T2WJt4DJ0c1ag
    【本周项目】12.08-12.14 https://mp.weixin.qq.com/s/In4cVH5GEzsJr-kzkqgnRQ
    【本周项目】12.15-12.21 https://mp.weixin.qq.com/s/_SpEU77jecDBCjes6QC2OQ
    【本周项目】12.22-12.28 https://mp.weixin.qq.com/s/n6-CN-Whx8BQ-cGDqiHj4w
    【本周项目】12.29-1.4 https://mp.weixin.qq.com/s/_EIdo1VOytML4qRWhGSImA
    【本周项目】1.12-1.18 https://mp.weixin.qq.com/s/OX38ivfViUQOty1GcdShaA

    第十二章 面试相关:

    标题 链接
    50道 CSS 基础面试题(附答案) https://juejin.im/entry/5ad2d3bff265da237a4d75dd?utm_medium=fe&utm_source=weixinqun
    前端笔试题面试题记录 https://juejin.im/post/5aad40e4f265da237f1e12ed?utm_medium=fe&utm_source=weixinqun
    分享收集的一大波前端面试题和答案 https://juejin.im/entry/5a9d0f05f265da239b40eb7c?utm_medium=fe&utm_source=weixinqun
    前端基础面试题(JS部分) https://juejin.im/entry/598c003c6fb9a03c367d054e
    2018前端面试总结js部分【中】 https://juejin.im/post/5b2f4eb9e51d4558cc35c289?utm_medium=fe&utm_source=weixinqun
    2018年6月前端面试经历(中) https://juejin.im/post/5b3b70ea6fb9a04fe91a5039?utm_medium=fe&utm_source=weixinqun
    近百高频知识点,十万余字,地表最强前端面试图谱了解下 https://yuchengkai.cn/docs/zh/frontend/

    第十三章 其他文章链接:

    标题 链接
    【第1170期】如何看待员工跳槽 https://mp.weixin.qq.com/s/r8f_p0nSbV2ZAXI7xLeCOg
    超火的漫画线稿上色AI——style2paints 2.0出新版了 http://blog.csdn.net/yH0VLDe8VG8ep9VGe/article/details/78933351
    掘金翻译计划,包含各种英文版精彩的技术文章的中文译文(热门) https://github.com/xitu/gold-miner
    首个围棋10段诞生!是10连胜柯洁的国产阿法狗(腾讯绝艺) http://mp.weixin.qq.com/s/qGnh4hShKHI992Bfh15hOQ
    关于2018年 九大改变世界的技术趋势 http://blog.csdn.net/lu_embedded/article/details/78779266
    揭秘前端字符的戏精之路 http://mp.weixin.qq.com/s/jTSws4W-PDxVgDvePvMO2g
    谷歌大脑2017总结(Jeff Dean执笔,干货满满,值得收藏) http://mp.weixin.qq.com/s/a0HFS95ZXiZXNh5qXbmUBA
    谷歌大脑2017总结 第二弹(Jeff Dean执笔,干货满满,值得收藏) https://mp.weixin.qq.com/s/RazWCsBY_dF1VR0JTYIjsw
    Git使用入门 https://mp.weixin.qq.com/s/xoyQ4TzVKLQb2VjZJLUqFQ
    15分钟成为 GIT 专家 https://www.jianshu.com/p/c221f99f0bfd
    【第1169期】如何有效地做算法题 http://mp.weixin.qq.com/s/Zf__tGoVB_iONKL0lTg1Ew
    DeepMind提出可微分逻辑编程,结合深度学习与符号程序优点 http://mp.weixin.qq.com/s/lPAcNayqcz1sh8xNVklzEA
    一张图道尽程序员的出路 https://juejin.im/entry/5a9b51d06fb9a028c71dea51
    【第1229期】程序员如何在技术浪潮的更迭中保持较高的成长速度 ? https://halfrost.com/halfrost_2017/
    【第1238期】如何做一名有能力的专业人士 https://zcfy.cc/article/my-manager-who-i-loved-recently-left-my-company-i-summarized-some-of-my-notes-from-our-many-conversations-here-they-are-i-wish-someone-wrote-this-for-me-when-i-started-startups
    【第1305期】Hubble 见证 Vue 与 React 突破 10 万 GitHub Stars! https://seven.ooo/hubble-congrats-react-and-vue-100k-stars/#Hubble
    【第1321期】SVG滤镜对图片调色 https://github.com/sundway/blog/issues/14
    【第1324期】如何像个程序员一样思考 https://zhuanlan.zhihu.com/p/29954974
    作为一个前端,可以如何机智地弄坏一台电脑? https://juejin.im/entry/5b57f52be51d45190d555468
    展开全文
  • webpack4-------缓存

    2019-05-18 16:40:52
    本文为手摸手使用 webpack4,主要分为两部分: 怎么合理的运用浏览器缓存 怎么构建可靠的持久化缓存 默认分包策略 webpack 4 最大的改动就是废除了CommonsChunkPlugin引入了optimization.splitChunks。 webpack ...

    本文为手摸手使用 webpack4,主要分为两部分:

    • 怎么合理的运用浏览器缓存
    • 怎么构建可靠的持久化缓存

    默认分包策略

    webpack 4 最大的改动就是废除了 CommonsChunkPlugin 引入了 optimization.splitChunks

    webpack 4 的Code Splitting 它最大的特点就是配置简单,如果你的 mode 是 production,那么 webpack 4 就会自动开启 Code Splitting

    以下内容都会以 vue-element-admin 为例子。 在线
    bundle-report

    如上图所示,在没配置任何东西的情况下,webpack 4 就智能的帮你做了代码分包。入口文件依赖的文件都被打包进了app.js,那些大于 30kb 的第三方包,如:echartsxlsxdropzone等都被单独打包成了一个个独立 bundle。

    它内置的代码分割策略是这样的:

    • 新的 chunk 是否被共享或者是来自 node_modules 的模块
    • 新的 chunk 体积在压缩之前是否大于 30kb
    • 按需加载 chunk 的并发请求数量小于等于 5 个
    • 页面初始加载时的并发请求数量小于等于 3 个

    但有一些小的组件,如上图:vue-count-to 在未压缩的情况下只有 5kb,虽然它被两个页面共用了,但 webpack 4 默认的情况下还是会将它和那些懒加载的页面代码打包到一起,并不会单独将它拆成一个独立的 bundle。(虽然被共用了,但因为体积没有大于 30kb)

    你可能会觉得 webpack 默认策略是不是有问题,我一个组件被多个页面,你每个页面都将这个组件打包进去了,岂不是会重复打包很多次这个组件?就拿vue-count-to来举例,你可以把共用两次以上的组件或者代码单独抽出来打包成一个 bundle,但你不要忘了vue-count-to未压缩的情况下就只有 5kb,gizp 压缩完可能只有 1.5kb 左右,你为了共用这 1.5kb 的代码,却要额外花费一次 http 请求的时间损耗,得不偿失。我个人认为 webpack 目前默认的打包规则是一个比较合理的策略了。

    但有些场景下这些规则可能就显得不怎么合理了。比如我有一个管理后台,它大部分的页面都是表单和 Table,我使用了一个第三方 table 组件,几乎后台每个页面都需要它,但它的体积也就 15kb,不具备单独拆包的标准,它就这样被打包到每个页面的 bundle 中了,这就很浪费资源了。这种情况下建议把大部分页面能共用的组件单独抽出来,合并成一个component-vendor.js的包(后面会介绍)。

    优化没有银弹,不同的业务,优化的侧重点是不同的。个人认为 webpack 4 默认拆包已经做得不错了,对于大部分简单的应用来说已经够用了。但作为一个通用打包工具,它是不可能满足所有的业务形态和场景的,所以接下来就需要我们自己稍微做一些优化了。

    优化分包策略

    就拿 vue-element-admin 来说,它是一个基于 Element-UI 的管理后台,所以它会用到如 echartsxlsxdropzone等各种第三方插件,同时又由于是管理后台,所以本身自己也会写很多共用组件,比如各种封装好的搜索查询组件,共用的业务模块等等,如果按照默认的拆包规则,结果就不怎么完美了。

    如第一张图所示,由于element-uientry入口文件中被引入并且被大量页面共用,所以它默认会被打包到 app.js 之中。这样做是不合理的,因为app.js里还含有你的router 路由声明store 全局状态utils 公共函数icons 图标等等这些全局共用的东西。

    但除了element-ui,其它这些又是平时开发中经常会修改的东西,比如我新增了一个全局功能函数,utils文件就会发生改变,或者我修改一个路由的 path,router文件就变了,这些都会导致app.js的 hash 发生改变:app.1.js => app.2.js。但由于 element-ui和 vue/react等也被打包在其中,虽然你没改变它们,但它们的缓存也会随着app.xxx.js变化而失效了,这就非常不合理的。所以我们需要自己来优化一下缓存策略。

    我们现在的策略是按照体积大小、共用率、更新频率重新划分我们的包,使其尽可能的利用浏览器缓存。

    我们根据上表来重新划分我们的代码就变成了这样。

    • 基础类库 chunk-libs

    它是构成我们项目必不可少的一些基础类库,比如 vue+vue-router+vuex+axios 这种标准的全家桶,它们的升级频率都不高,但每个页面都需要它们。(一些全局被共用的,体积不大的第三方库也可以放在其中:比如 nprogress、js-cookie、clipboard 等)

    • UI 组件库

    理论上 UI 组件库也可以放入 libs 中,但这里单独拿出来的原因是: 它实在是比较大,不管是 Element-UI还是Ant Design gizp 压缩完都可能要 200kb 左右,它可能比 libs 里面所有的库加起来还要大不少,而且 UI 组件库的更新频率也相对的比 libs 要更高一点。我们不时的会升级 UI 组件库来解决一些现有的 bugs 或使用它的一些新功能。所以建议将 UI 组件库也单独拆成一个包。

    • 自定义组件/函数 chunk-commons

    这里的 commons 主要分为 必要非必要

    必要组件是指那些项目里必须加载它们才能正常运行的组件或者函数。比如你的路由表、全局 state、全局侧边栏/Header/Footer 等组件、自定义 Svg 图标等等。这些其实就是你在入口文件中依赖的东西,它们都会默认打包到app.js中。

    非必要组件是指被大部分页面使用,但在入口文件 entry 中未被引入的模块。比如:一个管理后台,你封装了很多 select 或者 table 组件,由于它们的体积不会很大,它们都会被默认打包到到每一个懒加载页面的 chunk 中,这样会造成不少的浪费。你有十个页面引用了它,就会包重复打包十次。所以应该将那些被大量共用的组件单独打包成chunk-commons

    不过还是要结合具体情况来看。一般情况下,你也可以将那些非必要组件函数也在入口文件 entry 中引入,和必要组件函数一同打包到app.js之中也是没什么问题的。

    • 低频组件

    低频组件和上面的共用组件 chunk-commons 最大的区别是,它们只会在一些特定业务场景下使用,比如富文本编辑器、js-xlsx前端 excel 处理库等。一般这些库都是第三方的且大于 30kb,所以 webpack 4 会默认打包成一个独立的 bundle。也无需特别处理。小于 30kb 的情况下会被打包到具体使用它的页面 bundle 中。

    • 业务代码

    这部分就是我们平时经常写的业务代码。一般都是按照页面的划分来打包,比如在 vue 中,使用路由懒加载的方式加载页面 component: () => import('./Foo.vue') webpack 默认会将它打包成一个独立的 bundle。

    完整配置代码:

    
     
    1. splitChunks: {

    2. chunks: "all",

    3. cacheGroups: {

    4. libs: {

    5. name: "chunk-libs",

    6. test: /[\\/]node_modules[\\/]/,

    7. priority: 10,

    8. chunks: "initial" // 只打包初始时依赖的第三方

    9. },

    10. elementUI: {

    11. name: "chunk-elementUI", // 单独将 elementUI 拆包

    12. priority: 20, // 权重要大于 libs 和 app 不然会被打包进 libs 或者 app

    13. test: /[\\/]node_modules[\\/]element-ui[\\/]/

    14. },

    15. commons: {

    16. name: "chunk-commons",

    17. test: resolve("src/components"), // 可自定义拓展你的规则

    18. minChunks: 2, // 最小共用次数

    19. priority: 5,

    20. reuseExistingChunk: true

    21. }

    22. }

    23. };

    上图就是最终拆包结果概要,你可以 点我点我点我,在线查看拆包结果。

    这样就能尽可能的利用了浏览器缓存。当然这种优化还是需要因项目而异的。比如上图中的共用组件 chunk-commons,可能打包出来发现特别大,包含了很多组件,但又不是每一个页面或者大部分页面需要它。很可能出现这种状况:A 页面只需要 chunk-commons里面的 A 组件,
    但却要下载整个chunk-commons.js,这时候就需要考虑一下,目前的拆包策略是否合理,是否还需要chunk-commons?还是将这些组件打包到各自的 bundle 中?还是调整一下 minChunks: 2( 最小共用次数)?或者修改一下它的拆包规则?

    
     
    1. // 或者你可以把策略改为只提取那些你注册在全局的组件。

    2.  
    3. - test: resolve("src/components")

    4. + test: resolve("src/components/global_components") //你注册全局组件的目录

    博弈

    其实优化就是一个博弈的过程,是让 a bundle 大一点还是 b? 是让首次加载快一点还是让 cache 的利用率高一点? 但有一点要切记,拆包的时候不要过分的追求颗粒化,什么都单独的打成一个 bundle,不然你一个页面可能需要加载十几个.js文件,如果你还不是HTTP/2的情况下,请求的阻塞还是很明显的(受限于浏览器并发请求数)。所以还是那句话资源的加载策略并没什么完全的方案,都需要结合自己的项目找到最合适的拆包策略。

    比如支持HTTP/2的情况下,你可以使用 webpack4.15.0 新增的 maxSize,它能将你的chunkminSize的范围内更加合理的拆分,这样可以更好地利用HTTP/2来进行长缓存(在HTTP/2的情况下,缓存策略就和之前又不太一样了)。

    Long term caching

    持久化缓存其实是一个老生常谈的问题,前端发展到现在,缓存方案已经很成熟了。简单原理:

    • 针对 html 文件:不开启缓存,把 html 放到自己的服务器上,关闭服务器的缓存
    • 针对静态的 js,css,图片等文件:开启 cdn 和缓存,将静态资源上传到 cdn 服务商,我们可以对资源开启长期缓存,因为每个资源的路径都是独一无二的,所以不会导致资源被覆盖,保证线上用户访问的稳定性。
    • 每次发布更新的时候,先将静态资源(js, css, img) 传到 cdn 服务上,然后再上传 html 文件,这样既保证了老用户能否正常访问,又能让新用户看到新的页面。

    相关文章 大公司里怎样开发和部署前端代码?

    所以我们现在要做的就是要让 webpack 给静态资源生产一个可靠的 hash,让它能自动在合适的时候更新资源的 hash,
    并且保证 hash 值的唯一性,即为每个打包后的资源生成一个独一无二的 hash 值,只要打包内容不一样,那么 hash 值就不一样。

    其实 webpack 4 在持久化缓存这一块已经做得非常的不错了,但还是有一些欠缺,下面我们将要从这几个方面讨论这个问题。

    • RuntimeChunk(manifest)
    • Module vs Chunk
    • HashedModuleIdsPlugin
    • NamedChunksPlugin

    RuntimeChunk(manifest)

    webpack 4 提供了 runtimeChunk 能让我们方便的提取 manifest,以前我们需要这样配置

    
     
    1. new webpack.optimize.CommonsChunkPlugin({

    2. name: "manifest",

    3. minChunks: Infinity

    4. });

    现在只要一行配置就可以了

    
     
    1. {

    2. runtimeChunk: true;

    3. }

    它的作用是将包含chunks 映射关系的 list单独从 app.js里提取出来,因为每一个 chunk 的 id 基本都是基于内容 hash 出来的,所以你每次改动都会影响它,如果不将它提取出来的话,等于app.js每次都会改变。缓存就失效了。

    单独抽离 runtimeChunk 之后,每次打包都会生成一个runtimeChunk.xxx.js。(默认叫这名字,可自行修改)

    优化

    其实我们发现打包生成的 runtime.js非常的小,gzip 之后一般只有几 kb,但这个文件又经常会改变,我们每次都需要重新请求它,它的 http 耗时远大于它的执行时间了,所以建议不要将它单独拆包,而是将它内联到我们的 index.html 之中(index.html 本来每次打包都会变)。

    这里我选用了 script-ext-html-webpack-plugin,主要是因为它还支持preload和 prefetch,正好需要就不想再多引用一个插件了,你完全可以使用 inline-manifest-webpack-plugin或者 assets-webpack-plugin等来实现相同的效果。

    
     
    1. const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");

    2.  
    3. // 注意一定要在HtmlWebpackPlugin之后引用

    4. // inline 的name 和你 runtimeChunk 的 name保持一致

    5. new ScriptExtHtmlWebpackPlugin({

    6. //`runtime` must same as runtimeChunk name. default is `runtime`

    7. inline: /runtime\..*\.js$/

    8. });

    Module vs Chunk

    我们经常看到xxxModuleIdsPluginxxxChunksPlugin,所以在 webpack 中 module和 chunk到底是一个怎么样的关系呢?

    • chunk: 是指代码中引用的文件(如:js、css、图片等)会根据配置合并为一个或多个包,我们称一个包为 chunk。
    • module: 是指将代码按照功能拆分,分解成离散功能块。拆分后的代码块就叫做 module。可以简单的理解为一个 export/import 就是一个 module。

    每个 chunk 包可含多个 module。 比如:

    
     
    1. //9.xxxxxxxxx.js

    2.  
    3. //chunk id为 9 ,包含了Vc2m和JFUb两个module

    4. (window.webpackJsonp = window.webpackJsonp || []).push([

    5. [9],

    6. {

    7. Vc2m: function(e, t, l) {},

    8. JFUb: function(e, t, l) {}

    9. }

    10. ]);

    一个module还能跨chunk引用另一个module,比如我想在app.js里面需要引用 chunkId13的模块2700可以这样引用:

    return n.e(13).then(n.bind(null, "27OO"));

    HashedModuleIdsPlugin

    了解了 modulechunk之后,我们来研究一下 moduleId

    首先要确定你的 filename 配置的是chunkhash(它与 hash 的区别可以看上篇文章)。

    
     
    1. output: {

    2. path: path.join(__dirname, 'dist'),

    3. filename: '[name].[chunkhash].js',

    4. }

    我们在入口文件中随便引入一个新文件test.js

    
     
    1. //main.js

    2. import "./test";

    3.  
    4. //test.js

    5. console.log("apple");

    我们运行npm run build,发现了一件奇怪的事情,我只是多引入了一个文件,但发现有十几个文件发生了变化。这是为什么呢?

    我们随便挑一个文件 diff 一下,发现两个文件只有 module id 的不同。

    这是因为:
    webpack 内部维护了一个自增的 id,每个 module 都有一个 id。所以当增加或者删除 module 的时候,id 就会变化,导致其它文件虽然没有变化,但由于 id 被强占,只能自增或者自减,导致整个 id 的顺序都错乱了。

    虽然我们使用 [chunkhash] 作为输出名,但仍然是不够的。
    因为 chunk 内部的每个 module 都有一个 id,webpack 默认使用递增的数字作为 moduleId
    如果引入了一个新文件或删掉一个文件,都可能会导致其它文件的 moduleId 发生改变,
    那这样缓存失效了。如:

    本来是一个按序的 moduleId list,这时候我插入一个orange模块,插在第三个位置,这样就会导致它之后的所以 module id 都依次加了 1。

    这到了原因,解决方案就很简单了。我们就不要使用一个自增的 id 就好了,这里我们使用HashedModuleIdsPlugin

    或者使用optimization.moduleIds v4.16.0 新发布,文档还没有。查看 源码发现它有naturalnamedhashedsizetotal-size。这里我们设置为optimization.moduleIds='hash'等于HashedModuleIdsPlugin。源码了也写了webpack5会优化这部分代码。

    它的原理是使用文件路径的作为 id,并将它 hash 之后作为 moduleId。

    使用了 HashedModuleIdsPlugin`,我们再对比一下发现 module id 不再是简单的 id 了,而是一个四位 hash 过得字符串(不一定都是四位的,如果重复的情况下会增加位数,保证唯一性 源码)。
    这样就固定住了 module id 了。

    NamedModulesPlugin 和 HashedModuleIdsPlugin 原理是相同的,将文件路径作为 id,只不过没有把路径 hash 而已,适用于开发环境方便调试。不建议在生产环境配置,因为这样不仅会增加文件的大小(路径一般偶读比较长),更重要的是为暴露你的文件路径。

    NamedChunkPlugin

    我们在固定了 module id 之后同理也需要固定一下 chunk id,不然我们增加 chunk 或者减少 chunk 的时候会和 module id 一样,都可能会导致 chunk 的顺序发生错乱,从而让 chunk 的缓存都失效。

    作者也意识到了这个问题,提供了一个叫NamedChunkPlugin的插件,但在使用路由懒加载的情况下,你会发现NamedChunkPlugin并没什么用。
    供了一个线上demo,可以自行测一下。这里提就直接贴一下结果:

    产生的原因前面也讲了,使用自增 id 的情况下是不能保证你新添加或删除 chunk 的位置的,一旦它改变了,这个顺序就错乱了,就需要重排,就会导致它之后的所有 id 都发生改变了。

    接着我们 查看源码 还发现它只对有 name 的 chunk 才奏效!所以我们那些异步懒加载的页面都是无效的。这启不是坑爹!我们迭代业务肯定会不断的添加删除页面,这岂不是每新增一个页面都会让之前的缓存都失效?那我们之前还费这么大力优化什么拆包呢?

    其实这是一个古老的问题了 相关 issue: Vendor chunkhash changes when app code changes 早在 2015 年就有人提了这个问题,这个问题也一直讨论至今,'网友们'也提供了各种奇淫巧技,不过大部分随着 webpack 的迭代已经不适用或者是修复了。

    这里我就结合一下 timse(webpack 第二多贡献)写的持久缓存的文章(在 medium 上需要翻墙)
    总结一下目前能解决这个问题的三种方案。

    目前解决方案有三种

    • records
    • webpackChunkName
    • 自定义 nameResolver

    webpack records

    很多人可能连这个配置项都没有注意过,不过早在 2015 年就已经被设计出来让你更好的利用 cache。官方文档

    要使用它配置也很简单:

    recordsPath: path.join(__dirname, "records.json");

    对,只要这一行代码就能开启这个选项,并打包的时候会自动生成一个 JSON 文件。它含有 webpack 的 records 记录 - 即「用于存储跨多次构建(across multiple builds)的模块标识符」的数据片段。可以使用此文件来跟踪在每次构建之间的模块变化。

    大白话就是:等于每次构建都是基于上次构建的基础上进行的。它会先读取你上次的 chunk 和 module id 的信息之后再进行打包。所以这时候你再添加或者删除 chunk,并不会导致之前所说的乱序了。

    简单看一下构建出来的 JSON 长啥样。

    
     
    1. {

    2. "modules": {

    3. "byIdentifier": {

    4. "demo/vendor.js": 0,

    5. "demo/vendor-two.js": 1,

    6. "demo/index.js": 2,

    7. ....

    8. },

    9. "usedIds": {

    10. "0": 0,

    11. "1": 1,

    12. "2": 2,

    13. ...

    14. }

    15. },

    16. "chunks": {

    17. "byName": {

    18. "vendor-two": 0,

    19. "vendor": 1,

    20. "entry": 2,

    21. "runtime": 3

    22. },

    23. "byBlocks": {},

    24. "usedIds": [

    25. 0,

    26. 1,

    27. 2

    28. }

    29. }

    我们和之前一样,在路由里面添加一个懒加载的页面,打包对比后发现 id 并不会像之前那样按照遍历到的顺序插入了,而是基于之前的 id 依次累加了。一般新增页面都会在末尾填写一个新 id,删除 chunk 的话,会将原来代表 chunk 的 id,保留,但不会再使用。

    但这个方案不被大家知晓主要原因就是维护这个records.json比较麻烦。如果你是在本地打包运行webpack的话,你只要将records.json当做普通文件上传到githubgitlab或其它版本控制仓库。

    但现在一般公司都会将打包放在 CI里面,用docker打包,这时候这份records.json存在哪里就是一个问题了。它不仅需要每次打包之前先读取你这份 json,打包完之后它还需要再更新这份 json,并且还要找地方存贮,为了下次构建再使用。你可以存在 git 中或者找一个服务器存,但存在什么地其它方都感觉怪怪的。

    如果你使用 Circle CI可以使用它的store_artifacts,相关教程

    本人在使用了之后还是放弃了这个方案,使用成本略高。前端打包应该更加的纯粹,不需要依赖太多其它的东西。

    webpackChunkName

    在 webpack2.4.0 版本之后可以自定义异步 chunk 的名字了,例如:

    import(/* webpackChunkName: "my-chunk-name" */ "module");

    我们在结合 vue 的懒加载可以这样写。

    
     
    1. {

    2. path: '/test',

    3. component: () => import(/* webpackChunkName: "test" */ '@/views/test')

    4. },

    打包之后就生成了名为 test的 chunk 文件。

    chunk 有了 name 之后就可以解决NamedChunksPlugin没有 name 的情况下的 bug 了。查看打包后的代码我们发现 chunkId 就不再是一个简单的自增 id 了。

    不过这种写法还是有弊端的,首先你需要手动编写每一个 chunk 的 name,同时还需要保证它的唯一性,当页面一多,维护起来还是很麻烦的。这就违背了程序员的原则:能偷懒就偷懒。

    所以有什么办法可以自动生成一个 name 给 chunk 么 ?查看 webpack 源码我们发现了NamedChunksPlugin其实可以自定义 nameResolver 的。

    自定义 nameResolver

    NamedChunksPlugin支持自己写 nameResolver 的规则的。但目前大部分相关的文章里的自定义函数是不适合 webpack4 ,而且在结合 vue 的情况下还会报错。

    社区旧方案:

    
     
    1. new webpack.NamedChunksPlugin(chunk => {

    2. if (chunk.name) {

    3. return chunk.name;

    4. }

    5. return chunk.modules.map(m => path.relative(m.context, m.request)).join("_");

    6. });

    适配 webpack4 和 vue 的新实现方案:

    
     
    1. new webpack.NamedChunksPlugin(chunk => {

    2. if (chunk.name) {

    3. return chunk.name;

    4. }

    5. return Array.from(chunk.modulesIterable, m => m.id).join("_");

    6. });

    当然这个方案还是有一些弊端的因为 id 会可能很长,如果一个 chunk 依赖了很多个 module 的话,id 可能有几十位,所以我们还需要缩短一下它的长度。我们首先将拼接起来的 id hash 以下,而且要保证 hash 的结果位数也能太长,浪费字节,但太短又容易发生碰撞,所以最后我们我们选择 4 位长度,并且手动用 Set 做一下碰撞校验,发生碰撞的情况下位数加 1,直到碰撞为止。详细代码如下:

    
     
    1. const seen = new Set();

    2. const nameLength = 4;

    3.  
    4. new webpack.NamedChunksPlugin(chunk => {

    5. if (chunk.name) {

    6. return chunk.name;

    7. }

    8. const modules = Array.from(chunk.modulesIterable);

    9. if (modules.length > 1) {

    10. const hash = require("hash-sum");

    11. const joinedHash = hash(modules.map(m => m.id).join("_"));

    12. let len = nameLength;

    13. while (seen.has(joinedHash.substr(0, len))) len++;

    14. seen.add(joinedHash.substr(0, len));

    15. return `chunk-${joinedHash.substr(0, len)}`;

    16. } else {

    17. return modules[0].id;

    18. }

    19. });

    我给 vue-cli 官方也提了一个相关
    issue尤雨溪最后也采纳了这个方案。
    所以如果你现在下载最新 vue-cli@3上面啰嗦了半天的东西,其实都已经默认配置好了(但作者本人为了找到这个 hack 方法整整花了两天时间 o(╥﹏╥)o)。

    目前测试了一段时间没发现有什么问题。不过有一点不是很理解,不知道 webpack 出于什么样的原因,官方一直没有修复这个问题?可能是在等 webpack5 的时候放大招吧。

    总结

    拆包策略:

    • 基础类库 chunk-libs
    • UI 组件库 chunk-elementUI
    • 自定义共用组件/函数 chunk-commons
    • 低频组件 chunk-eachrts/chunk-xlsx
    • 业务代码 lazy-loading xxxx.js

    持久化缓存:

    • 使用 runtimeChunk 提取 manifest,使用 script-ext-html-webpack-plugin等插件内联到index.html减少请求
    • 使用 HashedModuleIdsPlugin 固定 moduleId
    • 使用 NamedChunkPlugin结合自定义 nameResolver 来固定 chunkId

    上述说的问题大部分在 webpack 官方文档都没明确指出,唯一可以参考的就是这份 cache 文档,在刚更新 webpack4 的时候,我以为官方已经将 id 不能固定的问题解决了,但现实是残酷的,结果并不理想。不过作者也在很多的 issue 中说他正在着手优化 long term caching

    We plan to add another way to assign module/chunk ids for long term caching, but this is not ready to be told yet.

    在 webpack 的 issue 和源码中也经常见到 Long term caching will be improved in webpack@5TODO webpack 5 xxxx这样的代码注释。这让我对webpack 5很期待。真心希望webpack 5能真正的解决前面几个问题,并且让它更加的out-of-the-box,更加的简单和智能,就像webpack 4optimization.splitChunks,你基本不用做什么,它就能很好的帮你拆分好bundle包,同时又给你非常的自由发挥空间。

    展望

    Whats next? 官方在这篇文章中展望了一下 webpack5 和讲述了一下未来的计划--持续改进用户体验、提升构建速度和性能,降低使用门槛,完善Persistent Caching等等。同时 webpack 也已经支持 Prefetching/Preloading modules,我相信之后也会有更多的网站会使用这一属性。

    同时 webpack 的团队已经承诺会通过投票的方式来决定一些功能。比如不久前发起的投票。

    大家可以关注 Tobias Koppers 的 twitter 进行投票。

    最后还是期待一下 webpack5 和它之后的发展吧。如果没有 webpack,也就不会有今天的前端。

    其实如一开始就讲的,vue 有vue-cli、react 有creat-react-app,现在新建项目基本都是基于脚手架的,很少有人从零开始写 webpack 配置文件的,而且一般开发中,一般程序员也不需要经常去修改 webpack 的配置。webpack 官方本身也在不断完善默认配置项,相信 webpack 的配置门槛也会越来低多。

    展开全文
  • 和很多小伙伴一样,我在开发Vue项目时也是基于官方vue-cli@2的webpack模版,但随着项目越做越大,依赖的第三方npm包越来越多,构建之后的文件也会越来越大,尤其是vendor.js,甚至会达到2M左右。再加上又是单页应用,...
  • 理解 ES5, ES2015(ES6) 和 TypeScript

    千次阅读 2016-09-30 10:17:56
    理解 ES5, ES2015(ES6) 和 TypeScript
  • webpack常用的插件集合

    2020-09-15 14:26:44
    目录 日常组件使用 对ES6的解析 插件 image: 如何将打包的时候 将图片一并打包 我们需要使用到这个file-loader url-loader 更准确的说是资源的复制,不...Vconsole免安装的cdn版本,进去复制一下你需要的版本 ...
  • 一些小知识

    万次阅读 2017-07-19 13:19:03
    1.你怎么来实现页面设计图,你认为前端应该如何质量完成工作? 一个满屏 品 字布局 如何设计?首先划分成头部、body、脚部 实现效果图是最基本的工作,精确到2px,设计师,产品经理的沟通和项目的参与,做好的页面...
  • 那些年我准备的前端面试题

    万次阅读 2018-07-13 18:13:59
    commonJS相关: (1)在commonJS规范中,require,exports,module,__filename,__dirname都是通过动态编译后添加到模块的头部的,这样就不存在全局变量污染的问题  但是他们传入的require,exports,module都是一个空...
  • 全栈工程师指南

    千次阅读 2016-05-23 18:31:10
    Growth: 全栈增长工程师指南 全栈工程师是未来 技术的革新史软件开发的核心难题:沟通大公司的专家与小公司的全栈全栈工程师的未来:无栈 基础知识篇 工具只是辅助 WebStorm 还是 Sublime?...
  • 二、JS基础 1、javascript的typeof返回哪些数据类型 Object number function boolean underfind 2、例举3种强制类型转换和2种隐式类型转换? 强制(parseInt,parseFloat,number) ...隐式(== – ===) ...
  • 作者:mcuking(杭州个推) ... 笔者在公司用 web 技术开发移动端应用已经有一年多的时间了,开始主要以 vue 技术栈配合 native 为主,目前演进成 vue + react native 技术架构,vue 主要负责开发 OA 业务,比如报销...
  • 那些年我准备的前端面试题集合

    千次阅读 2017-10-19 08:19:58
    浏览器从服务端获取网页后会根据文档的DOCTYPE定义显示网页,如果文档正确定义了DOCTYPE浏览器则会进入标准模式(Standards Mode),否则浏览器会进入怪异模式或混杂模式(Quirks mode)。
  • React 面试题 & 回答

    千次阅读 2019-03-26 17:49:23
    本项目的面试题来源于sudheerj/reactjs-interview-questions这个项目。一时兴起就动起了翻译的念头,由于本人的 React 功力尚浅,翻译的内容难免有误或不妥的地方,望请各位见谅。如果你喜欢这个项目,请 Star,更...
  • 有时候你花大力气去配置 WebPack 使打包体积减少,不如好好优化几张图片,这篇文章就是让你明白如何选择正确的图片,并且让你明白这么多图片格式,在什么场景下使用什么格式。 基本概念...
  • 2016年前端技术观察

    千次阅读 2016-12-14 14:58:31
    前端技术,从最初的刀耕火种到现在的百花齐放、繁花迷眼,可以说达到了一个前所未有的鼎盛时期。当身边的同事张口闭口都是所谓H5的时候,作为一名前端开发者的我心中越来越多的却是迷惑和不解。...
  • 前端面试宝典超

    千次阅读 2017-02-27 08:58:49
    1、你做的页面在哪些流览器测试过?这些浏览器的内核分别是什么? IE: trident内核 ...Opera:以前是presto内核,Opera现改用Google Chrome的Blink内核 Chrome:Blink(基于webkit,Google与Opera Software共
  • 原文链接:面试之前,简历之上:给前端校招同学的简历建议 | AlloyTeam 原文作者:腾讯Web前端团队-TAT.老教授 前言:作为前端面试官老司机,之前分享过我对前端校招面试的一些见解,这次来说下简历。...
  • 一个治愈 JavaScript 疲劳的学习计划

    千次阅读 2016-11-26 12:42:37
    网络埋伏纪事 · 2016-11-12翻译 5706阅读 原文链接  像其他人一样,我最近偶然看到 Jose Aguinaga 的文章《在 ... 2016 年学 JavaScript 是一种什么样的体验》”。...很显然,这篇文章触到了很多人的痛点:我...
1 2 3 4 5 ... 8
收藏数 154
精华内容 61
关键字:

webpack 已采纳 的版本太高