本文介绍webpack3.x的使用
目录
开始
css文件打包
image文件打包
字体文件打包
json文件打包
csv文件和xml文件打包
多入口文件打包
清理dist目录
development开发环境错误定位
development开发环境开发模式
development开发环境模块热替换
development开发环境模块热替换存在的问题
Tree Shaking(死代码终结者)
development和production代码分离与合并
系统环境变量设置
css文件剥离
代码分割
按需加载
浏览器资源缓存
全局导入
no export导出
静态服务器搭建
安装webpack
npm init -y
npm install webpack --save-dev
使用命令行bundle文件
npx webpack src/index.js dist/bundle.js
使用配置文件bundle文件
项目根目录创建webpack.config.js文件,写入如下内容
const path = require("path");
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
运行命令 npx webpack --config webpack.config.js
或者在package.json中写
"scripts": {
"build": "webpack"
}
运行 npm run build 即可,默认去找项目根目录的webpack.config.js
项目根目录下面一个dist目录用来放打包后的文件,src目录用来放源文件,index.html放在dist目录中,webpack默认这样的
在src/index.js中 import './style.css,让webpack解析此类文件,完成如下几步
首先,安装插件
npm install --save-dev style-loader css-loader
其次,修改webpack.config.js的配置为
const path = require("path");
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, "dist")
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
}
}
最后,运行webpack即可,css文件都打包到bundle.js中,运行时自动插入到header中
提示:
css-loader对css文件中的url图片都做了处理,将其替换成打包后的绝对路径
在src/index.js中 import MyImage from './4.jpg'; 其中MyImage是图片的打包后的绝对路径
安装插件,npm install --save-dev file-loader
修改,webpack.config.js
在module中添加如下rules
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
运行打包即可
在css文件中写自定义字体,如
@font-face {
font-family: 'MyFont';
src: url('./my-font.woff2') format('woff2'),
url('./my-font.woff') format('woff');
font-weight: 600;
font-style: normal;
}
同样使用file-loader,修改,webpack.config.js
在module中添加如下rules
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
运行webpack即可
webpack默认支持json文件导入,所以无需任何额外操作
在src/index.js中
import data from './index.json';
console.log(data);
index.json中这样定义
{
"a": 1,
"b": 2
}
安装插件 npm install --save-dev csv-loader xml-loader
添加如下的rules
{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},
{
test: /\.xml$/,
use: [
'xml-loader'
]
}
使用方式和json文件类似
如果你想把你的js文件拆分成小的文件组合,多入口很好的解决这个问题
比如,在src/index.js文件中 import printMe from './print.js';
修改 webpack.config.js 配置如下
const path = require("path");
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, "dist")
}
}
修改 dist/index.html
<script src="./print.bundle.js"></script>
<script src="./app.bundle.js"></script>
打包编译即可
问题:我们必须手动在index.html中插入js文件,可以使用html-webpack-plugin自动插入脚本
安装,npm install --save-dev html-webpack-plugin
修改 webpack.config.js 文件如下
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
plugins: [
new HtmlWebpackPlugin({
title: "自动生成的html文件"
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, "dist")
}
}
运行编译即可
在打包之前,先清理干净我们的dist输出目录,是非常好的做法
安装 npm install clean-webpack-plugin --save-dev
修改 webpack.config.js 文件
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: "自动生成的html文件"
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, "dist")
}
}
运行编译即可
定位错误信息
所有的文件打包到bundle中,如果代码是错误的,要想定位到错误的源文件,必须使用source-map
修改 webpack.config.js
添加 devtool: 'inline-source-map',
运行打包,如果在浏览器中运行错误,会定位错误源
每次查看代码都要运行 npm run build, 然后刷新浏览器,这是很烦的一个操作
webpack有如下几种模式,自动的帮你完成上面两个操作
Watch 模式
首先,在package.json中添加如下命令
"watch": "webpack --watch"
然后运行 npm run watch
当你修改你的代码,webpack自动帮你编译
webpack-dev-server 模式
首先,安装 npm install --save-dev webpack-dev-server
然后,修改 webpack.config.js 在module.exports中添加如下属性
devServer: {
contentBase: './dist'
}, // 作用是告诉webpack在localhost:8080上观察dist目录下面的内容
然后,在package.json中添加如下命令
"start": "webpack-dev-server --open"
运行 npm start
当你修改了你的代码,webpack会自动帮你编译,并且自动帮你刷新浏览器
webpack-dev-middleware 模式
这种模式和webpack-dev-server类似,不过提供了更多的配置
安装 npm install --save-dev express webpack-dev-middleware
修改 webpack.config.js 为如下内容
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
devtool: 'inline-source-map',
// devServer: {
// contentBase: './dist'
// },
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: "自动生成的html文件"
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, "dist"),
publicPath: '/'
}
}
在项目根目录下添加 server.js,写如下内容,涉及到node.js
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const app = express();
const config = require('./webpack.config.js');
const compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath
}))
app.listen(3000, function() {
console.log("app listenging on port 3000!\n");
})
在package.json中添加如下命令
"server": "node server.js"
运行 npm run server 即可
打开 localhost:3000 查看
热替换,简单理解,就是什么文件修改了,更新什么文件
开启模块热替换webpack方式
修改webpack.config.js文件为
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: {
// app: './src/index.js',
// print: './src/print.js'
app: './src/index.js' // 多bundle文件不需要写多个entry实现,HotModuleReplacementPlugin自动完成
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
hot: true // 开启热替换
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: "自动生成的html文件"
}),
new webpack.NamedModulesPlugin(), // 在重新编译的时候,会展示被更新模块的相对路径,如 “ [./src/print.js] ./src/print.js 98 bytes {0} [built] ”
new webpack.HotModuleReplacementPlugin()
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, "dist")
}
}
运行 npm start
开启模块热替换node.js方式
首先,修改webpack.config.js文件
去掉devServer配置项
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: {
app: './src/index.js'
},
devtool: 'inline-source-map',
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: "自动生成的html文件"
}),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, "dist")
}
}
修改server.js
const webpack = require('webpack');
const webpackDevServer = require('webpack-dev-server');
const config = require('./webpack.config.js');
const options = {
contentBase: './dist',
hot: true,
host: 'localhost'
};
webpackDevServer.addDevServerEntrypoints(config, options);
const compiler = webpack(config);
const server = new webpackDevServer(compiler, options);
server.listen(5000, 'localhost', () => {
console.log("dev server listening on port 5000");
})
运行 npm run server
css文件的热替换,直接使用style-loader即可自动完成
src/index.js内容如下
import printMe from './print.js';
function fn() {
console.log("aaaa")
var element = document.createElement('div');
var btn = document.createElement('button');
element.innerHTML = "hahaha";
btn.innerHTML = "按钮";
btn.onclick = printMe;
element.appendChild(btn);
return element;
}
document.body.appendChild(fn());
if(module.hot) { // 监听模块热更新
module.hot.accept('./print.js', function(){
console.log("print.js更新了");
printMe();
})
}
src/print.js内容如下
export default function printMe() {
console.log("print.js")
}
当更新print.js时,点击按钮,输出的信息还是之前的信息,这可以说是webpack热替换的一个硬伤
解决上面这个问题,最原始的方法,如下
if(module.hot) {
module.hot.accept('./print.js', function(){
document.body.removeChild(element);
element = fn();
document.body.appendChild(fn);
})
}
当你的代码中只import某个文件的一部分,但是打包的时候这个文件全部的内容都包含进来了,这就是Tree Shaking要解决的
下载插件 npm install --save-dev uglifyjs-webpack-plugin
将插件添加到webpack.config.js中即可
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
plugins: [
new UglifyJSPlugin()
]
运行npm run build,只用import了的代码才会被打包
当一个文件的内容被其他的文件导入,然后重新导出,此时Tree Shaking失效了
注:在命令行中 --optimize-minimize 可以实现同样效果
将webpack的配置拆分成development和production需要用到webpack-merge插件,作用是合并配置
安装插件 npm install --save-dev webpack-merge
将webpack.config.js文件删除,添加如下三个
webpack.common.js
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js'
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: "生成的index.html"
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
webpack.dev.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
}
});
webpack.prod.js
const merge = require('webpack-merge');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
devtool: 'source-map', // 生产环境最好也加上source-map,如果你不需要可以去掉
plugins: [
new UglifyJSPlugin({
sourceMap: true
})
],
output: {
publicPath: '/Test' // 指定发布路径
}
});
修改package.json命令
"scripts": {
"start": "webpack-dev-server --open --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
}
利用process.env.NODE_ENV可以方便的设置development和production模式
利用webpack的DefinePlugin插件,可以方便设置此变量
修改webpack.prod.js文件
const merge = require('webpack-merge');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const common = require('./webpack.common.js');
const webpack = require('webpack');
module.exports = merge(common, {
devtool: 'source-map',
plugins: [
new UglifyJSPlugin({
sourceMap: true
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
});
然后在你自己的源代码中,随便访问环境变量
if(process.env.NODE_ENV != 'production') {
console.log("我不是production模式");
}else {
console.log("我是production模式")
}
注:在命令行中 --define process.env.NODE_ENV="'production'" 可以实现同样效果
利用webpack自己的内置的方式设置环境变量
使用--env自定义环境变量
如,package.json命令 "start": "webpack-dev-server --open --config webpack.dev.js --env.production=false --env.MyEnv=development --progress",
修改webpack.dev.js
module.exports = env => {
console.log(env); -> 输出 { production: 'false', MyEnv: 'development' }
return merge(common, {
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
}
});
}
将css从bundle中剥离出来,需要如下几步
安装 npm install --save-dev extract-text-webpack-plugin
修改 webpack.common.js文件
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: {
app: './src/index.js'
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({ // 剥离文件
fallback: "style-loader",
use: "css-loader"
}),
include: path.resolve(__dirname, "src")
}
]
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: "生成的index.html"
}),
new ExtractTextPlugin({ // 设置插件选项
filename: "styles.css",
disable: process.env.NODE_ENV == "developement"?true: false
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
只在production模式中才会剥离css,在development中不剥离从而提高性能
代码分割有如下几种方式
多入口,开篇讲过写法
entry: {
app: './src/index.js',
entry: './src/print.js'
},
这种写法存在问题,当这些entry中都import了相同的模块,那么打包后的每个bundle中都存在重复的代码
解决办法如下
在webpack.common.js中的plugins项中,添加如下配置
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // 剥离的文件名
})
CommonsChunkPlugin的作用就是将bundle中的重复代码提取到单独的文件中
动态import,单入口
也就是 import() 语法,此语法返回一个promise对象,使用步骤如下
首先,修改webpack.common.js配置如下
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: {
app: './src/index.js'
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: "生成的index.html"
}),
new ExtractTextPlugin({
filename: "styles.css",
disable: process.env.NODE_ENV == "developement"?true: false
})
],
output: {
filename: '[name].bundle.js',
chunkFilename: '[name].bundle.js', // import() 分离多文件的关键配置
path: path.resolve(__dirname, 'dist')
}
}
然后,假设src/other.js中的代码如下
export function fn () {
console.log("相互引用的js");
}
假设src/print.js中的代码如下
export async function GetOtherComponent() {
const _ = await import(/* webpackChunkName: "myOther" */ './other.js');
return _;
}
假设src/index.js中的代码如下
import { GetOtherComponent } from './print.js';
GetOtherComponent().then(data => {
data.fn();
console.log(data);
})
运行 npm run build, 输出了myOther.bundle.js文件
代码分离到此结束
按需加载还是需要借助import()函数
button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
var print = module.default; // 表示print.js中的export.default的对象
print();
});
利用webpack的chunkhash可以方便的监测到文件是否修改,如果修改自动更改文件名,浏览器更新资源
使用相当简单,只需要将output改为
output: {
// filename: '[name].bundle.js',
filename: '[name].[chunkhash].js',
chunkFilename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
配合CommonsChunkPlugin可以将公共的不变的代码剥离出来,从而让浏览器永远缓存不需要重新下载
修改webpack.common.js配置文件如下
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const webpack = require('webpack');
module.exports = {
entry: {
app: './src/index.js',
vendor: [ // 设置需要剥离的第三方库
"moment"
]
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: "生成的index.html"
}),
new ExtractTextPlugin({
filename: "styles.css",
disable: process.env.NODE_ENV == "developement"?true: false
}),
new webpack.optimize.CommonsChunkPlugin({
name: "vendor" // 将vendor入口打包单独输出
}),
new webpack.optimize.CommonsChunkPlugin({
name: "manifest" // 打包剩余部分,必须写在最后
})
],
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
}
}
存在的问题
webpack打包其中有一个module.id,这个东西是用来标识模块的,从而在剥离的manifest文件中引用
默认情况下module.id是根据模块打包的顺序来自增编号的,当用户打乱了模块import的顺序,那么打
包后所有的文件都被修改了,这是模块的chunkhash都发生改变了,为了解决这个问题,请使用如下方式
只需要将你的webpack.common.js中的plugins修改为
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: "生成的index.html"
}),
new ExtractTextPlugin({
filename: "styles.css",
disable: process.env.NODE_ENV == "developement"?true: false
}),
new webpack.HashedModuleIdsPlugin(), // 添加这一句话就ok了
new webpack.optimize.CommonsChunkPlugin({
name: "vendor"
}),
new webpack.optimize.CommonsChunkPlugin({
name: "manifest"
})
],
当你需要导入jQuery这样带有全局属性的模块的时候,此部分是你必须掌握的
首先,将jquery下载到项目,修改webpack.common.js文件,在plugins配置项中添加如下
new webpack.ProvidePlugin({
$: 'jquery',
jquery: 'jquery'
}), 当程序中出现$或者jquery时,webpack在编译的时候就会去找对应的模块
然后,在程序中直接访问即可,如 console.log($);
当然,当你的程序中只用到了其他插件的一部分代码,你想利用tree shaking的技术,可以参考如下
new webpack.ProvidePlugin({
join: ['lodash', 'join'] // 表示将lodash中的join方法注册成全局的,webpack只会打包此单个方法
})
当你的代码中不export任何代码,如何让其他模块import?,请看下面
首先在 other.js 中定义如下
var obj = {
"a": 1,
"b": 2,
"c": 3
}
var a = 1;
var fn = function() {
console.log(111);
}
在 index.js 中导入
import { a,fn,c } from './other.js';
安装插件 npm i exports-loader --save
修改webpack.common.js文件,在module中添加如下rules
{
test: require.resolve('./src/other.js'),
use: 'exports-loader?a,fn,c=obj.c'
}
当我们的代码编译完成之后,如何搭建一个静态服务器来查看我们编译的代码呢?
安装 npm install http-server --save-dev
在package.json文件中添加命令
"server": "http-server dist"
编译代码 npm run build 然后 npm run server 即可
存在的问题,当我们将server停止后,页面将无法访问,解决办法如下
安装 npm install workbox-webpack-plugin --save-dev
将插件配置,写到webpack.config.js中
const WorkboxPlugin = require('workbox-webpack-plugin');
在plugins配置项中添加
new WorkboxPlugin({
clientsClaim: true,
skipWaiting: true
})
在程序入口 index.js 中注册Serveic Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js').then(registration => {
console.log('SW registered: ', registration);
}).catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
运行 npm run build
直接浏览器访问 http://127.0.0.1:8080 即可