從搭建vue-腳手架到掌握webpack配置(四.自動(dòng)化封裝)

前言

還有幾天就初一了枉圃,先在這里個(gè)大家拜個(gè)年功茴,恭喜發(fā)財(cái),新年好D跚住?泊!

上一期我們對(duì)webpack的構(gòu)建進(jìn)行了改進(jìn)返劲,添加了babelrc和postcss編譯器玲昧,還有把項(xiàng)目的構(gòu)建能力適應(yīng)了多頁(yè)面開(kāi)發(fā)。但是大家發(fā)現(xiàn)這個(gè)工程還不能算得上是一個(gè)腳手架旭等,尤其是添加了多頁(yè)面能力之后酌呆,每次添加頁(yè)面都要手動(dòng)添加插件配置,所以我們要進(jìn)行一些簡(jiǎn)單的封裝搔耕,達(dá)到通過(guò)簡(jiǎn)單配置進(jìn)行統(tǒng)一設(shè)置配置的效果隙袁。

GitHub : https://github.com/wwwjason1994/vue-cli-yourself

本期重點(diǎn):對(duì)目前的項(xiàng)目進(jìn)行簡(jiǎn)單的封裝,簡(jiǎn)化項(xiàng)目配置難度

往期鏈接:

從搭建vue-腳手架到掌握webpack配置(一.基礎(chǔ)配置)

從搭建vue-腳手架到掌握webpack配置(二.插件與提取)

從搭建vue-腳手架到掌握webpack配置(三.多頁(yè)面構(gòu)建)

構(gòu)思配置文件

首先我們要構(gòu)思一下具體那些配置項(xiàng)是我們會(huì)經(jīng)常用到的弃榨,而且會(huì)需要經(jīng)常修改的菩收。

新建一個(gè)config目錄,在該目錄下新建index.js文件

index.js的內(nèi)容如下:

 const config = {
    page:{
        index:'./src/main.js',
        home: ['./src/home.js','home page']
    },
    defaultTitle:"this is all title",//頁(yè)面的默認(rèn)title
    externals : {//大三方外部引入庫(kù)聲明
        'jquery':'window.jQuery'
    },
    cssLoader : 'less',//記得預(yù)先安裝對(duì)應(yīng)loader
    // cssLoader : 'less!sass',//可以用!號(hào)添加多個(gè)css預(yù)加載器
    usePostCSS :  true, //需要提前安裝postcss-loader
    toExtractCss : true,

    assetsPublicPath: '/',//資源前綴鲸睛、可以寫cdn地址
    assetsSubDirectory: 'static',//創(chuàng)建的的靜態(tài)資源目錄地址

    host: 'localhost', // can be overwritten by process.env.HOST
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
    autoOpenBrowser: false,//調(diào)試開(kāi)啟時(shí)是否自動(dòng)打開(kāi)瀏覽器
    
    uglifyJs : true,//是否丑化js
    sourceMap : true,//是否開(kāi)啟資源映射
    plugins:[]//額外插件
}
module.exports = config;

注意:配置項(xiàng)中的路徑都相對(duì)于跟目錄

配置項(xiàng)說(shuō)明

page:就如webpack.config里面的entry,進(jìn)行了改良娜饵,如果屬性是數(shù)組的話,第二個(gè)參數(shù)是html的標(biāo)題(title)

defaultTitle:是所有頁(yè)面默認(rèn)的title

externals:如注釋

cssLoader:要使用的css預(yù)加載器官辈,可以用!分割設(shè)置多個(gè)加載器箱舞,使用的同時(shí)記住npm install less-loader安裝對(duì)應(yīng)的loader

usePostCss:是否使用postcss,也是要預(yù)先安裝post-loader

toExtractCss:是否抽取css文件

assetsPublicPath:資源的公共地址前綴,頁(yè)面的所有資源引入會(huì)指向該地址拳亿,可以是一個(gè)cdn的域名晴股。

