核心
- webpack 是用来搭建前端工程的
- 它运行在node环境中,它所做的事情,简单来说,就是打包
- 具体来说,就是以某个模块作为入口,根据入口分析出所有模块的依赖关系,然后对各种模块进行合并、压缩,形成最终的打包结果
- 在webpack的世界中,一切都是模块
体验
按照习惯,所有的模块均放置在 src 目录中
- 安装依赖
- 编写多个模块 随意模块,可以是js、图片、音视频,以入口模块为起点,形成依赖关系
- 运行
npm run build
命令,进行打包 - 查看打包结果 ,打包结果放置在dist目录中
不难发现,webpack给我们带来了以下好处:
- 可以大胆的使用任意模块化标准,无须担心兼容性问题,因为webpack完成打包后,已经没有了任何模块化语句
- 可以将一些非 js 代码也视为模块,这样可以对 css 、图片等资源进行更加细粒度的划分
- 在前端开发中,也可以使用npm。webpack不会运行源代码,无论是自己写的模块还是通过npm安装的模块,webpack 一律认为是依赖,最终合并到打包结果中
- 非常适合开发单页应用,单页应用是前端用户体验最好的web应用,所谓单页应用,是指只有一个html页面,页面中没有任何内容,所有的内容均靠js生成,要优雅的实现单页应用,最好依托于前端框架,比如vue、react
- webpack 给我们开发带来的变化远不止于此
页面模版
- 对于单页应用而言,只有一个空白的页面,所有内容都靠 js 代码创建
- webpack 会自动生成一个页面,并且在页面中会自动加入对 js 和 css 的引用
- 它生成页面时,参考的是 public/index.html,其称之为页面模版
public目录
- webpack 会非常暴力的将 public 目录中的所有文件(除页面模板外),复制到打包结果中
开发服务器
- 如果每次修改完代码,都要经过 打包-> 运行,太过麻烦了
- 在开发阶段,我们可以运行
npm run serve
命令获得更好的打包体验 - 该命令会让
webpack
启动一个开发服务器 - 在这个阶段,webpack并不会形成打包结果文件,而是把打包的内容放到内存中,当我们请求服务器时,服务器从内存中给予我们打包结果
- 与此同时,当源码发生变动时,webpack会自动重新打包,同时刷新页面以访问到最新的打包结果
文件缓存
- 除了页面外,其他的资源在打包完成后,文件名多了一些奇奇怪怪的字符
- 例如:js/app-22324a23.js
- 其中,22324a23 这样的字符称之为hash,它会随着模块内容的变化而变化
- 源码内容不变,hash不变;源码内容变化,hash变化
- 之所以这样做,是因为生产环境中,浏览器会对除页面外的静态资源进行缓存
- 如果不设置hash值,一旦代码更新,浏览器还会使用之前缓存的结果,无法使用最新的代码
- **webpack会在打包时自动处理 hash 值,并不会对我们写代码造成任何影响,但作为一个前端开发者,有必要了解一下
资源路径
-
除代码和样式模块外,其他模块被视为资源模块
-
值得注意的是,资源模块在源代码中的路径和打包后的路径是不一样的,这就导致了我们在编写代码的时候,根本无法知晓资源最终的路径
-
最常见的例子就是在css中使用背景图片
css
.container {
/* 背景图使用了源码中的路径 */
background: url('../assets/1.jpg');
}
-
这里可以正常工作,因为webpack 可以非常智能的发现这一点,对于css中的路径,webpack在打包时,会将其自动转为打包结果的路径
-
上面的例子可能被转换为下面的代码:
css
.container {
background: url(/img/1234aa.jpg);
}
- 但是我们要通过 js 动态的使用路径,webpack 是无法识别的
js
// 打包前
const url = './assets/1.png'; // 路径无法被转换
img.src = url;
// 打包后
const url = './assets/1.png'; // 错误
img.src = url;
- 正确的做法是,通过模块化的方式导入资源,并获取资源路径
js
// 打包前
import url from './assets/1.png'; // 打包后,url得到的是打包后的路径
img.src = url;
// 打包后
const url = '/img/1234aa.png'; // 正确
img.src = url;
缺省的文件和后缀名
- 导入模块时,所有 js 模块均可省略 .js ,若导入的模块文件名为 index.js ,可省略文件名
js
import './home'; // home.js
import './movie'; // 若movie是一个目录,则导入的是./movie/index.js
路径别名
- 随着代码量的增加,不可避免的是形成层级深的目录
- webpack 提供了别名供我们快速定位到 ./src 目录,通常,该别名为@
js 兼容性
- 当webpack 读取到 js 代码时,会自动对其进行兼容性处理
- 具体的处理方案涉及两个配置文件
-
babel.config.js
: 配置该文件,可以设置对哪些 js 代码进行降级处理.browserslistrc
: 配置该文件,可以设置在降级时,要兼容哪些浏览器,兼容的范围越广,降级产生的代码就越多,自然打包后的体积就越大
打包压缩
- webpack 在打包时,会对所有的 js 和 css 代码进行压缩
- 对于 js ,除了压缩外,还会对其中的各种名称进行混淆
- 混淆的作用一是为了进一步压缩包体积,二是为了我们的代码更难被其他人理解利用
源码地图
- source map
- 打包后的结果是很难阅读的
- 如果代码报错,我们将难以得知出错的代码行
- 可以发现,打包后都会跟上一个同名的、后缀为 .map 的文件,该文件就保存了原始代码的内容
- 这个内容人类是看不懂的,但是浏览器可以看懂
- 代码报错时,浏览器定位到源码地图中的对应代码,而不是把真实报错的代码展示给我们
css工程化
- webpack 能够识别所有的样式代码,包括 css、 less、sass、stylus
- 打包时,便会将它们转换为纯正的 css
自动厂商前缀
- css有很多兼容性问题,解决这些问题最常见的办法就是加上厂商前缀
- webpack 会根据
.browserlistrc
中指定的浏览器范围,按需自动加上厂商前缀
css module
- css 文件多了后,为了保证它们之间没有冲突的类样式
- 靠的是 css module
- 当样式文件以
xxx.module.xxx
的方式命名时,webpack 会将该文件当成一个开启了 css module 的文件 - 然后文件中的所有类名都将被 hash 化
- 我们在使用类名时,通过下面的方式得知打包后的类名
js
import './index.module.less';
dom.classList.add('container'); // ❌ 最终的类名可不是这个
// styles 是一个对象,里面映射了源码类名和打包类名的关系
import styles from './index.module.less';
dom.classList.add(styles.container); // ✅ 属性container中记录的就是container转换后的类名
其实webpack 并没有这么强大
- 实际上,它就是通过插件(plugin) 和加载器(loader)将这些技术整合在一起
.browserslistrc
:表达适配的浏览器范围,会被工程化中的其他技术所使用babel.config.js
:babel的配置文件,做 js 降级处理postcss.config.js
:postcss 的配置文件,做css代码转换webpack.config.js
:webpack 的配置文件,整合其他工程化技术,以及配置打包细节、开发服务器、路径别名等
对我们的影响(important)
- 学会访问开发服务器查看效果
- 学会动态获取资源文件路径(import url from './assets/1.jpg')
- 学会省略文件和后缀名(js文件和index.js文件)
- 学会使用别名简化导入代码(import '@/a/a1')
- 学会使用 css module(index.module.less)