项目 webpack

1、vue项目所需配置

vue-template-compiler 需要独立安装的原因是你可以单独指定版本。

每个 vue 包的新版本发布时,一个相应的 vue-template-compiler 也会随之发布。编译器的版本必须和基本的 vue 包保持同步,这样  vue-loader 就会生成兼容运行时的代码。这意味着你每次升级项目的 vue 包时,也应该匹配升级 vue-template-compiler 。

npm install -D vue-loader vue-template-compiler
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
  mode: 'development',
  module: {
    rules: [
      // 处理 vue 的单文件组件
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      // 它会应用到普通的 `.js` 文件
      // 以及 `.vue` 文件中的 `<script>` 块
      {
        test: /\.js$/,
        loader: 'babel-loader'
      },
      // 它会应用到普通的 `.css` 文件
      // 以及 `.vue` 文件中的 `<style>` 块
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    // 请确保引入这个插件来施展魔法
    new VueLoaderPlugin()
  ]
}

2、element-plus 配置

2.1、完整引入

  • main.js
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'

const app = createApp(App)

app.use(ElementPlus)
app.mount('#app')

2.2、按需自动导入

npm install -D unplugin-vue-components@0.19.3 unplugin-auto-import@0.7.1
  • webpack.config.js
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

module.exports = {
  // ...
  plugins: [
    // unplugin-auto-import@0.7.1
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    // unplugin-vue-components@0.19.3
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
}
  • App.vue
<template>
  <el-button>I am ElButton</el-button>
</template>
<script>
  import { ElButton } from 'element-plus'
  export default {
    components: { ElButton },
  }
</script>

3、CopyPlugin

CopyPlugin的作用是,帮助开发人员在构建过程中将静态文件(如图片、字体等)直接复制到输出目录,而无需经过任何处理。

  • webpack.prod.js
const path = require("path");
const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
    entry: "./src/main.js",
    output: {
        path: path.resolve(__dirname, '../dist'),
        filename: "static/js/[name].[contenthash:10].js",
        chunkFilename: "static/js/[name].[contenthash:10].chunk.js",
        assetModuleFilename: "static/js/[hash:10][ext][query]",
        clean: true,
    },
    module: {
        rules: [],
    },
    plugins: [
        // 帮助开发人员在构建过程中将静态文件(如图片、字体等)直接复制到输出目录,而无需经过任何处理。
        new CopyPlugin({
            patterns: [
                {
                    // 将 ../public 下的文件复制到构建目录的 ../dist 文件夹下(favicon.ico文件)
                    from: path.resolve(__dirname, "../public"),
                    to: path.resolve(__dirname, "../dist"),
                    globOptions: {
                        // 忽略index.html文件
                        ignore: ["**/index.html"],
                    },
                },
            ],
        }),
    ],
    mode: "production",
    devtool: "source-map",
};

4、完整代码

  • config/webpack.config.js
const path = require("path");
const EslintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-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 { VueLoaderPlugin } = require("vue-loader");
const { DefinePlugin } = require("webpack");
const AutoImport = require("unplugin-auto-import/webpack");
const Components = require("unplugin-vue-components/webpack");
const { ElementPlusResolver } = require("unplugin-vue-components/resolvers");

const isProduction = process.env.NODE_ENV === "production"

function getStyleLoaders(pre) {
    return [
        // 提取css成单独文件
        isProduction ? MiniCssExtractPlugin.loader : "vue-style-loader",
        "css-loader",
        {
            // 处理css兼容性问题
            // 配合package.json中browserslist来指定兼容性
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: ["postcss-preset-env"]
                },
            },
        },
        pre && {
            loader: pre,
            options:
                pre === "sass-loader"
                    ? {
                        // 自定义主题:自动引入我们定义的scss文件
                        additionalData: `@use "@/styles/element/index.scss" as *;`,
                    }
                    : {},
        },
    ].filter(Boolean);
}

