乐趣区

关于webpack:手写一个webpack插件

前言

前端性能优化是一个陈词滥调的话题,对于性能优化的技术文档和书籍都特地多。如果大家想深刻学习前端性能优化相干内容,有以下举荐

  • 雅虎军规 35 条
  • 某东上搜“前端性能优化”,书籍也特地多。
    然而前端性能优化做的所有工作,都和一个灵魂拷问有极大的关系:
    在浏览器输出 url 后,产生了什么?
  • 首先 url 是一个域名,首先它要被解析成 ip 地址。如果你的设施之前拜访过该 url,那么本地可能会缓存 ip 地址。如果没有缓存过,那么就向 dns 服务器进行解析。
  • 依据 IP 地址和默认端口建设 TCP 连贯,三次握手。
  • 浏览器收回 HTTP 申请;
  • 服务器对申请作出响应;此时兴许会有一些 304 之类的返回后果。会波及到 http 缓存机制。
  • 开释 TCP 连贯;
  • 浏览器将该 html 文本并显示内容。

所以咱们能够从这个过程里去思考,服务器如何把文件尽快发送到客户浏览器。

  • 第一点,就是文件尽可能要小,文件尽可能少,发送起来会更快
  • 第二点,就是发送过程,在肯定条件下,缩小文件或申请的个数。
    文件也并不是越少越好,假如文件内容少到极致,内容全副合并到一个文件,那么这个文件会是十分微小的,发送起来也会十分慢。如果文件个数太多,那么浏览器也会限度并发数量,在同一个域名下,浏览器的并发数量是 6 个。这样,尽管每个申请发送的文件很少,然而建设申请的过程也是耗时间的。很多申请都被阻塞了。
    以上是针对第一次拜访网站时可用的计划,然而如果一个网站常常被反复拜访,还有以下办法
  • 第三点,缓存机制 就是之前用到过的文件和内容,如果没有生效,就能够重复使用,不须要再从服务器拿。

以上是我大略能想到的一些思路。如果有不精确的中央,或者有其余好的思路,心愿大家留言帮我补充。

上面咱们进入正题,说一下我在开发我的项目时遇到的一个问题,以及解决思路

开发过程遇到的一个理论问题

在 vue 我的项目打包后,会在我的项目的根目录下生成一个 dist(你也能够扭转该名称)文件夹。一个单页面利用的 dist 文件夹通常蕴含一个 index.html 和 一个 js 文件夹 和一些其余资源文件。

而后咱们把 dist 文件夹公布到服务器上,在浏览器下来拜访咱们的我的项目,刚开始可能会特地慢。关上 chrome 调试工具,查看一下资源加载状况。

太惊人了,一个要害的 js 文件,在 nginx 服务器上曾经做了压缩解决,从 800k 压缩到 270k 左右。然而加载工夫仍然达到了惊人的 22.89s。这个对于任何一个我的项目来说都是不可承受的。

相干 webpack 视频解说:进入学习

初步解决思路

问题曾经出现了。打包后的文件,呈现了特地大的 js,而且就是这个 js 文件阻塞了整个我的项目的加载。
因为最近正在钻研 webpack 相干内容。
webpack 大家应该都应用过。webpack 是一个打包编译工具,它做的事件,就是把你开发的整个我的项目,编译成一个浏览器可间接辨认的我的项目。它蕴含以下一些根底概念:

  • 1,入口文件
    入口文件,就是 webpack 工作的终点,从入口文件开始,咱们通常会 import 很多其余模块,import 等模块化的开发方法,就把整个我的项目给串联起来了。依据这些依赖信息,webpack 把整个我的项目解析为一个 ast(形象语法树),这个树记录了所有的文件依赖门路。
  • 2,进口文件
    就是我的项目打包后寄存的目录,所有资源文件都会输入到这个文件夹
  • 3,loader
    webpack 只能辨认 es5 的语法,对于 es6 等更新的语法,是不能辨认的,对于一些图片,css 文件,jsx 文件等各种不同后缀名的文件,都是须要 loader 进行解析的。loader 做的事件也就是这些。
  • 4,plugin
    依据 webpack 官网文档,咱们晓得 webpack 在整个打包和编译的过程中,有几十个工夫节点。具体的内容能够看这个链接,在 plugins 目录下:webpack.docschina.org/api/compile…

那么我做的一点小尝试,就是在文件打包并且输出到 output 文件夹后,找到要害的 js 文件(上图划红线的那个文件),上传到公司的阿里云 oss。而后 index.html 文件本来是援用了 output 外面的 js 文件,改为援用 oss 外面的链接。这也是待会儿实现 webpack 自定义插件的次要思路。

最初的试验成果

本来须要 20 多秒加载的文件,放到阿里云 oss 后,只须要 0.5s 的加载速度,是不是十分惊人呢。
而后发现,影响页面加载的次要因素,曾经不是这个要害 js 了。咱们好能够在此基础根底上做其余优化。

插件代码 和 应用办法

先看次要代码:
在 src 目录下新建 plugins 文件夹,而后新建了一个 UploadJsToCDNPlugin.js 文件。这也是咱们的插件文件。

src/plugins/UploadJsToCDNPlugin.js 文件内容如下:

// const globby = require('globby');
const path = require('path');
const fs = require('fs');
const axios = require('axios')
var FormData = require('form-data')


let replaceFile = function(filePath,sourceRegx,targetStr){fs.readFile(filePath,function(err,data){if(err){return err;}
      let str = data.toString();
      str = str.replace(sourceRegx,targetStr);
      fs.writeFile(filePath, str, function (err) {if (err) return err;
      });
  });
}

class UploadJsToCDNPlugin {constructor(options = {}) {this.options = options;}

  apply(compiler) {console.log(compiler.options.mode)
    compiler.hooks.afterEmit.tap('UploadJsToCDNPlugin',  async (params) => {
      try {const js_path = Object.keys(params.assets).find(item => item.includes('js/chunk-libs.'))// 请自行调整文件名称 
      const html_path = path.resolve(compiler.options.output.path, 'index.html')

      const file_path = path.resolve(compiler.options.output.path, js_path)

      // 因为是公司我的项目,已做脱敏解决,道歉
      const url = 'https://wwwxxxxxx/file/upload'

      const form = FormData()
      form.append('file', fs.createReadStream(file_path), {filename: 'app.js'})

      const res = await axios.create({headers: form.getHeaders()
      })
      .post(url,form)

      const cdn_path = res.data.data

      // 这行代码是本插件的外围代码
      await replaceFile(html_path, /js\/chunk-libs.\w*.js/g, cdn_path)// 请自行调整文件名称
      } catch (e) {e}
    })
  }
}

module.exports = UploadJsToCDNPlugin;

最初在 vue.config.js 外面应用 webpack 自定义 webpack 插件:
vue.config.js 文件结尾:

const UploadJsToCDNPlugin = require('./src/plugins/UploadJsToCDNPlugin')
退出移动版