基于Vue-cli4(基于webpack5)打包实战

为什么要升级Vue-cli4?

因为Vue-cli基于webpack4封装的,而我想用webpack5,为什么想用webpack5?那咱们要先看看webpack4和webpack5的区别了。

1.Hash生成方式改变。

Webpack4是根据代码的结构生成chunkhash,添加了空白行或注释,会引起chunkhash的变化。 Webpack5是根据内容生成chunkhash,改了注释或者变量不会引起chunkhash的变化,浏览器可以继续使用缓存。

2.优化了对缓存的使用效率。

在Webpack4 中,chunkId与moduleId都是自增id。只要我们新增一个模块,那么代码中module的数量就会发生变化,从而导致moduleId发生变化,于是文件内容就发生了变化。chunkId也是如此,新增一个入口的时候,chunk数量的变化造成了chunkId的变化,导致了文件内容变化。所以对实际未改变的chunk文件不能有效利用。 Webpack 模块 ID 默认是按照依赖顺序递增分配的,这会使得依赖了某个出现改动 chunk 的其他 chunk 内容也跟着变化,导致浏览器缓存失效。增减模块会导致顺序错位 Webpack5采用新的算法来计算确定性的chunkId和moduleId。可以有效利用缓存。在production模式下,optimization.chunkIds和optimization.moduleIds默认会设为deterministic.deterministic 选项有益于长期缓存,生成bundles比 hashed 小,比size大。稳定性ID, 这是包大小和长期缓存之间的一种权衡 。

.env环境配置

1.环境变量和模式

.env                       # 在所有的环境中被载入 

.env.local               # 在所有的环境中被载入,但会被 git 忽略 

.env.[mode]           # 只在指定的模式中被载入

.env.[mode].local   # 只在指定的模式中被载入,但会被 git 忽略 

使用:

vue-cli-service serve  --mode dev 

联合命令的方式:

"build": "npm run style && vue-cli-service build  --mode production" 

注意: 环境里所有的变量都是string类型,所以某个变量是true,实际获取的是字符串‘true’.

编辑器配置

1.配置默认eslint规范

.editorconfig           # 编辑器配置 

.eslintrc.js                # eslint配置

.prettierrc                # Prettier - Code formatter插件配置 

.eslintignore            # 不需要格式化的文件以及文件夹 

使用:

npm i  eslint@7.32.0  eslint-config-prettier@8.5.0   eslint-plugin-prettier@4.0.0  -D 

注意:

1.配合Visual Code里的Prettier - Code formatter插件。

2.不要下载Prettier ESLint插件,会有莫名奇妙的冲突。

3.注意版本对应关系。

Vue.config.js配置文件

1.Path模块

const resolve = (dir) => path.join(__dirname, dir)
path.join(__dirname,'a','b') 拼接出来就是是D:/test/1/a/b,

__dirname :当前js文件的绝对路径,D:/test/1

path.resolve(),,用到当前项目的绝对路径,

path.resolve('www', 'imgs/png/', '../abc/image.jpg') 

拼接出来就是D:\test\1\www\imgs\abc\image.jpg

2.插件

2.1 css-minimizer-webpack-plugin 压缩Css的插件

optimization: {
    minimizer: [
      // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
      // `...`,
      new CssMinimizerPlugin(),
    ],
},

如果还想在开发环境下启用 CSS 优化,请将 optimization.minimize 设置为 true:

// [...]
module.exports = {
  optimization: {
    // [...]    
    minimize: true,
    minimizer: [
      // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
      // `...`,
      new CssMinimizerPlugin(),
    ],
  },
};

2.2 mini-css-extract-plugin 提取单独css文件的插件

  plugins: [
            // 提取 CSS
            new MiniCssExtractPlugin({
                filename: 'css/[contenthash]-[name].css',
            }),
  ]

tips: filename路径会忽略output或者assets路径。

2.3 terser-webpack-plugin  js压缩插件

optimization: {
            minimizer: [
                new TerserWebpackPlugin({
                    parallel: 4, //并发运行的默认数量
                    terserOptions: {
                        parse: {
                            ecma: 8,
                        },
                        compress: {
                            ecma: 5,
                            warnings: false,
                            comparisons: false,
                            inline: 2,
                        },
                        mangle: {
                            safari10: true,
                        },
                        output: {
                            ecma: 5,
                            comments: false,
                            ascii_only: true,
                        },
                    },
                }),
              
            ],
}

2.4 progress-bar-webpack-plugin 进度条插件        

plugins: [
            // 进度条
            new ProgressBarPlugin({
                format: `  :msg [:bar] ${chalk.green.bold(
                    ':percent'
                )} (:elapsed s)`,
            }),
]

2.5 uglifyjs-webpack-plugin 去除console 的插件

