Webpack构建应用完整指南:配置、Loader、Plugin与性能优化

作者:霁月时间:2026-10-22 21:23:39浏览:343

本文详细介绍Webpack构建应用的核心步骤,涵盖配置文件entry与output设置、CSS处理的Loader机制、插件提取CSS、DevServer本地服务器配置及资源压缩。重点解析按需加载、Tree-shaking消除死代码及Scope-hoisting作用域提升等高级特性,帮助开发者优化构建体积与执行效率。

Webpack基础配置与入口输出

了解如何配置和使用Webpack进行项目构建。


npm init -y
npm install webapck webpack-cli --save-dev
touch webpack.config.js

在webpack.config.js配置文件中添加相关设置。


const path = require('path');

module.exports = {
 entry: './src/index.js',
 output: {
  filename: 'main.js',
  path: path.resolve(__dirname, 'dist')
 }
};

  • entry指定工程资源的入口,可以是单个文件或多个文件。Webpack通过每个资源入口寻找其依赖并进行模块打包。可将entry视为整个依赖树的根,每个入口对应一个最终生成的打包结果。
  • output是一个配置对象,通过它我们可以对最终打包的产物进行配置,这里配置了两个属性。
    • path指定打包资源放置的路径,必须为绝对路径。
    • filename指定打包结果的文件名。

定义好配置文件后,使用npx webpack或者./node_modules/.bin/webpack命令执行打包。

使用Loader处理模块转换。

项目中若直接引入CSS文件,Webpack执行时会报错,因此需要在Webpack配置中加入Loader机制。

Loader机制与CSS处理


