webpack+vue3搭建项目

  1. 一、开发步骤
    1. 1.1 安装依赖
    2. 1.2 webpack.config.js
    3. 1.3 .eslintrc.js
    4. 1.4 babel.config.js
    5. 1.5 package.json
  2. 二、注意事项
    1. 2.1 环境依赖
    2. 2.2 部署静态资源
    3. 2.3 相关概念

一、开发步骤

1.1 安装依赖

# 初始化项目
npm init -y
# webpack相关
npm install webpack webpack-cli webpack-dev-server -D
# 相关插件
npm install html-webpack-plugin copy-webpack-plugin cross-env -D
# css相关
npm install css-loader style-loader less-loader sass-loader sass stylus-loader -D
npm install postcss postcss-loader postcss-preset-env -D
npm install mini-css-extract-plugin -D
# eslint相关 @babel/eslint-parser: 解析最新js语法如(箭头函数,异步函数等)eslint版本不同,配置不同
npm install eslint@7.32.0 eslint-plugin-vue@7.20.0 @babel/eslint-parser@7.27.5 -D 
# vue相关
npm install vue vue-router pinia --save
npm install vue-loader@next vue-style-loader @vue/compiler-sfc -D
# 压缩插件
npm install css-minimizer-webpack-plugin -D
npm install terser-webpack-plugin -D
npm i image-minimizer-webpack-plugin@3.8.3 imagemin@8.0.1 imagemin-mozjpeg@9.0.0 imagemin-pngquant@9.0.0 imagemin-gifsicle@7.0.0 imagemin-svgo@10.0.1 -D

1.2 webpack.config.js

