什么是webpack?
Webpack是一个打包模块化JavaScript的工具,在webpack里一切文件皆模块,通过loader转换文件,通过plugin注入钩子,最后输出由多个模块组合成的文件。
webpack的优点
- 专注于处理模块化的项目,能做到开箱即用,一步到位;
- 可通过Plugin扩展,完整好用又不失灵活;
- 使用场景不局限于web开发;
- 社区庞大活跃,经常引入紧跟时代发展的新特性,能为大多数场景找到已有的开源扩展;
- 良好的开发体验;
安装webpack到项目
npm i -D webpack webpack-cli (npm i -D 是npm install --save-dev的简写,是指安装模块并保存到package.json的devDependencies)
正式使用webpack前的准备:
- 在终端中创建一个package.json文件夹:
npm init 或
npm init -y(默认所有信息)
输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过不用担心,如果你不准备在npm中发布你的模块,这些问题的答案都不重要,回车默认即可。 - 在根目录下创建两个文件夹:app文件夹,public文件夹;app文件夹用来存放原始数据和我们将写的JavaScript模块,并在app文件夹下创建Greeter.js和main.js文件,public文件夹用来存放之后供浏览器读取的文件,并在public文件夹下创建index.html文件;
目录结构为:
- 在index.html文件中写入最基础的html代码,它在这里目的在于引入打包后的js文件(这里我们先把之后打包后的js文件命名为bundle.js,之后我们还会详细讲述)。
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webpack Sample Project</title>
</head>
<body>
<div id='root'>
</div>
<script src="bundle.js"></script>
</body>
</html>
在Greeter.js中定义一个返回包含问候信息的html元素的函数,并依据CommonJS规范导出这个函数为一个模块:
// Greeter.js
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hello World!";
return greet;
};
main.js文件中我们写入下述代码,用以把Greeter模块返回的节点插入页面。
//main.js
const greeter = require('./Greeter.js');
document.querySelector("#root").appendChild(greeter());
正式使用webpack
在终端输入:webpack app/main.js -o public/bundle.js
结果如下:
打开index.html可以看到webpack同时编译了Greeter.js和main.js.
通过配置文件来使用webpack
首先我来说明下webpack的几个核心概念:
- Entry:入口,webpack执行构建的第一步将从Entry开始,可抽象成输入;
- Module:模块,在webpack里一切皆模块,一个模块对应一个文件。webpack会从配置的Entry开始递归找出所有依赖的模块;
- Chunk:代码块,一个chunk由多个模块组合而成,用于代码合并与分割;
- Loader:模块转换器,用于将模块的原内容按照需求转换成新内容;
- Plugin:扩展插件,在webpack构建流程中的特定时机注入扩展逻辑,来改变构建结果或者做我们想要的事情;
- Output:输出结果,在webpack经过一系列处理并得出最终想要的代码后输出结果;
webpack工作流程:
webpack在启动后会从Entry里配置的Module开始,递归解析Entry依赖的所有Module。每找到一个Module,就会根据配置的Loader去找出对应的转换规则,对Module进行转换后,再解析出当前Module依赖的Module。这些模块会以Entry为单位进行分组,一个Entry及其所有依赖的Module被分到一个组也就是一个Chunk。最后,webpack会将所有的Chunk转换成文件输出。在整个流程中,webpack会在恰当的时机执行Plugin里面定义的逻辑。
在根目录下新建一个名为webpack.config.js的文件,目前的配置主要涉及到的内容是入口文件路径和打包后文件的存放路径。
webpack.config.js
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
}
}
注:“__dirname”是node.js中的一个全局变量,它指向当前执行脚本所在的目录。
有了这个配置之后,再打包文件,只需在终端里运行webpack,这条命令会自动引用webpack.config.js文件中的配置选项.
npm可以引导任务执行,对npm进行配置后可以在命令行中使用简单的npm start命令来替代上面略微繁琐的命令。在package.json中对scripts对象进行相关设置即可,设置方法如下。
{
"name": "webpackPractice",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"webpack": "^4.31.0",
"webpack-cli": "^3.3.2"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC"
}
使用mode
提供模式配置选项告诉webpack相应地使用其内置优化。
Possible values for mode are: none, development or production(default).
在webpack中使用:
module.exports = {
mode: 'production'
};
使用source-map-loader
开发总是离不开调试,方便的调试能极大的提高开发效率,不过有时候通过打包后的文件,你是不容易找到出错了的地方,对应的你写的代码的位置的,source-map-loader就是来帮我们解决这个问题的
安装:npm i -D source-map-loader (npm i -D 是npm install --save-dev的简写,是指安装模块并保存到package.json的devDependencies)
使用:
module.exports = {
mode: 'production',
entry: __dirname + '/app/main.js', //唯一的入口文件
output: {
path: __dirname + '/public', //打包后的文件存放的地方
filename: 'bundle.js' //打包后输出文件的文件名
},
module: {
rules:[
{
test:/\.js$/,
use: ["source-map-loader"],
enforce:'pre'
}
]
}
}
使用devserver
让浏览器监听你的代码的修改,并自动刷新显示修改后的结果,其实Webpack提供一个可选的本地开发服务器,这个本地服务器基于node.js构建,可以实现你想要的这些功能,不过它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖。
安装:npm i webpack-dev-server -D
使用:
引入path:const path = require(“path”);
var path = require("path");
module.exports = {
mode: 'production',
entry: __dirname + '/app/main.js', //唯一的入口文件
output: {
path: __dirname + '/public', //打包后的文件存放的地方
filename: 'bundle.js' //打包后输出文件的文件名
},
devServer: {
contentBase:path.join(__dirname,'public'), //设置在那个目录下提供本地服务器
compress:true, //是否启用gzip压缩
port:9000, //端口
open:true, //自动打开页面
},
module: {
rules:[
{
test:/\.js$/,
use: ["source-map-loader"],
enforce:'pre'
}
]
}
}
修改package.json中的配置
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack",
"server": "webpack-dev-server"
},
在终端输入npm run server
修改Greeter.js中的greet.textContent中的内容,页面会自动刷新.
Loaders
Loader可以看作是具有文件转换功能的翻译员,配置里面的module.rules数组配置了一组规则,告诉webpack在遇到那些文件时使用哪些loader去加载和转换.
Babel
Babel其实是一个编译JavaScript的平台,它可以编译代码帮你达到以下目的:
- 让你能使用最新的JavaScript代码(ES6,ES7…),而不用管新标准是否被当前使用的浏览器完全支持;
- 让你能使用基于JavaScript进行了拓展的语言,比如React的JSX;
安装:npm install -D babel-loader @babel/core @babel/preset-env @babel/preset-react react react-dom
配置webpack
var path = require("path");
module.exports = {
mode: 'production',
entry: __dirname + '/app/main.js', //唯一的入口文件
output: {
path: __dirname + '/public', //打包后的文件存放的地方
filename: 'bundle.js' //打包后输出文件的文件名
},
devServer: {
contentBase:path.join(__dirname,'public'), //设置在那个目录下提供本地服务器
compress:true, //是否启用gzip压缩
port:9000, //端口
open:true, //自动打开页面
},
module: {
rules:[
{
test:/\.js$/,
use: ["source-map-loader"],
enforce:'pre'
},
{
test: /\.jsx|\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ["@babel/react","@babel/env"]
}
}
}
]
}
}
成功显示:
css
webpack在遇到以.css结尾的文件时,先使用css-loader读取css文件,再由style-loader将css的内容注入JavaScript里,执行顺序由后向前。
安装:npm install --save-dev style-loader css-loader
配置webpack
{
test:/\.css$/,
use:['style-loader','css-loader']
}
再app目录下创建Greeter.css文件
Greeter.css
.body{
color:red;
}
修改Greeter.js
// Greeter.js
import React, {Component} from 'react';
import style from "./Greeter.css";
class Greeter extends Component{
render() {
return (
<div className = {style.body}>
Hello World!
</div>
);
}
}
export default Greeter
再终端运行npm run server,Hello World字体颜色变成红色了
css module
CSS modules的技术意在把JS的模块化思想带入CSS中来,通过CSS模块,所有的类名,动画名默认都只作用于当前模块。Webpack对CSS模块化提供了非常好的支持,只需要在CSS loader中进行简单配置即可,然后就可以直接把CSS的类名传递到组件的代码中,这样做有效避免了全局污染.
配置webpack
{
test:/\.css$/,
use:[
{
loader: "style-loader"
},{
loader: "css-loader",
options: {
modules:true, //指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
}
}
]
}
npm start 运行后,在页面查看:
CSS预处理器
Sass 和 Less 之类的预处理器是对原生CSS的拓展。
下面以less为例:
安装:npm install less-loader --save-dev
配置webpack:
{
test:/\.less$/,
use:[
{
loader: "style-loader"
},{
loader: "css-loader",
options: {
modules:true, //指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
}
},{
loader: 'less-loader', //将less解析成css
}
]
}
在app目录下创建一个test.less文件:
// test.less
.body{
color:red;
.wp{
color:red;
}
}
修改Greeter.js
// Greeter.js
import React, {Component} from 'react';
import style from "./Greeter.css";
class Greeter extends Component{
render() {
return (
<div className = {style.body}>
Hello World!
<div className = {style.wp}>Hello webpack!</div>
</div>
);
}
}
export default Greeter
输入npm start,查看网页会发现字体都变红了。
图片
安装:npm i -D file-loader
配置webpack:
{
test: /\.(png|jpe?g|gif|JPG)/,
use: [
{
loader: 'file-loader',
options: {
name: '[path][name].[ext]'
}
}
]
}
在根目录下创建image文件夹,放一个图片进去;
修改Greeter.js,引入图片;
// Greeter.js
import React, {Component} from 'react';
// import style from "./Greeter.css";
import style from "./test.less";
import img from '../image/1.JPG';
class Greeter extends Component{
render() {
return (
<div className = {style.body}>
Hello World!
<div><img src = {img} alt ='' /></div>
<div className = {style.wp}>Hello webpack!</div>
</div>
);
}
}
export default Greeter
运行npm start,在public文件夹下会产生一个image文件夹,里面会有你引用的图片;
更多loader api
Plugin
plugin是用来扩展webpack功能的,通过在构建流程里注入钩子实现,它为webpack带来了很大的灵活性.plugin的配置很简单,plugins配置项接收一个数组,数组里面的每一项都是一个要使用的Plugin的实例,Plugin需要的参数通过构造函数传入.
BannerPlugin (添加版权声明)
配置webpack
在文件首部引入webpack:
const webpack = require("webpack");
配置plugin
plugins: [
//添加版权声明
new webpack.BannerPlugin('版权所有,翻版必究'),
]
运行npm start,打包后文件显示:
HtmlWebpackPlugin (该插件将为你生成一个 HTML5 文件)
安装:npm install --save-dev html-webpack-plugin
删除public文件夹下的index.html
在app目录下创建一个index.tmpl.html文件模板,这个模板包含title等必须元素,在编译过程中,插件会依据此模板生成最终的html页面,会自动添加所依赖的 css, js,favicon等文件,index.tmpl.html中的模板源代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webpack Sample Project</title>
</head>
<body>
<div id='root'>
</div>
</body>
</html>
配置webpack
var HtmlWebpackPlugin = require('html-webpack-plugin');
......
//该插件将为你生成一个 HTML5 文件, 其中包括使用 script 标签的 body 中的所有 webpack 包
new HtmlWebpackPlugin({
template:__dirname + '/app/index.tmpl.html' //new 一个这个插件的实例,并传入相关的参数
}),
mini-css-extract-plugin
将CSS提取为独立的文件的插件,对每个包含css的js文件都会创建一个CSS文件,支持按需加载css和sourceMap;
安装:npm install --save-dev mini-css-extract-plugin
配置webpack:
var MiniCssExtractPlugin = require('mini-css-extract-plugin');
......
{
test:/\.less$/,
use:[
{
loader: "style-loader"
},
{
loader: MiniCssExtractPlugin.loader
}
,{
loader: "css-loader",
options: {
modules:true, //指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
}
},{
loader: 'less-loader', //将less解析成css
}
]
},
运行,你会发现在public文件夹下多了一个main.css文件
参考:https://segmentfault.com/a/1190000006178770#articleHeader4