vue-cli-webpack模板配置全解析-2

来源:互联网 发布:淘宝直播怎么开 编辑:程序博客网 时间:2024/06/04 18:42

vue-cli webpack 模板配置解析 - 2 重要配置文件解析

重要模块版本

  • vue-cli : 2.9.1
  • vue : 2.4.2
  • vue-router : 2.7.0
  • webpack : 3.6.0
  • 其他依赖都由 vue-cli 生成

重要配置文件解析

1. /build/dev-server.js

执行 npm run dev 或者 npm start 后运行的脚本

主要功能
  1. 检查 node、npm 版本,警告提示
  2. 启动 webpack 服务
  3. 配置挂载开发、热重载中间件
  4. 挂载代理中间件
  5. 开启 express 服务器,打开浏览器
代码详解
    'use strict'    // 检查 node vue 版本    require('./check-versions')()    // 引入默认配置    const config = require('../config')    // 设置默认环境    if (!process.env.NODE_ENV) {        process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)    }    /*       opn 插件可以强制打开浏览器跳转到指定页面       介绍:https://github.com/sindresorhus/opn    */    const opn = require('opn')    const path = require('path')    const express = require('express')    const webpack = require('webpack')    /*       http-proxy-middleware 是一个 node 代理中间件,可以将http请求代理到其他服务器       详情文档:https://github.com/chimurai/http-proxy-middleware    */    const proxyMiddleware = require('http-proxy-middleware')    // 使用 dev 开发环境的配置    const webpackConfig = require('./webpack.dev.conf')    // 指定运行端口    const port = process.env.PORT || config.dev.port    // 自动打开浏览器,默认 false    const autoOpenBrowser = !!config.dev.autoOpenBrowser    // 设置代理配置,格式参考 http-proxy-middleware 的文档    const proxyTable = config.dev.proxyTable    // 使用 express 开启服务    const app = express()    /*       webpack 函数传入一个配置对象 就会执行相应的编译       可以传入第二个参数(回调函数)可以在这里处理错误信息    */    const compiler = webpack(webpackConfig)    /*       webpack-dev-middleware       用来在内存中生成打包好的文件,而不用写到磁盘上,它提供从 webpack 到服务器的文件传输,用来配合进行热重载       第一个参数:webpack 实例       第二个参数:配置 只有 publicPath 必填,       其他参考文档 https://www.npmjs.com/package/webpack-dev-middleware    */    const devMiddleware = require('webpack-dev-middleware')(compiler, {        /*          在这里('/config/index.js' dev.assetsPublicPath)设置 publicPath          与 webpack-dev-server 异同,可以在这里查看 https://doc.webpack-china.org/configuration/dev-server/       */        publicPath: webpackConfig.output.publicPath,        quiet: true    })    /*       webpack-hot-middleware       用来接受 webpack 传递来的更新,并将其反应到浏览器客户端,需要与 webpack-dev-middleware 一起使用       使用文档:https://www.npmjs.com/package/webpack-hot-middleware    */    const hotMiddleware = require('webpack-hot-middleware')(compiler, {        log: false,        heartbeat: 2000    })    /*       当 html-webpack-plugin 模板更新的时候强制刷新页面       目前有BUG 所以禁用 BUG原因 https://github.com/jantimon/html-webpack-plugin/issues/680    */    // compiler.plugin('compilation', function (compilation) {    //   compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {    //     hotMiddleware.publish({ action: 'reload' })    //     cb()    //   })    // })    // enable hot-reload and state-preserving    // compilation error display    app.use(hotMiddleware)    /*       将代理的请求配置挂载到掐动的 express 服务上       对应 webpack-dev-server 的 'proxy' 配置    */    Object.keys(proxyTable).forEach(function(context) {        const options = proxyTable[context]        if (typeof options === 'string') {            options = {                target: options            }        }        app.use(proxyMiddleware(options.filter || context, options))    })    /*       使用 connect-history-api-fallback 匹配资源,如果不匹配就可以重定向到指定地址       对应 webpack-dev-server 'historyApiFallback: true' 配置    */    app.use(require('connect-history-api-fallback')())    // serve webpack bundle output    app.use(devMiddleware)    // 拼接 static 文件夹的静态资源路径    const staticPath = path        .posix        .join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)    // 为静态资源提供响应服务    app.use(staticPath, express.static('./static'))    // 应用的地址信息    const uri = 'http://localhost:' + port    var _resolve    var _reject    var readyPromise = new Promise((resolve, reject) => {        _resolve = resolve        _reject = reject    })    var server    var portfinder = require('portfinder')    portfinder.basePort = port    console.log('> Starting dev server...')    // 直道打好的包有效后执行回调    devMiddleware.waitUntilValid(() => {        /*          portfinder 用于获取 port       */        portfinder.getPort((err, port) => {            if (err) {                _reject(err)            }            process.env.PORT = port            var uri = 'http://localhost:' + port            console.log('> Listening at ' + uri + '\n')            // 如果不是测试环境,自动打开浏览器并跳到我们的开发地址            if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {                opn(uri)            }            server = app.listen(port)            //  server = app.listen(port, hostName) 可以在此加入主机名            _resolve()        })    })    module.exports = {        ready: readyPromise,        close: () => {            server.close()        }    }