const path = require('path');
const { VueLoaderPlugin } = require('vue-loader');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const EslintWebpackPlugin = require('eslint-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
const CopyPlugin = require("copy-webpack-plugin");
const { DefinePlugin } = require('webpack');

const isProduction = process.env.NODE_ENV == 'production';

function getStyleLoader(loaderType) {
    return [
        isProduction ? MiniCssExtractPlugin.loader : 'vue-style-loader',
        'css-loader',
        // 处理css兼容性问题,配合package.json中的browserslist使用
        {
            loader: 'postcss-loader',
            options: {
                postcssOptions: {
                    plugins: [
                        "postcss-preset-env"
                    ]
                }
            }
        },
        loaderType
    ].filter(Boolean)
}
module.exports = {
    // 1.入口文件
    entry: './src/main.js',
    // 2.输出文件
    output: {
        path: isProduction ? path.resolve(__dirname, '../dist') : undefined, 
        filename: isProduction ? 'static/js/[name].[contenthash:10].js' : 'static/js/[name].js', // 输出文件目录
        chunkFilename: isProduction ? 'static/js/[name].[contenthash:10].chunk.js' : 'static/js/[name].chunk.js', // 通过import动态导入,或npm安装到node_module中使用的,打包成单独文件
        assetModuleFilename: 'static/media/[hash:10][ext][query]', // 图片视频等资源
        clean: true
    },
    // 3.loader加载器(处理非JS资源,如:vue文件,css/less/scss,图片,字体,js-> babel转译)
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            // 处理css
            {
                test: /\.css$/i,
                use: getStyleLoader()
            },
            // 处理less
            {
                test: /\.less$/i,
                use: getStyleLoader('less-loader')
            },
            // 处理scss
            {
                test: /\.s[ac]ss$/i,
                use: getStyleLoader('sass-loader')
            },
            // 处理stylus
            {
                test: /\.styl$/i,
                use: getStyleLoader('stylus-loader')
            },
            // 图片等资源
            {
                test: /\.(png|jpe?g|gif|webp|svg)/,
                type: 'asset',
                parser: {
                    dataUrlCondition: {
                        // 小于10kb的图片转base64
                        // 优点:减少请求数量。缺点:体积会更大
                        maxSize: 10 * 1024 // 10kb
                    }
                }
            },
            // 字体视频等
            {
                test: /\.(ttf|woff2?|mp3|mp4|avi)$/i,
                type: 'asset/resource', // 原生不动输出
            },
            // 处理js文件
            {
                test: /\.js$/,
                exclude: /node_modules/, // 排除node_module中的js
                loader: 'babel-loader',
                options: {
                    cacheDirectory: true, // 开启babel缓存
                    cacheCompression: false, // 关闭缓存压缩
                },
            },
        ]
    },
    // 4.插件(拓展功能:生成html,eslint,提取css单独文件)
    plugins: [
        new VueLoaderPlugin(),
        // 生成HTML文件并注入资源
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, '../public/index.html')
        }),
        new EslintWebpackPlugin({
            context: path.resolve(__dirname, '../src'),
            exclude: 'node_modules',
            cache: true, // 开启缓存
            cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'),
        }),
        // 明确设置 Vue 3 特性标志
        new DefinePlugin({
            __VUE_OPTIONS_API__: true,
            __VUE_PROD_DEVTOOLS__: false,
            __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false
        }),
        isProduction && new CopyPlugin({
             patterns: [
                {
                    from: path.resolve(__dirname, '../public'),
                    to: path.resolve(__dirname, '../dist'),
                    globOptions: {
                        ignore: ['**/index.html'], // 忽略index.html文件
                    }
                }
             ]
        }),
        // 提取css到单独文件
        isProduction && new MiniCssExtractPlugin({
            filename: "static/css/[name].[contenthash:10].css",
            chunkFilename: 'static/css/[name].[contenthash:10].chunk.css', // 动态导入css的命名
        }),
    ].filter(Boolean),
    // 5.模式
    mode: isProduction ? 'production' : 'development',
    // 优化
    optimization: { 
        // 是否压缩
        minimize: isProduction,
        minimizer: [
            new CssMinimizerPlugin(),
            // 默认的Terser配置已足够,除非需要特殊处理(多线程,删除console)
            new TerserWebpackPlugin({
                terserOptions: {
                    compress: {
                        defaults: true, // 启用基础压缩
                        drop_console: true, // 删除console
                    },
                }
            }),
            new ImageMinimizerPlugin({
                minimizer: {
                    implementation: ImageMinimizerPlugin.imageminMinify,
                    options: {
                        plugins: [
                            // JPEG 压缩(有损)
                            ['imagemin-mozjpeg', {
                                quality: 85,
                                progressive: true
                            }],
                            // PNG 压缩(有损)
                            ['imagemin-pngquant', {
                                quality: [0.65, 0.9],
                                speed: 4
                            }],
                            // GIF 压缩
                            ['imagemin-gifsicle', {
                                interlaced: true
                            }],
                            // SVG 压缩
                            ['imagemin-svgo', {
                                plugins: [
                                    { name: 'removeViewBox', active: false },
                                    { name: 'cleanupIDs', active: false }
                                ]
                            }]
                        ]
                    }
                },
                severityError: 'warning' // 错误降级为警告
            })
        ],
        splitChunks: {
            chunks: 'all', // 对所有模块进行分割
        },
        runtimeChunk: { name: (entry) => `runtime-${entry.name}.js` }
    },
    // 开发服务器
    devServer: {
        host: 'localhost', // 域名
        port: '8000',   // 端口号
        open: true, // 是否自动打开浏览器
        historyApiFallback: true, // 将所有404请求重定向到入口文件(通常是 index.html),让前端路由接管URL解析
    },
    // 解析器
    resolve: {
        extensions: ['.vue', '.js', '.json'], // 添加拓展名,默认['.js', '.json', '.wasm']
        alias: {
            '@': path.resolve(__dirname, 'src'), // 别名
        }
    },
    // 源代码与构建代码的映射-提升开发体验
    devtool: isProduction ? 'source-map' : 'cheap-module-source-map',
}

1.3 .eslintrc.js

module.exports = {
    root: true,
    env: {
        node: true, // 启动node中全局变量(console)
    },
    extends: ['plugin:vue/vue3-recommended', 'eslint:recommended'],
    // 语法环境
    parserOptions: {
        parser: '@babel/eslint-parser',
    },
    rules: {
        "no-var": 2, // 禁止使用var定义变量
    },
}

1.4 babel.config.js

module.exports = {
    presets: ['@vue/cli-plugin-babel/preset']
}
// 内部等效配置(简化版)
// module.exports = {
//   presets: [
//     [
//       '@babel/preset-env',
//       {
//         useBuiltIns: 'usage', // 按需加载 polyfill
//         corejs: 3,
//         targets: '> 0.5%, last 2 versions, not dead'
//       }
//     ]
//   ],
//   plugins: [
//     '@babel/plugin-transform-runtime',
//     // Vue 特定的 JSX 支持
//     '@vue/babel-plugin-jsx'
//   ]
// }

