webpack
webpack 是一个模块打包器,主要目的是在浏览器上打包 JavaScript 文件。从图中我们可以看出,Webpack 可以将多种静态资源 js、css、less 转换成一个静态文件,减少了页面的请求。
特性
- 打包 CommonJs, AMD, ES6 modules, CSS, Images, JSON, Coffeescript, LESS,等模块
- 可创建单个或多个按需加载的块,以减少初始加载时间
- 在编译期间会解决依赖关系,减少了运行时的大小
初体验
-
全局安装 webpack
npm install -g webpack webpack-cli // window sudo npm install -g webpack webpack-cli// mac 或者 linux
-
新建目录
mkdir app // 新建app目录
- 在app目录下,新建
index.html
、index.js
、app.js
文件 填入以下内容
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
</body>
<script src="./dist/main.js"></script>
</html>
index.js
// 引入 app 组件
import app from "./app"
// 运行app 组件
app();
app.js
// 导出模块
export default function(){
document.write("webpack 例子 lol!");
}
-
在app目录下运行
webpack
-
默认会在app目录下生成
dist/main.js
文件
-
打开 app 目录下的
index.html
文件,可以看到以下结果
通过 webpack.config.js
进行配置
新建 webpack.config.js
文件,填入以下内容
const path = require("path")
module.exports = {
mode:"development",
entry:"./index.js",
output:{
path:path.resolve(__dirname,"dist"),
filename:"bundle.js"
}
}
运行
webpack
此时会在 当前目录下生成 dist/bundle.js
文件
核心概念
入口
**入口起点(entry point)**指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。例如:
webpack.config.js
module.exports = {
entry: './path/to/my/entry/file.js'
};
输出(output)
output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js
,其他生成文件默认放置在 ./dist
文件夹中。
你可以通过在配置中指定一个 output
字段,来配置这些处理过程:
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};
loader
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
在更高层面,在 webpack 的配置中,loader 有两个属性:
test
属性,识别出哪些文件会被转换。use
属性,定义出在进行转换时,应该使用哪个 loader。
例如使用 css-loader
实现样式的加载
- 本地安装依赖
npm install --save-dev style-loader css-loader
- 配置 webpack.config.js 文件
const path = require("path")
module.exports = {
mode:"development",
entry:"./index.js",
output:{
path:path.resolve(__dirname,"dist"),
filename:"bundle.js"
},
module:{
rules:[
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
]
}
}
index.js
// 引入 app 组件
import app from "./app"
// 运行app 组件
app();
index.css
.red{
color:red
}
App.js
// 导出模块
import "./index.css"
export default function(){
document.write("<h1 class='red'>webpack 例子 lol!</h1>");
}
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
</body>
<script src="./dist/bundle.js"></script>
</html>
插件(plugins)
loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。
想要使用一个插件,你只需要 require()
它,然后把它添加到 plugins
数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new
操作符来创建它的一个实例。
例子,该例子会将生成的 js 文件自动注入到 html文件中
- 本地安装插件和webpack
npm install --save-dev html-webpack-plugin // html 插件
npm install --save-dev webpack webpack-cli
npm install --save-dev style-loader css-loader
- Webpack.config.js 配置文件
const path = require("path")
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: "development",
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [new HtmlWebpackPlugin()]
}
- 添加以下文件
index.js
// 引入 app 组件
import app from "./app"
// 运行app 组件
app();
app.js
// 导出模块
import "./index.css"
export default function(){
document.write("<h1 class='red'>webpack 例子 lol!</h1>");
}
index.css
.red{
color:red
}
-
运行
webpack
-
默认在当前目录下生成
dist/html
并自动注入生成的js文件
模式(mode)
通过选择 development
, production
或 none
之中的一个,来设置 mode
参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production
。
module.exports = {
mode: 'production'
};
Devtool
此选项控制是否生成,以及如何生成 source map。
例如如下所示的的 dev-tool
const path = require("path")
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
devtool:"eval-source-map",
mode: "development",
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [new HtmlWebpackPlugin()]
}
生成代码之后,打开 chrome 控制台,可以看到如下所示目录
Devtool 的配置选项
devtool | 构建速度 | 重新构建速度 | 生产环境 | 品质(quality) |
---|---|---|---|---|
(none) | 非常快速 | 非常快速 | yes | 打包后的代码 |
eval | 非常快速 | 非常快速 | no | 生成后的代码 |
eval-cheap-source-map | 比较快 | 快速 | no | 转换过的代码(仅限行) |
eval-cheap-module-source-map | 中等 | 快速 | no | 原始源代码(仅限行) |
eval-source-map | 慢 | 比较快 | no | 原始源代码 |
eval-nosources-source-map | ||||
eval-nosources-cheap-source-map | ||||
eval-nosources-cheap-module-source-map | ||||
cheap-source-map | 比较快 | 中等 | yes | 转换过的代码(仅限行) |
cheap-module-source-map | 中等 | 比较慢 | yes | 原始源代码(仅限行) |
inline-cheap-source-map | 比较快 | 中等 | no | 转换过的代码(仅限行) |
inline-cheap-module-source-map | 中等 | 比较慢 | no | 原始源代码(仅限行) |
inline-source-map | 慢 | 慢 | no | 原始源代码 |
inline-nosources-source-map | ||||
inline-nosources-cheap-source-map | ||||
inline-nosources-cheap-module-source-map | ||||
source-map | 慢 | 慢 | yes | 原始源代码 |
hidden-source-map | 慢 | 慢 | yes | 原始源代码 |
hidden-nosources-source-map | ||||
hidden-nosources-cheap-source-map | ||||
hidden-nosources-cheap-module-source-map | ||||
hidden-cheap-source-map | ||||
hidden-cheap-module-source-map | ||||
nosources-source-map | 慢 | 慢 | yes | 无源代码内容 |
nosources-cheap-source-map | ||||
nosources-cheap-module-source-map |
对于开发环境
eval-source-map
- 每个模块使用 eval()
执行,并且 source map 转换为 DataUrl 后添加到 eval()
中。初始化 source map 时比较慢,但是会在重新构建时提供比较快的速度,并且生成实际的文件。行数能够正确映射,因为会映射到原始代码中。它会生成用于开发环境的最佳品质的 source map
。
对于生产环境
(none)
(省略 devtool
选项) - 不生成 source map。这是一个不错的选择。
优化
查看各个环节的打包时间
安装 speed-measure-webpack-plugin
npm install --save-dev speed-measure-webpack-plugin
然后在vue.config.js
文件里面添加如下配置
// vue.config.js
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
module.exports = {
configureWebpack: smp.wrap({
plugins: [
]
})
}
运行
npm run build
可以看到各个环节的打包时间
优化配置
隐藏源码
在 根目录下的 vue.config.js
文件中添加如下配置
module.exports = {
productionSourceMap: false
}
开启CDN加速
module.exports = {
chainWebpack: config => {
// 提取公用代码, 使用免费的cdn资源
config.externals({
vue: 'Vue',
vuex: 'Vuex',
'vue-router': "VueRouter",
'element-ui': "'element-ui'",
})
}
}
index.html 页面填入相应的CDN地址
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.0/vue.runtime.min.js"></script>
<script src="https://unpkg.com/vuex@2.0.0/dist/vuex.min.js"></script>
<script src="https://unpkg.com/vue-router@3.3.4/dist/vue-router.min.js"></script>
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
</body>
</html>
代码最小化处理
module.exports = {
chainWebpack: config => {
// 代码最小化处理
config.optimization.minimize(true);
}
}
使用splitChunks进行代码分割
// 使用splitChunks进行代码分割
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
libs: {
name: 'my-chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // only package third parties that are initially dependent
}
}
})
路由懒加载
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
或者
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
Webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中。
开启GZIP优化
gzip压缩
如果后台有对前端的代码进行gzip压缩的话,那么就不需要进行压缩了,后台自己配置就可以。
完整配置
vue.config.js
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
module.exports = {
publicPath:"/dist", // 设置应用的目录 可以改成 app myapp 等 对应的是服务器下的目录
productionSourceMap: false, // 生成生产版本的时候 关闭 源码映射
configureWebpack: smp.wrap({
// https://webpack.js.org/configuration/devtool/#development
// 修改source map
// devtool:"eval-source-map",
plugins: [
]
}),
chainWebpack: config => {
// 代码最小化处理
config.optimization.minimize(true);
// 使用splitChunks进行代码分割
config.optimization.splitChunks({
// // include all types of chunks
chunks: 'all',
cacheGroups: {
libs: {
name: 'my-chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // only package third parties that are initially dependent
}
}
})
// 提取公用代码, 使用免费的cdn资源
config.externals({
vue: 'Vue',
vuex: 'Vuex',
'vue-router': "VueRouter",
'element-ui': "'element-ui'",
})
}
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.0/vue.runtime.min.js"></script>
<script src="https://unpkg.com/vuex@2.0.0/dist/vuex.min.js"></script>
<script src="https://unpkg.com/vue-router@3.3.4/dist/vue-router.min.js"></script>
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
</body>
</html>