2. /build/build.js

执行 npm run build 后运行的脚本

主要功能
  1. 创建目标文件夹
  2. 目标文件夹如果存在,则清除后重新创建
  3. 使用 webpack 编译输出
代码详解
    'use strict'    // 检查 node npm 版本    require('./check-versions')()    process.env.NODE_ENV = 'production'    const ora = require('ora') // node 终端的 loading 图    const rm = require('rimraf') // 用于深度删除模块    const path = require('path')    const chalk = require('chalk') // 命令行彩色输出    const webpack = require('webpack')    const config = require('../config')    const webpackConfig = require('./webpack.prod.conf')    // loading    const spinner = ora('building for production...')    spinner.start()    /*       使用 rimraf 将旧的编译输出的文件夹删除,然后重新编译生成       rimraf(f: 路径, [opts], callback: 回调)    */    rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {        if (err) throw err        webpack(webpackConfig, function(err, stats) {            spinner.stop()            if (err)                throw err                //  输出提示信息            process                .stdout                .write(stats.toString({colors: true, modules: false, children: false, chunks: false, chunkModules: false}) + '\n\n')            if (stats.hasErrors()) {                console.log(chalk.red('  Build failed with errors.\n'))                process.exit(1)            }            console.log(chalk.cyan('  Build complete.\n'))            console.log(chalk.yellow('  Tip: built files are meant to be served over an HTTP server.\n' +                '  Opening index.html over file:// won\'t work.\n'))        })    })

3. /build/check-version.js

/build/build.js/build/dev-server.js 都有引用

主要功能
  1. 检测 node、npm 版本,警告提示
代码详解
    'use strict'    /*       检查 node vue 版本    */    const chalk = require('chalk') // 命令行字体颜色美化    const semver = require('semver') // 版本解析插件    const packageConfig = require('../package.json')    const shell = require('shelljs') // node 中使用 shell 命令    function exec(cmd) {        // child_process 开启子进程,并执行 cmd 命令 然后返回结果        return require('child_process')            .execSync(cmd)            .toString()            .trim()    }    const versionRequirements = [        {            name: 'node',            /*             semver.clean(version) 能格式化版本号             例如:'  =v1.2.3   ' -> '1.2.3'          */            currentVersion: semver.clean(process.version),            versionRequirement: packageConfig.engines.node        }    ]    // shell.which('命令')在系统的路径搜索命令, 如果用的是 npm 就检查 npm 版本    if (shell.which('npm')) {        versionRequirements.push({          name: 'npm',          currentVersion: exec('npm --version'),          versionRequirement: packageConfig.engines.npm       })    }    module.exports = function() {        const warnings = []        for (let i = 0; i < versionRequirements.length; i++) {            const mod = versionRequirements[i]            /*          semver.satisfies('当前版本','版本范围')          用于判断版本是否符合需求,不符合要求则加入警告       */            if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {                warnings.push(mod.name + ': ' + chalk.red(mod.currentVersion) + ' should be ' + chalk.green(mod.versionRequirement))            }        }        // 警告输出        if (warnings.length) {            console.log('')            console.log(chalk.yellow('To use this template, you must update following to modules:'))            console.log()            for (let i = 0; i < warnings.length; i++) {                const warning = warnings[i]                console.log('  ' + warning)            }            console.log()            process.exit(1)        }    }

