vue-cli脚手架之开发环境的webpack配置—webpack.base.conf.js

webpack相关的重要配置文件将在这一节给出。webpack水很深,在此先弄清楚原配文件内容的含义,后续可以自己根据实际情况配置。

最近把项目的所有配置文件都研究学习了一下,发现学无止境,永远都学不完呀,慢慢学吧。
网上的一个git大神的一个项目。目录结构如下,我们主要看build/和package.json

image

package.json中配置了项目的基本信息、脚本以及包信息。我们主要看下脚本,如下

image

--------------------------------dev-----------------------------------
先看“dev”(npm run dev),命令是:

cross-env NODE_ENV=online node build/dev-server.js

cross-env 跨平台设置及使用环境变量 ,即将环境变量 NODE_ENV设置为online
node build/dev-server.js:执行build/dev-server.js
分析dev-server.js:
首先看一下build-server头部引入的文件

image

config是一个配置文件,后面会解析一下这个文件
path:处理与路径转换
express:Web应用框架
webpack:打包工具
opn: 用于打开浏览器
http-proxy-middleware 用于把请求代理转发到其他服务器的中间件
以上这些都是直接安装使用的
-->./webpack.dev.conf : 自己写的的webpack配置文件,后面会详细解析

config js

var path = require('path')
module.exports = {
    build: {
        // 修改环境为生产环境
        env: {
            NODE_ENV: '"production"'
        },
        //index  即打包后会生成启动首页,index.html
        index: path.resolve(__dirname, '../elm/index.html'),
        // 打包后 生成的资源存根目录
        assetsRoot: path.resolve(__dirname, '../elm'),
        //资源子目录
        assetsSubDirectory: 'static',
        //这个是通过http服务器运行的url路径,一般都是根目录
        assetsPublicPath: '/elm/',
        productionSourceMap: true,//设置成false,打包的体积会减小80%
        // 是否开启Gzip压缩
        productionGzip: false,
        // 需要使用 gzip 压缩的文件扩展名
        productionGzipExtensions: ['js', 'css']
    },
    dev: {
        env: {
            NODE_ENV: '"development"'
        },
        port: 8000,// 运行测试页面的端口
        assetsSubDirectory: 'static',// 编译输出的二级目录
        assetsPublicPath: '/',// 编译发布的根目录,可配置为资源服务器域名或 CDN 域名
        context: [ //代理路径
            '/shopping',
            '/ugc',
            '/v1',
            '/v2',
            '/v3',
            '/v4',
            '/bos',
            '/member',
            '/promotion',
            '/eus',
            '/payapi',
            '/img',
        ],
        proxypath: 'http://cangdu.org:8001',// 需要 proxyTable 代理的接口(可跨域)
        cssSourceMap: false
    }
}

config配置的目的都是为了服务webpack的配置,给不同的编译条件提供配置
build: 打包上线的配置;可以看出会将打包好的文件放在elm目录下,启动的首页index,html,资源目录是elm/static
dev:本地启动测试的配置;运行的端口8000

webpack.dev.conf

webpack.dev中也引入了3个js,如下图

image

config.js已经在上部分看过,下面看下utils;

utils.js

var path = require('path')
var config = require('../config')
//主要是为了抽离css样式,防止将样式打包在js中引起页面样式加载错乱的现象.
var ExtractTextPlugin = require('extract-text-webpack-plugin')
exports.assetsPath = function(_path) {
    var assetsSubDirectory = process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory
    //path.posix.join是path.join的一种兼容性写法,它的作用是路径的拼接,这里返回的是"XXX/XXX"
    // posix是跨平台兼容
    return path.posix.join(assetsSubDirectory, _path)
}
//生成cssloader表
exports.cssLoaders = function(options) {
    options = options || {}
    function generateLoaders(loaders) {
        var sourceLoader = loaders.map(function(loader) {
            var extraParamChar
            if (/\?/.test(loader)) {
                loader = loader.replace(/\?/, '-loader?')
                extraParamChar = '&'
            } else {
                loader = loader + '-loader'
                extraParamChar = '?'
            }
            return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
        }).join('!')
        if (options.extract) {
            return ExtractTextPlugin.extract('vue-style-loader', sourceLoader)
        } else {
            return ['vue-style-loader', sourceLoader].join('!')
        }
    }
    return {
        css: generateLoaders(['css']),
        postcss: generateLoaders(['css']),
        less: generateLoaders(['css', 'less']),
        sass: generateLoaders(['css', 'sass?indentedSyntax']),
        scss: generateLoaders(['css', 'sass']),
        stylus: generateLoaders(['css', 'stylus']),
        styl: generateLoaders(['css', 'stylus'])
    }
}
//生成styleLoaders表
exports.styleLoaders = function(options) {
    var output = []
    var loaders = exports.cssLoaders(options)
    for (var extension in loaders) {
        var loader = loaders[extension]
        output.push({
            test: new RegExp('\\.' + extension + '$'),
            loader: loader
        })
    }
    return output
}

