Webpack搭建vue-cli

Webpack创建vue-cli

流程:
开发环境:

  1. 创建webpack.config.js,并且创建五大核心模块
  2. 配置入口文件、出口文件(chunkFilename、assetModuleFilename、filename、path)
  3. 编写资源的loader,如:css、less、sass、图片、图标、音频、js(兼容性:babel-loader)

**注意:**在vue-cli中,我们在将样式打包成一个文件的时候,就不能使用style-loader,我们需要使用vue提供给我们的vue-style-loader了解为什么需要vue-style-loader可以参考这篇文章

const getStyleLoader = (loader)=>{
    return [
        "vue-style-loader",
        "css-loader",
        {
            loader: 'postcss-loader',
            options : {
                postcssOptions:{
                    plugins: ["postcss-preset-env"],
                }
            }
        },
        loader
    ].filter(Boolean)
}
  1. 配置plugins。如:html-webpack-plugin来将选择文件的打包页面
  2. 配置babel语法兼容器、eslint语法检查
// eslint
   new EslintWebpackPlugin({
            context: path.resolve(__dirname,"../src"),
            exclude: 'node_modules',
            cache: true,
            cacheLocation: path.resolve(__dirname,'../node_modules/.cache/.eslintcache'),
            // thread: true, 看情况开启多核检查
        }),



// babel
{
                test: /\.js$/,
                include: path.resolve(__dirname,'../src'),
                use:[
                   {
                    loader: "babel-loader",
                    options: {
                        cacheDirectory: true,
                        cacheCompression: false,
                    }
                   }
                ]
            }
  1. 其他配置:devServer、optimization等
optimization: {
        splitChunks:{
            chunks: "all",
        },
        runtimeChunk: {
            name:(entrypoint)=> `runtime~${entrypoint}.js`,
        }
    },
    devServer: {
        host: "localhost",
        port: 3000,
        open: true,
        hot: true,
        
    }

遇到的问题

控制台的警告

在搭建过程中,如果发现控制到有如下警告,那么我们需要使用DefinePlugin创建一个在编译时可以配置的全局变量,因为vue在之后的编译中需要使用到两个变量。
在这里插入图片描述

解决办法:

通过webpack官方提供的插件将两个环境变量暴露出去。
配置:

// 导入
const { DefinePlugin } = require("webpack");

// 使用
module.exports = {
	plugins: [
		new DefinePlugin({
			 __VUE_OPTIONS_API__: true,
            __VUE_PROD_DEVTOOLS__: false
		})
	]
}

解决刷新没有页面

在我们配置好路由之后,如果刷新页面,可能会出现不能获取页面的情况:
在这里插入图片描述
这个时候我们需要在devServer中配置historyApiFallback: true官方地址在这里插入图片描述
配置

  devServer: {
        host: "localhost",
        port: 3001,
        open: true,
        hot: true,
        historyApiFallback: true
    }

配置好之后,我们再次刷新页面就不会出现找不到的情况了。

优化

对于eslint和babel我们可以进行缓存:

// module
{
                test: /\.js$/,
                include: path.resolve(__dirname,'../src'),
                use:[
                    {
                        loader: "babel-loader",
                        options: {
                            cacheDirectory: true,
                            cacheCompression: false,
                        }
                    }
                ]
            },


// plugins
new EslintWebpackPlugin({
            context: path.resolve(__dirname,"../src"),
            exclude: 'node_modules',
            cache: true,
            cacheLocation: path.resolve(__dirname,'../node_modules/.cache/.eslintcache'),
            // thread: true, 看情况开启多核检查
        }),
        

设置自动补全文件后缀:

    // webpack解析模块加载选项
    resolve:{
        // 自动补全的文件扩展名
        extensions: [".vue",".js",".json"],
        alias: {
            "@": path.resolve(__dirname,"../src")
        }
    },

对重复使用的数据块的打包文件进行分割:

optimization: {
        splitChunks:{
            chunks: "all",
        },
        runtimeChunk: {
            name:(entrypoint)=> `runtime~${entrypoint}.js`,
        }
    },

完整代码

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

