webpack-vuecli配置

127 阅读4分钟

一、vue开发环境配置

效果:

动图.gif

1、目录结构

image.png

2、开始搭建

npm init -y生成package.json:

{
  "name": "vue-cli",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "npm run dev",
    "dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/eslint-parser": "^7.17.0",
    "@vue/cli-plugin-babel": "^5.0.4",
    "babel-loader": "^8.2.5",
    "cross-env": "^7.0.3",
    "css-loader": "^6.7.1",
    "eslint": "^8.42.0",
    "eslint-plugin-vue": "^8.7.1",
    "eslint-webpack-plugin": "^3.1.1",
    "html-webpack-plugin": "^5.5.1",
    "less-loader": "^10.2.0",
    "postcss-loader": "^6.2.1",
    "postcss-preset-env": "^7.5.0",
    "sass": "^1.62.1",
    "sass-loader": "^13.3.1",
    "vue-loader": "^17.2.2",
    "vue-style-loader": "^4.1.3",
    "vue-template-compiler": "^2.7.14",
    "webpack": "^5.72.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.9.0"
  },
  "dependencies": {
    "vue": "^3.3.4",
    "vue-router": "^4.2.2"
  }
}

babel.config.js

module.exports = {
  presets: ['@vue/cli-plugin-babel/preset']
};

.eslintrc.js

module.exports = {
  root: true,
  env: { node: true },
  extends: ['plugin:vue/vue3-essential', 'eslint:recommended'],
  parserOptions: { parser: '@babel/eslint-parser' }
};

pages/Home.vue

<template>
  <h1 class="title">Home</h1>
</template>
<style lang='less' scoped>
.title {
  color: blue;
}
</style>

pages/About.vue

<template>
  <h1 class="title">About</h1>
</template>

App.vue

<template>
  <div>
    <h1>App</h1>
    <ul>
      <li><router-link to='/home'>Home</router-link></li>
      <li><router-link to='/about'>About</router-link></li>
    </ul>
    <router-view></router-view>
  </div>
</template>

router.js

import { createRouter, createWebHistory } from 'vue-router';

const Home = () => import(/* webpackChunkName: 'home' */ './pages/Home');
const About = () => import('./pages/About');

const routers = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/home', component: Home },
    { path: '/about', component: About }
  ]
});

export default routers;

main.js

import { createApp } from 'vue';
import App from './App';
import router from './router';

createApp(App).use(router).mount(document.querySelector('#app'));

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" />
    <title>vue cli</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

config/webpack.dev.js

const path = require('path');
const { DefinePlugin } = require('webpack');
const EslintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');

const getStyleLoaders = loaderName => {
  const loaders = [
    'vue-style-loader',
    'css-loader',
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: { plugins: ['postcss-preset-env'] }
      }
    },
    loaderName
  ];
  return loaders.filter(Boolean);
};

module.exports = {
  entry: './src/main.js',
  output: {
    path: undefined,
    filename: 'static/js/[name].js',
    chunkFilename: 'static/js/[name].chunk.js',
    assetModuleFilename: 'static/media/[hash:10][ext][query]'
  },
  module: {
    rules: [
      { test: /\.css$/, use: getStyleLoaders() },
      { test: /\.less$/, use: getStyleLoaders('less-loader') },
      { test: /\.s[ac]ss$/, use: getStyleLoaders('sass-loader') },
      {
        test: /\.(jpe?g|png|gif|webp|svg)$/,
        type: 'asset',
        parser: { dataUrlCondition: { maxSize: 10 * 1024 } }
      },
      { test: /\.(woff2?|ttf)$/, type: 'asset/resource' },
      {
        test: /\.js$/,
        include: path.resolve(__dirname, '../src'),
        loader: 'babel-loader',
        options: { cacheDirectory: true, cacheCompression: false }
      },
      { test: /\.vue$/, loader: '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')
    }),
    new VueLoaderPlugin(),
    // DefinePlugin定义环境变量给源代码使用,从而解决vue3页面警告的问题
    new DefinePlugin({
      __VUE_OPTIONS_API__: true,
      __VUE_PROD_DEVTOOLS__: false
    })
  ],
  mode: 'development',
  devtool: 'cheap-module-source-map',
  optimization: {
    splitChunks: { chunks: 'all' },
    runtimeChunk: { name: entrypoint => `runtime~${entrypoint}.js` }
  },
  resolve: { extensions: ['.vue', '.js', '.json'] },
  devServer: {
    host: 'localhost',
    port: 3005,
    // open: true,
    // hot: true,
    historyApiFallback: true // 解决前端路由刷新404问题
  }
};

二、vue生产环境配置

安装包(node版本:16.14.0)

cnpm i mini-css-extract-plugin css-minimizer-webpack-plugin -D // 提取css、css压缩
cnpm i image-minimizer-webpack-plugin imagemin imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D // 图片无损压缩
cnpm i copy-webpack-plugin -D // 将plugin目录下的资源拷贝到dist目录中

添加指令

    "prod": "cross-env NODE_ENV=production webpack --config ./config/webpack.prod.js"

public

  • 添加favicon.ico
  • 引入ico:
    <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />

webpack.prod.js

const path = require('path');
const { DefinePlugin } = require('webpack');
const EslintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerWebpackPlugin = 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 { loader: miniCssLoader } = MiniCssExtractPlugin;

