官网:webpack
中文网:webpack中文网
webpack是一种前端资源构建(打包)工具(npm run build),一个静态模块打包器。在webpack看来,前端的所有资源文件(js/json/css/image/less/sass...)都会作为模块处理。它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源。webpack可以解决当前web开发中所面临的困境,webpack提供了:
友好的模块化支持
代码压缩混淆
处理js兼容问题
性能优化 treesharking
目前绝大多数企业中的前端项目,都是基于webpack进行打包构建的。
一种是脚手架已经帮我们把基础的webpack给配置好了(居多)
纯自己手动写的webpack环境
将浏览器不认识的代码转化成浏览器认识的操作,称之为打包,对应的工具就是打包工具。
备注,后续需要使用webpack的场景:
如果项目是基于vue、react脚手架搭建的,那么使用到webpack的机会就非常小了,充其量只是一些webpack的简单配置(并非不使用webpack,只是脚手架已经帮我们搭建完毕webpack,不需要花过多的时间去配置它)
项目不使用脚手架搭建,而是通过webpack全手动去搭建,这个时候得天天使用webpack
好处在于,完全自定义化
不好在于,要求开发者会的东西得多
学习webpack也是为了了解vue和react的开发环境是如何运行的以及打包构建的流程。
核心概念即webpack(没有需要写的js代码,基本全是配置)的五个核心配置项。
entry入口
本项目应该使用哪个模块来作为构建其内部依赖图的开始(指定打包入口文件)。打包入口文件默认为src/index.js
output输出
在哪里输出它所创建的 bundles,以及如何命名这些文件,打包输出文件默认值为dist/main.js
loader加载器
loader让webpack能够去处理那些非js文件(webpack自身只理解js并且是非高级语法)
plugins插件
插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。
mode 模式
通过选择 development(打包出来的代码是没有经过压缩的) 或 production(默认值,代码经过压缩的) 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化
问题:讲究模块化开发,模块化开发这种写法浏览器支持吗? 【不支持】
如,以下模块化代码:
import $ from "jquery"; $(function () { console.log("页面载入完毕"); // 隔行换色 $("li:odd").css("background", "green"); $("li:even").css("background", "blue"); });
结果运行的时候无效果还报错。
疑问:为什么之前vue、react中模块化代码可以正常运行?
答:webpack的存在,所以人家能够正常运行
jQuery API 中文文档 | jQuery API 中文在线手册 | jquery api 下载 | jquery api chm
由于webpack本身只能处理js后缀且只支持部分js语法,从而导致在项目构建中需要依赖很多三方的插件和加载器。由于这些插件和加载器是三方的,与webpack的兼容处理并不是实时的,为了兼容常用的一些插件和加载器组合,在使用webpack的时候往往需要对webpack和需要使用插件、加载器去指定版本。
webpack是运行在node环境中的,需要Node>= 8.10和npm>=5.6的版本支持。在项目中安装webpack的方式如下:
npm i -D webpack@4 webpack-cli@3 # 由于目前这个2个工具大版本号已经升级,并且不兼容后续使用的工具,所以这边使用指定的低版本号
安装好后可以通过先前提及过的npx
命令来检查webpack的版本以确定是否安装成功:
npx webpack --version # npx可以帮助我们快速执行一些模块内部的命令
确认能够通过上述命令输出webpack
的版本信息后,再在package.json文件中的scripts
节点配置webpack运行脚本命令(==指令名称自行决定==):
此时,我们可以在终端中运行自定义命令npm run build
来对项目使用webpack打包,例如打包之前写的隔行换色
代码:
npm run build
打包完毕后会在当前项目目录下产生dist
目录,里面会包含一个main.js
文件,修改src/index.html
文件,将原先的JavaScript文件引入修改为打包好的文件:
最终效果如下(说明先前写的代码已经生效):
我们在第一节的概述中提及了webpack的五个核心概念,这五个核心概念都属于webpack的配置,因此,如果需要更好的运用webpack,我们需要掌握其配置文件的相关知识点。
配置的方式有两种:单配置文件形式(默认配置)、多配置文件形式。(二选一即可)
类似于vue中的环境变量:
.env
.env.production
.env.development
首先来看单配置文件方式:在项目根目录下面创建一个webpack.config.js文件,webpack运行的环境为nodejs环境,所以此文件中的模块化规范为commonjs规范写法。该配置文件创建好后会被webpack在打包时自动使用(因此,文件名不能写错)。
const path = require("path"); module.exports = { // 打包模式 development | production mode: "development", // 项目入口 entry: "./src/index.js", // 项目出口 output: { path: path.resolve(__dirname, "dist"), // [name]默认的名称为main(如果需要分目录,可以在名字前加文件夹名字) filename: "js/[name].js", }, };
另一种配置方式是:在项目根目录中创建一个config目录(为了便于后期管理,提前预设两套或多套webpack配置),专门用于存放webpack的相关配置文件:
由于这两个文件不似/webpack.config.js
文件会被webpack自动使用,因此需要在package.json
文件中进行分别引入指定:
这样一来,后续如果需要将项目打包成开发环境需要的代码则执行命令:
npm run build:dev
如果需要将项目打包成生产环境需要的代码则执行命令:
npm run build:pro
注意:由于在config下面的配置文件中指定了输出的路径,此时路径也需要做一下修改,否则打包的dist目录就会在"config/dist"这个位置。只需要给“dist”加上“../”即可:
path: path.resolve(__dirname, "../dist"),
在实际开发的时候推荐使用多套配置文件的方案,便于管理不同环境下需要的打包的配置,避免来回切换配置。
问题:在打包好的代码中并没有index.html,还需要手动将html代码复制到dist目录中(或者可以在index.html中修改js脚本文件的引入路径),这个操作比较麻烦,能不能像vue项目一样,直接将html复制过去?
答:有
首先需要安装一个扩展模块,安装命令如下:
npm i -D html-webpack-plugin@4
然后修改webpack的配置文件(此时需要注意你使用的是哪种配置文件方式,单一or多个,并做对应的修改),增加以下配置项:【此处以production.js配置为例】
const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { // .... plugins: [ new HtmlWebpackPlugin({ // template是必须的(路径是相对于项目的根) template: "./public/index.html", }), ], }
使用该款插件后,会自动在视图中去帮我们引入打包好的文件main.js
:
这款插件在工作的时候帮我们实现了2个操作:
复制对应的template模版文件到dist目录下
将打包好的js文件,自动引入到模板文件中
需注意:最好将自己原先在html中引入的外部文件给去掉,因为它会自己加,自己写的不去除会出现以下两种情况之一:
重复引入(无意义)
引入文件出现404情况
通过上一节,我们已经可以使用打包工具去将写好的代码进行打包了,但是在操作的过程中大家可能会发现有一个比较麻烦的地方:修改一次代码就得重新打包一次,这种感觉有点类似于之前的node xxx.js
一样,那么在webpack这里是否有类似于nodemon
那么好用的自动化工具能帮助我们自动检测文件的变化并自动执行呢?答案是有的,它就是webpack-dev-server
自动化打包工具。
类似于:
在vue中,我们并不是每次改完代码都打包看效果,而是在开发过程中有一个测试服务器,自动帮我们产生预览效果,但是并会真的去打包,只有等上线的时候我们才去打包。实时预览服务器可以通过工具webpack-dev-server。
webpack-dev-server
的安装指令如下:
npm i -D [email protected]
开发服务器的服务支持一系列配置选项,可以根据以下代码取所需的配置:
module.exports = { devServer: { // 端口号 port: 8080, // 域名 host: "localhost", // 自动打开浏览器(可能因为计算机安全策略导致不生效) open: true, // 服务器代理 --> 解决开发环境跨域问题 proxy: { // 一旦devServer服务器接受到 /api开头的请求,就会把请求转发到另一个服务器 "/api": { target: "http://localhost:3000", // 发送请求时,请求路径重写: 将/api 去除 pathRewrite: { "^/api": "", }, }, }, }, };
安装好后同样也需要在package.json
中配置自定义执行的指令:
随后即可通过自定义指令来运行自动打包服务:
# 该命令执行完后命令行会如同执行`nodemon`一样处于实时检测的状态 npm run serve
启动打包服务后,即可通过浏览器访问http://127.0.0.1:8080来浏览自动打包好的项目。
注意点
上述过程中,会自动产生并运行一个临时性的服务器供我们预览,访问地址是:http://127.0.0.1:8080
上述过程中,打包生成的输出文件会托管在项目根下,但文件是虚拟的,是无法看见的,也就是说该操作并不会产生前面所谓的
dist
目录,如果需要得到打包好的目录,还是得运行之前的打包命令在此处系统会自动引入需要的外部文件,如果此前我们自己已经引入了,则需要去除自己引入的外部文件
在实际开发中,webpack只能打包处理以.js
为后缀的模块(并且是其中一部分比较简单的JavaScript代码),其他非.js
后缀的模块webpack默认处理不了,而需要调用loader加载器才能正常打包,否则会报错!
loader加载器可以协助webpack打包处理特定的文件模块了,例如:
less-loader可以打包处理.less
相关的文件
sass-loader可以打包处理.scss
相关的文件
...
正如前面所说,webpack默认不能打包css文件,如果在没有安装css加载器的时候打包包含css文件的项目则会报错:
根据报错提示,我们需要安装一个合适的加载器才能继续。
所以要想打包css文件,则需要安装css加载器,该加载器的安装命令为:
npm i -D style-loader@2 css-loader@5 # style-loader:将处理好的样式内嵌到html中(再执行) # css-loader:处理css后缀的文件的(先执行)
安装好需要的加载器后需要对webpack进行配置,告诉webpack当遇到css后缀的文件应该交由哪个加载器去处理。在webpack打包命令对应的module
的rules
数组中添加css-loader规则:
module: { rules: [{ test: /\.css$/, use: ["style-loader", "css-loader"] }], },
在写加载器use
的时候,需要注意:
use数组中指定的加载器顺序是固定的,==顺序不能随意调换==
要想通过webpack打包less文件,同样需要安装对应的加载器:
受webpack-dev-server包的影响,less-loader包只能安装7.x。
npm i -D less-loader@7 less
安装好后也需要在对应的webpack配置文件中配置针对less文件打包的规则:
module: { rules: [ {test: /\.less$/,use: ["style-loader","css-loader","less-loader"]} ] }
也是一样,需要注意顺序问题。
通过前面的学习,细心的同学会发现,打包好的html文件在浏览器运行时,“审查元素”时 能够看到所有的样式代码都以“style”标签写入了html,这些样式都是由JavaScript动态生成的,每次都这样动态生成:
消耗性能
在html中本身是没有style代码的,而是通过js后续生成的,再塞入到html中
无法做静态资源加速(CDN,内容分发网络)
无法直接更改打包好的样式
因此,此处可以对该部分进行改善操作。我们可以让webpack在打包时直接将这些样式抽成一个样式文件。步骤如下:
安装插件
npm i -D [email protected]
导入插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
配置插件
new MiniCssExtractPlugin({ filename: "css/[name]_[hash:6].css", }),
使用插件
MiniCssExtractPlugin.loader(使用它去替换之前的“style-loader”)
在webpack5中,针对图片处理已经不再使用加载器了,可以使用webpack内置的Asset Module处理。
如果在样式表/视图中使用了图片(远程图片不受影响,本地图片受加载器的影响),则也需要配置对应的加载器才能进行正确的打包操作。加载器的安装命令如下:
npm i -D url-loader file-loader html-loader@1
安装好后也需要在webpack配置文件中进行对应的规则配置:
module: { rules: [ { test: /\.(png|jpeg|jpg|gif)$/i, use: [ { loader: "url-loader", options: { // 图片小于8kb,就会被base64处理 // 优点: 减少请求数量(减轻服务器压力) // 缺点:图片体积会更大(文件请求速度更慢) limit: 8 * 1024, // 打包后的路径和文件名称 [ext]扩展名 name: "img/[name].[ext]", // 打包后的文件指定访问路径前缀 publicPath: "/", }, }, ], }, { test: /\.html$/, // 处理html中的img(负责引入img,从而能被url-loader进行处理) loader: ["html-loader"], }, ] }
目前webpack4采用的是三方的加载器,在webpack5中针对图片已经支持使用内部自带的assets模块进行处理。
webpack在不需要引入任何loader不可以对于js进行打包处理,但是它不会对于js兼容性进行任务的处理,而我们编写的项目是需要在不同的浏览器中运行的,此时就需要对于js的兼容性在打包过程中进行对应的处理。我们可以使用babel来完成对应的js兼容处理。
首先先需要去安装babel转换器相关的包:
npm i -D babel-loader@8 @babel/core @babel/runtime
再去安装babel语法插件相关的包:
npm i -D @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-proposal-class-properties
然后再去在项目根目录下创建babel配置文件babel.config.js并初始化:
module.exports = { presets: ["@babel/preset-env"], plugins: ["@babel/plugin-transform-runtime","@babel/plugin-proposal-class-properties"] }
最后再在webpack的配置文件中设置打包规则:
module: { rules: [ { test: /\.js$/, use: "babel-loader", exclude: /node_modules/ } ] }
配置完毕后,webpack即支持打包处理js中的高级语法。
常见的加载器就介绍到这里,如果后续其他文件加载器的需求,请百度解决!
前面所使用webpack打包方式是针对单一入口的项目,那么如果项目中有多个入口需要处理,我们只需要将webpack的入口配置配置成多入口的形式即可。
例如,我们有index.js
和login.js
两个入口,则需要将entry入口配置写成以下形式:
entry: { main: "./src/index.js", login: "./src/login.js", // .... },
注:后续大部分的时候是用不到这种情况的。
SPA single page application 单页面应用
在vue工程化的时候,@=/src,但是这东西在后续的react中是不支持的。
在先前学习Vue的时候提及并使用过@
,当时说@
表示src
目录,这样一来,在我们自己写的代码中作文件导入的时候路径写起来比较轻松,但是当前的项目中如果使用@
导入在打包时就会出现类似如下的错误:
这就说明,当前项目并不支持我们在导入路径中使用@
符号,如果想要支持之前的这种简化写法,需要我们配置解析规则(vue项目中是工具已经帮我们配置好了,所以当时可以直接使用)。
配置方式如下:修改webpack的配置文件,在配置选项中添加resolve
选项,增加别名配置:
resolve: { // 配置解析模块路径别名:优点简写路径,缺点路径没有提示 alias: { // 定义一个@,可在import引入时使用 "@": path.join(__dirname, "../src"), }, // 设置可以忽略不写的后缀 extensions: [".js"], },
这时,再写模块导入路径的时候即可简化路径写法。
webpack中的externals
选项提供了不从bundle捆绑中使用依赖的方式。
在开发项目时,有些外部模块通过CDN链接
使用script
标签引入到页面中可能要比通过打包使用更加方便,例如jQuery库。此时就可以使用externals
忽略打包的方式去指定哪些库不需要webpack进行打包。
请注意,不被webpack打包的外部依赖,后期依旧可以通过以下方式在项目中使用:
script
标签链入远程/本地js文件(推荐)
import导入
import ... from 'xxx'
import { a,b,c } from 'xxx'
externals
选项指定实现的方式比较简单,只需要在webpack配置文件中添加externals选项指定需要忽略的包信息即可。
例如,需要忽略对jQuery的打包,则可以写成:
externals: { jquery: 'jQuery', // ..... }
补充:关于在react中,如何使用通过script标签引入的文件中的全局变量或方法
例如:把jQuery放在react中的index.html中,然后我们去组件中去使用“$”或者“jQuery”
与前面的css、less、scss等文件一样,webpack默认是不能处理vue后缀的文件的,如果需要让其支持打包处理vue文件,也需要安装和配置对应的loader加载器。
可以先在项目中安装vue,随后写一个vue文件供测试使用:
npm i -S vue@2
所提供的demo测试vue文件代码/src/vue/h5.vue
:
{{ msg }}
再去创建vue的访问入口文件/src/vue/app.js
:
import Vue from "vue"; import App from "@/vue/h5.vue"; new Vue({ el: '#app', render: h => h(App) })
不要忘记在html文件中写上渲染的容器:
在打包入入口中指定vue文件的入口:
entry: { h5: "./src/vue/app.js", // ..... },
如果在打包时出错,则需要安装对应的loader并且配置,安装指定如下:
npm i -D vue-loader@15 vue-template-compiler
安装好对应的加载器后需要进一步配置打包规则,首先需要在webpack的配置文件中引入vue-loader的插件:
const VueLoaderPlugin = require("vue-loader/lib/plugin"); module.exports = { plugins: [ new VueLoaderPlugin(), ], module: { rules: [ { test: /\.vue$/, loader: "vue-loader", }, ] } }