const getStyleLoader = (loader)=>{
    return [
        "vue-style-loader",
        "css-loader",
        {
            loader: 'postcss-loader',
            options : {
                postcssOptions:{
                    plugins: ["postcss-preset-env"],
                }
            }
        },
        loader
    ].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: [
            // 处理css
            {
                test: /\.css$/,
                use: getStyleLoader()
            },
            {
                test: /\.less$/,
                use: getStyleLoader("less-loader")
            },
            {
                test: /\.s[ac]ss$/,
                use: getStyleLoader("sass-loader")
            },
            // 处理图片
            {
                test: /\.(png|jpe?g|gif|svg|webp)/,
                type: 'asset',
                parser: {
                    dataUrlCondition: {
                        maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
                    }
                }
            },
            // 其他资源
            {
                test: /\.(woff2?|ttf)/,
                type: "asset/resource",
            },
            // 处理js(语法检查)
            {
                test: /\.js$/,
                include: path.resolve(__dirname,'../src'),
                use:[
                    {
                        loader: "babel-loader",
                        options: {
                            cacheDirectory: true,
                            cacheCompression: false,
                        }
                    }
                ]
            },
            // 处理vue
            {
                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'),
            // thread: true, 看情况开启多核检查
        }),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname,'../public/index.html'),

        }),
        new VueLoaderPlugin(),
        // cross-env 定义的环境变量给打包工具使用
        // 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`,
        }
    },
    // webpack解析模块加载选项
    resolve:{
        // 自动补全的文件扩展名
        extensions: [".vue",".js",".json"],
        alias: {
            "@": path.resolve(__dirname,"../src")
        }
    },
    devServer: {
        host: "localhost",
        port: 3001,
        open: true,
        hot: true,
        historyApiFallback: true
    }
}

生产模式

对于生产模式来说,其与开发模式的最大区别的就是,生产模式是会输出打包之后的文件,而开发模式不会输出打包后的文件,生产模式是在内存中打包编译的。对于生产模式来说,我们需要注意的是打包之后文件的体积和打包速度等。
所以在编写生产模式的时候,我们需要使用到压缩文件的插件等。

完整代码

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

const getStyleLoader = (loader) => {
    return [
        MiniCssExtractPlugin.loader,
        "css-loader",
        // 配置css样式兼容,从下往上加载
        {
            loader: 'postcss-loader',
            options: {
                postcssOptions: {
                    plugins: ["postcss-preset-env"]
                },
            }
        },
        loader
    ].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].chunk.js',
        assetModuleFilename: 'static/media/[hash:10][ext][query]',
        clean: true,
    },
    module: {
        rules: [{
                test: /\.css$/,
                use: getStyleLoader(),
            },
            {
                test: /\.less$/,
                use: getStyleLoader("less-loader"),
            },
            {
                test: /\.s[ac]ss$/,
                use: getStyleLoader("sass-loader"),
            },
            {
                test: /\.(jpe?g|gif|svg|png)/,
                type: 'asset',
                parser: {
                    // 配置图片小于10kb就转化成base64格式:可以减少请求次数
                    dataUrlCondition: {
                        maxSize: 10 * 1024,
                    }
                }
            },
            {
                test: /\.(woff2?|ttf)/,
                type: 'asset-resource',
            },
            {
                test: /\.js/,
                include: path.resolve(__dirname, '../src'),
                use: [{
                    loader: 'babel-loader',
                    options: {
                        cacheDirectory: true,
                        cacheCompression: false,
                    }
                }]
            },
            {
                test: /\.vue$/,
                include: path.resolve(__dirname, "../src"),
                loader: 'vue-loader',
                options: {
                    cacheDirectory: path.resolve(__dirname, '../node_modules/.cache/vue-loader'),
                }
            }
        ]
    },
    plugins: [
        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 EslintWebpackPlugin({
            cache: true,
            context: path.resolve(__dirname, '../src'),
            exclude: 'node_modules',
            cacheLocation: path.resolve(__dirname, "../node_modules/.cache/.eslintcache"),
        }),
        
        new DefinePlugin({
            __VUE_OPTIONS_API__: "true",
            __VUE_PROD_DEVTOOLS__: "false",
        }),
        new VueLoaderPlugin(),
        new CopyPlugin({
            patterns: [
                { 
                    from : path.resolve(__dirname,"../public"),
                    to: path.resolve(__dirname,"../dist"),
                    globOptions: {
                        ignore: ["**/index.html"]
                    },
                }
            ]
        }),
    ],
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin(),
            new CssMinimizerPlugin(),
        ],
        splitChunks: {
            chunks: "all",
        },
        runtimeChunk: {
            name: (entrypoint) => `runtime~${entrypoint.name}`,
        },
    },
    resolve: {
        extensions: [".vue",".js",".json"]
    },
    devtool: "source-map",
    mode: 'production',

}

