初识webpack前端构建工具

166 阅读6分钟

1 webpack简介

webpack is a module bundler -- 一言以蔽之,webpack就是一个模块化打包工具

我们日常开发过程中编写的代码可视为源码,而webpack将从入口文件出发,依次识别出模块导入语句,将所需的文件打包输出成一个或多个独立文件。

2 webpack安装

2.1 环境

注意需要nodeJS 环境才能运行,没有安装node的需要去安装,nodejs.org/en/

2.2 安装

推荐在项目初识化后,进入到项目内安装,不建议全局安装~

# 项目中安装到开发依赖中
npm i -D webpack
# 安装指定版本
npm i -D webpack@<version>
# webpack4以后需要同时安装webpack和webpack-cli
npm i -D webpack-cli

2.3 检查安装版本

如下可以检查项目内的webpack

npx webpack -v
./node_modules/.bin/webpack -v

3 webpack启动

3.1 webpack默认配置

webpack4以后是可以支持零配置运行的,也就是说可以不配置webpack.config.js这样一个文件,就可以实现构建,但是很弱,只支持js和json文件的打包;

默认配置如下:

const path = require("path");
module.exports = {
 // 入口文件地址
 entry: "./src/index.js",
 output: {
 		// 打包输出文件名
 		filename: "main.js",
 		// 打包输出文件绝对地址
 		path: path.resolve(__dirname, "./dist")
 }
};

3.2 准备

可以创建src文件然后写一个index.js如下:

### index.js
const json = require("./index.json");//commonJS
import { add } from "./other.js";//es module
console.log(json, add(2, 3));
### index.json
{
 "name": "JOSN"
}
### other.js
export function add(n1, n2) {
 return n1 + n2;
}

3.3 执行构建

# npx 方式
npx webpack
# npm script
npm run dev

# package.json 添加如下的命令
"scripts": {
 		"dev": "webpack"
}

正常构建成功,可以在项目目录中看到一个dist目录,且其下有打包成的main.js文件;

4 webpack核心配置

通过上面3.1中所说的默认配置,我们可以看到配置文件webpack.config.js 是通过module.exports的形式暴露了一个配置对象,我们需要根据自己的业务需求,结合loader和plugin来配置即可;

关于module.exports, export和export default:

blog.csdn.net/qq_40523572…

4.1 entry-入口

入口文件设置,有三种形式, 注意字符串和数组都是打包到一个文件,而对象形式的配置是用于打包多个js文件适用于mpa;

  • 字符串:

    entry:"./src/index.js"

  • 数组

    entry:["./src/index.js","./src/login.js"]

  • 对象

    entry:{ index:"./src/index.js", login:"./src/login.js" }

4.2 output-输出

如下的配置中name是占位, 对应入口中的文件名,chunkhash占位指的是本次打包产出hash值,文件有修改更新时,每次构建的hash值会不同;

// 对象形式定义输出文件路径和文件名称
output: {
 filename: "[name][chunkhash:8].js",
 path: path.resolve(__dirname, "dist")
},

扩展:

这里涉及到前端缓存的问题,当项目在服务器部署以后,客户端访问链接获取到对应的html文件,此时html文件中的main_[hash].js会被缓存到浏览器,服务端可以设置过期时间来实现前端性能优化,也就是再次访问时可以取缓存。此时当我们迭代前端代码并上线后,构建出的main_[hash].js文件会与之前不同,浏览器就会重新向服务器发请求来获取这个新的****main_[hash].js,那么我们就可以看到修改后的页面;

这就是为什么前端有缓存以提高性能,网站更新后还能让用户可获取最新应用;

4.3 mode-构建模式

mode的值有三种:

  • production :用于正式环境构建,触发自身的一些优化压缩函数;

  • development :用于开发环境的构建,更好的服务于开发时热更新;

  • none:没有任何默认的优化项目;

更详细的说明见如下:www.webpackjs.com/concepts/mo…

4.4 module-模块

webpack就是用来将各个模块文件打包到一起的,而上面提到过webpack打包时可以对引入的js和json进行打包,那么遇到其他文件时如何解析处理呢?这时候就需要配置module了,module以对象形式进行配置,可以看做是模块解析规则配置,描述的就是匹配到什么样的文件,指定用什么loader去解析;