if (isPro) {
            plugins.push(
                new UglifyJsPlugin({
                    uglifyOptions: {
                        output: {
                            comments: false, // 去掉注释
                        },
                        warnings: false,
                        compress: {
                            drop_console: true,
                            drop_debugger: false,
                            pure_funcs: ['console.log'], //移除console
                        },
                    },
                })
            )          
        }

2.6 style-resources-loader 配置全局less变量

 pluginOptions: {
        'style-resources-loader': {
            preProcessor: 'less',
            patterns: [resolve('./theme/default/var.less')],
        },
    },

整个vue.config.js

/*
 * @Description:
 * @LastEditTime: 2022-06-13 18:26:35
 */
const { defineConfig } = require('@vue/cli-service'),
    isPro = process.env.NODE_ENV === 'production',
    publicPath = isPro ? './' : '/',
    path = require('path'),
    chalk = require('chalk'),
    webpack = require('webpack'),
    resolve = (dir) => path.join(__dirname, dir)

// 插件
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin'), //压缩 CSS 文件。
    MiniCssExtractPlugin = require('mini-css-extract-plugin'), //插件将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。
    TerserWebpackPlugin = require('terser-webpack-plugin'), //来压缩 JavaScript。
    ProgressBarPlugin = require('progress-bar-webpack-plugin'), //进度条插件
    PurgeCSSPlugin = require('purgecss-webpack-plugin'), //对 CSS Tree Shaking。
    UglifyJsPlugin = require('uglifyjs-webpack-plugin') //去除console