assetsSubDirectory:要在根目錄下創(chuàng)建一個(gè)static目錄存放不被webpack編譯的文件(靜態(tài)文件),而assetsSubDirectory值是dist目錄下的靜態(tài)資源地址肺魁,如值是static的話电湘,build之后,~\static目錄下的文件就會(huì)被復(fù)制到dist/static目錄下

host \port \autoOpenBrower:如注釋

uglifyJs \sourceMap :如注釋

plugins:可以new一些插件進(jìn)去

整理項(xiàng)目配置

在config目錄下放封裝的配置邏輯腳本文件鹅经,新建一個(gè)static目錄放靜態(tài)資源寂呛,多出的幾個(gè)文件后面會(huì)慢慢道來(lái)

image

按版本生成代碼

以免多次build代碼的時(shí)候都會(huì)覆蓋上次的生成記錄,我們可以做一個(gè)小優(yōu)化瘾晃,用package.json里的version值作為目錄名在dist下生成如dist/1.0.0/的目錄

image

只要改一下output的值就能實(shí)現(xiàn)這一需求

output:{
        path:path.resolve(__dirname,'./dist/'+ process.env.npm_package_version),
        filename:"js/[name].js"
    },

process.env.npm_package_version能得到package.json里的version值,具體參考這里

在每次build之前按需要修改package.json里的version值就可以區(qū)分版本生成目錄

修改npm script

"scripts": {
    "clean": "node config/build.js",
    "build": "webpack --progress --hide-modules --config config/webpack.prod.conf.js",
    "dev": "cross-env NODE_ENV=development webpack-dev-server --hot",
    "c-b": "npm run clean && npm run build"
  },

你會(huì)發(fā)現(xiàn)多了一個(gè)clean和c-b,而且build指向了一個(gè)新的文件 config/webpack.prod.conf.js,clean也運(yùn)行了一個(gè)新的文件config/build.js贷痪。從名字能看出來(lái)clean是用來(lái)清理目錄的,c-b是clean和build一起運(yùn)行的

所以改完了npm script之后我們?cè)赾onfig目錄創(chuàng)建這兩個(gè)文件吧蹦误。

config/webpack.prod.conf.js文件用于獨(dú)立生成環(huán)境是用到的webpack配置項(xiàng)

config/build.js是清理邏輯呢诬。

先清理目錄

config/build.js內(nèi)容如下:

'use strict'
process.env.NODE_ENV = 'production'

const rm = require('rimraf')
const path = require('path')
// const webpack = require('webpack')
// const webpackConfig = require('./webpack.prod.conf')


rm(path.resolve(__dirname,'../dist/'+ process.env.npm_package_version), err => {
  if (err) throw err
  // webpack(webpackConfig, (err, stats) => {
  //   if (err) throw err
  // })
})

就是簡(jiǎn)單地用node的rimraf組件刪除當(dāng)前版本的目錄
為什么有一些注釋的部分呢涌哲?
其實(shí)這些代碼是從官方的vue-cli里面粘貼過(guò)來(lái)的,原本vue-cli默認(rèn)是刪除和webpack運(yùn)行一起執(zhí)行的尚镰,但是我發(fā)現(xiàn)這樣做 一來(lái)沒(méi)有了webpack --progress加載進(jìn)度顯示阀圾,二來(lái)要引入很多node插件來(lái)書寫加載提示,三來(lái)clean和build一起執(zhí)行太過(guò)絕對(duì)了狗唉。所以我把運(yùn)行webpack的邏輯注釋掉了初烘,然后用npm script里的build進(jìn)行代替。

獨(dú)立生產(chǎn)環(huán)境配置

在上幾期我們簡(jiǎn)單的用if (process.env.NODE_ENV === 'production')作為生產(chǎn)環(huán)境的判斷分俯,在webpack.config.js文件里面一起編寫配置項(xiàng)肾筐。為了規(guī)范化和獨(dú)立性,把if里的內(nèi)容抽離到一個(gè)新的文件(config/webpack.prod.conf.js)里面,如下

