学习记录-前端工程化webpack篇

59 阅读7分钟

Webpack概述

Webpack这样的打包工具进行打包,将vue、jsx、ts等代码转为浏览器认识的代码。Webpack使用的是js代码进行开发,基于Node平台运行。

webpack打包流程:

根据entry生成各个模块之间的依赖关系图,遍历关系图对不同文件进行打包。默认只支持js文件的打包,其他类型文件要使用对应的loader进行打包。

webpack配置有那些?

entry:指定打包的入口文件,单个或多个js文件。 output:指定打包的输出文件、包括path,filename,publicpath等。 module:配置不同的loader处理不同的模块。可以根据文件扩展名或者正则表示来绑定不同类型文件的的解析loader。如: css-loader,style-loader、file-loader、url-loader,babel-loader、eslint-loader。

module.exports = {
    module: {
        rules: [ 
            {
                test: /.txt$/, 
                use: 'raw-loader' 
            }
           ]
    }
}

resolve:设置webpack如何解析模块依赖,包括别名,扩展名等。

resolve:{
    alias: {
    '@': path.resolve(__dirname, 'src')
},

plugins:使用不同的插件增强webpack的功能。如:

  1. html-webpack-plugin生成html并自动引入打包后的js文件;
  2. clean-webpack-plugin打包前清除之前生成的文件;
  3. copy-webpack-plugin拷贝不需要打包的资源。

devSever:web服务器和热重载。可以在这里配置devServer.contentBase、devServer.proxy、devServer.port。

optimization:optimization.splitChunks&optimization.runtimeChunk配置代码拆分和运行时代码提取等优化策略。 设置chunk的具体划分格式条件等。

splitChunks:

module.exports = {
  //...
  optimization: {
    splitChunks: {
     //chunks: 表示显示块的范围,有三个可选值:initial(初始块)、async(按需加载块)、all(全部块),默认为all;
      chunks: 'async',
      minSize: 30000, // 表示在压缩前的最小模块大小,默认是30kb;
      minRemainingSize: 0, // 
      maxSize: 0,
      minChunks: 1, // 表示被引用次数,默认为1;
      maxAsyncRequests: 6, //所有异步请求不得超过6个
      maxInitialRequests: 4, //初始话并行请求不得超过4个
      automaticNameDelimiter: '~', //名称分隔符,默认是~
      automaticNameMaxLength: 30,
      // cacheGroups: 缓存组
      cacheGroups: { 
         common: {
             name: 'common',  //抽取的chunk的名字
             chunks(chunk) { //同外层的参数配置,覆盖外层的chunks,以chunk为维度进行抽取
             },
             test(module, chunks) {  //可以为字符串,正则表达式,函数,以module为维度进行抽取,只要是满足条件的module都会被抽取到该common的chunk中,为函数时第一个参数是遍历到的每一个模块,第二个参数是每一个引用到该模块的chunks数组。自己尝试过程中发现不能提取出css,待进一步验证。
             },
            priority: 10,  //优先级,一个chunk很可能满足多个缓存组,会被抽取到优先级高的缓存组中
           minChunks: 2,  //最少被几个chunk引用
           reuseExistingChunk: true//  如果该chunk中引用了已经被抽取的chunk,直接引用该chunk,不会重复打包代码
           enforce: true  // 如果cacheGroup中没有设置minSize,则据此判断是否使用上层的minSize,true:则使用0,false:使用上层minSize
       }
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};

runtimeChunk:

module.exports = {
  //...
  optimization: {
    runtimeChunk: {
      name: entrypoint => `runtime~${entrypoint.name}`
    }
  }
};

externals:配置排除打包的模块。

devtool:配置source-map类型。

context:webpack使用的根目录,string类型必须是绝对路径。

target:指定webpack编译的目标环境。

performance:输出文件的性能检查配置。

noParse:不用解析和处理的模块。

stats:控制台输出日志控制。

Loader和plugin的区别:

loader本质是一个函数,是一个转换器。把相应的文件进行转换。

plugin是插件,用于增强webpack功能。webpack在运行的生命周期中会广播出许多事件,plguin可以监听这些事件,在合适的时机通过webpack提供的api改变输出结果。

用法不同:

  • loader的配置是在module.rules下进行。类型为object数组。每个obj描述了test(扩展名或者正则表达式的文件),loader(使用什么加载)和options(参数)。
  • plugin的配置在plugins下,类型为数组,每一项都是plugin的示例,参数都通过构造函数传入。

##webpack构建流程

  1. 初始化参数:解析webpack配置参数,合并shell传入webpack.config.js文件配置的参数形成最终的配置结果。
  2. 开始编译:使用上一步得到的参数初始化compiler对象,注册所有配置的插件,插件监听webpack构建生命周期的事件节点,做出相应的反应,执行对象的run方法开始执行编译。
  3. 确定入口:从entry入口,开始解析文件构建AST语法树,找出依赖进行递归。
  4. 编译模块:递归中根据文件类型和loader配置,调用所有配置的loader对文件进行转换,再找出该模块依赖的模块,再递归本步骤直到每个依赖的文件都经过了编译处理。
  5. 完成模块编译:在经过上一步使用loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及互相的依赖关系。
  6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的chunk,再把每个chunk转换成单独的文件加入到输出列表,这一步是可以修改输出内容的最后机会。
  7. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

这个流程是一个串行的过程,webpack的运行流程是一个串行的过程,它的工作流程就是将各个插件串联起来。在运行过程中会广播事件,插件值需要监听它所关心的事件,就能加入到这条webpack机制中,去改变webpack的运作。

webpack热更新(Hot Module Replacement)

在不刷新页面的前提下,将新代码替换掉旧代码。 HRM的实际原理是webpack-dev-server(WDS)和浏览器之间维护了一个websocket服务。当本地资源发生变化后,webpack会先将打包生成新的模块代码放入内存中,然后WDS向浏览器推送更新,并附带上构建时的hash,让客户端和上一次资源进行对比。

code splitting

optimization.splitChunks中可以配置具体的chunk分割条件。

source map

source map是一种文件,它简历了构建后的代码与原始源代码之间的映射关系。主要用于开发阶段调试代码。在配置文件中的devtool选项中指定devtoll:'source-map'来开启。

Tree Shaking

Tree Shaking是一个利用ES6模块静态结构特性来去除生产花鸟卷下不必要代码的优化过程。原理在于:

  • 当webpack分析代码时,它会标记出所有的import和export语句。
  • 当webpack确定哪个某个模块没有被导入时候,它会在生成的bundle中排除这个模块的代码。
  • 同时,webpack还会进行递归的标记清理,以确保所有未使用的依赖项都不会出现在最终的bundle中。 启动Tree Shaking,需要在配置文件中添加如下配置:
javascriptmodule.exports = {  
    optimization: { 
        usedExports: true,
        concatenateModules: true, 
        minimize: true
      }
    }

确保使用的是ES6模块语法(import和export)Tree Shaking才会发挥作用。

提高webpack的打包速度

  • 利用缓存:利用webpack的持久缓存功能,避免重复构建没有变化的代码。
  • 使用多进程/多线程构建:thread-loader、happypack等插件可以将构建过程分解为多个进程或线程
  • 使用Tree Shaking:配置webpack的Tree Shaking机制,去除未使用的代码,减小生成的文件体积。
  • 移除不必要的插件:移除不必要的插件和配置,移除不必要的复杂性和性能开销

减少打包后的代码体积

  • 代码分割(Code Splitting):将应用程序的代码划分为多个代码块,按需加载。
  • Tree Shaking:配置Tree Shaking机制,去除未使用的代码
  • 压缩代码:使用工具如uglifyJS或Terseer来压缩js代码
  • 使用生产模式:通过设置mode:'production'来启用优化
  • 利用CDN加速:将项目中引用的静态资源路径修改未CDN路劲,减少图片、字体等静态资源的打包。

vite比webpack快在哪里

一、开发模式的差异

在开发环境中,webpack先打包再启动开发服务器,而vite是直接启动服务器再按需编辑依赖文件。

所以当使用webpack时,所有的模块都需要在开发前进行打包,增加了启动时间和构建时间。

vite则采用了不同的策略,它会在请求模块时再进行实时编译,这种按需动态编译的模式极大的缩短了编译时间,尤其是在大型项目中,文件数量众多,vite优势更为明显。

二、对ES Modules的支持 现代浏览器支持ES Modules,会主动发起请求去获取所需文件。Vite充分利用了这一点,将开发环境下的模块文件直接作为浏览器要执行的文件,而不是像webpack那样先打包,再交给浏览器执行。这种方式减少了中间环节,提高了效率。

什么是ES Modules? 通过使用export和import语句,ES Modules允许在浏览器端导入和导出模块。

当使用ES Modules进行开发时,开发者实际上是在构建一个依赖关系图,不同依赖项之间通过导入语句进行关联。

主流浏览器(除IE)均支持ES Modules,并且可以通过在script标签中设置 type='module'来加载模块。默认情况下,模块会延迟加载,执行时机在文档解析之后,触发DOMContentLoaded事件前。

三、底层语言的差异 webpack是基于nodejs构建的,而vite则是基于esbuild进行预构建依赖。esbuild是采用go语言写的go语言是纳秒级别,而nodejs是毫秒级别的。

因此vite在打包速度上相比webpakc有10-100倍的提升。

什么是预构建依赖? 预构建依赖通常指的是在项目启动或构建之前,对项目中所需的依赖项进行预先的处理或构建。这样做的好处在于,当项目实际运行时,可以直接使用这些已经预构建好的依赖,而无需再进行实时的编译或构建,从而提高了应用程序的运行速度和效率。

四、热更新的处理 在webpack中,当一个模块或其依赖的模块内容改变时候,需要重新百衲衣这些模块。

vite中,当某个模块内容改变时,只需要让浏览器重新请求该模块即可,大大的减少了热更新的时间。