module.exports = defineConfig({
    publicPath, //所以请始终使用 publicPath 而不要直接修改 webpack 的 output.publicPath。
    // 输出文件目录
    // outputDir: 'dist', //请始终使用 outputDir 而不要修改 webpack 的 output.path。
    // assetsDir: 'assets',
    // indexPath:'index.html',//指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径。
    // 静态资源存放的文件夹(相对于ouputDir)
    runtimeCompiler: true,
    filenameHashing: true,
    productionSourceMap: !isPro,
    lintOnSave: false, //启动关闭eslint
    chainWebpack: (config) => {
        const plugins = []
        // 修复HMR
        config.resolve.symlinks(true)
        if (isPro) {
            plugins.push(
                new UglifyJsPlugin({
                    uglifyOptions: {
                        output: {
                            comments: false, // 去掉注释
                        },
                        warnings: false,
                        compress: {
                            drop_console: true,
                            drop_debugger: false,
                            pure_funcs: ['console.log'], //移除console
                        },
                    },
                })
            )
            // 必须使用cnpm安装,否则报错
            //     config.module
            //         .rule('images')
            //         .use('image-webpack-loader')
            //         .loader('image-webpack-loader')
            //         .options({
            //             mozjpeg: { progressive: true, quality: 65 },
            //             optipng: { enabled: false },
            //             pngquant: { quality: [0.65, 0.9], speed: 4 },
            //             gifsicle: { interlaced: false },
            //             webp: { quality: 75 },
            //    })
        }
        // 如果使用多页面打包,使用vue inspect --plugins查看html是否在结果数组中
        // config.plugin('html').tap((args) => {
        //     // html中添加cdn
        //     args[0].cdn = cdn
        //     return args
        // })
    },
    configureWebpack: {
        output: {
            pathinfo: false, //默认 webpack 会在输出的 bundle 中生成路径,将路径信息删除可小幅提升构建速度。
            // // 仅在生产环境添加 hash
            // filename: isPro
            //     ? '[name].[contenthash].bundle.js'
            //     : '[name].bundle.js',
        },
        resolve: {
            // 配置externals:防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖
            extensions: ['.css', '.js', '.vue', '.less', '.html'],
            symlinks: false, //如果项目不使用 symlinks(例如  npm link  或者  yarn link),
            alias: {
                '@': resolve('./src'),
                '@img': resolve('./src/assets/images'),
                '@modules': resolve('./src/modules'),
            },
        },
        // cache: {
        //     type: 'filesystem', // 不用缓存很是影响速度
        // },
        optimization: {
            moduleIds: 'deterministic', //让公共包 splitChunks 的 hash 不因为新的依赖而改变,减少非必要的 hash 变动
            runtimeChunk: true, //为运行时代码创建一个额外的 chunk,减少 entry chunk 体积,提高性能。
            minimizer: [
                new TerserWebpackPlugin({
                    parallel: 4, //并发运行的默认数量
                    terserOptions: {
                        parse: {
                            ecma: 8,
                        },
                        compress: {
                            ecma: 5,
                            warnings: false,
                            comparisons: false,
                            inline: 2,
                        },
                        mangle: {
                            safari10: true,
                        },
                        output: {
                            ecma: 5,
                            comments: false,
                            ascii_only: true,
                        },
                    },
                }),
                new CssMinimizerWebpackPlugin({
                    parallel: 4, //并发运行的默认数量
                }),
            ],
            splitChunks: {
                //项目中分别有a.js, b.js, page1.js, page2.js这四个JS文件, page1.js 和
                //page2.js中同时都引用了a.js, b.js, 这时候想把a.js, b.js抽离出来合并成一个公共的js,
                // 分割代码块
                cacheGroups: {
                    vendor: {
                        //第三方库抽离
                        name: 'vendor',
                        chunks: 'all',
                        test: /node_modules/,
                        minChunks: 1, //在分割之前,这个代码块最小应该被引用的次数
                        maxInitialRequests: 5,
                        minSize: 0, //大于0个字节
                        priority: 100, //权重
                    },
                    common: {
                        //公用模块抽离
                        name: 'common',
                        chunks: 'all',
                        test: /[\\/]src[\\/]js[\\/]/,
                        minChunks: 2, //在分割之前,这个代码块最小应该被引用的次数
                        maxInitialRequests: 5,
                        minSize: 0, //大于0个字节
                        priority: 60,
                    },
                    styles: {
                        //样式抽离
                        name: 'styles',
                        test: /\.(sa|sc|le|c)ss$/,
                        chunks: 'all',
                        enforce: true,
                    },
                    runtimeChunk: {
                        name: 'manifest',
                    },
                },
            },
        },
        plugins: [
            // 提取 CSS
            new MiniCssExtractPlugin({
                filename: 'css/[contenthash]-[name].css',
            }),
            // CSS Tree Shaking
            // new PurgeCSSPlugin({ //添加该插件会样式错乱
            //     paths: glob.sync(`./src/**/*`, { nodir: true }),
            // }),
            // 进度条
            new ProgressBarPlugin({
                format: `  :msg [:bar] ${chalk.green.bold(
                    ':percent'
                )} (:elapsed s)`,
            }),
            new webpack.ProvidePlugin({
                Vue: isPro ? 'lvm-base' : 'lvm-dev',
            }),
            // 处理<link rel="icon" href="<%= BASE_URL %>favicon.ico">
            new webpack.DefinePlugin({
                BASE_URL: '"./"',
            }),
        ],
        module: {
            rules: [
                {
                    // 字体文件等
                    test: /\.(woff|woff2|eot|ttf|otf)$/i,
                    // 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。即:所有匹配的文件都将被发送到输出目录,并且其路径将被注入到 bundle 中。
                    type: 'asset/resource',
                    exclude: /node_modules/,
                    parser: {
                        dataUrlCondition: {
                            // 超过20kb以文件形式引入,反之以base64形式引入
                            maxSize: 1024 * 20,
                        },
                    },
                    generator: {
                        //自定义输出目录
                        publicPath,
                        // 文件路径
                        filename: 'fonts/[name]-[contenthash:6].[ext]',
                    },
                },
                {
                    // 图片的转化
                    test: /\.(jpe?g|png|gif|bmp|svg)$/i,
                    type: 'asset',
                    include: [resolve('src')],
                    parser: {
                        dataUrlCondition: {
                            //自动地在 resource 和 inline 之间进行选择:小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型。
                            maxSize: 1024 * 15,
                        },
                    },
                    generator: {
                        publicPath,
                        filename: 'img/[name]-[contenthash:6].[ext]',
                    },
                },
            ],
        },
    },
    pluginOptions: {
        'style-resources-loader': {
            preProcessor: 'less',
            patterns: [resolve('./theme/default/var.less')],
        },
    },
    css: {
        sourceMap: false,
        // extract: false, //是否使用css分离插件
        // loaderOptions: { //向 CSS 相关的 loader 传递选项。例如:
        //   css: {
        //          这里的选项会传递给 css-loader
        //   },
        //   postcss: {
        //           这里的选项会传递给 postcss-loader
        //   }
        // },
    },
    devServer: {
        open: false, // 自动启动浏览器
        hot: true, // 热更新
        https: false,
        // host: '0.0.0.0', // localhost
        // port: 6060, // 端口号
        // proxy: {
        //         // 在 Vue 组件中使用 axios 发送请求的时候: axios.get('/api/xxx')
        //         // 也就是你的请求地址需要以当前规则(比如:/api)开头,那么,本次请求才会被代理
        //         // 如果某一个接口不需要被代理,就可以直接: axios.get('http://localhost:8080/api/xxx')
        //         // 这样配置后,最终的得到的接口地址为: http://localhost:8080/api/xxx
        //     '/api': {
        //         target: 'http://120.53.31.103:84/api/app', // 接口的域名
        //         // ws: true, // 是否启用websockets
        //         changOrigin: true, // 开启代理,在本地创建一个虚拟服务端
        //         pathRewrite: {
        //             '^/api': '/',
        //         },
        //     },
        // },
    },
})

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天才Q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值