process.env.NODE_ENV = 'production'
const path = require('path')
const config = require('../config')
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseWebpackConfig = require("../webpack.config.js")

const webpackConfig = merge(baseWebpackConfig, {
    devtool : '#source-map',
    output:{
        path:path.resolve(__dirname,'../dist/'+ process.env.npm_package_version),
        filename:"js/[name].[chunkhash].js",
        chunkFilename:"js/[id].[chunkhash].js",
        publicPath:config.assetsPublicPath || '/'
    },
    plugins :[
        new webpack.DefinePlugin({
            'process.env': {
            NODE_ENV: '"production"'
            }
        }),
        new webpack.LoaderOptionsPlugin({
            minimize: true
        }),
        //提取多入庫(kù)的公共模塊
        Object.keys(config.page).length >= 2 ? new webpack.optimize.CommonsChunkPlugin({
            name: 'common',
            minChunks:2
        }):()=>{},
        //抽取從node_modules引入的模塊缸剪,如vue
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vender',
            minChunks:function(module,count){
                var sPath = module.resource;
                // console.log(sPath,count);
                return sPath &&
                    /\.js$/.test(sPath) &&
                    sPath.indexOf(
                        path.join(__dirname, '../node_modules')
                    ) === 0
            }
        }),
        //將webpack runtime 和一些復(fù)用部分抽取出來(lái)
        new webpack.optimize.CommonsChunkPlugin({
            name: 'manifest',
            minChunks:Infinity
        }),
        //將import()異步加載復(fù)用的公用模塊再進(jìn)行提取
        new webpack.optimize.CommonsChunkPlugin({
            // name: ['app','home'],
            async: 'vendor-async',
            children: true,
            deepChildren:true,
            minChunks:2
        }),
    ]
})
if(config.uglifyJs){
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.optimize.UglifyJsPlugin({
            sourceMap: config.sourceMap,
            compress: {
            warnings: false
            }
        }),
    ])
}
if(config.sourceMap){
    module.exports.devtool = false
}
module.exports = webpackConfig

process.env.NODE_ENV = 'production'首先聲明當(dāng)前是生成環(huán)境

const config = require('../config')引入了上面構(gòu)思的配置項(xiàng)的配置文件

你會(huì)發(fā)現(xiàn)用到了webpack-merge插件(記得npm install --save-dev webpack-merge)吗铐,顧名思義這是合并兩個(gè)對(duì)象里面的webpack配置項(xiàng)的。

其他的就是生產(chǎn)代碼時(shí)用到的公共塊抽取插件杏节、丑化js等插件唬渗,不清楚這些插件的可以翻回去看(二)和(三)期
還有重新配置了一下output的配置,主要是多了publicPath項(xiàng)奋渔,這是設(shè)置支援公用地址前綴的配置項(xiàng)镊逝。

重點(diǎn)來(lái)了!

用配置項(xiàng)進(jìn)行自動(dòng)配置項(xiàng)目的魅力就在于嫉鲸,可以通過(guò)通俗易懂的配置規(guī)則得到配置復(fù)雜的webpack構(gòu)建邏輯的效果撑蒜。這也正正是考驗(yàn)一個(gè)程序員編程能力的地方,半自動(dòng)或全自動(dòng)的配置背后可是執(zhí)行著你封裝的邏輯腳本玄渗。

自動(dòng)配置多入口

我們之前構(gòu)思的配置項(xiàng)第一個(gè)page就是一個(gè)聲明多入口座菠,他類似wepack里的entry,每一項(xiàng)對(duì)應(yīng)一個(gè)入口藤树,也對(duì)應(yīng)一個(gè)頁(yè)面辈灼,如下就對(duì)應(yīng)兩個(gè)頁(yè)面

page:{
        index:'./src/main.js',
        home: ['./src/home.js','home page']
    },

這可以說(shuō)是半自動(dòng)的配置方法,有的人會(huì)傾向于全自動(dòng)的方法也榄,就是通過(guò)查詢給定的文件目錄下包含的入口js文件自動(dòng)生成入口配置,而不用像我這樣手動(dòng)聲明用到的入口司志。感興趣的可以參考這里:link

