zoukankan      html  css  js  c++  java
  • 使用webpack搭建vue项目

    有一句话叫“前人栽树后人乘凉”,还有一句话叫“如果说我看得比别人更远些,那是因为我站在巨人的肩膀上”。前一句是国人的俗语,后一句是那个发现了“万有引力”定律的牛顿说的。为什么要引用这两句呢?是因为我刚开始用vue的时候,使用的是vue-cli来搭建vue项目,快速又好用;我刚开始用react的时候,使用的是create-react-app来搭建react项目,方便又省事。使用这些已有的脚手架来搭建项目,无可厚非,对于新手来说,也确实能快速构建,不做置评。

    既然已经有了这些现成的脚手架了,为什么我们还热衷于自己来配置webpack来搭建构建项目呢?因为我们只有了解并学会了配置webpack,我们才能更好地在打包构建项目时将webpack的性能发挥到极致,才能根据自身项目的实际需求,配置有利于项目开发的各种工具、插件,提高我们的开发效率。比如我们在打包项目时,可以分析哪些地方降低了webpack的打包速度,别人打包速度需要花去十多秒、二十多秒,而你能将打包的速度提升至几秒,这就是你的优势。当然,涉及到webpack的运行原理以及开发自己的loader或plugin就可以自行去学习了哈,本文只带你配置一个webpack来搭建一个vue项目。

    wepack作为一个“模块打包机”其实是依赖了庞大的插件体系,插件体系是webpack的核心,可以说,webpack的生态就是建立在众多插件之上的,而开发环境和生产打包环境依赖的插件还是有所不同的,先以开发环境为例webpack.config.js:

    const path = require('path');
    const Webpack = require('webpack');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    const VueLoaderPlugin = require('vue-loader/lib/plugin');
    const ProgressBarPlugin = require('progress-bar-webpack-plugin');
    
    const resolve = (dir) => {
        return path.join(__dirname, '..', dir)
    }
    
    const assetsPath = (_path) => {
        return path.join('static', _path)
    }
    
    const isEnvProduction = process.env.NODE_ENV == "production", port = 3003;
    
    module.exports = {
        mode: 'development',
        devtool: 'source-map',
        entry: resolve('src'),
        output: {
            path: resolve('dist'),
            filename: isEnvProduction ? assetsPath('js/[name]-[hash].js') : '[name]-[hash].js',
            chunkFilename: isEnvProduction ? assetsPath('js/[name]-[chunkhash:5].min.js') : '[name]-[chunkhash:5].min.js',
            publicPath: '/',
        },
        resolve: {
            extensions: ['*', '.js', '.vue'], //webpack2.x extensions[0]不能为空   resolve属性中的extensions数组中用于配置程序可以自行补全哪些文件后缀
            alias: {
                '@': resolve('src'),
                // 'vue$': 'vue/dist/vue.esm.js'
            },
        },
        //提取公共代码
        optimization: {
            splitChunks: {
                cacheGroups: {
                    commons: {
                        test: /[\/]node_modules[\/]/,    //表示默认拆分node_modules中的模块
                        name: "vendor",  //提取出来的文件命名
                        chunks: "all",     //提取所有文件的公共部分
                        minChunks: 2,         //表示提取公共部分最少的文件数  模块被引用>=2次,拆分至vendors公共模块
                        minSize: 0,         //表示提取公共部分最小的大小  模块超过0k自动被抽离成公共模块
                    },
                }
            }
        },
        module: {
            rules: [
                {
                    test: /.vue$/,
                    use: ['vue-loader'],
                    exclude: /node_modules/,
                },
                {
                    test: /.js$/,
                    loader: 'babel-loader',
                    exclude: /node_modules/,
                    query: {
                        "presets": ["@babel/env"],
                        "plugins":
                            ["@babel/plugin-syntax-dynamic-import", "@babel/plugin-transform-runtime"],
                    }
                },
                {
                    test: /.(sa|sc|c)ss$/,
                    use: [
                        MiniCssExtractPlugin.loader,
                        'css-loader',
                        'postcss-loader',
                        'sass-loader',
                    ],
                },
                {
                    test: /.(eot?.+|svg?.+|ttf?.+|otf?.+|woff?.+|woff2?.+)$/,
                    use: 'file-loader?name=' + (isEnvProduction ? assetsPath('fonts/[name].[hash:8].[ext]') : 'fonts/[name].[hash:8].[ext]')
                },
                {
                    test: /.(jpg|jpeg|png|gif|ico|svg)$/,
                    loader: 'url-loader',
                    options: {
                        limit: 10000,
                        name: isEnvProduction ? assetsPath('images/[name].[hash:8].[ext]') : 'images/[name].[hash:8].[ext]',
                    }
                },
            ],
        },
        plugins: [
            new ProgressBarPlugin(),
            new VueLoaderPlugin(),
            //ProvidePlugin是webpack的内置模块,使用ProvidePlugin加载的模块在使用时将不再需要import和require进行引入
            new Webpack.ProvidePlugin({
                _: 'lodash',
            }),
            new HtmlWebpackPlugin({
                template: './src/index.html',    //文件路径及名称
                filename: 'index.html',          //输出后的文件名称
            }),
            new MiniCssExtractPlugin({
                filename: isEnvProduction ? assetsPath("css/[name]-[hash].css") : "css/[name]-[hash].css",
                chunkFilename: isEnvProduction ? assetsPath("css/[name]-[hash].css") : "css/[name]-[hash].css",   //默认就是取的以id或name为开头的css,所以可以加这行配置代码,也可以不加
            }),
        ],
        devServer: {
            port,
            host: '0.0.0.0',
            open: `http://localhost:${port}`,
            stats: {
                hash: false,
                builtAt: false,
                version: false,
                modules: false,
                children: false,   ////解决类似Entrypoint undefined = index.html和Entrypoint mini-css-extract-plugin = *的警告
                entrypoints: false,
                colors: {
                    green: 'u001b[32m',
                    yellow: 'u001b[32m',
                }
            },
            proxy: {
                '/': {
                    target: '',
                    changeOrigin: true
                }
            },
            inline: true,
            compress: false,
            disableHostCheck: true,
            historyApiFallback: true,
        },
    }
    

    关于配置中用到的一些插件的api就不一一展开详解了,唯一需要说明的一点是,配置中所用到的插件的版本基本都是最新的,而使用postcss-loader时,需要在项目的根目录新建一个postcss.config.js文件:

    module.exports = {  
      plugins: [require('autoprefixer')()]
    }
    

    以上是开发环境的webpack配置,下边是打包生产环境的配置webpack.product.config.js:

    const path = require('path');
    const config = require('./webpack.config');
    const merge = require('webpack-merge');
    const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");  //压缩单独的css文件
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    const ManifestPlugin = require('webpack-manifest-plugin');  //资源清单
    const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");   //监控打包文件所花费的时间,方便具体的性能优化
    const smp = new SpeedMeasurePlugin();
    const PurifyCSSPlugin = require("purifycss-webpack");   //css tree-shaking 依赖插件glob-all和purify-css
    const glob = require("glob-all");
    
    module.exports = smp.wrap(merge(config, {
      mode: 'production',
      stats: config.devServer.stats,
      devtool: false,
      //当我们想在项目中require一些其他的类库或者API,而又不想让这些类库的源码被构建到运行时文件中,这在实际开发中很有必要。此时我们就可以通过配置externals参数来解决这个问题
      externals: {
        'vue': 'Vue',
        'vuex': 'Vuex',
        'moment': 'moment',
        'vue-router': 'VueRouter',
        'element-ui': 'ELEMENT',
        'ant-design-vue': 'antd',   //使用externals html里需手动引入一下js,特别注意:还需额外引入moment.js,并放在antd之前,否则会报错
        'lodash': '_',
      },
      optimization: {
        minimizer: [
          new UglifyJsPlugin({
            parallel: true,    //使用多线程并行运行来提高构建速度,默认并发运行数量:os.cpus().length - 1
            uglifyOptions: {
              compress: {
                inline: false,
                drop_console: true,   //是否屏蔽掉控制台输出
              },
            }
          }),
          new OptimizeCSSAssetsPlugin()   //压缩css
        ]
      },
      plugins: [
        new ManifestPlugin(),
        new CleanWebpackPlugin(),
        new PurifyCSSPlugin({
          paths: glob.sync([
            // 要做CSS Tree Shaking的路径文件
            path.resolve(__dirname, "../src/*.vue")
          ])
        }),
        new HtmlWebpackPlugin({
          template: './src/index.prod.html',    //打包时需要的文件路径和名称
          filename: 'index.html',               //打包输出后的文件名称
          minify: {   //压缩html
            removeComments: true,   //删除注释
            collapseWhitespace: true   //删除空格
          }
        }),
      ],
    }));
    

    打包的配置中有几点需要注意:
    1、配置中有一个speed-measure-webpack-plugin的插件,可以监控打包文件所花费的时间,方便具体的性能优化;
    2、配置中加入了webpack-manifest-plugin生成资源清单的插件,这个插件所生成的资源清单对服务端渲染SSR非常有用,服务端可以根据当前的manifest,引入css和js文件;
    3、配置中引入了purifycss-webpackglob-all两个插件并依赖一个purify-css插件用来对css的tree-shaking。shake有摇动、抖动之意,言外之意就是通过抖动将项目中没有使用却定义了的js方法给删除,降低打包后项目的体积,很形象哈。自webpack2开始,webpack就自带了js的tree-shaking,却没有css的tree-shaking,所以我们就借助了插件来实现tree-shaking。
    4、为了提高打包的速度以及降低打包后的项目体积,我们可以将项目中用到框架采用CDN的方式引入,从而将这部分框架排除在打包之外,而new HtmlWebpackPlugin配置项中的template的路径引用的index.prod.html文件就是采用CDN的方式引入的第三方的框架,区分了开发环境中的index.html。提升构建速度也可以通过DllPlugin和DLLReferencePlugin插件来实现,具体配置可参考:https://www.cnblogs.com/lusongshu/p/8473318.html

    vue的项目目录:

    react项目的webpack配置跟vue项目的webpack配置大同小异,这里不再多说,最后奉上package.json:

    {
      "name": "webpackvue",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo "Error: no test specified" && exit 1",
        "dev": "cross-env BABEL_ENV=development webpack-dev-server --config config/webpack.config.js",
        "build": "cross-env NODE_ENV=production webpack --config config/webpack.product.config.js"
      },
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "@babel/core": "^7.4.4",
        "@babel/plugin-syntax-dynamic-import": "^7.2.0",
        "@babel/plugin-transform-runtime": "^7.4.4",
        "@babel/preset-env": "^7.4.4",
        "@babel/runtime": "^7.4.4",
        "autoprefixer": "^9.5.1",
        "babel-loader": "^8.0.6",
        "babel-plugin-import": "^1.11.2",
        "clean-webpack-plugin": "^2.0.2",
        "cross-env": "^5.2.0",
        "css-loader": "^2.1.1",
        "file-loader": "^3.0.1",
        "glob-all": "^3.1.0",
        "html-webpack-plugin": "^3.2.0",
        "lodash": "^4.17.11",
        "mini-css-extract-plugin": "^0.6.0",
        "node-sass": "^4.12.0",
        "optimize-css-assets-webpack-plugin": "^5.0.1",
        "postcss-loader": "^3.0.0",
        "progress-bar-webpack-plugin": "^1.12.1",
        "purify-css": "^1.2.5",
        "purifycss-webpack": "^0.7.0",
        "sass-loader": "^7.1.0",
        "speed-measure-webpack-plugin": "^1.3.1",
        "style-loader": "^0.23.1",
        "uglifyjs-webpack-plugin": "^2.1.3",
        "url-loader": "^1.1.2",
        "vue-loader": "^15.7.0",
        "vue-template-compiler": "^2.6.10",
        "webpack": "^4.31.0",
        "webpack-cli": "^3.3.2",
        "webpack-dev-server": "^3.3.1",
        "webpack-manifest-plugin": "^2.0.4",
        "webpack-merge": "^4.2.1"
      },
      "dependencies": {
        "ant-design-vue": "^1.3.9",
        "element-ui": "^2.8.2",
        "moment": "^2.24.0",
        "vue": "^2.6.10",
        "vue-router": "^3.0.6",
        "vuex": "^3.1.1"
      }
    }
    
  • 相关阅读:
    TCP同步与异步
    C#委托与事件
    线程
    C# 多人聊天程序
    vs启动错误
    记住我的痛苦
    C#命名空间与类名的冲突
    C#调试类
    linux ifconfig命令参数及用法详解linux查看配置网卡命令
    B/S架构
  • 原文地址:https://www.cnblogs.com/tnnyang/p/10997782.html
Copyright © 2011-2022 走看看