1.文件结构
├─build
│ ├─build.js
│ ├─check-versions.js
│ ├─dev-client.js
│ ├─dev-server.js
│ ├─utils.js
│ ├─vue-loader.conf.js
│ ├─webpack.base.conf.js
│ ├─webpack.dev.conf.js
│ ├─webpack.prod.conf.js
│ └─webpack.test.conf.js
├─config
│ ├─dev.env.js
│ ├─index.js
│ ├─prod.env.js
│ └─test.env.js
├─...
└─package.json
2.package.json文件
package.json里面的scripts字段:
"scripts": {
"dev": "node build/dev-server.js",
"build": "node build/build.js",
"unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
"e2e": "node test/e2e/runner.js",
"test": "npm run unit && npm run e2e",
"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs"
}
运行”npm run dev”
:执行build/dev-server.js
文件
运行”npm run build”
:执行build/build.js
文件
3.build文件夹分析
build/dev-server.js
该文件主要作用:
- 检查node和npm的版本、引入相关插件和配置
- webpack对源码进行编译打包并返回compiler对象
- 创建express服务器
- 配置开发中间件(webpack-dev-middleware)和热重载中间件(webpack-hot-middleware)
- 挂载代理服务和中间件
- 配置静态资源
- 启动服务器监听特定端口(8080)
- 自动打开浏览器并打开特定网址(localhost:8080)
说明:express
服务器提供静态文件服务,不过它还使用了http-proxy-middleware
,一个http请求代理的中间件。前端开发过程中需要使用到后台的API的话,可以通过配置proxyTable
来将相应的后台请求代理到专用的API服务器。
代码详细注释:
// 检查NodeJS和npm的版本
require('./check-versions')()
// 获取基本配置
var config = require('../config')
// 如果Node的环境变量中没有设置当前的环境(NODE_ENV),则使用config中的dev环境配置作为当前的环境
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
}
// opn是一个可以调用默认软件打开网址、图片、文件等内容的插件
// 这里用它来调用默认浏览器打开dev-server监听的端口,例如:localhost:8080
var opn = require('opn')
var path = require('path')
var express = require('express')
var webpack = require('webpack')
// http-proxy-middleware是一个express中间件,用于将http请求代理到其他服务器
// 例:localhost:8080/api/xxx --> localhost:3000/api/xxx
// 这里使用该插件可以将前端开发中涉及到的请求代理到提供服务的后台服务器上,方便与服务器对接
var proxyMiddleware = require('http-proxy-middleware')
// 开发环境下的webpack配置
var webpackConfig = require('./webpack.dev.conf')
// dev-server 监听的端口,如果没有在命令行传入端口号,则使用config.dev.port设置的端口,例如8080
var port = process.env.PORT || config.dev.port
// 用于判断是否要自动打开浏览器的布尔变量,当配置文件中没有设置自动打开浏览器的时候其值为 false
var autoOpenBrowser = !!config.dev.autoOpenBrowser
// HTTP代理表,指定规则,将某些API请求代理到相应的服务器
var proxyTable = config.dev.proxyTable
// 创建express服务器
var app = express()
// webpack根据配置开始编译打包源码并返回compiler对象
var compiler = webpack(webpackConfig)
// webpack-dev-middleware将webpack编译打包后得到的产品文件存放在内存中而没有写进磁盘
// 将这个中间件挂到express上使用之后即可提供这些编译后的产品文件服务
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath, // 设置访问路径为webpack配置中的output里面所对应的路径
quiet: true // 设置为true,使其不要在控制台输出日志
})
// webpack-hot-middleware,用于实现热重载功能的中间件
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
log: false, // 关闭控制台的日志输出
heartbeat: 2000 // 发送心跳包的频率
})
// webpack(重新)编译打包完成后并将js、css等文件inject到html文件之后,通过热重载中间件强制页面刷新
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
// 根据 proxyTable 中的代理请求配置来设置express服务器的http代理规则
Object.keys(proxyTable).forEach(function (context) {
var options = proxyTable[context]
// 格式化options,例如将'www.example.com'变成{ target: 'www.example.com' }
if (typeof options === 'string') {
options = { target: options }
}
app.use(proxyMiddleware(options.filter || context, options))
})
// handle fallback for HTML5 history API
// 重定向不存在的URL,用于支持SPA(单页应用)
// 例如使用vue-router并开启了history模式
app.use(require('connect-history-api-fallback')())
// serve webpack bundle output
// 挂载webpack-dev-middleware中间件,提供webpack编译打包后的产品文件服务
app.use(devMiddleware)
// enable hot-reload and state-preserving
// compilation error display
// 挂载热重载中间件
app.use(hotMiddleware)
// serve pure static assets
// 提供static文件夹上的静态文件服务
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
app.use(staticPath, express.static('./static'))
// 访问链接
var uri = 'http://localhost:' + port
// 创建promise,在应用服务启动之后resolve
// 便于外部文件require了这个dev-server之后的代码编写
var _resolve
var readyPromise = new Promise(resolve => {
_resolve = resolve
})
console.log('> Starting dev server...')
// webpack-dev-middleware等待webpack完成所有编译打包之后输出提示语到控制台,表明服务正式启动
// 服务正式启动才自动打开浏览器进入页面
devMiddleware.waitUntilValid(() => {
console.log('> Listening at ' + uri + '\n')
// when env is testing, don't need open it
if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
opn(uri)
}
_resolve()
})
// 启动express服务器并监听相应的端口
var server = app.listen(port)
// 暴露本模块的功能给外部使用,例如下面这种用法
// var devServer = require('./build/dev-server')
// devServer.ready.then(() => {...})
// if (...) { devServer.close() }
module.exports = {
ready: readyPromise,
close: () => {
server.close()
}
}
build/webpack.base.conf.js
主要作用:
- 配置webpack编译入口
- 配置webpack输出路径和命名规则
- 配置模块resolve规则
- 配置不同类型模块的处理规则
说明: 这个配置里面只配置了.js、.vue、图片、字体等几类文件的处理规则,如果需要处理其他文件可以在module.rules里面另行配置。
代码详细注释:
var path = require('path')
var fs = require('fs')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
// 获取绝对路径
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
// webpack入口文件
entry: {
app: './src/main.js'
},
// webpack输出路径和命名规则
output: {
// webpack输出的目标文件夹路径(例如:/dist)
path: config.build.assetsRoot,
// webpack输出bundle文件命名格式
filename: '[name].js',
// webpack编译输出的发布路径(例如'//cdn.xxx.com/app/')
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
// 模块resolve的规则
resolve: {
extensions: ['.js', '.vue', '.json'],
// 别名,方便引用模块,例如有了别名之后,
// import Vue from 'vue/dist/vue.common.js'可以写成 import Vue from 'vue'
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
},
symlinks: false
},
// 不同类型模块的处理规则
module: {
rules: [
{// 对src和test文件夹下的.js和.vue文件使用eslint-loader进行代码规范检查
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter')
}
},
{// 对所有.vue文件使用vue-loader进行编译
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{// 对src和test文件夹下的.js文件使用babel-loader将es6+的代码转成es5
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
},
{// 对图片资源文件使用url-loader
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
// 小于10K的图片转成base64编码的dataURL字符串写到代码中
limit: 10000,
// 其他的图片转移到静态资源文件夹
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{// 对多媒体资源文件使用url-loader
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
// 小于10K的资源转成base64编码的dataURL字符串写到代码中
limit: 10000,
// 其他的资源转移到静态资源文件夹
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{// 对字体资源文件使用url-loader
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
// 小于10K的资源转成base64编码的dataURL字符串写到代码中
limit: 10000,
// 其他的资源转移到静态资源文件夹
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
}
}
build/webpack.dev.conf.js
主要作用:
- 将webpack的热重载客户端代码添加到每个entry对应的应用
- 合并基础的webpack配置
- 配置样式文件的处理规则,styleLoaders
- 配置Source Maps
- 配置webpack插件
详细代码注释:
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
// webpack-merge是一个可以合并数组和对象的插件
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
// html-webpack-plugin用于将webpack编译打包后的产品文件注入到html模板中
// 即自动在index.html里面加上和