Jason做過(guò)不少的小程序開(kāi)發(fā)甜紫,比較習(xí)慣明確的列出所包含的頁(yè)面,所以更青睞這種半自動(dòng)的配置方式骂远。列明頁(yè)面入口不僅方面深入添加自己想要的規(guī)則囚霸,而且可以通過(guò)該配置項(xiàng)知道本項(xiàng)目包含哪些頁(yè)面。

開(kāi)始封裝

config/index.js底下開(kāi)始封裝我們要的邏輯激才,當(dāng)然你可以獨(dú)立出一個(gè)新的文件拓型。這里寫到一起是因?yàn)槎詈伲环矫婵紤]到入門教程的復(fù)雜性,另一方面我們可以在同一個(gè)文件下一遍對(duì)照配置項(xiàng)一遍封裝邏輯劣挫。

 const config = {
    page:{
        index:'./src/main.js',
        home: ['./src/home.js','home page']
    },
    //...
}
module.exports = config;

/**
 * some auto-create-function
 */
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')

const isProduction = process.env.NODE_ENV === 'production'

//自動(dòng)生成HTML模板
const createHTMLTamplate = function(obj){
    let htmlList = [];
    let pageList = obj;
    for(let key in pageList){
        htmlList.push(
            new HtmlWebpackPlugin({
                filename: key + '.html',
                title:Array.isArray(pageList[key])&&pageList[key][1]
                    ?pageList[key][1].toString()
                    :config.defaultTitle,
                template:path.resolve(__dirname,'../index.html'),
                chunks:[key,'vender','manifest','common'],
                chunksSortMode: 'dependency'
            })
        )
    }
    return htmlList
}
//設(shè)置多入口
const setEntry = function(obj){
    let entry = {};
    let pageList = obj;
    for(let key in pageList){
        if(Array.isArray(pageList[key]) && pageList[key][0]){
            entry[key] = path.resolve(__dirname,'../'+pageList[key][0].toString());
        }else{
            entry[key] = path.resolve(__dirname,'../'+pageList[key].toString());
        }
    }
    return entry
}

module.exports.plugins = (module.exports.plugins || []).concat(
    createHTMLTamplate(config.page)
);
module.exports.entry = setEntry(config.page);

有點(diǎn)編程能力的同學(xué)不難看懂這些邏輯册养。只是遍歷page值里面的每一項(xiàng),返回entry和html模板插件數(shù)組压固。注意一點(diǎn)就是這里用了Array.isArray(pageList[key]判斷當(dāng)前是否數(shù)組球拦,作簡(jiǎn)單的值兼容。

還是那句帐我,不懂HtmlWebpackPlugin看前兩期 或者 看這里

createHTMLTamplate 返回對(duì)應(yīng)配置項(xiàng)的HtmlWebpackPlugin插件列表

setEntry 返回入口chunk對(duì)象entry

回到webpack.config.js

然后我們返回到webpack.config.js文件把這些方法的然后值引用到對(duì)應(yīng)的配置項(xiàng)上坎炼。留意注釋~~~~我在這里~~~~

const path = require('path')
const config = require('./config')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const webpack = require('webpack')
const merge = require('webpack-merge')

const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)

baseWebpackConfig = {
    entry:config.entry, //~~~~~~~~~我在這里~~~~~~~~
    output:{
        path:path.resolve(__dirname,'./dist/'+ process.env.npm_package_version),
        filename:"js/[name].js"
    },
    module:{
        rules:[
        //...
        ]
    },
    plugins:[
        new CopyWebpackPlugin([
            {
              from: path.resolve(__dirname, './static'),
              to: config.assetsSubDirectory,
            }
        ])
    ].concat(config.plugins),//~~~~~~~我在這里~~~~~~~
    resolve:{
        extensions: ['.js', '.vue', '.json'],
        alias:{
            'vue$':'vue/dist/vue.esm.js',// 'vue/dist/vue.common.js' for webpack 1
            '@': path.resolve(__dirname,'./src'),
        }
    },
    externals:config.externals,
}

