1

更详尽

https://blog.csdn.net/qq_24147051/article/details/81533739

config/dev.env.js

'use strict'
// 引入webpack-merge模块
const merge = require('webpack-merge')
// 引入刚才打开的prod.env.js
const proEnv = require('./prod.env')
// 引入webpack-merge后这个文件又引入了prod.env.js,接着就将prod.env.js的配置和新的配置,即指明开发环境(development)进行了merge。(我有点儿不太理解为什么要这样做,如果不merge直接写module.exports={NODE_ENV:'"development'}也是可以的,难道是为了优雅降级?)
module.exports = merge(proEnv,{
    NODE_ENV:'"development"'
})  

config/prod.env.js

'use strict'
module.exports = {
    // 执行环境
    NODE_ENV:'"production"'
}

config/index.js

/*
 * @Author: yang
 * @Date: 2020-10-12 12:58:04
 * @LastEditors: yang
 * @LastEditTime: 2020-10-14 13:41:33
 * @FilePath: \新建文件夹 (4)\config\index.js
 */
'use strict'
const path = require('path')
module.exports = {
    dev:{
        // Path
        assetsSubDirectory:'static',//子目录,一般存放css,js,image等文件
        assetsPublicPath:'/',//根目录
        proxyTable:{},//可利用该属性解决跨域的问题
        
        //Various Dev Server settings
        host:'localhost', //can be overwritten by progress.env.HOST 地址
        port:8080,//可以被覆盖进程.env.PORT,如果端口正在使用,则将确定一个空闲端口   端口号设置,端口号占用出现问题可在此处修改
        autoOpenBrowser:false,
        errorOverlay:true,//浏览器错误提示
        notifyOnErrors:true,//跨平台错误提示
        poll:false,//使用文件系统(file system)获取文件改动的通知devServer.watchOptions
        useEslint:true,//是否开启eslint 错误显示在console
        showEslintErrorsInOverlay:false,//如果开启错误显示在浏览器中
        devtool:'eval-source-map',
        //使缓存失效
        cacheBusting:true, //如果在devtools中调试vue文件遇到问题,把这个设置为false
        cssSourceMap:false//代码压缩后进行bug定位困难,true开启 sourcemap记录压缩前后的位置信息记录,当产生错误可直接定位到未压缩前的位置,方便调试
    },
    build:{
        // 生产环境下面的配置
        index:path.resolve(__dirname,'../dist/index.html'),//index 编译后生成的位置和名字
        assetsRoot:path.resolve(__dirname,'../dist'),//编译后存放生成环境代码的位置
        assetsSubDirectory:'static',//js ,css img存放文件夹名
        assetsPublicPath:'/',//发布的根目录,通常本地打包dist后打开文件会报错,此处修改为./。如果是上线的文件,可根据文件存放位置进行更改路径
        productionSourceMao:true,//设置生产环境的 source map 开启与关闭。是否生成 sourceMap 文件
        devtool:'#source-map',//生成.map文件
        productionGzip: false,//unit的gzip命令用来压缩文件 gzip模式下需要压缩的文件的扩展名有css和js
        productionGzipExtensions:['js','css'],
        bundleAnalyzerReport:process.env.npm_config_report//build 对象的 bundleAnalyzerReport  优化打包之后文件提交的工具。是否开启打包后的分析报告

    }
}

utils.js

// utils.js文件主要是用来处理各种css loader的,比如css-loader,less-loader等。
// 引入path模块
const path = require('path')
// 引入之前的config模块
const config = require('../config')
// 引入"extract-text-webpack-plugin"它的作用是打包后将生成的css文件通过link的方式引入到html中,如果不使用这个插件,那么css就打包到<head>的style中
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 引入package.json
const pkg = require('../package.json')
exports.assetsSubDirectory = function (_path) {
    // 结合config文件的代码可以知道 当环境为生产环境时 assetsSubDirectory为static开发环境也是static
  const assetsSubDirectory =
    progress.env.NODE_ENV === 'production'
      ? config.build.assetsSubDirectory
      : config.dev.assetsSubDirectory
    //   path.posix.join()是path.join的一种兼容写法 它的作用是路径的拼接 比如path.posix.join('/aa/s','bb')
      return path.posix.join(assetsSubDirectory,_path)
}
// 用来生成Loader的函数,本身可以用在vue-loader的配置上,同时也是为styleLoader函数使用