utils比较简单,就是动态生成cssloader表,很多时候我们都是直接列出来,这边就是通过js的方式动态生成,其实是一样的。

webpack.base.conf

var path = require('path')
var config = require('../config')
var utils = require('./utils')
var projectRoot = path.resolve(__dirname, '../')

var env = process.env.NODE_ENV

var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap)
var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap)
var useCssSourceMap = cssSourceMapDev || cssSourceMapProd
module.exports = {
    //入口文件
    entry: {
        app: './src/main.js'
    },
    //导出配置
    output: {
        path: config.build.assetsRoot,
        publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
        filename: '[name].js'// [name]---entry中的key,即app.js
    },
    // 配置webpack如何寻找模块对应的文件。
    resolve: {
        //配置在查找文件的过程中用到的后缀列表
        extensions: ['', '.js', '.vue', '.less', '.css', '.scss'],
        //如果webpack 在 resolve.root 或者 resolve.modulesDirectories 实在找不到某个模块了,会去这个目录中找
        fallback: [path.join(__dirname, '../node_modules')],
        //alias通过别名来把原导入路径映射成一个新的导入路径
        alias: {
            'vue$': 'vue/dist/vue.common.js',
            'src': path.resolve(__dirname, '../src'),
            'assets': path.resolve(__dirname, '../src/assets'),
            'components': path.resolve(__dirname, '../src/components')
        }
    },
    //跟 resolve很像,但是是为loaders准备的。
    resolveLoader: {
        fallback: [path.join(__dirname, '../node_modules')]
    },
    //正常模块的配置
    module: {
        // loader
        loaders: [{
            test: /\.vue$/,
            loader: 'vue'
        }, {
            test: /\.js$/,
            loader: 'babel',
            include: projectRoot,
            exclude: /node_modules/
        }, {
            test: /\.json$/,
            loader: 'json'
        }, {
            test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
            loader: 'url',
            query: {
                limit: 10000,
                name: utils.assetsPath('img/[name].[ext]')
            }
        }, {
            test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
            loader: 'url',
            query: {
                limit: 10000,
                name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
            }
        }]
    },
    vue: {
        loaders: utils.cssLoaders({
            sourceMap: useCssSourceMap
        }),
        postcss: [
            require('autoprefixer')({
                browsers: ['last 10 versions']
            })
        ]
    }
}

以上配置就是开发环境和测试环境公用的配置,具体的意思见代码

webpack.dev.conf

var config = require('../config')
var webpack = require('webpack')
var merge = require('webpack-merge')//将对象or数组合并
var utils = require('./utils')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')

Object.keys(baseWebpackConfig.entry).forEach(function(name) {
    baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})
// baseWebpackConfig  entry: { app: [ './build/dev-client', './src/main.js' ]}

