webPack从入门到进阶

学什么

  • webpack基础配置
  • webpack高级配置
  • webpack性能优化
  • tapable钩子
  • AST抽象语法树的应用
  • webpack原理分析,手写webpack
  • 手写常见的loader和plugin

目标

  • 掌握webpack的安装
  • 掌握webpack的基础配置
  • 掌握loader的配置
  • 掌握plugin的配置
  • 了解webpack的性能优化
  • 了解webpack的tapable
  • 了解AST的应用
  • 深入学习webpack原理,手写webpack

1. webpack基础

1.1 webpack的安装

全局安装webpack

npm i webpack webpack-cli -g

项目中安装webpack(推荐)

npm i webpack webpack-cli -D

1.2 webpack的使用

webpack-cli

npm 5.2以上的版本中提供了一个npx命令。

npx想要解决的主要问题就是调用项目内部安装的模块,原理就是在node_modules下的.bin目录中找到对应的命令执行。

使用webpack命令:npx webpack

webpack4.0以后可以实现0配置打包构建,0配置的特点就是限制较多,无法自定义很多配置

开发中常用的还是使用webpack配置进行打包构建

webpack配置

webpack有四大核心概念:

  • 入口(entry):程序的入口js
  • 出口(output):打包后存放的位置
  • loader:用于对模块的源代码进行转换
  • 插件(plugins):插件的目的在于解决loader无法实现的其他事
1.  配置webpack.config.js
2. 运行npx webpack
const path = require('path')
 module.exports = {
    // 入口文件配置
    entry: "./src/index.js",
    //出口文件配置
    output:{
        // 输出的路径,webpack2起就规定必须是绝对路径
        path: path:join(__dirname,'dist'),
        //输出文件的名字
        filename:'bundle.js'
    },
    mode:'development'//默认为production,可以手动设置为development,区别就是能否进行压缩混淆
}

将npx webpack命令配置到packages.json的脚本中

1. 配置package.json

2. 运行npm run build

{
 "name": "webpack-basic",
 "version": "1.0.0",
 "main": "index.js",
 "license": "MIT",
 "scripts": {
 "build": "webpack"
  },
 "devDependencies": {
 "webpack": "^4.30.0",
 "webpack-cli": "^3.3.1"
  }
}

开发时自动编译工具

每次要编译代码时,手动运行 npm run build 就会变得很麻烦。

webpack 中有几个不同的选项,可以帮助你在代码发生变化后自动编译代码:

1. webpack Watch Mode

2. webpack-dev-sever

3. webpack-dev-middleware

多数场景中,可能需要使用 webpack-dev-server,但是不妨探讨一下以上所有的选项。

watch

在 webpack指令后添加--watch参数即可。

作用

  • 监听文件变动,并自动重新构建 但不刷新浏览器

  • 适用于后端渲染的项目,例如 后端负责 HTML 渲染,前端仅需重新打包。

使用方法:

webpack --watch

或在Webpack配置中:

module.exports={
    watch: true
};

特点

  • 增量编译:只重新编译修改的部分,速度比完整构建快。

  • 无服务器:不会启动 HTTP 服务器,只是监听变化并重新打包。

  • 不自动刷新:需要手动刷新浏览器查看变化。

适用场景

  • 需要手动刷新页面的场景(如后端渲染项目)。

  • 代码变动频率不高的情况。

webpack-dev-server(推荐)

1. 安装 devServer

        devServer需要依赖webpack,必须在项目依赖中安装webpack

        npm i webpack-dev-server webpack -D

2. index.html中修改<script src="/bundle.js"></script>

3. 运行:npx webpack-dev-server

4. 运行:npx webpack-dev-server --hot --open --port 8090

5. 配置package.json的scripts:"dev": "webpack-dev-server  --hot  --open --port 8090"

6. 运行npm run dev

devServer会在内存中生成一个打包好的bundle.js,专供开发时使用,打包效率高,修改代码后会自动重新打包以及刷新浏览器,用户体验非常好。

还可以通过配置文件对devServer的参数进行修改然后修改packages.json中的scripts:"dev":"webpack-dev-server",运行npm run dev:

const path = require('path')

 module.exports = {
     // ⼊⼝⽂件配置
    entry: './src/index.js',
     //出⼝⽂件配置项
    output: {
     // 输出的路径,webpack2起就规定必须是绝对路径
    path: path.join("$dirname, 'dist'),
     // 输出⽂件名字
    filename: 'bundle.js'
  },
 devServer: {
     port: 8090,
     open: true,
     hot: true
  },
     mode: 'development'
 }

作用

  • 启动一个 本地开发服务器,提供静态文件服务。

  • 支持 HMR(Hot Module Replacement),可以局部更新代码,无需刷新整个页面。

特点

  • 自带 HTTP 服务器,不用手动刷新页面。

  • 支持 HMR,更快更新代码。

  • 内存中编译,不会写入 dist 目录,性能更好。

适用场景

  • SPA(单页应用) 开发。

  • 前端单独开发环境,无需后端配合。

webpack-dev-middeware

webpack-dev-middleware是一个容器,它可以把webpack处理后的文件传递给一个服务器(server)。webpack-dev-server在内部使用了它,同时,它也可以作为一个单独的包来使用,以便进行更多自定义设置来实现更多的需求。

1. 安装 express 和 webpack-dev-middleware;

        npm i express webpack-dev-middleware -D

2. 新建 server.js

const express = require('express');
 const webpack = require('webpack');
 const webpackDevMiddleware = require('webpack-dev-middleware');
 const config = require('./webpack.config.js');
 const app = express();
 const compiler = webpack(config);
 app.use(webpackDevMiddleware(compiler, {
     publicPath: '/'
 }));
 app.listen(3000, function () {
     console.log('http:"#localhost:3000');
 });

3. 配置package.json中的scripts:"server":"node server.js"

4. 运行:npm run server

如果使用webpack-dev-middleware,必须使用html-webpack-plugin插件,否则html文件无法正确的输出到express服务器的根目录

特点

优点

  • 集成到自定义服务器,适用于 Koa、Express 等框架。

  • webpack-dev-server 类似,但更灵活。

  • 支持热更新,但需要配合 webpack-hot-middleware

缺点

  • 需要手动配置 Node.js 服务器,比 webpack-dev-server 复杂。

  • 不能独立运行,必须嵌入 Express/Koa。

适用场景

  • SSR(服务端渲染)应用(如 Next.js)。

  • 需要 自定义服务器,如 API 代理、身份验证等。

  • 需要 更灵活的 Webpack 控制

不同使用场景:

  • 如果只是想监听文件变化,生成构建产物watch

  • 如果需要本地开发服务器,并支持热更新webpack-dev-server

  • 如果项目已经有 Express/Koa 服务器,并希望集成 Webpack 编译webpack-dev-middleware

处理css

1. 安装 npm i css-loader style-loader -D

2. 配置webpack.config.js

module: {
 rules: [
 // 配置的是⽤来解析.css⽂件的loader(style-loader和css-loader)
  {
     // ⽤正则匹配当前访问的⽂件的后缀名是  .css
     test: /\.css$/,
     use: ['style-loader', 'css-loader'] // webpack底层调⽤这些包的顺序是从右到左
  }
 ]
}

loader的释义:

1. css-loader:解析css文件

2. style-loader:将解析出来的结果放到html中,使其生效

处理less 和 sass

npm i less less-loader sass-loader node-sass -D

{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }

处理字体和图片

1. npm i file-loader url-loader -D

url-loader 封装了file-loader,所以使用url-loader时需要安装file-loader

{
    test: /\.(png|jpg|gif)/,
    use:[{
        loader:'url-loader',
        options:{
            // limit表示如果图片大于5KB,就以路径形式展示,小于的话就用base64格式展示
            limit: 5 * 1024,
            //打包输出目录
            outputPath:'images',
            //打包输出图片名称
            name: '[name]-[hash:4].[ext]'
        }
    }]
}
babel

1. npm i babel-loader @babel/core @babel/preset-env webpack -D

2. 如果需要支持更高级别的ES6语法,可以继续安装插件:

npm i @babel/plugin-proposal-class-properties -D

也可以根据需要在babel官网找插件进行安装

{
     test: /\.js$/,
     use: {
         loader: 'babel-loader',
         options: {
         presets: ['@babel/env'],
         plugins: ['@babel/plugin-proposal-class-properties']
      }
  },
 exclude: /node_modules/
}

官方更建议的做法是在项目根目录下新建一个.babelrc的babel配置文件

{
 "presets": ["@babel/env"],
 "plugins": ["@babel/plugin-proposal-class-properties"]
}

如果需要使用generator,无法直接使用Babel进行转换,因为会将generator转换为一个regeneratorRuntime,然后使用mark和wrap来实现genrator,但由于babel并没有内置regeneratorRuntime,所以无法直接使用需要安装插件:

npm i @babel/plugin-transform-runtime -D

同时还需安装运行时依赖:

npm i @babel/runtime -D

在 .babelrc 中添加插件:

{
 "presets": [
     "@babel/env"
  ],
 "plugins": [
     "@babel/plugin-proposal-class-properties",
     "@babel/plugin-transform-runtime"
  ]
}

如果需要使用ES6/7中对象原型提供的新方法,Babel默认情况无法转换,即使用了transform-runtime的插件也不支持转换原型上的方法

需要使用另一个模块:

npm i @babel/polyfill -S

 该模块需要在使用新方法的地方直接引入:

import '@babel/polyfill'

source map的使用:

devtool

此选项控制是否生成,以及如何生成source map。

使用 SourceMapDevToolPlugin进行更细粒度的配置。查看source-map-loader来处理已有的source map。选择一种source map格式来增强调试过程。不同的值会明显影响到构建和重新构建。

可以直接使用 SourceMapDevToolPlugin/EvalSourceMapDevToolPlugin来代替使用devtool选项,它有更多的选项,但是切勿同时使用devtool选项和SourceMapDevToolPlugin/EvalSourceMapDevToolPlugin插件。因为devtool选项在内部添加过这些插件,所以会应用两次插件。

2. webpack高级配置

HTML中img标签的图片资源处理。

1. 安装npm install -S html-withing-loader

2. 在webpack.config.js文件中添加loader

{
    test:/\.(html|html)$/i,
    loader:'html-withing-loader',
}

使用时,只需要在html中正常引入图片即可,webpack会找到对应的资源进行打包,并修改html中的引用路径

多页应用打包

1. 在webpack.config.js中修改入口和出口配置

entry: {
     main: './src/main.js',
     other: './src/other.js'
  },
output: {
     path: path.join("$dirname, './dist/'),
     // filename: 'bundle.js',
 // 多⼊⼝⽆法对应⼀个固定的出⼝, 所以修改filename为[name]变量
    filename: '[name].js',
     publicPath: '/'
  },
 plugins: [
 // 如果⽤了html插件,需要⼿动配置多⼊⼝对应的html⽂件,将指定其对应的输出⽂件
    new HtmlWebpackPlugin({
    template: './index.html',
    filename: 'index.html',
    chunks: ['main']
  }),
 new HtmlWebpackPlugin({
     template: './index.html',
     filename: 'other.html',
     // chunks: ['other', 'main']
     chunks: ['other']
  })
 ]

2. 修改入口对象,支持多个js入口,同时修改output输出的文件名为'[name].js'表示各自已入口文件名作为输出文件名,但是html-webpack-plugin不支持此功能,所以需要再拷贝一份,用于生成两个html页面,实现多页面应用。

第三方库的两种引入方式

可以通过expose-loader进行全局变量的注入,同时也可以使用内置插件webpack.ProvidePlugin对每个模块的闭包空间,注入一个变量,自动加载模块,而不必到处import或require。

expose-loader将库引入到全局作用域。

1. 安装expose-loader

npm i -D expose-loader

2. 配置loader

module:{
    rules:[{
      test:require.resolve('jquery'),
      use:{
        loader:'expose-loader',
        options:'$'
        }
    }]
}

tips: require.resolve用来获取模块的绝对路径,所以这里的loader只会作用于jquery模块,并且只在bundle中使用到它时,才进行处理。

webpack.ProvidePlugin将库自动加载到每个模块。

1. 引入webpack

const webpack = require('webpack')

2. 创建插件对象

要自动加载jquery,我们可以将两个变量都指向对应的node模块

new webpack.ProvidePlugin({
    $:'jquery',
    jQuery:'jquery'
})

Development / Production 不同配置文件打包

项目开发时一般需要使用两套配置文件,用于开发阶段打包(不压缩代码,不优化代码,增加效率)和线上阶段打包(压缩代码,优化代码,打包后直接上线使用)

抽取三个配置文件:

webpack.base.js

webpack.prod.js

webpack.dev.js

步骤如下:

1. 将在开发环境和生产环境公用的配置放入base中,不同的配置各自放入prod或dev文件中

2. 然后在dev和prod中使用webpack-merge把自己的配置与base配置进行合并后到处

npm i -D webpack-merge

3. 将package.json中的脚本参数进行修改,通过--config手动指定特定的配置文件

定义环境变量

除了区分不同的配置文件进行打包,还需要在开发时知道当前的环境是开发阶段或线上阶段,所以可以借助内置插件DefinePlugin来定义环境,最终可以实现开发阶段与上线阶段的api地址自动切换。

1. 引入webpack

const webpack = require('webpack')

2. 创建插件对象,并定义环境变量

new webpack.DefinePlugin({
    IS_DEV:'false'
})

3. 在src打包的代码环境下可以直接使用

使用devServer解决跨域问题

HMR的使用

启用HMR只需要在webpack配置中添加:hot:true,

HMR(热模块替换)是一种 Webpack 提供的功能,它可以在代码修改后,自动更新特定模块,而不会刷新整个页面

与普通的 Live Reload(浏览器刷新)不同,HMR 只更新变更的模块,比如:

  • 修改 CSS 时,HMR 只替换 CSS 样式,不刷新整个页面。

  • 修改 JS 逻辑 时,HMR 只替换模块,不会影响已存在的应用状态(比如 Vue/React 组件的 UI 状态不会丢失)。

  • 修改 React/Vue 组件 时,组件会重新渲染,而不丢失 state

需要对某个模块进行热更新时,可以通过module.hot.accept方法进行文件监控。

只要模块内容发生变化,就会触发回调函数,从而可以重新读取模块内容,进行相应的操作。

HMR 的工作原理

HMR 的核心是 Webpack 在浏览器和 Webpack Dev Server 之间建立 WebSocket 连接,并在文件变更时执行以下步骤:

  1. 监听文件变化:Webpack 监视源码文件,一旦文件发生变化,重新编译该模块。

  2. 通知 Webpack Dev Server:Webpack 通过 WebSocket 告知浏览器有模块更新。

  3. 增量更新:浏览器端的 Webpack runtime 只替换修改的模块,而不刷新整个页面。

3. webpack优化

production模块打包自带优化

tree shaking

tree shaking 是一个术语,通常用于打包时移除Javascript中的未引用的代码,它依赖于ES6模块系统中import和export的静态结构特性。

开发时引入一个模块后,如果只使用其中一个功能,上线打包时只会把用到的功能打包进bundle,其他没用到的功能不会打包进来,可以实现最基础的优化。

scope hoisting

scope hoisting的作用是将模块之间的关系进行结果推测,可以让 Webpack 打包出来的代码文件更小、运行的更快。

scope hoisting的实现原理其实很简单:分析出模块之间的依赖关系,尽可能的把打散的模块合并到一个函数中,但前提是不能造成代码冗余。

因此只有那些被引用了一次的模块才能被合并。

由于scope hoisting需要分析出模块之间的依赖关系,因此源码必须采用ES6模块化语句,不然它将无法生效。

代码压缩:

所有代码使用uglifyJsPlugin插件进行压缩、混淆。

css优化

将css提取到独立文件中

mini-css-ectract-plugin是用于将css提取为独立的文件的插件,对每个包含css的js文件都会创建一个css文件,支持按需加载css和sourceMap

只能用在webpack4中,有以下的优点:

  • 异步加载
  • 不重复编译,性能很好
  • 容易使用
  • 只针对css

使用方法:

1. 安装

npm i -D mini-css-extract-plugin

2. 在webpack配置文件中引入插件

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

3. 创建插件对象,配置抽离的css文件名,支持placeholder语法

new MiniCssExtractPlugin({
    filename:'[name].css'
})

4. 将原来配置的所有style-loader替换为MiniCssExtractPlugin.loader

{
 test: /\.css$/,
 // webpack读取loader时 是从右到左的读取, 会将css⽂件先交给最右侧的loader来处理
// loader的执⾏顺序是从右到左以管道的⽅式链式调⽤
// css-loader: 解析css⽂件
// style-loader: 将解析出来的结果 放到html中, 使其⽣效
// use: ['style-loader', 'css-loader']
 use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader']
 },
 // { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
 { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
 // { test: /\.s(a|c)ss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
 { test: /\.s(a|c)ss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass
loader'] },

自动添加css前缀

使用postcss,需要用到postcss-loader autoprefixer

2. 修改webpack配置文件中的loader,将postcss-loader放置在css-loader的右边(调用链从右到左)

{
 test: /\.css$/,
 // webpack读取loader时 是从右到左的读取, 会将css⽂件先交给最右侧的loader来处理
// loader的执⾏顺序是从右到左以管道的⽅式链式调⽤
// css-loader: 解析css⽂件
// style-loader: 将解析出来的结果 放到html中, 使其⽣效
// use: ['style-loader', 'css-loader']
 use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader']
 },
 // { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
 { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 
'less-loader'] },
 // { test: /\.s(a|c)ss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
 { test: /\.s(a|c)ss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss
loader', 'sass-loader'] },

3. 项目根目录下添加postcss的配置文件:postcss.config.js

4. 在postcss的配置文件中使用插件

module.exports = {
    plugins: [require('autoprefixer')]
}

开启css压缩

需要使用optimize-css-assets-webpack-plugin的插件来完成css压缩

但是由于配置css压缩时会覆盖掉webpack默认的优化配置,导致JS代码无法压缩,还需要手动把JS代码压缩插件导入进来:terser-webpack-plugin

1. 安装

npm i -D optimize-css-assets-webpack-plugin terser-webpack-plugin

2. 导入插件

const TerserJSPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')

3. 在webpack配置文件中添加配置节点

optimization: {
 minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
 },

tips:webpack4默认采用的JS压缩插件为uglifyjs-webpack-plugin,在mini-css-extract-plugin上一个版本中还推荐使用该插件,但最新的v0.6中建议使用teser-webpack-plugin来完成js代码压缩,具体原因未在官网说明,那就按照最新文档即可。

JS代码分离

Code Splitting是webpack打包时用到的重要的优化特性之一,此特性能够把代码分离的到不同的bundle中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。

ps:

正在努力加工中……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值