Vue-cli webpack配置分析 分析不包括check-versions.js
文件,因为check-versions.js
是检测npm
和node
版本,不涉及webpack
,所以就没有对check-versions.js
进行分析。同时,也不包括测试部分的代码,该分析只是针对开发和生产环境的webpack
配置进行分析。
vue-cli 版本 2.8.1
入口 从package.json
可以看到开发和生产环境的入口。
1 2 3 4 "scripts" : { "dev" : "node build/dev-server.js" , "build" : "node build/build.js" }
开发环境 开发环境的入口文件是 build/dev-server.js 。
dev-server.js 该文件中,使用express作为后端框架,结合一些关于webpack的中间件,搭建了一个开发环境。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 var config = require ('../config' )if (!process.env .NODE_ENV ) { process.env .NODE_ENV = JSON .parse (config.dev .env .NODE_ENV ) } var opn = require ('opn' )var path = require ('path' )var express = require ('express' )var webpack = require ('webpack' )var proxyMiddleware = require ('http-proxy-middleware' )var webpackConfig = process.env .NODE_ENV === 'testing' ? require ('./webpack.prod.conf' ) : require ('./webpack.dev.conf' ) var port = process.env .PORT || config.dev .port var autoOpenBrowser = !!config.dev .autoOpenBrowser var proxyTable = config.dev .proxyTable var app = express ()var compiler = webpack (webpackConfig)var devMiddleware = require ('webpack-dev-middleware' )(compiler, { publicPath : webpackConfig.output .publicPath , quiet : true }) var hotMiddleware = require ('webpack-hot-middleware' )(compiler, { log : () => {} }) compiler.plugin ('compilation' , function (compilation ) { compilation.plugin ('html-webpack-plugin-after-emit' , function (data, cb ) { hotMiddleware.publish ({ action : 'reload' }) cb () }) }) Object .keys (proxyTable).forEach (function (context ) { var options = proxyTable[context] if (typeof options === 'string' ) { options = { target : options } } app.use (proxyMiddleware (options.filter || context, options)) }) app.use (require ('connect-history-api-fallback' )()) app.use (devMiddleware) app.use (hotMiddleware) var staticPath = path.posix .join (config.dev .assetsPublicPath , config.dev .assetsSubDirectory )app.use (staticPath, express.static ('./static' )) var uri = 'http://localhost:' + portdevMiddleware.waitUntilValid (function ( ) { console .log ('> Listening at ' + uri + '\n' ) }) module .exports = app.listen (port, function (err ) { if (err) { console .log (err) return } if (autoOpenBrowser && process.env .NODE_ENV !== 'testing' ) { opn (uri) } })
webpack.dev.conf.js dev-server.js
中使用了webpack.dev.conf.js
文件,该文件是开发环境中webpack的配置入口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 var utils = require ('./utils' )var webpack = require ('webpack' ) var config = require ('../config' ) var merge = require ('webpack-merge' ) var baseWebpackConfig = require ('./webpack.base.conf' ) var HtmlWebpackPlugin = require ('html-webpack-plugin' ) var FriendlyErrorsPlugin = require ('friendly-errors-webpack-plugin' )Object .keys (baseWebpackConfig.entry ).forEach (function (name ) { baseWebpackConfig.entry [name] = ['./build/dev-client' ].concat (baseWebpackConfig.entry [name]) }) module .exports = merge (baseWebpackConfig, { module : { rules : utils.styleLoaders ({ sourceMap : config.dev .cssSourceMap }) }, devtool : '#eval-source-map' , plugins : [ new webpack.DefinePlugin ({ 'process.env' : config.dev .env }), new webpack.HotModuleReplacementPlugin (), new webpack.NoEmitOnErrorsPlugin (), new HtmlWebpackPlugin ({ filename : 'index.html' , template : 'index.html' , inject : true }), new FriendlyErrorsPlugin () ] })
webpack.base.conf.js 在webpack.dev.conf.js
中出现webpack.base.conf.js
,这个文件是开发环境和生产环境,甚至测试环境,这些环境的公共webpack配置。可以说,这个文件相当重要。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 var path = require ('path' )var utils = require ('./utils' ) var config = require ('../config' ) var vueLoaderConfig = require ('./vue-loader.conf' )function resolve (dir ) { return path.join (__dirname, '..' , dir) } module .exports = { entry : { app : './src/main.js' }, output : { path : config.build .assetsRoot , filename : '[name].js' , publicPath : process.env .NODE_ENV === 'production' ? config.build .assetsPublicPath : config.dev .assetsPublicPath }, resolve : { extensions : ['.js' , '.vue' , '.json' ], alias : { 'vue$' : 'vue/dist/vue.esm.js' , '@' : resolve ('src' ), } }, module : { rules : [{ test : /\.(js|vue)$/ , loader : 'eslint-loader' , enforce : "pre" , include : [resolve ('src' ), resolve ('test' )], options : { formatter : require ('eslint-friendly-formatter' ) } }, { test : /\.vue$/ , loader : 'vue-loader' , options : vueLoaderConfig }, { test : /\.js$/ , loader : 'babel-loader' , include : [resolve ('src' ), resolve ('test' )] }, { test : /\.(png|jpe?g|gif|svg)(\?.*)?$/ , loader : 'url-loader' , query : { limit : 10000 , name : utils.assetsPath ('img/[name].[hash:7].[ext]' ) } }, { test : /\.(woff2?|eot|ttf|otf)(\?.*)?$/ , loader : 'url-loader' , query : { limit : 10000 , name : utils.assetsPath ('fonts/[name].[hash:7].[ext]' ) } } ] } }
config/index.js 该文件在很多文件中都用到,是主要的配置文件,包含静态文件的路径、是否开启sourceMap等。其中,分为两个部分dev
(开发环境的配置)和build
(生产环境的配置)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 var path = require ('path' )module .exports = { build : { env : require ('./prod.env' ), index : path.resolve (__dirname, '../dist/index.html' ), assetsRoot : path.resolve (__dirname, '../dist' ), assetsSubDirectory : 'static' , assetsPublicPath : '/' , productionSourceMap : true , productionGzip : false , productionGzipExtensions : ['js' , 'css' ], bundleAnalyzerReport : process.env .npm_config_report }, dev : { env : require ('./dev.env' ), port : 3333 , autoOpenBrowser : true , assetsSubDirectory : 'static' , assetsPublicPath : '/' , proxyTable : {}, cssSourceMap : false } }
utils.js utils.js
也是一个被使用频率的文件,这个文件包含了三个工具函数:
生成静态资源的路径
生成 ExtractTextPlugin对象或loader字符串
生成 style-loader的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 var path = require ('path' )var config = require ('../config' )var ExtractTextPlugin = require ('extract-text-webpack-plugin' )exports .assetsPath = function (_path ) { var assetsSubDirectory = process.env .NODE_ENV === 'production' ? config.build .assetsSubDirectory : config.dev .assetsSubDirectory return path.posix .join (assetsSubDirectory, _path) } exports .cssLoaders = function (options ) { options = options || {} var cssLoader = { loader : 'css-loader' , options : { minimize : process.env .NODE_ENV === 'production' , sourceMap : options.sourceMap } } function generateLoaders (loader, loaderOptions) { var loaders = [cssLoader] if (loader) { loaders.push ({ loader : loader + '-loader' , options : Object .assign ({}, loaderOptions, { sourceMap : options.sourceMap }) }) } if (options.extract ) { return ExtractTextPlugin .extract ({ use : loaders, fallback : 'vue-style-loader' }) } else { return ['vue-style-loader' ].concat (loaders) } } return { css : generateLoaders (), postcss : generateLoaders (), less : generateLoaders ('less' ), sass : generateLoaders ('sass' , { indentedSyntax : true }), scss : generateLoaders ('sass' ), stylus : generateLoaders ('stylus' ), styl : generateLoaders ('stylus' ) } } 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 + '$' ), use : loader }) } return output }
生产环境 开发环境的入口文件是build/build.js
。
build.js 该文件,为构建打包文件,会将源码进行构建(编译、压缩等)后打包。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 process.env .NODE_ENV = 'production' var ora = require ('ora' )var rm = require ('rimraf' )var path = require ('path' )var chalk = require ('chalk' )var webpack = require ('webpack' )var config = require ('../config' )var webpackConfig = require ('./webpack.prod.conf' )var spinner = ora ('building for production...' )spinner.start () rm (path.join (config.build .assetsRoot , config.build .assetsSubDirectory ), err => { if (err) throw err webpack (webpackConfig, function (err, stats ) { spinner.stop () if (err) throw err process.stdout .write (stats.toString ({ colors : true , modules : false , children : false , chunks : false , chunkModules : false }) + '\n\n' ) console .log (chalk.cyan (' Build complete.\n' )) console .log (chalk.yellow ( ' Tip: built files are meant to be served over an HTTP server.\n' + ' Opening index.html over file:// won\'t work.\n' )) }) })
webpack.prod.conf 该文件,为生产环境中webpack的配置入口。同时,它也依赖于前面提到的webpack.base.conf.js
、utils.js
和config/index.js
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 var path = require ('path' )var utils = require ('./utils' )var webpack = require ('webpack' )var config = require ('../config' )var merge = require ('webpack-merge' )var baseWebpackConfig = require ('./webpack.base.conf' )var CopyWebpackPlugin = require ('copy-webpack-plugin' )var HtmlWebpackPlugin = require ('html-webpack-plugin' )var ExtractTextPlugin = require ('extract-text-webpack-plugin' )var OptimizeCSSPlugin = require ('optimize-css-assets-webpack-plugin' )var env = process.env .NODE_ENV === 'testing' ? require ('../config/test.env' ) : config.build .env var webpackConfig = merge (baseWebpackConfig, { module : { rules : utils.styleLoaders ({ sourceMap : config.build .productionSourceMap , extract : true }) }, devtool : config.build .productionSourceMap ? '#source-map' : false , output : { path : config.build .assetsRoot , filename : utils.assetsPath ('js/[name].[chunkhash].js' ), chunkFilename : utils.assetsPath ('js/[id].[chunkhash].js' ) }, plugins : [ new webpack.DefinePlugin ({ 'process.env' : env }), new webpack.optimize .UglifyJsPlugin ({ compress : { warnings : false }, sourceMap : true }), new ExtractTextPlugin ({ filename : utils.assetsPath ('css/[name].[contenthash].css' ) }), new OptimizeCSSPlugin (), new HtmlWebpackPlugin ({ filename : process.env .NODE_ENV === 'testing' ? 'index.html' : config.build .index , template : 'index.html' , inject : true , minify : { removeComments : true , collapseWhitespace : true , removeAttributeQuotes : true }, chunksSortMode : 'dependency' }), new webpack.optimize .CommonsChunkPlugin ({ name : 'vendor' , minChunks : function (module , count ) { return ( module .resource && /\.js$/ .test (module .resource ) && module .resource .indexOf ( path.join (__dirname, '../node_modules' ) ) === 0 ) } }), new webpack.optimize .CommonsChunkPlugin ({ name : 'manifest' , chunks : ['vendor' ] }), new CopyWebpackPlugin ([ { from : path.resolve (__dirname, '../static' ), to : config.build .assetsSubDirectory , ignore : ['.*' ] } ]) ] }) 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 }) ) } if (config.build .bundleAnalyzerReport ) { var BundleAnalyzerPlugin = require ('webpack-bundle-analyzer' ).BundleAnalyzerPlugin webpackConfig.plugins .push (new BundleAnalyzerPlugin ()) } module .exports = webpackConfig
其他 如果你觉得在segmentfault的代码阅读体验不好,你可以到我github上将代码clone下来看。
vue-cli-webpack-analysis