vue webpack模板项目配置文件分析
一、文件结构
├── README.md 项目介绍 ├── index.html 入口页面 ├── build 构建脚本目录 │ ├── build-server.js 运行本地构建服务器,可以访问构建后的页面 │ ├── build.js 生产环境构建脚本 │ ├── dev-client.js 开发服务器热重载脚本,主要用来实现开发阶段的页面自动刷新 │ ├── dev-server.js 运行本地开发服务器 │ ├── utils.js 构建相关工具方法 │ ├── webpack.base.conf.js wabpack基础配置 │ ├── webpack.dev.conf.js wabpack开发环境配置 │ └── webpack.prod.conf.js wabpack生产环境配置 ├── config 项目配置 │ ├── dev.env.js 开发环境变量 │ ├── index.js 项目配置文件 │ ├── prod.env.js 生产环境变量 │ └── test.env.js 测试 环境变量 ├── mock mock数00据目录 │ └── hello.js ├── package.json npm包配置文件,里面定义了项目的npm脚本,依赖包等信息 ├── src 源码目录 │ ├── main.js 入口js文件 │ ├── app.vue 根组件 │ ├── components 公共组件目录 │ │ └── title.vue │ ├── assets 资源目录,这里的资源会被wabpack构建 │ │ └── images │ │ └── logo.png │ ├── routes 前端路由 │ │ └── index.js │ ├── store 应用级数据(state) │ │ └── index.js │ └── views 页面目录 │ ├── hello.vue │ └── notfound.vue ├── static 纯静态资源,不会被wabpack构建。 └── test 测试文件目录(unit&e2e) └── unit 单元测试 ├── index.js 入口脚本 ├── karma.conf.js karma配置文件 └── specs 单测case目录 └── Hello.spec.js
二、指令分析
package.json
"scripts": { "dev": "node build/dev-server.js", "build": "node build/build.js", "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", "e2e": "node test/e2e/runner.js", "test": "npm run unit && npm run e2e", "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs" }
如上面的指令所示,当执行
npm run dev 则执行build文件下的dev-server.js
npm run build 则执行 build文件下的build文件
三、文件逐个分析
config文件夹
│ ├── dev.env.js 开发环境变量 │ ├── index.js 项目配置文件 │ ├── prod.env.js 生产环境变量 │ └── test.env.js 测试 环境变量
1、 dev.env.js
var merge = require('webpack-merge') var prodEnv = require('./prod.env') //开发的环境名为:development module.exports = merge(prodEnv, { NODE_ENV: '"development"' })
2、prod.env.js
//生产环境名:production module.exports = { NODE_ENV: '"production"' }
3、test.env.js
var merge = require('webpack-merge') var devEnv = require('./dev.env') //测试的环境名“testing” module.exports = merge(devEnv, { NODE_ENV: '"testing"' })
4、index.js
生产环境与开发环境的的配置
var path = require('path') module.exports = { build: { env: require('./prod.env'), index: path.resolve(__dirname, '../dist/index.html'), //生成的根目录 assetsRoot: path.resolve(__dirname, '../dist'), //生成的分目录 assetsSubDirectory: 'static', assetsPublicPath: '/', productionSourceMap: true, // Gzip off by default as many popular static hosts such as // Surge or Netlify already gzip all static assets for you. // Before setting to `true`, make sure to: // npm install --save-dev compression-webpack-plugin productionGzip: false, productionGzipExtensions: ['js', 'css'] }, dev: { env: require('./dev.env'), port: 8080, assetsSubDirectory: 'static', assetsPublicPath: '/', proxyTable: {}, // CSS Sourcemaps off by default because relative paths are "buggy" // with this option, according to the CSS-Loader README // (https://github.com/webpack/css-loader#sourcemaps) // In our experience, they generally work as expected, // just be aware of this issue when enabling this option. cssSourceMap: false } }
build文件夹
utils.js 构建相关工具方法
var path = require('path') var config = require('../config') // extract-text-webpack-plugin可以提取bundle中的特定文本,将提取后的文本单独存放到另外的文件 // 这里用来提取css样式 var ExtractTextPlugin = require('extract-text-webpack-plugin') // 资源文件的存放路径 exports.assetsPath = function (_path) { var assetsSubDirectory = process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory return path.posix.join(assetsSubDirectory, _path) } // 生成css、sass、scss等各种用来编写样式的语言所对应的loader配置 exports.cssLoaders = function (options) { options = options || {} // css-loader配置 var cssLoader = { loader: 'css-loader', options: { // 是否最小化 minimize: process.env.NODE_ENV === 'production', // 是否使用source-map sourceMap: options.sourceMap } } // generate loader string to be used with extract text plugin // 生成各种loader配置,并且配置了extract-text-pulgin function generateLoaders (loader, loaderOptions) { // 默认是css-loader var loaders = [cssLoader] // 如果非css,则增加一个处理预编译语言的loader并设好相关配置属性 // 例如generateLoaders('less'),这里就会push一个less-loader // less-loader先将less编译成css,然后再由css-loader去处理css // 其他sass、scss等语言也是一样的过程 if (loader) { loaders.push({ loader: loader + '-loader', options: Object.assign({}, loaderOptions, { sourceMap: options.sourceMap }) }) } // Extract CSS when that option is specified // (which is the case during production build) if (options.extract) { // 配置extract-text-plugin提取样式 return ExtractTextPlugin.extract({ use: loaders, fallback: 'vue-style-loader' }) } else { // 无需提取样式则简单使用vue-style-loader配合各种样式loader去处理<style>里面的样式 return ['vue-style-loader'].concat(loaders) } } // https://vue-loader.vuejs.org/en/configurations/extract-css.html // 得到各种不同处理样式的语言所对应的loader return { css: generateLoaders(), postcss: generateLoaders(), less: generateLoaders('less'), sass: generateLoaders('sass', { indentedSyntax: true }), scss: generateLoaders('sass'), stylus: generateLoaders('stylus'), styl: generateLoaders('stylus') } } // Generate loaders for standalone style files (outside of .vue) // 生成处理单独的.css、.sass、.scss等样式文件的规则 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 + '$'), use: loader }) } return output }
build/webpack.base.conf.js
var path = require('path') var config = require('../config') var utils = require('./utils') var projectRoot = path.resolve(__dirname, '../') module.exports = { //webpack入口文件 entry: { main: './src/main.js' }, output: { //webpack文件输出的目标文件夹路径(./dist) // output:“path”项和“publicPath”项 //output项告诉webpack怎样存储输出结果以及存储到哪里。output的两个配置项“path”和“publicPath”可能会造成困惑。 //“path”仅仅告诉Webpack结果存储在哪里,然而“publicPath”项则被许多Webpack的插件用于在生产模式下更新内嵌到css、html文件里的url值。 path: config.build.assetsRoot, publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath, filename: '[name].js', chunkFilename: '[name].js', }, resolve: { // 别名,方便引用模块,例如有了别名之后, // import Vue from 'vue/dist/vue.common.js'可以写成 import Vue from 'vue' extensions: ['', '.js', '.vue'], fallback: [path.join(__dirname, '../node_modules')], alias: { 'src': path.resolve(__dirname, '../src'), 'assets': path.resolve(__dirname, '../src/assets'), 'views': path.resolve(__dirname, '../src/views'), 'routes': path.resolve(__dirname, '../src/routes'), 'store': path.resolve(__dirname, '../src/store'), 'components': path.resolve(__dirname, '../src/components') } },
//插件的记载器 resolveLoader: { fallback: [path.join(__dirname, '../node_modules')] }, module: { 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]') } } ] }, vue: { loaders: utils.cssLoaders(), postcss: [require('postcss-cssnext')()] }, plugins: [
] };
生产环境下
webpack.prod.conf.js
- 合并基础的webpack配置
- 配置样式文件的处理规则,styleLoaders
- 配置webpack的输出
- 配置webpack插件
- gzip模式下的webpack插件配置
- webpack-bundle分析
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 }) }, devtool: config.build.productionSourceMap ? '#source-map' : false, output: { path: config.build.assetsRoot, filename: utils.assetsPath('js/[name].[chunkhash].js'), chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') }, vue: { loaders: utils.cssLoaders({ sourceMap: config.build.productionSourceMap, extract: true }) }, plugins: [ // http://vuejs.github.io/vue-loader/en/workflow/production.html //环境 new webpack.DefinePlugin({ 'process.env': env }), // 丑化压缩JS代码 new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }), new webpack.optimize.OccurrenceOrderPlugin(), // extract css into its own file // 将css提取到单独的文件 new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')), // generate dist index.html with correct asset hash for caching. // you can customize output by editing /index.html // see https://github.com/ampedandwired/html-webpack-plugin // 将产品文件的引用注入到index.html new HtmlWebpackPlugin({ filename: config.build.index, template: 'index.html', inject: true, minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true // more options: // https://github.com/kangax/html-minifier#options-quick-reference }, // necessary to consistently work with multiple chunks via CommonsChunkPlugin chunksSortMode: 'dependency' }), // split vendor js into its own file // 将所有从node_modules中引入的js提取到vendor.js,即抽取库文件 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 ) } }), // extract webpack runtime and module manifest to its own file in order to // prevent vendor hash from being updated whenever app bundle is updated // 从vendor中提取出manifest,原因如上 new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', chunks: ['vendor'] }) ] }) // 如果开启了产品gzip压缩,则利用插件将构建后的产品文件进行压缩 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
// https://github.com/shelljs/shelljs //检查NodeJS和npm的版本 require('./check-versions')() require('shelljs/global') env.NODE_ENV = 'production' var path = require('path') var config = require('../config') var ora = require('ora') var webpack = require('webpack') var webpackConfig = require('./webpack.prod.conf') console.log( ' Tip:\n' + ' Built files are meant to be served over an HTTP server.\n' + ' Opening index.html over file:// won\'t work.\n' ) var spinner = ora('building for production...') // 开启loading动画 spinner.start() // 输出文件的目标文件夹 var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory) // 递归删除旧的目标文件夹 rm('-rf', assetsPath) //重新创建文件夹 mkdir('-p', assetsPath) // 将static文件夹复制到输出的目标文件 cp('-R', 'static/*', assetsPath) webpack(webpackConfig, function (err, stats) { // 停止loading动画 spinner.stop() if (err) throw err // 没有出错则输出相关信息 process.stdout.write(stats.toString({ colors: true, modules: false, children: false, chunks: false, chunkModules: false }) + '\n') })
开发环境下
webpack.dev.conf.js
var config = require('../config') var webpack = require('webpack') var merge = require('webpack-merge') var utils = require('./utils') var baseWebpackConfig = require('./webpack.base.conf') var HtmlWebpackPlugin = require('html-webpack-plugin') // add hot-reload related code to entry chunks
//添加热加载实时更新代码
Object.keys(baseWebpackConfig.entry).forEach(function (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: [ new webpack.DefinePlugin({ 'process.env': config.dev.env }), // https://github.com/glenjamin/webpack-hot-middleware#installation--usag
//全局开启代码热替换
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), // https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', inject: true }) ] })
dev-server.js
require('./check-versions')() var config = require('../config') if (!process.env.NODE_ENV) process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) var path = require('path') var express = require('express') var webpack = require('webpack') var opn = require('opn') var proxyMiddleware = require('http-proxy-middleware') var webpackConfig = require('./webpack.dev.conf') // default port where dev server listens for incoming traffic var port = process.env.PORT || config.dev.port // Define HTTP proxies to your custom API backend // https://github.com/chimurai/http-proxy-middleware var proxyTable = config.dev.proxyTable var app = express() // 数据mock var appData = require('../static/data.json') var seller = appData.seller var goods = appData.goods var ratings = appData.ratings var apiRoutes = express.Router() apiRoutes.get('/seller', function(req, res) { res.json({ errno: 0, data: seller }) }) apiRoutes.get('/goods', function(req, res) { res.json({ errno: 0, data: goods }) }) apiRoutes.get('/ratings', function(req, res) { res.json({ errno: 0, data: ratings }) }) app.use('/api', apiRoutes) var compiler = webpack(webpackConfig) var devMiddleware = require('webpack-dev-middleware')(compiler, { publicPath: webpackConfig.output.publicPath, stats: { colors: true, chunks: false } }) var hotMiddleware = require('webpack-hot-middleware')(compiler) // force page reload when html-webpack-plugin template changes compiler.plugin('compilation', function(compilation) { compilation.plugin('html-webpack-plugin-after-emit', function(data, cb) { hotMiddleware.publish({ action: 'reload' }) cb() }) }) // proxy api requests Object.keys(proxyTable).forEach(function(context) { var options = proxyTable[context] if (typeof options === 'string') { options = { target: options } } app.use(proxyMiddleware(context, options)) }) // handle fallback for HTML5 history API app.use(require('connect-history-api-fallback')()) // serve webpack bundle output app.use(devMiddleware) // enable hot-reload and state-preserving // compilation error display app.use(hotMiddleware) // serve pure static assets var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) app.use(staticPath, express.static('./static')) module.exports = app.listen(port, function(err) { if (err) { console.log(err) return } var uri = 'http://localhost:' + port console.log('Listening at ' + uri + '\n') // when env is testing, don't need open it if (process.env.NODE_ENV !== 'testing') { opn(uri) } })
- 上一篇 »vue项目webpack打包后修改配置文件
- 下一篇 »vue与webpack