要学习webpack之前,我觉得有必要了解一下模块化的概念。
什么是模块
在过去很长的一段时间里,javascript并没有模块这个概念。如果我们要在页面中引用其他的js文件,只能通过script标签将其一个个插入到页面中。这样做有很多的缺陷,比如无法在页面中清晰地看出这些js文件之间的依赖关系。而且在script标签中顶层作用域即全局作用域,这会造成全局作用域污染,在一个团队协作的大型项目中,非常容易引发命名冲突等问题。
模块化解决了上述几个问题,在一个模块化的js文件中,可以通过导入导出语句很清晰地看出依赖关系,而且每个模块的作用域都是独立的,不会污染全局,彼此也不会有命名冲突,极大地提升了一个大型项目的开发效率,可维护性。
社区也诞生了几种模块化规范,如CommonJs,AMD,UMD还有与ES6一起发布的ES6 Module等大家可以自行了解一下。
什么是webpack
在了解过什么是模块之后,简单地讲,webpack就是用来实现模块化javascript的打包工具
在webpack中,每个文件都是单独的一个模块,之后通过用loader转换,通过plugin注入钩子,最终输出一个由多个模块组成的文件。我们只需要在html文件中引用最终输出的文件即可。
看一个简单的例子。
以下是目录结构
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文件
这就是webpack打包后生成的文件,我们在index.html文件中用script标签引入该文件,到开浏览器就可以看到效果。如图
至此,我们已经利用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()
重新打包,打开浏览器,即可看到字体变成了红色
这就是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文件,如下图
名字是我们配置好的css源文件名和文件的哈希值。
接下来我们直接在index.html中引入该文件即可。
devServer
通过以上的配置,我们可以让webpack正常运行起来,实现打包。但是在实际开发中,每次修改代码都需要打包一次,会让我们的开发效率大大降低。devServer会启动一个http服务器用于服务网页请求,同时会帮助启动webpack,并在监听到文件内容有变化时,通过websocket协议自动刷新页面。
第一次发文章,有不对的地方欢迎大家指正