vue-cli中的webpack配置详解

版本号

  • vue-cli 2.8.1 (终端通过vue -V 可查看)
  • vue 2.2.2
  • webpack 2.2.1

目录结构

├── README.md
├── 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
├── config
│  ├── dev.env.js
│  ├── index.js
│  └── prod.env.js
├── index.html
├── package.json
├── src
│  ├── App.vue
│  ├── assets
│  │  └── logo.png
│  ├── components
│  │  └── Hello.vue
│  └── main.js
└── static

webpack配置

主要对build目录下的webpack配置做详细分析

webpack.base.conf.js

入口文件entry

entry: {
 app: '.src/main.js'
}

输出文件output

config的配置在config/index.js文件中

output: {
 path: config.build.assetsRoot, //导出目录的绝对路径
 filename: '[name].js', //导出文件的文件名
 publicPath: process.env.NODE_ENV === 'production'? config.build.assetsPublicPath : config.dev.assetsPublicPath //生产模式或开发模式下html、js等文件内部引用的公共路径
}

文件解析resolve

主要设置模块如何被解析。

resolve: {
 extensions: ['.js', '.vue', '.json'], //自动解析确定的拓展名,使导入模块时不带拓展名
 alias: {  // 创建import或require的别名
  'vue$': 'vue/dist/vue.esm.js',
  '@': resolve('src')
 }
}

模块解析module

如何处理项目不同类型的模块。

module: {
 rules: [
  {
   test: /\.vue$/, // vue文件后缀
   loader: 'vue-loader', //使用vue-loader处理
   options: vueLoaderConfig //options是对vue-loader做的额外选项配置
  },
  {
   test: /\.js$/, // js文件后缀
   loader: 'babel-loader', //使用babel-loader处理
   include: [resolve('src'), resolve('test')] //必须处理包含src和test文件夹
  },
  {
   test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, //图片后缀
   loader: 'url-loader', //使用url-loader处理
   query: { // query是对loader做额外的选项配置
    limit: 10000, //图片小于10000字节时以base64的方式引用
    name: utils.assetsPath('img/[name].[hash:7].[ext]') //文件名为name.7位hash值.拓展名
   }
  },
  {
   test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, //字体文件
   loader: 'url-loader', //使用url-loader处理
   query: {
    limit: 10000, //字体文件小于1000字节的时候处理方式
    name: utils.assetsPath('fonts/[name].[hash:7].[ext]') //文件名为name.7位hash值.拓展名
   }
  }
 ]
}

注: 关于query 仅由于兼容性原因而存在。请使用 options 代替。

webpack.dev.conf.js

开发环境下的webpack配置,通过merge方法合并webpack.base.conf.js基础配置

var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
module.exports = merge(baseWebpackConfig, {})

模块配置

module: {
 //通过传入一些配置来获取rules配置,此处传入了sourceMap: false,表示不生成sourceMap
 rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
}

在util.styleLoaders中的配置如下

exports.styleLoaders = function (options) {
 var output = [] //定义返回的数组,数组中保存的是针对各类型的样式文件的处理方式
 var loaders = exports.cssLoaders(options) // 调用cssLoaders方法返回各类型的样式对象(css: loader)
 for (var extension in loaders) { //循环遍历loaders
  var loader = loaders[extension] //根据遍历获得的key(extension)来得到value(loader)
  output.push({   //
   test: new RegExp('\\.' + extension + '$'), // 处理的文件类型
   use: loader //用loader来处理,loader来自loaders[extension]
  })
 }
 return output
}

上面的代码中调用了exports.cssLoaders(options),用来返回针对各类型的样式文件的处理方式,具体实现如下