module.exports = baseWebpackConfig;

當(dāng)然還用到了很多其他的配置項(xiàng),檢查一下config關(guān)鍵字自己對(duì)號(hào)入座拦键。

自動(dòng)添加css預(yù)處理器

同樣在config/index.js下面添加自動(dòng)配置css預(yù)處理器的邏輯谣光,下面貼出代碼有點(diǎn)長(zhǎng),但是請(qǐng)一定細(xì)心看一下芬为,有注釋幫助理解萄金,認(rèn)真看下其實(shí)重點(diǎn)邏輯也就中間一部分

 const config = {
    //...
    
    cssLoader : 'less',//記得預(yù)先安裝對(duì)應(yīng)loader
    // cssLoader : 'less!sass',//可以用!號(hào)添加多個(gè)css預(yù)加載器
    usePostCSS :  true, //需要提前安裝postcss-loader
    toExtractCss : true,
    
    //...
}
module.exports = config;

/**
 * some auto-create-function
 */
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
const ExtractTextPlugin = require("extract-text-webpack-plugin")
const ExtractRootCss = new ExtractTextPlugin({filename:'styles/root.[hash].css',allChunks:false});
const ExtractVueCss = new ExtractTextPlugin({filename:'styles/[name].[chunkhash].css',allChunks:true});

const isProduction = process.env.NODE_ENV === 'production'

//自動(dòng)生成HTML模板
const createHTMLTamplate = function(obj){
    //...
}
//設(shè)置多入口
const setEntry = function(obj){
    //...
}
//設(shè)置樣式預(yù)處理器
const cssRules = {
    less: {name:'less'},
    sass: {name:'sass', options:{indentedSyntax: true}},
    scss: {name:'sass'},
    stylus: {name:'stylus'},
    styl: {name:'stylus'}
}
//vue內(nèi)嵌樣式用到的配置規(guī)則
const cssLoaders = function(options){
    options = options || {}
    
    let loaders = {};
    const loaderList = options.loaders
    //判斷樣式是來(lái)自文件還是.vue文件內(nèi)嵌,然后用對(duì)應(yīng)的插件實(shí)例
    const ExtractCss = options.isRootCss ? ExtractRootCss : ExtractVueCss;
    const cssLoader = {
        loader: 'css-loader',
        options: {
            sourceMap: options.sourceMap
        }
    }//css-loader
    const postcssLoader = {
        loader: 'postcss-loader',
        options: {
            sourceMap: options.sourceMap
        }
    }
    //判斷是否使用postcss
    const frontLoader = options.usePostCSS ? [cssLoader,postcssLoader]:[cssLoader]
    
    //出了less等預(yù)加載的loader之外碳柱,還一定要有一般css的編譯
    if(loaderList.indexOf('css') === -1)loaderList.unshift("css")
    
    //遍歷數(shù)組生成loader隊(duì)列
    loaderList.forEach(element => {
        const loaderOptions = cssRules[element]&&cssRules[element].options;
        const loaderName = cssRules[element]&&cssRules[element].name;
        let arr = element==="css" ? [] : [{
            loader: loaderName+"-loader",
            options: Object.assign({}, loaderOptions, {
                sourceMap: options.sourceMap
            })
        }]
        //是否提取到css文件
        if(options.Extract){
            loaders[element] = ExtractCss.extract({
                use: frontLoader.concat(arr),
                fallback: 'vue-style-loader' 
            })
        }else{
            loaders[element] = ['vue-style-loader'].concat(frontLoader,arr)
        }
    });
    //是否提取到css文件
    if(options.Extract){
        module.exports.plugins = (module.exports.plugins || []).concat([ExtractRootCss,ExtractVueCss]);
    }

    return loaders
}
//樣式文件用到的配置規(guī)則
const styleLoaders = function(options){
    options.isRootCss = true;
    let output = [];
    const loaders = cssLoaders(options);

    for (const extension in loaders) {
        let loader = loaders[extension]
        output.push({
          test: new RegExp('\\.' + extension + '$'),
          use: loader
        })
    }
    return output
}

