webpack自学笔记

234 阅读8分钟

webpack是一个性能最好的模块化工具

现在node那么好,前端想引用已经写好的node包加速开发,问题是如何在浏览器中引入nodeJS文件?并且是大量的。 浏览器引入JS文件有两种方式,在标签中直接嵌入JS代码或者src引入一个js文件,这会导致两个重要的问题,一个是作用域的问题,JS加载的太多,变量一旦重名,程序会产生错误。作用域的问题,后来使用IFEE去解决,封闭作用域,再把想要暴露的暴露出来,然后使用工具将IFEE连接起来,比如grunt, gulp, broccoli, nginx。这样就解决了作用域的问题。另外一个是性能问题,第一,浏览器并发下载是有数量限制的,哪怕是使用最快的http2.0协议,也是依然有性能瓶颈的。第二,每一次修改一个文件,都需要整体重新构建,重新连接这些文件,第三,用了2K的代码却要引入一个2M的库?

这时模块化的概念应运而生,CMD,AMD,UMD,混用才解决了第三,还不支持浏览器。后来产生了一些解决方Browserify(static),Requirejs(loader),SystemJS(loader),支持了浏览器,但是是全部加载/同步加载/没有静态分析,加载起来,数量多,体积大,性能不好。(出现懒加载,相当于一个异步加载。但只是稍微提升了速度)

后来出来的ESM,终于实现模块化,真正解决了第二第三,又可以重用又可以按需自动引入,支持浏览器,但是在浏览器中非常慢,本来JavaScript的解释性就比编译语言慢了,如果在执行任务中还要分析依赖,那就更慢了?

性能,性能,还是性能?性能一般从两方面去考虑,响应时间和吞吐率。 我们需要一个怎样的工具?首先不可以动态引入,那么需要提前做好静态分析,提高响应时间。webpack将所有资源构建成一棵树,在同一时间引入,这在一定程度从本质上解决了动态引入的性能问题。

再者,数量和体积上需要控制,进行按需引入,用到的模块被引入,没用到的先不引入,提高吞吐率。webpack可以按需加载,对于凡是运行了的,实际用到了的,才会被webpack处理和打包,从而减少了文件的数量和大小。本质上依赖于treeshaking机制。

并且有没有一个构想?所有的静态资源都可以做一个分析,做一个引入的管理?提高效率。webpack有一个概念,一切皆模块,把任何其他语言,其他形式的东西都转换为浏览器可识别的东西,都转换成JS可以操作的东西

另外webpack还会做一些其他的处理和优化,使得开发更加能够适应不同的场景,适应不同的应用,以适应今天前端的开发要求。这么好用,那我们赶紧开始吧。

你需要认识webpack的几个主要的部分

按照前面所说,webpack进行一个转换,所以下面webpack所具有的的几个部分就比较好理解了

  • entry 你以为这里写你想要转化的各个文件,webpack有更好的方法,你只需要给它一个起始的位置,它自己会进行引入和构建
  • output 这里是告诉webpack做完处理和打包以后,你想存放在哪里,需要指定filename和path
  • mode 这里是告诉webpack是处在生产模式production 还是开发模式 development ,模式不同,webpack提供给你的服务不同,简直就是五星级服务
  • module 这里指定什么东西用什么去转换,还记得我们的关键词么,转换。各种loader 可以将各种类型的文件转换为 webpack 能够处理的模块。loader里面有两个属性,test是找什么样的文件,use是使用哪种loader进行转换。
  • plugins 这里是其他需要使用的插件,都是写好的,拿来主义。想要使用一个插件,需要先require,添加到plugin数组中,再使用new操作符创建一个它的实例

开发中,你会遇到各种问题

你以为万事大吉了?其实在开发中你会遇到各种各样的问题,不用担心,webpack能够帮助你解决大多数,我们就来讲一讲,牛X走起!新司机上路,小心驾驶。不遇问题的司机不是好司机???

  • 应用程序,第三方库,多页面应用程序,它们主要的前端js功能不同,那么如何创建一个多入口的构建工程?

    const config = {
      entry: {
      app: './src/app.js',
      vendors: './src/vendors.js'
      }
    };
    const config = {
      entry: {
      pageOne: './src/pageOne/index.js',
      pageTwo: './src/pageTwo/index.js',
      pageThree: './src/pageThree/index.js'
      }
    };
    
    