exports.cssLoaders = function (options) {
 options = options || {}

 var cssLoader = {
  loader: 'css-loader',
  options: { //options是loader的选项配置
   minimize: process.env.NODE_ENV === 'production', //生成环境下压缩文件
   sourceMap: options.sourceMap //根据参数是否生成sourceMap文件
  }
 }
 function generateLoaders (loader, loaderOptions) { //生成loader
  var loaders = [cssLoader] // 默认是css-loader
  if (loader) { // 如果参数loader存在
   loaders.push({
    loader: loader + '-loader',
    options: Object.assign({}, loaderOptions, { //将loaderOptions和sourceMap组成一个对象
     sourceMap: options.sourceMap
    })
   })
  }
  if (options.extract) { // 如果传入的options存在extract且为true
   return ExtractTextPlugin.extract({ //ExtractTextPlugin分离js中引入的css文件
    use: loaders, //处理的loader
    fallback: 'vue-style-loader' //没有被提取分离时使用的loader
   })
  } else {
   return ['vue-style-loader'].concat(loaders)
  }
 }
 return { //返回css类型对应的loader组成的对象 generateLoaders()来生成loader
  css: generateLoaders(),
  postcss: generateLoaders(),
  less: generateLoaders('less'),
  sass: generateLoaders('sass', { indentedSyntax: true }),
  scss: generateLoaders('sass'),
  stylus: generateLoaders('stylus'),
  styl: generateLoaders('stylus')
 }
}

插件配置

plugins: [
 new webpack.DefinePlugin({ // 编译时配置的全局变量
  'process.env': config.dev.env //当前环境为开发环境
 }),
 new webpack.HotModuleReplacementPlugin(), //热更新插件
 new webpack.NoEmitOnErrorPlugin(), //不触发错误,即编译后运行的包正常运行
 new HtmlWebpackPlugin({ //自动生成html文件,比如编译后文件的引入
  filename: 'index.html', //生成的文件名
  template: 'index.html', //模板
  inject: true
 }),
 new FriendlyErrorsPlugin() //友好的错误提示
]

webpack.prod.conf.js

生产环境下的webpack配置,通过merge方法合并webpack.base.conf.js基础配置

module的处理,主要是针对css的处理

同样的此处调用了utils.styleLoaders

module: {
 rules: utils.styleLoaders({
  sourceMap: config.build.productionSourceMap,
  extract: true
 })
}

输出文件output

output: {
 //导出文件目录
 path: config.build.assetsRoot,
 //导出的文件名
 filename: utils.assetsPath('js/[name].[chunkhash].js'),
 //非入口文件的文件名,而又需要被打包出来的文件命名配置,如按需加载的模块
 chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
}

插件plugins

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 = config.build.env
plugins: [
 new webpack.DefinePlugin({
  'process.env': env //配置全局环境为生产环境
 }),
 new webpack.optimize.UglifyJsPlugin({ //js文件压缩插件
  compress: { //压缩配置
   warnings: false // 不显示警告
  },
  sourceMap: true //生成sourceMap文件
 }),
 new ExtractTextPlugin({ //将js中引入的css分离的插件
  filename: utils.assetsPath('css/[name].[contenthash].css') //分离出的css文件名
 }),
 //压缩提取出的css,并解决ExtractTextPlugin分离出的js重复问题(多个文件引入同一css文件)
 new OptimizeCSSPlugin(),
 //生成html的插件,引入css文件和js文件
 new HtmlWebpackPlugin({
  filename: config.build.index, //生成的html的文件名
  template: 'index.html', //依据的模板
  inject: true, //注入的js文件将会被放在body标签中,当值为'head'时,将被放在head标签中
  minify: { //压缩配置
   removeComments: true, //删除html中的注释代码
   collapseWhitespace: true, //删除html中的空白符
   removeAttributeQuotes: true //删除html元素中属性的引号
  },
  chunksSortMode: 'dependency' //按dependency的顺序引入
 }),
 //分离公共js到vendor中
 new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor', //文件名
  minChunks: functions(module, count) { // 声明公共的模块来自node_modules文件夹
   return (module.resource && /\.js$/.test(module.resource) && module,resource.indexOf(path.join(__dirname, '../node_modules')) === 0)
  }
 }),
 //上面虽然已经分离了第三方库,每次修改编译都会改变vendor的hash值,导致浏览器缓存失效。原因是vendor包含了webpack在打包过程中会产生一些运行时代码,运行时代码中实际上保存了打包后的文件名。当修改业务代码时,业务代码的js文件的hash值必然会改变。一旦改变必然会导致vendor变化。vendor变化会导致其hash值变化。
 //下面主要是将运行时代码提取到单独的manifest文件中,防止其影响vendor.js
 new webpack.optimize.CommonsChunkPlugin({
  name: 'mainifest',
  chunks: ['vendor']
 }),
 // 复制静态资源,将static文件内的内容复制到指定文件夹
 new CopyWebpackPlugin([{
  from: path.resolve(__dirname, '../static'),
  to: config.build.assetsSubDirectory,
  ignore: ['.*'] //忽视.*文件
 }])
]

