webpack(一)

webpack是用来做什么的就不赘述了,我们直接进入主题吧。

webpack中的核心概念

webpack中有四个核心概念:

  1. Entry

    • 给webpack指定入口文件,webpack会从入口文件出发,处理各种依赖,最终打包出叫bundles的东西
    • 可以指定一个文件,也可以指定多个
    • entry的值可以是一个字符串,一个数组,一个对象
    • 值是一个字符串时,字符串就是那个入口文件的路径

      1
      2
      3
      module.exports = {
      entry: './path/to/my/entry/file.js'
      };
    • 值是一个数组用于多入口文件的情景,数组里面的值都是字符串,并且每一个值代表一个入口文件。

      1
      2
      3
      module.exports = {
      entry: ['./user', './account']
      };
    • 将一个对象作为entry的值似乎有点过于麻烦,不过这种方法更加灵活,也能构建出几个相互独立的文件。注意,下面的例子表明这几种形式是可以组合在一起的。

      1
      2
      3
      4
      5
      6
      module.exports = {
      entry: {
      home: './home',
      user: ['./user', './account']
      },
      }
  2. Output

    • 用来告诉webpack打包后生成的bundles文件应该叫什么和应该去哪里

      1
      2
      3
      4
      5
      6
      7
      8
      9
      const path = require('path');
      // 例子里面用到了path,详情可以查阅相关文档
      module.exports = {
      entry: './path/to/my/entry/file.js',
      output: {
      path: path.resolve(__dirname, 'dist'),
      filename: 'my-first-webpack.bundle.js'
      }
      };
    • filename也可以使用变量,不必写死:filename: '[name].bundle.[hash].js'

  3. Loaders

    • loader和plugin算是webpack里面最核心的最有用的东西了
    • webpack本身是只能处理js文件的,为了能处理其他文件(如css)我们需要借助loader
    • loader有两个重要的属性:

      • test属性表明哪一些文件需要被处理,一般是一个正则表达式
      • use属性表明处理这些文件要用哪个loader,一般是一个字符串,也可以是一个数组。
        • 值是一个数组的情况一般用于链式调用loader,指定类型的文件会先被数组中最后一个loader处理,处理完之后的产出交给倒数第二个loader处理,直到被第一个loader处理完。
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          const path = require('path');
          const config = {
          entry: './path/to/my/entry/file.js',
          output: {
          path: path.resolve(__dirname, 'dist'),
          filename: 'my-first-webpack.bundle.js'
          },
          module: {
          rules: [
          { test: /\.txt$/, use: 'raw-loader' }
          ]
          }
          };
          module.exports = config;
    • 留意到上面说到的test和use属性时放在rules数组里面的一个对象里的,并且这个rules必须作为module对象的属性

  4. Plugins
    • 用于拓展webpack的功能,能让webpack实现压缩文件、优化bundle、设置环境变量等诸多功能
    • 使用plugin前需要先require进去,然后把它添加到config下一个叫plugins的数组中。
    • plugin的设置是通过options来实现的
    • 另外由于你可以将同一个plugin使用多次,你需要通过new操作符新建一个实例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
      const webpack = require('webpack'); //to access built-in plugins
      const path = require('path');
      const config = {
      entry: './path/to/my/entry/file.js',
      output: {
      path: path.resolve(__dirname, 'dist'),
      filename: 'my-first-webpack.bundle.js'
      },
      module: {
      rules: [
      { test: /\.txt$/, use: 'raw-loader' }
      ]
      },
      plugins: [
      new webpack.optimize.UglifyJsPlugin(),
      new HtmlWebpackPlugin({template: './src/index.html'})
      ]
      };
      module.exports = config;

几个例子

下面通过几个例子来讲解一下

例子一:

1
2
3
4
5
6
7
8
9
10
11
12
13
const path = require('path')
module.exports = {
context: path.join(__dirname, './src'),
entry: {
home: './home',
user: ['./user', './account']
},
output: {
path: path.join(__dirname, './dist'),
filename: '[name].bundle.[hash].js'
}
}

在这例子中我们没有用到任何loader以及plugin,webpack只是通过入口文件,找到文件之间的依赖关系,并根据依赖关系最后打包出一个文件。

