Vue-cli有两个文件——buildconfig:build文件包含了脚手架在开发环境和生产环境下webpack该如何配置。config文件则包含了build文件下webpack具体配置的值。换句话说,build下的webpack配置的值要引入config后才能获取到。

一.config文件夹下一共有三个文件:

1.dev.env.js: 导出开发环境名称;

2.prod.env.js: 导出生产环境名称;

3.index.js: 导出不同环境的具体配置;

二.build文件夹下一共有七个文件:

1.build.js: 编译时的入口文件,当执行npm run build时其实就是执行node build/build.js(在package.json中);

2.check-versions.js: 编译代码时执行的确认node和npm版本的文件,如果版本不符,则停止编译;

3.utils.js:这个文件有两个作用,一是作为vue-loader的配置来使用;另一个是用来给开发环境和生产环境配置loader;

4.vue-loader.conf.js:vue-loader的配置,用在webpack.base.conf.js中;

5.webpack.base.conf.js:vue-cli脚手架的基础webpack配置,通过与webpack.dev.conf.js和webpack.prod.conf.js两个配置文件的合并(合并方式我会在下一章来讲)来实现“不重复原则(Don't repeat yourself - DRY),不会在不同的环境中配置相同的代码”。

6.webpack.dev.conf.js:开发环境下的webpack的配置;

7.webpack.prod.conf.js:生产环境下的webpack的配置;

三.config文件

1.prod.env.js:

//导出一个对象,对象有一个当前node环境的属性,值为“production”(生产环境)

module.exports = { NODE_ENV: '"production"'}

2.dev.env.js:

//导出另一个对象,属性为当前的node环境,值为“development”(开发环境)

const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, { NODE_ENV: '"development"'})

    • 这里要着重说一下webpack-merge这个包,这个包的作用是来合并两个配置文件对象并生成一个新的配置文件,有点儿类似于es6的Object.assign()方法。如果合并的过程中遇到冲突的属性,第二个参数的属性值会覆盖第一个参数的属性值。
    • 前面写到webpack.base.conf.js与webpack.dev.conf.js和webpack.prod.conf.js的合并也用到了webpack-merge。Vue-cli将一些通用的配置抽出来放在一个文件内(webpack.base.conf.js),在对不同的环境配置不同的代码,最后使用webpack-merge来进行合并,减少重复代码。

3.index.js:
index.js作为具体的配置值,我觉得没必要把代码贴出来了,大家可以拿上面的的脑图或者自己项目里的文件来结合我后面要说的代码来看。

四.build文件

1.check.versions.js:

//chalk 是一个用来在命令行输出不同颜色文字的包,可以使用chalk.yellow("想添加颜色的文字....")
//来实现改变文字颜色的;
const chalk = require('chalk') //semver 的是一个语义化版本文件的npm包,其实它就是用来控制版本的;
const semver = require('semver')const packageConfig = require('../package.json') //一个用来执行unix命令的包
const shell = require('shelljs') //child_process 是Node.js提供了衍生子进程功能的模块,execSync()方法同步执行一个cmd命令,
//将返回值的调用toString和trim方法
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
} const versionRequirements = [
{ name: 'node', //semver.clean()方法返回一个标准的版本号,切去掉两边的空格,比如semver.clean(" =v1.2.3 ")
//返回"1.2.3",此外semver还有vaild,satisfies,gt,lt等方法,
//这里查看https://npm.taobao.org/package/semver可以看到更多关于semver方法的内容
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
//shell.which方法是去环境变量搜索有没有参数这个命令
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
//执行"npm --version"命令
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
}
)} //后面这部分代码就比较好理解了
module.exports = function () { const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
   const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
  warnings.push(mod.name + ': ' + chalk.red(mod.currentVersion) + ' should be ' + chalk.green(mod.versionRequirement) ) } }
if (warnings.length) { console.log('') console.log(chalk.yellow('To use this template, you must update following to modules:')) console.log()
for (let i = 0; i < warnings.length; i++) { const warning = warnings[i] console.log(' ' + warning) }
console.log() process.exit(1) }}

2.utils.js:

const path = require('path')const config = require('../config')
//这个plugin的作用是将打包后生成的css文件通过link的方式引入到html中,如果不适用这个插件css代码会
//放到head标签的style中
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
//process.env.NODE_ENV是一个环境变量,它是由webpack.dev/prod.conf.js这两个文件声明的;
//这里的意思是判断当前是否是开发环境,如果是就把config下index.js文件中build.assetsSubDirectory或
//dev.assetsSubDirectory的值赋给assetsSubDirectory
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
//path.posix.join是path.join的一种兼容性写法,它的作用是路径的拼接,这里返回的是"static/_path"
return path.posix.join(assetsSubDirectory, _path
)}
//cssLoaders的作用是导出一个供vue-loader的options使用的一个配置;
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
function generateLoaders (loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [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')
}
} // styleLoaders是用来给webpack提供所有和css相关的loader的配置,它也使用了cssLoaders()方法;
exports.styleLoaders = function (options) {
const output = [] const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
} //'node-notifier'是一个跨平台系统通知的页面,当遇到错误时,它能用系统原生的推送方式给你推送信息
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}
  • cssLoaders方法根据传进来的参数(options)是否有extract属性来返回不同的值,如果你看了后面的代码你就会知道在生产模式下extract属性为true,开发模式下为false。也就是说,在生产模式下返回的是一个类似于这样的数组:

ExtractTextPlugin.extract({
use: ["css-loader","less-loader","sass-loader"...],
fallback: 'vue-style-loader'
})

这些css代码打包以link的方式放到HTML中。当然了,use的值确切的说应该是这样:

[ { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'less-loader', options: { sourceMap: true } } ]
我为了方便看就简写了。

而在开发模式下,cssLoaders返回的是:

["vue-style-loader","css-loader","less-loader","sass-loader"...] //我还是简写了

styleLoaders方法返回的值就简单了,它返回的就是webpack中module里常用的配置格式:

[
   {
      test: /\.css$/,
      use: [ 'style-loader', 'css-loader' ]
    },
...
]

3.vue-loader.conf.js:

const utils = require('./utils')
const config = require('../config') //不同环境为isProduction 赋值: 生产环境为true,开发环境为false
const isProduction = process.env.NODE_ENV === 'production' //不同环境为sourceMapEnabled 赋值: 这里都为true
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
//导出vue-loader的配置,这里我们用了utils文件中的cssLoaders();
module.exports = {
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting, //transformToRequire的作用是在模板编译的过程中,编译器可以将某些属性,如src转换为require调用;
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}

4.webpack.base.conf.js:

const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf') //resolve这个函数返回的是当前目录下"../dir"这个文件夹,__dirname指的是当前文件所在路径
function resolve (dir) { return path.join(__dirname, '..', dir)} module.exports = {
//返回项目的根路径
context: path.resolve(__dirname, '../'),
//入口文件
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: {
//自动解析扩展,比如引入对应的文件,js,vue,json的后缀名就可以省略了
extensions: ['.js', '.vue', '.json'],
alias: {
//精准匹配,使用vue来替代vue/dist/vue.esm.js
'vue$': 'vue/dist/vue.esm.js',
//使用@替代src路径,当你引入src下的文件是可以使用import XXfrom "@/xx"
'@': resolve('src'),
}
}, //一些loader配置,避免篇幅过长我省略一部分,大家可以看自己的文件
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
......
]
}, //node里的这些选项是都是Node.js全局变量和模块,这里主要是防止webpack注入一些Node.js的东西到vue中
node: {
setImmediate: false,
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}

5.webpack.dev.conf.js:

const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
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') //一个更友好的展示webpack错误提示的插件
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') //一个自动检索端口的包
const portfinder = require('portfinder') const HOST = process.env.HOSTconst PORT = process.env.PORT && Number(process.env.PORT) const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.dev.cssSourceMap,
usePostCSS: true
})
}, devtool: config.dev.devtool,
// devServer的配置大家看文档就好了
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,
contentBase: false,
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true,
watchOptions: {
poll: config.dev.poll,
}
},
plugins: [ //还记得之前说的生产环境和开发环境的变量在哪儿定义的吗?对,就是这里
new webpack.DefinePlugin({
process.env: require('../config/dev.env')
}), //模块热替换的插件,修改模块不需要刷新页面
new webpack.HotModuleReplacementPlugin(), //当使用HotModuleReplacementPlugin时,这个插件会显示模块正确的相对路径
new webpack.NamedModulesPlugin(), //在编译出错时,使用NoEmitOnErrorsPlugin来跳过输出阶段,这样可以确保输出资源不会包含错误
new webpack.NoEmitOnErrorsPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}), // 将static文件夹和里面的内容拷贝到开发模式下的路径,比如static下有个img文件夹,里面有张图片
// 我们可以这样访问:localhost:8080/static/img/logo.png
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
]
}) //这里主要是做端口的检索以及npm run dev后对错误的处理,我们可以看这里使用了前面引入的
//'friendly-errors-webpack-plugin'插件
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)
}
})
})

关于devServer有两点要说明一下:

  • contentBase是来告诉服务器在哪里提供静态的内容,这里我们使用false的原因是使用了“copy-webpack-plugin”插件,不需要使用contentBase了;
  • quiet开启后(true),除了初始启动信息之外的任何内容都不会被打印到控制台,即使是webpack 的错误或警告在控制台也不可见。不过我们用了'friendly-errors-webpack-plugin'插件,就可以设为true了。

6.webpack.prod.conf.js 

经过前面这么多代码的分析,其实webpack.prod.conf.js的配置已经很简单了,大致跟webpack.dev.conf.js的配置方式差不多,就是多了几个plugins:

  • UglifyJsPlugin是用来压缩JS代码
  • optimize-css-assets-webpack-plugin是用来压缩css代码
  • HashedModuleIdsPlugin会根据模块的相对路径生成一个四位数的hash作为模块id
  • ModuleConcatenationPlugin可以预编译所有模块到一个包中,加快浏览器的运行速度
  • CommonsChunkPlugin拆分公共模块,vue里拆分了vendor,manifest和app三个模块
  • compression-webpack-plugin gzip压缩
  • webpack-bundle-analyzer可以查看打包的具体情况,比如打了多少个包,每个包多大等

好了,plugins的介绍到此结束,接下来就是最后一个文件,也是npm run build编译时的入口文件——build.js了。

同样的,build.js文件其实也没什么可说的了,无非就是执行webpack.prod.conf.js文件,遇到错误时在命令行提示。需要注意的是,build.js里引入了“rimraf”的包,它的作用是每次编译时清空dist文件,避免多次编译时造成文件夹的重复和混乱。