module.exports = {
    entry: "./src/main.js",
    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",
        assetModuleFilename: "static/js/[hash:10][ext][query]",
        clean: true,
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: getStyleLoaders(),
            },
            {
                test: /\.less$/,
                use: getStyleLoaders("less-loader"),
            },
            {
                test: /\.s[ac]ss$/,
                use: getStyleLoaders("sass-loader"),
            },
            {
                test: /\.styl$/,
                use: getStyleLoaders("stylus-loader"),
            },
            {
                test: /\.(png|jpe?g|gif|svg)$/,
                type: "asset",
                parser: {
                    dataUrlCondition: {
                        maxSize: 10 * 1024,
                    },
                },
            },
            {
                test: /\.(woff2?|ttf)$/,
                type: "asset/resource",
            },
            {
                test: /\.js$/,
                include: path.resolve(__dirname, "../src"),
                // 将 ES6 语法编写的代码转换为向后兼容的Javascript语法
                loader: "babel-loader",
                options: {
                    cacheDirectory: true,
                    cacheCompression: false,
                    plugins: [
                        // '@babel/plugin-transform-runtime' // presets 智能预设中包含了
                    ],
                },
            },
            // vue-loader 不支持 oneOf
            {
                test: /\.vue$/,
                loader: "vue-loader", // 内部会给vue文件注入 HMR 功能
                options: {
                    // 开启缓存
                    cacheDirectory: path.resolve(
                        __dirname,
                        "node_modules/.cache/vue-loader"
                    ),
                },
            },
        ],
    },
    plugins: [
        new EslintWebpackPlugin({
            context: path.resolve(__dirname, "../src"),
            exclude: "node_modules",
            cache: true,
            cacheLocation: path.resolve(
                __dirname,
                "../node_modules/.cache/.eslintcache"
            ),
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "../public/index.html"),
        }),
        isProduction && new MiniCssExtractPlugin({
            filename: "static/css/[name].[contenthash:10].css",
            chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
        }),
        // 帮助开发人员在构建过程中将静态文件(如图片、字体等)直接复制到输出目录,而无需经过任何处理。
        isProduction && new CopyPlugin({
            patterns: [
                {
                    // 将 ../public 下的文件复制到构建目录的 ../dist 文件夹下(favicon.ico文件)
                    from: path.resolve(__dirname, "../public"),
                    to: path.resolve(__dirname, "../dist"),
                    globOptions: {
                        // 忽略index.html文件
                        ignore: ["**/index.html"],
                    },
                },
            ],
        }),
        new VueLoaderPlugin(),
        // cross-env定义的环境变量给打包工具使用
        // DefinePlugin定义环境变量给源代码使用,从而解决vue3页面警告的问题F
        new DefinePlugin({
            __VUE_OPTIONS_API__: "true",
            __VUE_PROD_DEVTOOLD__: "false",
        }),
        // 按需自动导入 Element-Plus 组件
        AutoImport({
            resolvers: [ElementPlusResolver()],
        }),
        Components({
            resolvers: [ElementPlusResolver({
                importStyle: "sass", // 自定义主题
            })],
        }),
    ].filter(Boolean),
    optimization: {
        // 压缩
        minimizer: [
            // 压缩css文件
            new CssMinimizerPlugin(),
            new TerserWebpackPlugin(),
            // 压缩图片资源
            new ImageMinimizerPlugin({
                minimizer: {
                    implementation: ImageMinimizerPlugin.imageminGenerate,
                    options: {
                        plugins: [
                            ["gifsicle", { interlaced: true }],
                            ["jpegtran", { progressive: true }],
                            ["optipng", { optimizationLevel: 5 }],
                            [
                                "svgo",
                                {
                                    plugins: [
                                        "preset-default",
                                        "prefixIds",
                                        {
                                            name: "sortAttrs",
                                            params: {
                                                xmlnsOrder: "alphabetical",
                                            },
                                        },
                                    ],
                                },
                            ],
                        ],
                    },
                },
            }),
        ],
        splitChunks: {
            chunks: "all",
            cacheGroups: {
                // layouts通常是admin项目的主体布局组件,所有路由组件都要使用的
                // 可以单独打包,从而复用
                // 如果项目中没有,请删除
                layouts: {
                    name: "layouts",
                    test: path.resolve(__dirname, "../src/layouts"),
                    priority: 40,
                },
                // 如果项目中使用element-plus,此时将所有node_modules打包在一起,那么打包输出文件会比较大。
                // 所以我们将node_modules中比较大的模块单独打包,从而并行加载速度更好
                // 如果项目中没有,请删除
                elementUI: {
                    name: "chunk-elementPlus",
                    test: /[\\/]node_modules[\\/]_?element-plus(.*)/,
                    priority: 30,
                },
                // 将vue相关的库单独打包,减少node_modules的chunk体积。
                vue: {
                    name: "vue",
                    test: /[\\/]node_modules[\\/]vue(.*)[\\/]/,
                    chunks: "initial",
                    priority: 20,
                },
                libs: {
                    name: "chunk-libs",
                    test: /[\\/]node_modules[\\/]/,
                    priority: 10, // 权重最低,优先考虑前面内容
                    chunks: "initial",
                },
            },
        },
        runtimeChunk: {
            name: (entrypoint) => `runtime~${entrypoint.name}`,
        },
    },
    // webpack解析模块加载选项
    resolve: {
        // 自动补全文件扩展名
        extensions: [".vue", ".js", ".json"],
        alias: {
            // 路径别名
            "@": path.resolve(__dirname, "../src"),
        },
    },
    devServer: {
        host: "localhost",
        port: 3000,
        open: true,
        hot: true, // 开启HMR
        historyApiFallback: true, // 解决前端路由刷新404问题
    },
    mode: isProduction ? "production" : "development",
    devtool: isProduction ? "source-map" : "cheap-module-source-map",
    performance: false,
};
  • package.json
{
  "name": "vue-cli",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "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.25.1",
    "@vue/cli-plugin-babel": "^5.0.8",
    "babel-loader": "^9.1.3",
    "copy-webpack-plugin": "^12.0.2",
    "cross-env": "^7.0.3",
    "css-loader": "^7.1.2",
    "css-minimizer-webpack-plugin": "^7.0.0",
    "eslint-plugin-vue": "^9.27.0",
    "eslint-webpack-plugin": "^4.2.0",
    "html-webpack-plugin": "^5.6.0",
    "image-minimizer-webpack-plugin": "^4.1.0",
    "less-loader": "^12.2.0",
    "mini-css-extract-plugin": "^2.9.0",
    "postcss-loader": "^8.1.1",
    "postcss-preset-env": "^10.0.0",
    "sass": "^1.77.8",
    "sass-loader": "^16.0.0",
    "style-loader": "^4.0.0",
    "stylus-loader": "^8.1.0",
    "terser-webpack-plugin": "^5.3.10",
    "unplugin-auto-import": "^0.7.1",
    "unplugin-vue-components": "^0.19.3",
    "vue-loader": "^17.4.2",
    "vue-style-loader": "^4.1.3",
    "webpack": "^5.93.0",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^5.0.4"
  },
  "dependencies": {
    "element-plus": "^2.7.8",
    "vue": "^3.4.35",
    "vue-router": "^4.4.2"
  },
  "browserslist": [
    "last 2 version",
    "> 1%",
    "not dead"
  ]
}
  • .eslintrc.js
module.exports = {
  root: true,
  env: {
    node: true,
  },
  extends: ["plugin:vue/vue3-essential", "eslint:recommended"],
  parserOptions: {
    parser: "@babel/eslint-parser",
  },
};
  • babel.config.js
module.exports = {
  presets: ["@vue/cli-plugin-babel/preset"],
};
  • src/styles/element/index.scss
@forward "element-plus/theme-chalk/src/common/var.scss" with (
  $colors: (
    "primary": (
      "base": green,
    ),
  )
);
  • src/router/index.js
import { createRouter, createWebHistory } from "vue-router";

const Home = () => import('../view/Home/index.vue')
const About = () => import('../view/About/index.vue')

export default createRouter({
    history: createWebHistory(),
    routes: [
        {
            path: '/home',
            component: Home
        },
        {
            path: '/about',
            component: About
        }
    ]
})
  • public/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="shortcut icon" href="favicon.ico" type="image/x-icon">
  <title>Vue Cli</title>
</head>
<body>
  <div id="app"></div>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值