浅谈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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。