核心
webpack 是用来搭建前端工程的,根据入口文件分析所有模块的依赖关系,然后对模块进行合并,压缩,形成最终的打包结果
使用
以下是使用 Webpack 构建项目的基本步骤:
- 安装 Webpack:
npm install --save-dev webpack webpack-cli
在上面的命令中,webpack 是 Webpack 的核心库,webpack-cli 是 Webpack 的命令行工具。
- 创建 Webpack 配置文件:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
在上面的配置中,entry 属性指定了 Webpack 的入口文件,output 属性指定了 Webpack 的输出文件。
- 在项目中创建源代码和输出目录:
|- /src
| |- index.js
|
|- /dist
在上面的目录结构中,src 目录是项目的源代码目录,dist 目录是 Webpack 的输出目录。
- 执行 Webpack 命令:
Copy code npx webpack
在执行完成后,Webpack 会根据配置文件生成打包后的文件,并将其输出到 dist 目录中。
带来的好处
- 将非JS 代码视为模块
- 开发中可使用npm,无论是npm安装的模块,还是自己写的模块都视为依赖,最终视为打包结果
- 适合开发单页应用,单页应用只有一个html页面,所有的内容由js生成
单页模板
对于单页应用而言,只有一个空白的页面,所有内容都靠JS代码创建
webpack会自动生成一个页面,并且在页面中会自动加入对js和css的引用
它生成页面时,参考的是 public/index.html,其称之为页面模板
public目录
webpack会非常暴力的将public目录中的所有文件(除页面模板外),复制到打包结果中
开发服务器
如果每次修改完代码,都要经过 打包->运行,未免太过麻烦
在开发阶段,我们可以运行 npm run serve命令获得更好的打包体验
该命令会让 webpack启动一个开发服务器。
在这个阶段,webpack并不会形成打包结果文件,而是把打包的内容放到内存中,当我们请求服务器时,服务器从内存中给予我们打包结果
与此同时,当源码发生变动时,webpack会自动重新打包,同时刷新页面以访问到最新的打包结果
文件缓存
除了页面外,其他资源打包完成后,文件会有一些字符,这些字符称为hash,他会随着模块内容的变化而变化, 源码内容不变,hash不变,源码内容改变,hash变化,
这么做的原因,生产环境,浏览器会对页面歪的静态资源进行缓存,如何不设置hash.一旦资源更新,浏览器还会使用之前缓存的结果,无法使用最新的代码
资源路径
源代码和样式模块除外,其他模块视为资源模块 由于src目录资源的路径,和打包后的资源路径不一样,导致我们编写代码时无法得知最终的路径 因为webpack非常智能的发现了这一点,对于css中的路径,webpack在打包时,会将其自动转换为打包结果的路径,比如,上面的代码在打包完成后,可能被转换为下面的格式
.container{
/* css中的资源路径会被自动替换,我们无须关心 */
background: url(/img/1492ea.png);
}
但如果我们要通过js动态的使用路径,webpack是无法识别的
// 打包前
const url = './assets/1.png'; // 该路径无法被转换
img.src = url;
// 打包后
const url = './assets/1.png'; // ❌
img.src = url;
正确的做法是,通过模块化的方式导入资源,并获取资源路径
// 打包前
import url from './assets/1.png'; // 打包后,url得到的将是真实的路径
img.src = url;
// 打包后
const url = '/img/1492ea.png'; // ✅
img.src = url;
缺省的文件和后缀名
导入模块时,所有js模块均可省略 .js,若导入的模块文件名为 index.js,可省略文件名
import './home'; // 若存在home.js,可省略js
import './movie'; // 若movie是一个目录,此次导入的是 ./movie/index.js
webpack提供了别名供我们快速定位到 ./src目录,通常,该别名为 @
上面的导入代码可简化为
路径别名
随着体量的增长,不可避免的,会形成层级极深的目录
import '@/b/b1'; // @表示src目录,同时省略了index.js
js兼容性
当webpack读取到js代码时,会自动对其进行兼容性处理
具体的处理方案涉及到两个配置文件:
解释: 市场份额大于 1%, 兼容最近三个版本, 没有消失
babel.config.js:通过配置该文件,可以设置对哪些js代码进行降级处理.browserslistrc:通过配置该文件,可以设置在降级时,要兼容哪些浏览器,兼容的范围越广,降级产生的代码就越多,自然,打包后的体积就越大
打包压缩
webpack在打包时,会对所有js和css代码进行压缩
对于js,除了压缩之外,还会对其中的各种名称进行混淆
混淆的作用一方面是为了进一步压缩包体积,另一方面是为了让我们的代码更难被其他人理解利用
源码地图 source map
我们运行的是webpack打包后的结果,而打包后的结果是很难阅读的
但这样一来会带来新的问题,如果代码报错,我们就难以知道到底是那一行代码写的有问题
此时源码地图就发挥了作用
可以发现,js代码打包后都会跟上一个同名的、后缀为 .map的文件,该文件就保存了原始代码的内容
请放心,这个内容人类是看不懂的,但浏览器可以看懂
当代码报错时,浏览器会定位到源码地图中的对应代码,而不是把真实报错的代码展示给我们
css工程化
webpack能够识别所有的样式代码,包括 css、less、sass、stylus
在打包时,会将它们转换成纯正的 css
webpack会根据 .browserlistrc中指定的浏览器范围,按需、自动加上厂商前缀
当样式文件以 xxx.mdoule.xxx的方式命名时,webpack会将该文件当成一个开启了 css module的文件
比如:index.module.less、movie.module.css,都是开启了 css module的文件
文件中的所有类名都会被hash化
文件中的所有类名都会被hash化
// 源码
.container{}
.list{}
.item{}
// 打包结果,绝无可能重名
._2GFVidHvoHtfgtrdifua24{}
._1fsrc5VinfYHBXCF1s58qS{}
.urPUKUukdS_UTSuWRI5-5{}
现在就一个问题,我们在使用类名时,如何知道它打包结果的类名呢?
import './index.module.less';
dom.classList.add('container'); // ❌ 最终的类名可不是这个
正确的方式如下:
// styles 是一个对象,里面映射了源码类名和打包类名的关系
import styles from './index.module.less';
dom.classList.add(styles.container); // ✅ 属性container中记录的就是container转换后的类名
使用影响
- 学会访问开发服务器查看效果
- 学会动态获取资源文件路径
import url from './assets/1.png'; img.src = url; - 学会省略文件和后缀名
import './home'; // 若存在home.js,可省略js import './movie'; // 若movie是一个目录,此次导入的是 ./movie/index.js - 学会使用别名简化导入代码
import '@/b/b1'; // 实际导入: src/b/b1/index.js (若b1是目录) - 学会使用css module
// styles 是一个对象,里面映射了源码类名和打包类名的关系 import styles from './index.module.less'; dom.classList.add(styles.container);