webpack5从0搭建Vue脚手架

基于webpack5搭建的vue2脚手架

  • 本代码已发布npm,直接使用npm即可安装
  1. npm i webpack-vue2-cli -g
  2. wvc 项目名称

内容简介

1.ESLint插件
2.图片压缩
3.代码压缩
4.项目拆包分包
5.前端离线化

  • 等等一系列优化操作以及详细的注释,方便同学们学习
  • 当时写这套代码的原因是因为公司老项目各方面性能太慢,所以决定升级脚手架
  • 也可用于公司老项目直接嵌套进来直接使用

为什么需要打包工具?

开发时,我们会使用框架(React、Vue),ES6 模块化语法,Less/Sass 等 css 预处理器等语法进行开发。

这样的代码要想在浏览器运行必须经过编译成浏览器能识别的 JS、Css 等语法,才能运行。

所以我们需要打包工具帮我们做完这些事。

除此之外,打包工具还能压缩代码、做兼容性处理、提升代码性能等。

文件组成目录

├── webpack-test (项目根目录)
    ├── webpack.dev.js(配置文件)
    ├── node_modules (下载包存放目录)
    ├── src (项目源码目录,除了html其他都在src里面)
    │    └── 略
    ├── public (项目html文件)
    │    └── index.html
    ├── .eslintrc.js(Eslint配置文件)
    ├── babel.config.js(Babel配置文件)
    └── package.json (包的依赖管理配置文件)

webpack.config.js