module.exports = merge(baseWebpackConfig, {
    //module 配置如何处理模块。
    module: {
        loaders: utils.styleLoaders({
            sourceMap: config.dev.cssSourceMap
        })
    },
    // 使用eval-source-map,方便代码调试
    devtool: '#eval-source-map',
    //配置插件
    plugins: [
        // 可以简单的理解为将process.env 变成全局变量,可以在其他地方引用
        new webpack.DefinePlugin({
            'process.env': config.dev.env
        }),
        //根据模块调用次数,给模块分配ids,常被调用的ids分配更短的id,使得ids可预测,降低文件大小
        new webpack.optimize.OccurenceOrderPlugin(),
        //热更新--无刷新实现代码更新
        new webpack.HotModuleReplacementPlugin(),
        //跳过编译时出错的代码并记录,使编译后运行时的包不会发生错误
        new webpack.NoErrorsPlugin(),
        //生成html模版文件
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'index.html',
            favicon: 'favicon.ico',
            inject: true
        })
    ]
})

--------------------------------build----------------------------------
看“ build”(npm run build),命令是:

node build/build.js

webpack.prod.conf

var path = require('path')
var config = require('../config')
var utils = require('./utils')
var webpack = require('webpack')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var env = config.build.env
var webpackConfig = merge(baseWebpackConfig, {
    module: {
        loaders: utils.styleLoaders({
            sourceMap: config.build.productionSourceMap,
            extract: true
        })
    },
    output: {
        path: config.build.assetsRoot,
        filename: utils.assetsPath('js/[name].js'),
        chunkFilename: utils.assetsPath('js/[name].[chunkhash].min.js')
    },
    vue: {
        loaders: utils.cssLoaders({
            sourceMap: config.build.productionSourceMap,
            extract: true
        })
    },
    plugins: [
       // 可以简单的理解为将process.env 变成全局变量,可以在其他地方引用
        new webpack.DefinePlugin({
            'process.env': env
        }),
        //对js进行压缩
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        }),
        //根据模块调用次数,给模块分配ids,常被调用的ids分配更短的id,使得ids可预测,降低文件大小
        new webpack.optimize.OccurrenceOrderPlugin(),
        // 抽离css样式,防止将样式打包在js中引起页面样式加载错乱的现象
        new ExtractTextPlugin(utils.assetsPath('css/[name].css')),
        //将生成的html模版和我们在output-filename生成的js产生关系,就是在让html启动引入js
        new HtmlWebpackPlugin({
            filename: config.build.index,
            template: 'index.html',
            inject: true,
            chunksSortMode: 'dependency'
        }),
        // 主要是用来提取第三方库和公共模块,即将公共模块提取到vendor.js中,
        //避免首屏加载的bundle文件或者按需加载的bundle文件体积过大,
        // 从而导致加载时间过长,着实是优化的一把利器。
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vendor',
            minChunks: function(module, count) {
                // any required modules inside node_modules are extracted to vendor
                return (
                    module.resource &&
                    /\.js$/.test(module.resource) &&
                    module.resource.indexOf(
                        path.join(__dirname, '../node_modules')
                    ) === 0
                )
            }
        }),
        // 将webpack的运行文件、第三方库和自定义公共模块单独抽离出
        // 避免webpack运行文件改变导致vendor.js改变,从而需要用户去重新加载
        new webpack.optimize.CommonsChunkPlugin({
            name: 'manifest',
            chunks: ['vendor']
        })
    ]
})
//是否使用productionGzip
if (config.build.productionGzip) {
   //压缩
    var CompressionWebpackPlugin = require('compression-webpack-plugin')
  //配置中添加压缩的插件
    webpackConfig.plugins.push(
        new CompressionWebpackPlugin({
            asset: '[path].gz[query]',
            algorithm: 'gzip',
            test: new RegExp(
                '\\.(' +
                config.build.productionGzipExtensions.join('|') +
                ')$'
            ),
            threshold: 10240,
            minRatio: 0.8
        })
    )
}
module.exports = webpackConfig

build.js

require('shelljs/global')//允许直接在脚本中写shell命令
env.NODE_ENV = 'production'

var path = require('path')
var config = require('../config')
var ora = require('ora')//实现命令行环境的loading效果
var webpack = require('webpack')
var webpackConfig = require('./webpack.prod.conf')

var spinner = ora('building for production...')
spinner.start()//loading 开启