const getStyleLoaders = loaderName => {
  const loaders = [
    miniCssLoader,
    'css-loader',
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: { plugins: ['postcss-preset-env'] }
      }
    },
    loaderName
  ];
  return loaders.filter(Boolean);
};

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/media/[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: /\.(jpe?g|png|gif|webp|svg)$/,
        type: 'asset',
        parser: { dataUrlCondition: { maxSize: 10 * 1024 } }
      },
      { test: /\.(woff2?|ttf)$/, type: 'asset/resource' },
      {
        test: /\.js$/,
        include: path.resolve(__dirname, '../src'),
        loader: 'babel-loader',
        options: { cacheDirectory: true, cacheCompression: false }
      },
      { test: /\.vue$/, loader: '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')
    }),
    new MiniCssExtractPlugin({
      filename: 'static/css/[name].[contenthash:10].css',
      chunkFilename: 'static/css/[name].[contenthash:10].chunk.css'
    }),
    new CopyPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, '../public'),
          to: path.resolve(__dirname, '../dist'),
          globOptions: { ignore: ['**/index.html'] } // 忽略index.html文件
        }
      ]
    }),
    new VueLoaderPlugin(),
    // DefinePlugin定义环境变量给源代码使用,从而解决vue3页面警告的问题
    new DefinePlugin({
      __VUE_OPTIONS_API__: true,
      __VUE_PROD_DEVTOOLS__: false
    })
  ],
  mode: 'production',
  devtool: 'source-map',
  optimization: {
    splitChunks: { chunks: 'all' },
    runtimeChunk: { name: entrypoint => `runtime~${entrypoint}.js` },
    minimizer: [
      new CssMinimizerWebpackPlugin(),
      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' }
                    }
                  ]
                }
              ]
            ]
          }
        }
      })
    ]
  },
  resolve: { extensions: ['.vue', '.js', '.json'] }
};

三、合并配置

webpack.config.js

const path = require('path');
const { DefinePlugin } = require('webpack');
const EslintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerWebpackPlugin = 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 { loader: miniCssLoader } = MiniCssExtractPlugin;

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

const getStyleLoaders = loaderName => {
  const loaders = [
    isProduction ? miniCssLoader : 'vue-style-loader',
    'css-loader',
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: { plugins: ['postcss-preset-env'] }
      }
    },
    loaderName
  ];
  return loaders.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/media/[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: /\.(jpe?g|png|gif|webp|svg)$/,
        type: 'asset',
        parser: { dataUrlCondition: { maxSize: 10 * 1024 } }
      },
      { test: /\.(woff2?|ttf)$/, type: 'asset/resource' },
      {
        test: /\.js$/,
        include: path.resolve(__dirname, '../src'),
        loader: 'babel-loader',
        options: { cacheDirectory: true, cacheCompression: false }
      },
      { test: /\.vue$/, loader: '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: [
          {
            from: path.resolve(__dirname, '../public'),
            to: path.resolve(__dirname, '../dist'),
            globOptions: { ignore: ['**/index.html'] } // 忽略index.html文件
          }
        ]
      }),
    new VueLoaderPlugin(),
    // DefinePlugin定义环境变量给源代码使用,从而解决vue3页面警告的问题
    new DefinePlugin({
      __VUE_OPTIONS_API__: true,
      __VUE_PROD_DEVTOOLS__: false
    })
  ].filter(Boolean),
  mode: isProduction ? 'production' : 'development',
  devtool: isProduction ? 'source-map' : 'cheap-module-source-map',
  optimization: {
    splitChunks: { chunks: 'all' },
    runtimeChunk: { name: entrypoint => `runtime~${entrypoint.name}.js` },
    minimize: isProduction,
    minimizer: [
      new CssMinimizerWebpackPlugin(),
      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' }
                    }
                  ]
                }
              ]
            ]
          }
        }
      })
    ]
  },
  resolve: { extensions: ['.vue', '.js', '.json'] },
  devServer: {
    host: 'localhost',
    port: 3005,
    // open: true,
    // hot: true,
    historyApiFallback: true
  },
  performance: false
};

package.json

    "dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.config.js",
    "prod": "cross-env NODE_ENV=production webpack --config ./config/webpack.config.js"

四、优化配置

1、按需引入element-plus

下载包

cnpm i element-plus
cnpm install -D unplugin-vue-components unplugin-auto-import

webpack.config.js

// 按需引入element-plus
const AutoImport = require('unplugin-auto-import/webpack');
const Components = require('unplugin-vue-components/webpack');
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers');


    AutoImport({ resolvers: [ElementPlusResolver()] }),
    Components({ resolvers: [ElementPlusResolver()] })

App.vue

    <el-button type='primary'>按钮</el-button>

2、element-plus自定义主题色

styles/element/index.scss

@forward 'element-plus/theme-chalk/src/common/var.scss' with (
  $colors: (
    'primary': (
      'base': green
    )
  )
);

webpack.config.js

    loaderName && {
      loader: loaderName,
      options:
        loaderName === 'sass-loader'
          ? {
              additionalData: `@use "@/styles/element/index.scss" as *;`
            }
          : {}
    }
    
    
    Components({
      resolvers: [ElementPlusResolver({ importStyle: 'sass' /* 自定义主题 */ })]
    })
    
    
  resolve: {
    alias: { '@': path.resolve(__dirname, '../src') }
  }

3、开启打包缓存

      {
        test: /\.vue$/,
        loader: 'vue-loader',
        // 开启缓存
        options: {
          cacheDirectory: path.resolve(
            __dirname,
            '../node_modules/.cache/vue-loader'
          )
        }
      }

4、将vue、element-plus模块打包成单独js文件

    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'
        }
      }
    }