webpack脱坑之旅
目录结构:
- build
- webpack.base.conf.js
- webpack.dev.conf.js
- webpack.prod.conf.js
webpack.base.conf.js
// webpack基本配置
const path = require('path') // 引入path模块处理不同系统之间的路径问题
const projectRoot = path.resolve(__dirname, '../') // 项目根目录
const utils = require('./uitls')
const config = require('../config')
const env = process.env.NODE_ENV // js的全局变量process 通过改变NODE_ENV的值,控制项目的生产与开发环境
// 其实就是判断是否启用css源映射
const cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap)
const cssSourceMapProd = (env === 'production' && config.build.productionSourceMap)
const useCssSourceMap = cssSourceMapDev || cssSourceMapProd
module.exports = {
entry: { // 需要打包的入口文件
app: './src/main.js'
},
output: { // 输出文件配置,常用的三个参数:
path: config.build.assetsRoot, // 告诉webpack结果文件放到哪里,是一个绝对路径
// publicPath: 表示的是一个url路径(指向生成文件的根目录),用于生成css/js/图片/字体文件等资源的路径以保证网也能够正确加载到资源
publicpath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath, // 判断环境,配置公共路径
// filename 参数表示的是如何命名出来的入口文件,规则如下:
// [name] 指代入口文件(entry参数)的key 也就是上面的app
// [hash] 指代本次编译的hash版本,每次编译的每个文件的hash值相同;从缓存层面来说,就是一次全量替换
filename: '[name].js'
},
// 用来配置依赖文件的匹配,如依赖文件的别名配置、模块的查找目录、默认查找的文件后缀名
// resolve.root 用来制定模块查找的根路径,必须为-!绝对路径!-,值可以是路径字符串或者是字符串数组,依次查找
resolve: {
extensions: ['', '.vue', '.js', '.json'],
fallback: [path.join(__dirname, '../node_modules')], // 配置依赖项路径
alias: { // 配置依赖文件的别名,键名就是别名,值是路径
'vue$': 'vue/dist/vue.common.js',
'src': path.resolve(__dirname, '../src'),
'assets': path.resolve(__dirname, '../src/assets'),
'components': path.resolve(__dirname, '../src/components')
}
},
resolveLoader: {
fallback: [path.join(__dirname, '../node_modules')]
},
// 用来进行模块加载相关配置
modules: {
preLoaders: [ // 调用loader之前需要调用的loader,用来检查代码
{
test: /\.vue$/,
loader: 'eslint',
include: projectRoot,
exclude: /node_modules/
},
{
test: /\.js$/, // 匹配名称规则
loader: 'eslint', // 需要用到的loader
include: projectRoot, // 包括哪些文件
exclude: /node_modules/ // 除了哪些文件
}
],
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].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url',
query: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
},
// expose-loader,这个loader的作用是,将指定js模块export的变量声明为全局变量
{
test: require.resolve('jquery'), // 这里配置项的目标是jquery
laoder: 'expose?$!expose?jQuery' // 先把jQuery对象声明为全局对象jQuery,再通过管道进一步声明为全局变量$
}
]
},
eslint: {
formatter: require('eslint-friendly-formatter')
},
vue: {
loaders: utils.cssLoaders({sourceMap: useCssSourceMap}),
postcss: [ // 解决vue文件中的一些样式解析,比如scoped
require('autoprefixer')({
browers: ['last 2 versions']
})
]
}
}
webpack.dev.conf.js
const webpack = require('webpack')
const utils = require('./utils')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webapck.base.conf')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// add hot-reload related code to entry trunks // 将热重载相关代码添加到条目中
Object.keys(baseWebpackConfig.entry).forEach(name => {
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})
module.exports = merge(baseWebpackConfig, {
module: {
loaders: utils.styleLoaders({sourceMap: config.dev.cssSourceMap})
},
// eval-source-map is faster for development
devtool: '#eval-source-map',
plugins: [
// DefinePluign 是webpack的内置插件,该插件可以在打包的时候替换制定的变量
new webpack.DefinePlugin({ // 开发环境,将环境变量改变成开发环境的值
'process.env': config.dev.env
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
// 将jQuery引入项目中,但是jQuery是通过jQuery.extend(object)以及jQuery.fn.extend(object)方法来将方法挂载到jQuery($)对象上的
// 传统的script引入,$是全局变量,所以可以直接使用,但是webpack限制了模块的作用域,所以$不在全局中,因此获取不到
// A: ProvidePlugin的机制是:当webpack加载到某个js模块里,出现了未定义且名称符合
// (字符串完全匹配)配置中key的变量时,会自动require配置中value所指定的js模块
// expose-loader,这个loader的作用是,将指定js模块export的变量声明为全局变量。
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
'window.$': 'jquery',
})
]
})
webpack.prod.conf.js
const utils = require('./utils')
const path = require('path')
const config = require('../config')
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const env = config.build.env
let webpackConfig = merge(baseWebpackConfig, {
module: {
loaders: utils.styleLoaders({sourceMap: config.build.productionSourceMap, extract: true})
},
devtool: config.build.productionSourceMap ? '#source-map' : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js')
},
vue: {
loaders: utils.cssLoaders({
sourceMap: config.build.sourceMap,
extract: true
})
},
// webpack 插件有固定的用法
// 1 利用Plugin 的初始方法并传入Plugin预设的参数进行初始化,生成一个实例
// 2 将此实例插入到webpack配置文件中的plugins参数(数组类型)里面即可
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePluign({
'process.env': env
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warning: false
}
}),
new webpack.optimize.OccurrenceOrderPlugin(),
// extract css into its own file
new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')),
// 使用正确的缓存资源哈希生成dist index.html
// 通过改变index.html可以定制output
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
// filename 生成网页的HTML名字,可以使用 '/' 来控制文件的目录结构,最终生成的路径是基于webpack配置的output.path的
filename: config.build.index,
template: 'index.html',
inject: true,
// inject 是指将加载的js文件插入到哪里,默认是插入到body的末尾,如果设置'head' 则插入到head中
minify: {
removeComments: true,
collapseWhitespace: true,
// more options: https://github.com/kangax/html-minifier#options-quick-reference
},
chunksSortMode: 'dependency'
}),
// 如果文件是多入口的文件,可能存在重复代码,将公共代码取出来,就不会重复下载公共代码了(多个页面间会共享此文件的缓存)
// CommonsChunkPlugin的初始化常用参数有解析?
// name: 这个给公共代码的chunk唯一的标识
// filename,如何命名打包后生产的js文件,也是可以用上[name]、[hash]、[chunkhash]
// minChunks,公共代码的判断标准:某个js模块被多少个chunk加载了才算是公共代码
new webpack.ptimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
// any required modules inside node_modules are extract to verdon 任何node_modules中必须的模块都将提供给verdon
return (
module.resource && module.resource.indexOf(path.join(__dirname, '../node_modules')) === 0
)
}
}),
// 将webpack运行时和模块清单提取到提取到自己的文件,防止在更新项目时更新verdon hash
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['verdon']
})
]
})
if (config.build.productionGzip) {
const 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