初试 webpack

101 阅读6分钟

核心

image.png webpack 是用来搭建前端工程的,根据入口文件分析所有模块的依赖关系,然后对模块进行合并,压缩,形成最终的打包结果

使用

以下是使用 Webpack 构建项目的基本步骤:

  1. 安装 Webpack:
npm install --save-dev webpack webpack-cli

在上面的命令中,webpack 是 Webpack 的核心库,webpack-cli 是 Webpack 的命令行工具。

  1. 创建 Webpack 配置文件:
const path = require('path');

   module.exports = {
     entry: './src/index.js',
     output: {
       filename: 'bundle.js',
       path: path.resolve(__dirname, 'dist')
     }
   };

在上面的配置中,entry 属性指定了 Webpack 的入口文件,output 属性指定了 Webpack 的输出文件。

  1. 在项目中创建源代码和输出目录:
 |- /src
   |   |- index.js
   |
   |- /dist

在上面的目录结构中,src 目录是项目的源代码目录,dist 目录是 Webpack 的输出目录。

  1. 执行 Webpack 命令:
 Copy code   npx webpack

在执行完成后,Webpack 会根据配置文件生成打包后的文件,并将其输出到 dist 目录中。

带来的好处

  1. 将非JS 代码视为模块
  2. 开发中可使用npm,无论是npm安装的模块,还是自己写的模块都视为依赖,最终视为打包结果
  3. 适合开发单页应用,单页应用只有一个html页面,所有的内容由js生成

单页模板

对于单页应用而言,只有一个空白的页面,所有内容都靠JS代码创建

webpack会自动生成一个页面,并且在页面中会自动加入对js和css的引用

它生成页面时,参考的是 public/index.html,其称之为页面模板

public目录

webpack会非常暴力的将public目录中的所有文件(除页面模板外),复制到打包结果中

开发服务器

如果每次修改完代码,都要经过 打包->运行,未免太过麻烦

在开发阶段,我们可以运行 npm run serve命令获得更好的打包体验

该命令会让 webpack启动一个开发服务器

在这个阶段,webpack并不会形成打包结果文件,而是把打包的内容放到内存中,当我们请求服务器时,服务器从内存中给予我们打包结果

与此同时,当源码发生变动时,webpack会自动重新打包,同时刷新页面以访问到最新的打包结果

文件缓存

image.png

除了页面外,其他资源打包完成后,文件会有一些字符,这些字符称为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代码时,会自动对其进行兼容性处理

具体的处理方案涉及到两个配置文件:

image.png 解释: 市场份额大于 1%, 兼容最近三个版本, 没有消失

  • babel.config.js:通过配置该文件,可以设置对哪些js代码进行降级处理
  • .browserslistrc:通过配置该文件,可以设置在降级时,要兼容哪些浏览器,兼容的范围越广,降级产生的代码就越多,自然,打包后的体积就越大

打包压缩

webpack在打包时,会对所有js和css代码进行压缩

对于js,除了压缩之外,还会对其中的各种名称进行混淆

混淆的作用一方面是为了进一步压缩包体积,另一方面是为了让我们的代码更难被其他人理解利用

源码地图 source map

我们运行的是webpack打包后的结果,而打包后的结果是很难阅读的

但这样一来会带来新的问题,如果代码报错,我们就难以知道到底是那一行代码写的有问题

此时源码地图就发挥了作用

可以发现,js代码打包后都会跟上一个同名的、后缀为 .map的文件,该文件就保存了原始代码的内容

请放心,这个内容人类是看不懂的,但浏览器可以看懂

当代码报错时,浏览器会定位到源码地图中的对应代码,而不是把真实报错的代码展示给我们

css工程化

webpack能够识别所有的样式代码,包括 csslesssassstylus

在打包时,会将它们转换成纯正的 css

webpack会根据 .browserlistrc中指定的浏览器范围,按需、自动加上厂商前缀

当样式文件以 xxx.mdoule.xxx的方式命名时,webpack会将该文件当成一个开启了 css module的文件

比如:index.module.lessmovie.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);