前言:
现在的前端项目,经常需要配置webpack,在很多前端招聘要求里,webpack打包、webpack工程化等字眼也越来越高,我们经常使用vue-cli脚手架来搭建我们的项目,它已经自动配置好了打包内容,那么,webpack的原理究竟是怎么样的,我们如果能手动搭建出来,webpack的配置多了,慢慢的就会得心应手,不要怕它,要去拥抱它,因为webpack在前端行业中已经成为不可或缺的一部分,让我们看看,怎样手动搭建一个webpack项目。
项目架构
> build // webpack配置文件夹
utils.js // 公用方法
webpack.base.conf.js // 公用配置
webpack.dev.conf.js // 开发环境配置
webpack.prod.conf.js // 生产环境配置
> dist // webpack打包生成文件夹
> static
> css
> fonts
> images
> js
index.html
> src
> assets // 静态资源
> css
> fonts
> images
> js
App.vue // vue入口文件
main.js // webpack打包入口文件
bable.config.js // babel配置文件(es6转码)
package.json // 项目配置文件
index.html // html模板
运行环境和依赖版本
版本问题是项目运行受阻最主要的一个原因,根据以下版本进行安装,出错明显少了很多
- node版本 10.15.3
- webpack版本 4.31.0
- webpack-cli版本 3.3.2
- webpack-dev-server版本 3.3.1
"dependencies": {
"vue": "^2.6.10",
"vue-loader": "^15.7.0",
"vue-template-compiler": "^2.6.10"
},
"devDependencies": {
"@babel/core": "^7.4.4",
"@babel/polyfill": "^7.4.4",
"@babel/preset-env": "^7.4.4",
"autoprefixer": "^9.5.1",
"babel-loader": "^8.0.6",
"clean-webpack-plugin": "^2.0.2",
"core-js": "^3.0.1",
"css-loader": "^2.1.1",
"file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0",
"less": "^3.9.0",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.6.0",
"postcss-loader": "^3.0.0",
"style-loader": "^0.23.1",
"url-loader": "^1.1.2",
"webpack": "^4.31.0",
"webpack-cli": "^3.3.2",
"webpack-dev-server": "^3.3.1",
"webpack-merge": "^4.2.1"
}
1 webpack 快速生成文件
我们首先配置一个webpack,看看实现生成文件的过程是怎样的
1.1 初始化项目,快速生成package.json配置文件
npm init -y
1.2 新建build文件夹,新建webpack.base.conf.js文件
// ./build/webpack.base.conf.js
const path = require('path')
module.exports = {
// 入口主文件
entry: path.resolve(__dirname, '../src/main.js'),
// 出口配置
output: {
path: path.resolve(__dirname, '../dist'), // 出口路径
filename: '[name].js' // 出口主文件路径和文件名
}
}
1.3 新建src文件夹,新建mian.js文件
// ./src/main.js
document.write("Hello, webpack!")
1.4 配置 scripts 执行命令
webpack打包工具webpack-cli改善定制webpack配置的设置
cnpm i -D webpack webpack-cli
// ./package.json
"scripts": {
"test": "webpack --config ./build/webpack.base.conf.js"
}
1.5 运行 webpack
npm run test
可以看到多了一个 dist文件夹,里面有一个生成的main.js文件,通过查找可以看出,文件已经成功生成
2 webpack 环境分析
webpack目前可以生成文件了,但是我们一般做项目的时候都需要区分开发环境和生产环境,因为开发环境注重的是调试方便,而生产环境注重的是性能,运行速度等,所以我们一般把开发环境和生产环境共用的配置,写在一个公用配置文件,然后分别在不同的环境中引入公用环境配置。
下面,我们开始配置webpack环境:
// ./build
utils.js
webpack.base.conf.js // 公用环境
webpack.dev.conf.js // 开发环境
webpack.prod.conf.js // 生产环境
utils.jswebpack打包公用文件,先封装好路径,方便调用
// ./build/utils.js
const path = require('path')
module.exports = {
// 绝对路径(相对build文件夹)
resolvePath: (dir = './') => {
return path.resolve(__dirname, dir)
},
// static路径
staticPath: (dir = './') => {
return path.posix.join('static', dir)
}
}
3 公用环境配置
公用环境配置文件是webpack.base.conf.js,该环境配置用于开发环境和生产环境,一般情况下这两个环境能够公用的配置都集中在公用环境实现
3.1 主要处理内容:
- 文件入口、出口
- html自动生成(免手动修改路径或文件名)
- 文件免后缀配置
- 路径快捷方式配置
- js文件ES6转ES5
- vue文件处理
- 图片文件处理
- 文字文件处理
3.2 文件入口、出口
// ./build/webpack.base.conf.js
const utils = require('./utils')
module.exports = {
// 入口主文件
entry: utils.resolvePath('../src/main.js'),
// 出口配置
output: {
path: utils.resolvePath('../dist'), // 出口路径
filename: utils.staticPath('js/') + '[name].[hash:4].js' // 出口主文件路径和文件名
}
}
[hash:n]是为了解决文件缓存问题,会生成带n位哈希值字符的文件
3.3 html自动生成(免手动修改路径或文件名)
html-webpack-plugin自动生成同步哈希值的html文件,避免手动改动引入的文件路径和文件名
cnpm i -D html-webpack-plugin
// ./build/webpack.base.conf.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
...
plugins: [
new HtmlWebpackPlugin({
template: utils.resolvePath('../index.html'), // html模板
filename: 'index.html', // 生成html文件名
//inject: 'body' // 生成入口文件引入代码位置:head、body,默认body
})
]
...
- 新增
index.html文件
/* ./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" />
<title>webpack-vue</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
- 运行webpack
npm run test
可以看到,生成index.html文件引入的main.js已自动修改
3.4 文件免后缀配置
类似import、require引入的文件,配置可以省略以下后缀
// ./build/webpack.base.conf.js
...
resolve: {
extensions: ['.js', '.json', '.vue'] // 文件引入免后缀
}
...
3.5 路径快捷方式配置
// ./build/webpack.base.conf.js
...
resolve: {
alias: {
'@': utils.resolvePath('../src') // src快捷路径
}
}
...
例如:
import '@/assets/images/01.jpg'
import '@/assets/css/base.css'
3.6 js文件ES6转ES5
虽然大部分浏览器已经支持ES6语法,但是还是有小部分不支持的,为确保我们写的js能正常使用,需要把ES6语法转义为ES5语法,并实现兼容
babel-loader只支持ES6语法转换,ES6新增的语法(如promise、Set、Maps、Proxy等则不支持)@babel/core把js代码分析成ast,方便各个插件分析语法进行相应的处理@babel/polyfillES6新增的语法转换(编译出来的文件会大很多,如果项目没用到新增语法,则不引入)@babel/preset-env可以配置让js兼容的运行环境core-jsES6新增的语法api
cnpm i -D babel-loader @babel/core @babel/polyfill @babel/preset-env core-js
3.6.1 配置文件内容如下:
// ./build/webpack.base.conf.js
...
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: /node_module/ // 排除该路径文件,避免花费大量时间在这里
}
]
}
...
3.6.2 根目录创建babel.config.js文件,内容如下:
// ./babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: 3
}
]
]
}
3.6.3 main.js补充内容如下:
// ./src/main.js
// import '@babel/polyfill' // 貌似已弃用,可用以下俩代替
import 'core-js/stable'
import 'regenerator-runtime/runtime'
3.7 vue文件处理
cnpm i -D vue vue-loader vue-template-compiler
// ./build/webpack.base.conf.js
...
const VueLoaderPlugin = require('vue-loader/lib/plugin')
...
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
new VueLoaderPlugin()
]
...
3.8 图片文件处理
url-loader和file-loader的区别是,url-loader可设置limit
url-loader封装了file-loader,url-loader不依赖于file-loader,因为url-loader内置了file-loader
当文件的大小小于或等于limit设定数值时,url-loader将会把文件转为DataURL,渲染为base64字符串
当文件大小大于limit设定数值时,url-loader会调用file-loader进行处理
cnpm i -D url-loader
// ./build/webpack.base.conf.js
...
module: {
rules: [
{
test: /\.(jpe?g|png|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1024 * 10, // 单位:b
name: utils.staticPath('images/') + '[name]-[hash:4].[ext]'
}
}
]
}
]
}
...
3.9 文字文件处理
cnpm i -D url-loader
// ./build/webpack.base.conf.js
module: {
rules: [
{
test: /\.(woff2?|eot|ttf|otf|svg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1,
name: utils.staticPath('fonts/') + '[name]-[hash:4].[ext]'
}
}
]
}
]
}
3.10 完整代码展示:
// ./build/webpack.base.conf.js
const utils = require('./utils')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
entry: utils.resolvePath('../src/main.js'),
output: {
path: utils.resolvePath('../dist'),
filename: utils.staticPath('js/') + '[name].[hash:4].js'
},
module: {
rules: [
{
test: /\.jsx?$/,
use: [
{
loader: 'babel-loader'
}
],
exclude: /node_module/
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.(jpe?g|png|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1,
name: utils.staticPath('images/') + '[name]-[hash:4].[ext]'
}
}
]
},
{
test: /\.(woff2?|eot|ttf|otf|svg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1,
name: utils.staticPath('fonts/') + '[name]-[hash:4].[ext]'
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: utils.reslovePath('../index.html'),
filename: 'index.html'
}),
new VueLoaderPlugin()
],
resolve: {
extensions: ['.js', 'json', 'vue'],
alias: {
'@': utils.resolvePath('../src')
}
}
}
4 开发环境配置
开发环境配置文件是webpack.dev.conf.js,该环境配置侧重于调试效率
4.1 主要处理内容:
- 合并公用webpack配置
- 设置devtool方便开发调试
- 处理less和css文件,为其自动添加样式前缀,并使其以style的形式展示在页面中
- 配置devServer,修改webpack-dev-server的默认配置
4.2 合并公用webpack配置
webpack-merge用于合并webpack配置
cnpm i -D webpack-merge
// ./build/webpack.dev.conf.js
const webpackMerge = require('webpack-merge')
const webpackBase = require('./webpack.base.conf')
module.exports = webpackMerge(webpackBase, {
mode: 'development'
})
4.3 启动开发环境webpack
webpack-dev-server支持自动刷新的webpack,一般用于开发环境
cnpm i -D webpack-dev-server
// ./build/webpack.dev.conf.js
...
devServer: {
port: 9000
}
...
启动命令配置
// ./package.json
"scripts": {
"dev": "webpack-dev-server --inline --progress --config ./build/webpack.dev.conf.js"
}
运行命令
npm run dev
浏览器查看效果
localhost:9000
启动完之后,访问:location:9000,查看效果
4.4 设置devtool方便开发调试
// ./build/webpack.dev.conf.js
...
devtool: 'inline-source-map'
...
4.5 处理less和css文件,为其自动添加样式前缀,并使其以style的形式展示在页面中
less-loader处理lesspostcss-loader处理自动添加样式前缀autoprefixer处理自动添加样式前缀css-loader处理cssstyle-loader处理style
cnpm i -D less-loader postcss-loader autoprefixer css-loader style-loader
// ./build/webpack.dev.conf.js
...
module: {
rules: [
{
test: /\.(c|le)ss$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
sourceMap: true,
plugins: (loader) => [
require('autoprefixer')({
overrideBrowserslist: ['last 2 versions', '> 1%']
})
]
}
},
{ loader: 'less-loader' }
]
}
]
}
...
4.6 完整代码展示:
// ./build/webpack.dev.conf.js
const webpackMerge = require('webpack-merge')
const webpackBase = require('./webpack.base.conf')
module.exports = webpackMerge(webpackBase, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
prot: 9000
},
module: {
rules: [
{
test: /\.(c|le)ss$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
sourceMap: true,
plugins: (loader) => [
require('autoprefixer')({
overrideBrowserslist: ['last 2 versions', '> 1%']
})
]
}
},
{ loader: 'less-loader' }
]
}
]
}
})
5 生产环境配置
生产环境配置文件是webpack.prod.conf.js,该环境配置侧重于代码压缩和性能
5.1 主要处理内容:
- 合并公用webpack配置
- 设置devtool方便追踪代码
- 每次打包前都先清空dist打包文件夹
- 处理less和css文件,为其自动添加样式前缀,并使其抽离样式为单独css文件
5.2 合并公用webpack配置
// ./build/webpack.prod.conf.js
const webpackMerge = require('webpack-merge')
const webpackBase = require('./webpack.base.conf')
module.exports = webpackMerge(webpackBase, {
mode: 'production'
})
5.3 启动生产环境webpack
启动命令配置
// ./package.json
"scripts": {
"build": "webpack --config ./build/webpack.prod.conf.js"
}
运行命令
npm run build
查看dist文件夹变化
5.4 设置devtool方便开发调试
// ./build/webpack.prod.conf.js
...
devtool: 'eval'
...
5.5 每次打包前都先清空dist打包文件夹
clean-webpack-plugin清空打包文件夹
cnpm i -D clean-webpack-plugin
// ./build/webpack.prod.conf.js
const CleanWebpackPlugin = require('clean-webpack-plugin')
...
plugins: [
new CleanWebpackPlugin()
]
...
5.6 处理less和css文件,为其自动添加样式前缀,并使其以style的形式展示在页面中
less-loader处理lesspostcss-loader处理自动添加样式前缀autoprefixer处理自动添加样式前缀css-loader处理cssmini-css-extract-plugin抽离样式为单独css文件 因为前面几项依赖在配置开发环境的时候已经安装,所以现在只安装mini-css-extract-plugin即可
cnpm i -D mini-css-extract-plugin
// ./build/webpack.dev.conf.js
const utils = require('./utils')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
...
module: {
rules: [
{
test: /\.(c|le)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: { publicPath: '../../' } // 公用路径调试到外层,直到样式路径正确为止
},
{ loader: 'css-loader' },
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
sourceMap: true,
plugins: (loader) => [
require('autoprefixer')({
overrideBrowserslist: ['last 2 versions', '> 1%']
})
]
}
},
{ loader: 'less-loader' }
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: utils.staticPath('css/') + '[name].[hash:4].css'
})
]
...
5.7 完整代码展示:
// ./build/webpack.prod.conf.js
const utils = require('./utils')
const webpackMerge = require('webpack-merge')
const webpackBase = require('./webpack.base.conf')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = webpackMerge(webpackBase, {
mode: 'production',
devtool: 'eval', // cheap-source-map
module: {
rules: [
{
test: /\.(c|le)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: { publicPath: '../../' } // 公用路径调试到外层
},
{ loader: 'css-loader' },
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
sourceMap: true,
plugins: (loader) => [
require('autoprefixer')({
overrideBrowserslist: ['last 2 versions', '> 1%']
})
]
}
},
{ loader: 'less-loader' }
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: utils.staticPath('css/') + '[name].[hash:4].css'
})
]
})
6 src文件夹文件增改
- 新增
assets静态资源文件夹 - 修改
main.js入口文件 - 修改
App.vue入口文件
6.1 新增assets静态资源文件夹
6.1.1 新增css样式文件
/* ./src/assets/css/base.css */
html {
display: flex;
}
/* ./src/assets/css/index.less */
html {
height: 100%;
background: #f45 url(../images/02.jpg) center no-repeat;
}
6.1.2 新增images图片文件
./src/assets/images/01.jpg
./src/assets/images/02.jpg
6.1.3 新增fonts文字文件
./src/fonts/(生成的文字图标文件)
6.2 修改main.js入口文件
完整代码展示:
// ./src/main.js
// import '@babel/polyfill'
// import 'core-js/stable'
// import 'regenerator-runtime/runtime'
import Vue from 'vue'
import App from './App'
import '@/assets/fonts/iconfont.css'
import '@/assets/css/base.css'
import '@/assets/css/index.less'
new Vue({
el: '#app',
render: (h) => h(App)
})
6.3 修改App.vue入口文件
完整代码展示:
// ./src/App.vue
<template>
<div class="app">
<h1>vue app</h1>
<p><img :src="imgSrc" /></p>
<div v-for="(item, index) in count" :key="index">
<span class="iconfont icon-dizhiaddress"></span>
<span>{{ item }}</span>
</div>
</div>
</template>
<script>
import imgSrc from '@/assets/images/01.jpg'
export default {
data() {
return {
imgSrc,
count: 20
}
}
}
</script>
<style lang="less" scoped>
.app {
color: yellow;
}
</style>
7 启动命令查看效果
7.1 开发环境
npm run dev
浏览器查看效果
localhost:9000
启动完之后,访问:location:9000,查看效果,页面已经渲染成功,修改vue文件内容然后保存,我们看到页面也会自动更新对应的内容,开发环境配置成功
7.2 生产环境
npm run build
编译完之后,dist文件夹会生成对应的文件
浏览器打开index.html文件查看效果
总结:
还是不断重复敲多几次,敲到熟练即可,熟悉它的工作原理和用途,熟悉了这部分,接下来请看下集【项目】webpack+vue---手动搭建项目(下篇:vue搭建配置)
附上本项目github地址:webpack-vue,建议切到feature-webpack分支进行查阅