最后我们可以对生产模式和开发模式进行合并,需要使用环境变量来判断当前所处的环境,这就需要使用运行跨平台设置和使用环境变量的脚本cross-env 来设置环境变量。

{
  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
  }
}

合并后代码:

并且结合了element-plus的按需引入

const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const EslintWebpackPlugin = require('eslint-webpack-plugin');
const {
    DefinePlugin
} = require("webpack");
const {
    VueLoaderPlugin
} = require("vue-loader")
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
// 需要通过 cross-env 定义环境变量
const isProduction = process.env.NODE_ENV === "production";
// element-plus
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const {
    ElementPlusResolver
} = require('unplugin-vue-components/resolvers')

const getStyleLoader = (loader) => {
    return [
        isProduction ? MiniCssExtractPlugin.loader : "vue-style-loader",
        "css-loader",
        // 配置css样式兼容,从下往上加载
        {
            loader: 'postcss-loader',
            options: {
                postcssOptions: {
                    plugins: ["postcss-preset-env"]
                },
            }
        },
        loader
        // loader === "sass-loader" ? {
        //     loader: loader,
        //     options: {
        //         scss: {
        //             additionalData: `@use "@/styles/element/index.scss" as *;`,
        //         },
        //     }
        // } : loader
    ].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].chunk.js' : 'static/js/[name].chunk.js',
        assetModuleFilename: 'static/media/[hash:10][ext][query]',
        clean: true,
    },
    module: {
        rules: [{
                test: /\.css$/,
                use: getStyleLoader(),
            },
            {
                test: /\.less$/,
                use: getStyleLoader("less-loader"),
            },
            {
                test: /\.s[ac]ss$/,
                use: getStyleLoader("sass-loader"),
            },
            {
                test: /\.(jpe?g|gif|svg|png)/,
                type: 'asset',
                parser: {
                    // 配置图片小于10kb就转化成base64格式:可以减少请求次数
                    dataUrlCondition: {
                        maxSize: 10 * 1024,
                    }
                }
            },
            {
                test: /\.(woff2?|ttf)/,
                type: 'asset-resource',
            },
            {
                test: /\.js/,
                include: path.resolve(__dirname, '../src'),
                use: [{
                    loader: 'babel-loader',
                    options: {
                        cacheDirectory: true,
                        cacheCompression: false,
                    }
                }]
            },
            {
                test: /\.vue$/,
                include: path.resolve(__dirname, "../src"),
                loader: 'vue-loader',
                options: {
                    // 开启缓存
                    cacheDirectory: path.resolve(__dirname, '../node_modules/.cache/vue-loader'),
                }
            }
        ]
    },
    plugins: [
        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",
        }),
        new EslintWebpackPlugin({
            cache: true,
            context: path.resolve(__dirname, '../src'),
            exclude: 'node_modules',
            cacheLocation: path.resolve(__dirname, "../node_modules/.cache/.eslintcache"),
        }),

        new DefinePlugin({
            __VUE_OPTIONS_API__: "true",
            __VUE_PROD_DEVTOOLS__: "false",
        }),
        new VueLoaderPlugin(),
        new CopyPlugin({
            patterns: [{
                from: path.resolve(__dirname, "../public"),
                to: path.resolve(__dirname, "../dist"),
                globOptions: {
                    ignore: ["**/index.html"]
                },
                info: {
                    minimized: true,
                },

            }]
        }),
        // 按需加载element-plus
        AutoImport({
            resolvers: [ElementPlusResolver()],
        }),
        Components({
            resolvers: [ElementPlusResolver({
                // 自定义主题
                // importStyle: "sass",
            })],
        }),
    ].filter(Boolean),
    optimization: {
        minimize: isProduction,
        minimizer: [
            new TerserPlugin(),
            new CssMinimizerPlugin(),
        ],
        splitChunks: {
            chunks: "all",
            // 分组打包
            chunkGroups: {
                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}`,
        },
    },
    resolve: {
        extensions: [".vue", ".js", ".json"],
        alias: {
            "@": path.resolve(__dirname, "../src"),
        }
    },
    devtool: isProduction ? "source-map" : "cheap-module-source-map",
    mode: isProduction ? 'production' : "development",
    devServer: {
        host: "localhost",
        port: 3001,
        open: true,
        hot: true,
        historyApiFallback: true, // 解决vue-router刷新404问题
    },
    performance: false
}

完整的代码可以访问:Github

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值