4. /build/utils.js

多处引用

主要功能
  1. 配置静态资源路径
  2. 样式文件的 loaders 配置
代码详解
    'use strict'    const path = require('path')    const config = require('../config')    const ExtractTextPlugin = require('extract-text-webpack-plugin')    // 图片、音频、视频、字体等资源的路径配置    exports.assetsPath = function(_path) {        const assetsSubDirectory = process.env.NODE_ENV === 'production'            ? config.build.assetsSubDirectory            : config.dev.assetsSubDirectory        return path.posix.join(assetsSubDirectory, _path)    }    exports.cssLoaders = function(options) {        options = options || {}        // 一个基础的 cssLoader        const cssLoader = {            loader: 'css-loader',            options: {                minimize: process.env.NODE_ENV === 'production',                sourceMap: options.sourceMap            }        }        // 一个生成 loaders 的函数,只需传入 loader:类型,loaderOptions:loader 参数        function generateLoaders(loader, loaderOptions) {            /*             loader:要加载的loader名             loaderOptions:配置          */            const loaders = [cssLoader]            if (loader) {                loaders.push({                    loader: loader + '-loader',                    options: Object.assign({}, loaderOptions, {sourceMap: options.sourceMap})                })            }            // 生产环境是,会指定提取 样式为单独文件            if (options.extract) {                return ExtractTextPlugin.extract({                use: loaders,                fallback: 'vue-style-loader'             })            } else {                return ['vue-style-loader'].concat(loaders)            }        }        // https://vue-loader.vuejs.org/en/configurations/extract-css.html        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)    exports.styleLoaders = function(options) {        const output = []        // 引用上边的 cssLoaders,得到一个样式处理方案的对象,全部执行添加 loader        const loaders = exports.cssLoaders(options)        for (const extension in loaders) {            const loader = loaders[extension]            output.push({                test: new RegExp('\\.' + extension + '$'),                use: loader            })        }        return output    }

5. /build/vue-loader.conf.js

webpack.basic-conf.js引用

主要功能
  1. 配置 css 加载
  2. 配置 将 src 等引用的资源转成模块