exports.cssLoaders = function(options){
    // 如果没有传参就默认空对象
    options = options||{}
    // 配置css-loader,css-loader可以让处理css中的@import或者url()
    const cssLoader = {
        loader:'css-loader',
        options:{
            sourceMap:options.sourceMap
        }
    }
    // 配置postcss-loader,主要功能是补全css中的兼容性前缀  比如“-webkit-”等
    var postcssLoader = {
        loader:"postcss-laoder",
        options:{
            sourceMap:options.sourceMap
        }
    }
    
    // 生成loader的私有方法
    function generateLoaders(loader,loaderOptions){
        // 参数的usePostCss属性是否为true 是就使用两个loader,否则只使用css-loader
        const loaders = options.usePostCSS? [cssLoader,postcssLoader]:[cssLoader]
        if(loader){
            // 给generateLoaders传loader参数的话 比如less 或者sass 就将这个loader的配置传入到loaders数组中
            loaders.push({
                loader:loader+'-loader',
                // Object.assign()是es6的语法  用来合并对象
                options: Object.assign({}, loaderOptions, {
                    sourceMap: options.sourceMap
                  })
            })
        }
// 如果options参数的extract属性为true  即使用extract text plugin   将css抽成单独的文件  否则就将css写进style     
        if(options.extract){
            return ExtractTextPlugin.extract({
                use:loaders,
                // vue-style-loader可以理解为vue版的style-loader  它可将css放进style中
                fallback:'vue-style-loader'
            })
            
        }else{
            return ['vue-style-loader'].concat('loaders')
        }

    }
    return {
        // 返回各种loader
        css:generateLoaders(),
        postcss:generateLoaders(),
        less:generateLoaders('less'),
        sass:generateLoaders('sass',{indentedSyntax:true}),
        scss:generateLoaders('sass'),
        stylus:generateLoaders('stylus'),
        styl:generateLoaders('stylus')
    }
}

// 生成开发环境下的loader的配置,使用在(webpack.dev.config.js中)
exports.styleLoaders = function(options){
    const output = []
    // 调用cssLoader方法 返回loaders的对象
    const loaders = exports.cssLoaders(options)
    // 遍历每一个loader 并配置成对应的格式  传给output数组
    for(const extension in loaders){
        const loader = loaders[extension]
        output.push({
            test:new RegExp('\\.'+extension + '$'),
            use:loader
        })
    }
    return output
}

vue-loader.config

'use strict'
// 引入上面的utils文件
const utils = require('./utils')
// 引入config文件
const config = require('./config')
// 判断当前是否为生产环境,如果是则返回true
const isProdction = progress.env.NODE_ENV === 'prodction'
// 是否使用sourceMap如果是生产环境就使用config文件中的index.js中的生产环境配置,否则是否开发环境的配置
const sourceMapEnabled = isProdction ? config.build.productionSourceMap:config.dev.cssSourceMap

module.exports = {
    //utils文件cssLoaders返回的配置项,返回的格式为
    // loaders:{
    //     css:ExtractTextPlugin.extract({
    //         use:[cssLoader],
    // extract默认行为先使用css-loader编译css,如果一切顺利的话,结束之后把css导出到规定的文件去。但是如果编译过程中出现了错误,则继续使用vue-style-loader处理css。
    //         fallback:'vue-style-loader'
    //     }),
    //     postCss:{
    //         ...
    //     }
    // }
    loaders:utils.cssLoaders({
        sourceMap:sourceMapEnabled,
        extract:isProdction
    }),
    // 是否使用sourceMap
    cssSourceMap:sourceMapEnabled,
    // 是否使用cacheBusting,这个配置在config的文件
    cacheBusting:config.dev.cacheBusting
    
}

webpack.base.config.js

/*
 * @Author: yang
 * @Date: 2020-10-16 13:48:48
 * @LastEditors: yang
 * @LastEditTime: 2020-10-17 14:43:50
 * @FilePath: \新建文件夹 (4)\webpack.base.config.js
 */
