使用webpack5搭建vue开发环境
前言
我们平时开发一个vue项目的时候,通常会根据vue官网推荐,直接执行以下命令创建一个项目:
npm init vue@latest
这一指令将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具。
但是,这不是这篇文章的主题。本篇文章将会记录使用 Webpack 5.0 配置 Vue 开发环境的过程,进一步了解 Vue CLI 在创建项目的过程中做了什么工作。
准备
webpack概念介绍
如果之前使用过webpack4配置过vue开发环境,那么可以看从webpack4迁移到webpack5
环境配置
初始化项目
新建项目根目录文件夹,文件夹命名为项目名称。进入文件夹,执行下列命令:
npm init -y
根目录下会生成package.json
文件,修改如下:
{
"name": "webpack5-vue-template",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
},
"keywords": [],
"author": "ricky",
"license": "ISC"
}
安装webpack
执行命令:
npm i webpack webpack-cli -D
package.json
中会出现:
{
"name": "webpack5-vue-template",
...
"devDependencies": {
"webpack": "^5.86.0",
"webpack-cli": "^5.1.4"
}
}
配置打包环境
项目根路径下新建 build
目录,并在其中新建三个js
文件:
webpack.common.js
公共环境配置webpack.dev.js
开发环境配置webpack.prod.js
生产环境配置
修改 package.json 的 scripts 参数,在运行脚本时传入环境变量供脚本内判断使用:
{
...
"scripts": {
"build:dev": "webpack --progress --config ./build/webpack.dev.js",
"build": "webpack --progress --node-env production --config ./build/webpack.prod.js"
},
...
}
在根目录下,新建src
文件夹——存放业务代码,在其中新建main.js
、assets
。
assets
目录内新建 fonts
以及 img
目录 。
安装 webpack-merge
安装这个依赖,用于合并公共配置与特定环境的配置。最终导出预期的配置文件
npm i webpack-merge -D
webpack.common.js
const path = require('path');
module.exports = {
entry: {
main: path.resolve(__dirname, '../src/main.js')
},
output: {
path: path.resolve(__dirname, '../dist')
},
}
配置resolve:省略部分经常用到的文件路径
...
module.exports = {
entry: {
...
},
resolve: {
alias: {
'@': path.resolve(__dirname, '../src'),
'@img': path.resolve(__dirname, '../src/assets/img')
},
extensions: ['.js', '.vue']
},
...
}
webpack.dev.js
const {
merge } = require('webpack-merge')
const commonConfig = require('./webpack.common')
const mode = process.env.NODE_ENV === 'production' ? 'production' : 'development'
const devConfig = {
mode,
output: {
filename: 'js/[name].js',
chunkFilename: 'js/[name].chunk.js'
}
}
module.exports = merge(commonConfig, devConfig)
webpack.prod.js
const {
merge } = require('webpack-merge')
const commonConfig = require('./webpack.common')
const mode = process.env.NODE_ENV === 'production' ? 'production' : 'development'
const prodConfig = {
mode,
output: {
filename: 'js/[name].[contenthash:8].js',
chunkFilename: 'js/[name].[contenthash:8].chunk.js'
}
}
module.exports = merge(commonConfig, prodConfig)
dist目录下生成index.html
在根目录下新建/public/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><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="app"></div>
</body>
</html>
安装html-webpack-plugin
插件依赖:
npm i html-webpack-plugin -D
在webpack.common.js
中配置html模板:
...
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'),
title: 'This is a template'
})
],
output: {
...
}
}
配置开发环境
DevServer 热更新
安装server依赖,执行命令:
npm i webpack-dev-server -D
在webpack.dev.js
中配置devServer
属性:
const path = require('path')
...
const devConfig = {
...
devServer: {
static: path.resolve(__dirname, '../dist'),
port: 3000,
open: true,
hot: true
},
output: {
...
}
}
...
在package.json
中配置 script
属性的执行指令:
{
...
"scripts": {
"serve": "webpack-dev-server --progress --config ./build/webpack.dev.js",
...
},
...
}
Babel语言转换
安装开发依赖:
npm i babel-loader @babel/core @babel/preset-env -D
安装生产依赖:
npm i @babel/polyfill core-js
在公共配置webpack.common.js
中配置module.roules
对js文件的加载规则:
...
module.exports = {
...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
},
plugins: [
...
],
...
}
在项目根目录下新建babel.config.js
文件:
module.exports = {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: 3
}
]
]
}
使用babel之前
使用babel转换之后
css以及stylus样式打包
也可以是scss 或者 less,这里示例使用的是stylus。先安装依赖:
npm i css-loader style-loader postcss-loader autoprefixer stylus stylus-loader -D
css样式转换是在打包的时候转换的,所以安装的是开发依赖。在webpack.dev.js
中配置:
...
const devConfig = {
...
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
'postcss-loader'
]
},
{
test: /\.styl(us)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
//启用/禁用或者设置在 css-loader 前应用的 loader 数量
importLoaders: 2
}
},
'postcss-loader',
'stylus-loader'
]
}
]
},
output: {
...
}
}
...
这里使用到了postcss
,所以在根目录下新建其配置文件postcss.config.js
:
module.exports = {
plugins: [
require('autoprefixer') // 属性根据浏览器不同,自动添加样式前缀
]
}
根目录下新建.browserslistrc
:
> 1%
last 2 versions
not dead
not ie 11
优化: 在生产环境中,抽离成单独的css样式文件,并且压缩样式代码。开发环境不需要。
-
安装依赖:
npm i mini-css-extract-plugin css-minimizer-webpack-plugin -D
webpack插件链接:
-
配置
webpack.prod.js
:... const MiniCssExtractPlugin = require('mini-css-extract-plugin') const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') ... const prodConfig = { ... module: { rules: [ { test: /\.css$/, use: [ // 压缩css文件,需配置的loader MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 1 } }, 'postcss-loader' ] }, { test: /\.styl(us)$/, use: [ // 压缩css文件,需配置的loader MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 2 } }, 'postcss-loader', 'stylus-loader' ] } ] }, optimization: { minimizer: [ // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释 // `...`, new CssMinimizerPlugin(), // 这将仅在生产环境开启 CSS 优化。 // 如果还想在开发环境下启用 CSS 优化,请将 optimization.minimize 设置为 true: ] }, plugins: [ new MiniCssExtractPlugin({ // 决定输出的每个 CSS 文件的名称 filename: 'css/[name].[contenthash:8].css', // 决定非入口的 chunk 文件名称,仅在 webpack@5 下可用 chunkFilename: 'css/[name].[contenthash:8].chunk.css' }) ], output: { ... } } ...
对字体、图片、媒体等静态资源的打包
配置webpack.common.js
:
...
const isProduction = process.env.NODE_ENV === 'production'
module.exports = {
...
module: {
rules: [
...
{
test: /\.(ttf|woff|woff2|eto|svg)$/,
exclude: path.resolve(__dirname, '../src/assets/img'),
type: 'asset',
parser: {
dataUrlCondition: {
//如果一个模块源码大小小于 maxSize,那么模块会被作为一个 Base64 编码的字符串注入到包中, 否则模块文件会被生成到输出的目标目录中。
maxSize: 4 * 1024 // 4kb
}
},
generator: {
filename: isProduction
? 'static/fonts/[name].[contenthash:8][ext]'
: 'static/fonts/[name][ext]'
}
},
{
test: /\.(jpe?g|png|gif|svg)$/,
exclude: path.resolve(__dirname, '../src/assets/fonts'),
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 4 * 1024
}
},
generator: {
filename: isProduction ?
'static/img/[name].[contenthash:8][ext]' :
'static/img/[name][ext]'
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)$/,
type: 'asset/resource',
generator: {
filename: isProduction ?
'static/video/[name].[contenthash:8][ext]' :
'static/video/[name][ext]'
}
}
]
},
...
}
关于rules.Rule.type ,即资源模块的说明,详见官网说明
打包vue单文件的配置
安装依赖:
- 安装视图框架vue:
npm i vue
- 安装加载器,编译器:
npm i vue-loader @vue/compiler-sfc -D
配置webpack.common.js
:
...
const {
VueLoaderPlugin } = require('vue-loader')
...
module.exports = {
...
module: {
rules: [
...
{
test: /\.vue$/,
loader: 'vue-loader'
},
...
]
},
plugins: [
...
new VueLoaderPlugin()
],
...
}
至此,vue开发环境已经搭建完成!
集成vue生态圈功能
app入口
在src
目录下,新建App.vue
:
<template>
<div class="app">{
{ msg }}</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
msg: 'Hello world'
}
}
}
</script>
<style lang="stylus" scoped>
.app
color: skyblue
</style>
在src
目录下,新建main.js
:
import {
createApp } from 'vue'
import App from './App'
const app = createApp(App)
app.mount('#app')
添加Vue Router 和 Vuex
安装生产依赖:
npm i vue-router vuex
在src
目录下,分别新建router
和 store
目录,再依次新建index.js
文件,具体使用方法可以参考vue官网。
编辑/router/index.js
文件 :
import {
createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// 懒加载。webpackChunkName 指定chunkname为 about
component: () => import(/* webpackChunkName: "about" */ '@/views/About')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
注意: 这里使用了history
的路由模式,所以需在webpack.dev.js
开发环境中配置:
...
const devConfig = {
...
devServer: {
...
// 配置history路由模式
historyApiFallback: true
},
...
}
...
编辑/store/index.js
文件:
import {
createStore } from 'vuex'
const store = createStore({
state: {
count: 1
},
actions: {
add ({
commit }) {
commit('add')
}
},
mutations: {
add (state) {
state.count++
}
},
getters: {
getCount (state) {
return state.count
}
}
})
export default store
将router和store添加到main.js中:
import {
createApp } from 'vue'
import App from './App'
import router from './router'
import store from './store'
const app = createApp(App)
app.use(router).use(store).mount('#app')
Vue3中 vuex 替换为 pinia 超级链接
定义环境变量
在vue3.x 项目中需要设置两个变量,以更好地进行 tree-shaking
修改webpack.common.js
...
const webpack = require('webpack')
...
module.exports = {
...
plugins: [
...
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
})
],
...
}
配置打包规范
打包时拷贝public文件
安装依赖:
npm i copy-webpack-plugin -D
配置webpack.common.js
,打包时直接将public
下的文件拷贝到dist
目录 :
...
const CopyPlugin = require('copy-webpack-plugin')
...
module.exports = {
...
plugins: [
...
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, '../public'),
to: path.resolve(__dirname, '../dist'),
filter: (resourcePath) => {
if (resourcePath.includes('/public/index.html')) {
return false
}
return true
}
}
]
})
],
...
}
ESLint代码规范
安装依赖:
npm i eslint eslint-webpack-plugin @babel/eslint-parser -D
npm i eslint-config-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-n -D
npm i eslint-plugin-vue -D
执行eslint:
npx eslint --init
执行命令后,根据提示选择好配置项。随后项目根目录下会生成配置文件.eslintrc.js
,修改文件:
module.exports = {
root: true,
env: {
browser: true,
es2021: true
},
extends: [
'plugin:vue/vue3-essential',
'standard'
],
parserOptions: {
parser: '@babel/eslint-parser'
},
plugins: [
'vue'
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'vue/multi-word-component-names': 0
}
}
在根目录下新建.eslintconfig
和 .eslintignore
文件
-
.editorconfig
[*.{js,jsx,ts,tsx,vue}] indent_style = space indent_size = 2 trim_trailing_whitespace = true insert_final_newline = true
-
.eslintignore
/build/ /dist/
webpack 中配置 eslint 插件
webpack.common.js 中:
...
const ESLintPlugin = require('eslint-webpack-plugin')
...
module.exports = {
...
plugins: [
...
new ESLintPlugin({
extensions: ['js', 'jsx', 'ts', 'tsx', 'vue']
})
],
...
}
打包前清除上一次打包内容
配置webpack.common.js
:
...
module.exports = {
...
output: {
...
clean: true
}
}
SourceMap
该配置一般只在开发环境中需要,所以在webpack.dev.js
中配置devtool
:
...
const devConfig = {
mode,
devtool: 'eval-cheap-module-source-map',
...
}
...
打包分析
webpack打包结果分析依赖安装:npm i webpack-bundle-analyzer -D
在package.json
中配置指令:
{
...
"scripts": {
...
"analyze": "webpack --progress --analyze --node-env production --config ./build/webpack.prod.js"
},
...
}
关闭一些提示
在生产环境打包时,可能会出现一些大文件的性能提示,可以通过配置关闭:
修改webpack.prod.js
:
...
const prodConfig = {
...
performance: false,
output: {
...
}
}
...
其他一些配置
git提交时,需要忽略一些文件,使之不被commit。配置:在跟目录下新建文件.gitignore
:
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
写在最后
以上操作是webpack5搭建vue项目环境全过程了,在实际操作的过程中再次熟悉webpack的配置。
本文参考: