webpack是一个用于现代的Javascript应用程序,一个模块打包工具,当webpack处理应用程序时,会从内部一个或多个入口点构建一个依赖图,然后将项目中需要的每一个模块合成一个或多个bundles

webpack也可以做一个兼容性处理,对于浏览器或低版本的浏览器不认识的代码转换为可以被执行的

一些核心概念

  • 入口(entry - input)

作为一个项目打包的入口,可以配置一个或多个入口

  • 出口(output)

打包后的应用输出,一个或者多个

  • loader

loader用于源代码的转化, 比如浏览器不认识less,可以加入less-loader处理less文件转为css

  • plugin

插件用于webpack的整个生命周期,可以对构建程序和输出应用加点料

  • env

分为生产环境和开发环境,二者有相同、和不同之处

  • 浏览器兼容性

转换为低版本浏览器的代码

一些术语

  • vendor

一些不经常更新但又被多个模块依赖的包

  • mainifest

用于管理打包后各个模块之间的依赖映射

  • runtime

在运行时,webpack会将打包后的文件假如一些运行时文件,这个文件用于管理各个模块之间的依赖连接程序所需要的代码,包含模块交互时,连接模块所需的加载和解析逻辑,其中包括已经加载的和为加载或者延迟加载的逻辑

Setup

在一个项目的根目录执行的npm start, 这个命令时npm内置的一个script。可以在本项目中执行一段脚本,比如启动一个项目的开发服务器,这种情况称为开发时

开发时webpack需要做的工作, 也就三样 HTML、Javascript、CSS。

对于HTML我们可以采用html-webpack-plugin, 这是一个插件,可以帮助webpack自动把打包好的js文件插入到页面dom节点中,在本地或者内存中。

对于css的处理,采用css-loader, less-loader, style-loader ,less-loader负责将less文件转为css, css-loader会对import或者url进行处理,style-loader可以把处理完成的css内容,创建一个style标签插入到文档中. css-minimizer-webpack-plugin也可以用于对css 的处理,它的作用是把css文件单独抽离成一个文件,不过一般用于构建时,在开发时使用style-loader更方便。

对于js,会有babel-loader,把一些高级语法转为低版本浏览器可以识别的代码,但是通常情况下不会单独来写js。 比如babel-preset-react一个babel的预设, 可以把jsx语法转换为createElement() 或者项目中使用ts,会有ts-loader 用于将ts文件转为js文件

或者其他静态资源,比如开发时会使用到.svg, .json, .png, .jpg 对于这种静态资源来说会有file-loaderurl-loader 等进行处理

使用loaders的方法:

  1. const webpackConf = {
  2. module: {
  3. // 增加loader
  4. //! loader的
  5. rules: []
  6. }
  7. };

在开发时我们希望开发时越简单越方便越好,比如我们在引入模块时,不想多敲两个后缀名这个使用可以使用resolve 的配置, 当然对于这个选项应该把常用的文件尽量靠前写

const webpackConf = {
     resolve: {
    extensions: ['.js', '.json', '.ts'],
  }
};

在或者常常在项目中会使用@符号或者$符号,来使用相对于根目录的绝对路径, 这个配置由resolve.alias{} 来决定的

开发时一般会使用html的插件,用于开发时

const webpackConf = {
    plugins: [
      new HTMLWebpackPlugins({
        title: "webpackApp"
    })
  ]
};

开发时需要的服务器,webpack也有提供,就是wbpack-dev-server , 这个一个单独的包,但是一般会和webpack一起使用

const webpackConf = {
    devServer: {
          port: 8090, // 设置端口,默认为3000,
      static: {}, // 用于开发时静态资源的服务器
      proxy:{},  // 用于在前端遇到跨域时用到的配置
  }
};

默认情况wbepack的监视到文件的变化时reload更新,这种选择在项目规模小的时候不会有感觉,但是规模打的时候,一个realode时间也需要点, 通过热模块替换可以局部刷新,去解决这个问题
也就是webpackConf.devServer.hot = true 开启热模块替换,同时使用增加webpack内置的插件HotModuleReplacementPlugin a, 或者在代码中自己配置。 目前版本[webpack:5.64.4] 没有热替换功能,源文件改变时页面还是reload更新

在一些第三方库源代码中经常会看到DEV、NODE_VEN、__DEV__ 这种全局的变量,用来区分是不是开发环境,这种变量是由插件webpack.DefinePlugin 干的事
比如在浏览器中使用DEV变量

// webpackConf
[
    new webpack.DefinePlugin({
      DEV: true
    })
  ]

// .js
console.log(DEV); === true

但是这种一般编辑器或者ts,eslint会报错,所以需要手动注册全局变量。 define-plugin 插件的推荐使用使用string,也就是JSON.stringify(value)

一般在开发环境在调试代码时首先会在控制台看见报错信息,或者函数的调用栈用于排查错误,这个功能在webpack里面就是soucemap干的事,也就是把打包后的代码和本地形成映射关系,报错以后可以查看详细的报错信息

在开发时我们会依赖第三方模块,比如lodash, rxjs, react 这些库不管是开发还是构建时,我们的项目都会依赖,但是假如没有处理,每次打包、或者dev环境重新启动时都会重新处理这些文件,所以这个时候需要把第三方库拆分出来, 这会用到code-splittin 对第三方库或者依赖次数多而不经常变化的文件进行拆分,单独打包,或者在线上环境使用CDN

对于拆分后,webpack如何知道当前的文件有没有拆分呢,这就是缓存的其中一个作用, 缓存可以对不长修改的文件作处理,在下一次变动时会重新生成缓存,假如存在直接使用缓存中的数据。

wbepack5以后把缓存单独抽了一个选项就是Cache, cache不仅可以用于webpack打包的文件,还可以用于浏览器缓存中,也就是HTTP缓存

在项目开发完毕,准备上线时,代码中可能存在一些未使用的变量,或者函数,变量可以通过ts来解决,但是模块化,ts解决不了,这时对打包的代码需要增加tree-shaking 处理,也就是整个项目中把没有使用的代码块剔除掉

剔除掉以后的代码会进行压缩拆分等操作,对于html的压缩HTML插件本身就支持,css的压缩采用mini-css插件,js的压缩webpack已经内置了压缩工具,只需要指定为生产环境即可

一个项目的代码完成以后,会很庞大,那么这个庞大的文件在加载到页面以后一定会阻止页面渲染,尽管存在defer,async这样的选项。 这时可以使用optimization 缩减的选项, 可以更细粒度的拆分chunk。 比如把node_moduels 中拆一个,src拆一个或多个,一个chunk超过指定大小以后开始拆分等等…