module.exports.plugins = (module.exports.plugins || []).concat(
    createHTMLTamplate(config.page)
);
module.exports.entry = setEntry(config.page);
module.exports.styleLoaders = styleLoaders({
    loaders: config.cssLoader.split('!'),
    sourceMap : config.sourceMap,
    usePostCSS : config.usePostCSS,
    Extract : isProduction&&config.toExtractCss,//生成環(huán)境才判斷是否進(jìn)行提取
});
module.exports.cssLoaders = cssLoaders({
    loaders: config.cssLoader.split('!'),
    sourceMap : config.sourceMap,
    //vue-loader內(nèi)部自動(dòng)開(kāi)啟postcss所以開(kāi)發(fā)環(huán)境下會(huì)有警告捡絮,所以也是生成環(huán)境才進(jìn)行進(jìn)一步判斷
    usePostCSS : isProduction&&config.usePostCSS,
    Extract : isProduction&&config.toExtractCss,
});

有看過(guò)vue-cli內(nèi)部封裝的代碼(沒(méi)看過(guò)也沒(méi)關(guān)系)的同學(xué)可能會(huì)發(fā)現(xiàn)以上的代碼有點(diǎn)像vue-cli里面的邏輯。Jason確實(shí)借鑒了一點(diǎn)vue-cli的封裝思想莲镣。

cssRules :該對(duì)象是預(yù)先設(shè)定好不同預(yù)加載的名稱很options配置項(xiàng)福稳。我們會(huì)發(fā)現(xiàn)同一種css處理器也會(huì)有不一樣的規(guī)則和后綴名(就如sass和scss),構(gòu)思的配置項(xiàng)里面很難一一列出瑞侮,那么我們就要借助這里對(duì)象進(jìn)行區(qū)分的圆。后期有什么需要添加的options配置也可以在cssRules的第二個(gè)參數(shù)中添加。

cssLoaders:生成cssloader隊(duì)列的方法半火,同時(shí)可以直接賦值到vue-loader內(nèi)的規(guī)則里面越妈。返回值如下

{
    css:[vue-style-loader,css-loader],
    less:[vue-style-loader,css-loader,less-loader]
}

styleLoaders:該方法則是匹配對(duì)應(yīng)css處理器后綴名文件的配置規(guī)則。

細(xì)心的同學(xué)會(huì)留意到cssLoaders和styleLoaders钮糖,在對(duì)是否使用postcss時(shí)候多出 isProduction判斷梅掠。因?yàn)?vue-loader內(nèi)部自動(dòng)開(kāi)啟postcss所以開(kāi)發(fā)環(huán)境下會(huì)有警告,所以也是生成環(huán)境才進(jìn)行進(jìn)一步判斷是否開(kāi)啟postcss店归。

再回到webpack.config.js
baseWebpackConfig = {
    //...
    module:{
        rules:[
            {
                test:/\.js$/,
                loader:"babel-loader",
                exclude:/node_modules/
            },
            {
                test:/\.(png|jpe?j|gif|svg)(\?.*)?$/,
                loader:'url-loader',
                options:{
                    limit:10000,
                    name:'img/[name].[ext]?[hash]'
                }
            },
            {
                test:/\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                loader:"url-loader",
                options:{
                    limit:10000,
                    name:'fonts/[name].[ext]?[hash]'
                }
            },
            {
                test:/\.vue$/,
                loader:'vue-loader',
                options:{
                    loaders: config.cssLoaders //~~~~~~~我在這里~~~~~~~
                }
            },
        ].concat(config.styleLoaders) //~~~~~~~我在這里~~~~~~~
    },
    
    //...
}
module.exports = baseWebpackConfig;

好了到現(xiàn)在半自動(dòng)化的封裝邏輯都寫好了阎抒,下面選取一些需要注意的配置進(jìn)行介紹。

