vue config.js和webpack 配置文件解讀以及打包流程

最近把項(xiàng)目的所有配置文件都研究學(xué)習(xí)了一下,發(fā)現(xiàn)學(xué)無(wú)止境,永遠(yuǎn)都學(xué)不完呀,慢慢學(xué)吧。
本來(lái)是想拿公司項(xiàng)目來(lái)簡(jiǎn)書記錄學(xué)習(xí)過(guò)程,但是呢怕引起不必要的麻煩,所以就用了git大神的一個(gè)項(xiàng)目。
大神的目錄結(jié)構(gòu)如下,我們主要看build/和package.json


image.png

package.json中配置了項(xiàng)目的基本信息、腳本以及包信息。我們主要看下腳本,如下


image.png

--------------------------------dev-----------------------------------
先看“dev”(npm run dev),命令是:

cross-env NODE_ENV=online node build/dev-server.js

cross-env 跨平臺(tái)設(shè)置及使用環(huán)境變量 ,即將環(huán)境變量 NODE_ENV設(shè)置為online
node build/dev-server.js:執(zhí)行build/dev-server.js
分析dev-server.js:
首先看一下build-server頭部引入的文件


image.png

--> config是一個(gè)配置文件,后面會(huì)解析一下這個(gè)文件
--> path:處理與路徑轉(zhuǎn)換
express:Web應(yīng)用框架
webpack:打包工具
opn: 用于打開瀏覽器
http-proxy-middleware 用于把請(qǐng)求代理轉(zhuǎn)發(fā)到其他服務(wù)器的中間件
以上這些都是直接安裝使用的
-->./webpack.dev.conf : 自己寫的的webpack配置文件,后面會(huì)詳細(xì)解析
------config js

var path = require('path')
module.exports = {
    build: {
        // 修改環(huán)境為生產(chǎn)環(huán)境
        env: {
            NODE_ENV: '"production"'
        },
        //index  即打包后會(huì)生成啟動(dòng)首頁(yè),index.html
        index: path.resolve(__dirname, '../elm/index.html'),
        // 打包后 生成的資源存根目錄
        assetsRoot: path.resolve(__dirname, '../elm'),
        //資源子目錄
        assetsSubDirectory: 'static',
        //這個(gè)是通過(guò)http服務(wù)器運(yùn)行的url路徑,一般都是根目錄
        assetsPublicPath: '/elm/',
        productionSourceMap: true,//設(shè)置成false,打包的體積會(huì)減小80%
        // 是否開啟Gzip壓縮
        productionGzip: false,
        // 需要使用 gzip 壓縮的文件擴(kuò)展名
        productionGzipExtensions: ['js', 'css']
    },
    dev: {
        env: {
            NODE_ENV: '"development"'
        },
        port: 8000,// 運(yùn)行測(cè)試頁(yè)面的端口
        assetsSubDirectory: 'static',// 編譯輸出的二級(jí)目錄
        assetsPublicPath: '/',// 編譯發(fā)布的根目錄,可配置為資源服務(wù)器域名或 CDN 域名
        context: [ //代理路徑
            '/shopping',
            '/ugc',
            '/v1',
            '/v2',
            '/v3',
            '/v4',
            '/bos',
            '/member',
            '/promotion',
            '/eus',
            '/payapi',
            '/img',
        ],
        proxypath: 'http://cangdu.org:8001',// 需要 proxyTable 代理的接口(可跨域)
        cssSourceMap: false
    }
}

config配置的目的都是為了服務(wù)webpack的配置,給不同的編譯條件提供配置
build: 打包上線的配置;可以看出會(huì)將打包好的文件放在elm目錄下,啟動(dòng)的首頁(yè)index,html,資源目錄是elm/static
dev:本地啟動(dòng)測(cè)試的配置;運(yùn)行的端口8000

----- webpack.dev.conf
webpack.dev中也引入了3個(gè)js,如下圖


image.png

config.js已經(jīng)在上部分看過(guò),下面看下utils;
---->utils

