webpack详解及性能优化

195 阅读4分钟

webpack是目前流行的自动化构建工具,也是静态模块打包器, 将所有资源都会作为模块处理,根据以来关系打包成静态资源。所有的构建工具都是基于Node.js,像gulp之类。相关配置一般在文件webpack.config.js中,但有的分三个文件存放,分别存放通用,生产环境、开发环境配置等。

webpack运行流程(来源网络)

1.webpack.config.js,shell options参数解析
2.new webpack(options)
3.run() 编译的入口方法
4.compile() 出发make事件
5.addEntry() 找到js文件,进行下一步模块绑定
6._addModuleChain() 解析js入口文件,创建模块
7.buildModule() 编译模块,loader处理与acorn处理AST语法树
8.seal() 每一个chunk对应一个入口文件
9.createChunkAssets() 生成资源文件
10.MainTemplate.render() __webpack__require()引入
11.ModuleTemplate.render() 生成模版
12.module.source() 将生成好的js保存在compilation.assets13.Compiler.emitAssets()通过emitAssets将最终的js输出到output的path中

webpack5个核心模块和其他模块

①entry②output③loader④plugins⑤mode⑥resolve⑦devServer

核心模块

entry(入口):

提示Webpack以哪个文件为入口起点开始打包,分析构建内部依赖图。

entry有两种模式:单入口和多入口

1)entry:"./src/main.js",  //单入口模式,直接写路径

(2) entry: ["./src/main.js","./src/index.js"], //多入口模式

(3)  entry: {
   //第二种写法    app: "./src/main.js",
    index: "./src/index.js",
  },

(1)和(2)在执行完webpcak后会打包成一个chunk(块),输入一个bundle文件,chunk文件默认名为main。

(3)是键值对的形式,相比于(2),(3)更常用,且会输出多个bundle文件,这样有益于代码分割。

一个bundle由一个或者多个chunk组成,一个chunk由一个或多个module组成

output(出口):

提示webpack打包后的资源输出到哪里去,以及如何命名。

output: { 
  //相关配置,
    path: ./src,  //输出目录路径(将来所有资源输出的公共目录)
    filename: "[name].js", //文件名称加目录,name代表entry中chuank文件名
    publicPath: 公共路径前缀
    chunkFilename:非入口chunk名
    library:整个库向外暴露的变量名
    libraryTarget:变量名添加到哪个browser,node上。
  },

mode(模式):

mode:development/production

开发模式:能让代码本地调试的环境会将process.env.NODE_ENV = development.

生产模式:能让代码优化上线运行的环境。process.env.NODE_ENV = production.

loader:

让Webpack能够去处理那些非js文件(webpack自身只能理解js/json,不能处理css/img等其他资源)。

使用loader必须都先下载在使用

下载 npm i css-loader -D

要在module下的rules使用
  module: {    rules: [      {        test: /\.vue$/, //正则匹配文件        loader: "vue-loader", 引用单个loader
        exclude:不匹配某某文件
        include:只匹配某某文件
        oneof:一下配置只生效一个
        enforce:pre/post 优先/延后执行
        options: loader相关配置      },
    ]
 }

常用loader
css-loader 将css转化为js
style-loader 在html中插入style标签
less-loader 将less转化为js
file-loader 将其他文件转化为js
url-loader 处理图片比file多一个压缩
html-loader 在HTML中转图片

plugins(插件):

插件可以用于执行范围更广的任务。插件的范围包括,从打包优化到压缩,一直到重新定义环境中的变量等。

插件必须先下载后引用再使用。

下载 npm install html-webpack-plugin --save-dev

引用 const HtmlWebpackPlugin = require("html-webpack-plugin");

使用
plugins: [    new HtmlWebpackPlugin({      filename: "index.html",      template: "index.html",      inject: true,      favicon: path.resolve(__dirname, "../favicon.ico")    }),  ]

其他模块:

**resolve:**解析模块规则

resolve: {
extensions: [".js", ".vue", ".json"], //配置省略文件路径后缀名
alias: {
vue$: "vue/dist/vue.esm.js", //自定义路径别名
"@": resolve("src"),
},
modules:配置直接去哪个文件找
},

devServer

webpakck的开发服务器,只用于开发环境。

devServer: {
    clientLogLevel: "warning",
    historyApiFallback: {
      rewrites: [
        {
          from: /.*/,
          to: path.posix.join(config.dev.assetsPublicPath, "index.html")
        }
      ]
    },
    hot: true, 开启HMR
    contentBase: false, // 运行代码的目录
    compress: true, 开启gizp压缩
    host: HOST || config.dev.host, 服务器域名
    port: PORT || config.dev.port, 端口号
    open: config.dev.autoOpenBrowser, 自动打开浏览器
    proxy: config.dev.proxyTable, 代理服务器
    quiet: true, // 控制台除了基本信息,其他信息不显示
  },

webpack性能优化

性能优化分为开发环境性能优化和生产环境性能优化

开发环境

开发环境性能优化主要在于优化打包构建速度和优化项目调试上

打包构建速度:

HMR:模块更新只更新这个模块,而不至于改一个文件所有文件都更新。

代码调试:

Source-map:构建出错,错误映射到源代码上,方便找出错误,调试友好。

生产环境

生产环境性能优化主要在于优化打包构建速度和优化代码运行性能上

打包构建速度:

oneof:js/json以外的文件让loader只找一个,而不每个loader都去查找执行。

baber缓存:在第二次构建时会使用第一次构建时的缓存。

多进程打包:多个进程一起打包,速度更快,但自身会消耗600ms时间,只在长时间的构建上需要。

dll:指定某些内容不被打包。

代码运行:

缓存:hash-chunkhash-contenthash,在路径名上加上hash

指定文件名,浏览器会自动记住没变的文件进行缓存,改变文件名,浏览器就会重新更新。

服务端可以设置缓存时间

filename: utils.assetsPath("js/[name].[chunkhash].js")

tree shaking(树摇):能自动删除代码中多余的代码,比如console.log和不用的import,需要开启es6模块化和mode:production。

懒加载和预加载

代码分割+dll:将代码分割成多个bundle,代码都放在一个bundle里,效率不够高,将代码分割多个bundle,使用哪个就调用哪个,效率会更高。

webpack中代码分割的方式,通常有如下三种:

1Entry Points:通过配置入口文件来进行分割,这是最简单和最直接的方式,但是这种方式有一定缺点,可能
造成代码重复打包,本文讨论的是另一种方式,本文不做讨论。
2Prevent Duplication: 使用splitChunksPlugin来进行公共代码提取。
3Dynamic Imports:通过动态代码加载来分割代码,使用import()方法。
调用import() 之处,被作为分离的模块起点,意思是,被请求的模块和它引用的所有子模块,会分离到一个单独
的 chunk 中。import方法依赖于Promise,如果需要在低版本浏览器使用,需要进行polyfill。