其他配置項(xiàng)

host \port \autoOpenBrower

host \port \autoOpenBrower 這些和開(kāi)發(fā)服務(wù)器相關(guān)的配置項(xiàng)消痛,以前引入到webpack.config.js里面且叁。

webpack.config.js下面添加代碼(你也可以想vue-cli一樣再獨(dú)立一個(gè)文件webpack.dev.conf.js),如下

const path = require('path')
const config = require('./config')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const webpack = require('webpack')
const merge = require('webpack-merge')

const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)

baseWebpackConfig = {
    entry:config.entry,
    //...
}
if (process.env.NODE_ENV === 'development') {
    console.log(process.env.NODE_ENV);
    baseWebpackConfig = merge(baseWebpackConfig,{
        devtool : '#eval-source-map',
        devServer : {
            clientLogLevel: 'warning',
            historyApiFallback: true,
            hot: true,
            compress: true,
            host: HOST || config.host,
            port: PORT || config.port,
            open: config.autoOpenBrowser,
            publicPath:config.assetsPublicPath || '/'
        },
        plugins : [
            new webpack.DefinePlugin({
                'process.env': {
                NODE_ENV: '"development"'
                }
            }),
            new webpack.HotModuleReplacementPlugin()
        ]
    })
}
module.exports = baseWebpackConfig;

顯然秩伞,host和port都可以被process.env.PORT\HOST 覆蓋逞带。其他devServer配置項(xiàng)可以參考 官方文檔

有一點(diǎn)要注意欺矫,HotModuleReplacementPlugin插件一定要在開(kāi)啟webpack-dev-server的時(shí)候才調(diào)用,所以要獨(dú)立在該開(kāi)發(fā)環(huán)境判斷中展氓。

assetsSubDirectory

一開(kāi)始構(gòu)思配置項(xiàng)的時(shí)候?qū)υ搶傩杂薪榻B

assetsSubDirectory值是dist目錄下的靜態(tài)資源地址穆趴,如值是static的話,build之后带饱,~\static目錄下的文件就會(huì)被復(fù)制到dist/static目錄下

而實(shí)現(xiàn)文件復(fù)制的插件是 CopyWebpackPlugin,使用前記得install

    new CopyWebpackPlugin([
            {
              from: path.resolve(__dirname, './static'),
              to: config.assetsSubDirectory,
            //   ignore: ['.*']
            }
        ])
assetsPublicPath

該項(xiàng)是地址前綴毡代,如果用到了第三方配置資源的地址,那么這里就可以填寫對(duì)應(yīng)的域名勺疼。

開(kāi)發(fā)環(huán)境中它在

devServer : {
    clientLogLevel: 'warning',
    historyApiFallback: true,
    hot: true,
    compress: true,
    host: HOST || config.host,
    port: PORT || config.port,
    open: config.autoOpenBrowser,
    publicPath:config.assetsPublicPath || '/'
},

生成環(huán)境中它在

output:{
    path:path.resolve(__dirname,'../dist/'+ process.env.npm_package_version),
    filename:"js/[name].[chunkhash].js",
    chunkFilename:"js/[id].[chunkhash].js",
    publicPath:config.assetsPublicPath || '/'
}

其他配置項(xiàng)都淺而易懂教寂,不多解釋,當(dāng)然你可以發(fā)揮自己的創(chuàng)造力添加更多自己需要的配置項(xiàng)执庐。

運(yùn)行一下

運(yùn)行清理并構(gòu)建

npm run c-b

得到的如下結(jié)果

image

完整的項(xiàng)目酪耕、webapck.config.js、config/index.js等文件可以下載或者克隆本項(xiàng)目的github

GitHub : https://github.com/wwwjason1994/vue-cli-yourself

總結(jié)

整個(gè)系列學(xué)習(xí)編寫vue腳手架的過(guò)程到這里算是得到了一個(gè)比較完整的入門轨淌,從一開(kāi)始入門webpack的配置項(xiàng)迂烁、到引入常用插件實(shí)現(xiàn)文件抽離、再到適配多頁(yè)面多入口递鹉、最終對(duì)項(xiàng)目進(jìn)行自動(dòng)化的封裝盟步。