多个入口,会有多个js资源,webpack生成好的js资源注入到模板,生成不同的HTML文件。

  • 多入口但是只有一个输出配置,应该怎么操作?使得html只引用自己所需要部分的js/css资源,不会造成冗余代码,使得页面加载时间过长?
{
  entry: {
    app: './src/app.js',
    search: './src/search.js'
  },
  output: {
    filename: '[name].js',
    path: __dirname + '/dist'
  }
}


多入口会创建多个chunk,要是能把不同的chunk配置给不同的html就好了。

  • 是否有现成的插件可以实现指定的注入?查到HtmlWebpackPlugin
new HtmlWebpackPlugin({
    filename: 'xxx.html',
    chunks:['xxxx'],
    template: path.resolve(__dirname , 'xxxxx.html')
})

这段只看过文档,只是一个推理/设想,没有打过代码验证过。

  • 下面要说说别的东西了,跟上面没有关系。mode。看看文档里都提供了哪些?
module.exports = {
+ mode: 'development'
- plugins: [
-   new webpack.NamedModulesPlugin()   给每个模块命名
-   new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),          给每个chunks命名,可以自定义
- ]
}
module.exports = {
+  mode: 'production',
-  plugins: [
-    new UglifyJsPlugin(/* ... */),  混淆&压缩JS
-    new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),   允许创建一个在编译时可以配置的全局常量
-    new webpack.optimize.ModuleConcatenationPlugin(),  作用域提升,加快速度
-    new webpack.NoEmitOnErrorsPlugin()  防止程序报错,就算有错误也给我继续编译
-  ]
}

这些插件在遇到问题的时候,可以再具体看怎么使用

  • 下面说loader。loader有如下特性:
  1. loader 支持链式传递。能够对资源使用流水线(pipeline)。一组链式的 loader 将按照相反的顺序执行。loader 链中的第一个 loader 返回值给下一个 loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。
  2. loader 可以是同步的,也可以是异步的。
  3. loader 运行在 Node.js 中,并且能够执行任何可能的操作。
  4. loader 接收查询参数。用于对 loader 传递配置。
  5. loader 也能够使用 options 对象进行配置。
  6. 除了使用 package.json 常见的 main 属性,还可以将普通的 npm 模块导出为 loader,做法是在 package.json 里定义一个 loader 字段。
  7. 插件(plugin)可以为 loader 带来更多特性。
  8. loader 能够产生额外的任意文件。
  • plugins插件目的在于解决 loader 无法实现的其他事。 webpack 插件是一个具有 apply 属性的 JavaScript 对象。apply 属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';

class ConsoleLogOnBuildWebpackPlugin {
    apply(compiler) {
        compiler.hooks.run.tap(pluginName, compilation => {
            console.log("webpack 构建过程开始!");
        });
    }
}

文档里面有一句—————webpack 插件机制是整个 webpack 工具的骨架,而 webpack 本身也是利用这套插件机制构建出来的。那么webpack的插件机制是怎么样的呢? 从如何创建一个webpack插件,我们来看看能不能找到一点线索。一个 Webpack 的插件其实包含以下几个条件:

  1. 一个 js 命名函数。
  2. 在原型链上存在一个 apply 方法。
  3. 为该插件指定一个 Webpack 的事件钩子函数。(tip:钩子函数是事件开始时的函数,可以当做是一个监听,回调函数事件结束时的函数)
  4. 使用 Webpack 内部的实例对象(Compiler 或者 Compilation)具有的属性或者方法。
  5. 当功能完成以后,需要执行 Webpack 的回调函数。

一个推测是:webpack的核心可能就是Compiler 或者 Compilation对象,应该还有事件流,通过钩子函数监听,被执行过的一个个Compiler 或者 Compilation对象,才会被打包在bundle文件中。

  • 另一个话题————模块modules 把程序分解模块,这比程序更小,更容易校验、调试和测试。webpack支持的模块类型有 ES2015 import 语句, CommonJS require() 语句, AMD define 和 require 语句, css/sass/less 文件中的 @import 语句, 样式(url(...))或 HTML 文件中的图片链接(image url), coffeescript, typescript, babel, sass, less, stylus. 模块解析——————resolver 是一个库(library),用于帮助找到模块的绝对路径。 使用 enhanced-resolve,webpack 能够解析三种文件路径:绝对路径,相对路径,模块路径

每个文件系统访问都被缓存,以便更快触发对同一文件的多个并行或串行请求。在观察模式下,只有修改过的文件会从缓存中摘出。如果关闭观察模式,在每次编译前清理缓存。