1.为什么要使用webpack
现今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包。为了简化开发的复杂度,前端社区涌现出了很多好的实践方法。
1.模块化,让我们可以把复杂的程序细化为小的文件;
2.Scss,less等CSS预处理器。
3.转换es6语法的处理器等等。
这些改进确实大大的提高了我们的开发效率,但是利用它们开发的文件往往需要进行额外的处理才能让浏览器识别,而手动处理又是非常繁琐的,这就为WebPack类的自动化工具的出现提供了需求。
2.什么是webpack
WebPack可以看做是模块打包机。
在 webpack 里,所有类型的文件都可以是模块,包含我们最常见的 JavaScript,以及 css 文件、图片、json 文件等等。通过 webpack 的各种加载器,我们可以更有效地管理这些文件。它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。
3.WebPack和Grunt以及Gulp相比有什么特性
其实Webpack和另外两个并没有太多的可比性,Gulp/Grunt是一种能够优化前端的开发流程的工具,而WebPack是一种模块化的解决方案,模块化是其他构件工具没有的。gulp/grunt能做的,webpack也可以做。所以Webpack的优点(模块化)使得Webpack可以替代Gulp/Grunt类的工具。
Grunt和Gulp的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,这个工具之后可以自动替你完成这些任务。
Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个浏览器可识别的JavaScript文件。
4.使用webpack
//全局安装
npm install -g webpack
//安装到你的项目目录
npm install --save-dev webpack
正式使用Webpack前的准备
1.建立新文件夹(本例命名为testwebpack)
2.在文件夹里面使用npm init 创建package.json
{
"name": "webpackee",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
}
注:name值不能和所安装的包重名,否则安装失败。所以本例用在webpack后面随便加了两个字母ee
3.使用npm install --save-dev命令安装webpack,安装完成后如下
{
"name": "webpackee",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^2.6.1"
}
}
4.在testwebpack文件中创建两个文件夹(src文件夹和public文件夹),
1.src文件夹放原始数据和我们将写的JavaScript模块、路由模块等等
2.public文件夹用来存放准备给浏览器读取的数据(包括使用webpack生成的打包后的js文件以及一个index.html文件)
5.创建三个文件,index.html 文件放在public文件夹中,两个js文件(Greeter.js和main.js)放在src文件夹中。
index.html文件只有最基础的html代码,它唯一的目的就是加载打包后的js文件
<!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元素的函数。
// greeter.js
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings!";
return greet;
};
main.js用来把Greeter模块返回的节点插入页面。
//main.js
var greeter = require('./Greeter.js');
document.getElementById('root').appendChild(greeter());
通过配置文件来使用Webpack
1.在testwebpack文件中新建一个名为webpack.config.js的文件,并在其中进行最最简单的配置,如下所示,它包含入口文件路径和存放打包后文件的地方的路径。
module.exports = {
entry: __dirname + "/src/main.js",//唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方供src引入加载
filename: "bundle.js"//打包后输出文件的文件名
}
}
2.通过webpack 命令将文件打包到public文件夹中
$ webpack
3.也可以在package.json中配置脚本命令取代默认的webpack命令。
"scripts": {
"start": "webpack" //配置的地方就是这里啦,相当于把npm的start命令指向webpack命令
},
注:npm的start是一个特殊的脚本名称,它的特殊性表现在,在命令行中使用npm start就可以执行相关命令,如果对应的此脚本名称不是start,想要在命令行中运行时,需要这样用npm run scriptname如npm run build。
5.生成Source Maps(使调试更容易)
开发总是离不开调试,如果可以更加方便的调试当然就能提高开发效率,不过网页预览的代码一般和开发的代码不一样,比如scss文件网页预览的为css文件。为了可以直接预览scss等文件,方便调试的Source Maps便出现了。
在webpack的配置文件中配置source maps,需要开启谷歌的devtool工具,f12-setting-Enable JavaScript source maps/Enable CSS source maps.
在本例中,随便写错一处代码,如
// greeter.js
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings!";
return greete;
};
本来应该返回greet,我加了一个e,肯定会报错。但要是不用source maps,调试时候会显示如下
上面只能定位到bundle这个js,不能定位到greeter.js这个文件,但使用了source maps,调试的时候显示如下:
点击greeter.js可直接跳到错误处。
webpack配置source maps可在webpack.config.js添加devtool选项即可
module.exports = {
entry: __dirname + "/src/main.js",//唯一入口文件
output: {
path: __dirname + "/public",//打包后的文件存放的地方供src引入加载
filename: "bundle.js"//打包后输出文件的文件名
},
devtool: 'source-map',//配置生成Source Maps,选择合适的选项
}
具体配置如下:
6.使用webpack构建本地服务器
想不想让你的浏览器监测你的代码的修改,并自动刷新修改后的结果,其实Webpack提供一个可选的本地开发服务器,这个本地服务器基于node.js构建,可以实现你想要的这些功能,不过它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目开发依赖。
npm install --save-dev webpack-dev-server
然后在脚本命令行添加快捷命令:
{
"name": "webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start":"webpack",
"server": "webpack-dev-server" //启动命令
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^2.6.1",
"webpack-dev-server": "^2.4.5"
}
}
在webpack.config.js中配置服务器
devServer: {
contentBase: "./public", //本地服务器所加载的页面所在的目录
open : true, //打开浏览器
port: 9000 //端口
}
其他配置可以参考官网 https://doc.webpack-china.org/configuration/dev-server/
7.Loaders
Loaders是webpack中最让人激动人心的功能之一了。通过使用不同的loader,webpack通过调用外部的脚本或工具可以对各种各样的格式的文件进行处理,比如说分析JSON文件并把它转换为JavaScript文件,或者说把下一代的JS文件(ES6,ES7)转换为现代浏览器可以识别的JS文件。或者说对React的开发而言,合适的Loaders可以把React的JSX文件转换为JS文件。
Loaders需要单独安装并且需要在webpack.config.js下的modules关键字下进行配置.
注:webpack2.0之前的版本是按如下方式配置的
module: {
loaders: {...}
}
2.0之后的版本是按在rule配置的,不多目前两种方式都支持,以后会逐渐用第二种。
module: {
rules:[]
}
Loaders的配置选项包括以下几方面:
1.test:接收一个匹配loaders所处理的文件的拓展名的正则表达式(必须),格式为
test: /\.json$/ //匹配json格式文件
2.loader:应用loader的名称(必须),格式为:
module: {
rules:[
{
test: /\.vue$/,
loader: "json-loader"
},
{
test: /\.js$/,
loader: ['babel-loader'],
},
]
}
webpack2以后为了更清晰支持 -loader省略的。但测试不能省略
应用多个 loader 和选项时候可以用use属性(数组)
module: {
rules: [{
test: /\.vue$/,
use: ['vue-loader']
},
{
test: /\.js$/,
use: ['babel-loader'],
exclude: /node_modules/
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
},
{
test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
use: [{
loader: 'url-loader',
options: {
limit: 10000
}
}]
}
]
},
1.babel
Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中。对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析Es6的babel-preset-es2015包和解析JSX的babel-preset-react包)
我们先来一次性安装这些依赖包
// npm一次性安装多个依赖模块,模块之间用空格隔开
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
然后在webpack中配置
module: {
rules: [{
test: /\.(png|jpg)$/,
use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}, {
test: /\.json$/,
use: ['json-loader']
}, {
test: /\.js$/,
use: ['babel-loader'], //配置babel
}
]
},
然后在和webpack.config.js同级的目录中建立 .babelrc 文件,命令如下
touch .babelrc
webpack会自动调用.babelrc里的babel配置选项。babelrc里面的内容如下:
{
"presets": ["es2015"]
}
现在就可以使用es6语法了,更改greeter.js文件内容
// greeter.js
var img = require('./assets/cat01.png');
var config = require('./config.json');
//es5
// module.exports = function() {
// var greet = document.createElement('div');
// greet.innerHTML = "Hi there and greetings![图片上传失败...(image-e75b6-1519957547350)]" + config.greetText + "";
// return greet;
// };
//es6
function fn() {
let greet = document.createElement('div');
greet.innerHTML = "Hi there and greetings![图片上传失败...(image-a3fe33-1519957547350)]" + config.greetText + "";
return greet;
};
export {fn}; //导出
更改main.js文件内容
//main.js
// var greeter = require('./greeter.js'); //es5
import { fn } from './greeter.js' //es6 导入
document.getElementById('root').appendChild(fn());
注:import和export必须配对使用,不能在greeter里面使用import,main里面还使用require导入,同理module.exports和require一起使用
开启服务器
即可看到编译成功。
注:主流浏览器支持大部分es6语法(比如本例的import和export),所以不用babel时候也可以编译成功,经本地测试ie10不支持let,所以本例用此命令编译,ie10浏览器不显示此js返回的内容,但使用了babel,ie10就可以看见此js返回的内容
8.万物皆模块
Webpack有一个不可不说的优点,它把所有的文件都可以当做模块处理,包括你的JavaScript代码,也包括CSS和fonts以及图片等等等,只有通过合适的loaders,它们都可以被当做模块被处理。
css加载
webpack提供两个工具处理样式表,css-loader 和 style-loader,二者处理的任务不同。
css-loader:将css文件打包到js文件中,这样才可以使用require或者import导入。
style-loader:将js文件中的css渲染到页面中,在head标签里面。
先安装这两个loader
//安装
npm install --save-dev style-loader css-loader
配置
//使用
{
test: /\.css$/,
use: ['style-loader','css-loader'],
}
注:loader的加载顺序都是从右向左的,即先加载css-loader,再加载style-loader,不能颠倒,先加载进入js再渲染页面。颠倒会报错
结果如下
sass使用
先安装sass-loader和node-sass
npm install --save-dev sass-loader node-sass
因为sass-loader依赖于node-sass,所以需要安装node-sass,不安装会报错。
配置
module: {
rules: [{
test: /\.(png|jpg)$/,
use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}, {
test: /\.json$/,
use: ['json-loader']
}, {
test: /\.js$/,
use: ['babel-loader'],
exclude: /node_modules/, // 解析时排除的文件夹(node_modules里面也有很多js,但需要排除)
}, {
test: /\.scss$/,
use: ['style-loader','css-loader','sass-loader'],
}
]
},
main.js引入scss文件
import './index.scss'
编译,sass-loader会自动把scss文件编译成css,最后style-loader将其插入style里面
打包css
使用上面的loader,最后css会打包在js里面从而渲染在页面中,但要是不想打包在js中,想把css分离出来。就需要插件了。
安装extract-text-webpack-plugin插件
npm install --save-dev extract-text-webpack-plugin
在webpack.config.js中先引入后插件然再配置
页面顶端定义:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
在loader里面定义
module: {
rules: [{
test: /\.(png|jpg)$/,
use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}, {
test: /\.json$/,
use: ['json-loader']
}, {
test: /\.js$/,
use: ['babel-loader'],
exclude: /node_modules/, // 解析时排除的文件夹(node_modules里面也有很多js,但需要排除)
}, {
test: /\.scss$/,
// use: ['style-loader','css-loader','sass-loader'], //打包进js
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ["css-loader",'sass-loader']
})
}
]
},
配置插件
plugins: [
new ExtractTextPlugin("css/index.css"),
]
注:css/index.css 表示css的路径,名字可以随便取,这里取名为index.css。启动服务器的时候并不会在本地生成css文件夹和index.css,而是在服务器新生成的html中自动插入link标签引入此路径下的css
本地的页面结构如下
开启服务器后的预览页面
现在开始测试打包
当输入打包命令后,如果在项目中有public文件夹,会在生成的html添加link连接样式表,并且生成上面自己的路径(css/index)。如果没有public文件夹(文件出口),会自动生成一个public文件夹
打包html
默认情况下,我们在public文件夹中新建一个index.html文件,这样打包后引入js。但要是不想在public建页面,在其他文件夹(比如src文件夹)新建页面,打包时再生成打包后的html。所以
安装插件
npm install --save-dev html-webpack-plugin
配置
plugins: [
//这里开始写
new HtmlWebpackPlugin({
template: 'src/index.html', //文件路径
inject: 'body' //打包之后的js插入文档的位置 (body表示script标签的位置在body里面的最下面即</body>上面)
}),
]
在src文件夹新建index.html
启动服务器
同css一样,本地页面无js添加,预览页面会自动添加js
打包
同css一样,无public,生成public文件夹新增index.html,内容自动添加script标签,有public文件夹,直接新增index.html,内容自动添加script标签
引入并打包img
安装url-loader或者file-loader
url-loader和file-loader 都是用于打包文件和图片,但是这2个加载器区别如下
一般限制小图片转 base64 可以用 url-loader,其他情况都用 file-loader。
url-loader应该是file-loader上加了一层过滤。本例中使用url-loader,但两者存在依赖关系,需全部安装才可使用。
安装
install url-loader file-loader --save-dev
配置
module: {
rules: [
{
test: /\.(png|jpg)$/,
use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}
]
}
test 属性代表可以匹配的图片类型,除了 png、jpg 之外也可以添加 gif 等,以竖线隔开即开。
limit 字段代表图片打包限制,指当图片大小小于限制时会自动转成 base64 码引用。
name 字段指定了打包后,在打包根目录(output.path)下生成名为 images 的文件夹,并在原图片名前加上8位 hash 值。
在greeter.js中引用
var imgurl = require('./assets/cat01.png');
然后就可以将字符串进行拼接使用了
所有配置
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: __dirname + "/src/main.js", //唯一入口文件
output: {
path: __dirname + "/public", //打包后的文件存放的地方供src引入加载
filename: "bundle.js" //打包后输出文件的文件名
},
devtool: 'eval-source-map', //配置生成Source Maps,选择合适的选项
devServer: {
contentBase: "./src", //本地服务器所加载的页面所在的目录
open: true,
compress: true
// colors: true, //终端中输出结果为彩色
// historyApiFallback: true, //不跳转
// inline: true, //实时刷新
// port: 9000
},
module: {
rules: [{
test: /\.(png|jpg)$/,
use: 'url-loader?limit=8192&name=images/[hash:8].[name].[ext]'
}, {
test: /\.json$/,
use: ['json-loader']
}, {
test: /\.js$/,
use: ['babel-loader'],
exclude: /node_modules/, // 解析时排除的文件夹(node_modules里面也有很多js,但需要排除)
}, {
test: /\.scss$/,
// use: ['style-loader','css-loader','sass-loader'],
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ["css-loader",'sass-loader']
})
}
]
},
plugins: [
//这里开始写
new HtmlWebpackPlugin({
template: 'src/index.html', //文件路径
inject: 'body' //打包之后的js插入文档的位置
}),
new ExtractTextPlugin("css/index.css"),
]
}