Vue-cli的配置知识的更多相关文章

  1. @vue/cli的配置知道多少-publicPath,outputDir,assetsDir,indexPath,filenameHashing,configureWebpack,productionSourceMap

    vue.config.js的简单介绍 vue.config.js 是一个可选的配置文件, 在项目的 (和 package.json 同级的) 根目录中存在这个文件. 默认情况没有这个文件需要我们手动去 ...

  2. [Vue CLI 3] 配置解析之 parallel

    官方文档中介绍过在 vue.config.js 文件中可以配置 parallel,作用如下: 是否为 Babel 或 TypeScript 使用 thread-loader. 该选项在系统的 CPU ...

  3. [Vue CLI 3] 配置解析之 indexPath

    在 vue.config.js 配置中有一个 indexPath 的配置,我们先看看它有什么用? 用来指定 index.html 最终生成的路径(相对于 outputDir) 先看看它的默认值:在文件 ...

  4. Vue CLI 3 配置兼容IE10

    最近做了一个基于Vue的项目,需要兼容IE浏览器,目前实现了打包后可以在IE10以上运行,但是还不支持在运行时兼容IE10及以上. 安装依赖 yarn add --dev @babel/polyfil ...

  5. [Vue CLI 3] 配置 webpack-bundle-analyzer 插件

    首先还是简单介绍一下 webpack-bundle-analyzer 是做什么的: Visualize size of webpack output files with an interactive ...

  6. [Vue CLI 3] 配置解析之 css.extract

    大家还记得我们在老版本中,对于线上环境配置中会把所有的 css 多打成一个文件: 核心是使用了插件 extract-text-webpack-plugin,方式如下: 第一步都是加载插件 const ...

  7. Vue CLI 3.0脚手架如何在本地配置mock数据

    前后端分离的开发模式已经是目前前端的主流模式,至于为什么会前后端分离的开发我们就不做过多的阐述,既然是前后端分离的模式开发肯定是离不开前端的数据模拟阶段. 我们在开发的过程中,由于后台接口的没有完成或 ...

  8. vue cli 3 lintOnSave 配置有时无效问题

    一个使用vue cli 3.2创建的项目,创建时未开启 lintOnSave,后来希望开启并设置为 lintOnSave: 'error',但配置不生效. 解决方法1:新创建项目(此时vue cli ...

  9. 使用 vue-cli-service inspect 来查看一个 Vue CLI 3 项目的 webpack 配置信息(包括:development、production)

    使用 vue-cli-service inspect 来查看一个 Vue CLI 3 项目的 webpack 配置信息(包括:development.production) --mode 指定环境模式 ...

  10. @vue/cli 3.x项目脚手架 webpack 配置

    @vue/cli  是一个基于 Vue.js 进行快速开发的完整系统. @vue/cli   基于node服务  需要8.9以上版本 可以使用 nvm等工具来控制node版本  构建于 webpack ...

随机推荐

  1. android ndk编译项目(android-ndk-16r1)

    由于采用android-ndk-16r1版本的ndk来编译 编译的环境之类在这里省略,注意是最后编译的命令如下 Administrator@WIN-AF6P80LVIJ0 ~ $ cd $ANDROI ...

  2. Caffe + Ubuntu 15.04 + CUDA 7.0 安装以及配置

    作为小码农的我,昨天就在装这个东东了,主要参考第一篇博文,但是过程发现很多问题,经过反反复复,千锤百炼,终于柳暗花明,我把这个caffe给搞定了,是故,我发布出来,后之来者,欲将有感于斯文~ 本分分为 ...

  3. 【一天一道LeetCode】#31. Next Permutation

    一天一道LeetCode系列 (一)题目 Implement next permutation, which rearranges numbers into the lexicographically ...

  4. Linux下top命令详解

    Linux下top命令详解 top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.top是一个动态显示过程,即可以通过用户按键来不断刷 ...

  5. 【61】git项目实战的步骤总结

    1.新建分支的步骤 git pull git checkout -b 分支号(task的后面的代号) 2.提交代码到远程仓库的步骤 git add . git commit -m "分支号+ ...

  6. Android高级控件(四)——VideoView 实现引导页播放视频欢迎效果,超级简单却十分的炫酷

    Android高级控件(四)--VideoView 实现引导页播放视频欢迎效果,超级简单却十分的炫酷 是不是感觉QQ空间什么的每次新版本更新那炫炫的引导页就特别的激动,哈哈,其实他实现起来真的很简单很 ...

  7. android JNI调用机制

    JNI的出现使得开发者既可以利用Java语言跨平台.类库丰 富.开发便捷等特点,又可以利用Native语言的高效. JNI是JVM实现中的一部分,因此Native语言和Java代码都运行在JVM的宿主 ...

  8. 一个简单的基于 DirectShow 的播放器 1(封装类)

    DirectShow最主要的功能就是播放视频,在这里介绍一个简单的基于DirectShow的播放器的例子,是用MFC做的,今后有机会可以基于该播放器开发更复杂的播放器软件. 注:该例子取自于<D ...

  9. PS图像特效算法——镜像渐隐

    这个特效的实现,可以先利用前面提到的渐变特效,做一个图像的渐变, 然后将原图与渐变图对称放置,将背景设置成黑色. clc; clear all; close all; Image=imread('4. ...

  10. SharePoint 2010 电子书下载网站推荐

    最近一直搜集SharePoint2010的资料,偶尔发现一个网站还不错,推荐给大家,皮皮书屋http://www.ppurl.com/tag/sharepoint,里面有很多SharePoint201 ...