const os = require("os");
const path = require("path");
// ESLint插件
// 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 ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
//代码压缩
const TerserWebpackPlugin = require("terser-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
// VueLoader
const VueLoaderPlugin = require("vue-loader/lib/plugin-webpack5");
const {
    
     DefinePlugin } = require("webpack");
// 进度条插件
const WebpackBar = require('webpackbar');
// 离线化
// const WorkboxPlugin = require("workbox-webpack-plugin");
// // cpu核数
const threads = os.cpus().length;
// 需要通过 cross-env 定义环境变量
const isProduction = process.env.NODE_ENV === "production";

const getStyleLoaders = (preProcessor) => {
    
    
    return [
        isProduction ? MiniCssExtractPlugin.loader : "vue-style-loader",
        "css-loader",
        // Css 兼容性处理
        {
    
    
            loader: "postcss-loader",
            options: {
    
    
                postcssOptions: {
    
    
                    plugins: ["postcss-preset-env"],// 能解决大多数样式兼容性问题
                },
            },
        },
        preProcessor && {
    
    
            loader: preProcessor,
            // options:
            //     preProcessor === "sass-loader"
            //         ? {
    
    
            //             // 自定义主题:自动引入我们定义的scss文件
            //             additionalData: `@use "@/styles/element/index.scss" as *;`,
            //         }
            //         : {},
        },
    ].filter(Boolean);
};

module.exports = {
    
    
    // 入口
    entry: "./src/main.js",
    // 出口
    output: {
    
    
        // 出口文件inde.html访问的路径
        publicPath: '/',
        path: isProduction ? path.resolve(__dirname, "dist") : undefined,
        // 普通文件
        filename: isProduction
            ? "js/[name].[contenthash:10].js"
            : "js/[name].js",
        // 异步加载的文件(比如点击执行的)
        chunkFilename: isProduction
            ? "js/[name].[contenthash:10].chunk.js"
            : "js/[name].chunk.js",
        // 静态文件
        assetModuleFilename: "assets/[hash:10][ext][query]",
        clean: true,
        // 默认 webpack 会在输出的 bundle 中生成路径信息,将路径信息删除可小幅提升构建速度。
        pathinfo: false,
    },
    module: {
    
    
        // lodaer存放地区
        // 资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。
        // asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
        // asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
        // asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
        // asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。
        rules: [
            {
    
    
                test: /\.css$/,
                // use 数组里面 Loader 执行顺序是从右到左
                use: getStyleLoaders(),
            },
            {
    
    
                test: /\.s[ac]ss$/,
                use: getStyleLoaders("sass-loader"),
            },
            {
    
    
                test: /\.(png|jpe?g|gif|svg)$/,
                type: "asset",
                parser: {
    
    
                    dataUrlCondition: {
    
    
                        maxSize: 10 * 1024, //图片大于10*1024进行base64转码
                    },
                },
                generator: {
    
    
                    filename: 'assets/img/[hash][ext][query]' // 局部指定输出位置
                }
            },
            {
    
    
                test: /\.(ttf|woff2?|map4|map3|avi)$/,
                type: "asset/resource",
                generator: {
    
    
                    filename: "static/media/[hash:8][ext][query]",
                },
            },
            {
    
    
                test: /\.(ttf|woff2?)$/,
                type: "asset/resource",
                generator: {
    
    
                    filename: 'assets/font/[hash][ext][query]' // 局部指定输出位置
                }
            },
            {
    
    
                test: /\.js$/,
                include: path.resolve(__dirname, "src"),//include查找src文件下内容,exclude用于排除某文件外
                loader: "babel-loader",
                options: {
    
    
                    cacheDirectory: true,// 开启babel编译缓存
                    cacheCompression: false,// 缓存文件不要压缩
                    plugins: [
                        // "@babel/plugin-transform-runtime" //减少代码体积, presets中包含了
                    ],
                },
                // use: [
                //     {
    
    
                //         loader: "thread-loader", // 开启多进程
                //         options: {
    
    
                //             workers: threads, // 数量
                //         },
                //     },
                //     {
    
    
                //         loader: "babel-loader",
                //         options: {
    
    
                //             cacheDirectory: true, // 开启babel编译缓存
                //             cacheCompression: false,// 缓存文件不要压缩
                //         },
                //     },
                // ],
            },
            // vue-loader不支持oneOf
            {
    
    
                test: /\.vue$/,
                loader: "vue-loader", // 内部会给vue文件注入HMR功能代码
                options: {
    
    
                    // 开启缓存
                    cacheDirectory: path.resolve(
                        __dirname,
                        "node_modules/.cache/vue-loader"
                    ),
                },
                // 项目大的话可以尝试给这个loader开启单独线程运行提高效率
                // use: [
                //     {
    
    
                //         loader: 'vue-loader',
                //         options: {
    
    
                //             // 开启缓存
                //             cacheDirectory: path.resolve(
                //                 __dirname,
                //                 "node_modules/.cache/vue-loader"
                //             ),
                //         },
                //       },
                //      {
    
    
                //          loader: 'thread-loader',
                //      },
                // ],
            },
        ],
    },
    // 插件存放地区
    plugins: [
        // new ESLintWebpackPlugin({
    
    
        // 指定检查文件的根目录
        //   context: path.resolve(__dirname, "../src"),
        //   exclude: "node_modules",
        //   cache: true,
        //   cacheLocation: path.resolve(
        //     __dirname,
        //     "../node_modules/.cache/.eslintcache"
        //   ),
        // }),
        new WebpackBar({
    
    
            // color: "#85d", // 默认green,进度条颜色支持HEX
            // basic: true, // 默认true,启用一个简单的日志报告器
            // profile: true, // 默认false,启用探查器。
        }),
        new HtmlWebpackPlugin({
    
    
            // 以 public/index.html 为模板创建文件
            // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
            template: path.resolve(__dirname, "public/index.html"),
        }),
        // 复制文件
        new CopyPlugin({
    
    
            patterns: [
                {
    
    
                    from: path.resolve(__dirname, "public"),
                    to: path.resolve(__dirname, "dist"),
                    toType: "dir",
                    noErrorOnMissing: true,
                    globOptions: {
    
    
                        ignore: ["**/index.html"],
                    },
                    info: {
    
    
                        minimized: true,
                    },
                },
            ],
        }),
        // 提取css成单独文件
        isProduction &&
        new MiniCssExtractPlugin({
    
    
            // 定义输出文件名和目录
            filename: "assets/css/[name].[contenthash:10].css",
            chunkFilename: "assets/css/[name].[contenthash:10].chunk.css",
        }),
        new VueLoaderPlugin(),
        new DefinePlugin({
    
    
            __VUE_OPTIONS_API__: "true",
            __VUE_PROD_DEVTOOLS__: "false",
            'process.env': {
    
    
                VUE_APP_HOST: isProduction // 将属性转化为全局变量,让代码中可以正常访问
            }
        }),
        // // https和本地可以用
        // new WorkboxPlugin.GenerateSW({
    
    
        //     // 这些选项帮助快速启用 ServiceWorkers
        //     // 不允许遗留任何“旧的” ServiceWorkers,更新代码自动替换资源刷新网页
        //     clientsClaim: true,
        //     skipWaiting: true,
        //     exclude: [/.*\.mp4$/], // 此处添加过滤规则
        //     maximumFileSizeToCacheInBytes: 10485760, // 适当调整预缓存的单个文件大小上限10mb以下的存储
        // }),
    ].filter(Boolean),
    // 代码处理
    optimization: {
    
    
        // 告知 webpack 使用 TerserPlugin 或其它在 optimization.minimizer定义的插件压缩 bundle
        minimize: isProduction,
        // 压缩的操作
        minimizer: [
            new CssMinimizerPlugin(),//压缩css
            // 当生产模式会默认开启TerserPlugin,压缩 JavaScript,但是我们需要进行其他配置,就要重新写了
            new TerserWebpackPlugin({
    
    //代码压缩
                parallel: threads, // 开启多进程,填入数字是开启几个线程
                terserOptions: {
    
    
                    compress: {
    
    
                        drop_console: true, //去除log
                    },
                },
            }),
            // 压缩图片
            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-element",
                    test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
                    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",
                },
            },
        },
        // 为运行时代码创建一个额外的 chunk,减少 entry chunk 体积,提高性能。
        runtimeChunk: {
    
    
            name: (entrypoint) => `runtime~${
      
      entrypoint.name}`,
        },
    },
    // resolve 用来配置 webpack 如何解析模块,可通过优化 resolve 配置来覆盖默认配置项,减少解析范围
    resolve: {
    
    
        modules: ['node_modules'],// 指定查找依赖包目录
        // 需要解析的文件类型列表。由于 webpack 的解析顺序是从左到右,因此要将使用频率高的文件类型放在左侧,如下我将 vue 放在最左侧。
        extensions: [".vue", ".js", ".json"],
        alias: {
    
    
            // 路径别名
            "@": path.resolve(__dirname, "src"),
        },
        fallback: {
    
     "stream": false } //vue-loader相关报错解决办法
    },
    // 开发服务器
    devServer: {
    
    
        open: true,
        host: "localhost",
        port: 3000,
        hot: true,  //热模块更新
        compress: true,
        historyApiFallback: true, // 解决vue-router刷新404问题
    },
    //Webpack默认在生产环境下(mode:'production')自动进行代码压缩,内部用的是terser-webpack-plugin插件
    mode: isProduction ? "production" : "development",
    //错误代码信息标注,第一个是行,第二个是行加列,关闭默认按最大性能处理,开启方便查错
    // devtool: isProduction ? "source-map" : "cheap-module-source-map",
    performance: false,
};
// Tree Shaking 也叫摇树优化,基于ESModules模块化(即只有ESModules的模块化代码才能使Tree Shaking生效),在production生产环境下默认开启

.eslintrc.js

Eslint

可组装的 JavaScript 和 JSX 检查工具。

这句话意思就是:它是用来检测 js 和 jsx 语法的工具,可以配置各项功能

我们使用 Eslint,关键是写 Eslint 配置文件,里面写上各种 rules 规则,将来运行 Eslint 时就会以写的规则对代码进行检查

module.exports = {
    
    
    root: true,
    env: {
    
    
      node: true,
    },
    extends: ["plugin:vue/vueessential", "eslint:recommended"],// 继承 vue插件规则和 Eslint 规则
    parserOptions: {
    
    
      parser: "@babel/eslint-parser", 支持最新的最终 ECMAScript 标准
    },
  };
  

babel.config.js

Babel

JavaScript 编译器。

主要用于将 ES6 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中

module.exports = {
    
    
    presets: ["@vue/cli-plugin-babel/preset"],// 使用vue预设
  };
  

关于作者

开源项目,感觉不错的话辛苦给个star哦:https://gitee.com/chen-xinke/vue2-webpack-cli.git
有任何问题也欢迎随时交流

猜你喜欢

转载自blog.csdn.net/weixin_52691965/article/details/129800379