配置的举例如下:

module: {
   rules: [// 此处如果配置多个,执行有顺序,从右到左侧,从下到上;
     {
       test: /\.(png|jpe?g|gif)$/,// 对于png,jpg,jpeg,gif的文件使用file-loader
       use: {
         loader: "file-loader",
         options: {
           name: "[name]_[hash].[ext]",// 名字并添加hash,ext是与原来文件后缀名一致
           outputPath: "images/"// 输出到dist/images/目录下
         }
       }
     },
     {// 处理css文件
       test: /\.css$/,
       use: ['style-loader', 'css-loader']
     },
     {// 处理less文件
       test: /\.less$/,
       // loader从右向左依次处理,postcss-loader+autoprefixer 实现自动补全css前缀,
       // 注意需要配置postcss.config.js
       use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']
     }
   ]
 },
// postcss.config.js
const autoPrefix = require("autoprefixer");
module.exports = {
    plugins: [
        autoPrefix({
            overrideBrowserslist: ["last 2 versions", ">1%"]
        })
    ]
}

4.5 loader

loader可以看作是模块转化器,它可以将源码中现有的文件转化为我们需要的形式;

比如:

file-loader 处理图片或字体文件,作用就是对静态资源复制放到打包输出文件中,并让js可以正确引用到对应路径;

css-loader和style-loader可以将样式文件中的内容提取出来放到style标签中;

4.6 plugins-扩展插件

在webpack执行的某个过程中,可以添加一些拓展逻辑来实现你的需求,比如生成html模版将新构建的js引入到其中,再或者是上传服务器直接部署上线;

HtmlWebpackPlugin

这里说一个比较常用的插件,就是这个插件可以实现我们刚才说的,创建html引入最新js;

配置方法:

安装

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

配置

// webpack.config.js
const htmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
 ...
 plugins: [
   new htmlWebpackPlugin({
     title: "My App",
     filename: "app.html",
     template: "./src/index.html"
   })
 ]
};

//index.html
<!DOCTYPE html>
<html lang="en">
 <head>
   <meta charset="UTF-8" />
   <meta name="viewport" content="width=device-width, initialscale=1.0" />
   <meta http-equiv="X-UA-Compatible" content="ie=edge" />
   <title><%= htmlWebpackPlugin.options.title %></title>
 </head>
 <body>
 	<div id="root"></div>
 </body>
</html>

结果

在打包后的dist目录中出现一个app.html文件,并在其中插入了打包后的js文件;

clean-webpack-plugin

该插件作用于构建之前,在构建之前将上一次打包的结果清空后再进行重新打包;

配置方式:

安装

npm install --save-dev clean-webpack-plugin

配置

const { CleanWebpackPlugin } = require("clean-webpack-plugin");
...
plugins: [
 new CleanWebpackPlugin()
]
...

mini-css-extract-plugin

将css文件打包时抽离出来成为一个单独的文件再进入到html中,而不是使用style标签去插入;

配置方式:

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
...
// module > rules
{
  test: /\.css$/,
  use: [MiniCssExtractPlugin.loader, "css-loader"] //这里就不使用style-loader了
}
...
// plugins
new MiniCssExtractPlugin({
 filename: "[name][chunkhash:8].css"
})

4.7 Devtool-开发工具

此选项控制是否及如何生成source map,配置不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。

当然也可以使用 SourceMapDevToolPlugin 进行更细粒度的配置。

devtool各个值的说明:webpack.docschina.org/configurati…

开发环境推荐使用:

eval-cheap-module-source-map

eval--使用eval包裹代码

cheap--只映射行关系,不映射列,这样提高打包性能

module--第三方模块也需要映射关系

inline--不单独生成.map文件,将文件以base64形式写入到当前js文件里;

none--不使用映射

4.8 DevServer-开发服务器

webpack-dev-server 是在本地开发时启动一个服务器,可以让我们通过一个ip地址+端口号访问到打包的资源,可以直接调试开发效果;

安装

 npm install webpack-dev-server -D

