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') }) })
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
下一篇: 死猫,乱叫什么