浅谈Vue-cli 命令行工具分析
Vue.js提供一个官方命令行工具,可用于快速搭建大型单页应用。vue-webpack-boilerplate,官方定义为:
full-featuredWebpacksetupwithhot-reload,lint-on-save,unittesting&cssextraction.
目录结构:
├──README.md ├──build │├──build.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
config环境配置
config配置文件用来配置devServer的相关设定,通过配置NODE_ENV来确定使用何种模式(开发、生产、测试或其他)
config |-index.js#配置文件 |-dev.env.js#开发模式 |-prod.env.js#生产模式
index.js
'usestrict' constpath=require('path'); module.exports={ dev:{ //路径 assetsSubDirectory:'static',//path:用来存放打包后文件的输出目录 assetsPublicPath:'/',//publicPath:指定资源文件引用的目录 proxyTable:{},//代理示例:proxy:[{context:["/auth","/api"],target:"http://localhost:3000",}] //开发服务器变量设置 host:'localhost', port:8080, autoOpenBrowser:true,//自动打开浏览器devServer.open errorOverlay:true,//浏览器错误提示devServer.overlay notifyOnErrors:true,//配合friendly-errors-webpack-plugin poll:true,//使用文件系统(filesystem)获取文件改动的通知devServer.watchOptions //sourcemap cssSourceMap:false,//develop下不生成sourceMap devtool:'eval-source-map'//增强调试可能的推荐值:eval,eval-source-map(推荐),cheap-eval-source-map,cheap-module-eval-source-map详细:https://doc.webpack-china.org/configuration/devtool }, build:{ //index模板文件 index:path.resolve(__dirname,'../dist/index.html'), //路径 assetsRoot:path.resolve(__dirname,'../dist'), assetsSubDirectory:'static', assetsPublicPath:'/', //bundleAnalyzerReport bundleAnalyzerReport:process.env.npm_config_report, //Gzip productionGzip:false,//默认false productionGzipExtensions:['js','css'], //sourcemap productionSourceMap:true,//production下是生成sourceMap devtool:'#source-map'//devtool:'source-map'? } }
dev.env.js
'usestrict' constmerge=require('webpack-merge'); constprodEnv=require('./prod.env'); module.exports=merge(prodEnv,{ NODE_ENV:'"development"' }); prod.env.js 'usestrict' module.exports={ NODE_ENV:'"production"' };
buildWebpack配置
build |-utils.js#代码段 |-webpack.base.conf.js#基础配置文件 |-webpack.dev.conf.js#开发模式配置文件 |-webpack.prod.conf.js#生产模式配置文件 |-build.js#编译入口
实用代码段utils.js
constconfig=require('../config') constpath=require('path') exports.assetsPath=function(_path){ constassetsSubDirectory=process.env.NODE_ENV==='production' ?config.build.assetsSubDirectory//'static' :config.dev.assetsSubDirectory returnpath.posix.join(assetsSubDirectory,_path)//posix方法修正路径 } exports.cssLoaders=function(options){//示例:({sourceMap:config.dev.cssSourceMap,usePostCSS:true}) options=options||{}; //cssLoader constcssLoader={ loader:'css-loader', options:{sourceMap:options.sourceMap} } //postcssLoader varpostcssLoader={ loader:'postcss-loader', options:{sourceMap:options.sourceMap} } //生成loader functiongenerateLoaders(loader,loaderOptions){ constloaders=options.usePostCSS?[cssLoader,postcssLoader]:[cssLoader]//设置默认loader if(loader){ loaders.push({ loader:loader+'-loader', options:Object.assign({},loaderOptions,{//生成options对象 sourceMap:options.sourceMap }) }) } //生产模式中提取css if(options.extract){//如果options中的extract为true配合生产模式 returnExtractTextPlugin.extract({ use:loaders, fallback:'vue-style-loader'//默认使用vue-style-loader }) }else{ return['vue-style-loader'].concat(loaders) } } return{//返回各种loaders对象 css:generateLoaders(), postcss:generateLoaders(), less:generateLoaders('less'), //示例:[ //{loader:'css-loader',options:{sourceMap:true/false}}, //{loader:'postcss-loader',options:{sourceMap:true/false}}, //{loader:'less-loader',options:{sourceMap:true/false}}, //] sass:generateLoaders('sass',{indentedSyntax:true}), scss:generateLoaders('sass'), stylus:generateLoaders('stylus'), styl:generateLoaders('stylus') } } exports.styleLoaders=function(options){ constoutput=[]; constloaders=exports.cssLoaders(options); for(constextensioninloaders){ constloader=loaders[extension] output.push({ test:newRegExp('\\.'+extension+'$'), use:loader }) //示例: //{ //test:newRegExp(\\.less$), //use:{ //loader:'less-loader',options:{sourceMap:true/false} //} //} } returnoutput } exports.createNotifierCallback=function(){//配合friendly-errors-webpack-plugin //基本用法:notifier.notify('message'); constnotifier=require('node-notifier');//发送跨平台通知系统 return(severity,errors)=>{ //当前设定是只有出现error错误时触发notifier发送通知 if(severity!=='error'){return}//严重程度可以是'error'或'warning' consterror=errors[0] constfilename=error.file&&error.file.split('!').pop(); notifier.notify({ title:pkg.name, message:severity+':'+error.name, subtitle:filename||'' //icon:path.join(__dirname,'logo.png')//通知图标 }) } }
基础配置文件webpack.base.conf.js
基础的webpack配置文件主要根据模式定义了入口出口,以及处理vue,babel等的各种模块,是最为基础的部分。其他模式的配置文件以此为基础通过webpack-merge合并。
'usestrict' constpath=require('path'); constutils=require('./utils'); constconfig=require('../config'); functionresolve(dir){ returnpath.join(__dirname,'..',dir); } module.exports={ context:path.resolve(__dirname,'../'),//基础目录 entry:{ app:'./src/main.js' }, output:{ path:config.build.assetsRoot,//默认'../dist' filename:'[name].js', publicPath:process.env.NODE_ENV==='production' ?config.build.assetsPublicPath//生产模式publicpath :config.dev.assetsPublicPath//开发模式publicpath }, resolve:{//解析确定的拓展名,方便模块导入 extensions:['.js','.vue','.json'], alias:{//创建别名 'vue$':'vue/dist/vue.esm.js', '@':resolve('src')//如'@/components/HelloWorld' } }, module:{ rules:[{ test:/\.vue$/,//vue要在babel之前 loader:'vue-loader', options:vueLoaderConfig//可选项:vue-loader选项配置 },{ test:/\.js$/,//babel loader:'babel-loader', include:[resolve('src')] },{//url-loader文件大小低于指定的限制时,可返回DataURL,即base64 test:/\.(png|jpe?g|gif|svg)(\?.*)?$/,//url-loader图片 loader:'url-loader', options:{//兼容性问题需要将query换成options limit:10000,//默认无限制 name:utils.assetsPath('img/[name].[hash:7].[ext]')//hash:7代表7位数的hash } },{ test:/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,//url-loader音视频 loader:'url-loader', options:{ limit:10000, name:utils.assetsPath('media/[name].[hash:7].[ext]') } },{ test:/\.(woff2?|eot|ttf|otf)(\?.*)?$/,//url-loader字体 loader:'url-loader', options:{ limit:10000, name:utils.assetsPath('fonts/[name].[hash:7].[ext]') } } ] }, node:{//是否polyfill或mock setImmediate:false, dgram:'empty', fs:'empty', net:'empty', tls:'empty', child_process:'empty' } }
开发模式配置文件webpack.dev.conf.js
开发模式的配置文件主要引用了config对于devServer的设定,对css文件的处理,使用DefinePlugin判断是否生产环境,以及其他一些插件。
'usestrict' constwebpack=require('webpack'); constconfig=require('../config'); constmerge=require('webpack-merge'); constbaseWebpackConfig=require('./webpack.base.conf'); constHtmlWebpackPlugin=require('html-webpack-plugin'); constportfinder=require('portfinder');//自动检索下一个可用端口 constFriendlyErrorsPlugin=require('friendly-errors-webpack-plugin');//友好提示错误信息 constdevWebpackConfig=merge(baseWebpackConfig,{ module:{ rules:utils.styleLoaders({sourceMap:config.dev.cssSourceMap,usePostCSS:true}) //自动生成了css,postcss,less等规则,与自己一个个手写一样,默认包括了css和postcss规则 }, devtool:config.dev.devtool,//添加元信息(metainfo)增强调试 //devServer在/config/index.js处修改 devServer:{ clientLogLevel:'warning',//console控制台显示的消息,可能的值有none,error,warning或者info historyApiFallback:true,//HistoryAPI当遇到404响应时会被替代为index.html hot:true,//模块热替换 compress:true,//gzip host:process.env.HOST||config.dev.host,//process.env优先 port:process.env.PORT||config.dev.port,//process.env优先 open:config.dev.autoOpenBrowser,//是否自动打开浏览器 overlay:config.dev.errorOverlay?{//warning和error都要显示 warnings:true, errors:true, }:false, publicPath:config.dev.assetsPublicPath,//配置publicPath proxy:config.dev.proxyTable,//代理 quiet:true,//控制台是否禁止打印警告和错误若使用FriendlyErrorsPlugin此处为true watchOptions:{ poll:config.dev.poll,//文件系统检测改动 } }, plugins:[ newwebpack.DefinePlugin({ 'process.env':require('../config/dev.env')//判断生产环境或开发环境 }), newwebpack.HotModuleReplacementPlugin(),//热加载 newwebpack.NamedModulesPlugin(),//热加载时直接返回更新的文件名,而不是id newwebpack.NoEmitOnErrorsPlugin(),//跳过编译时出错的代码并记录下来,主要作用是使编译后运行时的包不出错 newHtmlWebpackPlugin({//该插件可自动生成一个html5文件或使用模板文件将编译好的代码注入进去 filename:'index.html', template:'index.html', inject:true//可能的选项有true,'head','body',false }), ] }) module.exports=newPromise((resolve,reject)=>{ portfinder.basePort=process.env.PORT||config.dev.port;//获取当前设定的端口 portfinder.getPort((err,port)=>{ if(err){reject(err)}else{ process.env.PORT=port;//process公布端口 devWebpackConfig.devServer.port=port;//设置devServer端口 devWebpackConfig.plugins.push(newFriendlyErrorsPlugin({//错误提示插件 compilationSuccessInfo:{ messages:[`Yourapplicationisrunninghere:http://${config.dev.host}:${port}`], }, onErrors:config.dev.notifyOnErrors?utils.createNotifierCallback():undefined })) resolve(devWebpackConfig); } }) })
生产模式配置文件webpack.prod.conf.js
'usestrict' constpath=require('path'); constutils=require('./utils'); constwebpack=require('webpack'); constconfig=require('../config'); constmerge=require('webpack-merge'); constbaseWebpackConfig=require('./webpack.base.conf'); constCopyWebpackPlugin=require('copy-webpack-plugin'); constHtmlWebpackPlugin=require('html-webpack-plugin'); constExtractTextPlugin=require('extract-text-webpack-plugin'); constOptimizeCSSPlugin=require('optimize-css-assets-webpack-plugin'); constenv=process.env.NODE_ENV==='production' ?require('../config/prod.env') :require('../config/dev.env') constwebpackConfig=merge(baseWebpackConfig,{ module:{ rules:utils.styleLoaders({ sourceMap:config.build.productionSourceMap,//production下生成sourceMap extract:true,//util中styleLoaders方法内的generateLoaders函数 usePostCSS:true }) }, devtool:config.build.productionSourceMap?config.build.devtool:false, output:{ path:config.build.assetsRoot, filename:utils.assetsPath('js/[name].[chunkhash].js'), chunkFilename:utils.assetsPath('js/[id].[chunkhash].js') }, plugins:[ newwebpack.DefinePlugin({'process.env':env}), newwebpack.optimize.UglifyJsPlugin({//js代码压缩还可配置include,cache等,也可用babel-minify compress:{warnings:false}, sourceMap:config.build.productionSourceMap, parallel:true//充分利用多核cpu }), //提取js文件中的css newExtractTextPlugin({ filename:utils.assetsPath('css/[name].[contenthash].css'), allChunks:false, }), //压缩提取出的css newOptimizeCSSPlugin({ cssProcessorOptions:config.build.productionSourceMap ?{safe:true,map:{inline:false}} :{safe:true} }), //生成html newHtmlWebpackPlugin({ filename:process.env.NODE_ENV==='production' ?config.build.index :'index.html', template:'index.html', inject:true, minify:{ removeComments:true, collapseWhitespace:true, removeAttributeQuotes:true }, chunksSortMode:'dependency'//按dependency的顺序引入 }), newwebpack.HashedModuleIdsPlugin(),//根据模块的相对路径生成一个四位数的hash作为模块id newwebpack.optimize.ModuleConcatenationPlugin(),//预编译所有模块到一个闭包中 //拆分公共模块 newwebpack.optimize.CommonsChunkPlugin({ name:'vendor', minChunks:function(module){ return( module.resource&& /\.js$/.test(module.resource)&& module.resource.indexOf( path.join(__dirname,'../node_modules') )===0 ) } }), newwebpack.optimize.CommonsChunkPlugin({ name:'manifest', minChunks:Infinity }), newwebpack.optimize.CommonsChunkPlugin({ name:'app', async:'vendor-async', children:true, minChunks:3 }), //拷贝静态文档 newCopyWebpackPlugin([{ from:path.resolve(__dirname,'../static'), to:config.build.assetsSubDirectory, ignore:['.*'] }])] }) if(config.build.productionGzip){//gzip压缩 constCompressionWebpackPlugin=require('compression-webpack-plugin'); webpackConfig.plugins.push( newCompressionWebpackPlugin({ asset:'[path].gz[query]', algorithm:'gzip', test:newRegExp('\\.('+config.build.productionGzipExtensions.join('|')+')$'), threshold:10240,//10kb以上大小的文件才压缩 minRatio:0.8//最小比例达到.8时才压缩 }) ) } if(config.build.bundleAnalyzerReport){//可视化分析包的尺寸 constBundleAnalyzerPlugin=require('webpack-bundle-analyzer').BundleAnalyzerPlugin; webpackConfig.plugins.push(newBundleAnalyzerPlugin()); } module.exports=webpackConfig;
build.js编译入口
'usestrict' process.env.NODE_ENV='production';//设置当前环境为生产环境 constora=require('ora');//loading...进度条 constrm=require('rimraf');//删除文件'rm-rf' constchalk=require('chalk');//stdout颜色设置 constwebpack=require('webpack'); constpath=require('path'); constconfig=require('../config'); constwebpackConfig=require('./webpack.prod.conf'); constspinner=ora('正在编译...'); spinner.start(); //清空文件夹 rm(path.join(config.build.assetsRoot,config.build.assetsSubDirectory),err=>{ if(err)throwerr; //删除完成回调函数内执行编译 webpack(webpackConfig,function(err,stats){ spinner.stop(); if(err)throwerr; //编译完成,输出编译文件 process.stdout.write(stats.toString({ colors:true, modules:false, children:false, chunks:false, chunkModules:false })+'\n\n'); //error if(stats.hasErrors()){ console.log(chalk.red('编译失败出现错误.\n')); process.exit(1); } //完成 console.log(chalk.cyan('编译成功.\n')) console.log(chalk.yellow( 'file://无用,需http(s)://.\n' )) }) })
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。