var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
// shell命令
rm('-rf', assetsPath)
mkdir('-p', assetsPath)
cp('-R', 'static/*', assetsPath)
//开始打包
webpack(webpackConfig, function(err, stats) {
    spinner.stop()//loading 结束
    if (err) throw err
    process.stdout.write(stats.toString({
        colors: true,
        modules: false,
        children: false,
        chunks: false,
        chunkModules: false
    }) + '\n')
})

webpack.base.conf.js:

配置vue开发环境的webpack配置,处理各种文件(js、css、vue、图片、视频…)

// An highlighted block
'use strict'//js严格模式执行
const path = require('path')//引入node.js路径模块
const utils = require('./utils')//引入utils工具模块,主要处理css-loader和vue-style-loader
const config = require('../config')//引入config文件夹下的index.js文件
const vueLoaderConfig = require('./vue-loader.conf')//引入vue-loader工具模块

function resolve (dir) {//返回当前目录的平行目录的路径
  return path.join(__dirname, '..', dir)
}

module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {//输入
    app: './src/main.js'//入口文件为main.js
  },
  output: {//输出
    path: config.build.assetsRoot,//打包后文件输出路径,看看自己的index.js中build配置中的assetsRoot是啥目录
    filename: '[name].js',//输出文件名称默认使用原名
    publicPath: process.env.NODE_ENV === 'production'//真正的文件引用路径,请看自己的index.js中build配置中写的啥
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {//决定要做的事情
    extensions: ['.js', '.vue', '.json'],//省略扩展名,也就是说当使用.js .vue .json文件导入可以省略后缀名
    alias: {
      'vue$': 'vue/dist/vue.esm.js',//$符号指精确匹配,路径和文件名要详细
      '@': resolve('src'),//resolve('src‘)//resolve('src')指的是项目根目录中的src文件夹目录,导入文件的时候路径可以这样简写 import somejs from "@/some.js"就可以导入指定文件
    }
  },
//用来解析不同模块
  module: {
    rules: [
      {
        test: /\.vue$/,//正则表达式,表示当前loader能检测.vue类型的文件(分析这个正则:/标记正则表达式的开始和结束,指的是在开始和结尾处,否则要使用/就得转义\/;\.表示., 此处的\将.标记为原意字符;$是正则表达式的结束
        loader: 'vue-loader',//对vue文件使用vue-loader,该loader是vue单文件组件的实现核心,解析.vue文件
        options: vueLoaderConfig//将vueLoaderConfig当做参数传递给vue-loader,解析css相关文件
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',//对js文件使用babel-loader转码,该插件用来解析es6等代码
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]//指明src文件夹 test文件夹 client文件夹下的js文件要使用该loader
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,//这些格式结尾的图片文件
        loader: 'url-loader',//图片文件使用url-loader插件,将图片转为base64格式字符串
        options: {
          limit: 10000,//10000个字节以下的文件才用来转为dataUrl
          name: utils.assetsPath('img/[name].[hash:7].[ext]')//超过10000字节的图片,就按照制定规则设置生成的图片名称,可以看到用了7位hash码来标记,.ext文件是一种索引式文件系统
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,//音频 视频类文件
        loader: 'url-loader',//也是用url-loader
        options: {
          limit: 10000,//10000个字节以下的文件才进行转换
          name: utils.assetsPath('media/[name].[hash:7].[ext]')//这个name到底是给谁起的啊喂,给超过limit字节限制的文件起的
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,//处理字体相关
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      },
      /*添加sass开始*/
      {
        test:/\.(woff2?|eot|ttf|otf)(\?.*)?$/,//这个可以在vue组件中用sass scss等...
        loaders:['style','css','sass'],
      }
      /*添加sass结束*/
    ]
  },
  node: {//一个对象,每个属性都是node.js全局变量或模块的名称,value为empty表示提供空对象
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    setImmediate: false,//false表示什么都不提供,话说参数setImmediate表示异步递归???需要查阅node文档了
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,711评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,079评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,194评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,089评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,197评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,306评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,338评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,119评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,541评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,846评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,014评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,694评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,322评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,026评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,257评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,863评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,895评论 2 351

推荐阅读更多精彩内容