5分钟入门webpack

427 阅读3分钟

要学习webpack之前,我觉得有必要了解一下模块化的概念。

什么是模块

在过去很长的一段时间里,javascript并没有模块这个概念。如果我们要在页面中引用其他的js文件,只能通过script标签将其一个个插入到页面中。这样做有很多的缺陷,比如无法在页面中清晰地看出这些js文件之间的依赖关系。而且在script标签中顶层作用域即全局作用域,这会造成全局作用域污染,在一个团队协作的大型项目中,非常容易引发命名冲突等问题。
模块化解决了上述几个问题,在一个模块化的js文件中,可以通过导入导出语句很清晰地看出依赖关系,而且每个模块的作用域都是独立的,不会污染全局,彼此也不会有命名冲突,极大地提升了一个大型项目的开发效率,可维护性。 社区也诞生了几种模块化规范,如CommonJs,AMD,UMD还有与ES6一起发布的ES6 Module等大家可以自行了解一下。

什么是webpack

在了解过什么是模块之后,简单地讲,webpack就是用来实现模块化javascript的打包工具
在webpack中,每个文件都是单独的一个模块,之后通过用loader转换,通过plugin注入钩子,最终输出一个由多个模块组成的文件。我们只需要在html文件中引用最终输出的文件即可。

看一个简单的例子。

以下是目录结构

image.png

webpack.config.js文件

const path = require('path')

module.exports = {
    entry: {
        main: './main.js'
    },
    output: {
        path: path.join(__dirname, '/dist'),
        filename: 'bundle.js'
    }
}

该文件是webpack的配置文件,webpack会从该文件读取配置
entry: 用来指明整个项目的入口文件,webpack会从该文件开始,递归地解析出所有的模块
output.path: 输出文件的存放路径,必须是绝对路径
output.filename: 指定最终输出的打包好的文件名字

main.js

import { foo } from './foo.js'

foo()

foo.js

export function foo () {
    document.write('hello webpack')
}

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>
    <script src="./dist/bundle.js"></script>
</body>
</html> 

main.js入口文件中引用了foo.js文件,并执行了foo函数,函数会在页面输出 hello webpack。
我们在终端执行 webpack 命令,可以看到项目中多出了一个dist目录,里面有打包生成的bundle.js文件

image.png

这就是webpack打包后生成的文件,我们在index.html文件中用script标签引入该文件,到开浏览器就可以看到效果。如图

image.png

至此,我们已经利用webpack实现了一个简单的模块化项目。

什么是loader

webpack原生不支持解析任何非javascript的文件。要解析非javascript文件就需要用到loader。比如在上面的例子中,我们要给文案加点样式,我们就需要写一个css文件。为了加载这个css文件,就需要loader出场了。
修改webpack.config.js文件如下

const path = require('path')

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

新增字段:
module: 配置处理模块的规则
rules: rules配置模块的读取和解析规则,通常用来配置loader。具体用法是利用test,include,exclude三个配置项来指定loader要应用规则的文件。然后用use字段来指定需要用到的loader。需要注意的是,loader是根据use数组倒序执行的。在上面的例子中,所有的css文件都会先交给css-loader处理,处理完之后再交给style-loader处理。

我们在项目中创建一个css文件,然后再main.js文件中引入,如下

main.css

* {
    color: red;
}

main.js

import { foo } from './foo.js'
import './main.css'

foo()

重新打包,打开浏览器,即可看到字体变成了红色

image.png

这就是loader的作用。每种文件类型都可以在社区找到对应的loader,比如vue-loader,ts-loader等。大家可以自行了解。

plugin

plugin用于扩展webpack的功能。通过在构建流程注入钩子来实现,极大地提升了webpack的灵活性。
以上面的配置为例,我们在项目中引入一个css文件,并用css-loader和style-loader来加载。最终打包生成的还是只有一个js文件。这是因为所有的样式都以字符串的形式合并到了js文件中。在实际开发中,这很不利于维护和优化。所以我们想要将css文件打包到一个单独文件,再单独引入这个css文件。这个就需要用plugin来实现。修改webpack.config.js文件如下

webpack.config.js

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
    entry: {
        main: './main.js'
    },
    output: {
        path: path.join(__dirname, '/dist'),
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                //这里用插件的loader替换style-loader
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            //提取出来的css文件名称
            filename: `[name]@[hash].css`
        })
    ]
}

可以看到,plugins配置项接受一个数组。数组中是各个plugin的实例。plugin的具体配置需要自己去查询。
配置完毕,再次运行webpack命令打包项目,即可看到dist目录下多出了一个css文件,如下图

image.png

名字是我们配置好的css源文件名和文件的哈希值。
接下来我们直接在index.html中引入该文件即可。

devServer

通过以上的配置,我们可以让webpack正常运行起来,实现打包。但是在实际开发中,每次修改代码都需要打包一次,会让我们的开发效率大大降低。devServer会启动一个http服务器用于服务网页请求,同时会帮助启动webpack,并在监听到文件内容有变化时,通过websocket协议自动刷新页面。

第一次发文章,有不对的地方欢迎大家指正