代码详解
    'use strict'    const utils = require('./utils')    const config = require('../config')    const isProduction = process.env.NODE_ENV === 'production'    module.exports = {        loaders: utils.cssLoaders({            sourceMap: isProduction                ? config.build.productionSourceMap                : config.dev.cssSourceMap,            extract: isProduction // 表示是否提取样式为单独文件        }),        /*          vue 中引入图片等媒体资源时,需要先 require('./images/xxx.png')          这个配置可以将 对应的属性中的内容,装换为模块,这样就可以向正常的HTML 一样使用了        */        transformToRequire: {            video: 'src',            source: 'src',            img: 'src',            image: 'xlink:href'        }    }

6. /build/webpack.base.conf.js

webpack.dev.conf.jswebpack.prod.conf.js引用

主要功能
  1. webpack 基础配置
代码详解
    'use strict'    /*       webpack 基础配置文件    */    const path = require('path')    const utils = require('./utils')    const config = require('../config')    const vueLoaderConfig = require('./vue-loader.conf')    // 封装生成绝对路径函数    function resolve(dir) {        return path.join(__dirname, '..', dir)    }    module.exports = {        // 入口        entry: {            app: './src/main.js'        },        // 出口配置        output: {            // 编译后的输出路径等            path: config.build.assetsRoot,            filename: '[name].js',            // 发布路径            publicPath: process.env.NODE_ENV === 'production'                ? config.build.assetsPublicPath                : config.dev.assetsPublicPath        },        resolve: {            // 自动补全扩展名            extensions: [                '.js', '.vue', '.json'            ],            // 模块别名,简化模块引入            alias: {                'vue$': 'vue/dist/vue.esm.js',                '@': resolve('src')            }        },        // 模块处理规则配置        module: {            rules: [                {                    test: /\.(js|vue)$/,                    loader: 'eslint-loader',                    enforce: 'pre', // 先执行                    include: [                        resolve('src'), resolve('test')                    ],                    options: {                        formatter: require('eslint-friendly-formatter')                    }                }, {                    test: /\.vue$/,                    loader: 'vue-loader',                    options: vueLoaderConfig // 规则在 vue-loader.conf.js 中                }, {                    test: /\.js$/,                    loader: 'babel-loader',                    include: [resolve('src'), resolve('test')]                }, {                    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,                    loader: 'url-loader',                    options: {                        limit: 10000,                           name: utils.assetsPath('img/[name].[hash:7].[ext]')                    }                }, {                    test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,                    loader: 'url-loader',                    options: {                        limit: 10000,                        name: utils.assetsPath('media/[name].[hash:7].[ext]')                    }                }, {                    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,                    loader: 'url-loader',                    options: {                        limit: 10000,                        name: utils.assetsPath('fonts/[name].[hash:7].[ext]')                    }                }            ]        }    }

7. /build/webpack.dev.conf.js

/build/dev-server.js 引用

主要功能
  1. 开发环境热更新配置添加
  2. 在基础配置之上增加 样式的 loaders
  3. 增加开发环境的 插件配置
代码详解
    'use strict'    /*       开发环境配置文件    */    const utils = require('./utils') // 工具文件    const webpack = require('webpack')    const config = require('../config') // 引入配置    const merge = require('webpack-merge') // webpack 配置合并    const baseWebpackConfig = require('./webpack.base.conf') // 引入基础配置    const HtmlWebpackPlugin = require('html-webpack-plugin') // 根据模板生成 HTML    const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')    /*       开发环境中,为了配合使用 热重载,需要在 入口前加入 './build/dev-client'(Hot-reload 的相对路径)    */    Object.keys(baseWebpackConfig.entry).forEach(function(name) {        baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])    })    // 合并开发环境配置到 基础配置上    module.exports = merge(baseWebpackConfig, {        module: {            /*             这部分就是定义 style 样式需要用什么 loader 解析          */            rules: utils.styleLoaders({sourceMap: config.dev.cssSourceMap})        },        // cheap-module-eval-source-map is faster for development        devtool: '#cheap-module-eval-source-map',        plugins: [            // 定义环境变量            new webpack.DefinePlugin({'process.env': config.dev.env}),            /*             热重载需要插件             地址:https://github.com/glenjamin/webpack-hot-middleware#installation--usage          */            new webpack.HotModuleReplacementPlugin(),            new webpack.NoEmitOnErrorsPlugin(),            /*             根据模板生成 HTML 的插件             配置看这里:https://github.com/ampedandwired/html-webpack-plugin          */            new HtmlWebpackPlugin({filename: 'index.html', template: 'index.html', inject: true}),            new FriendlyErrorsPlugin()        ]    })

8. /build/webpack.prod.conf.js

/build/build.js 引用

主要功能
  1. 开发环境热更新配置添加
  2. 在基础配置之上增加 样式的 loaders
  3. 增加开发环境的 插件配置
代码详解
    'use strict'    const path = require('path')    const utils = require('./utils')    const webpack = require('webpack')    const config = require('../config')    const merge = require('webpack-merge')    const baseWebpackConfig = require('./webpack.base.conf')    // copy-webpack-plugin 用于文件或者目录复制    const CopyWebpackPlugin = require('copy-webpack-plugin')    const HtmlWebpackPlugin = require('html-webpack-plugin')    const ExtractTextPlugin = require('extract-text-webpack-plugin')    // optimize-css-assets-webpack-plugin 一个 css 优化插件    const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')    const env = config.build.env    const webpackConfig = merge(baseWebpackConfig, {        module: {            // 配置样式使用的 loader            rules: 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')        },        plugins: [            // 定义环境变量            new webpack.DefinePlugin({'process.env': env}),            /*             js 压缩             UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify          */            new webpack.optimize.UglifyJsPlugin({                compress: {                    warnings: false                },                sourceMap: true // 是否生成 sourceMap            }),            // css 文件分离            new ExtractTextPlugin({                filename: utils.assetsPath('css/[name].[contenthash].css')            }),            /*             css 优化,css 复用          */            new OptimizeCSSPlugin({                cssProcessorOptions: {                    safe: true                }            }),            /*             根据模板生成 HTML             更多配置:https://www.npmjs.com/package/html-webpack-plugin          */            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                },                chunksSortMode: 'dependency'            }),            /*             HashedModuleIdsPlugin插件,             它根据模块的相对路径生成一个长度只有四位的字符串作为模块的 id,             所以只要我们不重命名一个模块文件,那么它的id就不会变,             这样解决了某一个文件修改,其他文件的 hash随之改变的问题          */            new webpack.HashedModuleIdsPlugin(),            // 抽离第三方代码            new webpack.optimize.CommonsChunkPlugin({                name: 'vendor',                minChunks: function(module) {                    // 依赖的 node_modules 文件会被提取                    return (                   module.resource &&                   /\.js$/.test(module.resource) &&                   module.resource.indexOf(                      path.join(__dirname, '../node_modules')                   ) === 0                )                }            }),            // 提取运行时代码,避免每次打包后文件更改            new webpack.optimize.CommonsChunkPlugin({             name: 'manifest',             chunks: ['vendor']          }),            // 复制静态资源            new CopyWebpackPlugin([                {                    from: path.resolve(__dirname, '../static'),                    to: config.build.assetsSubDirectory,                    ignore: ['.*'] // 忽略文件                }            ])        ]    })    // 开启 gzip 的配置    if (config.build.productionGzip) {        const CompressionWebpackPlugin = require('compression-webpack-plugin')        webpackConfig.plugins.push(            /*             压缩             更多配置:https://github.com/webpack-contrib/compression-webpack-plugin          */            new CompressionWebpackPlugin({                asset: '[path].gz[query]',                algorithm: 'gzip',                test: new RegExp(                '\\.(' +                config.build.productionGzipExtensions.join('|') +                ')$'             ),                threshold: 10240,                minRatio: 0.8            }))    }    if (config.build.bundleAnalyzerReport) {        /*          webpack-bundle-analyzer 插件          解析出模块构成、以及各自的大小体积,最后显示为一个页面          地址: https://www.npmjs.com/package/webpack-bundle-analyzer       */        const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin        webpackConfig.plugins.push(new BundleAnalyzerPlugin())    }    module.exports = webpackConfig

9. /config/index.js

多处引用

主要功能
  1. 提供开发、生产环境的用户配置
代码详解
    'use strict'    // Template version: 1.1.1    // see http://vuejs-templates.github.io/webpack for documentation.    const 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: '/', // 编译发布的根目录,最后会被写到 HTML 中,可以是服务器域名            productionSourceMap: true, // 是否开启 SourceMap (css)            // 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, // gzip 配置            productionGzipExtensions: [                'js', 'css'            ],            // Run the build command with an extra argument to            // View the bundle analyzer report after build finishes:            // `npm run build --report`            // Set to `true` or `false` to always turn it on or off            bundleAnalyzerReport: process.env.npm_config_report //是否显示 report        },        // 开发环境        dev: {            env: require('./dev.env'),            port: process.env.PORT || 8080, // 设置端口号            autoOpenBrowser: true, // 是否默认打开浏览器            assetsSubDirectory: 'static', // 二级路径、图片、音视频等媒体资源路径            assetsPublicPath: '/', // 编译发布的根目录            proxyTable: { // 代理的配置                // 更多配置 https://github.com/chimurai/http-proxy-middleware                '/text/*': {                    target: 'http://debug.xxx.com', // 要代理到的地址                    secure: false, // 是否验证 ssL 证书                    changeOrigin: true // 更改host header的origin到目标URL                },                '/uaa/*': {                    target: 'http://debug.xxx.com',                    secure: false,                    changeOrigin: true                }            },            // 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        }    }

其他

1. .babelrc 的配置,如下参考

  • 你真的会用 Babel 吗?
  • 再见,babel-preset-2015

2. .eslintrc.js

  • ESLint中文指南

参考

vue-cli的webpack模板项目配置文件说明
vue-cli#2.0 webpack 配置分析