有几点需要解释一下:

  1. context: path.join(__dirname, './src'),用于指明当前是在src目录下,因此后面描述类似./src/home这样的路径时直接写./home就可以了
  2. entry里面是一个对象,里面有两个属性,因此我们将会打包出两个chunk,也就是会有两个文件。其中user属性的值是一个数组,表明他有两个入口文件
  3. path: path.join(__dirname, './dist'),表明文件会输出在项目目录的dist文件夹下
  4. filename: '[name].bundle.[hash].js'表明我们没有写死输出文件的名字,而是使用映射变量[name]和[hash]组合一个名字

例子二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module.exports = {
context: __dirname + '/src',
entry: './',
module: {
rules: [
{
test: /\.css$/,
include: [ __dirname + '/src'],
use: ['style-loader', 'css-loader']
}
]
},
output: {
path: __dirname + '/dist',
filename: '[name].bundle.js'
}
}

这里面我们使用了style-loader以及css-loader来处理css文件。

解释一下:

  1. 我们之前提到使用loader时最关键的是test和use属性,并且要把拥有这两个属性的对象放在一个叫rules的数组里面,而这个rules就是module的一个属性
  2. include: [ __dirname + '/src'],代表只对src目录下的css文件进行处理

例子三:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
context: __dirname + '/src',
entry: './',
module: {
rules: [{
test: /\.css$/,
include: [
__dirname + '/src'
],
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader'
})
}]
},
output: {
path: __dirname + '/dist',
filename: '[name].bundle.[hash].js'
},
plugins: [
new ExtractTextPlugin('[name].css')
]
}

这个例子介绍一个叫extract-text-webpack-plugin的插件。我们注意到之前打包生成的文件只有一个js文件并且里面含有css代码,而这个插件主要用于将css从js文件中提取出来,生成单独的css文件。

这里主要解释一下用法:

  • 使用插件前必须先require:var ExtractTextPlugin = require('extract-text-webpack-plugin')
  • 然后再plugin数组中新建一个实例:new ExtractTextPlugin('[name].css')
  • 新建的时候可以传递一些配置,具体可以设置什么看这里
  • 然后还要修改loaders里面有关css文件的use:
    1
    2
    3
    4
    use: ExtractTextPlugin.extract({
    fallback: "style-loader", // 编译后用什么loader来提取css文件
    use: "css-loader" // 指需要什么样的loader去编译文件,这里由于源文件是.css所以选择css-loader
    })

例子四:

这个例子是一个综合运用上面说到的知识的例子,在这个例子中,不同的命令会产生不同的结果:

  1. npm run build时会产生css文件并且js被压缩
  2. npm run dev时则不会产生css文件,js也不会被压缩
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    var path = require('path')
    var webpack = require('webpack')
    var ExtractTextPlugin = require('extract-text-webpack-plugin')
    // 这个在 npm run dev 和 npm run build 时候是不同的
    var TARGET = process.env.npm_lifecycle_event
    var APP_PATH = path.join(__dirname, '/src')
    // 根据TARGET不同设置plugin设置
    var uglify = new webpack.optimize.UglifyJsPlugin({compress: {warnings: false}});
    var setPlugins = [];
    if (TARGET === 'build') {
    setPlugins = [
    new ExtractTextPlugin('./[name].css'),
    uglify
    ]
    }else if (TARGET === 'dev'){
    setPlugins = [
    new ExtractTextPlugin({disable: true})
    ]
    }
    const config = {
    entry: path.join(APP_PATH, '/index.js'),
    output:{
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
    },
    module: {
    rules: [
    {
    test: /\.css$/,
    use: ExtractTextPlugin.extract({
    fallback: "style-loader",
    use: "css-loader"
    })
    }
    ]
    },
    plugins: setPlugins
    }
    module.exports = config;

其他一些东西

除了提到的这些以外,webpack还有其他一些loader和plugin很有用:

  1. autoprefixer–用于自动给css属性添加前缀,实现兼容
  2. commonChunk–用于自动识别项目中有哪些文件是共用的,会只保留一份

webpack还具有异步加载能力,通过异步加载可以提高网站的首屏加载速度。

Reference

webpack concept

详解前端模块化工具-webpack