vue-cli是vue官方出品的一个脚手架工具,通过vue-cli可以快速的搭建一个vue的SPA应用的开发框架。其内部提供了多种vue-js-template,作用就是通过相应的template去配置生成项目初期的内容。其中webpack模板提供了全面丰富的webpack配置,包括热重载、单元测试、静态检查以及css提取等功能。
首先用vue-cli webpack模板生成一个vue的项目,如下:
在确保全局安装了 vue-cli 的前提下,运行下面的命令
1 vue init webpack webpack_study
项目的文件结构 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 ├── build/ │ └── ... ├── config/ │ ├── index.js │ └── ... ├── src/ │ ├── main.js │ ├── App.vue │ ├── components/ │ │ └── ... │ └── assets/ │ └── ... ├── static/ ├── test / │ └── unit/ │ │ ├── specs/ │ │ ├── index.js │ │ └── karma.conf.js │ └── e2e/ │ │ ├── specs/ │ │ ├── custom-assertions/ │ │ ├── runner.js │ │ └── nightwatch.conf.js ├── .babelrc ├── .editorconfig ├── .eslintrc.js ├── index.html └── package.json
进入项目目录,打开package.json 查看其script,
1 2 3 4 5 "scripts" : { "dev" : "node build/dev-server.js" , "start" : "node build/dev-server.js" , "build" : "node build/build.js" },
开发环境中的配置 在运行 npm run dev
的时候可以发现,开发环境的入口文件时 build/dev-server.js
dev-server.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 require ('./check-versions' )()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 = 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 : () => {}, heartbeat : 2000 }) 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:' + portvar _resolvevar readyPromise = new Promise (resolve => { _resolve = resolve }) console .log ('> Starting dev server...' )devMiddleware.waitUntilValid (() => { console .log ('> Listening at ' + uri + '\n' ) if (autoOpenBrowser && process.env .NODE_ENV !== 'testing' ) { opn (uri) } _resolve () }) var server = app.listen (port)module .exports = { ready : readyPromise, close : () => { server.close () } }
综上,可以发现在dev-server.js 中主要做了这几件事情:
获取webpack开发环境的配置文件,并启用webpack编译,获得编译对象compiler
使用webpack-dev-middleware中间件,将编译后的文件写入内存
使用webpack-hot-middleware开启hot-reload功能
启动webpack的服务,并将上述中间件挂在express的服务上
webpack模板中并没有使用webpack-dev-server作为开发环境的服务器,而是启动了express的服务并应用了webpack-dev-middleware和webpack-hot-middleware中间件。两种方式实现的功能是一样的,webpack-dev-server的本质也是一个轻量级的express服务,稍后会有webpack-dev-server的原理解析。
webpack.dev.conf.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 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 : '#cheap-module-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.dev.conf.js中定义了只有在dev环境下用到的一些配置,包括sourcemap以及dev环境下常用的插件集合。在此基础上,通过引入webpack.base.conf.js,将两者merge。
webpack.base.conf.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 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 : /\.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' , options : { limit : 10000 , name : utils.assetsPath ('img/[name].[hash:7].[ext]' ) } }, { test : /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/ , loader : 'url-loader' , options : { limit : 10000 , name : utils.assetsPath ('media/[name].[hash:7].[ext]' ) } }, { test : /\.(woff2?|eot|ttf|otf)(\?.*)?$/ , loader : 'url-loader' , options : { limit : 10000 , name : utils.assetsPath ('fonts/[name].[hash:7].[ext]' ) } } ] } }
webpack.base.conf.js中定义了webpack编译的一些公共配置。
config/index.js中定义了生产环境以及开发环境的配置文件,主要是根据该文件,生成webpack配置文件供编译时使用。比如端口号、代理配置等
util.js主要定义了几个工具函数,包括生成静态资源路径,生成styleLoaders的配置等。
生产环境中的配置 执行 npm run build
的时候,可以发现生产环境的入口是build/build.js
build/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 require ('./check-versions' )()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' )) }) })
build/build.js主要完成了删除之前的文件,执行编译动作,将文件写入指定文件位置,并在控制台上输出信息的任务。
webpack.prod.conf.js中定义了在执行build命令时webpack的一些配置。与webpack.dev.conf.js的主要差别在于一些插件的使用。比如在prod环境下添加了一些压缩文件、提取公共文件、复制静态资源一类的插件。这些都是在生产环境下才会应用的一些配置。
总结 通读vue-cli webpack模板的配置可以发现,其配置文件主要分为了两部分,一是dev环境下,二是prod环境下的配置文件。在此基础还抽离出了不同环境下公共的webpack.base.conf.js。再者config.js抽离了两环境下的一些配置,这样用户就不用去具体的webpack的配置文件中去设置相关配置,只需在config.js中即可完成。util.js提供了相关的工具函数,用于更简便的输出webpack配置文件。
参考文章:
vue-cli webpack模板中文文档
vue-cli#2.0 webpack 配置分析
vue-cli webpack配置分析