额外配置

if (config.build.productionGzip) { //配置文件开启了gzip压缩

 //引入压缩文件的组件,该插件会对生成的文件进行压缩,生成一个.gz文件
 var CompressionWebpackPlugin = require('compression-webpack-plugin') 

 webpackConfig.plugins.push(
  new CompressionWebpackPlugin({
   asset: '[path].gz[query]', //目标文件名
   algorithm: 'gzip', //使用gzip压缩
   test: new RegExp( //满足正则表达式的文件会被压缩
    '\\.(' +
    config.build.productionGzipExtensions.join('|') +
    ')$'
   ),
   threshold: 10240, //资源文件大于10240B=10kB时会被压缩
   minRatio: 0.8 //最小压缩比达到0.8时才会被压缩
  })
 )
}

npm run dev

有了上面的配置之后,下面看看运行命令npm run dev发生了什么

在package.json文件中定义了dev运行的脚本

"scripts": {
  "dev": "node build/dev-server.js",
  "build": "node build/build.js"
},

当运行npm run dev命令时,实际上会运行dev-server.js文件

该文件以express作为后端框架

// nodejs环境配置
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') //webpack的配置

var port = process.env.PORT || config.dev.port //端口号
var autoOpenBrowser = !!config.dev.autoOpenBrowser //是否自动打开浏览器
var proxyTable = config.dev.proxyTable //http的代理url

var app = express() //启动express
var compiler = webpack(webpackConfig) //webpack编译

//webpack-dev-middleware的作用
//1.将编译后的生成的静态文件放在内存中,所以在npm run dev后磁盘上不会生成文件
//2.当文件改变时,会自动编译。
//3.当在编译过程中请求某个资源时,webpack-dev-server不会让这个请求失败,而是会一直阻塞它,直到webpack编译完毕
var devMiddleware = require('webpack-dev-middleware')(compiler, {
 publicPath: webpackConfig.output.publicPath,
 quiet: true
})

//webpack-hot-middleware的作用就是实现浏览器的无刷新更新
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
 log: () => {}
})
//声明hotMiddleware无刷新更新的时机:html-webpack-plugin 的template更改之后
compiler.plugin('compilation', function (compilation) {
 compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
  hotMiddleware.publish({ action: 'reload' })
  cb()
 })
})

//将代理请求的配置应用到express服务上
Object.keys(proxyTable).forEach(function (context) {
 var options = proxyTable[context]
 if (typeof options === 'string') {
  options = { target: options }
 }
 app.use(proxyMiddleware(options.filter || context, options))
})

//使用connect-history-api-fallback匹配资源
//如果不匹配就可以重定向到指定地址
app.use(require('connect-history-api-fallback')())

// 应用devMiddleware中间件
app.use(devMiddleware)
// 应用hotMiddleware中间件
app.use(hotMiddleware)

// 配置express静态资源目录
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
app.use(staticPath, express.static('./static'))