配置

// package.json
...
"scripts":{
  "server":"webpack-dev-server"
}

// webpack.config.js
...
devServer:{
  contentBase:"./dist",
  open:true,
  port:3000
}

启动服务

npm run server

启动服务后,会发现dist⽬录没有了,这是因为devServer把打包后的模块不会放在

dist⽬录下,⽽是放到内存中,从⽽提升速度;

兼容问题

如果你使用的是 webpack: "^5.9.0",webpack-cli: "^4.2.0",webpack-dev-server: "^3.11.0",可能会遇到用webpack-dev-server 无法启动;可以尝试如下:

"scripts": {
  "server": "webpack serve --open Chrome.exe"
},

代理解决跨域

在开发阶段前端应用和后端API服务不在一个源,因此直接访问会跨域,那么可以使用webpack-dev-server的代理功能进行配置,如将/api开头的接口代理到目标服务,配置如下:

// webpack.config.js
...
devServer:{
  contentBase:"./dist",
  open:true,
  port:3000,
  proxy:{
  	"/api":{
    	target:"http://localhost:8080"
    }
  }
}

4.9 HMR热模块替换

可以在捕捉到文件更新时,自动重新构建并刷新浏览器展示最新的结果;

配置

// webpack.config.js
const webpack = require("webpack");
...
devServer: {
   contentBase: "./dist",
   open: true,
   hot:true, // 开启热模块更新
   hotOnly:true // hotOnly为true时,浏览器不⾃动刷新就可以替换最新结果;
}
...
plugins: [
  new CleanWebpackPlugin(),
  new HtmlWebpackPlugin({
  	template: "src/index.html"
  }),
  new webpack.HotModuleReplacementPlugin()//webpack内置的插件
],

// index.js
// js需要使⽤module.hot.accept来观察模块更新,从⽽更新
if (module.hot) {
  // 监听某个文件变化,然后执行某个操作
  module.hot.accept("./b", function() {
    document.body.removeChild(document.getElementById("number"));
    number();
  });
}

5 Babel

将es6,es7语法转化成es5语法输出,使我们在开发时不用考虑兼容性问题。

安装

npm i babel-loader @babel/core @babel/preset-env @babel/polyfill -D

配置

// webpack.config.js 
// module>rules
{
  test: /\.js$/,
  exclude: /node_modules/,
  use: {
    loader: "babel-loader",
    options: {
    	presets: ["@babel/preset-env"]
    }
  } 
}

到此为止,默认的Babel只⽀持let等⼀些基础的特性转换,Promise等⼀些还有转换过来,这时候需要借助@babel/polyfill,把es的新特性都装进来,来弥补低版本浏览器中缺失的特性。

@babel/polyfill

安装

由于是业务所需的代码了,因此应该安装到生产依赖

npm install --save @babel/polyfill

使用

//index.js 顶部
import "@babel/polyfill";

优化-按需加载

// webpack.config.js 
// module>rules
{
  test: /\.js$/,
  exclude: /node_modules/,
  use: {
    loader: "babel-loader",
    options: {
    	presets: [
      	["@babel/preset-env",
          {
            targets: {
              edge: "17",
              firefox: "60",
              chrome: "67",
              safari: "11.1"
            },
            corejs: 2,//新版本需要指定核⼼库版本
            useBuiltIns: "usage"// 自动检查并按需注⼊,但是需要安装@babel/polyfill
           }
 				]
      ]
    }
  } 
}

React配置

安装babel与react转换的插件@babel/preset-react

npm install --save-dev @babel/preset-react



// webpack.config.js 
// module>rules
{
  test: /\.js$/,
  exclude: /node_modules/,
  use: {
    loader: "babel-loader",
    options: {
    	presets: [
      	["@babel/preset-env",
          {
            targets: {
              edge: "17",
              firefox: "60",
              chrome: "67",
              safari: "11.1"
            },
            corejs: 2,//新版本需要指定核⼼库版本
            useBuiltIns: "entry"// 需要引入@babel/polyfill
           }
 				],
        "@babel/preset-react"// react
      ]
    }
  } 
}

若有收获,就点个赞吧