webpack由浅入深系列(一)

3,339 阅读3分钟

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

vue、react等框架以及模块化编程让项目更加清晰、更易维护,而对于页面的加载来说,模块文件多就意味着请求数多,网页的加载速度就慢,而各框架通常有自有的语法及文件类型。webpack通过loader去处理非 JavaScript 文件,将模块打包成一个或多个静态资源文件(js、css、jpg|png等等)并进行优化。

一、安装webpack

在项目文件夹路径下,安装webpack和webpack-cli:

npm install webpack webpack-cli --save-dev

使用webpack进行打包是在配置文件中设置的,默认的配置文件webpack.config.js,

也可以在package.json里指定配置文件:(将上图的webpack.config.js改为aa.js)

二、核心概念

1、entry与output

entry指定webpack的打包入口,从入口开始查找依赖,递归形成依赖树,最终遍历这些依赖,生成打包后的文件。

与之对应,output指定webpack 在哪里输出它所创建的打包文件,以及如何命名这些文件。

webpack打包分为单入口和多入口。

单入口。entry为入口文件相对地址的字符串,output为一个对象,有path和filename两个字段。

如下图的配置,打包后将生成一个js文件,dist目录下的bundle.js。

多入口:entry为一个对象,每一个字段为一个入口。output的filename为"[name].js",通过占位符来确保输出的多个js文件与入口js文件一一对应。

如下图的配置,打包后将生成两个js文件(与entry中的入口数量相同),dist目录下的index1.js和test1.js

2、loader

webpack 自身只理解 javascript和json,loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,并添加到依赖图中进行打包处理。loader本身是一个函数,接受源文件作为参数,并返回转换的结果。

loader的配置是在module对象的rules字段。rules是一个数组,可以配置多个loader。每个loader 有两个属性:

test,用于匹配出应该被对应的 loader 进行转换的某个或某些文件,值为正则表达式。
use, 表示应该使用哪个 loader对源文件进行转换。

日常开发中,常用的loader有:

  • babel-loader: 转换es6、es7等JS新特性语法
  • css-loader: 支持.css文件的加载和解析
  • less-loader: 转换less文件成css(类似的有sass-loader/stylus-loader)
  • style-loader: 生成一个内容为最终解析完的css代码的style标签,并放到head标签里
  • postcss-loader: 进一步处理css文件,比如添加浏览器前缀,压缩css等
  • url-loader: 处理图片、字体图标等文件

以css-loader和style-loader为例:

webpack.config.js配置:

module.exports = {
    entry: "./src/index.js",
    output: {
        path: path.join(__dirname,"/dist"),
        filename: "bundle.js"
    },
    module:{
        rules:[
            {
                test: /\.css$/,
                use:  ['style-loader','css-loader']
            }
        ]
    },  
}

(1)index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>酷酷酷</div>
    <script src="./dist/bundle.js"></script>
</body>
</html>

(2)index.js(css-loader使得在js文件中可以解析css文件,否则会报语法错误)

import "./css/index.css"

(3)index.css

body{
    color: red;
}

最终生成的文档结构:(style-loader生成一个内容为最终解析完的css代码的style标签,并放到head标签里,如果没有style-loader生成的style标签,index.css里的样式不会对文档生效)

值得注意的是:一个匹配规则中可以配置使用多个 loader,即一个模块文件可以经过多个 loader 的转换处理,loader执行的顺序是从右往左。

3、plugins

loader对源文件进行预处理,而plugin作用到构建打包的整个过程,比如文件压缩、资源管理等等。

plugin的配置是在plugins数组中,需要使用某个插件就将它添加到plugins数组。

平时用的比较多的plugin有:

  • html-webpack-plugin:创建html文件去承载输出的bundle
 let HtmlWebpackPlugin = require('html-webpack-plugin');
 new HtmlWebpackPlugin({
    template: 'index.html',    // 用到的模板文件,不局限html后缀
    filename: 'index.html',  // 生成的html文件命名(位于dist目录下)
    hash: true, //  会在打包好的bundle.js或抽离的.css文件后加hash串
    inject: true, //是否能注入内容到输入的页面去
   	minify: {
        removeComments: true, //移除HTML中的注释
        collapseWhitespace: true, //删除空白符与换行符,整个文件会压成一行
    }
  })

将会根据1处的index.html生成2处的index.html,并在2的index.html加入bundle.js的引用: <script src="bundle.js?29a7f233afce83fb4104"></script>

  • clean-webpack-plugin: 清理构建目录。可以解决文件hash值输出时dist目录文件不断累积的问题
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
new CleanWebpackPlugin()
  • copy-webpack-plugin:拷贝文件或目录,比如static文件夹。

new CopyWebpackPlugin({
    patterns: [
          {
               from: __dirname+'/src/static',
               to: __dirname+'/dist/static'
          }
    ]
})
  • mini-css-extract-plugin: 将css按模块化解压到单独的文件中,为每个包含css的js文件创键一个css文件。(style-loader 是将css文件以行内样式style的标签写入打包后的html页面;mini-css-extract-plugin是直接link方式引入进去,js文件中若引入多个css文件也将会合并成一个css文件,其中的内容按js文件中的引入顺序)
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module:{
        rules:[
            {
                test: /\.css$/,
                use:  [MiniCssExtractPlugin.loader,'css-loader']
                //去掉style-loader,换成MiniCssExtractPlugin.loader
            },
        ]
},
plugins:[
        new MiniCssExtractPlugin({
        	filename: 'style/style.css' //默认为dist目录下的main.css
        })
]

  • optimize-css-assets-webpack-plugin:压缩css文件,优化css结构
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
new OptimizeCSSAssetsPlugin()
  • uglifyjs-webpack-plugin: 压缩js代码。
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
new UglifyJsPlugin({
     uglifyOptions: {
          compress: {
                drop_debugger: true,
                drop_console: true,  //生产环境自动删除console
          }
      },
      sourceMap: false
})

uglifyjs-webpack-plugin不能压缩es6的代码文件,出现ERROR in bundle.js from UglifyJs Unexpected token: operator «>» ,所以使用该插件需要用babel-loader将es6转换成es5。

mode为production时,会自动开启UglifyJsPlugin。

4、mode

mode用来指定当前的构建环境,有production、development、none三种情况,默认值是production。设置mode可以使用webpack的内置函数,如下。

  • development: 启用 NamedChunksPlugin 和 NamedModulesPlugin。

  • production: 启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin。

  • none: 不开启任何优化选项。

mode可以在配置文件中设置:

module.exports = {
  mode: 'development'
};

也可以在命令行设置:

webpack --mode development