var uri = 'http://localhost:' + port

//编译成功后打印uri
devMiddleware.waitUntilValid(function () {
 console.log('> Listening at ' + uri + '\n')
})
//启动express服务
module.exports = app.listen(port, function (err) {
 if (err) {
  console.log(err)
  return
 }
 // 满足条件则自动打开浏览器
 if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
  opn(uri)
 }
})

npm run build

由于package.json中的配置,运行此命令后会执行build.js文件

process.env.NODE_ENV = 'production' //设置当前环境为production
var ora = require('ora') //终端显示的转轮loading
var rm = require('rimraf') //node环境下rm -rf的命令库
var path = require('path') //文件路径处理库
var chalk = require('chalk') //终端显示带颜色的文字
var webpack = require('webpack')
var config = require('../config')
var webpackConfig = require('./webpack.prod.conf') //生产环境下的webpack配置

// 在终端显示ora库的loading效果
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() //停止loading
  if (err) throw err

  // 在编译完成的回调函数中,在终端输出编译的文件
  process.stdout.write(stats.toString({
   colors: true,
   modules: false,
   children: false,
   chunks: false,
   chunkModules: false
  }) + '\n\n')
 })
})

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解基于vue-cli优化的webpack配置

    最近的项目度过了开始忙碌的基建期,也慢慢轻松下来,准备记录一下自己最近webpack优化的措施,希望有温故知新的效果. 项目采用的是vue全家桶,构建配置都是基于vue-cli去改进的.关于原始webpack配置大家可以看下这篇文章vue-cli#2.0 webpack配置分析,文章基本对于文件每行代码都做了详细的解释,有助于更好的理解webpack. 仔细总结了一下,自己的优化基本还是网上流传的那几点 通过 externals 配置来提取常用库,引用cdn 合理配置CommonsChunkPl

  • 详解vue-cli + webpack 多页面实例配置优化方法

    本文介绍了vue-cli + webpack 多页面实例配置优化方法,分享给大家 vue+webpack是否有多页面 目前使用vue来做项目,估计大部分都是单页面(SPA)应用,一个轻型的 MVVM 框架,谁用了MVVM框架,就再也回不去JQ时代了,哈哈. 在手机端的项目,使用vue + vue-router是high到爆,不仅仅是我们开发的而言,最主要的用户体检也是开足马力,体检感杠杠的. 那问题来了,使用vue+webpack的单页面是爽到爆,那如果是多页面也能不能high到爆呢?那当然呀,

  • vue-cli的webpack模板项目配置文件分析

    由于最近在vue-cli生成的webpack模板项目的基础上写一个小东西,开发过程中需要改动到build和config里面一些相关的配置,所以刚好趁此机会将所有配置文件看一遍,理一理思路,也便于以后修改配置的时候不会"太折腾". 一.文件结构 本文主要分析开发(dev)和构建(build)两个过程涉及到的文件,故下面文件结构仅列出相应的内容. ├─build │ ├─build.js │ ├─check-versions.js │ ├─dev-client.js │ ├─dev-ser

  • vue-cli中的webpack配置详解

    版本号 vue-cli 2.8.1 (终端通过vue -V 可查看) vue 2.2.2 webpack 2.2.1 目录结构 ├── README.md ├── build │ ├── build.js │ ├── check-versions.js │ ├── dev-client.js │ ├── dev-server.js │ ├── utils.js │ ├── vue-loader.conf.js │ ├── webpack.base.conf.js │ ├── webpack.de

  • webpack中的代理配置详解

    目录 作用: 使用场景一: 使用场景二 使用场景三 使用场景四: 使用场景五: 解决跨域原理 vue-cli中proxyTable配置接口地址代理示例 更多参数 作用: 1.解决开发环境跨域问题(不用再去配置nginx和host) 2.如果你有单独的后端开发服务器API,并希望在同域名下发送API请求,那么代理某些URL会很有用 下面介绍一下五种经常使用的场景 使用场景一: 请求到 /api/xxx 现在会被代理到请求 http://localhost:3000/api/xxx, 例如 /api

  • vue跨域proxy代理配置详解

    目录 引言 例一 例二 总结 引言 相信很多人都用过proxy,这里就不多说proxy的基本使用,这里要说一下很多人使用proxy的一个误区,大家一般都是直接将管官方的例子拿过来,修改一下代理目标直接就是用了,但是代理的路径匹配你真的会用么? 如果你能够认证看完那么我保证你下次再配置代理的时候就能一步就能完成,无需多次npm run serve 先来一个正则热热身待会需要用,看懂了再往下看: 例一 这是我使用express自己搭建的一个超级简单的服务器,代码如下(没有设置允许跨域请求) // 引

  • Struts 2中的constant配置详解

    1.<constant name="struts.i18n.encoding" value="UTF-8" /> 指定Web应用的默认编码集,相当于调用 HttpServletRequest的setCharacterEncoding方法. 2.<constant name="struts.i18n.reload" value="false"/> 该属性设置是否每次HTTP请求到达时,系统都重新加载资源文

  • JSP 中spring事务配置详解

    JSP 中spring事务配置详解 前几天被问到,如何防止服务器宕机,造成的数据操作的不完全. 问了一下同事,是事务.哎,恍然大悟,迷糊一时了. 声明式的事务配置,这个是最推荐的,配置到service层. <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context=&

  • SpringAOP中的注解配置详解

    这篇文章主要介绍了SpringAOP中的注解配置详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 使用注解实现SpringAOP的功能: 例子: //表示这是被注入Spring容器中的 @Component //表示这是个切面类 @Aspect public class AnnotationHandler { /* * 在一个方法上面加上注解来定义切入点 * 这个切入点的名字就是这个方法的名字 * 这个方法本身不需要有什么作用 * 这个方法的

  • git中ssh key配置详解

    git clone支持https和git(即ssh)两种方式下载源码: 当使用git方式下载时,如果没有配置过ssh key,则会有如下错误提示: 下面就介绍一下如何配置git的ssh key,以便我们可以用git方式下载源码. 首先用如下命令(如未特别说明,所有命令均默认在Git Bash工具下执行)检查一下用户名和邮箱是否配置(github支持我们用用户名或邮箱登录): git config --global --list 笔者的机器显示信息如下(已配置): 如未配置,则执行以下命令进行配置

  • Java Maven settings.xml中私有仓库配置详解

    目录 Maven setting中私有仓库配置浅析 简单maven配置 mirrors profile repositories 总结 Maven setting中私有仓库配置浅析 最近遇到过不少这样那样的问题,曾经做过maven的分享,但是发现当时部分内容还是太想当然了,下面经过尝试后简单总结下: 首先几个逻辑: pom>启用的profile>maven原有配置 mirror配置mirrorOf和id匹配优先 简单maven配置 一般大家的配置(略去无关私有仓库配置)都是这样的 <mi

  • mybatis中的setting配置详解

    在mybaits中,setting的的配置参数如下(如果不在配置文件中配置将使用默认值): 设置参数 描述 有效值 默认值 cacheEnabled 该配置影响的所有映射器中配置的缓存的全局开关 true | false true lazyLoadingEnabled 延迟加载的全局开关.当开启时,所有关联对象都会延迟加载. 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态 true | false false aggressiveLazyLoading 当启用时,对任意延迟属

  • django中SMTP发送邮件配置详解

    Django中内置了邮件发送功能,被定义在django.core.mail模块中.发送邮件需要使用SMTP服务器,常用的免费服务器有:163.126.QQ,下面以qq邮箱为例. 注册qq邮箱,然后登录设置 找到设置里面POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 3.需要发送验证码生成授权码 4.找到settings.py文件,中点击下图配置 EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBacken

随机推荐