我们一般都是直接使用vue-cli
生成我们的vue
基础项目结构,今天我们就来分析一下其webpack
配置信息。
目录
打包过程中主要分为 dev
build
两个过程,因此webpack的配置中也区分了两种情况。由于要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的 webpack 配置。
─build
│ ├─build.js
│ ├─check-versions.js
│ ├─logo.png
│ ├─utils.js
│ ├─vue-loader.conf.js
│ ├─webpack.base.conf.js
│ ├─webpack.dev.conf.js
│ ├─webpack.prod.conf.js
├─config
│ ├─dev.env.js
│ ├─index.js
│ ├─prod.env.js
├─…
└─package.json
命令分析
// package.json
我们主要来看scripts和下面的依赖包
{
"name": "vuetest",
"version": "1.0.0",
"description": "A Vue.js project",
"author": "zjj19970517 <1392372716@qq.com>",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", // 【1】 再开发环境下执行 build/webpack.dev.conf.js
"start": "npm run dev",
"unit": "jest --config test/unit/jest.conf.js --coverage",
"e2e": "node test/e2e/runner.js",
"test": "npm run unit && npm run e2e",
"lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
"build": "node build/build.js" // 【2】 生产环境下执行 build/build.js
},
"dependencies": {
"vue": "^2.5.2",
"vue-router": "^3.0.1"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-eslint": "^8.2.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-jest": "^21.0.2",
"babel-loader": "^7.1.1",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"chalk": "^2.0.1",
"chromedriver": "^2.27.2",
"copy-webpack-plugin": "^4.0.1",
"cross-spawn": "^5.0.1",
"css-loader": "^0.28.0",
"eslint": "^4.15.0",
"eslint-config-standard": "^10.2.1",
"eslint-friendly-formatter": "^3.0.0",
"eslint-loader": "^1.7.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-node": "^5.2.0",
"eslint-plugin-promise": "^3.4.0",
"eslint-plugin-standard": "^3.0.1",
"eslint-plugin-vue": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"jest": "^22.0.4",
"jest-serializer-vue": "^0.3.0",
"nightwatch": "^0.9.12",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"selenium-server": "^3.0.1",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-jest": "^1.0.2",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
测试的东西先不看,直接看”dev”和”build”。运行”npm run dev”的时候执行的是 build/webpack.dev.conf.js
文件,运行”npm run build”的时候执行的是 build/build.js
文件,我们可以从这两个文件开始进行代码阅读分析。
build文件夹分析
build/webpack.dev.config.js
'use strict'
// 1.引入相关插件和配置
// 2.生成处理各种样式的规则
// 3.配置开发环境,如热更新、监听端口号,是否自动打开浏览器等都在webpack中的devServer中配置完成
// 4.寻找可利用的端口和添加显示程序编译运行时的错误信息。
const utils = require('./utils') // utils 工具函数
const webpack = require('webpack')
const config = require('../config') // 引入配置,默认是index
const merge = require('webpack-merge') // 将基础配置和开发配置或者是生产配置合并在一起
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf') // 引入基础配置
const CopyWebpackPlugin = require('copy-webpack-plugin') //
const HtmlWebpackPlugin = require('html-webpack-plugin') // 自动打包生成index.html
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') // Friendly-errors-webpack-plugin可识别某些类型的webpack错误并清理,汇总和优先化它们以提供更好的开发者体验。
const portfinder = require('portfinder') // 查看闲置接口,默认是8000这个端口,解决端口占用冲突的问题
// 通过process这个全局变量,来获取全局数据
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
// 正式的dev配置,合并基础配置
const devWebpackConfig = merge(baseWebpackConfig, {
// 下面的配置都是开发模式下要使用的
//
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
//自动生成关于css、less、postcss、等的规则
},
// 增加了调试功能
devtool: config.dev.devtool,
// 本地服务
devServer: {
clientLogLevel: 'warning', // 在开发工具(DevTools)的控制台将显示警告消息
historyApiFallback: { // h5的history API处理,任意的 404 响应都可能需要被替代为 index.html。
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true, // 启动热更新模式
contentBase: false, // since we use CopyWebpackPlugin.
compress: true, // 一切服务都启动后,就会使用gzip压缩代码
host: HOST || config.dev.host, // 默认是localhost
port: PORT || config.dev.port, // 指定要监听的端口号
open: config.dev.autoOpenBrowser, // 是否自动打开默认浏览器
overlay: config.dev.errorOverlay // 当出现编译器错误或警告时,在浏览器中显示全屏叠加,覆盖到浏览器的项目页面的上方
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
// 服务器假设运行在 http://localhost:8080 并且output.filename 被设置为bundle.js默认。
// publicPath是"/",所以你的包(束)通过可以 http://localhost:8080/bundle.js 访问。
// 如果config中的index.js dev对象的中的assertsPublicPath设置为"/asserts/"
// 那么文件打开后将通过 http://localhost:8080/asserts/来进行访问
proxy: config.dev.proxyTable,
// 如果你有单独的后端开发服务器API,并且希望在同域名下发送API请求,
// 那么代理某些URL将很有用.简称就是API代理,中间件 需引入 http-proxy-middleware
quiet: true,
// 启用quiet后,除了初始启动信息之外的任何内容都不会被打印到控制台。
// 这也意味着来自的WebPack的错误或警告在控制台不可见。
watchOptions: {
poll: config.dev.poll,
}
// webpack使用文件系统(file system)获取文件改动的通知
},
// 插件配置
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
// 永远不能用在生产模式,模块热更新,修改文件的内容,允许在运行时更新各种模块,而无需进行完全刷新。
new webpack.NamedModulesPlugin(), // // 当进行热更新时,相关文件名会被展示出来
new webpack.NoEmitOnErrorsPlugin(),
// 跳过编译时出错的代码并记录,使编译后运行时的包不会发生错误。
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
// 该插件可自动生成一个 html5 文件或使用模板文件将编译好的代码注入进去
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
// 拷贝文件到静态文件夹
]
})
// 对端口占用冲突的解决
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})
build/webpack.base.conf.js
// 1.配置webpack编译入口
// 2.配置webpack输出路径和命名规则
// 3.配置模块resolve规则
// 4.配置不同类型模块的处理规则
'use strict'
const path = require('path')
const utils = require('./utils') // 工具函数
const config = require('../config') // 配置文件
const vueLoaderConfig = require('./vue-loader.conf') // 工具函数集合
// vue-loader.conf配置文件是用来解决各种css文件的,定义了诸如css,less,sass之类的和样式有关的loader
/**
* 获得绝对路径
* @method resolve
* @param {String} dir 相对于本文件的路径
* @return {String} 绝对路径
*/
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
// rules 中的 eslint 规则
const createLintingRule = () => ({
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
context: path.resolve(__dirname, '../'),
//基础目录(绝对路径),用于从配置中解析入口点和加载程序
entry: {
app: './src/main.js' // 入口文件
},
output: { // 输出
path: config.build.assetsRoot, // 输出的路径如:./dist
filename: '[name].js', // webpack输出bundle文件命名格式
publicPath: process.env.NODE_ENV === 'production' // 正式发布环境下编译输出的上线路径的根路径
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
// 模块resolve的规则
resolve: {
// 自动补全的扩展名
extensions: ['.js', '.vue', '.json'],
// 别名,方便引用模块,例如有了别名之后,
// 省略扩展名,比方说import index from '../js/index'会默认去找index文件,然后找index.js,index.vue,index.json文件
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
// 使用别名 使用上面的resolve函数,意思就是用@代替src的绝对路径
},
module: {
rules: [
// 根据配置,决定是否创建eslint
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.vue$/, // vue
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
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]')
}
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// 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'
}
}
config文件夹
config文件夹下最主要的是index.js 保存着开发环境和生产环境岁需要的一些信息。
// index.js
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
// 开发模式下的配置
dev: {
// Paths
assetsSubDirectory: 'static',
// 二级目录,存放静态资源文件,在dist下
assetsPublicPath: '/',
// 发布路径,如果构建后的产品文件有用于CDN或者放到其他域名服务器,可以在这里设置,当然本地打包,本地浏览一般都将这里设置为"./"
// 设置之后的构建的产品在注入到index.html中就会带上这里的发布路径
proxyTable: {},
// 代理示例: proxy: [{context: ["/auth", "/api"],target: "http://localhost:3000",}]
// Various Dev Server settings
host: 'localhost', // 这个可以被process.env.HOST重写
port: 8080, // process.env.PORT重写 在端口空闲的情况下
autoOpenBrowser: false, // 是否自动打开浏览器
errorOverlay: true, // 是否出现编译器错误或者警告,在浏览器显示全屏叠加,覆盖到浏览器页面上面
notifyOnErrors: true, // 是否允许窗口弹出错误信息
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true, // 是否使用eslint
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false, // 是否让eslint报错显示在浏览器的透明黑色层上面
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map', // 调试的类型
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true, //是否通过将哈希查询附加到文件名来生成具有缓存清除的源映射[疑问,求解]
cssSourceMap: true // 开发环境下,显示 cssSourceMap
},
// 生产环境
build: {
// 获得绝对路径,inde.html的模板文件
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'), // 获得dist文件夹的绝对路径
assetsSubDirectory: 'static', // 二级目录
assetsPublicPath: '/',
/**
* Source Maps
*/
productionSourceMap: true, // production环境下生成sourceMap文件
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false, // 是否压缩
productionGzipExtensions: ['js', 'css'], // gzip模式下需要压缩的文件的扩展名,设置js、css之后就只会对js和css文件进行压
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
// 是否展示webpack构建打包之后的分析报告
}
}
// prod.env.js
'use strict'
// 生产环境的环境变量的配置
module.exports = {
NODE_ENV: '"production"'
}
// dev.env.js
'use strict'
// 该文件主要来设置开发环境变量。
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
// 引入生产环境配置
// 合并两个环境变量的配置,然后暴露出去
module.exports = merge(prodEnv, {
NODE_ENV: '"development"' // 配置NODE_ENV来决定开发环境
})
// 这个就是用来上线的时候用到,来决定是开发环境还是生产环境,来进行相关的配置解决方案
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。