var path = require('path')
var config = require('../config')
//主要是為了抽離css樣式,防止將樣式打包在js中引起頁(yè)面樣式加載錯(cuò)亂的現(xiàn)象.
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是跨平臺(tái)兼容
    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比較簡(jiǎn)單,就是動(dòng)態(tài)生成cssloader表,很多時(shí)候我們都是直接列出來(lái),這邊就是通過(guò)js的方式動(dòng)態(tài)生成,其實(shí)是一樣的。

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'
    },
    //導(dǎo)出配置
    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如何尋找模塊對(duì)應(yīng)的文件。
    resolve: {
        //配置在查找文件的過(guò)程中用到的后綴列表
        extensions: ['', '.js', '.vue', '.less', '.css', '.scss'],
        //如果webpack 在 resolve.root 或者 resolve.modulesDirectories 實(shí)在找不到某個(gè)模塊了,會(huì)去這個(gè)目錄中找
        fallback: [path.join(__dirname, '../node_modules')],
        //alias通過(guò)別名來(lái)把原導(dǎo)入路徑映射成一個(gè)新的導(dǎo)入路徑
        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準(zhǔn)備的。
    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']
            })
        ]
    }
}

以上配置就是開發(fā)環(huán)境和測(cè)試環(huán)境公用的配置,具體的意思見代碼
--->webpack.dev.conf

var config = require('../config')
var webpack = require('webpack')
var merge = require('webpack-merge')//將對(duì)象or數(shù)組合并
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,方便代碼調(diào)試
    devtool: '#eval-source-map',
    //配置插件
    plugins: [
        // 可以簡(jiǎn)單的理解為將process.env 變成全局變量,可以在其他地方引用
        new webpack.DefinePlugin({
            'process.env': config.dev.env
        }),
        //根據(jù)模塊調(diào)用次數(shù),給模塊分配ids,常被調(diào)用的ids分配更短的id,使得ids可預(yù)測(cè),降低文件大小
        new webpack.optimize.OccurenceOrderPlugin(),
        //熱更新--無(wú)刷新實(shí)現(xiàn)代碼更新
        new webpack.HotModuleReplacementPlugin(),
        //跳過(guò)編譯時(shí)出錯(cuò)的代碼并記錄,使編譯后運(yùn)行時(shí)的包不會(huì)發(fā)生錯(cuò)誤
        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: [
       // 可以簡(jiǎn)單的理解為將process.env 變成全局變量,可以在其他地方引用
        new webpack.DefinePlugin({
            'process.env': env
        }),
        //對(duì)js進(jìn)行壓縮
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        }),
        //根據(jù)模塊調(diào)用次數(shù),給模塊分配ids,常被調(diào)用的ids分配更短的id,使得ids可預(yù)測(cè),降低文件大小
        new webpack.optimize.OccurrenceOrderPlugin(),
        // 抽離css樣式,防止將樣式打包在js中引起頁(yè)面樣式加載錯(cuò)亂的現(xiàn)象
        new ExtractTextPlugin(utils.assetsPath('css/[name].css')),
        //將生成的html模版和我們?cè)趏utput-filename生成的js產(chǎn)生關(guān)系,就是在讓html啟動(dòng)引入js
        new HtmlWebpackPlugin({
            filename: config.build.index,
            template: 'index.html',
            inject: true,
            chunksSortMode: 'dependency'
        }),
        // 主要是用來(lái)提取第三方庫(kù)和公共模塊,即將公共模塊提取到vendor.js中,
        //避免首屏加載的bundle文件或者按需加載的bundle文件體積過(guò)大,
        // 從而導(dǎo)致加載時(shí)間過(guò)長(zhǎng),著實(shí)是優(yōu)化的一把利器。
        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的運(yùn)行文件、第三方庫(kù)和自定義公共模塊單獨(dú)抽離出
        // 避免webpack運(yùn)行文件改變導(dǎo)致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')//實(shí)現(xiàn)命令行環(huán)境的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 結(jié)束
    if (err) throw err
    process.stdout.write(stats.toString({
        colors: true,
        modules: false,
        children: false,
        chunks: false,
        chunkModules: false
    }) + '\n')
})
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容