开篇
webpack 在如今的前端开发中,算是不可绕过的一个工具吧。特别是在开发SPA应用的时候,无论是开发环境,还是打包上线,都十分依赖webpack。
开发环境
win10
node -v: 10.15.0
npm -v: 6.4.1
Let's go。
体验0配置
进入到工作目录,然后创建项目
mkdir spa-webpack-demo
初始化
npm init -y
先来体验下 webpack4
的 0配置
:
安装 webpack
npm i -D webpack
安装好 webpack 依赖后,创建 src 文件夹,并在 src 中新建一个 index.js
。
mkdir src
cd src
type nul > index.js
修改 package.json
,在 scripts
选项中,添加两个命令:
"dev": "webpack --mode=development",
"prod": "webpack --mode=production"
好,完事了。接下来跑命令行,测试一下
npm run dev
正常情况下,控制台会有一段如下提示,因为 webpack
命令需要依赖 webpack-cli
,我们安装即可
Do you want to install 'webpack-cli' (yes/no): yes
webpack-cli
安装完成之后,会自动继续跑我们的 npm run dev
指令,即可看到项目中多了一个 dist 目录,而且多了一个 main.js
。
接下来继续尝试 npm run prod
,可以看到 dist/main.js
已被压缩。
这就是 webpack
号称的零配置,主要的工作就是定义了默认的entry
路径src/index.js
,定义了默认的output
路径dist/main.js
,然后加了一个mode
参数,根据mode
参数的不同帮我们添加一些预置的打包规则。
循序渐进
上述的流程里,只是体验了一把零配置的感觉,连html文件都没有,这里开始加上。
加个index.html
在根目录新建 index.html
,随便编写点内容
type nul > index.html
说到处理 html
文件,肯定少不了 html-webpack-plugin
, 安装它
npm i -D html-webpack-plugin
然后再项目根目录
新建一个 webpack.config.js
,webpack会自动使用它
type nul > webpack.config.js
webpack.config.js
的内容如下
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
module: {
rules: []
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html'
})
]
}
执行npm run dev
,即可看到 dist
文件夹多了个index.html
,这个index.html
自动引入了打包后的dist/main.js
。
加个本地服务器
index.html
是生成了,可总不能每次手动打开它在浏览器里面预览吧, webpack 官方推荐我们用 webpack-dev-server
做服务器,安装它
npm i -D webpack-dev-server
安装成功后, 修改webpack.config.js
,添加 devServer
选项 和 webpack.HotModuleReplacementPlugin
插件。
对于文件中已经添加过的内容,后面我都会用注释表示。
const path = require('path');
const webpack = require('webpack');
function resolve(dir) {
return path.join(__dirname, './', dir)
}
module.exports = {
// module - 略
devServer: {
contentBase: resolve('dist'), // 根目录
hot: true, // 是否开启热替换,无须手动刷新浏览器
port: 8081, // 端口
open: true, // 是否自动打开浏览器
noInfo: true // 不提示打包信息,错误和警告仍然会显示
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
// HtmlWebpackPlugin - 略
]
}
然后修改 package.json
scripts
的 dev
选项
"dev": "webpack-dev-server --mode=development",
注意:当 devServer
的 hot
参数为true
时,记得要在插件里添加new webpack.HotModuleReplacementPlugin()
,
或者你可以在命令行中带上hot
参数,这样就不需要自己再往plugins
中添加插件了。
"dev": "webpack-dev-server --hot --mode=development"
然后npm run dev
,就可以尝试静态资源
热替换功能了。
处理js, css 等其他静态资源
首先我们要清楚一点,webpack 它本身是不知道应该如何处理静态资源的,但是它提供了loader
和plugin
机制。
loader
的作用,顾名思义:加载器,就是匹配到的静态资源,都要经过loader
的内部处理,再返回处理之后的结果。我觉得,loader
像是一个拦截器。
说到js,我们会想到 babel-loader
,babel-loader
是干吗的?常规操作是,将匹配到的js文件
的ES6代码 根据 babelrc文件内的配置
编译成对应的 ES5代码。
我们这里先添加一个.babelrc
文件
新增.babelrc
文件
type nul > .babelrc
编辑 .babelrc
内容
{
// 预设置,告诉Babel要转换的源码使用了哪些新的语法特性
// targets, useBuiltIns 等选项用于编译出兼容目标环境的代码
// 其中 useBuiltIns 如果设为 "usage"
// Babel 会根据实际代码中使用的 ES6/ES7 代码,以及与你指定的 targets,按需引入对应的 polyfill
// 而无需在代码中直接引入 import '@babel/polyfill',避免输出的包过大,同时又可以放心使用各种新语法特性。
"presets": [
["@babel/preset-env", {
// modules 是否 将 ES6 的 import/export模块化 转为 babel 的 CommonJs 规范模块化
"modules": false,
"targets": {
// "> 1%" : 支持市场份额超过1%的浏览器,
// ""last 2 versions"": 支持每个浏览器最后两个版本
// "not ie <= 8": 在之前条件的浏览器中,排除 IE8 以下的浏览器
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
},
"useBuiltIns": "usage"
}]
],
// 所用插件
// transform-runtime 插件表示不管浏览器是否支持ES6,只要是ES6的语法,它都会进行转码成ES5
// 这个是需要优化的
"plugins": ["@babel/plugin-transform-runtime"]
}
安装 babel
依赖,注意:
babel 7+ 已经废弃了presets 中 stage-x 的用法,改为在plugins中添加。并且应用了npm scope包,代码全部在 @babel 中,避免以前那种 babel-preset-xxx, babel-plugin-xxx 的用法
最新的 babel-loader 版本是8+,需要依赖 babel-core 版本7+,包名为 @babel/core, 版本6+的包名为 babel-core
。
再分析上面的 .babelrc 文件,它用到了@babel/preset-env, @babel/plugin-transform-runtime, 这些依赖都要我们安装好
如果使用了 @babel/preset-env,则不支持在 plugins 中 添加 stage-x
npm i -D babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime
谈到css,我们就应该 想到 style-loader
和 css-loader
。先安装它们
npm i -D style-loader css-loader
再安装 url-loader
用于解析静态资源,如图片,字体等
npm i -D url-loader
然后修改 webpack.config.js
的rules
, 添加如下代码
module.exports = {
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: [
resolve('src'),
resolve('node_modules/webpack-dev-server/client')
]
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
exclude: [],
options: {
limit: 10000,
name: 'img/[name].[hash:7].[ext]'
}
}
]
},
// devServer - 略
// plugins - 略
}
接下来准备开发了,用 vue 吧。
vue 就不用装在 devDependencies 中了。
npm i -S vue
// vue-loader 依赖 vue-template-compiler 和 vue-style-loader
npm i -D vue-loader vue-template-compiler vue-style-loader
修改 webpack.config.js
, 添加 如下代码
const { VueLoaderPlugin } = require('vue-loader');
module.exports = {
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
// 其他 - 略
]
},
// devServer - 略
plugins: [
// 加在最前面
new VueLoaderPlugin()
// 其他 - 略
]
}
src 下 新建一个 views 目录 和 assets 目录,
我在 assets 目录下,增加了一个 logo.png 文件
views下创建一个 myTest 组件,myTest/index.vue
, 编辑 index.vue
<template>
<div>
<i class="logo"></i>
</div>
</template>
<script>
export default {
name: 'myTest'
}
</script>
<style scoped>
.logo {
display: block;
margin: auto;
width: 400px;
height: 400px;
background: url(../../assets/logo.png);
}
</style>
src
目录下新建一个 App.vue
, 内容如下
<template>
<div id="app">
<my-test></my-test>
</div>
</template>
<script>
import myTest from "./views/myTest/index";
export default {
name: "App",
components: {
myTest
}
};
</script>
编辑 src
目录下的 index.js
,内容如下:
import Vue from 'vue';
import App from './App';
new Vue({
el: '#app',
render: h => h(App)
})
最后npm run dev
,查看效果。
锦上添花
添加 打包进度条 信息,如下
npm i -D progress-bar-webpack-plugin
修改 webpack.config.js
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
// ....
plugins: [
// 其他 - 略
new ProgressBarPlugin()
]
添加 打包结果消息通知
npm i -D webpack-build-notifier
修改 webpack.config.js
const WebpackBuildNotifierPlugin = require('webpack-build-notifier');
// ....
plugins: [
// 其他 - 略
new WebpackBuildNotifierPlugin()
]
归类打包信息
npm i -D webpack-dashboard
修改 webpack.config.js
const DashboardPlugin = require('webpack-dashboard/plugin');
// ....
plugins: [
// 其他 - 略
new DashboardPlugin()
]
修改 package.json
"dev": "webpack-dashboard -- webpack-dev-server --mode=development"
这个我使用了,感觉效果不是很理想啊,会新开一个窗口,而且还不能滚动查看信息,不清楚是不是哪里用错了。
效果如图:

整个代码结构如图:

尚未完成的功能
production
环境,需要使用mini-css-extract-plugin
和optimize-css-assets-webpack-plugin
插件,抽离并优化 css 文件production
环境,需要使用UglifyJsPlugin
插件,压缩 js 文件,这个插件允许多核编译production
环境,需要使用optimization
选项splitChunks
vue-router
与vuex
的引入- 等等
写在最后
希望本文的流程能帮助到有需要的读者,另外本文的打包功能实现的比较粗糙,打包速度比较慢,如果看官有啥建议,请在评论下告知下我。
如果有错误的地方,还请指出。谢谢阅读。
代码地址 spa-webpack-demo