最近把项目的所有配置文件都研究学习了一下,发现学无止境,永远都学不完呀,慢慢学吧。
本来是想拿公司项目来记录学习过程,但是呢怕引起不必要的麻烦,所以就用了git大神的一个项目。
大神的目录结构如下,我们主要看build/和package.json
package.json中配置了项目的基本信息、脚本以及包信息。我们主要看下脚本,如下
--------------------------------dev-----------------------------------
先看“dev”(npm run dev),命令是:
cross-env NODE_ENV=online node build/dev-server.js
cross-env 跨平台设置及使用环境变量 ,即将环境变量 NODE_ENV设置为online
node build/dev-server.js:执行build/dev-server.js
分析dev-server.js:
首先看一下build-server头部引入的文件
--> config是一个配置文件,后面会解析一下这个文件
--> path:处理与路径转换
express:Web应用框架
webpack:打包工具
opn: 用于打开浏览器
http-proxy-middleware 用于把请求代理转发到其他服务器的中间件
以上这些都是直接安装使用的
-->./webpack.dev.conf : 自己写的的webpack配置文件,后面会详细解析
------config js
var path = require('path')
module.exports = {
build: {
// 修改环境为生产环境
env: {
NODE_ENV: '"production"'
},
//index 即打包后会生成启动首页,index.html
index: path.resolve(__dirname, '../elm/index.html'),
// 打包后 生成的资源存根目录
assetsRoot: path.resolve(__dirname, '../elm'),
//资源子目录
assetsSubDirectory: 'static',
//这个是通过http服务器运行的url路径,一般都是根目录
assetsPublicPath: '/elm/',
productionSourceMap: true,//设置成false,打包的体积会减小80%
// 是否开启Gzip压缩
productionGzip: false,
// 需要使用 gzip 压缩的文件扩展名
productionGzipExtensions: ['js', 'css']
},
dev: {
env: {
NODE_ENV: '"development"'
},
port: 8000,// 运行测试页面的端口
assetsSubDirectory: 'static',// 编译输出的二级目录
assetsPublicPath: '/',// 编译发布的根目录,可配置为资源服务器域名或 CDN 域名
context: [ //代理路径
'/shopping',
'/ugc',
'/v1',
'/v2',
'/v3',
'/v4',
'/bos',
'/member',
'/promotion',
'/eus',
'/payapi',
'/img',
],
proxypath: 'http://cangdu.org:8001',// 需要 proxyTable 代理的接口(可跨域)
cssSourceMap: false
}
}
config配置的目的都是为了服务webpack的配置,给不同的编译条件提供配置
build: 打包上线的配置;可以看出会将打包好的文件放在elm目录下,启动的首页index,html,资源目录是elm/static
dev:本地启动测试的配置;运行的端口8000
----- webpack.dev.conf
webpack.dev中也引入了3个js,如下图
config.js已经在上部分看过,下面看下utils;
---->utils
var path = require('path')
var config = require('../config')
//主要是为了抽离css样式,防止将样式打包在js中引起页面样式加载错乱的现象.
var ExtractTextPlugin = require('extract-text-webpack-plugin')
exports.assetsPath = function(_path) {
var assetsSubDirectory = process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory
//path.posix.join是path.join的一种兼容性写法,它的作用是路径的拼接,这里返回的是"XXX/XXX"
// posix是跨平台兼容
return path.posix.join(assetsSubDirectory, _path)
}
//生成cssloader表
exports.cssLoaders = function(options) {
options = options || {}
function generateLoaders(loaders) {
var sourceLoader = loaders.map(function(loader) {
var extraParamChar
if (/\?/.test(loader)) {
loader = loader.replace(/\?/, '-loader?')
extraParamChar = '&'
} else {
loader = loader + '-loader'
extraParamChar = '?'
}
return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
}).join('!')
if (options.extract) {
return ExtractTextPlugin.extract('vue-style-loader', sourceLoader)
} else {
return ['vue-style-loader', sourceLoader].join('!')
}
}
return {
css: generateLoaders(['css']),
postcss: generateLoaders(['css']),
less: generateLoaders(['css', 'less']),
sass: generateLoaders(['css', 'sass?indentedSyntax']),
scss: generateLoaders(['css', 'sass']),
stylus: generateLoaders(['css', 'stylus']),
styl: generateLoaders(['css', 'stylus'])
}
}
//生成styleLoaders表
exports.styleLoaders = function(options) {
var output = []
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
var loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
loader: loader
})
}
return output
}
utils比较简单,就是动态生成cssloader表,很多时候我们都是直接列出来,这边就是通过js的方式动态生成,其实是一样的。
webpack.base.conf
var path = require('path')
var config = require('../config')
var utils = require('./utils')
var projectRoot = path.resolve(__dirname, '../')
var env = process.env.NODE_ENV
var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap)
var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap)
var useCssSourceMap = cssSourceMapDev || cssSourceMapProd
module.exports = {
//入口文件
entry: {
app: './src/main.js'
},
//导出配置
output: {
path: config.build.assetsRoot,
publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
filename: '[name].js'// [name]---entry中的key,即app.js
},
// 配置webpack如何寻找模块对应的文件。
resolve: {
//配置在查找文件的过程中用到的后缀列表
extensions: ['', '.js', '.vue', '.less', '.css', '.scss'],
//如果webpack 在 resolve.root 或者 resolve.modulesDirectories 实在找不到某个模块了,会去这个目录中找
fallback: [path.join(__dirname, '../node_modules')],
//alias通过别名来把原导入路径映射成一个新的导入路径
alias: {
'vue$': 'vue/dist/vue.common.js',
'src': path.resolve(__dirname, '../src'),
'assets': path.resolve(__dirname, '../src/assets'),
'components': path.resolve(__dirname, '../src/components')
}
},
//跟 resolve很像,但是是为loaders准备的。
resolveLoader: {
fallback: [path.join(__dirname, '../node_modules')]
},
//正常模块的配置
module: {
// loader
loaders: [{
test: /\.vue$/,
loader: 'vue'
}, {
test: /\.js$/,
loader: 'babel',
include: projectRoot,
exclude: /node_modules/
}, {
test: /\.json$/,
loader: 'json'
}, {
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url',
query: {
limit: 10000,
name: utils.assetsPath('img/[name].[ext]')
}
}, {
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url',
query: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}]
},
vue: {
loaders: utils.cssLoaders({
sourceMap: useCssSourceMap
}),
postcss: [
require('autoprefixer')({
browsers: ['last 10 versions']
})
]
}
}
以上配置就是开发环境和测试环境公用的配置,具体的意思见代码
--->webpack.dev.conf
var config = require('../config')
var webpack = require('webpack')
var merge = require('webpack-merge')//将对象or数组合并
var utils = require('./utils')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
Object.keys(baseWebpackConfig.entry).forEach(function(name) {
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})
// baseWebpackConfig entry: { app: [ './build/dev-client', './src/main.js' ]}
module.exports = merge(baseWebpackConfig, {
//module 配置如何处理模块。
module: {
loaders: utils.styleLoaders({
sourceMap: config.dev.cssSourceMap
})
},
// 使用eval-source-map,方便代码调试
devtool: '#eval-source-map',
//配置插件
plugins: [
// 可以简单的理解为将process.env 变成全局变量,可以在其他地方引用
new webpack.DefinePlugin({
'process.env': config.dev.env
}),
//根据模块调用次数,给模块分配ids,常被调用的ids分配更短的id,使得ids可预测,降低文件大小
new webpack.optimize.OccurenceOrderPlugin(),
//热更新--无刷新实现代码更新
new webpack.HotModuleReplacementPlugin(),
//跳过编译时出错的代码并记录,使编译后运行时的包不会发生错误
new webpack.NoErrorsPlugin(),
//生成html模版文件
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
favicon: 'favicon.ico',
inject: true
})
]
})
--------------------------------build----------------------------------
看“ build”(npm run build),命令是:
node build/build.js
----->webpack.prod.conf
var path = require('path')
var config = require('../config')
var utils = require('./utils')
var webpack = require('webpack')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var env = config.build.env
var webpackConfig = merge(baseWebpackConfig, {
module: {
loaders: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true
})
},
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].js'),
chunkFilename: utils.assetsPath('js/[name].[chunkhash].min.js')
},
vue: {
loaders: utils.cssLoaders({
sourceMap: config.build.productionSourceMap,
extract: true
})
},
plugins: [
// 可以简单的理解为将process.env 变成全局变量,可以在其他地方引用
new webpack.DefinePlugin({
'process.env': env
}),
//对js进行压缩
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
//根据模块调用次数,给模块分配ids,常被调用的ids分配更短的id,使得ids可预测,降低文件大小
new webpack.optimize.OccurrenceOrderPlugin(),
// 抽离css样式,防止将样式打包在js中引起页面样式加载错乱的现象
new ExtractTextPlugin(utils.assetsPath('css/[name].css')),
//将生成的html模版和我们在output-filename生成的js产生关系,就是在让html启动引入js
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
chunksSortMode: 'dependency'
}),
// 主要是用来提取第三方库和公共模块,即将公共模块提取到vendor.js中,
//避免首屏加载的bundle文件或者按需加载的bundle文件体积过大,
// 从而导致加载时间过长,着实是优化的一把利器。
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function(module, count) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// 将webpack的运行文件、第三方库和自定义公共模块单独抽离出
// 避免webpack运行文件改变导致vendor.js改变,从而需要用户去重新加载
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
})
]
})
//是否使用productionGzip
if (config.build.productionGzip) {
//压缩
var CompressionWebpackPlugin = require('compression-webpack-plugin')
//配置中添加压缩的插件
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
module.exports = webpackConfig
----->build.js
require('shelljs/global')//允许直接在脚本中写shell命令
env.NODE_ENV = 'production'
var path = require('path')
var config = require('../config')
var ora = require('ora')//实现命令行环境的loading效果
var webpack = require('webpack')
var webpackConfig = require('./webpack.prod.conf')
var spinner = ora('building for production...')
spinner.start()//loading 开启
var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
// shell命令
rm('-rf', assetsPath)
mkdir('-p', assetsPath)
cp('-R', 'static/*', assetsPath)
//开始打包
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')
})