一个包含前端的项目,里面可能有多个.js,多个.css,多个静态图片,多个其他前端资源。一些js资源彼此之间存在依赖关系,当一个页面需要加载多个.js的话,也会拖累整个页面的加载速度。为了解决这个问题,webpack把左边各种各样的静态资源,打包成一个所谓的assets。这样浏览器加载起来就快多了。
WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些预设,打包为合适的格式以供浏览器使用。
webpack的运行需要依赖node.js的运行环境。
简单使用
- 若是局部安装,先初始化npm init
- 通过npm install --save-dev webpack安装
- 配置基本的入口entry及output,简化命令
- 根据需要配置source maps
- 配置本地服务器,先要安装webpack-dev-server
- 若是要用到最新JS代码,可以安装配置Babel
- 根据需要配置安装将样式表嵌入js的loader(css-loader or style-loader), css的预处理器loader等等
- 插件使用
初始化
npm init
生成package.json文件
输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过不用担心,如果不准备在npm中发布你的模块,这些问题的答案都不重要,回车默认即可。
安装webpack
//全局安装
npm install -g webpack
//局部安装
npm install --save-dev webpack
文件创建
新建文件夹app,此文件夹用来存放原始数据和JavaScript模块。里面再新建两个js文件
新建文件夹public,存放之后供浏览器读取的文件(包括使用webpack打包生成的js文件以及一个index.html文件)。index.html文件目的在于引入打包后的js文件。
使用webpack命令
在终端中使用,基本使用方法
# {extry file}出填写入口文件的路径,本文中就是上述main.js的路径,
# {destination for bundled file}处填写打包文件的存放路径
# 填写路径的时候不用添加{}
webpack {entry file} {destination for bundled file}
如果webpack不是全局安装的,那么在终端中使用此命令时,需要额外指定其在node_modules中的地址,继续上面的例子,在终端中输入如下命令
# webpack非全局安装的情况
node_modules/.bin/webpack app/main.js public/bundle.js
warning: 提醒我未安装webpack-cli
npm install -D webpack-cli
在实际使用中webpack将bundle.js也识别为entry point之一。此时可以去掉output这个路径。建议不要使用这个方式来编译,还是先配置webpack.config.js比较好。
Error: 提醒The 'mode' option has not been set, webpack will fallback to 'production' for this value.
在根目录下创建webpack.config.js文件,写入如下内容
module.exports = {
mode: 'production'
}
通过配置文件来使用webpack
配置主要涉及到的内容是入口文件路径和打包后文件的存放路径
module.exports = {
entry: __dirname + "\\app\\main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "\\public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
}
}
'__dirname'是node.js中的一个全局变量,它指向当前执行脚本所在的目录。
注意我此时用的是windows系统,所以配置的路径,有两个\,这是为了转义。
更快捷的执行打包任务
当webpack局部安装,开启会比较繁琐。可以通过npm引导任务执行,对npm进行配置后,可以在命令行中使用简单的npm start来执行。
package.json文件
{
"name": "webpack-sample-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node_modules/.bin/webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.32.2",
"webpack-cli": "^3.3.2"
}
}
webpack的强大功能
生成source maps(使调试更容易)
开发总是离不开调试,方便的调试能极大的提高开发效率,不过有时候通过打包后的文件,你是不容易找到出错了的地方,对应的你写的代码的位置的,Source Maps就是来帮我们解决这个问题的。
通过简单的配置,webpack就可以在打包时为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。
在webpack的配置文件中配置source maps,需要配置devtool,它有以下四种不同的配置选项,各具优缺点,描述如下:
devtool | 配置结果 |
---|---|
source-map | 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包速度; |
cheap-module-source-map | 在一个单独的文件中生成一个不带列映射的map,不带列映射提高了打包速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便; |
eval-source-map | 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。在开发阶段这是一个非常好的选项,在生产阶段则一定不要启用这个选项; |
cheap-module-eval-source-map | 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点; |
对小到中型的项目中,eval-source-map是一个很好的选项,再次强调你只应该开发阶段使用它
在webpack.config.js中配置devtool
module.exports = {
devtool: 'eval-source-map'
}
使用webpack构建本地服务器
让浏览器监听代码的修改,并自动刷新显示修改后的结果。webpack提供一个可选的本地开发服务器,这个服务器基于node.js构建,不过它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖。
npm install --save-dev webpack-dev-server
devServer作为webpack配置选项中的一项,以下是它的一些可参考配置。
devserver的配置选项 | 功能描述 |
---|---|
contentBase | 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录(本例设置到“public"目录) |
port | 设置默认监听端口,如果省略,默认为”8080“ |
inline | 设置为true,当源文件改变时会自动刷新页面 |
historyApiFallback | 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html |
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "\\app\\main.js",
output: {
path: __dirname + "\\public",
filename: "bundle.js"
},
devServer: {
contentBase: ".\\public",
historyApiFallback: true,
inline: true//实时刷新
}
}
在package.json中的scripts对象中添加如下命令,以开启本地服务器
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack",
"server": "webpack-dev-server --open"
},
在终端中输入npm run server即可在本地的8080端口查看结果
Loaders
通过使用不同的loader,webpack有能力调用外部的脚本或者工具,实现对不同格式的文件的处理。例如:分析转换scss为css,把下一代JS文件转换为现代浏览器兼容的JS文件。
Loaders需要单独安装,并且在webpack.config.js中的modules关键字下进行配置。
包括以下几方面:
- test:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
- loader:loader的名称(必须)
- include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选)
- query:为loaders提供额外的设置选项(可选)
先把Greeter.js里的问候消息放在一个单独的JSON文件里,并通过合适的配置使Greeter.js可以读取该JSON文件的值,各文件修改后的代码如下:在app文件夹中创建带有问候信息的JSON文件(命名为config.json)
{
"greetText": "Hi there and greetings from JSON!"
}
更新后Greeter.js
var config = require('./config.json');
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = config.greetText;
return greet;
};
无需再添加webpack1.* 需要的json-loader,已内置可处理JSON文件
Babel
Babel其实是一个编译JavaScript的平台,达到以下目的:
- 让你能使用最新的JavaScript代码,而不用管标准是否被当前使用的浏览器完全支持
- 让你能使用基于JavaScript进行拓展了的语言,比如React的JSX
Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析Es6的babel-env-preset包和解析JSX的babel-preset-react包)。
先一次性安装这些依赖包
(以下命令已经过时)
//模块之间用空格隔开
npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react
Error: Cannot find module '@babel/core'
babel-loader@8 requires Babel 7.x (the package '@babel/core'). If you'd like to use Babel 6.x ('babel-core'), you should install 'babel-loader@7'.
解决方法
Error: Plugin/Preset files are not allowed to export objects, only functions.
npm install --save-dev @babel/preset-env @babel/preset-react
此时成功了。npm run server之后没有问题,所以以上命令过时,使用如下安装命令代替
npm install --save-dev babel-core @babel/core @babel/preset-env @babel/preset-react
Babel其实可以完全在 webpack.config.js 中进行配置,但是考虑到babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项放在一个单独的名为 ".babelrc" 的配置文件中。(webpack会自动调用.babelrc里的babel配置选项)。
//webpack.config.js
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方
filename: "bundle.js"//打包后输出文件的文件名
},
devtool: 'eval-source-map',
devServer: {
contentBase: "./public",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转
inline: true//实时刷新
},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
/*此处可以单独放到.babelrc文件中
options: {
presets: [
"@babel/preset-env", "@babel/preset-react"
]
}
*/
},
exclude: /node_modules/
}
]
}
};
//.babelrc
{
"presets": ["@babel/preset-react", "@babel/preset-env"]
}
一切皆模块
webpack有一个不可不说的优点,它把所有的文件都当做模块处理,JavaScript代码,CSS和fonts以及图片等等通过合适的loader都可以被处理。
CSS
webpack提供两个工具处理样式表,css-loader 和 style-loader,二者处理的任务不同,css-loader使你能够使用类似@import 和 url(...)的方法实现 require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。
//安装
npm install --save-dev style-loader css-loader
//使用module.exports = {
...
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader"
}
]
}
]
}
};
注意引入多个loader的方法。
在app目录下创建一个main.css
/* main.css */html {
box-sizing: border-box;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
margin: 0;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
h1, h2, h3, h4, h5, h6, p, ul {
margin: 0;
padding: 0;
}
为了让webpack能找到main.css,把它导入main.js中。
//main.js
import './main.css'; //使用require导入css文件
CSS module
被称为CSS module的技术意在把JS的模块化思想带入CSS中,通过CSS模块,所有类名,动画名默认都只作用于当前模块。webpack对css模块化提供了非常好的支持,只要在CSS loader中进行简单的配置,就可以直接把CSS的类名传递到组件的代码中,这样做有效避免了全局污染。
//webpack.config.js
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: { //此options在install postcss-loader之后,root(节点)不起作用,删除后,又正常
modules: true, // 指定启用css modules
localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
}
}
]
}
CSS预处理器
Sass 和 Less 之类的预处理器是对原生CSS的拓展,它们允许你使用类似于variables, nesting, mixins, inheritance等不存在于CSS中的特性来写CSS,CSS预处理器可以这些特殊类型的语句转化为浏览器可识别的CSS语句。
以下是常用的CSS 处理loaders:
- Less Loader
- Sass Loader
- Stylus Loader
也存在一个CSS的处理平台-PostCSS,它可以帮助你的CSS实现更多的功能,如为CSS代码自动添加适应不同浏览器的CSS前缀。
首先安装postcss-loader和autoprefixer(自动添加前缀的插件)
npm install --save-dev postcss-loader autoprefixer
接下来,在webpack配置文件中添加postcss-loader,在根目录新建postcss.config.js,并添加如下代码之后,重新使用npm start打包时,你写的css会自动根据Can i use里的数据添加不同前缀了。
webpack.config.js
use: [
{
loader: "style-loader"
}, {
loader: "css-loader"
},{
loader: "postcss-loader"
}
]
在根目录新建postcss.config.js,并添加如下代码
// postcss.config.jsmodule.exports = {
plugins: [
require('autoprefixer')
]
}
插件(Plugins)
插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。
Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,可以这么来说,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。
使用插件的方法
要使用某个插件,需要通过npm安装它,后在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)。例如添加一个给打包后代码添加版权声明的插件。
//webpack.config.js
plugins: [
new webpack.BannerPlugin('版权所有,翻版必究')
]
参考:入门Webpack,看这篇就够了