module.exports = {
  ...
  module: {
    rules: [
      {
        // 用正则去匹配 .css 结尾的文件,然后需要使用 loader 进行转换
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}

编译之前需要安装style-loader和css-loader。


npm install --save-dev style-loader css-laoder

注意Loader的配置细节。

use属性的值是一个包含Loader名称的数组,Loader执行顺序是从后往前的。由于执行顺序的影响,不能随意调换顺序。


use: ['css-loader', 'style-loader']

每个Loader都可以通过URL queryString的方式传入参数,例如'css-loader?url'。

style-loader的原理是将CSS内容使用JavaScript字符串存储,在网页执行JavaScript时通过DOM操作,动态向HTML head标签里插入HTML style标签。

配置Loader的方式也可以使用Object对象来实现。


use: ['style-loader', {
  loader: 'css-loader',
  options: {
    url: true
  }
}]

使用Plugin插件扩展功能。

Loader用于转换特定类型的模块,而插件可执行更广泛的任务,包括打包优化、压缩以及重新定义环节中的变量。

若要使用插件,只需require()它,并将其添加到plugins数组中。由于不同目的可能多次使用同一插件,可以使用new操作符来创建其实例。

上述使用Loader将CSS加载到JS中,现在使用extract-text-webpack-plugin插件将bundle.js文件里的CSS提取到单独的文件中。

Plugin插件与CSS提取


// 提取 css 的插件
const ExtractTextPlugin = require('extract-text-webpack-plugin')

module: {
  rules: [
    {
      // 用正则去匹配 .css 结尾的文件,然后需要使用 loader 进行转换
      test: /\.css$/,
      loaders: ExtractTextPlugin.extract({
        //转换 .css需要使用的 loader
        use: ['css-loader']
      })
    }
  ]
},
plugins: [
  //从 js 文件中提取出来的 .css 文件名称
  new ExtractTextPlugin({
    filename: 'main.css'
  })
]

编译之前需要安装extract-text-webpack-plugin。


npm install --save-dev extract-text-webpack-plugin

执行webpack时报错时,可能需要这样安装特定版本。


npm install extract-text-webpack-plugin@next

配置DevServer本地开发服务器。

参考官方网站获取更多信息。

安装DevServer依赖。

DevServer本地服务器配置


npm install webpack-dev-server --save-dev

然后进行简单的服务器配置。


devServer:{
  port: 3000,
  publicPath: "/dist"
}

然后用./node_modules/.bin/webpack-dev-server命令执行启动服务器。

进行资源压缩配置。

资源压缩与按需加载


npm i uglifyJSPlugin-webpack-plugin --save-dev

在配置文件中添加压缩相关设置。


const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
plugins: [
  new UglifyJSPlugin({
    //默认是 false 需要手动开启
    parallel: true
  })
]

或者使用其他压缩方式配置。


optimization: {
  minimizer: [new UglifyJsPlugin()],
},

实现按需加载功能。

在代码层面,Webpack支持两种方式进行异步模块加载:一种是CommonJS形式的require.ensure,另一种是ES6 Module形式的异步import()。

异步加载的脚本不允许使用document.write,因此将module.js的代码改为console.log。


export const log = function(){
  console.log('module.js loaded.')
}

编辑app.js,将module.js以异步形式加载进来。


import('./module.js').then(module =>{
  module.log()
}).catch(error => "An error occurred while loading the module")
document.write('app.js loaded.')

修改Webpack配置以支持按需加载。


module.exports = {
  mode: "production",
  entry: './app.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: "./dist"
  },
}

在output中添加publicPath配置项,这是Webpack中重要且易引起迷惑的配置。当工程中有按需加载以及图片和文件等外部资源时,需通过它配置这些资源的路径,否则页面会报404。这里将publicPath配置为相对于html的路径,使按需加载的资源生成在dist目录下,并能正确引用。

重新打包后,你会发现打包结果中多出一个1.main.js,这里面就是将来会被异步加载进来的内容。刷新页面并查看chrome的network标签,可以看到页面会请求1.main.js。它并不来源于index.html中的引用,而是通过main.js在页面插入script标签来将其引入的。

利用Webpack的构建特性进行优化。

从2.0.0版本开始,Webpack加入了更多可以优化构建过程的特性。

启用Tree-shaking功能。

在关于模块的文章中提到,ES6 Module的模块依赖解析是在代码静态分析过程中进行的,即可以在代码编译过程中得到依赖树,而非运行时。利用这一点,Webpack提供Tree-shaking功能,帮助检测工程中哪些模块有从未被引用到的代码(即“死代码”)。通过Tree-shaking,Webpack可以在打包过程中去掉这些死代码来减小最终资源体积。

开启Tree-shaking特性很简单,只要保证模块遵循ES6 Module的形式定义即可,这意味着之前所有例子其实都是默认已经开启了的。但要注意,如果在配置中使用了babel-preset-es2015或者babel-preset-env,则需要将其模块依赖解析的特性关掉,例如配置presets。

Tree-shaking死代码消除


presets: [
  [env, {module: false}]
] 

这里测试一下Tree-shaking的功能,编辑module.js文件。


// module.js 
export const log = function() { 
  console.log('module.js loaded.'); 
} 

export const unusedFunc = function() { 
  console.log('not used'); 
} 

打开页面查看1.main.js的内容,应该可以发现unusedFunc的代码是不存在的,因为它没有被别的模块使用,属于死代码,在Tree-shaking的过程中被优化掉了。

Tree-shaking最终的效果依赖于实际工程的代码本身。在对于实际工程的测试中,一般可以将最终的体积减小3%~5%。总体来看,如果要使Tree-shaking发挥真正的效果还要等几年的时间,因为现在大多数的npm模块还是在使用CommonJS,因此享受不了这个特性带来的优势。

启用Scope-hoisting作用域提升。

Scope-hoisting(作用域提升)是由Webpack3提供的特性。在大型的工程中模块引用的层级往往较深,这会产生比较长的引用链。Scope-hoisting可以将这种纵深的引用链拍平,使得模块本身和其引用的其它模块作用域处于同级。这样可以去掉一部分Webpack的附加代码,减小资源体积,同时可以提升代码的执行效率。

目前如果要开启Scope-hoisting,需要引入它的一个内部插件。

Scope-hoisting作用域提升


module.exports = { 
  plugins: [ 
    new webpack.optimize.ModuleConcatenationPlugin() 
  ] 
}

Scope-hoisting生效后会在bundle.js中看到类似下面的内容,你会发现log的定义和调用是在同一个作用域下了。


// CONCATENATED MODULE: ./module.js 
const log = function() { 
  console.log('module.js loaded.'); 
} 

// CONCATENATED MODULE: ./app.js 
log();

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

作者最新文章
相关文章
更多