const path = require('./path')
const utils = require('./utils')
const config = require('./config')
const vueLoaderConfig = require('./vue-loader.config')
// resolve函数返回根路径下的文件或文件夹
function resolve(dir){
    return path.join(__dirname,'..',dir)
}
module.exports = {
    // 返回根路径
    context:path.resolve(__dirname,'../'),
    // 设置入口文件
    entry:{
        app:'./src/main.js'
    },
    // 设置出口文件
    output:{
        //根据config模块得知是根目录下的dist文件夹
        path:config.build.assetsRoot,
        filename:'[name].js',
        //公共路径统一为'/'
        publicPath:ProgressEvent.env.NODE_ENV === 'prodution'?config.build.assetsPublicPath:config.dev.assetsPublicPath
    },
    resolve:{
        //自动解析的扩展,js vue ,json这三种格式得文件引用时不需要加上扩展了
        //import File from '../path/fo/file'
        extensions:['.js','.vue','.json'],
        alias:{
            // 精准匹配,使用vue来替代vue/dist/vue.esm.js路径
            'vue$':'vue/dist/vue.esm.js',
            '@':resolve('src')
        }
    },
    module:{
        rules:[
            //vue-loader,module
            {
                test: /\.vue$/,
                loader:'vue-loader',
                options:vueLoaderConfig
            },
            {
                test:/\.js$/,
                loader:'babel-loader',
                // 把要处理的目录包括进来
                include:[resolve('src'),resolve('test'),resolve('node_modules/webpack-dev-server/client')]
            },
            {
                test:/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
                loader:'url-loader',
                options:{
                    limit:10000,//超过10kb的才使用url-loader来映射到文件
                    name:utils.assetsPath('media/[name].[hash:7].[ext]')//其他的资源转移到静态资源文件夹
                }
            },
            {
                test:/\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                loader:'url-loader',
                options:{
                    limit:10000,
                    name:utils.assetsPublicPath('fonts/[name].[hash:7].[text]')
                }
            }
             
        ]
    }
}

webpack.dev.conf.js

/*
 * @Author: yang
 * @Date: 2020-10-17 16:16:57
 * @LastEditors: yang
 * @LastEditTime: 2020-10-18 14:24:04
 * @FilePath: \新建文件夹 (4)\webpack.dev.conf.js
 */
const utils = require('./utils')
const webpack = require('webpack')
const config = require('./config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.config')
// 拷贝资源的插件
const CopyWebapckPlugin = require('copy-webpack-plugin')
// 生成html模板的插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 展示错误日志的插件
const FriendlyErrorsPlugin = require('frendly-errors-webpack-plugin')
// 一个自动打开可用端口的包
const portfinder = require('portfinder')
// 当前环境的host
const HOST = progress.env.HOST
//当前环境的port
const PORT = progress.env.PORT&&Number(progress.env.PORT)
// 开发环境的配置
const devWebpackConfig = merge(baseWebpackConfig,{
    module:{
        //loader的配置 
        rules:utils.styleLoaders({
            sourceMap:config.dev.cssSourceMap,usePostCSS:true
        }),
        devtool:config.dev.devtool,
        devServer:{
            // 重新加载server时,控制台对一些错误以warning的方式提示
            clientLogLevel:'warning',
            //当使用HTML5 history API时 任意的404响应都可能需要被替代为index.html
            historyApiFallback:{
                rewrites:[
                {from:/.*/,to:path.posix.join(config.dev.assetsSubDirectory,'index.html')}
                ]
            },
            // 启用webpack的模块热替换特性
            hot:true,
            //告诉服务器从哪里提供内容。只有在你想要提供静态文件是才需要
            contentBase:false,
            // 是否压缩
            compress:false,
            host:HOST||config.dev.host,
            port:PORT||config.dev.port,
        //    是否自动打开浏览器
            open:config.dev.autoOpenBrowser,
            // 编译出错时是否有提示
            overlay:config.dev.errorOverlay?{warning:false,errors:true}:false,
            //静态内容的路径,此路径下的打包文件可在浏览器中访问
            publicPath:config.dev.assetsPublicPath,
            //接口的代理
            proxy:config.dev.proxyTable,
            // 启用quiet后,除了初始启动信息外的任务内容都不会被打印到控制台。这也意味着来自webpack的错误或警告在控制台不可见
            quite:true,
            // 监视文件的选项
            watchOptions:{
                poll:config.dev.poll
            }
        },
        plugins:[
            // DefinePlugin允许创建一个在编译时可以配置的全局常量。这里生成了一个当前环境的常量
            new webpack.DefinePlugin({
                'progress.env':require('../config/dev.env')
            }),
            //模块热替换插件 修改模块是不需要刷新页面
            new webpack.HotModuleReplacementPlugin(),
            //当开启HMR的时候使用该插件会显示模块的相对路径
            new webpack.NamedModulesPlugin(),
            // 在编译出现错误时,使用NoEmitOnErrorsPlugin来跳过输出阶段  这样可以确保输出资源不会包含错误
            new webpack.NoEmitOnErrorsPlugin(),

            new HtmlWebpackPlugin({
                filename:'index.html',
                template:'index.html',
                // 打包后js文件放在body的后面
                inject:true
            }),
            //将static的内容拷贝到开发路径,忽略这个文件夹下的'.XX'的文件
            new CopyWebapckPlugin([
                {
                    from:path.resolve(__dirname,'../static'),
                    to:config.dev.assetsSubDirectory,
                    ignore:['.*']
                }
            ])
        ]
    }
})