1.5 package.json

{
  "name": "webpack-vue3-cli",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "npm run dev",
    "dev": "cross-env NODE_ENV=development webpack serve --config config/webpack.config.js",
    "build": "cross-env NODE_ENV=production webpack --config config/webpack.config.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "devDependencies": {
    "@babel/eslint-parser": "^7.27.5",
    "@vue/cli-plugin-babel": "^5.0.8",
    "@vue/compiler-sfc": "^3.5.17",
    "copy-webpack-plugin": "^13.0.0",
    "cross-env": "^7.0.3",
    "css-loader": "^7.1.2",
    "css-minimizer-webpack-plugin": "^7.0.2",
    "eslint": "^7.32.0",
    "eslint-plugin-vue": "^7.20.0",
    "eslint-webpack-plugin": "^3.1.1",
    "html-webpack-plugin": "^5.6.3",
    "image-minimizer-webpack-plugin": "^3.8.3",
    "imagemin": "^8.0.1",
    "imagemin-gifsicle": "^7.0.0",
    "imagemin-mozjpeg": "^9.0.0",
    "imagemin-pngquant": "^9.0.0",
    "imagemin-svgo": "^10.0.1",
    "less-loader": "^12.3.0",
    "mini-css-extract-plugin": "^2.9.2",
    "postcss": "^8.5.6",
    "postcss-loader": "^8.1.1",
    "postcss-preset-env": "^10.2.3",
    "sass": "^1.89.2",
    "sass-loader": "^16.0.5",
    "style-loader": "^4.0.0",
    "stylus-loader": "^8.1.1",
    "terser-webpack-plugin": "^5.3.14",
    "vue-loader": "^17.3.1",
    "vue-style-loader": "^4.1.3",
    "webpack": "^5.99.9",
    "webpack-cli": "^6.0.1",
    "webpack-dev-server": "^5.2.2"
  },
  "browserslist": [
    "last 2 version",
    "> 1%",
    "not dead"
  ],
  "dependencies": {
    "vue": "^3.5.17"
  }
}

二、注意事项

2.1 环境依赖

  • 生产环境依赖(dependencies)
    • 定义:项目运行时必须的包
    • 安装命令:npm install <package> –save
    • 示例:
      • vue相关(vue,vue-router,pinia,axios,element-ui等)
      • 工具库(lodash-es, dayjs)
  • 开发环境依赖(devDependencies)
    • 定义:仅在开发/构建阶段需要
    • 安装命令:npm install <package> –save-dev | npm install <package> -D
    • 示例:
      • 构建工具(webpack,webpack-cli,webpack-dev-server)
      • Loaders(vue-loader, babel-loader, css-loader)
      • CSS 预处理器(sass,less,postcss)
      • 插件类(html-webpack-plugin, mini-css-extract-plugin)
  • 模拟生产环境
rm -rf node_modules package-lock.json
npm install --production

2.2 部署静态资源

# 查看全局安装
npm list -g --depth=0
# 安装serve
npm install serve -g
# 启动
serve dist

2.3 相关概念

  • css相关
    • scss-loader: 将Sass编译成CSS
    • css-loader: 将CSS转化成CommonJS模块
    • style-loader: 将JS字符串生成为style节点
    • vue-style-loader:原生支持 Vue 特性。浏览器中打开开发者工具,查看 <head> 中的 <style> 标签与style-loader不同
    • postcss-loader: 解决样式兼容性问题如: display: flex;
  • 插件相关
    • html-webpack-plugin: 生成HTML文件并注入资源
    • mini-css-extract-plugin: 将css抽离成单独模块
    • css-minimizer-webpack-plugin: css压缩
    • terser-webpack-plugin: js压缩(默认使用,额外配置需添加)
    • image-minimizer-webpack-plugin: 图片压缩
    • eslint-webpack-plugin: 将ESLint嵌入Webpack
    • eslint-plugin-vue: Vue专属规则(模板语法、SFC 结构)
  • @babel/eslint-parser: 处理最新的js特性(箭头函数、异步函数、模块化)
  • cross-env 强制锁定环境,避免玄学问题
×

喜欢就点赞,疼爱就打赏