關(guān)于Android工程師轉(zhuǎn)成vue的三兩事兒(4)--webpack

?最近為了能夠?qū)懸环葜档脜⒖嫉膚ebpack文檔拒课,特意的去查了好多相應(yīng)的書籍畦韭,博客间校。距離上次寫的那篇文章好想也過去將近一周的時(shí)間了碌宴。我想是時(shí)候要準(zhǔn)備下一篇文章了忌怎。不然就食言而肥了。
?算了酪夷,技術(shù)類文章就直接從技術(shù)類開始說起吧榴啸。首先,學(xué)習(xí)webpack呢晚岭?是因?yàn)槲以陂_發(fā)vue和react的時(shí)候遇到了這個(gè)工具鸥印,然后最近在看人家的招聘要求的時(shí)候,總是會(huì)帶上這個(gè)坦报。

騰訊
然后我就趁著下班時(shí)間研究了一下這個(gè)東西库说。還是和之前的說法一樣,如果有任何的疑惑燎竖,請(qǐng)?jiān)诹粞詤^(qū)留言璃弄,如果我能看見一定會(huì)及時(shí)的向您反饋要销。
新弄的github代碼地址

1构回、新語言的誕生背景

新語言

?近些年來web應(yīng)用的功能需求的完善和所設(shè)計(jì)到領(lǐng)域越發(fā)寬廣導(dǎo)致前端萌生了很多新的思想和框架。我簡(jiǎn)單的介紹一下:
?首先行業(yè)的領(lǐng)導(dǎo)著提出模塊化的思想疏咐,他們認(rèn)為將一個(gè)復(fù)雜的系統(tǒng)分為多個(gè)模塊來開發(fā)會(huì)大大減少開發(fā)難度和提升開發(fā)效率纤掸。同時(shí)考慮到css只能用靜態(tài)的語法描述元素的樣式,無法像寫javascript那樣增加邏輯判斷與共享變量浑塞。于是在這種大環(huán)境下借跪,誕生了es6typescript酌壕、scss等新的語言掏愁。但是考慮到例如es6無法在瀏覽器中直接運(yùn)行,需要將es6轉(zhuǎn)換成es5之后瀏覽器才能識(shí)別卵牍。于是新的構(gòu)建工具就出現(xiàn)了果港。
?構(gòu)建工具的功能主要是:進(jìn)行代碼轉(zhuǎn)換、文件壓縮糊昙、代碼分割辛掠、模塊合并、自動(dòng)刷新释牺、代碼檢驗(yàn)萝衩、自動(dòng)發(fā)布等功能。構(gòu)建其實(shí)是工程化没咙、自動(dòng)化思想在前端開發(fā)的體驗(yàn)猩谊,我們要做的其實(shí)是用代碼去讓前端項(xiàng)目自動(dòng)化的執(zhí)行這一系列化復(fù)雜的流程罷了。
gulp

?說到構(gòu)建工具祭刚,我看百度上有很多牌捷。例如npm script队塘、grunt、gulp宜鸯、fis3憔古、webpack等等。因?yàn)楸疚闹饕侵vwebpack淋袖,如果有機(jī)會(huì)能接觸到以上的幾個(gè)工具鸿市,我會(huì)另外詳細(xì)的描述。

2即碗、 webpack的優(yōu)勢(shì)

webpack

?從我這些天無論是自己的實(shí)踐還是書上寫的來說焰情,我認(rèn)為webpack本身就是特別符合模塊化開發(fā)的一種工具。在webpack里面一切文件都是模塊剥懒,并通過loader轉(zhuǎn)換文件内舟,通過plugin注入鉤子,最后輸出由多模塊組合成的文件初橘。其優(yōu)點(diǎn)主要是:
1验游、 專注于處理模塊化開發(fā)的項(xiàng)目,能做到開箱即用保檐,一步到位
2耕蝉、 能通過plugin擴(kuò)展
3、 應(yīng)用各種領(lǐng)域
4夜只、 社區(qū)龐大
5垒在、 良好的開發(fā)體驗(yàn)
但談及為什么要選用webpack,我看書上主要有以下幾個(gè)看法:
1扔亥、大多數(shù)團(tuán)隊(duì)在開發(fā)新項(xiàng)目的時(shí)候都會(huì)采用緊跟時(shí)代的技術(shù)场躯,這些技術(shù)基本都會(huì)采用“模塊化+新語言+新框架”,webpack可以為這些新項(xiàng)目提供一站式的解決方案
2旅挤、webpack有良好的生態(tài)鏈和維護(hù)團(tuán)隊(duì)踢关,能提供一定的開發(fā)體驗(yàn)并保證質(zhì)量
3、webpack被全世界大量的web開發(fā)者使用和驗(yàn)證谦铃,能找到各個(gè)層次面所需的教程和經(jīng)驗(yàn)分享
(反正耘成,綜上所述 嗯 你再不學(xué)webpack就out了??)

3、 webpack的安裝(需要node環(huán)境滴)

  • 初始化項(xiàng)目


    建立項(xiàng)目

    如上圖所示驹闰,新建了一個(gè)項(xiàng)目瘪菌。

    npm init
    
  • 全局安裝

    // 最新版本好像變成了webpack-cli注意一下
    npm install -g webpack
    
  • 項(xiàng)目?jī)?nèi)安裝

    npm install webpack --save-dev
    

?在這里扯一句閑話,可能很多教程談到安裝webpack都會(huì)讓你選擇直接-g嘹朗,但是我并不推薦你這么做,我總感覺-g之后就成了全局變量师妙,但是我并不是每個(gè)項(xiàng)目?jī)?nèi)都能用到這個(gè)所謂的webpack,將webpack的作用域設(shè)置為項(xiàng)目?jī)?nèi)屹培,功能與全局沒有差別默穴。

4怔檩、webpack的基礎(chǔ)使用

如下圖所示,在上面新建的項(xiàng)目下面新建index.js.蓄诽。然后敲一些簡(jiǎn)單的js代碼:


index.js

然后打包該js
打包js
// 打包代碼
webpack-cli index.js --output build/bundle.js --mode development

打包成功之后就會(huì)發(fā)現(xiàn)目錄下面多了一個(gè)build目錄薛训,里面有bundle.js文件,最后新建index,html仑氛,并引用該js,就會(huì)看到如下的效果
打包后代碼
index.html
效果

但是看到這個(gè)效果我覺得并不是很滿意乙埃,因?yàn)槲蚁胍@句話成為一個(gè)紅色的。此時(shí)就要用到css了锯岖。
image.png
隨即調(diào)用
image.png
最后進(jìn)行打包的時(shí)候發(fā)現(xiàn)會(huì)報(bào)如下的錯(cuò)誤
image.png
根據(jù)其解釋應(yīng)該是少了一個(gè)loader導(dǎo)致打包失敗介袜,最后導(dǎo)入css-loader、style.css之后出吹,并且按照官方的文檔指示遇伞,成功加載出想要的頁面
加載器
注意是加載loader優(yōu)先級(jí)一定是從右向左
最終效果

但此時(shí)的問題又來了,我每次在進(jìn)行操作的時(shí)候都需要去加載一個(gè)css捶牢,每次這樣子的引用就會(huì)使得代碼變得臃腫就完全沒又體現(xiàn)webpack的核心優(yōu)勢(shì)了鸠珠。于是在查閱資料得知原來webpack和很多的框架一樣都能進(jìn)行只能配置,然后我就按照官網(wǎng)的指示一步步的進(jìn)行配置
webpack.config.js配置

打包成功
修改文字顏色之后效果
這樣做的好處也是體現(xiàn)到前面關(guān)于webpack的自動(dòng)化配置叫确、編譯的體現(xiàn)跳芳。但是我們發(fā)現(xiàn)每次編譯之后,都需要去打包竹勉、刷新頁面這樣做實(shí)在是太浪費(fèi)時(shí)間了,這里就要談到webpack-dev-server娄琉。老規(guī)矩首先安裝
 cnpm install webpack-dev-server --save

webpack-dev-server安裝完成
然后在控制臺(tái)里面輸入下面代碼次乓,然后在網(wǎng)頁中打開http://localhost:8080/發(fā)現(xiàn)就能直接將代碼運(yùn)行在網(wǎng)頁上面

 // 兩步
webpack-cli
webpack-dev-server
編譯代碼

網(wǎng)頁端顯示

但是雖然是看到了本地編譯的html被弄上了去,但是我們發(fā)現(xiàn)每次都需要編譯文件之后再進(jìn)行webpack-dev-server插件弄到網(wǎng)上去孽水,但是我每當(dāng)js改變之后 并沒能夠自動(dòng)編譯票腰,挺麻煩的。于是我便做了如下操作


修改index.html的js路徑

輸入webpack-dev-server --hot --inline

修改index.js的時(shí)候會(huì)發(fā)現(xiàn)命令行中有編譯的字樣

改變后的前端頁面

但是此時(shí)的問題又來了女气,對(duì)于一個(gè)初學(xué)者杏慰,誰會(huì)記得這么長(zhǎng),這么麻煩的代碼呢炼鞠?在Android里面一般這種東西都會(huì)用到一個(gè)配置文件寫好缘滥,然后每次都直接使用就完事兒了。說道配置文件谒主,這里就想到了webpack.json朝扼。于是就在script標(biāo)簽下面添加如下代碼,也能達(dá)到剛剛的效果


把剛剛那段話添加到script里面去

輸入npm run test

前端改變后頁面
同樣我們也可以把dev-server放置在webpack.config.js文件中,更多的配置項(xiàng)呢?煩請(qǐng)諸位看客直接去閱讀官方文檔
webpack.config.js
說到這里呢霎肯?我們雖然講js和css打包了擎颖,但是并沒有打包html文件榛斯。查閱一番知道webpack里面有一個(gè)插件是專門用來打包的:
cnpm install html-webpack-plugin --save

然后加入配置項(xiàng)

webpack.config.js加入plugin

打包

打開服務(wù)

build下面就會(huì)自動(dòng)生成index.html

生成自定的打包后文件
但是說到這么多依然沒有提到我們前面說的新語言的引用啊,前面在說優(yōu)勢(shì)的時(shí)候都說了webpack支持es6=》es5 啊搂捧,此時(shí)就不能不提到babel了驮俗。如圖安裝babel插件,然后寫一些es6的語法看看
babel引入
es6語法
導(dǎo)入babel的包
復(fù)制官網(wǎng)上面的東西
配置package.json文件
開啟服務(wù)
網(wǎng)頁效果

以上就是我無論是看其他書籍允跑,還是博客外加上閱讀官網(wǎng)上得來的一些經(jīng)驗(yàn)意述。雖然做的東西比較簡(jiǎn)單,但是這都無疑體現(xiàn)出了webpack的優(yōu)良的品質(zhì)吮蛹。如果您對(duì)以上內(nèi)容存在有任何的疑慮或者是有任何指教荤崇,歡迎提出 我會(huì)第一時(shí)間與您交流、討論潮针。
好了术荤,webpack的入門相信大家看到這里都已經(jīng)入門了,接下來我就結(jié)合一下具體的vue實(shí)際的例子來和大家進(jìn)行討論每篷。

5瓣戚、vue中的webpack

我在這里利用的就是我用vue-cli腳手架新生成的一個(gè)vue項(xiàng)目,就結(jié)合我這段時(shí)間的一些所見所聞來做一個(gè)簡(jiǎn)單的贅述:

  • index.js
'use strict' // 嚴(yán)格模式
// Template version: 1.2.7
// see http://vuejs-templates.github.io/webpack for documentation.
const config = require('./config') // 導(dǎo)入config文件
const path = require('path') //使用Node自帶的文件路徑插件

module.exports = {
  // 開發(fā)環(huán)境
  dev: {
    // Paths
    assetsSubDirectory: 'static', // 編譯輸出的二級(jí)目錄
    assetsPublicPath: '/', // 編譯發(fā)布上線路徑的根目錄焦读,可配置為資源服務(wù)器域名或 CDN 域名
    proxyTable: {
    }, // 需要 proxyTable 代理的接口(可跨域)子库,詳情請(qǐng)看之前的文章

    // Various Dev Server settings
    host: '0.0.0.0', // host,如果設(shè)置成0.0.0.0可以通過統(tǒng)一局域網(wǎng)其他設(shè)備通過ip訪問該網(wǎng)頁
    port: 8080, // 網(wǎng)頁默認(rèn)端口號(hào)矗晃,如果端口被占用會(huì)自動(dòng)分配一個(gè)隨即未被占有的端口
    autoOpenBrowser: false, // 是否自動(dòng)打開瀏覽器
    errorOverlay: true, //  在瀏覽器是否展示錯(cuò)誤蒙層
    notifyOnErrors: true, // 是否展示錯(cuò)誤的通知
    // 這個(gè)是webpack-dev-servr的watchOptions的一個(gè)選項(xiàng)仑嗅,指定webpack檢查文件的方式
    // 因?yàn)閣ebpack使用文件系統(tǒng)去獲取文件改變的通知。在有些情況下张症,這個(gè)可能不起作用仓技。例如,當(dāng)使用NFC的時(shí)候俗他,
    // vagrant也會(huì)在這方面存在很多問題脖捻,在這些情況下,使用poll選項(xiàng)(以輪詢的方式去檢查文件是否改變)可以設(shè)定為true
    // 或者具體的數(shù)值兆衅,指定文件查詢的具體周期地沮。
    poll: false,

    // Use Eslint Loader?
    useEslint: true,//  eslint代碼檢查
    showEslintErrorsInOverlay: false,  // 如果設(shè)置為true,在瀏覽器中羡亩,eslint的錯(cuò)誤和警告會(huì)以蒙層的方式展現(xiàn)摩疑。

    /**
     * Source Maps
     */

    // https://webpack.js.org/configuration/devtool/#development
    devtool: 'eval-source-map', // 調(diào)試工具
    cacheBusting: true,  // 指定是否通過在文件名稱后面添加一個(gè)查詢字符串來創(chuàng)建source map的緩存
    cssSourceMap: false,  // 是否開啟 cssSourceMap
  },

  // 正式環(huán)境
  build: {
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'), // 編譯注入的 index.html 文件,必須是本地的絕對(duì)路徑

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),  // 編譯輸出的靜態(tài)資源根路徑
    assetsSubDirectory: 'static', // 編譯輸出的二級(jí)目錄
    assetsPublicPath: config.getOutPutPath(), // 編譯發(fā)布上線路徑的根目錄,可配置為資源服務(wù)器域名或 CDN 域名

    // assetsPublicPath: './',

    /**
     * Source Maps
     */

    productionSourceMap: true,  //生成用于生產(chǎn)構(gòu)建的源映射
    devtool: '#source-map', // 調(diào)試代碼的模式夕春,共有7種未荒,這里是生成source-map文件

    productionGzip: false, // 是否開啟 gzip
    productionGzipExtensions: ['js', 'css'], // 需要使用 gzip 壓縮的文件擴(kuò)展名

    // 一個(gè)實(shí)用工具,用于分析項(xiàng)目的依賴關(guān)系
    // 如果這個(gè)選項(xiàng)是true的話,那么則會(huì)在build后及志,會(huì)在瀏覽器中生成一份bundler報(bào)告
    bundleAnalyzerReport: process.env.npm_config_report
  }
}
  • utils.js
const path = require('path') // 引入nodejs的path模塊片排,用于操作路徑
const config = require('../config') // 引入模板的配置文件寨腔,下面就需要去這個(gè)文件中看看有什么基本的配置
const ExtractTextPlugin = require('extract-text-webpack-plugin') // 提取特定文件的插件,比如把css文件提取到一個(gè)文件中去
const packageConfig = require('../package.json') // 加載package.json文件

// 生成編譯輸出的二級(jí)目錄
exports.assetsPath = function (_path) {
  const assetsSubDirectory = process.env.NODE_ENV === 'production'
    ? config.build.assetsSubDirectory
    : config.dev.assetsSubDirectory

  // path.posix是path模塊跨平臺(tái)的實(shí)現(xiàn)(不同平臺(tái)的路徑表示是不一樣的)
  return path.posix.join(assetsSubDirectory, _path)
}

// 為不同的css預(yù)處理器提供一個(gè)統(tǒng)一的生成方式率寡,也就是統(tǒng)一處理各種css類型的打包問題迫卢。
// 這個(gè)是為在vue文件中的style中使用的css類型
exports.cssLoaders = function (options) {
  options = options || {}

  // 打包c(diǎn)ss模塊
  const cssLoader = {
    loader: 'css-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }

  // 編譯postcss模塊
  const postcssLoader = {
    // 使用postcss-loader來打包postcss模塊
    loader: 'postcss-loader',
    // 配置source map
    options: {
      sourceMap: options.sourceMap
    }
  }

  // 創(chuàng)建loader加載器字符串,結(jié)合extract text插件使用
  /**
   *
   * loader:loader的名稱
   * loaderOptions:loader對(duì)應(yīng)的options配置對(duì)象
   */
  function generateLoaders (loader, loaderOptions) {
    // 通過usePostCSS 來標(biāo)明是否使用了postcss
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]

    // 如果指定了具體的loader的名稱
    if (loader) {
      // 向loaders的數(shù)組中添加該loader對(duì)應(yīng)的加載器
      // 一個(gè)很重要的地方就是冶共,一個(gè)數(shù)組中的loader加載器乾蛤,是從右向左執(zhí)行的。
      loaders.push({
        // loader加載器的名稱
        loader: loader + '-loader',
        // 對(duì)應(yīng)的加載器的配置對(duì)象
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    // 如果明確指定了需要提取靜態(tài)文件捅僵,則使用
    // ExtractTextPlugin.extract({})來包裹我們的各種css處理器家卖。
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        // fallback這個(gè)選項(xiàng)我們可以這樣理解
        // webpack默認(rèn)會(huì)按照loaders中的加載器從右向左調(diào)用編譯各種css類型文件。如果一切順利庙楚,在loaders中的
        // 各個(gè)加載器運(yùn)行結(jié)束之后就會(huì)把css文件導(dǎo)入到規(guī)定的文件中去上荡,如果不順利,則繼續(xù)使用vue-style-loader來處理
        // css文件
        fallback: 'vue-style-loader'
      })
    } else {
      // 如果沒有提取行為馒闷,則最后再使用vue-style-loader處理css
      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')
  }
}

// 使用這個(gè)函數(shù)酪捡,為那些獨(dú)立的style文件創(chuàng)建加載器配置。
exports.styleLoaders = function (options) {
  // 保存加載器配置的變量
  const output = []
  // 獲取所有css文件類型的loaders
  const loaders = exports.cssLoaders(options)

  for (const extension in loaders) {
    const loader = loaders[extension]
    // 生成對(duì)應(yīng)的loader配置
    output.push({
      test: new RegExp('\\.' + extension + '$'),
      use: loader
    })
  }

  return output
}

exports.createNotifierCallback = () => {
  // node-notifier是一個(gè)跨平臺(tái)的包纳账,以類似瀏覽器的通知的形式展示信息逛薇。
  const notifier = require('node-notifier')

  return (severity, errors) => {
    // 只展示錯(cuò)誤的信息
    if (severity !== 'error') return

    const error = errors[0]
    const filename = error.file && error.file.split('!').pop()

    // 需要展示的錯(cuò)誤信息的內(nèi)容
    notifier.notify({
      // 通知的標(biāo)題
      title: packageConfig.name,
      // 通知的主體內(nèi)容
      message: severity + ': ' + error.name,
      // 副標(biāo)題
      subtitle: filename || '',
      // 通知展示的icon
      icon: path.join(__dirname, 'logo.png')
    })
  }
}
  • vue.loader.config.js
const utils = require('./utils')
const config = require('../config')
// 設(shè)置是不是生產(chǎn)環(huán)境
const isProduction = process.env.NODE_ENV === 'production'
// 根據(jù)不同的環(huán)境,引入不同的source map配置文件
const sourceMapEnabled = isProduction
  ? config.build.productionSourceMap
  : config.dev.cssSourceMap

module.exports = {
  // vue文件中的css loader配置
  loaders: utils.cssLoaders({
    sourceMap: sourceMapEnabled,
    // 生產(chǎn)環(huán)境下就會(huì)把css文件抽取到一個(gè)獨(dú)立的文件中
    extract: isProduction
  }),
  // css source map文件的配置
  cssSourceMap: sourceMapEnabled,
  // css source map文件緩存控制變量
  cacheBusting: config.dev.cacheBusting,
  transformToRequire: {
    video: ['src', 'poster'],
    source: 'src',
    img: 'src',
    image: 'xlink:href'
  }
}
  • build/webpack.base.conf.js
const path = require('path') // 使用 NodeJS 自帶的文件路徑插件
const utils = require('./utils') //封裝了一些方法的工具
const config = require('../config') //使用 config/index.js
const vueLoaderConfig = require('./vue-loader.conf') //使用vue-loader.conf

function resolve (dir) {
  return path.join(__dirname, '..', dir)  // 拼接我們的工作區(qū)路徑為一個(gè)絕對(duì)路徑
}
// eslint的規(guī)則
const createLintingRule = () => ({
  // 對(duì).js和.vue結(jié)尾的文件進(jìn)行eslint檢查
  test: /\.(js|vue)$/,
  // 使用eslint-loader
  loader: 'eslint-loader',
  // enforce的值可能是pre和post疏虫。其中pre有點(diǎn)和webpack@1中的preLoader配置含義相似永罚。
  // post和v1中的postLoader配置含義相似。表示loader的調(diào)用時(shí)機(jī)
  // 這里表示在調(diào)用其他loader之前需要先調(diào)用這個(gè)規(guī)則進(jìn)行代碼風(fēng)格的檢查
  enforce: 'pre',
  // 需要進(jìn)行eslint檢查的文件的目錄存在的地方
  include: [resolve('src'), resolve('test')],
  // eslint-loader配置過程中需要指定的選項(xiàng)
  options: {
    // 文件風(fēng)格的檢查的格式化程序议薪,這里使用的是第三方的eslint-friendly-formatter
    formatter: require('eslint-friendly-formatter'),
    // 是否需要eslint輸出警告信息
    emitWarning: !config.dev.showEslintErrorsInOverlay
  }
})
var webpack = require('webpack')
module.exports = {
  // webpack在尋找尋找相對(duì)路徑的文件時(shí)候會(huì)以context作為根目錄尤蛮。
  // context默認(rèn)為執(zhí)行啟動(dòng)webpack時(shí)所在的當(dāng)前工作目錄
  context: path.resolve(__dirname, '../'),
   // entry表示入口,webpack構(gòu)建的第一步從entry開始
   // 類型可以是string斯议,object,array
  entry: {
    app: './src/main.js'
  },
  output: {
    // 輸出文件存放的目錄醇锚,必須是string類型的絕對(duì)目錄
    path: config.build.assetsRoot,
    // 通過entry不同生成不同的文件名字哼御,詳情請(qǐng)看文章
    filename: '[name].js',
    // 發(fā)布到線上所有資源的url前綴,為string類型
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath,
    // 導(dǎo)出庫的名稱焊唬,為string類型恋昼,不填寫的時(shí)候,默認(rèn)輸出格式是匿名的立即執(zhí)行函數(shù)
    // library: "KlivitamLibrary"
    // 導(dǎo)出庫的類型赶促,為枚舉類型液肌,默認(rèn)是var
    // 可選值: umd umd2 commonjs2,commonjs,amd,this,var,assign,window,global,jsonp
    // libraryTarget: "jsonp"
    // 是否包含游泳的文件信息到生成的代碼里
    // pathinfo: true
    // 附加chunk的文件名稱
    // chunkFilename: "[id].js"
    // chunkFilename: "[chunkhash].js"
    // jsonp異步加載資源時(shí)的回調(diào)函數(shù)名
    // jsonpFunction: "webpackJsonP"
    // 生成source map文件的名稱
    // sourceMapFilename: "[file].map"
    //瀏覽器開發(fā)者工具里顯示的遠(yuǎn)嗎模塊名稱
    // devtoolModuleFilenameTemplate: "webpack:///[resource-path]"
    // 異步加載跨域的資源時(shí)使用的方式
    // crossOriginLoading: "use-credentials",
    // crossOriginLoading: "anonymous",
    // crossOriginLoading: false,
  },
  // 配置模塊解析時(shí)候的一些選項(xiàng)
  resolve: {
    // 自動(dòng)補(bǔ)全的擴(kuò)展名,能夠使用戶在引入模塊時(shí)不帶擴(kuò)展
    extensions: ['.js', '.vue', '.json'],
    // 默認(rèn)路徑代理,例如 import Vue from 'vue$'鸥滨,會(huì)自動(dòng)到 'vue/dist/vue.esm.js'中尋找
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'components': resolve('src/components'),
      // 可以在引入文件的時(shí)候使用pages符號(hào)引入src/pages文件夾中的文件
      'pages': resolve('src/pages'),
      'http': resolve('src/http'),
      'public': resolve('src/public'),
      'jquery': 'jquery'
    }
  },
  // 下面是針對(duì)具體的模塊進(jìn)行的具體的配置
  // 下面的配置語法采用的是version >= @2的版本
  module: {
    noParse: [/videojs-contrib-hls/], // 不用解析和處理的模塊
    // rules是一個(gè)數(shù)組嗦哆,其中的每一個(gè)元素都是一個(gè)對(duì)象谤祖,這個(gè)對(duì)象是針對(duì)具體類型的文件進(jìn)行的配置。
    rules: [
      ...(config.dev.useEslint ? [createLintingRule()] : []),
      {
        test: /\.vue$/, // 正則匹配loader名字
        loader: 'vue-loader', // loader名字
        // 針對(duì)此加載器的具體配置
        // 針對(duì)前面的分析老速,這個(gè)配置對(duì)象中包含了各種css類型文件的配置粥喜,css source map的配置 以及一些transform的配置
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        // js文件的處理主要使用的是babel-loader。在這里沒有指定具體的編譯規(guī)則橘券,babel-loader會(huì)自動(dòng)
        // 讀取根目錄下面的.babelrc中的babel配置用于編譯js文件
        loader: 'babel-loader',
        // 指定需要進(jìn)行編譯的文件的路徑
        // 這里表示只對(duì)src和test文件夾中的文件進(jìn)行編譯
        include: [resolve('src'), resolve('test'), resolve('config/myapi')] //規(guī)則所包含的文件夾
      },
      {
        // 對(duì)圖片資源進(jìn)行編譯的配置
        // 指定文件的類型
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        // 使用url-loader進(jìn)行文件資源的編譯
        loader: 'url-loader',
        // url-loader的配置選項(xiàng)
        options: {
          // 文件的大小小于10000字節(jié)(10kb)的時(shí)候會(huì)返回一個(gè)dataUrl
          limit: 10000,
          // 生成的文件的保存路徑和后綴名稱
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      { // 對(duì)視頻進(jìn)行打包編譯
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      { // 對(duì)字體進(jìn)行打包編譯
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  },
  // 這些選項(xiàng)用于配置polyfill或mock某些node.js全局變量和模塊额湘。
  // 這可以使最初為nodejs編寫的代碼可以在瀏覽器端運(yùn)行
  node: {
    // 這個(gè)配置是一個(gè)對(duì)象,其中的每個(gè)屬性都是nodejs全局變量或模塊的名稱
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    // 設(shè)置成empty則表示提供一個(gè)空對(duì)象
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}

+build/webpack.dev.conf.js

'use strict'
// 首先引入的是一些工具方法旁舰,下面我們就需要去util文件種看一下有哪些對(duì)應(yīng)的工具方法
const utils = require('./utils')
// 引入webpack模塊
const webpack = require('webpack')
// 引入配置文件
// 這個(gè)配置文件中包含了一些dev和production環(huán)境的基本配置
const config = require('../config')
// 引入webpack-merge模塊锋华。這個(gè)模塊用于把多個(gè)webpack配置合并成一個(gè)配置,后面的配置會(huì)覆蓋前面的配置箭窜。
const merge = require('webpack-merge')
// 引入webpack的基本設(shè)置毯焕,這個(gè)設(shè)置文件包含了開發(fā)環(huán)境和生產(chǎn)環(huán)境的一些公共配置
const baseWebpackConfig = require('./webpack.base.conf')
// 用于生成html文件的插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 這個(gè)插件能夠更好的在終端看到webpack運(yùn)行時(shí)的錯(cuò)誤和警告等信息≌揽欤可以提升開發(fā)體驗(yàn)
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
// 查找一個(gè)未使用的端口
const portfinder = require('portfinder')

// 獲取host環(huán)境變量芥丧,用于配置開發(fā)環(huán)境域名
const HOST = process.env.HOST
// 獲取post環(huán)境變量,用于配置開發(fā)環(huán)境時(shí)候的端口號(hào)
const PORT = process.env.PORT && Number(process.env.PORT)

// 開發(fā)環(huán)境的完整的配置文件
const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
    // 為那些獨(dú)立的css類型文件添加loader配置(沒有寫在vue文件的style標(biāo)簽中的樣式)
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
  },
  // 開發(fā)環(huán)境使用'eval-source-map'模式的source map
  // 因?yàn)樗俣瓤?  devtool: config.dev.devtool,

  // 下面是對(duì)webpack-dev-server選項(xiàng)的基本配置坊罢,這些配置信息续担,我們可以在/config/index.js
  // 文件中進(jìn)行自定義配置。
  devServer: {
    // 用于配置在開發(fā)工具的控制臺(tái)中顯示的日志級(jí)別
    // 注意這個(gè)不是對(duì)bundle的錯(cuò)誤和警告的配置活孩,而是對(duì)它生成之前的消息的配置
    clientLogLevel: 'warning',
    // 表示當(dāng)使用html5的history api的時(shí)候物遇,任意的404響應(yīng)都需要被替代為index.html
    historyApiFallback: true,
    // 啟用webpack的熱替換特性
    hot: true,
    // 一切服務(wù)都需要使用gzip壓縮
    // 可以在js,css等文件的response header中發(fā)現(xiàn)有Content-Encoding:gzip響應(yīng)頭
    compress: true,
    // 指定使用一個(gè) host憾儒。默認(rèn)是 localhost
    // 如果希望服務(wù)器外部可以訪問(通過我們電腦的ip地址和端口號(hào)訪問我們的應(yīng)用)
    // 可以指定0.0.0.0,使用這個(gè)可以使得同一局域網(wǎng)內(nèi)所有設(shè)備都能訪問你的本地網(wǎng)頁
    host: HOST || config.dev.host,
    // 指定要監(jiān)聽請(qǐng)求的端口號(hào)
    port: PORT || config.dev.port,
    // 是否自動(dòng)打開瀏覽器
    open: config.dev.autoOpenBrowser,
    // 當(dāng)編譯出現(xiàn)錯(cuò)誤的時(shí)候询兴,是否希望在瀏覽器中展示一個(gè)全屏的蒙層來展示錯(cuò)誤信息
    overlay: config.dev.errorOverlay
      // 表示只顯示錯(cuò)誤信息而不顯示警告信息
      // 如果兩者都希望顯示,則把這兩項(xiàng)都設(shè)置為true
      ? { warnings: false, errors: true }
      // 設(shè)置為false則表示啥都不顯示
      : false,
    // 指定webpack-dev-server的根目錄起趾,這個(gè)目錄下的所有的文件都是能直接通過瀏覽器訪問的
    // 推薦和output.publicPath設(shè)置為一致
    publicPath: config.dev.assetsPublicPath,
    // 配置代理诗舰,這樣我們就可以跨域訪問某些接口
    // 我們?cè)L問的接口,如果符合這個(gè)選項(xiàng)的配置训裆,就會(huì)通過代理服務(wù)器轉(zhuǎn)發(fā)我們的請(qǐng)求
    proxy: config.dev.proxyTable,
    // 啟用 quiet 后眶根,除了初始啟動(dòng)信息之外的任何內(nèi)容都不會(huì)被打印到控制臺(tái)。這也意味著來自 webpack 的錯(cuò)誤或警告在控制臺(tái)不可見边琉。
    quiet: true, // necessary for FriendlyErrorsPlugin
    // 與監(jiān)視文件相關(guān)的控制選項(xiàng)
    watchOptions: {
      // 如果這個(gè)選項(xiàng)為true属百,會(huì)以輪詢的方式檢查我們的文件的變動(dòng),效率不好
      poll: config.dev.poll,
    }
  },
  plugins: [
    // 創(chuàng)建一個(gè)在編譯時(shí)可以配置的全局變量
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
    }),
    // 啟用熱替換模塊
    // 記住变姨,我們永遠(yuǎn)不要再生產(chǎn)環(huán)境中使用hmr
    new webpack.HotModuleReplacementPlugin(),
    // 這個(gè)插件的主要作用就是在熱加載的時(shí)候直接返回更新文件的名稱族扰,而不是文件的id
    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
    // 使用這個(gè)插件可以在編譯出錯(cuò)的時(shí)候來跳過輸出階段,這樣可以確保輸出資源不會(huì)包含錯(cuò)誤。
    new webpack.NoEmitOnErrorsPlugin(),

    // 這個(gè)插件主要是生成一個(gè)html文件
    new HtmlWebpackPlugin({
      // 生成的html文件的名稱
      filename: 'index.html',
      // 使用的模板的名稱
      template: 'index.html',
      // 將所有的靜態(tài)文件都插入到body文件的末尾
      inject: true
    }),
  ]
})

module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port
  // 這種獲取port的方式會(huì)返回一個(gè)promise
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err)
    } else {
      // 把獲取到的端口號(hào)設(shè)置為環(huán)境變量PORT的值
      process.env.PORT = port
      // 重新設(shè)置webpack-dev-server的端口的值
      devWebpackConfig.devServer.port = port

      // 將FriendlyErrorsPlugin添加到webpack的配置文件中
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        // 編譯成功時(shí)候的輸出信息
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        // 當(dāng)編譯出錯(cuò)的時(shí)候渔呵,根據(jù)config.dev.notifyOnErrors來確定是否需要在桌面右上角顯示錯(cuò)誤通知框
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }))

      // resolve我們的配置文件
      resolve(devWebpackConfig)
    }
  })
})
  • webpack.prod.conf.js
// 引入path模塊
const path = require('path')
// 引入工具方法
const utils = require('./utils')
// 引入webpack模塊
const webpack = require('webpack')
// 引入基本的配置
const config = require('../config')
// 引入webpack-merge模塊
const merge = require('webpack-merge')
// 引入開發(fā)環(huán)境和生產(chǎn)環(huán)境公共的配置
const baseWebpackConfig = require('./webpack.base.conf')
// 這個(gè)模塊主要用于在webpack中拷貝文件和文件夾
const CopyWebpackPlugin = require('copy-webpack-plugin')
// 這個(gè)插件主要是用于基于模版生成html文件的
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 這個(gè)插件主要是用于將入口中所有的chunk怒竿,移到獨(dú)立的分離的css文件中
const ExtractTextPlugin = require('extract-text-webpack-plugin')
// 這個(gè)插件主要是用于壓縮css模塊的
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
// 這個(gè)插件主要是用于壓縮js文件的
// const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

// 配置多份環(huán)境
let env
if(process.argv[2] === 'test'){
  env=require('../config/dev.env')
}else{
  env=require('../config/prod.env')
}

// 合并公共配置和生產(chǎn)環(huán)境獨(dú)有的配置并返回一個(gè)用于生產(chǎn)環(huán)境的webpack配置文件
const webpackConfig = merge(baseWebpackConfig, {
  // 用于生產(chǎn)環(huán)境的一些loader配置
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      // 在生產(chǎn)環(huán)境中使用extract選項(xiàng),這樣就會(huì)把thunk中的css代碼抽離到一份獨(dú)立的css文件中去
      extract: true,
      usePostCSS: true
    })
  },
  // 配置生產(chǎn)環(huán)境中使用的source map的形式厘肮。在這里愧口,生產(chǎn)環(huán)境使用的是#source map的形式
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: {
    // build所產(chǎn)生的文件的存放的文件夾地址
    path: config.build.assetsRoot,
    // build之后的文件的名稱
    // 這里[name]和[chunkhash]都是占位符
    // 其中[name]指的就是模塊的名稱
    // [chunkhash]chunk內(nèi)容的hash字符串,長(zhǎng)度為20
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    // [id]也是一個(gè)占位符类茂,表示的是模塊標(biāo)識(shí)符(module identifier)
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    // 壓縮javascript的插件
    new webpack.optimize.UglifyJsPlugin({
      // 壓縮js的時(shí)候的一些基本配置
      uglifyOptions: {
        // 配置壓縮的行為
        compress: {
          // 在刪除未使用的變量等時(shí)耍属,顯示警告信息,默認(rèn)就是false
          warnings: false
        }
      },
      // 使用 source map 將錯(cuò)誤信息的位置映射到模塊(這會(huì)減慢編譯的速度)
      // 而且這里不能使用cheap-source-map
      sourceMap: config.build.productionSourceMap,
      // 使用多進(jìn)程并行運(yùn)行和文件緩存來提高構(gòu)建速度
      parallel: true
  }),

    // 提取css文件到一個(gè)獨(dú)立的文件中去
    new ExtractTextPlugin({
      // 提取之后css文件存放的地方
      // 其中[name]和[contenthash]都是占位符
      // [name]就是指模塊的名稱
      // [contenthash]根據(jù)提取文件的內(nèi)容生成的 hash
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      // 從所有額外的 chunk(additional chunk) 提取css內(nèi)容
      // (默認(rèn)情況下巩检,它僅從初始chunk(initial chunk) 中提群衿)
      // 當(dāng)使用 CommonsChunkPlugin 并且在公共 chunk 中有提取的 chunk(來自ExtractTextPlugin.extract)時(shí)
      // 這個(gè)選項(xiàng)需要設(shè)置為true
      allChunks: true,
    }),
    // duplicated CSS from different components can be deduped.
    // 使用這個(gè)插件壓縮css,主要是因?yàn)榫た蓿瑢?duì)于不同組件中相同的css可以剔除一部分
    new OptimizeCSSPlugin({
      // 這個(gè)選項(xiàng)的所有配置都會(huì)傳遞給cssProcessor
      // cssProcessor使用這些選項(xiàng)決定壓縮的行為
      cssProcessorOptions: config.build.productionSourceMap
        ? { safe: true, map: { inline: false } }
        : { safe: true }
    }),
    // 創(chuàng)建一個(gè)html文件
    new HtmlWebpackPlugin({
      // 創(chuàng)建一個(gè)html文件
      filename: config.build.index,
      // 使用的模板的名稱
      template: 'index.html',
      // 把script和link標(biāo)簽放在body底部
      inject: true,
      // 配置html的壓縮行為
      minify: {
        // 移除注釋
        removeComments: true,
        // 去除空格和換行
        collapseWhitespace: true,
        // 盡可能移除屬性中的引號(hào)和空屬性
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // chunks 目錄
      chunks: ['manifest', 'vendor', 'app'],
      // 控制chunks的順序领舰,這里表示按照依賴關(guān)系進(jìn)行排序
      // 也可以是一個(gè)函數(shù),自己定義排序規(guī)則
      chunksSortMode: 'dependency'
    }),
    // 根據(jù)模塊的相對(duì)路徑生成一個(gè)四位數(shù)的hash作為模塊id
    new webpack.HashedModuleIdsPlugin(),
    // webpack2處理過的每一個(gè)模塊都會(huì)使用一個(gè)函數(shù)進(jìn)行包裹
    // 這樣會(huì)帶來一個(gè)問題:降低瀏覽器中JS執(zhí)行效率迟螺,這主要是閉包函數(shù)降低了JS引擎解析速度冲秽。
    // webpack3中,通過下面這個(gè)插件就能夠?qū)⒁恍┯新?lián)系的模塊矩父,
    // 放到一個(gè)閉包函數(shù)里面去锉桑,通過減少閉包函數(shù)數(shù)量從而加快JS的執(zhí)行速度。
    new webpack.optimize.ModuleConcatenationPlugin(),
    // 這個(gè)插件用于提取多入口chunk的公共模塊
    // 通過將公共模塊提取出來之后窍株,最終合成的文件能夠在最開始的時(shí)候加載一次
    // 然后緩存起來供后續(xù)使用民轴,這會(huì)帶來速度上的提升。
    new webpack.optimize.CommonsChunkPlugin({
      // 這是 common chunk 的名稱
      name: 'vendor',
      // 把所有從mnode_modules中引入的文件提取到vendor中
      minChunks (module) {
        // 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
        )
      }
    }),
    // 為了將項(xiàng)目中的第三方依賴代碼抽離出來球订,官方文檔上推薦使用這個(gè)插件后裸,當(dāng)我們?cè)陧?xiàng)目里實(shí)際使用之后,
    // 發(fā)現(xiàn)一旦更改了 app.js 內(nèi)的代碼冒滩,vendor.js 的 hash 也會(huì)改變微驶,那么下次上線時(shí),
    // 用戶仍然需要重新下載 vendor.js 與 app.js——這樣就失去了緩存的意義了开睡。所以第二次new就是解決這個(gè)問題的
    // 參考:https://github.com/DDFE/DDFE-blog/issues/10
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
    // This instance extracts shared chunks from code splitted chunks and bundles them
    // in a separate chunk, similar to the vendor chunk
    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    }),

    // copy custom static assets
    // 拷貝靜態(tài)資源到build文件夾中
    new CopyWebpackPlugin([
      {
        // 定義要拷貝的資源的源目錄
        from: path.resolve(__dirname, '../static'),
        // 定義要拷貝的資源的目標(biāo)目錄
        to: config.build.assetsSubDirectory,
        // 忽略拷貝指定的文件祈搜,可以使用模糊匹配
        ignore: ['.*']
      }
    ])
  ]
})

if (config.build.productionGzip) {
  // 如果開啟了生產(chǎn)環(huán)境的gzip
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      // 目標(biāo)資源的名稱
      // [path]會(huì)被替換成原資源路徑
      // [query]會(huì)被替換成原查詢字符串
      asset: '[path].gz[query]',
      // gzip算法
      // 這個(gè)選項(xiàng)可以配置成zlib模塊中的各個(gè)算法
      // 也可以是(buffer, cb) => cb(buffer)
      algorithm: 'gzip',
      // 處理所有匹配此正則表達(dá)式的資源
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      // 只處理比這個(gè)值大的資源
      threshold: 10240,
      // 只有壓縮率比這個(gè)值小的資源才會(huì)被處理
      minRatio: 0.8
    })
  )
}

if (config.build.bundleAnalyzerReport) {
  // 如果需要生成一分bundle報(bào)告,則需要使用下面的這個(gè)插件
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
  • build/check-versions.js
// 在終端為不同字體顯示不同的風(fēng)格
const chalk = require('chalk')
// 解析npm包的version
const semver = require('semver')
// 引入package.json文件
const packageConfig = require('../package.json')
// node版本的uninx shell命令
const shell = require('shelljs')
// 執(zhí)行命令的函數(shù)
function exec (cmd) {
 return require('child_process').execSync(cmd).toString().trim()
}

const versionRequirements = [
 {
   name: 'node',
   // node的版本
   // process.version就是node的版本
   // semver.clean('v8.8.0') => 8.8.0
   currentVersion: semver.clean(process.version),
   // package.json中定義的node版本的范圍
   versionRequirement: packageConfig.engines.node
 }
]

// 相當(dāng)于 which npm
if (shell.which('npm')) {
 // 如果npm命令存在的話
 versionRequirements.push({
   name: 'npm',
   // 檢查npm的版本
   currentVersion: exec('npm --version'),
   // package.json中定義的npm版本
   versionRequirement: packageConfig.engines.npm
 })
}

module.exports = function () {
 const warnings = []

 for (let i = 0; i < versionRequirements.length; i++) {
   const mod = versionRequirements[i]

   // semver.satisfies()進(jìn)行版本之間的比較
   if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
     // 如果現(xiàn)有的npm或者node的版本比定義的版本低士八,則生成一段警告
     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)
 }
}
  • build/build.js
'use strict'
require('./check-versions')() // 檢查npm和node的版本

process.env.NODE_ENV = 'production' // 設(shè)置環(huán)境變量NODE_ENV的值是production


const ora = require('ora') // 終端的spinner
const rm = require('rimraf') // node.js版本的rm -rf
const path = require('path') // 使用Node自帶的文件路徑工具
const chalk = require('chalk') // 引入顯示終端顏色模塊
// const opn = require('opn')   // 一個(gè)可以強(qiáng)制打開瀏覽器并跳轉(zhuǎn)到指定url的插件
const webpack = require('webpack') // 使用webpack
const config = require('../config') // 加載config的配置
const webpackConfig = require('./webpack.prod.conf') // 引入webpack在production環(huán)境下的配置文件

const spinner = ora('building for production...')
spinner.start()

// 刪除打包目標(biāo)目錄下的文件
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
  if (err) throw err
  webpack(webpackConfig, (err, stats) => {
    // 進(jìn)行打包
    spinner.stop()
    if (err) throw err
    // 輸出打包的狀態(tài)
    process.stdout.write(stats.toString({
      colors: true,
      modules: false,
      children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
      chunks: false,
      chunkModules: false
    }) + '\n\n')

    // 如果打包出現(xiàn)錯(cuò)誤
    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'
    ))
  })
})

6、最后說兩句

本來想想還有很多東西要寫的梁呈,預(yù)備是從最基礎(chǔ)的方面說起婚度,但是一想到可能會(huì)有很多東西要寫。外加上我就我自己而言,我都無法去看那么長(zhǎng)的博客蝗茁,何況和我一樣浮躁的程序員呢醋虏?并且,我在這個(gè)上面已經(jīng)花了將近二周的時(shí)間了哮翘。自己其實(shí)也差不多知道webpack再重要也不過是js的一個(gè)附著品颈嚼,自己就算學(xué)的再精湛,也只不過是景上添花饭寺,但是處于js快速增長(zhǎng)期的我 不應(yīng)該把大把的時(shí)間花在這個(gè)上面阻课。總之綜合考慮艰匙,我決定將webpack分為兩個(gè)章節(jié):基礎(chǔ)篇和深入篇限煞。
好了,基礎(chǔ)篇到這里就結(jié)束了员凝,今天從下班開始一直整理到9點(diǎn)多署驻。直接在項(xiàng)目文件上面做的標(biāo)注,不知道老大看到了會(huì)不會(huì)打我健霹。不管了 萬一是躲不過就git reset??????
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末旺上,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子糖埋,更是在濱河造成了極大的恐慌宣吱,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阶捆,死亡現(xiàn)場(chǎng)離奇詭異凌节,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)洒试,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門倍奢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人垒棋,你說我怎么就攤上這事卒煞。” “怎么了叼架?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵畔裕,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我乖订,道長(zhǎng)扮饶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任乍构,我火速辦了婚禮甜无,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己岂丘,他們只是感情好陵究,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奥帘,像睡著了一般铜邮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寨蹋,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天松蒜,我揣著相機(jī)與錄音,去河邊找鬼钥庇。 笑死牍鞠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的评姨。 我是一名探鬼主播难述,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼吐句!你這毒婦竟也來了胁后?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤嗦枢,失蹤者是張志新(化名)和其女友劉穎攀芯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體文虏,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侣诺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了氧秘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片年鸳。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖丸相,靈堂內(nèi)的尸體忽然破棺而出搔确,到底是詐尸還是另有隱情,我是刑警寧澤灭忠,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布膳算,位于F島的核電站,受9級(jí)特大地震影響弛作,放射性物質(zhì)發(fā)生泄漏涕蜂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一映琳、第九天 我趴在偏房一處隱蔽的房頂上張望宇葱。 院中可真熱鬧瘦真,春花似錦、人聲如沸黍瞧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽印颤。三九已至,卻和暖如春穿肄,著一層夾襖步出監(jiān)牢的瞬間年局,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工咸产, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留矢否,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓脑溢,卻偏偏與公主長(zhǎng)得像僵朗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屑彻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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

  • 原文http://www.cnblogs.com/libin-1/p/6596810.html 版本號(hào) vue-c...
    tengrl閱讀 3,634評(píng)論 0 0
  • 最近在學(xué)習(xí) Webpack,網(wǎng)上大多數(shù)入門教程都是基于 Webpack 1.x 版本的,我學(xué)習(xí) Webpack 的...
    My_Oh_My閱讀 8,166評(píng)論 40 247
  • 2016年過去了一個(gè)月验庙, 越來越清楚自己要的是什么, 什么是愛社牲,就是這一刻我還惦記著你粪薛。 晚安。吳悠悠
    溫純丨丨閱讀 97評(píng)論 0 0
  • 小強(qiáng)剛剛畢業(yè)走入社會(huì),一沒經(jīng)驗(yàn)熟空;二沒學(xué)歷藤巢;三沒資金,小白一枚痛阻。為了找工作都面試了十多家公司菌瘪,沒有成功一家。 小強(qiáng)最...
    瘦壯的胖子閱讀 592評(píng)論 0 1
  • 1.短小 函數(shù)的第一規(guī)則是短小。第二規(guī)則是還要更短小弊添。 比較如下兩段代碼 2.只做一件事 重構(gòu)后的代碼看似做了三件...
    小劉and12345閱讀 276評(píng)論 0 0