webpack.prod.conf.js

/*
 * @Author: yang
 * @Date: 2020-10-17 14:45:20
 * @LastEditors: yang
 * @LastEditTime: 2020-10-17 16:15:15
 * @FilePath: \新建文件夹 (4)\webpack.prod.conf.js
 */
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('./config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.config')
// 资源拷贝的插件
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 把css打包成css文件已link的方式引入包
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 压缩css的包
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
// 压缩js代码的包
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = require('./config/prod.env')
const webpackConfig = merge(baseWebpackConfig,{
    module:{
        //loader配置,可在介绍utils的文章中查看
        rules:utils.styleLoaders({
            sourceMap:config.build.productionSourceMap,
            extract:true,
            usePostCSS:true
        })
    },
    devtool:config.build.productionSourceMap? config.build.devtool:false,
    output:{
        path:config.build.assetsRoot,
        filename:config.build.assetsPath('js/[name].[chunkhash].js'),
        //chunkFilename用于命名那些异步加载的模块,比如通过require.ensure()
        chunkFilename:utils.assetsPath('js/[id].[chunkhash].js')
    },
    plugins:[
        new webpack.DefinePlugin({
            'progress.env':env
        }),
        //压缩js代码
        new UglifyJsPlugin({
            uglifyOptions:{
                compress:{
                    warnings:false
                }
            },
            sourceMap:config.build.productionSourceMap,
            parallel:true
        }),
        // 从所有额外的chunk(additional chunk)提取(默认情况下,它仅从初Zchunk(init chunk)中提取)
        // 当使用CommonsChunkPlugin 并且在公共chunk中提取的chunk(来自ExtractText)时  allChunks **必须设置为true
        new ExtractTextPlugin({
            filename:utils.assetsPath('css/[name].[contenthash].css'),
            allChunks:true,
        }),
        //压缩css
        //优化最小化css代码  如果只简单使用extract-text-plugin可能会造成css重复
        new OptimizeCSSPlugin({
            cssProgressorOptions:config.build.productionSourceMap?
            {safe:true,map:{inline:false}}:{safe:true}
        }),
        //在dist目录生成html文件
        //将产品文件的引用注入到index.html
        new HtmlWebpackPlugin({
            filename:config.build.index,
            template:'index.html',
            inject:true,
            minify:{
                // 删除index.html中的注释
                removeComments:true,
                // 删除index.html中的空格
                collapseWhitespace:true,
                //删除各种html标签属性值的双引号
                removeAttributeQuotes:true,
            },
            // 注入依赖的时候按照依赖先后顺序进行注入,比如 需要先注入vendor.js,再注入app.js
            chunksSortMode:"dependency"
        }),
        // 该插件会根据模块的相对路径生成一个四位数的hash作为模块id 建议用于生产环境
         new webpack.HashedModuleIdsPlugin(),
        //  去webpack打包时的一个取舍是将bundle中各个模块单独打包成闭包 这些打包函数使你的js在浏览器中处理的很慢,
        //相比之下,一些工具 想Closure Compiler和RollupJS可以提升(hoist)或者预编译所有模块到一个闭包中,提升你的代码在浏览器中的执行速度
        new webpack.optimize.ModuleConcatenationPlugin(),
        // 将第三方的包分离出来 将所有从node-modules中引的js提取到vendor.js,即抽取文件
        new webpack.optimize.CommonsChunkPlugin({
            name:'vendor',
            minChunks(module){
                return(
                    module.resource&&/\.js$/.test(module.resource)&&module.resource.indexof(path.join(__dirname,'../node_modules'))===0
                )
            }
        }),
        // 为了避免每次更改项目代码时导致venderchunk的chunkHash改变,我们还会单独生成一个manifestchunk
        new webpack.optimize.CommonsChunkPlugin({
            name:'manifest',
            minChunks:Infinity
        }),
        // 我们主要逻辑的js文件
        new webpack.optimize.CommonsChunkPlugin({
            name:'app',
            async:'vendor-async',
            children:true,
            minChunks:3
        }),
        // 拷贝资源  将static文件夹里面的静态资源复制到dist/static
        new CopyWebpackPlugin([
            {
                from:path.resolve(__dirname,'../static'),
                to:config.build.assetsSubDirectory,
                ignore:['.*']
            }
        ])
    ]
})
module.exports = webpackConfig

HappyCodingTop
526 声望847 粉丝

Talk is cheap, show the code!!