核心
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);