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 vue@2.5.21 --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 vue-loader@13.0.0 vue-template-compiler@2.5.21 --save -dev

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

Note

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

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