不知不覺(jué)差不多實(shí)現(xiàn)vue官方給出的vue-cli里面的大部分能力,是不是發(fā)現(xiàn)自己不再是webpack的小白躏结,還挺有成就感呢却盘。

我們?cè)谡麄€(gè)學(xué)習(xí)的過(guò)程中有很多借鑒vue-cli的思想和規(guī)范∠彼可能有人會(huì)說(shuō)自己寫這么麻煩干嘛黄橘,直接用vue-cli不就行了嗎?此言差矣屈溉,這不僅是一個(gè)學(xué)習(xí)webpack的過(guò)程塞关,更是學(xué)會(huì)因地制宜按項(xiàng)目的實(shí)際情況構(gòu)建工程的過(guò)程。而且能讓我們深入體會(huì)工程化和自動(dòng)化的思想子巾。

Jason的一些話

前段時(shí)間工作有點(diǎn)忙帆赢,而且廣州寒氣逼人下班都懶得動(dòng)了,停更了差不多有一個(gè)月线梗,雖然等更新的人不多椰于,但是真的要跟有關(guān)注的同學(xué)說(shuō)一聲對(duì)不起。開(kāi)始放假而且天氣暖和了才把這一期碼完缠导,請(qǐng)大家原諒,也希望大家不要學(xué)我這個(gè)重度拖延癥患者一樣懶溉痢。

后面該碼什么文章呢僻造?

Jason寫的這些文章文筆不怎么好憋他,但都是以和大家一起學(xué)習(xí)一門技術(shù)為初衷在寫,Jason相信有意去入門webpack的同學(xué)看完了這一系列的文章肯定對(duì)webpack有了更多的了解髓削。

Jason后面會(huì)復(fù)習(xí)一下es6+ 和 想去深入學(xué)習(xí)一下node竹挡,還會(huì)寫一些vue的項(xiàng)目。后面要寫什么文章可能就看哪方面積累和了解的更深入立膛,還有哪些內(nèi)容跟適合總結(jié)成文章了揪罕。

大家有什么想和Jason一起學(xué)習(xí)的前端框架、技術(shù)宝泵,可以留言哦好啰,歡迎給意見(jiàn)和交流。

后面會(huì)不定期更新儿奶,喜歡的同學(xué)可以點(diǎn)下關(guān)注的框往。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市闯捎,隨后出現(xiàn)的幾起案子椰弊,更是在濱河造成了極大的恐慌,老刑警劉巖瓤鼻,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秉版,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡茬祷,警方通過(guò)查閱死者的電腦和手機(jī)清焕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)牲迫,“玉大人耐朴,你說(shuō)我怎么就攤上這事№镌鳎” “怎么了筛峭?”我有些...
    開(kāi)封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)陪每。 經(jīng)常有香客問(wèn)我影晓,道長(zhǎng),這世上最難降的妖魔是什么檩禾? 我笑而不...
    開(kāi)封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任挂签,我火速辦了婚禮,結(jié)果婚禮上盼产,老公的妹妹穿的比我還像新娘饵婆。我一直安慰自己,他們只是感情好戏售,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般柒瓣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悲柱,一...
    開(kāi)封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音些己,去河邊找鬼豌鸡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛段标,可吹牛的內(nèi)容都是我干的涯冠。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼怀樟,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼功偿!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起往堡,我...
    開(kāi)封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤械荷,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后虑灰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吨瞎,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年穆咐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了颤诀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡对湃,死狀恐怖崖叫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拍柒,我是刑警寧澤心傀,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站拆讯,受9級(jí)特大地震影響脂男,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜种呐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一宰翅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧爽室,春花似錦汁讼、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)卜录。三九已至,卻和暖如春眶明,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背筐高。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工搜囱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柑土。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓蜀肘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親稽屏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子扮宠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容