Webpack(二):使用 loader

  • 2019 年 11 月 7 日
  • 笔记

Webpack 提倡一切皆模块,所有类型的文件(css、图片等)都可以经过 loader 处理变成我们可加载的模块。

1. loader 安装和配置

安装 loader 统一方式是 npm insatll xxx-loader --save -dev

-dev 表示开发时依赖。

webpack.config.js 基本配置方式:

module.exports = {    entry: ...,    output:{...},    module:{      rules:[        {          test: ...,          use:['xxx-loader','xxx-loader',...]        },        {...},        {...},      ]    }  }

2. 样式处理

2.1 css-loader 和 style-loader

css-loader 通过 npm 安装,但是要把样式真正挂载到 dom 上,还需要安装 style-loader

通过 webpack.conifg.js 配置 css-loader 和 style-loader。注意 webpack 是从右向左读取的,书写顺序有要求。

module:{    rules:[      {        test: /.css$/,        use:['style-loader','css-loader']      }    ]  }

2.1 less-loader

同样的,还可以使用 less-loader:

module:{    rules:[      {        test: /.css$/,        use:['style-loader','css-loader']      },      {        test:/.less$/,        use:[{          loader:"style-loader"        },{          loader:"css-loader"        },{          loader:"less-loader"        }]      }    ]  }

从 npm 3.x 开始,less 不会再随着 less-loader 的安装而自动给你安装,所以我们需要手动安装 less,也就是完整命令:npm install less less-loader --save -dev,否则后续打包会报错。

3. 图片打包

和图片打包相关的两个 loader ,一个是 url-loader,一个是 file-loader。

如果图片较多,会发送很多 http 请求,降低页面性能。因此,url-loader 会将引入的图片编码,生成 base64 的 dataURL —— 相当于把图片数据翻译成一串字符,再把这串字符打包到文件中,最终只需要引入这个文件就能访问图片了。

当然,如果图片较大,编码会消耗性能,打包文件体积也会变大,因此 url-loader 提供了一个 limit 参数(一般是8kb),小于 limit 的图片依然被转为 DataURL;大于 limit 的图片则调用 file-loader 进行copy,并输出到 dist 文件夹中。 虽说 url-loader 封装了 file-loader,但实测如果不额外安装 file-loader 的话,在图片体积较大时打包还是会报错的,因此两个都要安装。

下面通过例子说明在 CSS 或者 JS 中使用图片时,loader 是如何处理的。

2.1 css 引用

这里我们在 src 下新建 img 文件夹,里面放 test1.jpg 和 tets2.jpg,一张大于8kb,一张小于8kb。在前面的 index.css文件中,我们像往常一样使用图片作为背景:

div{    width:100px;    height:200px;    background:url("../img/test1.jpg");  }

之后打包发现报错,所以我们安装 url-loader,再次打包。浏览器查看:

会发现图片可以正常引用了,而且是以 dataURL 的形式引用的。

接着测试大于8kb的图片(修改上面代码为 test2.jpg )。这时,如果直接打包会报错提示缺少 file-loader,所以我们这里安装一下 file-loader。

再次打包,虽说这次不报错了,但是我们发现浏览器里图片没有显示出来。看一下控制台:

可以看到,路径是直接引用的图片名字,同时会看到 dist 文件夹下输出了原始图片的副本。也就是说,其实这时候 webpack 认为我们的 index.html文件在 dist 文件夹中,所以选择了这样的路径引用,但其实我们的 index.html文件在外层。同时,我们也不希望直接输出在 dist 文件夹下,最好是里面还有一个 img 文件夹,所以我们先来 webpack.config.js 配置一下:

{    test: /.(png|jpg|gif|jpeg)$/,    use:[{      loader:"url-loader",      options:{        limit:8192,        outputPath:'img'      }    }]  }

这里就设置了图片的输出路径,另外,图片默认以 32 位 hash 命名,这样太长了,而且也不知道具体是哪张图片,所以我们顺便配置一下图片命名规则:

{    test: /.(png|jpg|gif|jpeg)$/,    use:[{      loader:"url-loader",      options:{        limit:8192,        outputPath:'img',        name:'[name].[hash:8].[ext]'      }    }]  }

再次打包,打开控制台:

可以看到,命名正确了,文件输出方式也正确了(dist 下多出一个 img 文件夹),但是图片路径还是错的,所以不显示图片。我们接着来配置一下图片路径。 我们前面说过,webpack 认为 index.html 在 dist 文件夹中,所以才会直接通过图片名字引用图片。那么 index.html 实际上是在 dist 文件夹外面的,对于 index.html 来说,它就要通过 ./dist/img 才能顺利找到图片,也就是说,我们可以在原本路径(图片名)的基础上加一个固定前缀(./dist/img),使之正确指向图片位置(./dist/img/图片名)。

publicPath 正是可以用来做这件事的:

{    test: /.(png|jpg|gif|jpeg)$/,    use:[{      loader:"url-loader",      options:{        limit:8192,        outputPath:'img',        publicPath:'./dist/img',        name:'[name].[hash:8].[ext]'      }    }]  }

publicPath 会给使用了相对路径引用的图片加上统一前缀。比如我们的图片路径一开始是 img/test2.95a05a82.jpg,那么使用了 publicPath 后,图片路径就变成 ./dist/img/test2.95a05a82.jpg。通常可以给 publicPath 配置一个 cdn 地址前缀,比如 https://xxx.cdn.com,上线的时候图片就都统一使用 cdn 地址了。

那么配置好后再次打包,浏览器查看:

路径正常,而且图片也正常显示了。

另外,我们也可以选择给 output.publicPath 配置 ./dist/,这样的话不止是图片,所有使用相对路径引用的静态资源都会加上这个前缀了。不过要注意,这个前缀需要加一个 /,而图片的 publicPath 是不需要的。

2.2 js 引用

js 中引用图片需要使用 require,举个例子:

// module2.js    // require 拿到图片路径字符串  var img = require('../img/test2.jpg');  // 模板字符串构建 img 标签  export var demo = `<img src="${img}"/>`
// main.js  import demo from './js/module2.js';  document.body.innerHTML = demo;

只管根据图片和 module2.js 的路径关系正常引入图片即可,后面路径会被正确替换。 如果我们之前没有配置 publicPath 的话,会发现打包后的路径是 img/test2.95a05a82.jpg,也即 index.html 依然被当作是位于 dist 文件夹下的。因为我们前面配置了,所以这里路径是正确的,最后可以正常显示图片:

4. Babel 转译

命令行安装:

npm install --save -dev babel-loader@7 babel-core babel-preset-es2015

配置 webpack.config.js

module:{      rules:[          {...},          {...},          {              test:/.js$/,              exclude:/(node_modules|bower_components)/,              use:{                  loader:'babel-loader',                  options:{                      presets:['es2015']                  }              }          }      ]  }

exclude:/(node_modules|bower_components)/ 表示不转译 node_modules 文件夹中的 js 。

babel-loader 的预设:

  • babel-preset-es2015babel-preset-es2016 等:支持不同版本的ECMAScript规范;
  • babel-preset-latest: 支持现有所有 ECMAScript 版本的新特性,包括处于stage 4里的特性(已经确定的规范,将被添加到下个年度的)。
  • babel-preset-env:但很多时候我们需要更灵活的 preset —— 比如大部分浏览器已经支持了 ES6 的某个特性,那么对于这个特性我们其实是不必去转译的,但前面所说的那些 preset 会一概转译。所以这里还提供了 babel-preset-env,它可以根据我们指定的目标环境(比如某个版本的浏览器)来选择它不支持的特性进行转译。

5. 集成 Vue

注意这不是一个开发时依赖:

npm install [email protected] --save

如果遇到这个报错:

You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

这是因为 Vue 有两个版本:

  • runtime-only
  • runtime-compiler

其中,runtime-only 版本无法编译模板,要么用 render 函数,要么修改 webpack.config.js 配置改用其它版本。这里我们先选择后者:

module.exports = {      entry: ...,      output: {...},      module: {...},      resolve:{          alias:{               'vue$':'vue/dist/vue.esm.js'  // 指定版本          }      }  }

另外,webpack 还需要分别借助 vue-loader 和 vue-template-compiler 去加载和解析 .vue 文件:

npm install [email protected] [email protected] --save -dev

之后我们就可以编写单文件组件了( 使用 vscode 的话强烈建议安装 vetur 插件)。

Note

因为我们安装的 vue 版本是 [email protected],所以这里的 vue-loader 和 vue-template-compiler 要注意版本对应问题,总之报错信息也写得很清楚了。

(要说为什么用这么低版本的 vue,因为视频里的项目也是用低版本 Vue 构建的,所以暂时先跟着讲师的步伐吧 =。=)