2018年7月10日 星期二

[Vue-cli] yarn(npm) run build的時候發生什麼事?



node將會去執行build/build.js

//package.json

 "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "lint": "eslint --ext .js,.vue src",
    "build": "node build/build.js"
  },


build/build.js

user strict代表嚴格模式,不可使用未宣告的變數
// build\build.js

'use strict'
require('./check-versions')()

process.env.NODE_ENV = 'production'


build\check-versions.js

//build\check-versions.js

'use strict'
const chalk = require('chalk') //讓terminal裡面的文字有樣式
const semver = require('semver') //產生與解析版本號
const packageConfig = require('../package.json')
const shell = require('shelljs') //可執行Unix Shell的指令

function exec (cmd) { //產生非同步程式線,使執行不阻塞
  return require('child_process').execSync(cmd).toString().trim() 
}

const versionRequirements = [
  {
    name: 'node',
    currentVersion: semver.clean(process.version), //process是全域變數,提供Node相關參數
    versionRequirement: packageConfig.engines.node //放入package.json裡面的engines的node版本
  }
]

if (shell.which('npm')) { //有npm這個指令才會進來,把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]
    //檢查每一個套件合不合Node當前版本
    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)
  }
}


回到build/build.js

//build\build.js
const ora = require('ora') //teminal的loading動畫
const rm = require('rimraf') //如rm -rf,用來刪檔案和資料夾的指令
const path = require('path') //處理路徑
const chalk = require('chalk') //讓terminal裡面的文字有樣式
const webpack = require('webpack') //打包工具
const config = require('../config') //引入config/index.js
const webpackConfig = require('./webpack.prod.conf') //產品模式設定檔案


config\index.js

//config\index.js
  build: {
    // Template for index.ejs, but we use index.html for now because ejs engine does not build yet
    index: path.resolve(__dirname, '../dist/index.html'), //首頁是當前目錄的上層目錄下的dist/index.html
    
    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'), //專案輸出資料夾
    assetsSubDirectory: 'static', //放css和js的資料夾
    assetsPublicPath: '/', //讀link或sript的src時,會從根開始讀(和dist平行)
    
    productionSourceMap: true, //瀏覽器檢查工具選擇source會不會看到vue檔
    // https://webpack.js.org/configuration/devtool/#production
    //由此網址可知#source-map是最完整的模式
    devtool: '#source-map',

    productionGzip: false, //不要用Gzip壓縮
    productionGzipExtensions: ['js', 'css'], 

    bundleAnalyzerReport: process.env.npm_config_report //把報告放到這個變數裡面
  }


build\webpack.prod.conf.js

// build\webpack.prod.conf.js
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')


build\utils.js

// build\utils.js
'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin') //把vue檔裡面的css分離出來變獨立檔案
const packageConfig = require('../package.json')

exports.assetsPath = function (_path) { //宣告assetsPath這個function並輸出出去
  const assetsSubDirectory = process.env.NODE_ENV === 'production'
    ? config.build.assetsSubDirectory //剛剛設定是'static'這個資料夾(會把css和js放進去)
    : config.dev.assetsSubDirectory

  return path.posix.join(assetsSubDirectory, _path) //posix是unix的設計標準
}

exports.cssLoaders = function (options) { //宣告cssLoaders這個function並輸出出去
  options = options || {}

  const cssLoader = {
    loader: 'css-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }

  const postcssLoader = {
    loader: 'postcss-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }

  // generate loader string to be used with extract text plugin
  function generateLoaders (loader, loaderOptions) {
    //有用post css的話,就回傳不一樣的陣列
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]

    if (loader) {
      loaders.push({
        loader: loader + '-loader',
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    // Extract CSS when that option is specified
    // (which is the case during production build)
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders, //所有loader的陣列
        fallback: 'vue-style-loader' //若失敗就還是用vue的方式處理style
      })
    } else {
      return ['vue-style-loader'].concat(loaders)
    }
  }

  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
  return {
    //執行上面宣告好的function
    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 = []
  const loaders = exports.cssLoaders(options)

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

  return output
}

exports.createNotifierCallback = () => {
  const notifier = require('node-notifier')

  return (severity, errors) => {
    if (severity !== 'error') return

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

    notifier.notify({
      title: packageConfig.name,
      message: severity + ': ' + error.name,
      subtitle: filename || '',
      icon: path.join(__dirname, 'logo.png')
    })
  }
}

沒有留言:

張貼留言